summaryrefslogtreecommitdiff
path: root/drivers/power
diff options
context:
space:
mode:
authorSyed Rafiuddin <srafiuddin@nvidia.com>2012-02-28 23:03:09 +0530
committerSimone Willett <swillett@nvidia.com>2012-03-02 17:52:16 -0800
commit96fe8d635926a4b02c7ebe1368c9e70f94816f43 (patch)
tree795d24db06f76198e9ad17c2039b630e27ffa2a3 /drivers/power
parent1de7d65987afa175e552e300a2ef435bada02f08 (diff)
power: max17048: Add battery custom model data
- Addition of battery custom data to max17048 driver - Update battery properties like online and charging/discharging status instantaneously based on the interrupt. Change-Id: I84f4833caf4c25fb4d73c74c9e986084bb33a94a Signed-off-by: Syed Rafiuddin <srafiuddin@nvidia.com> Reviewed-on: http://git-master/r/83505 Reviewed-by: Simone Willett <swillett@nvidia.com> Tested-by: Simone Willett <swillett@nvidia.com>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/max17048_battery.c374
1 files changed, 273 insertions, 101 deletions
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);