#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
#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
* @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 {
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;
};
"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);
}
/*
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
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;
/*
* 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,
* 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;
}
* 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;
}
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 = { };
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);