zoukankan      html  css  js  c++  java
  • I.MX6 bq27441 driver hacking

    /*************************************************************************
     *                 I.MX6 bq27441 driver hacking
     * 声明:
     *     本文主要是记录对电池计量芯片bq27441芯片驱动注册过程进行代码跟踪。
     *
     *                                      2016-2-19 深圳 南山平山村 曾剑锋 
     ************************************************************************/
    
    
    
    static int __init bq27x00_battery_init(void)
    {
        int ret;
    
        ret = bq27x00_battery_i2c_init();     -----------------------+
        if (ret)                                                     |
            return ret;                                              |
                                                                     |
        ret = bq27x00_battery_platform_init();                       |
        if (ret)                                                     |
            bq27x00_battery_i2c_exit();       -----------------------*-----+
                                                                     |     |
        return ret;                                                  |     |
    }                                                                |     |
    module_init(bq27x00_battery_init);                               |     |
                                                                     |     |
    static void __exit bq27x00_battery_exit(void)                    |     |
    {                                                                |     |
        bq27x00_battery_platform_exit();                             |     |
        bq27x00_battery_i2c_exit();                                  |     |
    }                                                                |     |
    module_exit(bq27x00_battery_exit);                               |     |
                                                                     |     |
    MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");           |     |
    MODULE_DESCRIPTION("BQ27x00 battery monitor driver");            |     |
    MODULE_LICENSE("GPL");                                           |     |
                                                                     |     |
                                                                     |     |
    static inline int __init bq27x00_battery_i2c_init(void)  <-------+     |
    {                                                                      |
        int ret = i2c_add_driver(&bq27x00_battery_driver);   -----------+  |
        if (ret)                                                        |  |
            printk(KERN_ERR "Unable to register BQ27x00 i2c driver
    "); |  |
                                                                        |  |
        return ret;                                                     |  |
    }                                                                   |  |
                                                                        |  |
    static inline void __exit bq27x00_battery_i2c_exit(void)    <-------*--+
    {                                                                   |
        i2c_del_driver(&bq27x00_battery_driver);                        |
    }                                                                   |
                                                                        |
                                                                        |
    static const struct i2c_device_id bq27x00_id[] = {    <------+      |
        { "bq27200", BQ27200 },                                  |      |
        { "bq27500", BQ27500 },                                  |      |
        { "bq27520", BQ27520 },                                  |      |
        { "bq274xx", BQ274XX },                                  |      |
        { "bq276xx", BQ276XX },                                  |      |
        { "bq2753x", BQ2753X },                                  |      |
        {},                                                      |      |
    };                                                           |      |
    MODULE_DEVICE_TABLE(i2c, bq27x00_id);                        |      |
                                                                 |      |
    static struct i2c_driver bq27x00_battery_driver = {      <---|------+
        .driver = {                                              |
            .name = "bq27x00-battery",                           |
        },                                                       |
        .probe = bq27x00_battery_probe,                   -------*-------+
        .remove = bq27x00_battery_remove,                        |       |
        .id_table = bq27x00_id,                          --------+       |
    };                                                                   |
                                                                         |
    static int __init bq27x00_battery_probe(struct i2c_client *client, <-+
                     const struct i2c_device_id *id)
    {
        char *name;
        struct bq27x00_device_info *di;
        int num;
        int retval = 0;
        u8 *regs;
    
        /* Get new ID for the new battery device */
        retval = idr_pre_get(&battery_id, GFP_KERNEL);
        if (retval == 0)
            return -ENOMEM;
        mutex_lock(&battery_mutex);
        retval = idr_get_new(&battery_id, client, &num);
        mutex_unlock(&battery_mutex);
        if (retval < 0)
            return retval;
    
        name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
        if (!name) {
            dev_err(&client->dev, "failed to allocate device name
    ");
            retval = -ENOMEM;
            goto batt_failed_1;
        }
    
        di = kzalloc(sizeof(*di), GFP_KERNEL);
        if (!di) {
            dev_err(&client->dev, "failed to allocate device info data
    ");
            retval = -ENOMEM;
            goto batt_failed_2;
        }
    
        di->id = num;
        di->dev = &client->dev;
        di->chip = id->driver_data;
        di->bat.name = name;
        di->bus.read = &bq27xxx_read_i2c;                -------------+
        di->bus.write = &bq27xxx_write_i2c;              -------------*-+
        di->bus.blk_read = bq27xxx_read_i2c_blk;         -------------*-*-+
        di->bus.blk_write = bq27xxx_write_i2c_blk;       -------------*-*-*-+
        di->dm_regs = NULL;                                           | | | |
        di->dm_regs_count = 0;                                        | | | |
                                                                      | | | |
        if (di->chip == BQ27200)                                      | | | |
            regs = bq27200_regs;                                      | | | |
        else if (di->chip == BQ27500)                                 | | | |
            regs = bq27500_regs;                                      | | | |
        else if (di->chip == BQ27520)                                 | | | |
            regs = bq27520_regs;                                      | | | |
        else if (di->chip == BQ2753X)                                 | | | |
            regs = bq2753x_regs;                                      | | | |
        else if (di->chip == BQ274XX) {                               | | | |
            regs = bq274xx_regs;                                      | | | |
            di->dm_regs = bq274xx_dm_regs;               -------------*-*-*-*-+
            di->dm_regs_count = ARRAY_SIZE(bq274xx_dm_regs);          | | | | |
        } else if (di->chip == BQ276XX) {                             | | | | |
            /* commands are same as bq274xx, only DM is different */  | | | | |
            regs = bq276xx_regs;                                      | | | | |
            di->dm_regs = bq276xx_dm_regs;                            | | | | |
            di->dm_regs_count = ARRAY_SIZE(bq276xx_dm_regs);          | | | | |
        } else {                                                      | | | | |
            dev_err(&client->dev,                                     | | | | |
                "Unexpected gas gague: %d
    ", di->chip);              | | | | |
            regs = bq27520_regs;                                      | | | | |
        }                                                             | | | | |
                                                                      | | | | |
        memcpy(di->regs, regs, NUM_REGS);                             | | | | |
                                                                      | | | | |
        di->fw_ver = bq27x00_battery_read_fw_version(di);             | | | | |
        dev_info(&client->dev, "Gas Guage fw version is 0x%04x
    ",    | | | | |
                di->fw_ver);                                          | | | | |
                                                                      | | | | |
        retval = bq27x00_powersupply_init(di);                 -------*-*-*-*-*-+
        if (retval)                                                   | | | | | |
            goto batt_failed_3;                                       | | | | | |
                                                                      | | | | | |
        /* Schedule a polling after about 1 min */                    | | | | | |
        schedule_delayed_work(&di->work, 60 * HZ);                    | | | | | |
                                                                      | | | | | |
        i2c_set_clientdata(client, di);                               | | | | | |
        retval = sysfs_create_group(&client->dev.kobj,                | | | | | |
                &bq27x00_attr_group);                                 | | | | | |
        if (retval)                                                   | | | | | |
            dev_err(&client->dev, "could not create sysfs files
    ");  | | | | | |
                                                                      | | | | | |
        return 0;                                                     | | | | | |
                                                                      | | | | | |
    batt_failed_3:                                                    | | | | | |
        kfree(di);                                                    | | | | | |
    batt_failed_2:                                                    | | | | | |
        kfree(name);                                                  | | | | | |
    batt_failed_1:                                                    | | | | | |
        mutex_lock(&battery_mutex);                                   | | | | | |
        idr_remove(&battery_id, num);                                 | | | | | |
        mutex_unlock(&battery_mutex);                                 | | | | | |
                                                                      | | | | | |
        return retval;                                                | | | | | |
    }                                                                 | | | | | |
                                                                      | | | | | |
    static int bq27xxx_read_i2c(struct bq27x00_device_info *di, <-----+ | | | | |
            u8 reg, bool single)                                        | | | | |
    {                                                                   | | | | |
        struct i2c_client *client = to_i2c_client(di->dev);             | | | | |
        struct i2c_msg msg[2];                                          | | | | |
        unsigned char data[2];                                          | | | | |
        int ret;                                                        | | | | |
                                                                        | | | | |
        if (!client->adapter)                                           | | | | |
            return -ENODEV;                                             | | | | |
                                                                        | | | | |
        msg[0].addr = client->addr;                                     | | | | |
        msg[0].flags = 0;                                               | | | | |
        msg[0].buf = &reg;                                              | | | | |
        msg[0].len = sizeof(reg);                                       | | | | |
        msg[1].addr = client->addr;                                     | | | | |
        msg[1].flags = I2C_M_RD;                                        | | | | |
        msg[1].buf = data;                                              | | | | |
        if (single)                                                     | | | | |
            msg[1].len = 1;                                             | | | | |
        else                                                            | | | | |
            msg[1].len = 2;                                             | | | | |
                                                                        | | | | |
        ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));      | | | | |
        if (ret < 0)                                                    | | | | |
            return ret;                                                 | | | | |
                                                                        | | | | |
        if (!single)                                                    | | | | |
            ret = get_unaligned_le16(data);                             | | | | |
        else                                                            | | | | |
            ret = data[0];                                              | | | | |
                                                                        | | | | |
        return ret;                                                     | | | | |
    }                                                                   | | | | |
                                                                        | | | | |
    static int bq27xxx_write_i2c(struct bq27x00_device_info *di,   <----+ | | | |
            u8 reg, int value, bool single)                               | | | |
    {                                                                     | | | |
        struct i2c_client *client = to_i2c_client(di->dev);               | | | |
        struct i2c_msg msg;                                               | | | |
        unsigned char data[4];                                            | | | |
        int ret;                                                          | | | |
                                                                          | | | |
        if (!client->adapter)                                             | | | |
            return -ENODEV;                                               | | | |
                                                                          | | | |
        data[0] = reg;                                                    | | | |
        if (single) {                                                     | | | |
            data[1] = (unsigned char)value;                               | | | |
            msg.len = 2;                                                  | | | |
        } else {                                                          | | | |
            put_unaligned_le16(value, &data[1]);                          | | | |
            msg.len = 3;                                                  | | | |
        }                                                                 | | | |
                                                                          | | | |
        msg.buf = data;                                                   | | | |
        msg.addr = client->addr;                                          | | | |
        msg.flags = 0;                                                    | | | |
                                                                          | | | |
        ret = i2c_transfer(client->adapter, &msg, 1);                     | | | |
        if (ret < 0)                                                      | | | |
            return ret;                                                   | | | |
                                                                          | | | |
        return 0;                                                         | | | |
    }                                                                     | | | |
                                                                          | | | |
    static int bq27xxx_read_i2c_blk(struct bq27x00_device_info *di, <-----+ | | |
        u8 reg, u8 *data, u8 len)                                           | | |
    {                                                                       | | |
        struct i2c_client *client = to_i2c_client(di->dev);                 | | |
        struct i2c_msg msg[2];                                              | | |
        int ret;                                                            | | |
                                                                            | | |
        if (!client->adapter)                                               | | |
            return -ENODEV;                                                 | | |
                                                                            | | |
        msg[0].addr = client->addr;                                         | | |
        msg[0].flags = 0;                                                   | | |
        msg[0].buf = &reg;                                                  | | |
        msg[0].len = 1;                                                     | | |
                                                                            | | |
        msg[1].addr = client->addr;                                         | | |
        msg[1].flags = I2C_M_RD;                                            | | |
        msg[1].buf = data;                                                  | | |
        msg[1].len = len;                                                   | | |
                                                                            | | |
        ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));          | | |
        if (ret < 0)                                                        | | |
            return ret;                                                     | | |
                                                                            | | |
        return ret;                                                         | | |
    }                                                                       | | |
                                                                            | | |
    static int bq27xxx_write_i2c_blk(struct bq27x00_device_info *di, <------+ | |
            u8 reg, u8 *data, u8 sz)                                          | |
    {                                                                         | |
        struct i2c_client *client = to_i2c_client(di->dev);                   | |
        struct i2c_msg msg;                                                   | |
        int ret;                                                              | |
        u8 buf[33];                                                           | |
                                                                              | |
        if (!client->adapter)                                                 | |
            return -ENODEV;                                                   | |
                                                                              | |
        buf[0] = reg;                                                         | |
        memcpy(&buf[1], data, sz);                                            | |
                                                                              | |
        msg.buf = buf;                                                        | |
        msg.addr = client->addr;                                              | |
        msg.flags = 0;                                                        | |
        msg.len = sz + 1;                                                     | |
                                                                              | |
        ret = i2c_transfer(client->adapter, &msg, 1);                         | |
        if (ret < 0)                                                          | |
            return ret;                                                       | |
                                                                              | |
        return 0;                                                             | |
    }                                                                         | |
                                                                              | |
    static struct dm_reg bq274xx_dm_regs[] = {              <-----------------+ |
        {82, 0, 2, 1000},    /* Qmax */                                         |
        {82, 5, 1, 0x81},    /* Load Select */                                  |
        {82, 10, 2, 1340},    /* Design Capacity */                             |
        {82, 12, 2, 3700},    /* Design Energy */                               |
        {82, 16, 2, 3250},    /* Terminate Voltage */                           |
        {82, 27, 2, 110},    /* Taper rate */                                   |
    };                                                                          |
                                                                                |
    static int __init bq27x00_powersupply_init(             <-------------------+
            struct bq27x00_device_info *di)
    {
        int ret;
    
        di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
        if (di->chip == BQ274XX) {
            set_properties_array(di, bq274xx_battery_props,
                ARRAY_SIZE(bq274xx_battery_props));
        } else if (di->chip == BQ276XX) {
            set_properties_array(di, bq276xx_battery_props,
                ARRAY_SIZE(bq276xx_battery_props));
        } else if (di->chip == BQ27520) {
            set_properties_array(di, bq27520_battery_props,
                ARRAY_SIZE(bq27520_battery_props));
        } else if (di->chip == BQ2753X) {
            set_properties_array(di, bq2753x_battery_props,
                ARRAY_SIZE(bq2753x_battery_props));
        } else {
            set_properties_array(di, bq27x00_battery_props,
                ARRAY_SIZE(bq27x00_battery_props));
        }
        di->bat.get_property = bq27x00_battery_get_property;
        di->bat.external_power_changed = bq27x00_external_power_changed;
    
        INIT_DELAYED_WORK(&di->work, bq27x00_battery_poll);    -------------+
        mutex_init(&di->lock);                                              |
                                                                            |
        ret = power_supply_register(di->dev, &di->bat);                     |
        if (ret) {                                                          |
            dev_err(di->dev, "failed to register battery: %d
    ", ret);      |
            return ret;                                                     |
        }                                                                   |
                                                                            |
        dev_info(di->dev, "support ver. %s enabled
    ", DRIVER_VERSION);     |
                                                                            |
        bq27x00_update(di);                                                 |
                                                                            |
        return 0;                                                           |
    }                                                                       |
                                                                            |
    static void bq27x00_battery_poll(struct work_struct *work) <------------+
    {
        struct bq27x00_device_info *di =
            container_of(work, struct bq27x00_device_info, work.work);
    
        if (((di->chip == BQ274XX) || (di->chip == BQ276XX)) &&
            !rom_mode_gauge_dm_initialized(di)) {
            rom_mode_gauge_dm_init(di);                        -------------+
        }                                                                   |
                                                                            |
        bq27x00_update(di);                                    -------------*---+
                                                                            |   |
        if (poll_interval > 0) {                                            |   |
            /* The timer does not have to be accurate. */                   |   |
            set_timer_slack(&di->work.timer, poll_interval * HZ / 4);       |   |
            schedule_delayed_work(&di->work, poll_interval * HZ);           |   |
        }                                                                   |   |
    }                                                                       |   |
                                                                            |   |
    #define INITCOMP_TIMEOUT_MS        10000                                |   |
    static void rom_mode_gauge_dm_init(struct bq27x00_device_info *di)  <---+   |
    {                                                                           |
        int i;                                                                  |
        int timeout = INITCOMP_TIMEOUT_MS;                                      |
        u8 subclass, offset;                                                    |
        u32 blk_number;                                                         |
        u32 blk_number_prev = 0;                                                |
        u8 buf[32];                                                             |
        bool buf_valid = false;                                                 |
        struct dm_reg *dm_reg;                                                  |
                                                                                |
        dev_dbg(di->dev, "%s:
    ", __func__);                                    |
                                                                                |
        while (!rom_mode_gauge_init_completed(di) && timeout > 0) {             |
            msleep(100);                                                        |
            timeout -= 100;                                                     |
        }                                                                       |
                                                                                |
        if (timeout <= 0) {                                                     |
            dev_err(di->dev, "%s: INITCOMP not set after %d seconds
    ",         |
                __func__, INITCOMP_TIMEOUT_MS/100);                             |
            return;                                                             |
        }                                                                       |
                                                                                |
        if (!di->dm_regs || !di->dm_regs_count) {                               |
            dev_err(di->dev, "%s: Data not available for DM initialization
    ",  |
                __func__);                                                      |
            return;                                                             |
        }                                                                       |
                                                                                |
        enter_cfg_update_mode(di);                            ------------+     |
        for (i = 0; i < di->dm_regs_count; i++) {                         |     |
            dm_reg = &di->dm_regs[i];                                     |     |
            subclass = dm_reg->subclass;                                  |     |
            offset = dm_reg->offset;                                      |     |
                                                                          |     |
            /*                                                            |     |
             * Create a composite block number to see if the subsequent   |     |
             * register also belongs to the same 32 btye block in the DM  |     |
             */                                                           |     |
            blk_number = subclass << 8;                                   |     |
            blk_number |= offset >> 5;                                    |     |
                                                                          |     |
            if (blk_number == blk_number_prev) {                          |     |
                copy_to_dm_buf_big_endian(di, buf, offset,                |     |
                    dm_reg->len, dm_reg->data);                           |     |
            } else {                                                      |     |
                                                                          |     |
                if (buf_valid)                                            |     |
                    update_dm_block(di, blk_number_prev >> 8,             |     |
                        (blk_number_prev << 5) & 0xFF , buf);             |     |
                else                                                      |     |
                    buf_valid = true;                                     |     |
                                                                          |     |
                read_dm_block(di, dm_reg->subclass, dm_reg->offset,       |     |
                    buf);                                                 |     |
                copy_to_dm_buf_big_endian(di, buf, offset,                |     |
                    dm_reg->len, dm_reg->data);                           |     |
            }                                                             |     |
            blk_number_prev = blk_number;                                 |     |
        }                                                                 |     |
                                                                          |     |
        /* Last buffer to be written */                                   |     |
        if (buf_valid)                                                    |     |
            update_dm_block(di, subclass, offset, buf); ------------------*-+   |
                                                                          | |   |
        exit_cfg_update_mode(di);                           --------------*-*-+ |
    }                                                                     | | | |
                                                                          | | | |
    #define CFG_UPDATE_POLLING_RETRY_LIMIT 50                             | | | |
    static int enter_cfg_update_mode(struct bq27x00_device_info *di) <----+ | | |
    {                                                                       | | |
        int i = 0;                                                          | | |
        u16 flags;                                                          | | |
                                                                            | | |
        dev_dbg(di->dev, "%s:
    ", __func__);                                | | |
                                                                            | | |
        if (!unseal(di, BQ274XX_UNSEAL_KEY))                                | | |
            return 0;                                                       | | |
                                                                            | | |
        control_cmd_wr(di, SET_CFGUPDATE_SUBCMD);                           | | |
        msleep(5);                                                          | | |
                                                                            | | |
        while (i < CFG_UPDATE_POLLING_RETRY_LIMIT) {                        | | |
            i++;                                                            | | |
            flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);             | | |
            if (flags & (1 << 4))                                           | | |
                break;                                                      | | |
            msleep(100);                                                    | | |
        }                                                                   | | |
                                                                            | | |
        if (i == CFG_UPDATE_POLLING_RETRY_LIMIT) {                          | | |
            dev_err(di->dev, "%s: failed %04x
    ", __func__, flags);         | | |
            return 0;                                                       | | |
        }                                                                   | | |
                           +------------------------------------------------+ | |
        return 1;          |                                                  | |
    }                      |                                                  | |
                           |                                                  | |
                           V                                                  | |
    static int update_dm_block(struct bq27x00_device_info *di, u8 subclass,   | |
        u8 offset, u8 *data)                                                  | |
    {                                                                         | |
        u8 buf[32];                                                           | |
        u8 cksum;                                                             | |
        u8 blk_offset = offset >> 5;                                          | |
                                                                              | |
        dev_dbg(di->dev, "%s: subclass %d offset %d
    ",                       | |
            __func__, subclass, offset);                                      | |
                                                                              | |
        di->bus.write(di, BLOCK_DATA_CONTROL, 0, true);                       | |
        msleep(5);                                                            | |
                                                                              | |
        di->bus.write(di, BLOCK_DATA_CLASS, subclass, true);                  | |
        msleep(5);                                                            | |
                                                                              | |
        di->bus.write(di, DATA_BLOCK, blk_offset, true);                      | |
        msleep(5);                                                            | |
                                                                              | |
        di->bus.blk_write(di, BLOCK_DATA, data, 32);                          | |
        msleep(5);                                                            | |
        print_buf(__func__, data);                                            | |
                                                                              | |
        cksum = checksum(data);                                               | |
        di->bus.write(di, BLOCK_DATA_CHECKSUM, cksum, true);                  | |
        msleep(5);                                                            | |
                                                                              | |
        /* Read back and compare to make sure write is successful */          | |
        di->bus.write(di, DATA_BLOCK, blk_offset, true);                      | |
        msleep(5);                                                            | |
        di->bus.blk_read(di, BLOCK_DATA, buf, 32);                            | |
        if (memcmp(data, buf, 32)) {                                          | |
            dev_err(di->dev, "%s: error updating subclass %d offset %d
    ",    | |
                __func__, subclass, offset);                                  | |
            return 0;                                                         | |
        } else {                                                              | |
            return 1;                                                         | |
        }                                                                     | |
    }                                                                         | |
                                                                              | |
    static int exit_cfg_update_mode(struct bq27x00_device_info *di)   <-------+ |
    {                                                                           |
        int i = 0;                                                              |
        u16 flags;                                                              |
                                                                                |
        dev_dbg(di->dev, "%s:
    ", __func__);                                    |
                                                                                |
        control_cmd_wr(di, BQ274XX_SOFT_RESET);                                 |
                                                                                |
        while (i < CFG_UPDATE_POLLING_RETRY_LIMIT) {                            |
            i++;                                                                |
            flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);                 |
            if (!(flags & (1 << 4)))                                            |
                break;                                                          |
            msleep(100);                                                        |
        }                                                                       |
                                                                                |
        if (i == CFG_UPDATE_POLLING_RETRY_LIMIT) {                              |
            dev_err(di->dev, "%s: failed %04x
    ", __func__, flags);             |
            return 0;                                                           |
        }                                                                       |
                                                                                |
        if (seal(di))                                                           |
            return 1;                                                           |
        else                                                                    |
            return 0;                                                           |
    }                                                                           |
                                                                                |
                                                                                |
    static void bq27x00_update(struct bq27x00_device_info *di)     <------------+
    {
        struct bq27x00_reg_cache cache = {0, };
        bool is_bq27200 = (di->chip == BQ27200);
        bool is_bq27500 = (di->chip == BQ27500);
        bool is_bq274xx = (di->chip == BQ274XX);
        bool is_bq276xx = (di->chip == BQ276XX);
    
        cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, !is_bq27500);
        if (cache.flags >= 0) {
            if (is_bq27200 && (cache.flags & BQ27200_FLAG_CI)) {
                dev_info(di->dev, "battery is not calibrated!
                        ignoring capacity values
    ");
                cache.capacity = -ENODATA;
                cache.energy = -ENODATA;
                cache.time_to_empty = -ENODATA;
                cache.time_to_empty_avg = -ENODATA;
                cache.time_to_full = -ENODATA;
                cache.charge_full = -ENODATA;
                cache.health = -ENODATA;
            } else {
                cache.capacity = bq27x00_battery_read_soc(di);
                if (!(is_bq274xx || is_bq276xx)) {
                    cache.energy = bq27x00_battery_read_energy(di);
                    cache.time_to_empty =
                        bq27x00_battery_read_time(di,
                                BQ27XXX_REG_TTE);
                    cache.time_to_empty_avg =
                        bq27x00_battery_read_time(di,
                                BQ27XXX_REG_TTECP);
                    cache.time_to_full =
                        bq27x00_battery_read_time(di,
                                BQ27XXX_REG_TTF);
                }
                cache.charge_full = bq27x00_battery_read_fcc(di);
                cache.health = bq27x00_battery_read_health(di);
            }
            cache.temperature = bq27x00_battery_read_temperature(di);
            if (!is_bq274xx)
                cache.cycle_count = bq27x00_battery_read_cyct(di);
            cache.power_avg =
                bq27x00_battery_read_pwr_avg(di, BQ27XXX_POWER_AVG);
    
            /* We only have to read charge design full once */
            if (di->charge_design_full <= 0)
                di->charge_design_full = bq27x00_battery_read_dcap(di);
        }
    
        if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) {
            di->cache = cache;
            power_supply_changed(&di->bat);
        }
    
        di->last_update = jiffies;
    }
  • 相关阅读:
    sp_addlinkedserver的一些操作
    MVC3.0,路由设置实现伪静态IIS中404错误
    Chrome本地跨域请求设置,实现HTML模板页
    jQuery异步请求(如getJSON)跨域解决方案
    【海淘域名】GoDaddy账户被锁定后的解决方法
    jquery-qrcode客户端二维码生成类库扩展--融入自定义Logo图片
    JavaScript获取URL参数方法总汇
    ASP.NET 内联代码、内联表达式、数据绑定表达式使用方法罗列(形式就是常说的尖括号 百分号 等于号 井号)
    如何获取Repeater行号(索引)、记录总数?
    判断jquery类库是否加载,如未加载则加载。
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/5200888.html
Copyright © 2011-2022 走看看