1. init/exit
static int __init lp3925_init(void)
{
int ret = 0;
ret = i2c_add_driver(&lp3925_driver); //添加lp3925 i2c驱动.
if (ret) {
printk(KERN_WARNING "lp3925: Driver registration failed, \
module not inserted.\n");
return ret;
}
return ret;
}
static void __exit lp3925_exit(void)
{
i2c_del_driver(&lp3925_driver);
}
subsys_initcall(lp3925_init);
module_exit(lp3925_exit);
2. lp3925_driver
static struct i2c_driver lp3925_driver = {
.driver = {
.name = "lp3925",
},
.probe = lp3925_probe, //i2c_add_driver() 注册完成,就会自动运行这个probe函数.
.remove = __devexit_p(lp3925_remove),
.id_table = lp3925_id,
};
lp3925_id static const struct i2c_device_id lp3925_id[] = {
{ "lp3925", 0 }, // "lp3925" , 这个名字必须与 i2c_board_info 中的设备名字一样,系统会进行名字匹配,匹配上了才能i2c_add_driver成功。
{ },
};
3. lp3925_probe()
static int __devinit lp3925_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct lp3925_platform_data *pdata;
struct lp3925_data *data;
int err = 0;
pdata = client->dev.platform_data; //这个platform_data, 就是i2c_board_info中的 *platform_data, 在board.c中进行初始化。
if (pdata == NULL) {
dev_dbg(&client->dev, "No platform data\n");
return -EINVAL;
}
if ((pdata->buck1_dvs > LP3925_BUCK_DVS_MAX)
|| (pdata->buck2_dvs > LP3925_BUCK_DVS_MAX)) {
dev_dbg(&client->dev, "Invalid platform data\n");
return -EINVAL;
}
if (!(data = kzalloc(sizeof(struct lp3925_data), GFP_KERNEL)))
return -ENOMEM;
lp3925_client = client;
/* Init real i2c_client */
data->pdata = pdata;
data->irq_mask = 0xffffffff;
data->irq_gpio = pdata->irq_gpio;
data->irq = gpio_to_irq(pdata->irq_gpio); //irq_gpio就是下面platform_data中的.irq_gpio = mfp_to_gpio(LP3925_INT_PIN),
i2c_set_clientdata(client, data); //设置i2c client data.标准I2C操作。
mutex_init(&data->power_lock);
mutex_init(&data->adc_lock);
mutex_init(&data->reg_lock);
/* Initialize LP3925 hardware. */
if ((err = lp3925_hw_init(data)) != 0) //硬件初始化
goto exit;
/* Set PMIC ops. */
pmic_ops_set(&lp3925_pmic_ops); //lp3925芯片驱动的各种操作
return 0;
exit:
kfree(data);
return err;
}
4.
解析 pdata = client->dev.platform_data; 这里的platform_data 在board.c中初始化:
#if defined(CONFIG_COMIP_LP3925)
/* LP3925. */
{LP3925_INT_PIN, MFP_PIN_MODE_GPIO},
#endif
#if defined(CONFIG_COMIP_LP3925)
static struct lp3925_ldo_ctrl_map comip_lp3925_ldo_ctrl_map[] = { //因为是个PMIC 芯片,所以肯定会出现个种可调节LDO。
{LP3925_LDO_1, LP3925_LDO_CTRL_ALWAYS_ON, LP3925_LDO_CTRL_GPIO_ID_NONE, 2850},
{LP3925_LDO_2, LP3925_LDO_CTRL_REG21, LP3925_LDO_CTRL_GPIO_ID_NONE, 1800},
{LP3925_LDO_3, LP3925_LDO_CTRL_REG00, LP3925_LDO_CTRL_GPIO_ID_NONE, 2200},
{LP3925_LDO_5, LP3925_LDO_CTRL_GPIO4, LP3925_LDO_CTRL_GPIO_ID_NONE, 1800},
{LP3925_LDO_6, LP3925_LDO_CTRL_REG00, LP3925_LDO_CTRL_GPIO_ID_NONE, 3000},
{LP3925_LDO_7, LP3925_LDO_CTRL_REG01, LP3925_LDO_CTRL_GPIO_ID_NONE, 1800},
{LP3925_LDO_11, LP3925_LDO_CTRL_REG10, LP3925_LDO_CTRL_GPIO_ID_NONE, 2850},
{LP3925_LDO_12, LP3925_LDO_CTRL_REG10, LP3925_LDO_CTRL_GPIO_ID_NONE, 3000},
{LP3925_LDO_13, LP3925_LDO_CTRL_REG11, LP3925_LDO_CTRL_GPIO_ID_NONE, 2850},
{LP3925_REFOUT, LP3925_LDO_CTRL_GPIO3, LP3925_LDO_CTRL_GPIO_ID_NONE, 2500},
};
static struct lp3925_ldo_module_map comip_lp3925_ldo_module_map[] = {
{LP3925_LDO_2, PMIC_POWER_CAMERA_DIGITAL, 1, 0},
{LP3925_LDO_3, PMIC_POWER_SWITCH, 0, 0},
{LP3925_LDO_7, PMIC_POWER_CMMB_BB_CORE, 0, 0},
{LP3925_LDO_6, PMIC_POWER_USIM, 0, 0},
{LP3925_LDO_8, PMIC_POWER_RF_GSM_CORE, 0, 1},
{LP3925_LDO_9, PMIC_POWER_RF_GSM_IO, 0, 1},
{LP3925_LDO_10, PMIC_POWER_RF_TDD_CORE, 0, 1},
{LP3925_LDO_11, PMIC_POWER_CMMB_IO, 0, 0},
{LP3925_LDO_12, PMIC_POWER_SDIO, 0, 0},
{LP3925_LDO_13, PMIC_POWER_CAMERA_IO, 1, 0},
{LP3925_LDO_13, PMIC_POWER_CAMERA_CORE, 0, 0},
{LP3925_LDO_13, PMIC_POWER_LCD_CORE, 0, 0},
{LP3925_LDO_EXT_1, PMIC_POWER_LED, 0, 0},
{LP3925_LDO_EXT_2, PMIC_POWER_VIBRATOR, 0, 0},
};
static struct lp3925_pin_map comip_lp3925_pin_map[] = {
{LP3925_PIN_INP1, LP3925_PIN_INP_1000MV},
{LP3925_PIN_INP2, LP3925_PIN_INP_1000MV},
{LP3925_PIN_SINK1, LP3925_PIN_SINK1_125MA},
{LP3925_PIN_SINK2, LP3925_PIN_SINK2_3_100MA},
};
static struct lp3925_platform_data comip_i2c_lp3925_info = {
.irq_gpio = mfp_to_gpio(LP3925_INT_PIN),
.ctrl_map = comip_lp3925_ldo_ctrl_map,
.ctrl_map_num = ARRAY_SIZE(comip_lp3925_ldo_ctrl_map),
.module_map = comip_lp3925_ldo_module_map,
.module_map_num = ARRAY_SIZE(comip_lp3925_ldo_module_map),
.pin_map = comip_lp3925_pin_map,
.pin_map_num = ARRAY_SIZE(comip_lp3925_pin_map),
.buck1_dvs = 1,
.buck2_dvs = 3,
};
#endif
最后是i2c0_board_info中添加的I2C数据项:
#if defined(CONFIG_COMIP_LP3925)
{
.type = "lp3925", // 这个名字就必须与上面的 id 同一个名字,以便进行匹配。
.addr = 0x7e, //i2c slave 地址
.platform_data = &comip_i2c_lp3925_info, //这就是platform_data
},
#endif
一个LP3925重要的数据结构,表达了该芯片的各种属性,细节就不跟了,得跟datasheet联系:
/* Platform data for the LP3925 driver */
struct lp3925_platform_data {
int irq_gpio;
struct lp3925_pin_map* pin_map;
int pin_map_num;
struct lp3925_ldo_timing* ldo_timing;
int ldo_timing_num;
struct lp3925_ldo_pull_down* ldo_pull_down;
int ldo_pull_down_num;
struct lp3925_ldo_ctrl_map* ctrl_map;
int ctrl_map_num;
struct lp3925_ldo_module_map* module_map;
int module_map_num;
/* BUCK DVS selectors. */
u8 buck1_dvs;
u8 buck2_dvs;
};
5. lp3925_hw_init()硬件初始化,芯片的各种初始化流程。
static int lp3925_hw_init(struct lp3925_data *data)
{
int i;
int ret;
/* Mask all interrupts. */
lp3925_reg_write(LP3925_REG_INT_MASK0, 0xff);
lp3925_reg_write(LP3925_REG_INT_MASK1, 0xff);
/* Read version id. */
lp3925_reg_read(LP3925_REG_SILICON_ID, &data->silicon_id);
lp3925_reg_read(LP3925_REG_EEPROM_VERSION_ID, &data->version_id);
/* Read enable registers. */
for (i = 0; i < LP3925_REG_ENABLE_NUM; i++)
lp3925_reg_read(LP3925_REG_ENABLE_BASE + i, &data->enable_reg[i]);
/* Initialize LP3925 reboot. */
lp3925_reboot_init(data);
/* Initialize LP3925 RTC. */
lp3925_rtc_init(data);
/* Initialize LP3925 pin. */
lp3925_pin_init(data);
/* Initialize LP3925 power. */
lp3925_power_init(data);
/* Initialize LP3925 irq. */
ret = lp3925_irq_init(data);
if (ret)
goto exit;
return 0;
exit:
return ret;
}
6. pmic_ops_set(&lp3925_pmic_ops),这个函数在arch/arm/mach-xxx/xxx-pmic.c中。这里就有点设计PM模块了。
int pmic_ops_set(struct pmic_ops *ops)
{
if (g_pmic_ops != NULL) {
printk(KERN_ERR "set pmic_ops when pmic_ops is not NULL\n");
return -EFAULT;
}
g_pmic_ops = ops;
INIT_LIST_HEAD(&g_pmic_ops->list);
spin_lock_init(&g_pmic_ops->cb_lock);
return 0;
}
EXPORT_SYMBOL(pmic_ops_set);
pmic_ops /* PMIC ops. */
struct pmic_ops {
/* Registers. */
int (*reg_write)(u16 reg, u16 val);
int (*reg_read)(u16 reg, u16 *pval);
/* Event. */
int (*event_mask)(int event);
int (*event_unmask)(int event);
/* Power. */
int (*voltage_get)(u8 module, u8 param, int *pmv);
int (*voltage_set)(u8 module, u8 param, int mv);
/* RTC. */
int (*rtc_time_get)(struct rtc_time *tm);
int (*rtc_time_set)(struct rtc_time *tm);
int (*rtc_alarm_get)(u8 id, struct rtc_wkalrm *alrm);
int (*rtc_alarm_set)(u8 id, struct rtc_wkalrm *alrm);
/* Comparator. */
int (*comp_state_get)(u8 id);
/* Power on type. */
int (*power_on_type_get)(void);
/* Power key. */
int (*power_key_state_get)(void);
/* Callback list. */
struct list_head list;
/* Spinlock for callback list. */
spinlock_t cb_lock;
};
7. lp3925_pmic_ops, LP3925具体的pmic_ops.
static struct pmic_ops lp3925_pmic_ops = {
.reg_read = lp3925_pmu_reg_read,
.reg_write = lp3925_pmu_reg_write,
.event_mask = lp3925_event_mask,
.event_unmask = lp3925_event_unmask,
.voltage_get = lp3925_voltage_get,
.voltage_set = lp3925_voltage_set,
.rtc_time_get = lp3925_rtc_time_get,
.rtc_time_set = lp3925_rtc_time_set,
.rtc_alarm_get = lp3925_rtc_alarm_get,
.rtc_alarm_set = lp3925_rtc_alarm_set,
.comp_state_get = lp3925_comp_state_get,
.power_key_state_get = lp3925_power_key_state_get,
.power_on_type_get = lp3925_power_on_type_get,
};