diff options
Diffstat (limited to 'drivers/power/supply')
-rw-r--r-- | drivers/power/supply/sc27xx_fuel_gauge.c | 62 |
1 files changed, 51 insertions, 11 deletions
diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 8613584eb87a..66f4db2650da 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -6,10 +6,12 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/nvmem-consumer.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/regmap.h> +#include <linux/slab.h> /* PMIC global control registers definition */ #define SC27XX_MODULE_EN0 0xc08 @@ -39,8 +41,6 @@ #define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0) #define SC27XX_FGU_CLBCNT_SHIFT 16 -#define SC27XX_FGU_1000MV_ADC 686 -#define SC27XX_FGU_1000MA_ADC 1372 #define SC27XX_FGU_CUR_BASIC_ADC 8192 #define SC27XX_FGU_SAMPLE_HZ 2 @@ -59,6 +59,8 @@ * @init_clbcnt: the initial coulomb counter * @max_volt: the maximum constant input voltage in millivolt * @table_len: the capacity table length + * @cur_1000ma_adc: ADC value corresponding to 1000 mA + * @vol_1000mv_adc: ADC value corresponding to 1000 mV * @cap_table: capacity table with corresponding ocv */ struct sc27xx_fgu_data { @@ -76,6 +78,8 @@ struct sc27xx_fgu_data { int init_clbcnt; int max_volt; int table_len; + int cur_1000ma_adc; + int vol_1000mv_adc; struct power_supply_battery_ocv_table *cap_table; }; @@ -86,14 +90,14 @@ static const char * const sc27xx_charger_supply_name[] = { "sc2723_charger", }; -static int sc27xx_fgu_adc_to_current(int adc) +static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc) { - return DIV_ROUND_CLOSEST(adc * 1000, SC27XX_FGU_1000MA_ADC); + return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc); } -static int sc27xx_fgu_adc_to_voltage(int adc) +static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc) { - return DIV_ROUND_CLOSEST(adc * 1000, SC27XX_FGU_1000MV_ADC); + return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); } /* @@ -116,7 +120,7 @@ static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) return ret; cur <<= 1; - oci = sc27xx_fgu_adc_to_current(cur - SC27XX_FGU_CUR_BASIC_ADC); + oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); /* * Should get the OCV from SC27XX_FGU_POCV register at the system @@ -127,7 +131,7 @@ static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) if (ret) return ret; - volt = sc27xx_fgu_adc_to_voltage(volt); + volt = sc27xx_fgu_adc_to_voltage(data, volt); ocv = volt * 1000 - oci * data->internal_resist; /* @@ -201,7 +205,7 @@ static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap) * as 100 to improve the precision. */ temp = DIV_ROUND_CLOSEST(delta_clbcnt, 360); - temp = sc27xx_fgu_adc_to_current(temp); + temp = sc27xx_fgu_adc_to_current(data, temp); /* * Convert to capacity percent of the battery total capacity, @@ -225,7 +229,7 @@ static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val) * It is ADC values reading from registers which need to convert to * corresponding voltage values. */ - *val = sc27xx_fgu_adc_to_voltage(vol); + *val = sc27xx_fgu_adc_to_voltage(data, vol); return 0; } @@ -242,7 +246,7 @@ static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val) * It is ADC values reading from registers which need to convert to * corresponding current values. */ - *val = sc27xx_fgu_adc_to_current(cur - SC27XX_FGU_CUR_BASIC_ADC); + *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); return 0; } @@ -469,6 +473,38 @@ static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity) return DIV_ROUND_CLOSEST(cur_cap * 36, 10); } +static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) +{ + struct nvmem_cell *cell; + int calib_data, cal_4200mv; + void *buf; + size_t len; + + cell = nvmem_cell_get(data->dev, "fgu_calib"); + if (IS_ERR(cell)) + return PTR_ERR(cell); + + buf = nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); + + if (IS_ERR(buf)) + return PTR_ERR(buf); + + memcpy(&calib_data, buf, min(len, sizeof(u32))); + + /* + * Get the ADC value corresponding to 4200 mV from eFuse controller + * according to below formula. Then convert to ADC values corresponding + * to 1000 mV and 1000 mA. + */ + cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256; + data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42); + data->cur_1000ma_adc = data->vol_1000mv_adc * 4; + + kfree(buf); + return 0; +} + static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) { struct power_supply_battery_info info = { }; @@ -503,6 +539,10 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) power_supply_put_battery_info(data->battery, &info); + ret = sc27xx_fgu_calibration(data); + if (ret) + return ret; + /* Enable the FGU module */ ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, SC27XX_FGU_EN); |