硬件配置: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;
}