diff options
-rw-r--r-- | arch/arm/mach-tegra/board-kai.c | 19 | ||||
-rw-r--r-- | drivers/power/max17048_battery.c | 374 | ||||
-rw-r--r-- | include/linux/max17048_battery.h | 16 |
3 files changed, 303 insertions, 106 deletions
diff --git a/arch/arm/mach-tegra/board-kai.c b/arch/arm/mach-tegra/board-kai.c index 0a9cbd117d9b..45acedc363b0 100644 --- a/arch/arm/mach-tegra/board-kai.c +++ b/arch/arm/mach-tegra/board-kai.c @@ -248,16 +248,25 @@ static struct tegra_i2c_platform_data kai_i2c5_platform_data = { .arb_recovery = arb_lost_recovery, }; -struct max17048_platform_data max17048_pdata = { - .charger_online = smb349_charger_type, - .battery_online = smb349_battery_online, - .charging_status = smb349_charging_status, +struct max17048_battery_model max17048_mdata = { + .rcomp = 170, + .soccheck_A = 252, + .soccheck_B = 254, + .bits = 19, + .alert_threshold = 0x00, + .one_percent_alerts = 0x40, + .alert_on_reset = 0x40, + .rcomp_seg = 0x0800, + .hibernate = 0x3080, + .vreset = 0x9696, + .valert = 0xD4AA, + .ocvtest = 55600, }; static struct i2c_board_info kai_i2c4_max17048_board_info[] = { { I2C_BOARD_INFO("max17048", 0x36), - .platform_data = &max17048_pdata, + .platform_data = &max17048_mdata, }, }; diff --git a/drivers/power/max17048_battery.c b/drivers/power/max17048_battery.c index cf8ea0bfdd65..3f5fa04fa52e 100644 --- a/drivers/power/max17048_battery.c +++ b/drivers/power/max17048_battery.c @@ -23,20 +23,22 @@ #include <linux/slab.h> #include <linux/max17048_battery.h> -#define MAX17048_VCELL_MSB 0x02 -#define MAX17048_VCELL_LSB 0x03 -#define MAX17048_SOC_MSB 0x04 -#define MAX17048_SOC_LSB 0x05 -#define MAX17048_MODE_MSB 0x06 -#define MAX17048_MODE_LSB 0x07 -#define MAX17048_VER_MSB 0x08 -#define MAX17048_VER_LSB 0x09 -#define MAX17048_RCOMP_MSB 0x0C -#define MAX17048_RCOMP_LSB 0x0D -#define MAX17048_CMD_MSB 0xFE -#define MAX17048_CMD_LSB 0xFF -#define RESET_MSB 0x54 -#define RESET_LSB 0x00 +#define MAX17048_VCELL 0x02 +#define MAX17048_SOC 0x04 +#define MAX17048_VER 0x08 +#define MAX17048_HIBRT 0x0A +#define MAX17048_CONFIG 0x0C +#define MAX17048_OCV 0x0E +#define MAX17048_VLRT 0x14 +#define MAX17048_VRESET 0x18 +#define MAX17048_STATUS 0x1A +#define MAX17048_UNLOCK 0x3E +#define MAX17048_TABLE 0x40 +#define MAX17048_RCOMPSEG1 0x80 +#define MAX17048_RCOMPSEG2 0x90 +#define MAX17048_CMD 0xFF +#define MAX17048_UNLOCK_VALUE 0x4a57 +#define MAX17048_RESET_VALUE 0x5400 #define MAX17048_DELAY 1000 #define MAX17048_BATTERY_FULL 95 #define MAX17048_VERSION_NO 0x11 @@ -47,7 +49,7 @@ struct max17048_chip { struct power_supply battery; struct power_supply ac; struct power_supply usb; - struct max17048_platform_data *pdata; + struct max17048_battery_model *model_data; /* State Of Connect */ int ac_online; @@ -62,10 +64,43 @@ struct max17048_chip { int lasttime_vcell; int lasttime_soc; int lasttime_status; - int lasttime_ac_online; - int lasttime_usb_online; }; +uint8_t max17048_custom_data[] = { + 0xAA, 0x00, 0xB1, 0xF0, 0xB7, 0xE0, 0xB9, 0x60, 0xBB, 0x80, + 0xBC, 0x40, 0xBD, 0x30, 0xBD, 0x50, 0xBD, 0xF0, 0xBE, 0x40, + 0xBF, 0xD0, 0xC0, 0x90, 0xC4, 0x30, 0xC7, 0xC0, 0xCA, 0x60, + 0xCF, 0x30, 0x01, 0x20, 0x09, 0xC0, 0x1F, 0xC0, 0x2B, 0xE0, + 0x4F, 0xC0, 0x30, 0x00, 0x47, 0x80, 0x4F, 0xE0, 0x77, 0x00, + 0x15, 0x60, 0x46, 0x20, 0x13, 0x80, 0x1A, 0x60, 0x12, 0x20, + 0x14, 0xA0, 0x14, 0xA0}; + +static int max17048_write_word(struct i2c_client *client, int reg, u16 value) +{ + int ret; + + ret = i2c_smbus_write_word_data(client, reg, swab16(value)); + + if (ret < 0) + dev_err(&client->dev, "%s(): Failed in writing register" + "0x%02x err %d\n", __func__, reg, ret); + + return ret; +} + +static uint16_t max17048_read_word(struct i2c_client *client, int reg) +{ + uint16_t ret; + + ret = i2c_smbus_read_word_data(client, reg); + + if (ret < 0) + dev_err(&client->dev, "%s(): Failed in reading register" + "0x%02x err %d\n", __func__, reg, ret); + + return swab16(ret); +} + static int max17048_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -119,94 +154,33 @@ static int max17048_usb_get_property(struct power_supply *psy, return 0; } -static int max17048_write_reg(struct i2c_client *client, int reg, u8 value) -{ - int ret; - - ret = i2c_smbus_write_byte_data(client, reg, value); - - if (ret < 0) - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - - return ret; -} - -static int max17048_read_reg(struct i2c_client *client, int reg) -{ - int ret; - - ret = i2c_smbus_read_byte_data(client, reg); - - if (ret < 0) - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - - return ret; -} - -static void max17048_reset(struct i2c_client *client) -{ - max17048_write_reg(client, MAX17048_CMD_MSB, RESET_MSB); - max17048_write_reg(client, MAX17048_CMD_LSB, RESET_LSB); -} - static void max17048_get_vcell(struct i2c_client *client) { struct max17048_chip *chip = i2c_get_clientdata(client); - u8 msb; - u8 lsb; + uint16_t vcell; - msb = max17048_read_reg(client, MAX17048_VCELL_MSB); - lsb = max17048_read_reg(client, MAX17048_VCELL_LSB); + vcell = max17048_read_word(client, MAX17048_VCELL); + if (vcell < 0) + dev_err(&client->dev, "%s: err %d\n", __func__, vcell); - chip->vcell = (msb << 4) + (lsb >> 4); + chip->vcell = vcell; } static void max17048_get_soc(struct i2c_client *client) { struct max17048_chip *chip = i2c_get_clientdata(client); - u8 msb; - u8 lsb; + uint16_t soc; - msb = max17048_read_reg(client, MAX17048_SOC_MSB); + soc = max17048_read_word(client, MAX17048_SOC); + if (soc < 0) + dev_err(&client->dev, "%s: err %d\n", __func__, soc); - chip->soc = msb; + chip->soc = soc >> 9; } static uint16_t max17048_get_version(struct i2c_client *client) { - return swab16(i2c_smbus_read_word_data(client, MAX17048_VER_MSB)); -} - -static void max17048_get_online(struct i2c_client *client) -{ - struct max17048_chip *chip = i2c_get_clientdata(client); - - if (chip->pdata->charging_status && chip->pdata->charging_status()) { - if (chip->pdata->charger_online && - chip->pdata->charger_online()) { - chip->ac_online = 1; - chip->usb_online = 0; - } else { - chip->ac_online = 0; - chip->usb_online = 1; - } - } else { - chip->ac_online = 0; - chip->usb_online = 0; - } -} - -static void max17048_get_status(struct i2c_client *client) -{ - struct max17048_chip *chip = i2c_get_clientdata(client); - - if (chip->pdata->charging_status()) - chip->status = POWER_SUPPLY_STATUS_CHARGING; - else - chip->status = POWER_SUPPLY_STATUS_DISCHARGING; - - if (chip->soc > MAX17048_BATTERY_FULL) - chip->status = POWER_SUPPLY_STATUS_FULL; + return swab16(i2c_smbus_read_word_data(client, MAX17048_VER)); } static void max17048_work(struct work_struct *work) @@ -217,28 +191,45 @@ static void max17048_work(struct work_struct *work) max17048_get_vcell(chip->client); max17048_get_soc(chip->client); - max17048_get_online(chip->client); - max17048_get_status(chip->client); if (chip->vcell != chip->lasttime_vcell || chip->soc != chip->lasttime_soc || - chip->ac_online != chip->lasttime_ac_online || - chip->usb_online != chip->lasttime_usb_online || chip->status != chip->lasttime_status) { chip->lasttime_vcell = chip->vcell; chip->lasttime_soc = chip->soc; - chip->lasttime_status = chip->status; - chip->lasttime_ac_online = chip->ac_online; - chip->lasttime_usb_online = chip->usb_online; power_supply_changed(&chip->battery); - power_supply_changed(&chip->usb); - power_supply_changed(&chip->ac); } schedule_delayed_work(&chip->work, MAX17048_DELAY); } +static void max17048_battery_status(enum charging_states status, + enum charger_type chrg_type, void *data) +{ + struct max17048_chip *chip = data; + + chip->ac_online = 0; + chip->usb_online = 0; + + if (chrg_type == AC) + chip->ac_online = 1; + else if (chrg_type == USB) + chip->usb_online = 1; + + if (status == progress) + chip->status = POWER_SUPPLY_STATUS_CHARGING; + else + chip->status = POWER_SUPPLY_STATUS_DISCHARGING; + + if (chip->soc > MAX17048_BATTERY_FULL) + chip->status = POWER_SUPPLY_STATUS_FULL; + + power_supply_changed(&chip->battery); + power_supply_changed(&chip->usb); + power_supply_changed(&chip->ac); +} + static enum power_supply_property max17048_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -253,10 +244,182 @@ static enum power_supply_property max17048_usb_props[] = { POWER_SUPPLY_PROP_ONLINE, }; +static int max17048_write_rcomp_seg(struct i2c_client *client, + uint16_t rcomp_seg) +{ + uint8_t rs1, rs2; + int ret; + + rs2 = rcomp_seg | 0x00FF; + rs1 = rcomp_seg >> 8; + uint8_t rcomp_seg_table[16] = { rs1, rs2, rs1, rs2, + rs1, rs2, rs1, rs2, + rs1, rs2, rs1, rs2, + rs1, rs2, rs1, rs2}; + + ret = i2c_smbus_write_i2c_block_data(client, MAX17048_RCOMPSEG1, + 16, (uint8_t *)rcomp_seg_table); + if (ret < 0) { + dev_err(&client->dev, "%s: err %d\n", __func__, ret); + return ret; + } + + ret = i2c_smbus_write_i2c_block_data(client, MAX17048_RCOMPSEG2, + 16, (uint8_t *)rcomp_seg_table); + if (ret < 0) { + dev_err(&client->dev, "%s: err %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static int max17048_load_model_data(struct max17048_chip *chip) +{ + struct i2c_client *client = chip->client; + struct max17048_battery_model *mdata = chip->model_data; + uint16_t soc_tst, ocv; + int i, ret = 0; + + /* read OCV */ + ocv = max17048_read_word(client, MAX17048_OCV); + if (ocv == 0xffff) { + dev_err(&client->dev, "%s: Failed in unlocking" + "max17048 err: %d\n", __func__, ocv); + return -1; + } + + /* write custom model data */ + for (i = 0; i < 4; i += 1) { + if (i2c_smbus_write_i2c_block_data(client, + (MAX17048_TABLE+i*16), 16, + &max17048_custom_data[i*0x10]) < 0) { + dev_err(&client->dev, "%s: error writing model data:\n", + __func__); + return -1; + } + } + + /* Write OCV Test value */ + ret = max17048_write_word(client, MAX17048_OCV, mdata->ocvtest); + if (ret < 0) + return ret; + + ret = max17048_write_rcomp_seg(client, mdata->rcomp_seg); + if (ret < 0) + return ret; + + /* Disable hibernate */ + ret = max17048_write_word(client, MAX17048_HIBRT, 0x0000); + if (ret < 0) + return ret; + + /* Lock model access */ + ret = max17048_write_word(client, MAX17048_UNLOCK, 0x0000); + if (ret < 0) + return ret; + + /* Delay between 150ms to 600ms */ + mdelay(200); + + /* Read SOC Register and compare to expected result */ + soc_tst = max17048_read_word(client, MAX17048_SOC); + if (!((soc_tst >> 8) >= mdata->soccheck_A && + (soc_tst >> 8) <= mdata->soccheck_B)) { + dev_err(&client->dev, "%s: soc comparison failed %d\n", + __func__, ret); + return ret; + } else { + dev_info(&client->dev, "MAX17048 Custom data" + " loading successfull\n"); + } + + /* unlock model access */ + ret = max17048_write_word(client, MAX17048_UNLOCK, + MAX17048_UNLOCK_VALUE); + if (ret < 0) + return ret; + + /* Restore OCV */ + ret = max17048_write_word(client, MAX17048_OCV, ocv); + if (ret < 0) + return ret; + + return ret; +} + +static int max17048_initialize(struct max17048_chip *chip) +{ + uint8_t ret, config, status; + uint16_t wrt_status; + struct i2c_client *client = chip->client; + struct max17048_battery_model *mdata = chip->model_data; + + /* unlock model access */ + ret = max17048_write_word(client, MAX17048_UNLOCK, + MAX17048_UNLOCK_VALUE); + if (ret < 0) + return ret; + + /* load model data */ + ret = max17048_load_model_data(chip); + if (ret < 0) { + dev_err(&client->dev, "%s: err %d\n", __func__, ret); + return ret; + } + + if (mdata->bits == 19) + config = 32 - (mdata->alert_threshold * 2); + else if (mdata->bits == 18) + config = 32 - mdata->alert_threshold; + + config = mdata->one_percent_alerts | config; + + ret = max17048_write_word(client, MAX17048_CONFIG, + ((mdata->rcomp << 8) | config)); + if (ret < 0) + return ret; + + /* Voltage Alert configuration */ + ret = max17048_write_word(client, MAX17048_VLRT, mdata->valert); + if (ret < 0) + return ret; + + /* Hibernate configuration */ + ret = max17048_write_word(client, MAX17048_HIBRT, mdata->hibernate); + if (ret < 0) + return ret; + + ret = max17048_write_word(client, MAX17048_VRESET, mdata->vreset); + if (ret < 0) + return ret; + + /* clears the reset indicator */ + ret = max17048_read_word(client, MAX17048_STATUS); + if (ret < 0) + return ret; + + /* Sets the EnVR bit if selected */ + status = (ret & 0xFE) | mdata->alert_on_reset; + wrt_status = status << 8; + + ret = max17048_write_word(client, MAX17048_STATUS, wrt_status); + if (ret < 0) + return ret; + + /* Lock model access */ + ret = max17048_write_word(client, MAX17048_UNLOCK, 0x0000); + if (ret < 0) + return ret; + + /* Add delay */ + mdelay(200); + return 0; +} + static int __devinit max17048_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct max17048_chip *chip; int ret; uint16_t version; @@ -266,7 +429,7 @@ static int __devinit max17048_probe(struct i2c_client *client, return -ENOMEM; chip->client = client; - chip->pdata = client->dev.platform_data; + chip->model_data = client->dev.platform_data; chip->ac_online = 0; chip->usb_online = 0; @@ -279,6 +442,16 @@ static int __devinit max17048_probe(struct i2c_client *client, } dev_info(&client->dev, "MAX17048 Fuel-Gauge Ver 0x%x\n", version); + ret = max17048_initialize(chip); + if (ret < 0) { + dev_err(&client->dev, "Error: Initializing fuel-gauge\n"); + goto error2; + } + + ret = register_callback(max17048_battery_status, chip); + if (ret < 0) + goto error2; + chip->battery.name = "battery"; chip->battery.type = POWER_SUPPLY_TYPE_BATTERY; chip->battery.get_property = max17048_get_property; @@ -314,7 +487,6 @@ static int __devinit max17048_probe(struct i2c_client *client, dev_err(&client->dev, "failed: power supply register\n"); goto error; } - max17048_reset(client); INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17048_work); schedule_delayed_work(&chip->work, MAX17048_DELAY); diff --git a/include/linux/max17048_battery.h b/include/linux/max17048_battery.h index f557b317be69..d997082fb5a9 100644 --- a/include/linux/max17048_battery.h +++ b/include/linux/max17048_battery.h @@ -10,6 +10,22 @@ #ifndef __MAX17048_BATTERY_H_ #define __MAX17048_BATTERY_H_ +#include <linux/smb349-charger.h> + +struct max17048_battery_model { + uint8_t rcomp; + uint8_t soccheck_A; + uint8_t soccheck_B; + uint8_t bits; + uint8_t alert_threshold; + uint8_t one_percent_alerts; + uint8_t alert_on_reset; + uint16_t rcomp_seg; + uint16_t hibernate; + uint16_t vreset; + uint16_t valert; + uint16_t ocvtest; +}; struct max17048_platform_data { int (*battery_online)(void); |