linux实现双电池

2019-07-14 01:34发布

硬件配置: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 ");
        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 ");
        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! ");
        goto fail2;
    }
    retval = power_supply_register(dev, &android_ac);
    if (retval) {
        dev_err(dev, "power supply reigister error! ");
        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 ");

    return 0;

fail2:
    platform_set_drvdata(pdev, NULL);
    power_supply_unregister(&btdata->battery);
fail1:
    kfree(btdata);
    return retval;
}