diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/power/max17042_fuelgauge_px.c | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2 kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip |
samsung update 1
Diffstat (limited to 'drivers/power/max17042_fuelgauge_px.c')
-rw-r--r-- | drivers/power/max17042_fuelgauge_px.c | 1622 |
1 files changed, 1622 insertions, 0 deletions
diff --git a/drivers/power/max17042_fuelgauge_px.c b/drivers/power/max17042_fuelgauge_px.c new file mode 100644 index 00000000000..6244f2be6b8 --- /dev/null +++ b/drivers/power/max17042_fuelgauge_px.c @@ -0,0 +1,1622 @@ +/* + * max17042_battery.c + * fuel-gauge systems for lithium-ion (Li+) batteries + * + * Copyright (C) 2010 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/types.h> +#include <linux/irq.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/firmware.h> +#include <linux/wakelock.h> +#include <linux/blkdev.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/rtc.h> +#include <mach/gpio.h> +#include <linux/power/sec_battery_px.h> +#include <linux/power/max17042_fuelgauge_px.h> + +#define PRINT_COUNT 1 + +static struct i2c_client *fg_i2c_client; +struct max17042_chip *max17042_chip_data; + +static int fg_get_battery_type(void) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + + return chip->info.battery_type; +} + +static int fg_i2c_read(struct i2c_client *client, u8 reg, u8 *data, u8 length) +{ + s32 value; + + value = i2c_smbus_read_i2c_block_data(client, reg, length, data); + if (value < 0 || value != length) { + pr_err("%s: Failed to fg_i2c_read. status = %d\n", + __func__, value); + return -1; + } + + return 0; +} + +static int fg_i2c_write(struct i2c_client *client, u8 reg, u8 *data, u8 length) +{ + s32 value; + + value = i2c_smbus_write_i2c_block_data(client, reg, length, data); + if (value < 0) { + pr_err("%s: Failed to fg_i2c_write, error code=%d\n", + __func__, value); + return -1; + } + + return 0; +} + +static int fg_read_register(u8 addr) +{ + struct i2c_client *client = fg_i2c_client; + u8 data[2]; + + if (fg_i2c_read(client, addr, data, 2) < 0) { + pr_err("%s: Failed to read addr(0x%x)\n", __func__, addr); + return -1; + } + + return (data[1] << 8) | data[0]; +} + +static int fg_write_register(u8 addr, u16 w_data) +{ + struct i2c_client *client = fg_i2c_client; + u8 data[2]; + + data[0] = w_data & 0xFF; + data[1] = w_data >> 8; + + if (fg_i2c_write(client, addr, data, 2) < 0) { + pr_err("%s: Failed to write addr(0x%x)\n", __func__, addr); + return -1; + } + + return 0; +} + +static int fg_read_16register(u8 addr, u16 *r_data) +{ + struct i2c_client *client = fg_i2c_client; + u8 data[32]; + int i = 0; + + if (fg_i2c_read(client, addr, data, 32) < 0) { + pr_err("%s: Failed to read addr(0x%x)\n", __func__, addr); + return -1; + } + + for (i = 0; i < 16; i++) + r_data[i] = (data[2 * i + 1] << 8) | data[2 * i]; + + return 0; +} + +void fg_write_and_verify_register(u8 addr, u16 w_data) +{ + u16 r_data; + u8 retry_cnt = 2; + + while (retry_cnt) { + fg_write_register(addr, w_data); + r_data = fg_read_register(addr); + + if (r_data != w_data) { + pr_err("%s: verification failed (addr : 0x%x, w_data : 0x%x, r_data : 0x%x)\n", + __func__, addr, w_data, r_data); + retry_cnt--; + } else + break; + } +} + +static void fg_test_print(void) +{ + struct i2c_client *client = fg_i2c_client; + u8 data[2]; + u32 average_vcell; + u16 w_data; + u32 temp; + u32 temp2; + u16 fullcap, remcap, mixcap, avcap; + + if (fg_i2c_read(client, AVR_VCELL_REG, data, 2) < 0) { + pr_err("%s: Failed to read VCELL\n", __func__); + return; + } + + w_data = (data[1]<<8) | data[0]; + + temp = (w_data & 0xFFF) * 78125; + average_vcell = temp / 1000000; + + temp = ((w_data & 0xF000) >> 4) * 78125; + temp2 = temp / 1000000; + average_vcell += (temp2 << 4); + + fullcap = fg_read_register(FULLCAP_REG); + remcap = fg_read_register(REMCAP_REP_REG); + mixcap = fg_read_register(REMCAP_MIX_REG); + avcap = fg_read_register(REMCAP_AV_REG); + + pr_info("avg_vcell(%d), fullcap(%d), remcap(%d), mixcap(%d), avcap(%d)\n", + average_vcell, fullcap/2, remcap/2, mixcap/2, avcap/2); + msleep(20); +} + +static int fg_read_voltage_now(void) +{ + struct i2c_client *client = fg_i2c_client; + u8 data[2]; + u32 voltage_now; + u16 w_data; + u32 temp; + u32 temp2; + + if (fg_i2c_read(client, VCELL_REG, data, 2) < 0) { + pr_err("%s: Failed to read VCELL\n", __func__); + return -1; + } + + w_data = (data[1]<<8) | data[0]; + + temp = (w_data & 0xFFF) * 78125; + voltage_now = temp / 1000; + + temp = ((w_data & 0xF000) >> 4) * 78125; + temp2 = temp / 1000; + voltage_now += (temp2 << 4); + + return voltage_now; +} + +static int fg_read_vcell(void) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + u8 data[2]; + u32 vcell; + u16 w_data; + u32 temp; + u32 temp2; + + if (fg_i2c_read(client, VCELL_REG, data, 2) < 0) { + pr_err("%s: Failed to read VCELL\n", __func__); + return -1; + } + + w_data = (data[1]<<8) | data[0]; + + temp = (w_data & 0xFFF) * 78125; + vcell = temp / 1000000; + + temp = ((w_data & 0xF000) >> 4) * 78125; + temp2 = temp / 1000000; + vcell += (temp2 << 4); + + if (!(chip->info.pr_cnt % PRINT_COUNT)) + pr_info("%s: VCELL(%d), data(0x%04x)\n", + __func__, vcell, (data[1]<<8) | data[0]); + + return vcell; +} + +static int fg_read_avg_vcell(void) +{ + struct i2c_client *client = fg_i2c_client; + u8 data[2]; + u32 avg_vcell; + u16 w_data; + u32 temp; + u32 temp2; + + if (fg_i2c_read(client, AVR_VCELL_REG, data, 2) < 0) { + pr_err("%s: Failed to read VCELL\n", __func__); + return -1; + } + + w_data = (data[1]<<8) | data[0]; + + temp = (w_data & 0xFFF) * 78125; + avg_vcell = temp / 1000000; + + temp = ((w_data & 0xF000) >> 4) * 78125; + temp2 = temp / 1000000; + avg_vcell += (temp2 << 4); + + return avg_vcell; +} + +static int fg_read_vfocv(void) +{ + struct i2c_client *client = fg_i2c_client; + u8 data[2]; + u32 vfocv = 0; + u16 w_data; + u32 temp; + u32 temp2; + + if (fg_i2c_read(client, VFOCV_REG, data, 2) < 0) { + pr_err("%s: Failed to read VFOCV\n", __func__); + return -1; + } + + w_data = (data[1]<<8) | data[0]; + + temp = (w_data & 0xFFF) * 78125; + vfocv = temp / 1000000; + + temp = ((w_data & 0xF000) >> 4) * 78125; + temp2 = temp / 1000000; + vfocv += (temp2 << 4); + + return vfocv; +} + +static int fg_check_battery_present(void) +{ + struct i2c_client *client = fg_i2c_client; + u8 status_data[2]; + int ret = 1; + + /* 1. Check Bst bit */ + if (fg_i2c_read(client, STATUS_REG, status_data, 2) < 0) { + pr_err("%s: Failed to read STATUS_REG\n", __func__); + return 0; + } + + if (status_data[0] & (0x1 << 3)) { + pr_info("%s - addr(0x01), data(0x%04x)\n", __func__, + (status_data[1]<<8) | status_data[0]); + pr_info("%s: battery is absent!!\n", __func__); + ret = 0; + } + + return ret; +} + + +static int fg_read_temp(void) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + u8 data[2] = { 0, }; + int temper = 0; + + if (fg_check_battery_present()) { + if (fg_i2c_read(client, TEMPERATURE_REG, data, 2) < 0) { + pr_err("%s: Failed to read TEMPERATURE_REG\n", + __func__); + return -1; + } + + if (data[1]&(0x1 << 7)) { + temper = ((~(data[1]))&0xFF)+1; + temper *= (-1000); + } else { + temper = data[1] & 0x7f; + temper *= 1000; + temper += data[0] * 39 / 10; + + /* Adjust temperature over 47, only for SDI battery */ + if (fg_get_battery_type() == SDI_BATTERY_TYPE) { + if (temper >= 47000 && temper < 60000) { + temper = temper * SDI_TRIM1_1 / 100; + temper -= SDI_TRIM1_2; + } else if (temper >= 60000) { + temper = temper * SDI_TRIM2_1 / 100; + temper -= 51000; + } + } + } + } else + temper = 20000; + + if (!(chip->info.pr_cnt % PRINT_COUNT)) + pr_info("%s: TEMPERATURE(%d), data(0x%04x)\n", __func__, + temper, (data[1]<<8) | data[0]); + + return temper; +} + +static int fg_read_soc(void) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + u8 data[2]; + u32 soc = 0; + + if (fg_i2c_read(client, SOCREP_REG, data, 2) < 0) { + pr_err("%s: Failed to read SOCREP\n", __func__); + return -1; + } + + soc = data[1]; + + if (!(chip->info.pr_cnt % PRINT_COUNT)) + pr_info("%s: SOC(%d), data(0x%04x)\n", __func__, + soc, (data[1]<<8) | data[0]); + + return soc; +} + +static int fg_read_vfsoc(void) +{ + struct i2c_client *client = fg_i2c_client; + u8 data[2]; + + if (fg_i2c_read(client, VFSOC_REG, data, 2) < 0) { + pr_err("%s: Failed to read VFSOC\n", __func__); + return -1; + } + + return (int)data[1]; +} + +static int fg_read_current(void) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + u8 data1[2], data2[2]; + u32 temp, sign; + s32 i_current; + s32 avg_current; + + if (fg_i2c_read(client, CURRENT_REG, data1, 2) < 0) { + pr_err("%s: Failed to read CURRENT\n", __func__); + return -1; + } + + if (fg_i2c_read(client, AVG_CURRENT_REG, data2, 2) < 0) { + pr_err("%s: Failed to read AVERAGE CURRENT\n", __func__); + return -1; + } + + temp = ((data1[1]<<8) | data1[0]) & 0xFFFF; + if (temp & (0x1 << 15)) { + sign = NEGATIVE; + temp = (~temp & 0xFFFF) + 1; + } else + sign = POSITIVE; + + i_current = temp * MAX17042_CURRENT_UNIT; + if (sign) + i_current *= -1; + + temp = ((data2[1]<<8) | data2[0]) & 0xFFFF; + if (temp & (0x1 << 15)) { + sign = NEGATIVE; + temp = (~temp & 0xFFFF) + 1; + } else + sign = POSITIVE; + + avg_current = temp * MAX17042_CURRENT_UNIT; + if (sign) + avg_current *= -1; + + if (!(chip->info.pr_cnt++ % PRINT_COUNT)) { + fg_test_print(); + pr_info("%s: CURRENT(%dmA), AVG_CURRENT(%dmA)\n", __func__, + i_current, avg_current); + chip->info.pr_cnt = 1; + /* Read max17042's all registers every 5 minute. */ + fg_periodic_read(); + } + + return i_current; +} + +static int fg_read_avg_current(void) +{ + struct i2c_client *client = fg_i2c_client; + u8 data2[2]; + u32 temp, sign; + s32 avg_current; + + if (fg_i2c_read(client, AVG_CURRENT_REG, data2, 2) < 0) { + pr_err("%s: Failed to read AVERAGE CURRENT\n", __func__); + return -1; + } + + temp = ((data2[1]<<8) | data2[0]) & 0xFFFF; + if (temp & (0x1 << 15)) { + sign = NEGATIVE; + temp = (~temp & 0xFFFF) + 1; + } else + sign = POSITIVE; + + avg_current = temp * MAX17042_CURRENT_UNIT; + + if (sign) + avg_current *= -1; + + return avg_current; +} + +int fg_reset_soc(void) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + u8 data[2]; +#if defined(CONFIG_MACH_P8) || defined(CONFIG_MACH_P8LTE) + u32 fullcap; + int vfocv = 0; +#endif + + /* delay for current stablization */ + msleep(500); + + pr_info("%s: %s - VCELL(%d), VFOCV(%d), VfSOC(%d), RepSOC(%d)\n", + __func__, "Before quick-start", fg_read_vcell(), + fg_read_vfocv(), fg_read_vfsoc(), fg_read_soc()); + pr_info("%s: Before quick-start - current(%d), avg current(%d)\n", + __func__, fg_read_current(), + fg_read_avg_current()); + + if (!chip->pdata->check_jig_status()) { + pr_info("%s: Return by No JIG_ON signal\n", __func__); + return 0; + } + + fg_write_register(CYCLES_REG, 0); + + if (fg_i2c_read(client, MISCCFG_REG, data, 2) < 0) { + pr_err("%s: Failed to read MiscCFG\n", __func__); + return -1; + } + + data[1] |= (0x1 << 2); + if (fg_i2c_write(client, MISCCFG_REG, data, 2) < 0) { + pr_err("%s: Failed to write MiscCFG\n", __func__); + return -1; + } + + msleep(250); + fg_write_register(FULLCAP_REG, chip->info.capacity); + msleep(500); + + pr_info("%s: %s - VCELL(%d), VFOCV(%d), VfSOC(%d), RepSOC(%d)\n", + __func__, "After quick-start", fg_read_vcell(), + fg_read_vfocv(), fg_read_vfsoc(), fg_read_soc()); + pr_info("%s: After quick-start - current(%d), avg current(%d)\n", + __func__, fg_read_current(), fg_read_avg_current()); + fg_write_register(CYCLES_REG, 0x00a0); + +/* P8 is not turned off by Quickstart @3.4V(It's not a problem, depend on + * mode data. Power off for factory test(File system, etc..) */ +#if defined(CONFIG_MACH_P8) || defined(CONFIG_MACH_P8LTE) +#define QUICKSTART_POWER_OFF_VOLTAGE 3400 + vfocv = fg_read_vfocv(); + if (vfocv < QUICKSTART_POWER_OFF_VOLTAGE) { + pr_info("%s: Power off condition(%d)\n", __func__, vfocv); + + fullcap = fg_read_register(FULLCAP_REG); + /* FullCAP * 0.009 */ + fg_write_register(REMCAP_REP_REG, (u16)(fullcap * 9 / 1000)); + msleep(200); + pr_info("%s: new soc=%d, vfocv=%d\n", __func__, + fg_read_soc(), vfocv); + } + + pr_info("%s: Additional step - VfOCV(%d), VfSOC(%d), RepSOC(%d)\n", + __func__, fg_read_vfocv(), fg_read_vfsoc(), fg_read_soc()); +#endif + + return 0; +} + + +int fg_reset_capacity(void) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + + return fg_write_register(DESIGNCAP_REG, chip->info.vfcapacity-1); +} + +int fg_adjust_capacity(void) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + u8 data[2]; + + data[0] = 0; + data[1] = 0; + + /* 1. Write RemCapREP(05h)=0; */ + if (fg_i2c_write(client, REMCAP_REP_REG, data, 2) < 0) { + pr_err("%s: Failed to write RemCap_REP\n", __func__); + return -1; + } + msleep(200); + + pr_info("%s: After adjust - RepSOC(%d)\n", __func__, fg_read_soc()); + + chip->info.soc_restart_flag = 1; + + return 0; +} + +void fg_low_batt_compensation(u32 level) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + int read_val; + u32 temp; + + pr_info("%s: Adjust SOCrep to %d!!\n", __func__, level); + + read_val = fg_read_register(FULLCAP_REG); + if (read_val < 0) + return; + + /* RemCapREP (05h) = FullCap(10h) x 0.0304 or 0.0104 */ + temp = read_val * (level*100 + 4) / 10000; + fg_write_register(REMCAP_REP_REG, (u16)temp); + + /* (0x67) * 0.0039 = 0.4%*/ + temp = 0; + temp = (u16)((level << 8) | 0x67); + fg_write_register(SOCREP_REG, temp); + + chip->info.low_batt_comp_flag = 1; +} + +void fg_periodic_read(void) +{ + int i; + u16 data[0x10]; + struct timespec ts; + struct rtc_time tm; + + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + + pr_info("[MAX17042] %d/%d/%d %02d:%02d,", + tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900, tm.tm_hour, + tm.tm_min); + + for (i = 0; i < 16; i++) { + fg_read_16register(i, data); + + pr_info("%04xh,%04xh,%04xh,%04xh,%04xh,%04xh,%04xh,%04xh,", + data[0x00], data[0x01], data[0x02], data[0x03], + data[0x04], data[0x05], data[0x06], data[0x07]); + pr_info("%04xh,%04xh,%04xh,%04xh,%04xh,%04xh,%04xh,%04xh,", + data[0x08], data[0x09], data[0x0a], data[0x0b], + data[0x0c], data[0x0d], data[0x0e], data[0x0f]); + if (i == 4) + i = 13; + + msleep(20); + } + pr_info("\n"); +} + +static void fg_read_model_data(void) +{ + u16 data0[16], data1[16], data2[16]; + int i; + int relock_check; + + pr_info("[FG_Model] "); + + /* Unlock model access */ + fg_write_register(0x62, 0x0059); + fg_write_register(0x63, 0x00C4); + + /* Read model data */ + fg_read_16register(0x80, data0); + fg_read_16register(0x90, data1); + fg_read_16register(0xa0, data2); + + /* Print model data */ + for (i = 0; i < 16; i++) + pr_info("0x%04x, ", data0[i]); + + for (i = 0; i < 16; i++) + pr_info("0x%04x, ", data1[i]); + + for (i = 0; i < 16; i++) { + if (i == 15) + pr_info("0x%04x", data2[i]); + else + pr_info("0x%04x, ", data2[i]); + } + + do { + relock_check = 0; + /* Lock model access */ + fg_write_register(0x62, 0x0000); + fg_write_register(0x63, 0x0000); + + /* Read model data again */ + fg_read_16register(0x80, data0); + fg_read_16register(0x90, data1); + fg_read_16register(0xa0, data2); + + for (i = 0; i < 16; i++) { + if (data0[i] || data1[i] || data2[i]) { + pr_info("%s: data is non-zero, lock again!\n", + __func__); + relock_check = 1; + } + } + } while (relock_check); + +} + +int fg_alert_init(void) +{ + struct i2c_client *client = fg_i2c_client; + u8 misccgf_data[2]; + u8 salrt_data[2]; + u8 config_data[2]; + u8 valrt_data[2]; + u8 talrt_data[2]; + u16 read_data = 0; + + /* Using RepSOC */ + if (fg_i2c_read(client, MISCCFG_REG, misccgf_data, 2) < 0) { + pr_err("%s: Failed to read MISCCFG_REG\n", __func__); + return -1; + } + misccgf_data[0] = misccgf_data[0] & ~(0x03); + + if (fg_i2c_write(client, MISCCFG_REG, misccgf_data, 2) < 0) { + pr_info("%s: Failed to write MISCCFG_REG\n", __func__); + return -1; + } + + /* SALRT Threshold setting */ + salrt_data[1] = 0xff; + salrt_data[0] = 0x01; + if (fg_i2c_write(client, SALRT_THRESHOLD_REG, salrt_data, 2) < 0) { + pr_info("%s: Failed to write SALRT_THRESHOLD_REG\n", __func__); + return -1; + } + + /* Reset VALRT Threshold setting (disable) */ + valrt_data[1] = 0xFF; + valrt_data[0] = 0x00; + if (fg_i2c_write(client, VALRT_THRESHOLD_REG, valrt_data, 2) < 0) { + pr_info("%s: Failed to write VALRT_THRESHOLD_REG\n", __func__); + return -1; + } + + read_data = fg_read_register((u8)VALRT_THRESHOLD_REG); + if (read_data != 0xff00) + pr_err("%s: VALRT_THRESHOLD_REG is not valid (0x%x)\n", + __func__, read_data); + + /* Reset TALRT Threshold setting (disable) */ + talrt_data[1] = 0x7F; + talrt_data[0] = 0x80; + if (fg_i2c_write(client, TALRT_THRESHOLD_REG, talrt_data, 2) < 0) { + pr_info("%s: Failed to write TALRT_THRESHOLD_REG\n", __func__); + return -1; + } + + read_data = fg_read_register((u8)TALRT_THRESHOLD_REG); + if (read_data != 0x7f80) + pr_err("%s: TALRT_THRESHOLD_REG is not valid (0x%x)\n", + __func__, read_data); + + mdelay(100); + + /* Enable SOC alerts */ + if (fg_i2c_read(client, CONFIG_REG, config_data, 2) < 0) { + pr_err("%s: Failed to read CONFIG_REG\n", __func__); + return -1; + } + config_data[0] = config_data[0] | (0x1 << 2); + + if (fg_i2c_write(client, CONFIG_REG, config_data, 2) < 0) { + pr_info("%s: Failed to write CONFIG_REG\n", __func__); + return -1; + } + + return 1; +} + +static int fg_check_status_reg(void) +{ + struct i2c_client *client = fg_i2c_client; + u8 status_data[2]; + int ret = 0; + + /* 1. Check Smn was generatedread */ + if (fg_i2c_read(client, STATUS_REG, status_data, 2) < 0) { + pr_err("%s: Failed to read STATUS_REG\n", __func__); + return -1; + } + pr_info("%s - addr(0x00), data(0x%04x)\n", __func__, + (status_data[1]<<8) | status_data[0]); + + if (status_data[1] & (0x1 << 2)) + ret = 1; + + /* 2. clear Status reg */ + status_data[1] = 0; + if (fg_i2c_write(client, STATUS_REG, status_data, 2) < 0) { + pr_info("%s: Failed to write STATUS_REG\n", __func__); + return -1; + } + + return ret; +} + +void fg_fullcharged_compensation(u32 is_recharging, u32 pre_update) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + static int new_fcap; + pr_info("%s: is_recharging(%d), pre_update(%d)\n", __func__, + is_recharging, pre_update); + + new_fcap = fg_read_register(FULLCAP_REG); + if (new_fcap < 0) + new_fcap = chip->info.capacity; + + pr_info("%s: prevFCap(0x%04x), newFcap(0x%04x)\n", __func__, + chip->info.prev_fullcap, new_fcap); + if (new_fcap > (chip->info.capacity * 110 / 100)) { + new_fcap = (chip->info.capacity * 110) / 100; + pr_info("%s: init FCap +10%%: newFcap(0x%04x)\n", + __func__, new_fcap); + fg_write_register(REMCAP_REP_REG, (u16)(new_fcap)); + fg_write_register(FULLCAP_REG, (u16)(new_fcap)); + } else if (new_fcap < (chip->info.capacity * 50 / 100)) { + new_fcap = (chip->info.capacity * 50) / 100; + pr_info("%s: init FCap -50%%: newFcap(0x%04x)\n", + __func__, new_fcap); + fg_write_register(REMCAP_REP_REG, (u16)(new_fcap)); + fg_write_register(FULLCAP_REG, (u16)(new_fcap)); + } else { + if (new_fcap > (chip->info.prev_fullcap * 110 / 100)) { + new_fcap = (chip->info.prev_fullcap * 110) / 100; + pr_info("%s: prev FCap +10%%: newFcap(0x%04x)\n", + __func__, new_fcap); + fg_write_register(REMCAP_REP_REG, (u16)(new_fcap)); + fg_write_register(FULLCAP_REG, (u16)(new_fcap)); + } else if (new_fcap < (chip->info.prev_fullcap * 90 / 100)) { + new_fcap = (chip->info.prev_fullcap * 90) / 100; + pr_info("%s: prev FCap -10%%: newFcap(0x%04x)\n", + __func__, new_fcap); + fg_write_register(REMCAP_REP_REG, (u16)(new_fcap)); + fg_write_register(FULLCAP_REG, (u16)(new_fcap)); + } else { + pr_info("%s: keep FCap: newFcap(0x%04x)\n", + __func__, new_fcap); + } + } + + /* 4. Write RepSOC(06h)=100%; */ + fg_write_register(SOCREP_REG, (u16)(0x64 << 8)); + + /* 5. Write MixSOC(0Dh)=100%; */ + fg_write_register(SOCMIX_REG, (u16)(0x64 << 8)); + + /* 6. Write AVSOC(0Eh)=100%; */ + fg_write_register(SOCAV_REG, (u16)(0x64 << 8)); + + /* if pre_update case, skip updating PrevFullCAP value. */ + if (!pre_update) + chip->info.prev_fullcap = fg_read_register(FULLCAP_REG); + + pr_info("%s: FullCap(0x%04x), RemCap(0x%04x)\n", __func__, + fg_read_register(FULLCAP_REG), + fg_read_register(REMCAP_REP_REG)); + + fg_periodic_read(); +} + +void fg_check_vf_fullcap_range(void) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + static int new_vffcap; + u16 print_flag = 1; + pr_debug("%s\n", __func__); + + new_vffcap = fg_read_register(FULLCAP_NOM_REG); + if (new_vffcap < 0) + new_vffcap = chip->info.vfcapacity; + + pr_info("%s: prevVFFCap(0x%04x), newVFFcap(0x%04x)\n", __func__, + chip->info.prev_vffcap, new_vffcap); + if (new_vffcap > (chip->info.vfcapacity * 110 / 100)) { + new_vffcap = (chip->info.vfcapacity * 110) / 100; + pr_info("%s: init VFFCap +10%%: newVFFcap(0x%04x)\n", + __func__, new_vffcap); + fg_write_register(DQACC_REG, (u16)(new_vffcap / 4)); + fg_write_register(DPACC_REG, (u16)0x3200); + } else if (new_vffcap < (chip->info.vfcapacity * 50 / 100)) { + new_vffcap = (chip->info.vfcapacity * 50) / 100; + pr_info("%s: init VFFCap -50%%: newVFFcap(0x%04x)\n", + __func__, new_vffcap); + fg_write_register(DQACC_REG, (u16)(new_vffcap / 4)); + fg_write_register(DPACC_REG, (u16)0x3200); + } else { + if (new_vffcap > (chip->info.prev_vffcap * 105 / 100)) { + new_vffcap = (chip->info.prev_vffcap * 105) / 100; + pr_info("%s: prev VFFCap +10%%: newVFFcap(0x%04x)\n", + __func__, new_vffcap); + fg_write_register(DQACC_REG, (u16)(new_vffcap / 4)); + fg_write_register(DPACC_REG, (u16)0x3200); + } else if (new_vffcap < (chip->info.prev_vffcap * 90 / 100)) { + new_vffcap = (chip->info.prev_vffcap * 90) / 100; + pr_info("%s: prev VFFCap -10%%: newVFFcap(0x%04x)\n", + __func__, new_vffcap); + fg_write_register(DQACC_REG, (u16)(new_vffcap / 4)); + fg_write_register(DPACC_REG, (u16)0x3200); + } else { + pr_info("%s: keep VFFCap: newVFFcap(0x%04x)\n", + __func__, new_vffcap); + print_flag = 0; + } + } + + /* delay for register setting (dQacc, dPacc) */ + if (print_flag) + msleep(300); + + chip->info.prev_vffcap = fg_read_register(FULLCAP_NOM_REG); + + if (print_flag) + pr_info("%s: VfFullCap(0x%04x), dQacc(0x%04x), dPacc(0x%04x)\n", + __func__, + fg_read_register(FULLCAP_NOM_REG), + fg_read_register(DQACC_REG), + fg_read_register(DPACC_REG)); +} + +int fg_check_cap_corruption(void) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + int vfsoc = fg_read_vfsoc(); + int repsoc = fg_read_soc(); + int mixcap = fg_read_register(REMCAP_MIX_REG); + int vfocv = fg_read_register(VFOCV_REG); + int remcap = fg_read_register(REMCAP_REP_REG); + int fullcapacity = fg_read_register(FULLCAP_REG); + int vffullcapacity = fg_read_register(FULLCAP_NOM_REG); + u32 temp, temp2, new_vfocv, pr_vfocv; + int designcap; + int ret = 0; + + /* If usgin Jig or low batt compensation flag is set, + then skip checking. */ + if (chip->pdata->check_jig_status()) { + fg_write_register(DESIGNCAP_REG, chip->info.vfcapacity - 1); + designcap = fg_read_register(DESIGNCAP_REG); + pr_info("%s: return by jig, vfcap(0x%04x), designcap(0x%04x)\n", + __func__, chip->info.vfcapacity, designcap); + return 0; + } + + if (vfsoc < 0 || repsoc < 0 || mixcap < 0 || vfocv < 0 || + remcap < 0 || fullcapacity < 0 || vffullcapacity < 0) + return 0; + + /* Check full charge learning case. */ + if (((vfsoc >= 70) + && ((remcap >= (fullcapacity * 995 / 1000)) + && (remcap <= (fullcapacity * 1005 / 1000)))) + || chip->info.low_batt_comp_flag + || chip->info.soc_restart_flag) { + + chip->info.prev_repsoc = repsoc; + chip->info.prev_remcap = remcap; + chip->info.prev_fullcapacity = fullcapacity; + if (chip->info.soc_restart_flag) + chip->info.soc_restart_flag = 0; + + ret = 1; + } + + /* ocv calculation for print */ + temp = (vfocv & 0xFFF) * 78125; + pr_vfocv = temp / 1000000; + + temp = ((vfocv & 0xF000) >> 4) * 78125; + temp2 = temp / 1000000; + pr_vfocv += (temp2 << 4); + + /* MixCap differ is greater than 265mAh */ + if ((((vfsoc+5) < chip->info.prev_vfsoc) + || (vfsoc > (chip->info.prev_vfsoc+5))) + || (((repsoc+5) < chip->info.prev_repsoc) + || (repsoc > (chip->info.prev_repsoc+5))) + || (((mixcap+530) < chip->info.prev_mixcap) + || (mixcap > (chip->info.prev_mixcap+530)))) { + fg_periodic_read(); + + pr_info("[FG_Recovery] (B) VfSOC(%d), prevVfSOC(%d),", + vfsoc, chip->info.prev_vfsoc); + pr_info(" RepSOC(%d), prevRepSOC(%d), MixCap(%d),", + repsoc, chip->info.prev_repsoc, (mixcap/2)); + pr_info(" prevMixCap(%d),VfOCV(0x%04x, %d)\n", + (chip->info.prev_mixcap/2), vfocv, pr_vfocv); + + mutex_lock(&chip->fg_lock); + + fg_write_and_verify_register(REMCAP_MIX_REG, + chip->info.prev_mixcap); + fg_write_register(VFOCV_REG, chip->info.prev_vfocv); + mdelay(200); + + fg_write_and_verify_register(REMCAP_REP_REG, + chip->info.prev_remcap); + vfsoc = fg_read_register(VFSOC_REG); + fg_write_register(0x60, 0x0080); + fg_write_and_verify_register(0x48, vfsoc); + fg_write_register(0x60, 0x0000); + + fg_write_and_verify_register(0x45, + (chip->info.prev_vfcapacity / 4)); + fg_write_and_verify_register(0x46, 0x3200); + fg_write_and_verify_register(FULLCAP_REG, + chip->info.prev_fullcapacity); + fg_write_and_verify_register(FULLCAP_NOM_REG, + chip->info.prev_vfcapacity); + + mutex_unlock(&chip->fg_lock); + + msleep(200); + + /* ocv calculation for print */ + new_vfocv = fg_read_register(VFOCV_REG); + temp = (new_vfocv & 0xFFF) * 78125; + pr_vfocv = temp / 1000000; + + temp = ((new_vfocv & 0xF000) >> 4) * 78125; + temp2 = temp / 1000000; + pr_vfocv += (temp2 << 4); + + pr_info("[FG_Recovery] (A) newVfSOC(%d), newRepSOC(%d),", + fg_read_vfsoc(), + fg_read_soc()); + pr_info(" newMixCap(%d), newVfOCV(0x%04x, %d)\n", + (fg_read_register(REMCAP_MIX_REG)/2), + new_vfocv, + pr_vfocv); + + fg_periodic_read(); + + ret = 1; + } else { + chip->info.prev_vfsoc = vfsoc; + chip->info.prev_repsoc = repsoc; + chip->info.prev_remcap = remcap; + chip->info.prev_mixcap = mixcap; + chip->info.prev_fullcapacity = fullcapacity; + chip->info.prev_vfcapacity = vffullcapacity; + chip->info.prev_vfocv = vfocv; + } + + return ret; +} + +void fg_set_full_charged(void) +{ + pr_info("[FG_Set_Full] (B) FullCAP(%d), RemCAP(%d)\n", + (fg_read_register(FULLCAP_REG)/2), + (fg_read_register(REMCAP_REP_REG)/2)); + + fg_write_register(FULLCAP_REG, (u16)fg_read_register(REMCAP_REP_REG)); + + pr_info("[FG_Set_Full] (A) FullCAP(%d), RemCAP(%d)\n", + (fg_read_register(FULLCAP_REG)/2), + (fg_read_register(REMCAP_REP_REG)/2)); +} + +static void display_low_batt_comp_cnt(void) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + u8 type_str[10]; + + if (fg_get_battery_type() == SDI_BATTERY_TYPE) + sprintf(type_str, "SDI"); + else if (fg_get_battery_type() == ATL_BATTERY_TYPE) + sprintf(type_str, "ATL"); + else + sprintf(type_str, "Unknown"); + + pr_info("Check Array(%s):[%d,%d], [%d,%d], [%d,%d], [%d,%d], [%d,%d]\n", + type_str, + chip->info.low_batt_comp_cnt[0][0], + chip->info.low_batt_comp_cnt[0][1], + chip->info.low_batt_comp_cnt[1][0], + chip->info.low_batt_comp_cnt[1][1], + chip->info.low_batt_comp_cnt[2][0], + chip->info.low_batt_comp_cnt[2][1], + chip->info.low_batt_comp_cnt[3][0], + chip->info.low_batt_comp_cnt[3][1], + chip->info.low_batt_comp_cnt[4][0], + chip->info.low_batt_comp_cnt[4][1]); +} + +static void add_low_batt_comp_cnt(int range, int level) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + int i; + int j; + + /* Increase the requested count value, and reset others. */ + chip->info.low_batt_comp_cnt[range-1][level/2]++; + + for (i = 0; i < LOW_BATT_COMP_RANGE_NUM; i++) { + for (j = 0; j < LOW_BATT_COMP_LEVEL_NUM; j++) { + if (i == range-1 && j == level/2) + continue; + else + chip->info.low_batt_comp_cnt[i][j] = 0; + } + } +} + +#define POWER_OFF_SOC_HIGH_MARGIN 0x200 +#define POWER_OFF_VOLTAGE_HIGH_MARGIN 3500 +#define POWER_OFF_VOLTAGE_NOW_LOW_MARGIN 3350 +#define POWER_OFF_VOLTAGE_AVG_LOW_MARGIN 3400 + +void prevent_early_late_poweroff(int vcell, int *fg_soc) +{ + struct i2c_client *client = fg_i2c_client; + int repsoc, repsoc_data = 0; + int read_val; + int avg_vcell; + u8 data[2]; + + if (fg_i2c_read(client, SOCREP_REG, data, 2) < 0) { + pr_err("%s: Failed to read SOCREP\n", __func__); + return; + } + repsoc = data[1]; + repsoc_data = ((data[1] << 8) | data[0]); + + if (repsoc_data > POWER_OFF_SOC_HIGH_MARGIN) + return; + + avg_vcell = fg_read_avg_vcell(); + pr_info("%s: soc=%d%%(0x%04x), vcell=%d avg_vcell=%d\n", + __func__, repsoc, repsoc_data, vcell, avg_vcell); + + if (vcell > POWER_OFF_VOLTAGE_HIGH_MARGIN) { + read_val = fg_read_register(FULLCAP_REG); + /* FullCAP * 0.013 */ + fg_write_register(REMCAP_REP_REG, (u16)(read_val * 13 / 1000)); + msleep(200); + *fg_soc = fg_read_soc(); + pr_info("%s: new soc=%d, vcell=%d, avg_vcell=%d\n", + __func__, *fg_soc, vcell, avg_vcell); + } else if ((vcell < POWER_OFF_VOLTAGE_NOW_LOW_MARGIN) && + (avg_vcell < POWER_OFF_VOLTAGE_AVG_LOW_MARGIN)) { + read_val = fg_read_register(FULLCAP_REG); + /* FullCAP * 0.009 */ + fg_write_register(REMCAP_REP_REG, (u16)(read_val * 9 / 1000)); + msleep(200); + *fg_soc = fg_read_soc(); + pr_info("%s: new soc=%d, vcell=%d, avg_vcell=%d\n", + __func__, *fg_soc, vcell, avg_vcell); + } +} + +void reset_low_batt_comp_cnt(void) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + + memset(chip->info.low_batt_comp_cnt, 0, + sizeof(chip->info.low_batt_comp_cnt)); +} + +static int check_low_batt_comp_condtion(int *nLevel) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + int i; + int j; + int ret = 0; + + for (i = 0; i < LOW_BATT_COMP_RANGE_NUM; i++) { + for (j = 0; j < LOW_BATT_COMP_LEVEL_NUM; j++) { + if (chip->info.low_batt_comp_cnt[i][j] \ + >= MAX_LOW_BATT_CHECK_CNT) { + display_low_batt_comp_cnt(); + ret = 1; + *nLevel = j*2 + 1; + break; + } + } + } + + return ret; +} + +static int get_low_batt_threshold(int range, int level, int nCurrent) +{ + int ret = 0; + + if (fg_get_battery_type() == SDI_BATTERY_TYPE) { + switch (range) { +/* P4 & P8 needs one more level */ +#if defined(CONFIG_MACH_P4NOTE) \ + || defined(CONFIG_MACH_P8) || defined(CONFIG_MACH_P8LTE) + case 5: + if (level == 1) + ret = SDI_Range5_1_Offset + \ + ((nCurrent * SDI_Range5_1_Slope) / 1000); + else if (level == 3) + ret = SDI_Range5_3_Offset + \ + ((nCurrent * SDI_Range5_3_Slope) / 1000); + break; +#endif + case 4: + if (level == 1) + ret = SDI_Range4_1_Offset + \ + ((nCurrent * SDI_Range4_1_Slope) / 1000); + else if (level == 3) + ret = SDI_Range4_3_Offset + \ + ((nCurrent * SDI_Range4_3_Slope) / 1000); + break; + + case 3: + if (level == 1) + ret = SDI_Range3_1_Offset + \ + ((nCurrent * SDI_Range3_1_Slope) / 1000); + else if (level == 3) + ret = SDI_Range3_3_Offset + \ + ((nCurrent * SDI_Range3_3_Slope) / 1000); + break; + + case 2: + if (level == 1) + ret = SDI_Range2_1_Offset + \ + ((nCurrent * SDI_Range2_1_Slope) / 1000); + else if (level == 3) + ret = SDI_Range2_3_Offset + \ + ((nCurrent * SDI_Range2_3_Slope) / 1000); + break; + + case 1: + if (level == 1) + ret = SDI_Range1_1_Offset + \ + ((nCurrent * SDI_Range1_1_Slope) / 1000); + else if (level == 3) + ret = SDI_Range1_3_Offset + \ + ((nCurrent * SDI_Range1_3_Slope) / 1000); + break; + + default: + break; + } + } else if (fg_get_battery_type() == ATL_BATTERY_TYPE) { + switch (range) { + case 4: + if (level == 1) + ret = ATL_Range4_1_Offset + \ + ((nCurrent * ATL_Range4_1_Slope) / 1000); + else if (level == 3) + ret = ATL_Range4_3_Offset + \ + ((nCurrent * ATL_Range4_3_Slope) / 1000); + break; + + case 3: + if (level == 1) + ret = ATL_Range3_1_Offset + \ + ((nCurrent * ATL_Range3_1_Slope) / 1000); + else if (level == 3) + ret = ATL_Range3_3_Offset + \ + ((nCurrent * ATL_Range3_3_Slope) / 1000); + break; + + case 2: + if (level == 1) + ret = ATL_Range2_1_Offset + \ + ((nCurrent * ATL_Range2_1_Slope) / 1000); + else if (level == 3) + ret = ATL_Range2_3_Offset + \ + ((nCurrent * ATL_Range2_3_Slope) / 1000); + break; + + case 1: + if (level == 1) + ret = ATL_Range1_1_Offset + \ + ((nCurrent * ATL_Range1_1_Slope) / 1000); + else if (level == 3) + ret = ATL_Range1_3_Offset + \ + ((nCurrent * ATL_Range1_3_Slope) / 1000); + break; + + default: + break; + } + } + + return ret; +} + +int low_batt_compensation(int fg_soc, int fg_vcell, int fg_current) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + int fg_avg_current = 0; + int fg_min_current = 0; + int new_level = 0; + int bCntReset = 0; + + /* Not charging, flag is none, Under 3.60V or 3.45V */ + if (!chip->info.low_batt_comp_flag && \ + (fg_vcell <= chip->info.check_start_vol)) { + + fg_avg_current = fg_read_avg_current(); + fg_min_current = min(fg_avg_current, fg_current); + + if (fg_min_current < CURRENT_RANGE_MAX) { + if (fg_soc >= 2 && \ + fg_vcell < get_low_batt_threshold( \ + CURRENT_RANGE_MAX_NUM, + 1, fg_min_current)) + add_low_batt_comp_cnt(CURRENT_RANGE_MAX_NUM, 1); + else if (fg_soc >= 4 && \ + fg_vcell < get_low_batt_threshold( \ + CURRENT_RANGE_MAX_NUM, + 3, fg_min_current)) + add_low_batt_comp_cnt(CURRENT_RANGE_MAX_NUM, 3); + else + bCntReset = 1; + } +/* P4 & P8 needs more level */ +#if defined(CONFIG_MACH_P4NOTE) \ + || defined(CONFIG_MACH_P8) || defined(CONFIG_MACH_P8LTE) + else if (fg_min_current >= CURRENT_RANGE5 && \ + fg_min_current < CURRENT_RANGE4) { + if (fg_soc >= 2 && fg_vcell < \ + get_low_batt_threshold(4, 1, fg_min_current)) + + add_low_batt_comp_cnt(4, 1); + else if (fg_soc >= 4 && fg_vcell < \ + get_low_batt_threshold(4, 3, fg_min_current)) + + add_low_batt_comp_cnt(4, 3); + else + bCntReset = 1; + } +#endif + else if (fg_min_current >= CURRENT_RANGE4 && \ + fg_min_current < CURRENT_RANGE3) { + if (fg_soc >= 2 && fg_vcell < \ + get_low_batt_threshold(3, 1, fg_min_current)) + + add_low_batt_comp_cnt(3, 1); + else if (fg_soc >= 4 && fg_vcell < \ + get_low_batt_threshold(3, 3, fg_min_current)) + add_low_batt_comp_cnt(3, 3); + else + bCntReset = 1; + } else if (fg_min_current >= CURRENT_RANGE3 && \ + fg_min_current < CURRENT_RANGE2) { + if (fg_soc >= 2 && fg_vcell < \ + get_low_batt_threshold(2, 1, fg_min_current)) + + add_low_batt_comp_cnt(2, 1); + else if (fg_soc >= 4 && fg_vcell < \ + get_low_batt_threshold(2, 3, fg_min_current)) + + add_low_batt_comp_cnt(2, 3); + else + bCntReset = 1; + } else if (fg_min_current >= CURRENT_RANGE2 && \ + fg_min_current < CURRENT_RANGE1) { + if (fg_soc >= 2 && fg_vcell < \ + get_low_batt_threshold(1, 1, fg_min_current)) + + add_low_batt_comp_cnt(1, 1); + else if (fg_soc >= 4 && fg_vcell < \ + get_low_batt_threshold(1, 3, fg_min_current)) + + add_low_batt_comp_cnt(1, 3); + else + bCntReset = 1; + } + + + if (check_low_batt_comp_condtion(&new_level)) { +#if defined(CONFIG_MACH_P4NOTE) || \ + defined(CONFIG_MACH_P8) || defined(CONFIG_MACH_P8LTE) + /* + * Disable 3% low battery compensation + * duplicated action with 1% low battery compensation + */ + if (new_level < 2) +#endif + fg_low_batt_compensation(new_level); + reset_low_batt_comp_cnt(); + } + + if (bCntReset) + reset_low_batt_comp_cnt(); + + /* if compensation finished, then read SOC again!!*/ + if (chip->info.low_batt_comp_flag) { + pr_info("%s: MIN_CURRENT(%d), AVG_CURRENT(%d),", + __func__, fg_min_current, fg_avg_current); + pr_info(" CURRENT(%d), SOC(%d), VCELL(%d)\n", + fg_current, fg_soc, fg_vcell); +#if defined(CONFIG_MACH_P8) || defined(CONFIG_MACH_P8LTE) + /* Do not update soc right after low battery compensation */ + /* to prevent from powering-off suddenly (only for P8s) */ + pr_info("%s: SOC is set to %d\n", + __func__, fg_read_soc()); +#else + fg_soc = fg_read_soc(); + pr_info("%s: SOC is set to %d\n", __func__, fg_soc); +#endif + } + } + +#if defined(CONFIG_MACH_P4NOTE) || defined(CONFIG_MACH_P2) + /* Prevent power off over 3500mV */ + /* Force power off under 3400mV */ + prevent_early_late_poweroff(fg_vcell, &fg_soc); +#endif + + return fg_soc; +} + +static void fg_set_battery_type(void) +{ + struct i2c_client *client = fg_i2c_client; + struct max17042_chip *chip = i2c_get_clientdata(client); + + u16 data; + u8 type_str[10]; + + data = fg_read_register(DESIGNCAP_REG); + + if ((data == chip->pdata->sdi_vfcapacity) || \ + (data == chip->pdata->sdi_vfcapacity-1)) + chip->info.battery_type = SDI_BATTERY_TYPE; + else if ((data == chip->pdata->atl_vfcapacity) || \ + (data == chip->pdata->atl_vfcapacity-1)) + chip->info.battery_type = ATL_BATTERY_TYPE; + else { + pr_info("%s: Unknown battery is set to SDI type.\n", __func__); + chip->info.battery_type = SDI_BATTERY_TYPE; + } + + if (chip->info.battery_type == SDI_BATTERY_TYPE) + sprintf(type_str, "SDI"); + else if (chip->info.battery_type == ATL_BATTERY_TYPE) + sprintf(type_str, "ATL"); + else + sprintf(type_str, "Unknown"); + + pr_info("%s: DesignCAP(0x%04x), Battery type(%s)\n", + __func__, data, type_str); + + switch (chip->info.battery_type) { + case ATL_BATTERY_TYPE: + chip->info.capacity = chip->pdata->atl_capacity; + chip->info.vfcapacity = chip->pdata->atl_vfcapacity; + break; + + case SDI_BATTERY_TYPE: + default: + chip->info.capacity = chip->pdata->sdi_capacity; + chip->info.vfcapacity = chip->pdata->sdi_vfcapacity; + break; + } + + /* If not initialized yet, then init threshold values. */ + if (!chip->info.check_start_vol) { + if (chip->info.battery_type == SDI_BATTERY_TYPE) + chip->info.check_start_vol = \ + chip->pdata->sdi_low_bat_comp_start_vol; + else if (chip->info.battery_type == ATL_BATTERY_TYPE) + chip->info.check_start_vol = \ + chip->pdata->atl_low_bat_comp_start_vol; + } + +} + +int get_fuelgauge_capacity(enum capacity_type type) +{ + int cap = -1; + pr_debug("%s\n", __func__); + + switch (type) { + case CAPACITY_TYPE_FULL: + cap = fg_read_register(FULLCAP_REG); + break; + case CAPACITY_TYPE_MIX: + cap = fg_read_register(REMCAP_MIX_REG); + break; + case CAPACITY_TYPE_AV: + cap = fg_read_register(REMCAP_AV_REG); + break; + case CAPACITY_TYPE_REP: + cap = fg_read_register(REMCAP_REP_REG); + break; + default: + pr_info("%s: invalid type(%d)\n", __func__, type); + cap = -EINVAL; + break; + } + + pr_debug("%s: type(%d), cap(0x%x, %d)\n", __func__, + type, cap, (cap / 2)); + return cap; +} + +int get_fuelgauge_value(int data) +{ + int ret; + + switch (data) { + case FG_LEVEL: + ret = fg_read_soc(); + break; + + case FG_TEMPERATURE: + ret = fg_read_temp(); + break; + + case FG_VOLTAGE: + ret = fg_read_vcell(); + break; + + case FG_CURRENT: + ret = fg_read_current(); + break; + + case FG_CURRENT_AVG: + ret = fg_read_avg_current(); + break; + + case FG_BATTERY_TYPE: + ret = fg_get_battery_type(); + break; + + case FG_CHECK_STATUS: + ret = fg_check_status_reg(); + break; + + case FG_VF_SOC: + ret = fg_read_vfsoc(); + break; + + case FG_VOLTAGE_NOW: + ret = fg_read_voltage_now(); + break; + + default: + ret = -1; + break; + } + + return ret; +} + +static int fg_i2c_remove(struct i2c_client *client) +{ + struct max17042_chip *chip = i2c_get_clientdata(client); + + kfree(chip); + return 0; +} + +static int +fg_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct max17042_chip *chip; + + if (!client->dev.platform_data) { + pr_err("%s: No platform data\n", __func__); + return -EINVAL; + } + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) { + pr_info("failed to allocate memory.\n"); + return -ENOMEM; + } + + chip->client = client; + chip->pdata = client->dev.platform_data; + i2c_set_clientdata(client, chip); + + pr_info("%s: fuelgauge driver loading\n", __func__); + + max17042_chip_data = chip; + /* rest of the initialisation goes here. */ + pr_info("Fuel gauge attach success!!!\n"); + + fg_i2c_client = client; + fg_set_battery_type(); + + /* Init parameters to prevent wrong compensation. */ + chip->info.prev_fullcap = fg_read_register(FULLCAP_REG); + chip->info.prev_vffcap = fg_read_register(FULLCAP_NOM_REG); + /* Init FullCAP of first full charging. */ + chip->info.full_charged_cap = chip->info.prev_fullcap; + + chip->info.prev_vfsoc = fg_read_vfsoc(); + chip->info.prev_repsoc = fg_read_soc(); + chip->info.prev_remcap = fg_read_register(REMCAP_REP_REG); + chip->info.prev_mixcap = fg_read_register(REMCAP_MIX_REG); + chip->info.prev_vfocv = fg_read_register(VFOCV_REG); + chip->info.prev_fullcapacity = chip->info.prev_fullcap; + chip->info.prev_vfcapacity = chip->info.prev_vffcap; + + mutex_init(&chip->fg_lock); + + fg_alert_init(); + fg_read_model_data(); + fg_periodic_read(); + return 0; +} + +static const struct i2c_device_id fg_device_id[] = { + {"fuelgauge", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, fg_device_id); + +static struct i2c_driver fg_i2c_driver = { + .driver = { + .name = "fuelgauge", + .owner = THIS_MODULE, + }, + .probe = fg_i2c_probe, + .remove = fg_i2c_remove, + .id_table = fg_device_id, +}; + +static int __init max17042_init(void) +{ + return i2c_add_driver(&fg_i2c_driver); +} +subsys_initcall(max17042_init); + +static void __exit max17042_exit(void) +{ + i2c_del_driver(&fg_i2c_driver); +} +module_exit(max17042_exit); + +MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>"); +MODULE_DESCRIPTION("MAX17042 Fuel Gauge"); +MODULE_LICENSE("GPL"); + |