硬件配置:IMX53 双电池采用smbus接口
需求:android 能正确显示当前两块电池中电量高的那块容量等信息
问题:android 没有双电池架构,所以底层(linux驱动)实现两块电池是不可行
解决方案:
1.linux电池设备驱动调用两次,注册两个电池设备,在之上写一层驱动,负责产生向上报告事件
2.由于电池本身是i2c设备,可以注册i2c设备驱动,在该驱动内什么不做,只进行I2C总线读写,并供电池驱动调用。
3.不注册i2c设备驱动,并只注册一个电池驱动,但嵌套进两个i2c_client结构。
方法1,2在linux驱动层并没有对应的框架,实现起来会很麻烦。
方法三起初没有想到具体的实现,即如何得到i2c_client结构体,以及对应的adapter。
通常开发嵌入式驱动的人都习惯在平台文件里,静态的添加I2C设备信息,即I2C_board_info结构数组,在调用
63 i2c_register_board_info(int busnum,
64 struct i2c_board_info const *info, unsigned len)
注册进__i2c_board_list,一个I2C设备对应一个i2c_devinfo结构。这样在加载驱动后,会进行probe和绑定。
在阅读了linux内核文档后发现这样几个函数,可以实现I2C设备的添加,而并不需要注册对应驱动就能得到I2c_client。
447 extern struct i2c_adapter *i2c_get_adapter(int id);根据总线号得到adapter。
448 extern void i2c_put_adapter(struct i2c_adapter *adap);释放adapter
279 /* Add-on boards should register/unregister their devices; e.g. a board
280 * with integrated I2C, a config eeprom, sensors, and a codec that's
281 * used in conjunction with the primary hardware.
282 */
283 extern struct i2c_client *
284 i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);将i2c_board_info信息添加近对应的adapter上,并得到对应的i2c_client结构。
285
286 /* If you don't know the exact address of an I2C device, use this variant
287 * instead, which can probe for device presence in a list of possible
288 * addresses. The "probe" callback function is optional. If it is provided,
289 * it must return 1 on successful probe, 0 otherwise. If it is not provided,
290 * a default probing method is used.
291 */
292 extern struct i2c_client *
293 i2c_new_probed_device(struct i2c_adapter *adap,
294 struct i2c_board_info *info,
295 unsigned short const *addr_list,
296 int (*probe)(struct i2c_adapter *, unsigned short addr));
这样将电池注册成platform_device设备,在platform_driver驱动probe函数中,动态注册i2c设备并得到其i2c_client就可以操作总线了。
#define I2C_ADAPTER1_DEVICE 1
#define I2C_ADAPTER2_DEVICE 2
驱动私有数据
struct android_battery_data {
struct i2c_client *bt1_client;
struct i2c_client *bt2_client;
struct power_supply battery;
struct workqueue_struct *wq;
struct delayed_work work;
};
probe函数如下:
static int __devinit android_battery_probe(struct platform_device *pdev)
{
int retval = -1;
struct android_battery_data *btdata;
struct i2c_adapter *adapter1 = NULL;
struct i2c_adapter *adapter2 = NULL;
struct i2c_client *client;
struct device *dev = &pdev->dev;
adapter1 = i2c_get_adapter(I2C_ADAPTER1_DEVICE);
if (!adapter1)
return -ENODEV;
if (!i2c_check_functionality(adapter1, I2C_FUNC_SMBUS_BYTE))
return -EIO;
adapter2 = i2c_get_adapter(I2C_ADAPTER2_DEVICE);
if (!adapter2)
return -ENODEV;
if (!i2c_check_functionality(adapter2, I2C_FUNC_SMBUS_BYTE))
return -EIO;
btdata = kmalloc(sizeof(struct android_battery_data), GFP_KERNEL);
if (!btdata) {
return -ENOMEM;
}
memset(btdata, 0, sizeof(struct android_battery_data));
client = i2c_new_device(adapter1, &mxc_battery1_info);
if (!client) {
dev_err(dev, "Not found battery 1\n");
retval = -EINVAL;
goto fail1;
}
btdata->bt1_client = client;
i2c_set_clientdata(client, btdata);
client = i2c_new_device(adapter2, &mxc_battery2_info);
if (!client) {
dev_err(dev, "Not found battery 2\n");
retval = -EINVAL;
goto fail1;
}
btdata->bt2_client = client;
i2c_set_clientdata(client, btdata);
btdata->battery.name = "android_battery";
btdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
btdata->battery.supplied_to = android_battery_supplied_to;
btdata->battery.num_supplicants = ARRAY_SIZE(android_battery_supplied_to);
btdata->battery.properties = android_battery_props;
btdata->battery.num_properties = ARRAY_SIZE(android_battery_props);
btdata->battery.get_property = android_battery_get_property;
btdata->battery.use_for_apm = 1;
platform_set_drvdata(pdev, btdata);
retval = power_supply_register(dev, &btdata->battery);
if (retval) {
dev_err(dev, "power supply register error!\n");
goto fail2;
}
retval = power_supply_register(dev, &android_ac);
if (retval) {
dev_err(dev, "power supply reigister error!\n");
goto fail2;
}
INIT_DELAYED_WORK(&btdata->work, android_battery_work);
btdata->wq = create_singlethread_workqueue("android_battery");
if (!btdata->wq) {
retval = -ESRCH;
goto fail2;
}
queue_delayed_work(btdata->wq, &btdata->work, HZ);
dev_dbg(dev, "android battery probed\n");
return 0;
fail2:
platform_set_drvdata(pdev, NULL);
power_supply_unregister(&btdata->battery);
fail1:
kfree(btdata);
return retval;
}