tegrakernel/kernel/kernel-4.9/drivers/power/supply/bq27441_battery.c

1521 lines
40 KiB
C

/*
* Copyright (c) 2013-2017, 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 <http://www.gnu.org/licenses/>.
*/
#include <asm/unaligned.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/power/battery-charger-gauge-comm.h>
#include <linux/pm.h>
#include <linux/jiffies.h>
#include <linux/regmap.h>
#include <linux/interrupt.h>
#include <linux/power/bq27441_battery.h>
#define BQ27441_DELAY msecs_to_jiffies(30000)
#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_1 0x07
#define BQ27441_FLAGS_FC_DETECT BIT(1)
#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_QMAX_CELL_1 0x40
#define BQ27441_QMAX_CELL_2 0x41
#define BQ27441_RESERVE_CAP_1 0x43
#define BQ27441_RESERVE_CAP_2 0x44
#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_CC_GAIN 0x44
#define BQ27441_CC_DELTA 0x48
#define BQ27441_MAX_REGS 0x7F
#define BQ27441_DESIGN_CAPACITY_DEFAULT 7800
#define BQ27441_DESIGN_ENERGY_DEFAULT 28080
#define BQ27441_TAPER_RATE_DEFAULT 100
#define BQ27441_TERMINATE_VOLTAGE_DEFAULT 3200
#define BQ27441_VAT_CHG_TERM_DEFAULT 4100
#define BQ27441_CC_GAIN_DEFAULT 0x7F7806C9
#define BQ27441_CC_DELTA_DEFAULT 0x940D197A
#define BQ27441_QMAX_CELL_DEFAULT 16384
#define BQ27441_RESERVE_CAP_DEFAULT 0
#define BQ27441_OPCTEMPS_MASK 0x01
#define BQ27441_OPCONFIG_1 0x40
#define BQ27441_OPCONFIG_2 0x41
#define BQ27441_CHARGE_CURNT_THRESHOLD 2000
#define BQ27441_INPUT_POWER_THRESHOLD 7000
#define BQ27441_BATTERY_SOC_THRESHOLD 90
#define MAX_STRING_PRINT 50
struct bq27441_chip {
struct i2c_client *client;
struct delayed_work work;
struct delayed_work fc_work;
struct power_supply *battery;
struct power_supply_desc psy_desc;
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 temperature;
int read_failed;
int full_capacity;
int design_energy;
int taper_rate;
int terminate_voltage;
int v_chg_term;
u32 cc_gain;
u32 cc_delta;
u32 qmax_cell;
u32 reserve_cap;
int lasttime_soc;
int lasttime_status;
int shutdown_complete;
int charge_complete;
int print_once;
bool enable_temp_prop;
bool full_charge_state;
bool fcc_fg_initialize;
bool low_battery_shutdown;
u32 full_charge_capacity;
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_u32(struct i2c_client *client, u8 reg)
{
int ret;
u32 val;
struct bq27441_chip *chip = i2c_get_clientdata(client);
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);
return ret;
}
return val;
}
static int bq27441_read_word(struct i2c_client *client, u8 reg)
{
int ret;
u16 val;
struct bq27441_chip *chip = i2c_get_clientdata(client);
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);
return ret;
}
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);
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);
return ret;
}
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;
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);
return ret;
}
static int bq27441_get_battery_soc(struct battery_gauge_dev *bg_dev)
{
struct bq27441_chip *chip = battery_gauge_get_drvdata(bg_dev);
int 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
val = battery_gauge_get_adjusted_soc(chip->bg_dev,
chip->pdata->threshold_soc,
chip->pdata->maximum_soc, val * 100);
return val;
}
static int bq27441_update_soc_voltage(struct bq27441_chip *chip)
{
int val;
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 = battery_gauge_get_adjusted_soc(chip->bg_dev,
chip->pdata->threshold_soc,
chip->pdata->maximum_soc, val * 100);
if (chip->low_battery_shutdown && chip->soc == 0)
chip->soc = 1;
if (chip->soc == BQ27441_BATTERY_FULL && chip->charge_complete) {
chip->status = POWER_SUPPLY_STATUS_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->charge_complete = 0;
chip->status = chip->lasttime_status;
chip->health = POWER_SUPPLY_HEALTH_GOOD;
chip->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
}
return 0;
}
static void bq27441_work(struct work_struct *work)
{
struct bq27441_chip *chip;
chip = container_of(work, struct bq27441_chip, work.work);
mutex_lock(&chip->mutex);
if (chip->shutdown_complete) {
mutex_unlock(&chip->mutex);
return;
}
bq27441_update_soc_voltage(chip);
if (chip->soc != chip->lasttime_soc ||
chip->status != chip->lasttime_status) {
chip->lasttime_soc = chip->soc;
power_supply_changed(chip->battery);
}
mutex_unlock(&chip->mutex);
battery_gauge_report_battery_soc(chip->bg_dev, chip->soc);
schedule_delayed_work(&chip->work, BQ27441_DELAY);
}
static int bq27441_battemps_enable(struct bq27441_chip *chip)
{
struct i2c_client *client = chip->client;
u8 temp;
int ret;
int old_opconfig;
int old_csum, new_csum;
unsigned long timeout = jiffies + HZ;
/* 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, 0x40);
if (ret < 0)
goto fail;
ret = bq27441_write_byte(client, BQ27441_DATA_BLOCK, 0x00);
if (ret < 0)
goto fail;
mdelay(1);
old_opconfig = bq27441_read_word(client, BQ27441_OPCONFIG_1);
/* if the TEMPS bit is set seal the fuel gauge and return */
if (BQ27441_OPCTEMPS_MASK & be16_to_cpu(old_opconfig)) {
dev_info(&chip->client->dev, "FG TEMPS already enabled\n");
goto seal;
}
/* read check sum */
old_csum = bq27441_read_byte(client, BQ27441_BLOCK_DATA_CHECKSUM);
/* place the fuel gauge into config update */
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");
goto fail;
}
msleep(20);
}
/* update TEMPS config to fuel gauge */
ret = bq27441_write_byte(client, BQ27441_OPCONFIG_2,
((old_opconfig >> 8) | BQ27441_OPCTEMPS_MASK));
if (ret < 0)
goto fail;
temp = (255 - old_csum
- (old_opconfig & 0xFF)
- ((old_opconfig >> 8) & 0xFF)) % 256;
new_csum = 255 - ((temp
+ (old_opconfig & 0xFF)
+ ((old_opconfig >> 8) |
BQ27441_OPCTEMPS_MASK)) % 256);
ret = bq27441_write_byte(client, BQ27441_BLOCK_DATA_CHECKSUM,
new_csum);
if (ret < 0)
goto fail;
seal:
/* seal 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:
dev_info(&chip->client->dev, "FG OPCONFIG TEMPS enable failed\n");
return -EIO;
}
static int bq27441_initialize_cc_cal_block(struct bq27441_chip *chip)
{
struct i2c_client *client = chip->client;
int old_csum, new_csum;
u8 temp;
u32 old_cc_gain, old_cc_delta;
u8 addr, val;
int i = 0;
int ret;
/* setup cc cal 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, 105);
if (ret < 0)
goto fail;
ret = bq27441_write_byte(client, BQ27441_DATA_BLOCK, 0x00);
if (ret < 0)
goto fail;
mdelay(1);
/* read check sum */
old_csum = bq27441_read_byte(client, BQ27441_BLOCK_DATA_CHECKSUM);
/* read all the old values that we want to update */
old_cc_gain = bq27441_read_u32(client, BQ27441_CC_GAIN);
old_cc_delta = bq27441_read_u32(client, BQ27441_CC_DELTA);
dev_info(&chip->client->dev, "cc cal values:\n gain old: %08x new: %08x\n"
"delta old: %08x new: %08x\n",
be32_to_cpu(old_cc_gain), chip->cc_gain,
be32_to_cpu(old_cc_delta), chip->cc_delta);
if (be32_to_cpu(old_cc_delta) != chip->cc_delta ||
be32_to_cpu(old_cc_gain) != chip->cc_gain) {
temp = (255 - old_csum
- (old_cc_gain & 0xFF)
- ((old_cc_gain >> 8) & 0xFF)
- ((old_cc_gain >> 16) & 0xFF)
- ((old_cc_gain >> 24) & 0xFF)
- (old_cc_delta & 0xFF)
- ((old_cc_delta >> 8) & 0xFF)
- ((old_cc_delta >> 16) & 0xFF)
- ((old_cc_delta >> 24) & 0xFF)) % 256;
new_csum = 255 - (((temp
+ (chip->cc_gain & 0xFF)
+ ((chip->cc_gain >> 8) & 0xFF)
+ ((chip->cc_gain >> 16) & 0xFF)
+ ((chip->cc_gain >> 24) & 0xFF)
+ (chip->cc_delta & 0xFF)
+ ((chip->cc_delta >> 8) & 0xFF)
+ ((chip->cc_delta >> 16) & 0xFF)
+ ((chip->cc_delta >> 24) & 0xFF)
)) % 256);
/* program cc_gain */
for (i = 0; i < sizeof(u32); i++) {
val = (chip->cc_gain >> (sizeof(u32) - i - 1)*8) & 0xFF;
addr = BQ27441_CC_GAIN + i;
ret = bq27441_write_byte(client, addr, val);
if (ret < 0)
goto fail;
}
/* program cc_delta */
for (i = 0; i < sizeof(u32); i++) {
val = ((chip->cc_delta >> (sizeof(u32) - i - 1)*8)
& 0xFF);
addr = BQ27441_CC_DELTA + i;
ret = bq27441_write_byte(client, addr, val);
if (ret < 0)
goto fail;
}
ret = bq27441_write_byte(client, BQ27441_BLOCK_DATA_CHECKSUM,
new_csum);
if (ret < 0)
goto fail;
}
return 0;
fail:
return -EIO;
}
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;
u32 old_qmax_cell;
u32 old_reserve_cap;
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_qmax_cell = bq27441_read_word(client, BQ27441_QMAX_CELL_1);
old_reserve_cap = bq27441_read_word(client, BQ27441_RESERVE_CAP_1);
old_des_cap = bq27441_read_word(client, BQ27441_DESIGN_CAPACITY_1);
old_des_energy = bq27441_read_word(client, BQ27441_DESIGN_ENERGY_1);
old_taper_rate = bq27441_read_word(client, BQ27441_TAPER_RATE_1);
old_terminate_voltage = bq27441_read_word(client,
BQ27441_TERMINATE_VOLTAGE_1);
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"
"qmax_cell old:%d new:%d\n"
"reserve_cap 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_qmax_cell), chip->qmax_cell,
be16_to_cpu(old_reserve_cap), chip->reserve_cap,
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)) && !chip->fcc_fg_initialize) {
dev_info(&chip->client->dev, "FG is already programmed\n");
goto seal;
}
/* 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(20);
}
/* update new config to fuel gauge */
ret = bq27441_write_byte(client, BQ27441_QMAX_CELL_1,
(chip->qmax_cell & 0xFF00) >> 8);
if (ret < 0)
goto fail;
ret = bq27441_write_byte(client, BQ27441_QMAX_CELL_2,
chip->qmax_cell & 0xFF);
if (ret < 0)
goto fail;
ret = bq27441_write_byte(client, BQ27441_RESERVE_CAP_1,
(chip->reserve_cap & 0xFF00) >> 8);
if (ret < 0)
goto fail;
ret = bq27441_write_byte(client, BQ27441_RESERVE_CAP_2,
chip->reserve_cap & 0xFF);
if (ret < 0)
goto fail;
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 & 0xFF)
- ((old_des_cap >> 8) & 0xFF)
- (old_des_energy & 0xFF)
- ((old_des_energy >> 8) & 0xFF)
- (old_taper_rate & 0xFF)
- ((old_taper_rate >> 8) & 0xFF)
- (old_qmax_cell & 0xFF)
- ((old_qmax_cell >> 8) & 0xFF)
- (old_reserve_cap & 0xFF)
- ((old_reserve_cap >> 8) & 0xFF)
- (old_terminate_voltage & 0xFF)
- ((old_terminate_voltage >> 8) & 0xFF)) % 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->qmax_cell & 0xFF)
+ ((chip->qmax_cell & 0xFF00) >> 8)
+ (chip->reserve_cap & 0xFF)
+ ((chip->reserve_cap & 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;
/* setup fuel gauge state data block page 1 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);
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 & 0xFF)
- ((old_v_chg_term >> 8) & 0xFF)) % 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;
/* program the cc_cal block */
if (bq27441_initialize_cc_cal_block(chip) < 0)
goto fail;
/* 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(20);
}
seal:
/* seal 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;
int retries = 2;
int i;
if (bq27441_data->shutdown_complete)
return bq27441_data->temperature;
for (i = 0; i < retries; i++) {
val = bq27441_read_word(bq27441_data->client,
BQ27441_TEMPERATURE);
if (val < 0)
continue;
}
if (val < 0) {
bq27441_data->read_failed++;
dev_err(&bq27441_data->client->dev,
"%s: err %d\n", __func__, val);
if (bq27441_data->read_failed > 50)
return val;
return bq27441_data->temperature;
}
bq27441_data->read_failed = 0;
bq27441_data->temperature = val / 100;
return val / 100;
}
static enum power_supply_property bq27441_battery_props[] = {
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_CHARGER_STANDARD,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
POWER_SUPPLY_PROP_CURRENT_NOW,
};
static int bq27441_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct bq27441_chip *chip = power_supply_get_drvdata(psy);
int temperature;
int power_mw = 0, input_curnt_ma = 0;
int ret = 0;
mutex_lock(&chip->mutex);
if (chip->shutdown_complete) {
mutex_unlock(&chip->mutex);
return -EINVAL;
}
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 = 1000 * chip->vcell;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = chip->soc;
if (chip->print_once && (chip->soc > 15 || chip->soc < 5))
chip->print_once = 0;
if (chip->soc == 15 && chip->print_once != 15) {
chip->print_once = 15;
dev_warn(&chip->client->dev,
"System Running low on battery - 15 percent\n");
}
if (chip->soc == 10 && chip->print_once != 10) {
chip->print_once = 10;
dev_warn(&chip->client->dev,
"System Running low on battery - 10 percent\n");
}
if (chip->soc == 5 && chip->print_once != 5) {
chip->print_once = 5;
dev_warn(&chip->client->dev,
"System Running low on battery - 5 percent\n");
}
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = chip->health;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = 1;
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
val->intval = chip->capacity_level;
break;
case POWER_SUPPLY_PROP_CHARGER_STANDARD:
val->strval = "unknown";
input_curnt_ma = battery_gauge_get_input_current_limit(
chip->bg_dev);
battery_gauge_get_input_power(chip->bg_dev, &power_mw);
power_mw = abs(power_mw);
if (input_curnt_ma >= BQ27441_CHARGE_CURNT_THRESHOLD &&
power_mw <= BQ27441_INPUT_POWER_THRESHOLD &&
chip->soc < BQ27441_BATTERY_SOC_THRESHOLD)
val->strval = "sub-standard";
else if (input_curnt_ma == 0 && power_mw == 0)
val->strval = "no-charger";
else
val->strval = "standard";
break;
case POWER_SUPPLY_PROP_TEMP:
case POWER_SUPPLY_PROP_TEMP_INFO:
if (chip->enable_temp_prop) {
temperature = bq27441_get_temperature();
val->intval = temperature;
} else if (chip->pdata->tz_name) {
ret = battery_gauge_get_battery_temperature(
chip->bg_dev, &temperature);
if (ret < 0) {
dev_err(&chip->client->dev,
"temp invalid %d\n", ret);
chip->read_failed++;
if (chip->read_failed > 50)
break;
temperature = chip->temperature;
ret = 0;
} else {
chip->read_failed = 0;
chip->temperature = temperature;
}
val->intval = temperature * 10;
} else {
ret = -EINVAL;
}
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
val->intval = 0;
input_curnt_ma = battery_gauge_get_input_current_limit(
chip->bg_dev);
if (input_curnt_ma > 0)
val->intval = input_curnt_ma * 1000;
else
ret = input_curnt_ma;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = 0;
ret = battery_gauge_get_battery_current(chip->bg_dev,
&input_curnt_ma);
if (!ret)
val->intval = 1000 * input_curnt_ma;
break;
default:
ret = -EINVAL;
break;
}
mutex_unlock(&chip->mutex);
return ret;
}
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);
int val;
mutex_lock(&chip->mutex);
if (chip->shutdown_complete) {
mutex_unlock(&chip->mutex);
return 0;
}
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 = battery_gauge_get_adjusted_soc(chip->bg_dev,
chip->pdata->threshold_soc,
chip->pdata->maximum_soc, val * 100);
if (chip->low_battery_shutdown && chip->soc == 0)
chip->soc = 1;
if (status == BATTERY_CHARGING) {
chip->charge_complete = 0;
chip->status = POWER_SUPPLY_STATUS_CHARGING;
} else if (status == BATTERY_CHARGING_DONE) {
if (chip->soc == BQ27441_BATTERY_FULL) {
chip->charge_complete = 1;
chip->status = POWER_SUPPLY_STATUS_FULL;
chip->capacity_level =
POWER_SUPPLY_CAPACITY_LEVEL_FULL;
}
goto done;
} else {
chip->status = POWER_SUPPLY_STATUS_DISCHARGING;
chip->charge_complete = 0;
}
chip->lasttime_status = chip->status;
done:
mutex_unlock(&chip->mutex);
power_supply_changed(chip->battery);
dev_dbg(&chip->client->dev,
"%s() Battery status: %d and SoC: %d%% UI status: %d\n",
__func__, status, chip->soc, chip->status);
return 0;
}
static struct battery_gauge_ops bq27441_bg_ops = {
.update_battery_status = bq27441_update_battery_status,
.get_battery_temp = bq27441_get_temperature,
.get_battery_soc = bq27441_get_battery_soc,
};
static struct battery_gauge_info bq27441_bgi = {
.cell_id = 0,
.bg_ops = &bq27441_bg_ops,
.current_channel_name = "battery-current",
};
static void bq27441_fc_work(struct work_struct *work)
{
struct bq27441_chip *chip;
int val = 0;
chip = container_of(to_delayed_work(work),
struct bq27441_chip, fc_work);
if (chip->full_charge_capacity) {
val = bq27441_read_word(chip->client,
BQ27441_FULL_CHG_CAPACITY);
if (val < 0) {
dev_err(&chip->client->dev, "FCC read failed %d\n",
val);
} else if (val < chip->full_charge_capacity) {
dev_info(&chip->client->dev,
"Full charge capacity %d\n", val);
chip->fcc_fg_initialize = true;
val = bq27441_initialize(chip);
if (val < 0)
dev_err(&chip->client->dev,
"chip init failed:%d\n", val);
schedule_delayed_work(&chip->fc_work, BQ27441_DELAY);
return;
} else
dev_info(&chip->client->dev,
"Full Charge Capacity %d\n", val);
chip->fcc_fg_initialize = false;
}
dev_info(&chip->client->dev, "Full charge status: %d\n",
chip->full_charge_state);
battery_gauge_fc_state(chip->bg_dev, chip->full_charge_state);
}
static ssize_t bq27441_show_lowbat_shutdown_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct bq27441_chip *bq27441 = i2c_get_clientdata(client);
mutex_lock(&bq27441->mutex);
if (bq27441->shutdown_complete) {
mutex_unlock(&bq27441->mutex);
return -EIO;
}
mutex_unlock(&bq27441->mutex);
if (bq27441->low_battery_shutdown)
return snprintf(buf, MAX_STRING_PRINT, "disabled\n");
else
return snprintf(buf, MAX_STRING_PRINT, "enabled\n");
}
static ssize_t bq27441_set_lowbat_shutdown_state(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct bq27441_chip *bq27441 = i2c_get_clientdata(client);
bool enabled;
if ((*buf == 'E') || (*buf == 'e'))
enabled = true;
else if ((*buf == 'D') || (*buf == 'd'))
enabled = false;
else
return -EINVAL;
mutex_lock(&bq27441->mutex);
if (bq27441->shutdown_complete) {
mutex_unlock(&bq27441->mutex);
return -EIO;
}
if (enabled)
bq27441->low_battery_shutdown = false;
else
bq27441->low_battery_shutdown = true;
mutex_unlock(&bq27441->mutex);
return count;
}
static DEVICE_ATTR(low_battery_shutdown, (S_IRUGO | (S_IWUSR | S_IWGRP)),
bq27441_show_lowbat_shutdown_state,
bq27441_set_lowbat_shutdown_state);
static struct attribute *bq27441_attributes[] = {
&dev_attr_low_battery_shutdown.attr,
NULL
};
static const struct attribute_group bq27441_attr_group = {
.attrs = bq27441_attributes,
};
static irqreturn_t bq27441_irq(int id, void *dev)
{
struct bq27441_chip *chip = dev;
struct i2c_client *client = chip->client;
bq27441_update_soc_voltage(chip);
power_supply_changed(chip->battery);
dev_info(&client->dev, "%s() Battery Voltage %dmV and SoC %d%%\n",
__func__, chip->vcell, chip->soc);
if (chip->soc == BQ27441_BATTERY_FULL && !chip->full_charge_state) {
chip->full_charge_state = 1;
schedule_delayed_work(&chip->fc_work, 0);
} else if (chip->soc < BQ27441_BATTERY_FULL &&
chip->full_charge_state) {
chip->full_charge_state = 0;
schedule_delayed_work(&chip->fc_work, 0);
}
return IRQ_HANDLED;
}
static void of_bq27441_parse_platform_data(struct i2c_client *client,
struct bq27441_platform_data *pdata)
{
u32 tmp;
char const *pstr;
bool dt_param_not_found = 0;
struct device_node *np = client->dev.of_node;
if (!of_property_read_u32(np, "ti,design-capacity", &tmp))
pdata->full_capacity = (unsigned long)tmp;
else {
dt_param_not_found = 1;
dev_warn(&client->dev, "fail to read design-capacity\n");
}
if (!of_property_read_u32(np, "ti,design-energy", &tmp))
pdata->full_energy = (unsigned long)tmp;
else {
dt_param_not_found = 1;
dev_warn(&client->dev, "fail to read design-energy\n");
}
if (!of_property_read_u32(np, "ti,taper-rate", &tmp))
pdata->taper_rate = (unsigned long)tmp;
else {
dt_param_not_found = 1;
dev_warn(&client->dev, "fail to read taper-rate\n");
}
if (!of_property_read_u32(np, "ti,terminate-voltage", &tmp))
pdata->terminate_voltage = (unsigned long)tmp;
else {
dt_param_not_found = 1;
dev_warn(&client->dev, "fail to read terminate-voltage\n");
}
if (!of_property_read_u32(np, "ti,v-at-chg-term", &tmp))
pdata->v_at_chg_term = (unsigned long)tmp;
if (!of_property_read_u32(np, "ti,cc-gain", &tmp))
pdata->cc_gain = tmp;
else {
dt_param_not_found = 1;
dev_warn(&client->dev, "fail to read cc-gain\n");
}
if (!of_property_read_u32(np, "ti,cc-delta", &tmp))
pdata->cc_delta = tmp;
else {
dt_param_not_found = 1;
dev_warn(&client->dev, "fail to read cc-delta\n");
}
if (!of_property_read_u32(np, "ti,qmax-cell", &tmp))
pdata->qmax_cell = tmp;
else {
dt_param_not_found = 1;
dev_warn(&client->dev, "fail to read qmax-cell\n");
}
if (!of_property_read_u32(np, "ti,reserve-cap-mah", &tmp))
pdata->reserve_cap = tmp;
else {
dt_param_not_found = 1;
dev_warn(&client->dev, "fail to read reserve-cap-mah\n");
}
if (!of_property_read_string(np, "ti,tz-name", &pstr))
pdata->tz_name = pstr;
else
dev_warn(&client->dev, "Failed to read tz-name\n");
if (!of_property_read_u32(np, "ti,kernel-threshold-soc", &tmp))
pdata->threshold_soc = tmp;
if (!of_property_read_u32(np, "ti,kernel-maximum-soc", &tmp))
pdata->maximum_soc = tmp;
else
pdata->maximum_soc = 100;
if (!of_property_read_u32(np, "ti,full-charge-capacity", &tmp))
pdata->full_charge_capacity = tmp;
pdata->enable_temp_prop = of_property_read_bool(np,
"ti,enable-temp-prop");
pdata->support_battery_current = of_property_read_bool(np,
"io-channel-names");
if (dt_param_not_found)
dev_warn(&client->dev,
"All the FG properties not provided in DT\n");
}
static int bq27441_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct bq27441_chip *chip;
struct power_supply_config bq27441_psy_cfg = {};
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;
chip->print_once = 0;
chip->full_charge_state = 0;
chip->fcc_fg_initialize = 0;
chip->low_battery_shutdown = 0;
chip->full_capacity = chip->pdata->full_capacity ?:
BQ27441_DESIGN_CAPACITY_DEFAULT;
chip->design_energy = chip->pdata->full_energy ?:
BQ27441_DESIGN_ENERGY_DEFAULT;
chip->taper_rate = chip->pdata->taper_rate ?:
BQ27441_TAPER_RATE_DEFAULT;
chip->terminate_voltage = chip->pdata->terminate_voltage ?:
BQ27441_TERMINATE_VOLTAGE_DEFAULT;
chip->v_chg_term = chip->pdata->v_at_chg_term ?:
BQ27441_VAT_CHG_TERM_DEFAULT;
chip->cc_gain = chip->pdata->cc_gain ?: BQ27441_CC_GAIN_DEFAULT;
chip->cc_delta = chip->pdata->cc_delta ?: BQ27441_CC_DELTA_DEFAULT;
chip->qmax_cell = chip->pdata->qmax_cell ?: BQ27441_QMAX_CELL_DEFAULT;
chip->reserve_cap = chip->pdata->reserve_cap ?:
BQ27441_RESERVE_CAP_DEFAULT;
chip->enable_temp_prop = chip->pdata->enable_temp_prop;
chip->full_charge_capacity = chip->pdata->full_charge_capacity;
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->psy_desc.name = "battery";
chip->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY,
chip->psy_desc.get_property = bq27441_get_property,
chip->psy_desc.properties = bq27441_battery_props,
chip->psy_desc.num_properties = ARRAY_SIZE(bq27441_battery_props),
chip->status = POWER_SUPPLY_STATUS_DISCHARGING;
chip->lasttime_status = POWER_SUPPLY_STATUS_DISCHARGING;
chip->charge_complete = 0;
if (chip->pdata->tz_name)
bq27441_battery_props[0] = POWER_SUPPLY_PROP_TEMP_INFO;
/* Remove current property if it is not supported */
if (!chip->pdata->support_battery_current)
chip->psy_desc.num_properties--;
if (!chip->enable_temp_prop && chip->pdata->tz_name)
chip->temperature = 23;
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;
}
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;
}
bq27441_psy_cfg.drv_data = chip;
chip->battery = power_supply_register(&client->dev,
&chip->psy_desc, &bq27441_psy_cfg);
if (IS_ERR(chip->battery)) {
ret = PTR_ERR(chip->battery);
dev_err(&client->dev, "failed: power supply register\n");
goto error;
}
ret = bq27441_initialize(chip);
if (ret < 0)
dev_err(&client->dev, "chip init failed - %d\n", ret);
if (chip->enable_temp_prop)
bq27441_battemps_enable(chip);
bq27441_update_soc_voltage(chip);
INIT_DEFERRABLE_WORK(&chip->work, bq27441_work);
schedule_delayed_work(&chip->work, 0);
INIT_DELAYED_WORK(&chip->fc_work, bq27441_fc_work);
if (client->irq) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, bq27441_irq,
IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
dev_name(&client->dev), chip);
if (ret < 0) {
dev_err(&client->dev,
"%s: request IRQ %d fail, err = %d\n",
__func__, client->irq, ret);
client->irq = 0;
goto irq_reg_error;
}
}
device_set_wakeup_capable(&client->dev, 1);
device_wakeup_enable(&client->dev);
dev_info(&client->dev, "Battery Voltage %dmV and SoC %d%%\n",
chip->vcell, chip->soc);
if (chip->soc == BQ27441_BATTERY_FULL && !chip->full_charge_state) {
chip->full_charge_state = 1;
schedule_delayed_work(&chip->fc_work, 0);
}
ret = sysfs_create_group(&client->dev.kobj, &bq27441_attr_group);
if (ret < 0) {
dev_err(&client->dev, "sysfs create failed %d\n", ret);
return ret;
}
return 0;
irq_reg_error:
cancel_delayed_work_sync(&chip->work);
cancel_delayed_work_sync(&chip->fc_work);
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);
if (chip->client->irq)
disable_irq(chip->client->irq);
mutex_lock(&chip->mutex);
chip->shutdown_complete = 1;
mutex_unlock(&chip->mutex);
cancel_delayed_work_sync(&chip->work);
dev_info(&chip->client->dev, "At shutdown Voltage %dmV and SoC %d%%\n",
chip->vcell, chip->soc);
}
#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);
if (device_may_wakeup(&chip->client->dev) && chip->client->irq)
enable_irq_wake(chip->client->irq);
dev_info(&chip->client->dev, "At suspend Voltage %dmV and SoC %d%%\n",
chip->vcell, chip->soc);
return 0;
}
static int bq27441_resume(struct device *dev)
{
struct bq27441_chip *chip = dev_get_drvdata(dev);
if (device_may_wakeup(&chip->client->dev) && chip->client->irq)
disable_irq_wake(chip->client->irq);
mutex_lock(&chip->mutex);
bq27441_update_soc_voltage(chip);
power_supply_changed(chip->battery);
mutex_unlock(&chip->mutex);
dev_info(&chip->client->dev, "At resume Voltage %dmV and SoC %d%%\n",
chip->vcell, chip->soc);
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 <bandik@nvidia.com>");
MODULE_DESCRIPTION("BQ27441 Fuel Gauge");
MODULE_LICENSE("GPL");