linux實現雙電池

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




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章