/* * Copyright (c) 2013-2014, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BQ27441_DELAY (30*HZ) #define BQ27441_CONTROL_STATUS 0x0000 #define BQ27441_DEVICE_TYPE 0x0001 #define BQ27441_FW_VERSION 0x0002 #define BQ27441_DM_CODE 0x0004 #define BQ27441_PREV_MACWRITE 0x0007 #define BQ27441_CHEM_ID 0x0008 #define BQ27441_BAT_INSERT 0x000C #define BQ27441_BAT_REMOVE 0x000D #define BQ27441_SET_HIBERNATE 0x0011 #define BQ27441_CLEAR_HIBERNATE 0x0012 #define BQ27441_SET_CFGUPDATE 0x0013 #define BQ27441_SHUTDOWN_ENABLE 0x001B #define BQ27441_SHUTDOWN 0x001C #define BQ27441_SEALED 0x0020 #define BQ27441_PULSE_SOC_INT 0x0023 #define BQ27441_RESET 0x0041 #define BQ27441_SOFT_RESET 0x0042 #define BQ27441_CONTROL_1 0x00 #define BQ27441_CONTROL_2 0x01 #define BQ27441_TEMPERATURE 0x02 #define BQ27441_VOLTAGE 0x04 #define BQ27441_FLAGS 0x06 #define BQ27441_FLAGS_ITPOR (1 << 5) #define BQ27441_NOMINAL_AVAIL_CAPACITY 0x08 #define BQ27441_FULL_AVAIL_CAPACITY 0x0a #define BQ27441_REMAINING_CAPACITY 0x0c #define BQ27441_FULL_CHG_CAPACITY 0x0e #define BQ27441_AVG_CURRENT 0x10 #define BQ27441_STANDBY_CURRENT 0x12 #define BQ27441_MAXLOAD_CURRENT 0x14 #define BQ27441_AVERAGE_POWER 0x18 #define BQ27441_STATE_OF_CHARGE 0x1c #define BQ27441_INT_TEMPERATURE 0x1e #define BQ27441_STATE_OF_HEALTH 0x20 #define BQ27441_BLOCK_DATA_CHECKSUM 0x60 #define BQ27441_BLOCK_DATA_CONTROL 0x61 #define BQ27441_DATA_BLOCK_CLASS 0x3E #define BQ27441_DATA_BLOCK 0x3F #define BQ27441_DESIGN_CAPACITY_1 0x4A #define BQ27441_DESIGN_CAPACITY_2 0x4B #define BQ27441_DESIGN_ENERGY_1 0x4C #define BQ27441_DESIGN_ENERGY_2 0x4D #define BQ27441_TAPER_RATE_1 0x5B #define BQ27441_TAPER_RATE_2 0x5C #define BQ27441_TERMINATE_VOLTAGE_1 0x50 #define BQ27441_TERMINATE_VOLTAGE_2 0x51 #define BQ27441_V_CHG_TERM_1 0x41 #define BQ27441_V_CHG_TERM_2 0x42 #define BQ27441_BATTERY_LOW 15 #define BQ27441_BATTERY_FULL 100 #define BQ27441_MAX_REGS 0x7F struct bq27441_chip { struct i2c_client *client; struct delayed_work work; struct power_supply battery; struct bq27441_platform_data *pdata; struct battery_gauge_dev *bg_dev; struct regmap *regmap; /* battery voltage */ int vcell; /* battery capacity */ int soc; /* State Of Charge */ int status; /* battery health */ int health; /* battery capacity */ int capacity_level; int full_capacity; int design_energy; int taper_rate; int terminate_voltage; int v_chg_term; int lasttime_soc; int lasttime_status; int shutdown_complete; int charge_complete; struct mutex mutex; }; struct bq27441_chip *bq27441_data; static const struct regmap_config bq27441_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = BQ27441_MAX_REGS, }; static int bq27441_read_word(struct i2c_client *client, u8 reg) { int ret; u16 val; struct bq27441_chip *chip = i2c_get_clientdata(client); mutex_lock(&chip->mutex); if (chip && chip->shutdown_complete) { mutex_unlock(&chip->mutex); return -ENODEV; } ret = regmap_raw_read(chip->regmap, reg, (u8 *) &val, sizeof(val)); if (ret < 0) { dev_err(&client->dev, "error reading reg: 0x%x\n", reg); mutex_unlock(&chip->mutex); return ret; } mutex_unlock(&chip->mutex); return val; } static int bq27441_read_byte(struct i2c_client *client, u8 reg) { int ret; u8 val; struct bq27441_chip *chip = i2c_get_clientdata(client); mutex_lock(&chip->mutex); if (chip && chip->shutdown_complete) { mutex_unlock(&chip->mutex); return -ENODEV; } ret = regmap_raw_read(chip->regmap, reg, (u8 *) &val, sizeof(val)); if (ret < 0) { dev_err(&client->dev, "error reading reg: 0x%x\n", reg); mutex_unlock(&chip->mutex); return ret; } mutex_unlock(&chip->mutex); return val; } static int bq27441_write_byte(struct i2c_client *client, u8 reg, u8 value) { struct bq27441_chip *chip = i2c_get_clientdata(client); int ret; mutex_lock(&chip->mutex); if (chip && chip->shutdown_complete) { mutex_unlock(&chip->mutex); return -ENODEV; } ret = regmap_write(chip->regmap, reg, value); if (ret < 0) dev_err(&client->dev, "%s(): Failed in writing register" "0x%02x err %d\n", __func__, reg, ret); mutex_unlock(&chip->mutex); return ret; } static void bq27441_work(struct work_struct *work) { struct bq27441_chip *chip; int val; chip = container_of(work, struct bq27441_chip, work.work); val = bq27441_read_word(chip->client, BQ27441_VOLTAGE); if (val < 0) dev_err(&chip->client->dev, "%s: err %d\n", __func__, val); else chip->vcell = val; val = bq27441_read_word(chip->client, BQ27441_STATE_OF_CHARGE); if (val < 0) dev_err(&chip->client->dev, "%s: err %d\n", __func__, val); else chip->soc = val; if (chip->soc >= BQ27441_BATTERY_FULL && chip->charge_complete != 1) chip->soc = BQ27441_BATTERY_FULL-1; if (chip->status == POWER_SUPPLY_STATUS_FULL && chip->charge_complete) { chip->soc = BQ27441_BATTERY_FULL; chip->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; chip->health = POWER_SUPPLY_HEALTH_GOOD; } else if (chip->soc < BQ27441_BATTERY_LOW) { chip->status = chip->lasttime_status; chip->health = POWER_SUPPLY_HEALTH_DEAD; chip->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; } else { chip->status = chip->lasttime_status; chip->health = POWER_SUPPLY_HEALTH_GOOD; chip->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; } if (chip->soc != chip->lasttime_soc || chip->status != chip->lasttime_status) { chip->lasttime_soc = chip->soc; power_supply_changed(&chip->battery); } schedule_delayed_work(&chip->work, BQ27441_DELAY); } static int bq27441_initialize(struct bq27441_chip *chip) { struct i2c_client *client = chip->client; int old_csum; u8 temp; int new_csum; int old_des_cap; int old_des_energy; int old_taper_rate; int old_terminate_voltage; int old_v_chg_term; u8 old_des_cap_lsb; u8 old_des_cap_msb; u8 old_taper_rate_lsb; u8 old_taper_rate_msb; u8 old_des_energy_lsb; u8 old_des_energy_msb; u8 old_terminate_voltage_lsb; u8 old_terminate_voltage_msb; u8 old_v_chg_term_msb; u8 old_v_chg_term_lsb; int flags_lsb; unsigned long timeout = jiffies + HZ; int ret; flags_lsb = bq27441_read_byte(client, BQ27441_FLAGS); /* Unseal the fuel gauge for data access */ ret = bq27441_write_byte(client, BQ27441_CONTROL_1, 0x00); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_CONTROL_2, 0x80); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_CONTROL_1, 0x00); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_CONTROL_2, 0x80); if (ret < 0) goto fail; /* setup fuel gauge state data block block for ram access */ ret = bq27441_write_byte(client, BQ27441_BLOCK_DATA_CONTROL, 0x00); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_DATA_BLOCK_CLASS, 0x52); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_DATA_BLOCK, 0x00); if (ret < 0) goto fail; /* read check sum */ mdelay(1); old_csum = bq27441_read_byte(client, BQ27441_BLOCK_DATA_CHECKSUM); /* read all the old values that we want to update */ old_des_cap = bq27441_read_word(client, BQ27441_DESIGN_CAPACITY_1); old_des_cap_msb = old_des_cap & 0xFF; old_des_cap_lsb = (old_des_cap & 0xFF00) >> 8; old_des_energy = bq27441_read_word(client, BQ27441_DESIGN_ENERGY_1); old_des_energy_msb = old_des_energy & 0xFF; old_des_energy_lsb = (old_des_energy & 0xFF00) >> 8; old_taper_rate = bq27441_read_word(client, BQ27441_TAPER_RATE_1); old_taper_rate_msb = old_taper_rate & 0xFF; old_taper_rate_lsb = (old_taper_rate & 0xFF00) >> 8; old_terminate_voltage = bq27441_read_word(client, BQ27441_TERMINATE_VOLTAGE_1); old_terminate_voltage_msb = old_terminate_voltage & 0xFF; old_terminate_voltage_lsb = (old_terminate_voltage & 0xFF00) >> 8; dev_info(&chip->client->dev, "FG values:\n capacity old: %d new: %d\n" "design_energy old:%d new:%d\n" "taper_rate old:%d new:%d\n" "terminate_voltage old:%d new:%d\n" "itpor flag:%d checksum:%d\n", be16_to_cpu(old_des_cap), chip->full_capacity, be16_to_cpu(old_des_energy), chip->design_energy, be16_to_cpu(old_taper_rate), chip->taper_rate, be16_to_cpu(old_terminate_voltage), chip->terminate_voltage, (flags_lsb & BQ27441_FLAGS_ITPOR), old_csum); /* if the values match with the required ones or if POR bit is not set seal the fuel gauge and return */ if ((chip->full_capacity == be16_to_cpu(old_des_cap)) && (chip->design_energy == be16_to_cpu(old_des_energy)) && (chip->taper_rate == be16_to_cpu(old_taper_rate)) && (chip->terminate_voltage == be16_to_cpu(old_terminate_voltage)) && (!(flags_lsb & BQ27441_FLAGS_ITPOR))) { dev_info(&chip->client->dev, "FG is already programmed\n"); goto unseal; } /* place the fuel gauge into config update */ dev_info(&chip->client->dev, "Programming Bq27441!\n"); ret = bq27441_write_byte(client, BQ27441_CONTROL_1, 0x13); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_CONTROL_2, 0x00); if (ret < 0) goto fail; while (!(bq27441_read_byte(client, BQ27441_FLAGS) & 0x10)) { if (time_after(jiffies, timeout)) { dev_warn(&chip->client->dev, "timeout waiting for cfg update\n"); return -ETIMEDOUT; } msleep(1); } /* update new config to fuel gauge */ ret = bq27441_write_byte(client, BQ27441_DESIGN_CAPACITY_1, (chip->full_capacity & 0xFF00) >> 8); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_DESIGN_CAPACITY_2, chip->full_capacity & 0xFF); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_DESIGN_ENERGY_1, (chip->design_energy & 0xFF00) >> 8); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_DESIGN_ENERGY_2, chip->design_energy & 0xFF); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_TAPER_RATE_1, (chip->taper_rate & 0xFF00) >> 8); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_TAPER_RATE_2, chip->taper_rate & 0xFF); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_TERMINATE_VOLTAGE_1, (chip->terminate_voltage & 0xFF00) >> 8); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_TERMINATE_VOLTAGE_2, chip->terminate_voltage & 0xFF); if (ret < 0) goto fail; /* calculate the new checksum */ temp = (255 - old_csum - old_des_cap_lsb - old_des_cap_msb - old_des_energy_lsb - old_des_energy_msb - old_taper_rate_lsb - old_taper_rate_msb - old_terminate_voltage_lsb - old_terminate_voltage_msb) % 256; new_csum = 255 - (((temp + (chip->full_capacity & 0xFF) + ((chip->full_capacity & 0xFF00) >> 8) + (chip->design_energy & 0xFF) + ((chip->design_energy & 0xFF00) >> 8) + (chip->taper_rate & 0xFF) + ((chip->taper_rate & 0xFF00) >> 8) + (chip->terminate_voltage & 0xFF) + ((chip->terminate_voltage & 0xFF00) >> 8) )) % 256); ret = bq27441_write_byte(client, BQ27441_BLOCK_DATA_CHECKSUM, new_csum); if (ret < 0) goto fail; /* read checksum again to ensure that data was written properly */ ret = bq27441_write_byte(client, BQ27441_DATA_BLOCK_CLASS, 0x52); if (ret < 0) goto fail; old_csum = bq27441_read_byte(client, BQ27441_BLOCK_DATA_CHECKSUM); if (old_csum != new_csum) dev_info(&chip->client->dev, "checksum write failed old:%d, new:%d\n", new_csum, old_csum); else dev_info(&chip->client->dev, "checksum written old:%d, new:%d\n", new_csum, old_csum); /* setup fuel gauge state data block block for ram access */ ret = bq27441_write_byte(client, BQ27441_BLOCK_DATA_CONTROL, 0x00); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_DATA_BLOCK_CLASS, 0x52); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_DATA_BLOCK, 0x01); if (ret < 0) goto fail; mdelay(1); old_csum = bq27441_read_byte(client, BQ27441_BLOCK_DATA_CHECKSUM); old_v_chg_term = bq27441_read_word(client, BQ27441_V_CHG_TERM_1); old_v_chg_term_msb = old_v_chg_term & 0xFF; old_v_chg_term_lsb = (old_v_chg_term & 0xFF00) >> 8; ret = bq27441_write_byte(client, BQ27441_V_CHG_TERM_1, (chip->v_chg_term & 0xFF00) >> 8); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_V_CHG_TERM_2, chip->v_chg_term & 0xFF); if (ret < 0) goto fail; temp = (255 - old_csum - old_v_chg_term_lsb - old_v_chg_term_msb) % 256; new_csum = 255 - ((temp + (chip->v_chg_term & 0xFF) + ((chip->v_chg_term & 0xFF00) >> 8) ) % 256); ret = bq27441_write_byte(client, BQ27441_BLOCK_DATA_CHECKSUM, new_csum); if (ret < 0) goto fail; /* read checksum again to ensure that data was written properly */ ret = bq27441_write_byte(client, BQ27441_DATA_BLOCK_CLASS, 0x52); if (ret < 0) goto fail; old_csum = bq27441_read_byte(client, BQ27441_BLOCK_DATA_CHECKSUM); if (old_csum != new_csum) dev_info(&chip->client->dev, "checksum write failed old:%d, new:%d\n", new_csum, old_csum); else dev_info(&chip->client->dev, "checksum written old:%d, new:%d\n", new_csum, old_csum); /* exit config update mode */ ret = bq27441_write_byte(client, BQ27441_CONTROL_1, 0x42); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_CONTROL_2, 0x00); if (ret < 0) goto fail; while (!(bq27441_read_byte(client, BQ27441_FLAGS) & 0x10)) { if (time_after(jiffies, timeout)) { dev_warn(&chip->client->dev, "timeout waiting for cfg update\n"); return -ETIMEDOUT; } msleep(1); } unseal: /* unseal the fuel gauge before exit */ ret = bq27441_write_byte(client, BQ27441_CONTROL_1, 0x20); if (ret < 0) goto fail; ret = bq27441_write_byte(client, BQ27441_CONTROL_2, 0x00); if (ret < 0) goto fail; return 0; fail: return -EIO; } static int bq27441_get_temperature(void) { int val; val = bq27441_read_word(bq27441_data->client, BQ27441_TEMPERATURE); if (val < 0) { dev_err(&bq27441_data->client->dev, "%s: err %d\n", __func__, val); return -EINVAL; } return val / 100; } static enum power_supply_property bq27441_battery_props[] = { POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, }; static int bq27441_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct bq27441_chip *chip = container_of(psy, struct bq27441_chip, battery); int temperature; switch (psp) { case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; break; case POWER_SUPPLY_PROP_STATUS: val->intval = chip->status; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: val->intval = chip->vcell; battery_gauge_record_voltage_value(chip->bg_dev, chip->vcell); break; case POWER_SUPPLY_PROP_CAPACITY: val->intval = chip->soc; if (chip->soc == 15) dev_warn(&chip->client->dev, "\nSystem Running low on battery - 15 percent\n"); if (chip->soc == 10) dev_warn(&chip->client->dev, "\nSystem Running low on battery - 10 percent\n"); if (chip->soc == 5) dev_warn(&chip->client->dev, "\nSystem Running low on battery - 5 percent\n"); battery_gauge_record_capacity_value(chip->bg_dev, chip->soc); break; case POWER_SUPPLY_PROP_HEALTH: val->intval = chip->health; break; case POWER_SUPPLY_PROP_CAPACITY_LEVEL: val->intval = chip->capacity_level; break; case POWER_SUPPLY_PROP_TEMP: temperature = bq27441_get_temperature(); val->intval = temperature; break; default: return -EINVAL; } return 0; } static int bq27441_update_battery_status(struct battery_gauge_dev *bg_dev, enum battery_charger_status status) { struct bq27441_chip *chip = battery_gauge_get_drvdata(bg_dev); if (status == BATTERY_CHARGING) { chip->charge_complete = 0; chip->status = POWER_SUPPLY_STATUS_CHARGING; } else if (status == BATTERY_CHARGING_DONE) { chip->charge_complete = 1; chip->soc = BQ27441_BATTERY_FULL; chip->status = POWER_SUPPLY_STATUS_FULL; power_supply_changed(&chip->battery); return 0; } else { chip->status = POWER_SUPPLY_STATUS_DISCHARGING; chip->charge_complete = 0; } chip->lasttime_status = chip->status; power_supply_changed(&chip->battery); return 0; } static struct battery_gauge_ops bq27441_bg_ops = { .update_battery_status = bq27441_update_battery_status, .get_battery_temp = bq27441_get_temperature, }; static struct battery_gauge_info bq27441_bgi = { .cell_id = 0, .bg_ops = &bq27441_bg_ops, }; static void of_bq27441_parse_platform_data(struct i2c_client *client, struct bq27441_platform_data *pdata) { u32 tmp; char const *pstr; struct device_node *np = client->dev.of_node; if (!of_property_read_u32(np, "ti,design-capacity", &tmp)) pdata->full_capacity = (unsigned long)tmp; if (!of_property_read_u32(np, "ti,design-energy", &tmp)) pdata->full_energy = (unsigned long)tmp; if (!of_property_read_u32(np, "ti,taper-rate", &tmp)) pdata->taper_rate = (unsigned long)tmp; if (!of_property_read_u32(np, "ti,terminate-voltage", &tmp)) pdata->terminate_voltage = (unsigned long)tmp; if (!of_property_read_u32(np, "ti,v-at-chg-term", &tmp)) pdata->v_at_chg_term = (unsigned long)tmp; if (!of_property_read_string(np, "ti,tz-name", &pstr)) pdata->tz_name = pstr; else dev_err(&client->dev, "Failed to read tz-name\n"); } static int bq27441_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct bq27441_chip *chip; int ret; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; chip->client = client; if (client->dev.of_node) { chip->pdata = devm_kzalloc(&client->dev, sizeof(*chip->pdata), GFP_KERNEL); if (!chip->pdata) return -ENOMEM; of_bq27441_parse_platform_data(client, chip->pdata); } else { chip->pdata = client->dev.platform_data; } if (!chip->pdata) return -ENODATA; chip->full_capacity = 1200; if (chip->pdata->full_capacity) chip->full_capacity = chip->pdata->full_capacity; if (chip->pdata->full_energy) chip->design_energy = chip->pdata->full_energy; if (chip->pdata->taper_rate) chip->taper_rate = chip->pdata->taper_rate; if (chip->pdata->terminate_voltage) chip->terminate_voltage = chip->pdata->terminate_voltage; if (chip->pdata->v_at_chg_term) chip->v_chg_term = chip->pdata->v_at_chg_term; dev_info(&client->dev, "Battery capacity is %d\n", chip->full_capacity); bq27441_data = chip; mutex_init(&chip->mutex); chip->shutdown_complete = 0; i2c_set_clientdata(client, chip); chip->battery.name = "battery"; chip->battery.type = POWER_SUPPLY_TYPE_BATTERY; chip->battery.get_property = bq27441_get_property; chip->battery.properties = bq27441_battery_props; chip->battery.num_properties = ARRAY_SIZE(bq27441_battery_props); chip->status = POWER_SUPPLY_STATUS_DISCHARGING; chip->lasttime_status = POWER_SUPPLY_STATUS_DISCHARGING; chip->charge_complete = 0; chip->regmap = devm_regmap_init_i2c(client, &bq27441_regmap_config); if (IS_ERR(chip->regmap)) { ret = PTR_ERR(chip->regmap); dev_err(&client->dev, "regmap init failed with err %d\n", ret); goto error; } /* Dummy read to check if the slave is present */ ret = bq27441_read_word(chip->client, BQ27441_VOLTAGE); if (ret < 0) { dev_err(&chip->client->dev, "Exiting driver as xfer failed\n"); goto error; } ret = power_supply_register(&client->dev, &chip->battery); if (ret) { dev_err(&client->dev, "failed: power supply register\n"); goto error; } bq27441_bgi.tz_name = chip->pdata->tz_name; chip->bg_dev = battery_gauge_register(&client->dev, &bq27441_bgi, chip); if (IS_ERR(chip->bg_dev)) { ret = PTR_ERR(chip->bg_dev); dev_err(&client->dev, "battery gauge register failed: %d\n", ret); goto bg_err; } ret = bq27441_initialize(chip); if (ret < 0) dev_err(&client->dev, "chip init failed - %d\n", ret); INIT_DEFERRABLE_WORK(&chip->work, bq27441_work); schedule_delayed_work(&chip->work, 0); battery_gauge_record_snapshot_values(chip->bg_dev, jiffies_to_msecs(BATTERY_SNAPSHOT_INTERVAL)); return 0; bg_err: power_supply_unregister(&chip->battery); error: mutex_destroy(&chip->mutex); return ret; } static int bq27441_remove(struct i2c_client *client) { struct bq27441_chip *chip = i2c_get_clientdata(client); battery_gauge_unregister(chip->bg_dev); power_supply_unregister(&chip->battery); cancel_delayed_work_sync(&chip->work); mutex_destroy(&chip->mutex); return 0; } static void bq27441_shutdown(struct i2c_client *client) { struct bq27441_chip *chip = i2c_get_clientdata(client); cancel_delayed_work_sync(&chip->work); mutex_lock(&chip->mutex); chip->shutdown_complete = 1; mutex_unlock(&chip->mutex); } #ifdef CONFIG_PM_SLEEP static int bq27441_suspend(struct device *dev) { struct bq27441_chip *chip = dev_get_drvdata(dev); cancel_delayed_work_sync(&chip->work); return 0; } static int bq27441_resume(struct device *dev) { struct bq27441_chip *chip = dev_get_drvdata(dev); schedule_delayed_work(&chip->work, BQ27441_DELAY); return 0; } #endif /* CONFIG_PM */ static SIMPLE_DEV_PM_OPS(bq27441_pm_ops, bq27441_suspend, bq27441_resume); #ifdef CONFIG_OF static const struct of_device_id bq27441_dt_match[] = { { .compatible = "ti,bq27441" }, { }, }; MODULE_DEVICE_TABLE(of, bq27441_dt_match); #endif static const struct i2c_device_id bq27441_id[] = { { "bq27441", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, bq27441_id); static struct i2c_driver bq27441_i2c_driver = { .driver = { .name = "bq27441", .of_match_table = of_match_ptr(bq27441_dt_match), .pm = &bq27441_pm_ops, }, .probe = bq27441_probe, .remove = bq27441_remove, .id_table = bq27441_id, .shutdown = bq27441_shutdown, }; static int __init bq27441_init(void) { return i2c_add_driver(&bq27441_i2c_driver); } fs_initcall_sync(bq27441_init); static void __exit bq27441_exit(void) { i2c_del_driver(&bq27441_i2c_driver); } module_exit(bq27441_exit); MODULE_AUTHOR("Chaitanya Bandi "); MODULE_DESCRIPTION("BQ27441 Fuel Gauge"); MODULE_LICENSE("GPL");