1195ca170SBaolin Wang // SPDX-License-Identifier: GPL-2.0 2195ca170SBaolin Wang // Copyright (C) 2018 Spreadtrum Communications Inc. 3195ca170SBaolin Wang 4195ca170SBaolin Wang #include <linux/gpio/consumer.h> 5195ca170SBaolin Wang #include <linux/iio/consumer.h> 6195ca170SBaolin Wang #include <linux/interrupt.h> 7195ca170SBaolin Wang #include <linux/kernel.h> 8ac31585fSChunyan Zhang #include <linux/math64.h> 9195ca170SBaolin Wang #include <linux/module.h> 1065c9fab7SBaolin Wang #include <linux/nvmem-consumer.h> 11195ca170SBaolin Wang #include <linux/of.h> 12195ca170SBaolin Wang #include <linux/platform_device.h> 13195ca170SBaolin Wang #include <linux/power_supply.h> 14195ca170SBaolin Wang #include <linux/regmap.h> 1565c9fab7SBaolin Wang #include <linux/slab.h> 16195ca170SBaolin Wang 17195ca170SBaolin Wang /* PMIC global control registers definition */ 18195ca170SBaolin Wang #define SC27XX_MODULE_EN0 0xc08 19195ca170SBaolin Wang #define SC27XX_CLK_EN0 0xc18 20195ca170SBaolin Wang #define SC27XX_FGU_EN BIT(7) 21195ca170SBaolin Wang #define SC27XX_FGU_RTC_EN BIT(6) 22195ca170SBaolin Wang 23195ca170SBaolin Wang /* FGU registers definition */ 24195ca170SBaolin Wang #define SC27XX_FGU_START 0x0 25195ca170SBaolin Wang #define SC27XX_FGU_CONFIG 0x4 26195ca170SBaolin Wang #define SC27XX_FGU_ADC_CONFIG 0x8 27195ca170SBaolin Wang #define SC27XX_FGU_STATUS 0xc 28195ca170SBaolin Wang #define SC27XX_FGU_INT_EN 0x10 29195ca170SBaolin Wang #define SC27XX_FGU_INT_CLR 0x14 30195ca170SBaolin Wang #define SC27XX_FGU_INT_STS 0x1c 31195ca170SBaolin Wang #define SC27XX_FGU_VOLTAGE 0x20 32195ca170SBaolin Wang #define SC27XX_FGU_OCV 0x24 33195ca170SBaolin Wang #define SC27XX_FGU_POCV 0x28 34195ca170SBaolin Wang #define SC27XX_FGU_CURRENT 0x2c 35edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD 0x34 36195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETH 0x50 37195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETL 0x54 38edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTH 0x58 39edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTL 0x5c 40195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALH 0x68 41195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALL 0x6c 42195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_QMAXL 0x74 434a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_SET 0xa0 444a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_CLEAR 0xa4 454a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_STATUS 0xa8 46e5431c34SYuanjiang Yu #define SC27XX_FGU_VOLTAGE_BUF 0xd0 47e5431c34SYuanjiang Yu #define SC27XX_FGU_CURRENT_BUF 0xf0 48195ca170SBaolin Wang 49195ca170SBaolin Wang #define SC27XX_WRITE_SELCLB_EN BIT(0) 50195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0) 51195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SHIFT 16 52edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD_MASK GENMASK(12, 0) 53edcb1c0aSYuanjiang Yu 54edcb1c0aSYuanjiang Yu #define SC27XX_FGU_INT_MASK GENMASK(9, 0) 55edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD_INT BIT(0) 56edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTA_INT BIT(2) 57195ca170SBaolin Wang 584a040e7cSYuanjiang Yu #define SC27XX_FGU_MODE_AREA_MASK GENMASK(15, 12) 594a040e7cSYuanjiang Yu #define SC27XX_FGU_CAP_AREA_MASK GENMASK(11, 0) 604a040e7cSYuanjiang Yu #define SC27XX_FGU_MODE_AREA_SHIFT 12 614a040e7cSYuanjiang Yu 624a040e7cSYuanjiang Yu #define SC27XX_FGU_FIRST_POWERTON GENMASK(3, 0) 634a040e7cSYuanjiang Yu #define SC27XX_FGU_DEFAULT_CAP GENMASK(11, 0) 644a040e7cSYuanjiang Yu #define SC27XX_FGU_NORMAIL_POWERTON 0x5 654a040e7cSYuanjiang Yu 66195ca170SBaolin Wang #define SC27XX_FGU_CUR_BASIC_ADC 8192 67195ca170SBaolin Wang #define SC27XX_FGU_SAMPLE_HZ 2 68058d4256SBaolin Wang /* micro Ohms */ 69058d4256SBaolin Wang #define SC27XX_FGU_IDEAL_RESISTANCE 20000 70195ca170SBaolin Wang 71195ca170SBaolin Wang /* 72195ca170SBaolin Wang * struct sc27xx_fgu_data: describe the FGU device 73195ca170SBaolin Wang * @regmap: regmap for register access 74195ca170SBaolin Wang * @dev: platform device 75195ca170SBaolin Wang * @battery: battery power supply 76195ca170SBaolin Wang * @base: the base offset for the controller 77195ca170SBaolin Wang * @lock: protect the structure 78195ca170SBaolin Wang * @gpiod: GPIO for battery detection 79195ca170SBaolin Wang * @channel: IIO channel to get battery temperature 800a4f97a1SBaolin Wang * @charge_chan: IIO channel to get charge voltage 81195ca170SBaolin Wang * @internal_resist: the battery internal resistance in mOhm 82195ca170SBaolin Wang * @total_cap: the total capacity of the battery in mAh 83195ca170SBaolin Wang * @init_cap: the initial capacity of the battery in mAh 84edcb1c0aSYuanjiang Yu * @alarm_cap: the alarm capacity 85195ca170SBaolin Wang * @init_clbcnt: the initial coulomb counter 86195ca170SBaolin Wang * @max_volt: the maximum constant input voltage in millivolt 87edcb1c0aSYuanjiang Yu * @min_volt: the minimum drained battery voltage in microvolt 887c1c5e38SYuanjiang Yu * @boot_volt: the voltage measured during boot in microvolt 89195ca170SBaolin Wang * @table_len: the capacity table length 906af82888SYuanjiang Yu * @resist_table_len: the resistance table length 9165c9fab7SBaolin Wang * @cur_1000ma_adc: ADC value corresponding to 1000 mA 9265c9fab7SBaolin Wang * @vol_1000mv_adc: ADC value corresponding to 1000 mV 93058d4256SBaolin Wang * @calib_resist: the real resistance of coulomb counter chip in uOhm 94195ca170SBaolin Wang * @cap_table: capacity table with corresponding ocv 956af82888SYuanjiang Yu * @resist_table: resistance percent table with corresponding temperature 96195ca170SBaolin Wang */ 97195ca170SBaolin Wang struct sc27xx_fgu_data { 98195ca170SBaolin Wang struct regmap *regmap; 99195ca170SBaolin Wang struct device *dev; 100195ca170SBaolin Wang struct power_supply *battery; 101195ca170SBaolin Wang u32 base; 102195ca170SBaolin Wang struct mutex lock; 103195ca170SBaolin Wang struct gpio_desc *gpiod; 104195ca170SBaolin Wang struct iio_channel *channel; 1050a4f97a1SBaolin Wang struct iio_channel *charge_chan; 106195ca170SBaolin Wang bool bat_present; 107195ca170SBaolin Wang int internal_resist; 108195ca170SBaolin Wang int total_cap; 109195ca170SBaolin Wang int init_cap; 110edcb1c0aSYuanjiang Yu int alarm_cap; 111195ca170SBaolin Wang int init_clbcnt; 112195ca170SBaolin Wang int max_volt; 113edcb1c0aSYuanjiang Yu int min_volt; 1147c1c5e38SYuanjiang Yu int boot_volt; 115195ca170SBaolin Wang int table_len; 1166af82888SYuanjiang Yu int resist_table_len; 11765c9fab7SBaolin Wang int cur_1000ma_adc; 11865c9fab7SBaolin Wang int vol_1000mv_adc; 119058d4256SBaolin Wang int calib_resist; 120195ca170SBaolin Wang struct power_supply_battery_ocv_table *cap_table; 1216af82888SYuanjiang Yu struct power_supply_resistance_temp_table *resist_table; 122195ca170SBaolin Wang }; 123195ca170SBaolin Wang 124edcb1c0aSYuanjiang Yu static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity); 12558066527SYuanjiang Yu static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, 12658066527SYuanjiang Yu int cap, bool int_mode); 1277cfd33d9SYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap); 1286af82888SYuanjiang Yu static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp); 129edcb1c0aSYuanjiang Yu 130195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = { 131195ca170SBaolin Wang "sc2731_charger", 132195ca170SBaolin Wang "sc2720_charger", 133195ca170SBaolin Wang "sc2721_charger", 134195ca170SBaolin Wang "sc2723_charger", 135195ca170SBaolin Wang }; 136195ca170SBaolin Wang 137ac31585fSChunyan Zhang static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, s64 adc) 138195ca170SBaolin Wang { 139ac31585fSChunyan Zhang return DIV_S64_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc); 140195ca170SBaolin Wang } 141195ca170SBaolin Wang 142ac31585fSChunyan Zhang static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, s64 adc) 143195ca170SBaolin Wang { 144ac31585fSChunyan Zhang return DIV_S64_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); 145195ca170SBaolin Wang } 146195ca170SBaolin Wang 147edcb1c0aSYuanjiang Yu static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol) 148edcb1c0aSYuanjiang Yu { 149edcb1c0aSYuanjiang Yu return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000); 150edcb1c0aSYuanjiang Yu } 151edcb1c0aSYuanjiang Yu 1524a040e7cSYuanjiang Yu static bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data) 1534a040e7cSYuanjiang Yu { 1544a040e7cSYuanjiang Yu int ret, status, cap, mode; 1554a040e7cSYuanjiang Yu 1564a040e7cSYuanjiang Yu ret = regmap_read(data->regmap, 1574a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_STATUS, &status); 1584a040e7cSYuanjiang Yu if (ret) 1594a040e7cSYuanjiang Yu return false; 1604a040e7cSYuanjiang Yu 1614a040e7cSYuanjiang Yu /* 1624a040e7cSYuanjiang Yu * We use low 4 bits to save the last battery capacity and high 12 bits 1634a040e7cSYuanjiang Yu * to save the system boot mode. 1644a040e7cSYuanjiang Yu */ 1654a040e7cSYuanjiang Yu mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT; 1664a040e7cSYuanjiang Yu cap = status & SC27XX_FGU_CAP_AREA_MASK; 1674a040e7cSYuanjiang Yu 1684a040e7cSYuanjiang Yu /* 1694a040e7cSYuanjiang Yu * When FGU has been powered down, the user area registers became 1704a040e7cSYuanjiang Yu * default value (0xffff), which can be used to valid if the system is 1714a040e7cSYuanjiang Yu * first power on or not. 1724a040e7cSYuanjiang Yu */ 1734a040e7cSYuanjiang Yu if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP) 1744a040e7cSYuanjiang Yu return true; 1754a040e7cSYuanjiang Yu 1764a040e7cSYuanjiang Yu return false; 1774a040e7cSYuanjiang Yu } 1784a040e7cSYuanjiang Yu 1794a040e7cSYuanjiang Yu static int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data, 1804a040e7cSYuanjiang Yu int boot_mode) 1814a040e7cSYuanjiang Yu { 1824a040e7cSYuanjiang Yu int ret; 1834a040e7cSYuanjiang Yu 1844a040e7cSYuanjiang Yu ret = regmap_update_bits(data->regmap, 1854a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 1864a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 1874a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK); 1884a040e7cSYuanjiang Yu if (ret) 1894a040e7cSYuanjiang Yu return ret; 1904a040e7cSYuanjiang Yu 191d3e67c94SYuanjiang Yu /* 192d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 193d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 194d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 195d3e67c94SYuanjiang Yu * successfully according to the datasheet. 196d3e67c94SYuanjiang Yu */ 197d3e67c94SYuanjiang Yu udelay(200); 198d3e67c94SYuanjiang Yu 199d3e67c94SYuanjiang Yu ret = regmap_update_bits(data->regmap, 2004a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_SET, 2014a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 2024a040e7cSYuanjiang Yu boot_mode << SC27XX_FGU_MODE_AREA_SHIFT); 203d3e67c94SYuanjiang Yu if (ret) 204d3e67c94SYuanjiang Yu return ret; 205d3e67c94SYuanjiang Yu 206d3e67c94SYuanjiang Yu /* 207d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 208d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 209d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 210d3e67c94SYuanjiang Yu * successfully according to the datasheet. 211d3e67c94SYuanjiang Yu */ 212d3e67c94SYuanjiang Yu udelay(200); 213d3e67c94SYuanjiang Yu 214d3e67c94SYuanjiang Yu /* 215d3e67c94SYuanjiang Yu * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to 216d3e67c94SYuanjiang Yu * make the user area data available, otherwise we can not save the user 217d3e67c94SYuanjiang Yu * area data. 218d3e67c94SYuanjiang Yu */ 219d3e67c94SYuanjiang Yu return regmap_update_bits(data->regmap, 220d3e67c94SYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 221d3e67c94SYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 0); 2224a040e7cSYuanjiang Yu } 2234a040e7cSYuanjiang Yu 2244a040e7cSYuanjiang Yu static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap) 2254a040e7cSYuanjiang Yu { 2264a040e7cSYuanjiang Yu int ret; 2274a040e7cSYuanjiang Yu 2284a040e7cSYuanjiang Yu ret = regmap_update_bits(data->regmap, 2294a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 2304a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, 2314a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK); 2324a040e7cSYuanjiang Yu if (ret) 2334a040e7cSYuanjiang Yu return ret; 2344a040e7cSYuanjiang Yu 235d3e67c94SYuanjiang Yu /* 236d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 237d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 238d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 239d3e67c94SYuanjiang Yu * successfully according to the datasheet. 240d3e67c94SYuanjiang Yu */ 241d3e67c94SYuanjiang Yu udelay(200); 242d3e67c94SYuanjiang Yu 243d3e67c94SYuanjiang Yu ret = regmap_update_bits(data->regmap, 2444a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_SET, 2454a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, cap); 246d3e67c94SYuanjiang Yu if (ret) 247d3e67c94SYuanjiang Yu return ret; 248d3e67c94SYuanjiang Yu 249d3e67c94SYuanjiang Yu /* 250d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 251d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 252d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 253d3e67c94SYuanjiang Yu * successfully according to the datasheet. 254d3e67c94SYuanjiang Yu */ 255d3e67c94SYuanjiang Yu udelay(200); 256d3e67c94SYuanjiang Yu 257d3e67c94SYuanjiang Yu /* 258d3e67c94SYuanjiang Yu * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to 259d3e67c94SYuanjiang Yu * make the user area data available, otherwise we can not save the user 260d3e67c94SYuanjiang Yu * area data. 261d3e67c94SYuanjiang Yu */ 262d3e67c94SYuanjiang Yu return regmap_update_bits(data->regmap, 263d3e67c94SYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 264d3e67c94SYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, 0); 2654a040e7cSYuanjiang Yu } 2664a040e7cSYuanjiang Yu 2674a040e7cSYuanjiang Yu static int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap) 2684a040e7cSYuanjiang Yu { 2694a040e7cSYuanjiang Yu int ret, value; 2704a040e7cSYuanjiang Yu 2714a040e7cSYuanjiang Yu ret = regmap_read(data->regmap, 2724a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_STATUS, &value); 2734a040e7cSYuanjiang Yu if (ret) 2744a040e7cSYuanjiang Yu return ret; 2754a040e7cSYuanjiang Yu 2764a040e7cSYuanjiang Yu *cap = value & SC27XX_FGU_CAP_AREA_MASK; 2774a040e7cSYuanjiang Yu return 0; 2784a040e7cSYuanjiang Yu } 2794a040e7cSYuanjiang Yu 280195ca170SBaolin Wang /* 281195ca170SBaolin Wang * When system boots on, we can not read battery capacity from coulomb 282195ca170SBaolin Wang * registers, since now the coulomb registers are invalid. So we should 283195ca170SBaolin Wang * calculate the battery open circuit voltage, and get current battery 284195ca170SBaolin Wang * capacity according to the capacity table. 285195ca170SBaolin Wang */ 286195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) 287195ca170SBaolin Wang { 288195ca170SBaolin Wang int volt, cur, oci, ocv, ret; 2894a040e7cSYuanjiang Yu bool is_first_poweron = sc27xx_fgu_is_first_poweron(data); 2904a040e7cSYuanjiang Yu 2914a040e7cSYuanjiang Yu /* 2924a040e7cSYuanjiang Yu * If system is not the first power on, we should use the last saved 2934a040e7cSYuanjiang Yu * battery capacity as the initial battery capacity. Otherwise we should 2944a040e7cSYuanjiang Yu * re-calculate the initial battery capacity. 2954a040e7cSYuanjiang Yu */ 2964a040e7cSYuanjiang Yu if (!is_first_poweron) { 2974a040e7cSYuanjiang Yu ret = sc27xx_fgu_read_last_cap(data, cap); 2984a040e7cSYuanjiang Yu if (ret) 2994a040e7cSYuanjiang Yu return ret; 3004a040e7cSYuanjiang Yu 3014a040e7cSYuanjiang Yu return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 3024a040e7cSYuanjiang Yu } 303195ca170SBaolin Wang 304195ca170SBaolin Wang /* 305195ca170SBaolin Wang * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved 306195ca170SBaolin Wang * the first sampled open circuit current. 307195ca170SBaolin Wang */ 308195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL, 309195ca170SBaolin Wang &cur); 310195ca170SBaolin Wang if (ret) 311195ca170SBaolin Wang return ret; 312195ca170SBaolin Wang 313195ca170SBaolin Wang cur <<= 1; 31465c9fab7SBaolin Wang oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 315195ca170SBaolin Wang 316195ca170SBaolin Wang /* 317195ca170SBaolin Wang * Should get the OCV from SC27XX_FGU_POCV register at the system 318195ca170SBaolin Wang * beginning. It is ADC values reading from registers which need to 319195ca170SBaolin Wang * convert the corresponding voltage. 320195ca170SBaolin Wang */ 321195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt); 322195ca170SBaolin Wang if (ret) 323195ca170SBaolin Wang return ret; 324195ca170SBaolin Wang 32565c9fab7SBaolin Wang volt = sc27xx_fgu_adc_to_voltage(data, volt); 326195ca170SBaolin Wang ocv = volt * 1000 - oci * data->internal_resist; 3277c1c5e38SYuanjiang Yu data->boot_volt = ocv; 328195ca170SBaolin Wang 329195ca170SBaolin Wang /* 330195ca170SBaolin Wang * Parse the capacity table to look up the correct capacity percent 331195ca170SBaolin Wang * according to current battery's corresponding OCV values. 332195ca170SBaolin Wang */ 333195ca170SBaolin Wang *cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len, 334195ca170SBaolin Wang ocv); 335195ca170SBaolin Wang 3364a040e7cSYuanjiang Yu ret = sc27xx_fgu_save_last_cap(data, *cap); 3374a040e7cSYuanjiang Yu if (ret) 3384a040e7cSYuanjiang Yu return ret; 3394a040e7cSYuanjiang Yu 3404a040e7cSYuanjiang Yu return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 341195ca170SBaolin Wang } 342195ca170SBaolin Wang 343195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt) 344195ca170SBaolin Wang { 345195ca170SBaolin Wang int ret; 346195ca170SBaolin Wang 347195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 348195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETL, 349195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, clbcnt); 350195ca170SBaolin Wang if (ret) 351195ca170SBaolin Wang return ret; 352195ca170SBaolin Wang 353195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 354195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETH, 355195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, 356195ca170SBaolin Wang clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 357195ca170SBaolin Wang if (ret) 358195ca170SBaolin Wang return ret; 359195ca170SBaolin Wang 360195ca170SBaolin Wang return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START, 361195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN, 362195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN); 363195ca170SBaolin Wang } 364195ca170SBaolin Wang 365195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt) 366195ca170SBaolin Wang { 367195ca170SBaolin Wang int ccl, cch, ret; 368195ca170SBaolin Wang 369195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL, 370195ca170SBaolin Wang &ccl); 371195ca170SBaolin Wang if (ret) 372195ca170SBaolin Wang return ret; 373195ca170SBaolin Wang 374195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH, 375195ca170SBaolin Wang &cch); 376195ca170SBaolin Wang if (ret) 377195ca170SBaolin Wang return ret; 378195ca170SBaolin Wang 379195ca170SBaolin Wang *clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK; 380195ca170SBaolin Wang *clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT; 381195ca170SBaolin Wang 382195ca170SBaolin Wang return 0; 383195ca170SBaolin Wang } 384195ca170SBaolin Wang 385e5431c34SYuanjiang Yu static int sc27xx_fgu_get_vol_now(struct sc27xx_fgu_data *data, int *val) 386e5431c34SYuanjiang Yu { 387e5431c34SYuanjiang Yu int ret; 388e5431c34SYuanjiang Yu u32 vol; 389e5431c34SYuanjiang Yu 390e5431c34SYuanjiang Yu ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE_BUF, 391e5431c34SYuanjiang Yu &vol); 392e5431c34SYuanjiang Yu if (ret) 393e5431c34SYuanjiang Yu return ret; 394e5431c34SYuanjiang Yu 395e5431c34SYuanjiang Yu /* 396e5431c34SYuanjiang Yu * It is ADC values reading from registers which need to convert to 397e5431c34SYuanjiang Yu * corresponding voltage values. 398e5431c34SYuanjiang Yu */ 399e5431c34SYuanjiang Yu *val = sc27xx_fgu_adc_to_voltage(data, vol); 400e5431c34SYuanjiang Yu 401e5431c34SYuanjiang Yu return 0; 402e5431c34SYuanjiang Yu } 403e5431c34SYuanjiang Yu 404e5431c34SYuanjiang Yu static int sc27xx_fgu_get_cur_now(struct sc27xx_fgu_data *data, int *val) 405e5431c34SYuanjiang Yu { 406e5431c34SYuanjiang Yu int ret; 407e5431c34SYuanjiang Yu u32 cur; 408e5431c34SYuanjiang Yu 409e5431c34SYuanjiang Yu ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT_BUF, 410e5431c34SYuanjiang Yu &cur); 411e5431c34SYuanjiang Yu if (ret) 412e5431c34SYuanjiang Yu return ret; 413e5431c34SYuanjiang Yu 414e5431c34SYuanjiang Yu /* 415e5431c34SYuanjiang Yu * It is ADC values reading from registers which need to convert to 416e5431c34SYuanjiang Yu * corresponding current values. 417e5431c34SYuanjiang Yu */ 418e5431c34SYuanjiang Yu *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 419e5431c34SYuanjiang Yu 420e5431c34SYuanjiang Yu return 0; 421e5431c34SYuanjiang Yu } 422e5431c34SYuanjiang Yu 423195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap) 424195ca170SBaolin Wang { 425195ca170SBaolin Wang int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp; 426195ca170SBaolin Wang 427195ca170SBaolin Wang /* Get current coulomb counters firstly */ 428195ca170SBaolin Wang ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt); 429195ca170SBaolin Wang if (ret) 430195ca170SBaolin Wang return ret; 431195ca170SBaolin Wang 432195ca170SBaolin Wang delta_clbcnt = cur_clbcnt - data->init_clbcnt; 433195ca170SBaolin Wang 434195ca170SBaolin Wang /* 435195ca170SBaolin Wang * Convert coulomb counter to delta capacity (mAh), and set multiplier 4367384b0e7SYuanjiang Yu * as 10 to improve the precision. 437195ca170SBaolin Wang */ 4387384b0e7SYuanjiang Yu temp = DIV_ROUND_CLOSEST(delta_clbcnt * 10, 36 * SC27XX_FGU_SAMPLE_HZ); 4397384b0e7SYuanjiang Yu temp = sc27xx_fgu_adc_to_current(data, temp / 1000); 440195ca170SBaolin Wang 441195ca170SBaolin Wang /* 442195ca170SBaolin Wang * Convert to capacity percent of the battery total capacity, 443195ca170SBaolin Wang * and multiplier is 100 too. 444195ca170SBaolin Wang */ 445195ca170SBaolin Wang delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap); 446195ca170SBaolin Wang *cap = delta_cap + data->init_cap; 447195ca170SBaolin Wang 44858066527SYuanjiang Yu /* Calibrate the battery capacity in a normal range. */ 44958066527SYuanjiang Yu sc27xx_fgu_capacity_calibration(data, *cap, false); 45058066527SYuanjiang Yu 451195ca170SBaolin Wang return 0; 452195ca170SBaolin Wang } 453195ca170SBaolin Wang 454195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val) 455195ca170SBaolin Wang { 456195ca170SBaolin Wang int ret, vol; 457195ca170SBaolin Wang 458195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol); 459195ca170SBaolin Wang if (ret) 460195ca170SBaolin Wang return ret; 461195ca170SBaolin Wang 462195ca170SBaolin Wang /* 463195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 464195ca170SBaolin Wang * corresponding voltage values. 465195ca170SBaolin Wang */ 46665c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_voltage(data, vol); 467195ca170SBaolin Wang 468195ca170SBaolin Wang return 0; 469195ca170SBaolin Wang } 470195ca170SBaolin Wang 471195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val) 472195ca170SBaolin Wang { 473195ca170SBaolin Wang int ret, cur; 474195ca170SBaolin Wang 475195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur); 476195ca170SBaolin Wang if (ret) 477195ca170SBaolin Wang return ret; 478195ca170SBaolin Wang 479195ca170SBaolin Wang /* 480195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 481195ca170SBaolin Wang * corresponding current values. 482195ca170SBaolin Wang */ 48365c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 484195ca170SBaolin Wang 485195ca170SBaolin Wang return 0; 486195ca170SBaolin Wang } 487195ca170SBaolin Wang 488195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) 489195ca170SBaolin Wang { 4906af82888SYuanjiang Yu int vol, cur, ret, temp, resistance; 491195ca170SBaolin Wang 492195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 493195ca170SBaolin Wang if (ret) 494195ca170SBaolin Wang return ret; 495195ca170SBaolin Wang 496195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &cur); 497195ca170SBaolin Wang if (ret) 498195ca170SBaolin Wang return ret; 499195ca170SBaolin Wang 5006af82888SYuanjiang Yu resistance = data->internal_resist; 5016af82888SYuanjiang Yu if (data->resist_table_len > 0) { 5026af82888SYuanjiang Yu ret = sc27xx_fgu_get_temp(data, &temp); 5036af82888SYuanjiang Yu if (ret) 5046af82888SYuanjiang Yu return ret; 5056af82888SYuanjiang Yu 5066af82888SYuanjiang Yu resistance = power_supply_temp2resist_simple(data->resist_table, 5076af82888SYuanjiang Yu data->resist_table_len, temp); 5086af82888SYuanjiang Yu resistance = data->internal_resist * resistance / 100; 5096af82888SYuanjiang Yu } 5106af82888SYuanjiang Yu 511195ca170SBaolin Wang /* Return the battery OCV in micro volts. */ 5126af82888SYuanjiang Yu *val = vol * 1000 - cur * resistance; 513195ca170SBaolin Wang 514195ca170SBaolin Wang return 0; 515195ca170SBaolin Wang } 516195ca170SBaolin Wang 5170a4f97a1SBaolin Wang static int sc27xx_fgu_get_charge_vol(struct sc27xx_fgu_data *data, int *val) 5180a4f97a1SBaolin Wang { 5190a4f97a1SBaolin Wang int ret, vol; 5200a4f97a1SBaolin Wang 5210a4f97a1SBaolin Wang ret = iio_read_channel_processed(data->charge_chan, &vol); 5220a4f97a1SBaolin Wang if (ret < 0) 5230a4f97a1SBaolin Wang return ret; 5240a4f97a1SBaolin Wang 5250a4f97a1SBaolin Wang *val = vol * 1000; 5260a4f97a1SBaolin Wang return 0; 5270a4f97a1SBaolin Wang } 5280a4f97a1SBaolin Wang 529195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp) 530195ca170SBaolin Wang { 531195ca170SBaolin Wang return iio_read_channel_processed(data->channel, temp); 532195ca170SBaolin Wang } 533195ca170SBaolin Wang 534195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health) 535195ca170SBaolin Wang { 536195ca170SBaolin Wang int ret, vol; 537195ca170SBaolin Wang 538195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 539195ca170SBaolin Wang if (ret) 540195ca170SBaolin Wang return ret; 541195ca170SBaolin Wang 542195ca170SBaolin Wang if (vol > data->max_volt) 543195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 544195ca170SBaolin Wang else 545195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_GOOD; 546195ca170SBaolin Wang 547195ca170SBaolin Wang return 0; 548195ca170SBaolin Wang } 549195ca170SBaolin Wang 550195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status) 551195ca170SBaolin Wang { 552195ca170SBaolin Wang union power_supply_propval val; 553195ca170SBaolin Wang struct power_supply *psy; 554195ca170SBaolin Wang int i, ret = -EINVAL; 555195ca170SBaolin Wang 556195ca170SBaolin Wang for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) { 557195ca170SBaolin Wang psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]); 558195ca170SBaolin Wang if (!psy) 559195ca170SBaolin Wang continue; 560195ca170SBaolin Wang 561195ca170SBaolin Wang ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, 562195ca170SBaolin Wang &val); 563195ca170SBaolin Wang power_supply_put(psy); 564195ca170SBaolin Wang if (ret) 565195ca170SBaolin Wang return ret; 566195ca170SBaolin Wang 567195ca170SBaolin Wang *status = val.intval; 568195ca170SBaolin Wang } 569195ca170SBaolin Wang 570195ca170SBaolin Wang return ret; 571195ca170SBaolin Wang } 572195ca170SBaolin Wang 573195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy, 574195ca170SBaolin Wang enum power_supply_property psp, 575195ca170SBaolin Wang union power_supply_propval *val) 576195ca170SBaolin Wang { 577195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 578195ca170SBaolin Wang int ret = 0; 579195ca170SBaolin Wang int value; 580195ca170SBaolin Wang 581195ca170SBaolin Wang mutex_lock(&data->lock); 582195ca170SBaolin Wang 583195ca170SBaolin Wang switch (psp) { 584195ca170SBaolin Wang case POWER_SUPPLY_PROP_STATUS: 585195ca170SBaolin Wang ret = sc27xx_fgu_get_status(data, &value); 586195ca170SBaolin Wang if (ret) 587195ca170SBaolin Wang goto error; 588195ca170SBaolin Wang 589195ca170SBaolin Wang val->intval = value; 590195ca170SBaolin Wang break; 591195ca170SBaolin Wang 592195ca170SBaolin Wang case POWER_SUPPLY_PROP_HEALTH: 593195ca170SBaolin Wang ret = sc27xx_fgu_get_health(data, &value); 594195ca170SBaolin Wang if (ret) 595195ca170SBaolin Wang goto error; 596195ca170SBaolin Wang 597195ca170SBaolin Wang val->intval = value; 598195ca170SBaolin Wang break; 599195ca170SBaolin Wang 600195ca170SBaolin Wang case POWER_SUPPLY_PROP_PRESENT: 601195ca170SBaolin Wang val->intval = data->bat_present; 602195ca170SBaolin Wang break; 603195ca170SBaolin Wang 604195ca170SBaolin Wang case POWER_SUPPLY_PROP_TEMP: 605195ca170SBaolin Wang ret = sc27xx_fgu_get_temp(data, &value); 606195ca170SBaolin Wang if (ret) 607195ca170SBaolin Wang goto error; 608195ca170SBaolin Wang 609195ca170SBaolin Wang val->intval = value; 610195ca170SBaolin Wang break; 611195ca170SBaolin Wang 612195ca170SBaolin Wang case POWER_SUPPLY_PROP_TECHNOLOGY: 613195ca170SBaolin Wang val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 614195ca170SBaolin Wang break; 615195ca170SBaolin Wang 616195ca170SBaolin Wang case POWER_SUPPLY_PROP_CAPACITY: 617195ca170SBaolin Wang ret = sc27xx_fgu_get_capacity(data, &value); 618195ca170SBaolin Wang if (ret) 619195ca170SBaolin Wang goto error; 620195ca170SBaolin Wang 621195ca170SBaolin Wang val->intval = value; 622195ca170SBaolin Wang break; 623195ca170SBaolin Wang 624e5431c34SYuanjiang Yu case POWER_SUPPLY_PROP_VOLTAGE_AVG: 625195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &value); 626195ca170SBaolin Wang if (ret) 627195ca170SBaolin Wang goto error; 628195ca170SBaolin Wang 629195ca170SBaolin Wang val->intval = value * 1000; 630195ca170SBaolin Wang break; 631195ca170SBaolin Wang 632195ca170SBaolin Wang case POWER_SUPPLY_PROP_VOLTAGE_OCV: 633195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_ocv(data, &value); 634195ca170SBaolin Wang if (ret) 635195ca170SBaolin Wang goto error; 636195ca170SBaolin Wang 637195ca170SBaolin Wang val->intval = value; 638195ca170SBaolin Wang break; 639195ca170SBaolin Wang 6400a4f97a1SBaolin Wang case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 6410a4f97a1SBaolin Wang ret = sc27xx_fgu_get_charge_vol(data, &value); 6420a4f97a1SBaolin Wang if (ret) 6430a4f97a1SBaolin Wang goto error; 6440a4f97a1SBaolin Wang 6450a4f97a1SBaolin Wang val->intval = value; 6460a4f97a1SBaolin Wang break; 6470a4f97a1SBaolin Wang 648195ca170SBaolin Wang case POWER_SUPPLY_PROP_CURRENT_AVG: 649195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &value); 650195ca170SBaolin Wang if (ret) 651195ca170SBaolin Wang goto error; 652195ca170SBaolin Wang 653195ca170SBaolin Wang val->intval = value * 1000; 654195ca170SBaolin Wang break; 655195ca170SBaolin Wang 6567cff19b9SYuanjiang Yu case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 6577cff19b9SYuanjiang Yu val->intval = data->total_cap * 1000; 6587cff19b9SYuanjiang Yu break; 6597cff19b9SYuanjiang Yu 6601c5dfc5eSBaolin Wang case POWER_SUPPLY_PROP_CHARGE_NOW: 6611c5dfc5eSBaolin Wang ret = sc27xx_fgu_get_clbcnt(data, &value); 6621c5dfc5eSBaolin Wang if (ret) 6631c5dfc5eSBaolin Wang goto error; 6641c5dfc5eSBaolin Wang 6651c5dfc5eSBaolin Wang value = DIV_ROUND_CLOSEST(value * 10, 6661c5dfc5eSBaolin Wang 36 * SC27XX_FGU_SAMPLE_HZ); 6671c5dfc5eSBaolin Wang val->intval = sc27xx_fgu_adc_to_current(data, value); 6681c5dfc5eSBaolin Wang 6691c5dfc5eSBaolin Wang break; 6701c5dfc5eSBaolin Wang 671e5431c34SYuanjiang Yu case POWER_SUPPLY_PROP_VOLTAGE_NOW: 672e5431c34SYuanjiang Yu ret = sc27xx_fgu_get_vol_now(data, &value); 673e5431c34SYuanjiang Yu if (ret) 674e5431c34SYuanjiang Yu goto error; 675e5431c34SYuanjiang Yu 676e5431c34SYuanjiang Yu val->intval = value * 1000; 677e5431c34SYuanjiang Yu break; 678e5431c34SYuanjiang Yu 679e5431c34SYuanjiang Yu case POWER_SUPPLY_PROP_CURRENT_NOW: 680e5431c34SYuanjiang Yu ret = sc27xx_fgu_get_cur_now(data, &value); 681e5431c34SYuanjiang Yu if (ret) 682e5431c34SYuanjiang Yu goto error; 683e5431c34SYuanjiang Yu 684e5431c34SYuanjiang Yu val->intval = value * 1000; 685e5431c34SYuanjiang Yu break; 686e5431c34SYuanjiang Yu 6877c1c5e38SYuanjiang Yu case POWER_SUPPLY_PROP_VOLTAGE_BOOT: 6887c1c5e38SYuanjiang Yu val->intval = data->boot_volt; 6897c1c5e38SYuanjiang Yu break; 6907c1c5e38SYuanjiang Yu 691195ca170SBaolin Wang default: 692195ca170SBaolin Wang ret = -EINVAL; 693195ca170SBaolin Wang break; 694195ca170SBaolin Wang } 695195ca170SBaolin Wang 696195ca170SBaolin Wang error: 697195ca170SBaolin Wang mutex_unlock(&data->lock); 698195ca170SBaolin Wang return ret; 699195ca170SBaolin Wang } 700195ca170SBaolin Wang 7014a040e7cSYuanjiang Yu static int sc27xx_fgu_set_property(struct power_supply *psy, 7024a040e7cSYuanjiang Yu enum power_supply_property psp, 7034a040e7cSYuanjiang Yu const union power_supply_propval *val) 7044a040e7cSYuanjiang Yu { 7054a040e7cSYuanjiang Yu struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 7064a040e7cSYuanjiang Yu int ret; 7074a040e7cSYuanjiang Yu 7084a040e7cSYuanjiang Yu mutex_lock(&data->lock); 7094a040e7cSYuanjiang Yu 7107cfd33d9SYuanjiang Yu switch (psp) { 7117cfd33d9SYuanjiang Yu case POWER_SUPPLY_PROP_CAPACITY: 7124a040e7cSYuanjiang Yu ret = sc27xx_fgu_save_last_cap(data, val->intval); 7134a040e7cSYuanjiang Yu if (ret < 0) 7144a040e7cSYuanjiang Yu dev_err(data->dev, "failed to save battery capacity\n"); 7157cfd33d9SYuanjiang Yu break; 7167cfd33d9SYuanjiang Yu 7177cfd33d9SYuanjiang Yu case POWER_SUPPLY_PROP_CALIBRATE: 7187cfd33d9SYuanjiang Yu sc27xx_fgu_adjust_cap(data, val->intval); 7197cfd33d9SYuanjiang Yu ret = 0; 7207cfd33d9SYuanjiang Yu break; 7217cfd33d9SYuanjiang Yu 7228720b255SYuanjiang Yu case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 7238720b255SYuanjiang Yu data->total_cap = val->intval / 1000; 7248720b255SYuanjiang Yu ret = 0; 7258720b255SYuanjiang Yu break; 7268720b255SYuanjiang Yu 7277cfd33d9SYuanjiang Yu default: 7287cfd33d9SYuanjiang Yu ret = -EINVAL; 7297cfd33d9SYuanjiang Yu } 7307cfd33d9SYuanjiang Yu 7317cfd33d9SYuanjiang Yu mutex_unlock(&data->lock); 7324a040e7cSYuanjiang Yu 7334a040e7cSYuanjiang Yu return ret; 7344a040e7cSYuanjiang Yu } 7354a040e7cSYuanjiang Yu 736195ca170SBaolin Wang static void sc27xx_fgu_external_power_changed(struct power_supply *psy) 737195ca170SBaolin Wang { 738195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 739195ca170SBaolin Wang 740195ca170SBaolin Wang power_supply_changed(data->battery); 741195ca170SBaolin Wang } 742195ca170SBaolin Wang 7434a040e7cSYuanjiang Yu static int sc27xx_fgu_property_is_writeable(struct power_supply *psy, 7444a040e7cSYuanjiang Yu enum power_supply_property psp) 7454a040e7cSYuanjiang Yu { 7467cfd33d9SYuanjiang Yu return psp == POWER_SUPPLY_PROP_CAPACITY || 7478720b255SYuanjiang Yu psp == POWER_SUPPLY_PROP_CALIBRATE || 7488720b255SYuanjiang Yu psp == POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; 7494a040e7cSYuanjiang Yu } 7504a040e7cSYuanjiang Yu 751195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = { 752195ca170SBaolin Wang POWER_SUPPLY_PROP_STATUS, 753195ca170SBaolin Wang POWER_SUPPLY_PROP_HEALTH, 754195ca170SBaolin Wang POWER_SUPPLY_PROP_PRESENT, 755195ca170SBaolin Wang POWER_SUPPLY_PROP_TEMP, 756195ca170SBaolin Wang POWER_SUPPLY_PROP_TECHNOLOGY, 757195ca170SBaolin Wang POWER_SUPPLY_PROP_CAPACITY, 758195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_NOW, 759195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_OCV, 760e5431c34SYuanjiang Yu POWER_SUPPLY_PROP_VOLTAGE_AVG, 7617c1c5e38SYuanjiang Yu POWER_SUPPLY_PROP_VOLTAGE_BOOT, 762195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_NOW, 763195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_AVG, 7640a4f97a1SBaolin Wang POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 7657cff19b9SYuanjiang Yu POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 7667cfd33d9SYuanjiang Yu POWER_SUPPLY_PROP_CALIBRATE, 7671c5dfc5eSBaolin Wang POWER_SUPPLY_PROP_CHARGE_NOW 768195ca170SBaolin Wang }; 769195ca170SBaolin Wang 770195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = { 771195ca170SBaolin Wang .name = "sc27xx-fgu", 772195ca170SBaolin Wang .type = POWER_SUPPLY_TYPE_BATTERY, 773195ca170SBaolin Wang .properties = sc27xx_fgu_props, 774195ca170SBaolin Wang .num_properties = ARRAY_SIZE(sc27xx_fgu_props), 775195ca170SBaolin Wang .get_property = sc27xx_fgu_get_property, 7764a040e7cSYuanjiang Yu .set_property = sc27xx_fgu_set_property, 777195ca170SBaolin Wang .external_power_changed = sc27xx_fgu_external_power_changed, 7784a040e7cSYuanjiang Yu .property_is_writeable = sc27xx_fgu_property_is_writeable, 779f3912a5dSYuanjiang Yu .no_thermal = true, 780195ca170SBaolin Wang }; 781195ca170SBaolin Wang 782edcb1c0aSYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap) 783edcb1c0aSYuanjiang Yu { 78458066527SYuanjiang Yu int ret; 78558066527SYuanjiang Yu 786edcb1c0aSYuanjiang Yu data->init_cap = cap; 78758066527SYuanjiang Yu ret = sc27xx_fgu_get_clbcnt(data, &data->init_clbcnt); 78858066527SYuanjiang Yu if (ret) 78958066527SYuanjiang Yu dev_err(data->dev, "failed to get init coulomb counter\n"); 79058066527SYuanjiang Yu } 79158066527SYuanjiang Yu 79258066527SYuanjiang Yu static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, 79358066527SYuanjiang Yu int cap, bool int_mode) 79458066527SYuanjiang Yu { 79558066527SYuanjiang Yu int ret, ocv, chg_sts, adc; 79658066527SYuanjiang Yu 79758066527SYuanjiang Yu ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 79858066527SYuanjiang Yu if (ret) { 79958066527SYuanjiang Yu dev_err(data->dev, "get battery ocv error.\n"); 80058066527SYuanjiang Yu return; 80158066527SYuanjiang Yu } 80258066527SYuanjiang Yu 80358066527SYuanjiang Yu ret = sc27xx_fgu_get_status(data, &chg_sts); 80458066527SYuanjiang Yu if (ret) { 80558066527SYuanjiang Yu dev_err(data->dev, "get charger status error.\n"); 80658066527SYuanjiang Yu return; 80758066527SYuanjiang Yu } 80858066527SYuanjiang Yu 80958066527SYuanjiang Yu /* 81058066527SYuanjiang Yu * If we are in charging mode, then we do not need to calibrate the 81158066527SYuanjiang Yu * lower capacity. 81258066527SYuanjiang Yu */ 81358066527SYuanjiang Yu if (chg_sts == POWER_SUPPLY_STATUS_CHARGING) 81458066527SYuanjiang Yu return; 81558066527SYuanjiang Yu 81658066527SYuanjiang Yu if ((ocv > data->cap_table[0].ocv && cap < 100) || cap > 100) { 81758066527SYuanjiang Yu /* 81858066527SYuanjiang Yu * If current OCV value is larger than the max OCV value in 81958066527SYuanjiang Yu * OCV table, or the current capacity is larger than 100, 82058066527SYuanjiang Yu * we should force the inititial capacity to 100. 82158066527SYuanjiang Yu */ 82258066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, 100); 82358066527SYuanjiang Yu } else if (ocv <= data->cap_table[data->table_len - 1].ocv) { 82458066527SYuanjiang Yu /* 82558066527SYuanjiang Yu * If current OCV value is leass than the minimum OCV value in 82658066527SYuanjiang Yu * OCV table, we should force the inititial capacity to 0. 82758066527SYuanjiang Yu */ 82858066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, 0); 82958066527SYuanjiang Yu } else if ((ocv > data->cap_table[data->table_len - 1].ocv && cap <= 0) || 83058066527SYuanjiang Yu (ocv > data->min_volt && cap <= data->alarm_cap)) { 83158066527SYuanjiang Yu /* 83258066527SYuanjiang Yu * If current OCV value is not matchable with current capacity, 83358066527SYuanjiang Yu * we should re-calculate current capacity by looking up the 83458066527SYuanjiang Yu * OCV table. 83558066527SYuanjiang Yu */ 83658066527SYuanjiang Yu int cur_cap = power_supply_ocv2cap_simple(data->cap_table, 83758066527SYuanjiang Yu data->table_len, ocv); 83858066527SYuanjiang Yu 83958066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, cur_cap); 84058066527SYuanjiang Yu } else if (ocv <= data->min_volt) { 84158066527SYuanjiang Yu /* 84258066527SYuanjiang Yu * If current OCV value is less than the low alarm voltage, but 84358066527SYuanjiang Yu * current capacity is larger than the alarm capacity, we should 84458066527SYuanjiang Yu * adjust the inititial capacity to alarm capacity. 84558066527SYuanjiang Yu */ 84658066527SYuanjiang Yu if (cap > data->alarm_cap) { 84758066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, data->alarm_cap); 84858066527SYuanjiang Yu } else { 84958066527SYuanjiang Yu int cur_cap; 85058066527SYuanjiang Yu 85158066527SYuanjiang Yu /* 85258066527SYuanjiang Yu * If current capacity is equal with 0 or less than 0 85358066527SYuanjiang Yu * (some error occurs), we should adjust inititial 85458066527SYuanjiang Yu * capacity to the capacity corresponding to current OCV 85558066527SYuanjiang Yu * value. 85658066527SYuanjiang Yu */ 85758066527SYuanjiang Yu cur_cap = power_supply_ocv2cap_simple(data->cap_table, 85858066527SYuanjiang Yu data->table_len, 85958066527SYuanjiang Yu ocv); 86058066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, cur_cap); 86158066527SYuanjiang Yu } 86258066527SYuanjiang Yu 86358066527SYuanjiang Yu if (!int_mode) 86458066527SYuanjiang Yu return; 86558066527SYuanjiang Yu 86658066527SYuanjiang Yu /* 86758066527SYuanjiang Yu * After adjusting the battery capacity, we should set the 86858066527SYuanjiang Yu * lowest alarm voltage instead. 86958066527SYuanjiang Yu */ 87058066527SYuanjiang Yu data->min_volt = data->cap_table[data->table_len - 1].ocv; 87158066527SYuanjiang Yu data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, 87258066527SYuanjiang Yu data->table_len, 87358066527SYuanjiang Yu data->min_volt); 87458066527SYuanjiang Yu 87558066527SYuanjiang Yu adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 87658066527SYuanjiang Yu regmap_update_bits(data->regmap, 87758066527SYuanjiang Yu data->base + SC27XX_FGU_LOW_OVERLOAD, 87858066527SYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_MASK, adc); 87958066527SYuanjiang Yu } 880edcb1c0aSYuanjiang Yu } 881edcb1c0aSYuanjiang Yu 882edcb1c0aSYuanjiang Yu static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id) 883edcb1c0aSYuanjiang Yu { 884edcb1c0aSYuanjiang Yu struct sc27xx_fgu_data *data = dev_id; 88558066527SYuanjiang Yu int ret, cap; 886edcb1c0aSYuanjiang Yu u32 status; 887edcb1c0aSYuanjiang Yu 888edcb1c0aSYuanjiang Yu mutex_lock(&data->lock); 889edcb1c0aSYuanjiang Yu 890edcb1c0aSYuanjiang Yu ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS, 891edcb1c0aSYuanjiang Yu &status); 892edcb1c0aSYuanjiang Yu if (ret) 893edcb1c0aSYuanjiang Yu goto out; 894edcb1c0aSYuanjiang Yu 895edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 896edcb1c0aSYuanjiang Yu status, status); 897edcb1c0aSYuanjiang Yu if (ret) 898edcb1c0aSYuanjiang Yu goto out; 899edcb1c0aSYuanjiang Yu 900edcb1c0aSYuanjiang Yu /* 901edcb1c0aSYuanjiang Yu * When low overload voltage interrupt happens, we should calibrate the 902edcb1c0aSYuanjiang Yu * battery capacity in lower voltage stage. 903edcb1c0aSYuanjiang Yu */ 904edcb1c0aSYuanjiang Yu if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT)) 905edcb1c0aSYuanjiang Yu goto out; 906edcb1c0aSYuanjiang Yu 907edcb1c0aSYuanjiang Yu ret = sc27xx_fgu_get_capacity(data, &cap); 908edcb1c0aSYuanjiang Yu if (ret) 909edcb1c0aSYuanjiang Yu goto out; 910edcb1c0aSYuanjiang Yu 91158066527SYuanjiang Yu sc27xx_fgu_capacity_calibration(data, cap, true); 912edcb1c0aSYuanjiang Yu 913edcb1c0aSYuanjiang Yu out: 914edcb1c0aSYuanjiang Yu mutex_unlock(&data->lock); 915edcb1c0aSYuanjiang Yu 916edcb1c0aSYuanjiang Yu power_supply_changed(data->battery); 917edcb1c0aSYuanjiang Yu return IRQ_HANDLED; 918edcb1c0aSYuanjiang Yu } 919edcb1c0aSYuanjiang Yu 920195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id) 921195ca170SBaolin Wang { 922195ca170SBaolin Wang struct sc27xx_fgu_data *data = dev_id; 923195ca170SBaolin Wang int state; 924195ca170SBaolin Wang 925195ca170SBaolin Wang mutex_lock(&data->lock); 926195ca170SBaolin Wang 927195ca170SBaolin Wang state = gpiod_get_value_cansleep(data->gpiod); 928195ca170SBaolin Wang if (state < 0) { 929195ca170SBaolin Wang dev_err(data->dev, "failed to get gpio state\n"); 930195ca170SBaolin Wang mutex_unlock(&data->lock); 931195ca170SBaolin Wang return IRQ_RETVAL(state); 932195ca170SBaolin Wang } 933195ca170SBaolin Wang 934195ca170SBaolin Wang data->bat_present = !!state; 935195ca170SBaolin Wang 936195ca170SBaolin Wang mutex_unlock(&data->lock); 937195ca170SBaolin Wang 938195ca170SBaolin Wang power_supply_changed(data->battery); 939195ca170SBaolin Wang return IRQ_HANDLED; 940195ca170SBaolin Wang } 941195ca170SBaolin Wang 942195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data) 943195ca170SBaolin Wang { 944195ca170SBaolin Wang struct sc27xx_fgu_data *data = _data; 945195ca170SBaolin Wang 946195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 947195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 948195ca170SBaolin Wang } 949195ca170SBaolin Wang 950195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity) 951195ca170SBaolin Wang { 952195ca170SBaolin Wang /* 953195ca170SBaolin Wang * Get current capacity (mAh) = battery total capacity (mAh) * 954195ca170SBaolin Wang * current capacity percent (capacity / 100). 955195ca170SBaolin Wang */ 956195ca170SBaolin Wang int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100); 957195ca170SBaolin Wang 958195ca170SBaolin Wang /* 959195ca170SBaolin Wang * Convert current capacity (mAh) to coulomb counter according to the 960195ca170SBaolin Wang * formula: 1 mAh =3.6 coulomb. 961195ca170SBaolin Wang */ 9627384b0e7SYuanjiang Yu return DIV_ROUND_CLOSEST(cur_cap * 36 * data->cur_1000ma_adc * SC27XX_FGU_SAMPLE_HZ, 10); 963195ca170SBaolin Wang } 964195ca170SBaolin Wang 96565c9fab7SBaolin Wang static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) 96665c9fab7SBaolin Wang { 96765c9fab7SBaolin Wang struct nvmem_cell *cell; 96865c9fab7SBaolin Wang int calib_data, cal_4200mv; 96965c9fab7SBaolin Wang void *buf; 97065c9fab7SBaolin Wang size_t len; 97165c9fab7SBaolin Wang 97265c9fab7SBaolin Wang cell = nvmem_cell_get(data->dev, "fgu_calib"); 97365c9fab7SBaolin Wang if (IS_ERR(cell)) 97465c9fab7SBaolin Wang return PTR_ERR(cell); 97565c9fab7SBaolin Wang 97665c9fab7SBaolin Wang buf = nvmem_cell_read(cell, &len); 97765c9fab7SBaolin Wang nvmem_cell_put(cell); 97865c9fab7SBaolin Wang 97965c9fab7SBaolin Wang if (IS_ERR(buf)) 98065c9fab7SBaolin Wang return PTR_ERR(buf); 98165c9fab7SBaolin Wang 98265c9fab7SBaolin Wang memcpy(&calib_data, buf, min(len, sizeof(u32))); 98365c9fab7SBaolin Wang 98465c9fab7SBaolin Wang /* 98565c9fab7SBaolin Wang * Get the ADC value corresponding to 4200 mV from eFuse controller 98665c9fab7SBaolin Wang * according to below formula. Then convert to ADC values corresponding 98765c9fab7SBaolin Wang * to 1000 mV and 1000 mA. 98865c9fab7SBaolin Wang */ 98965c9fab7SBaolin Wang cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256; 99065c9fab7SBaolin Wang data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42); 991058d4256SBaolin Wang data->cur_1000ma_adc = 992058d4256SBaolin Wang DIV_ROUND_CLOSEST(data->vol_1000mv_adc * 4 * data->calib_resist, 993058d4256SBaolin Wang SC27XX_FGU_IDEAL_RESISTANCE); 99465c9fab7SBaolin Wang 99565c9fab7SBaolin Wang kfree(buf); 99665c9fab7SBaolin Wang return 0; 99765c9fab7SBaolin Wang } 99865c9fab7SBaolin Wang 999195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) 1000195ca170SBaolin Wang { 1001195ca170SBaolin Wang struct power_supply_battery_info info = { }; 1002195ca170SBaolin Wang struct power_supply_battery_ocv_table *table; 1003edcb1c0aSYuanjiang Yu int ret, delta_clbcnt, alarm_adc; 1004195ca170SBaolin Wang 1005195ca170SBaolin Wang ret = power_supply_get_battery_info(data->battery, &info); 1006195ca170SBaolin Wang if (ret) { 1007195ca170SBaolin Wang dev_err(data->dev, "failed to get battery information\n"); 1008195ca170SBaolin Wang return ret; 1009195ca170SBaolin Wang } 1010195ca170SBaolin Wang 1011195ca170SBaolin Wang data->total_cap = info.charge_full_design_uah / 1000; 1012195ca170SBaolin Wang data->max_volt = info.constant_charge_voltage_max_uv / 1000; 1013195ca170SBaolin Wang data->internal_resist = info.factory_internal_resistance_uohm / 1000; 1014edcb1c0aSYuanjiang Yu data->min_volt = info.voltage_min_design_uv; 1015195ca170SBaolin Wang 1016195ca170SBaolin Wang /* 1017195ca170SBaolin Wang * For SC27XX fuel gauge device, we only use one ocv-capacity 1018195ca170SBaolin Wang * table in normal temperature 20 Celsius. 1019195ca170SBaolin Wang */ 1020195ca170SBaolin Wang table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len); 1021195ca170SBaolin Wang if (!table) 1022195ca170SBaolin Wang return -EINVAL; 1023195ca170SBaolin Wang 1024195ca170SBaolin Wang data->cap_table = devm_kmemdup(data->dev, table, 1025195ca170SBaolin Wang data->table_len * sizeof(*table), 1026195ca170SBaolin Wang GFP_KERNEL); 1027195ca170SBaolin Wang if (!data->cap_table) { 1028195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 1029195ca170SBaolin Wang return -ENOMEM; 1030195ca170SBaolin Wang } 1031195ca170SBaolin Wang 1032edcb1c0aSYuanjiang Yu data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, 1033edcb1c0aSYuanjiang Yu data->table_len, 1034edcb1c0aSYuanjiang Yu data->min_volt); 1035ff062d06SYuanjiang Yu if (!data->alarm_cap) 1036ff062d06SYuanjiang Yu data->alarm_cap += 1; 1037edcb1c0aSYuanjiang Yu 10386af82888SYuanjiang Yu data->resist_table_len = info.resist_table_size; 10396af82888SYuanjiang Yu if (data->resist_table_len > 0) { 10406af82888SYuanjiang Yu data->resist_table = devm_kmemdup(data->dev, info.resist_table, 10416af82888SYuanjiang Yu data->resist_table_len * 10426af82888SYuanjiang Yu sizeof(struct power_supply_resistance_temp_table), 10436af82888SYuanjiang Yu GFP_KERNEL); 10446af82888SYuanjiang Yu if (!data->resist_table) { 10456af82888SYuanjiang Yu power_supply_put_battery_info(data->battery, &info); 10466af82888SYuanjiang Yu return -ENOMEM; 10476af82888SYuanjiang Yu } 10486af82888SYuanjiang Yu } 10496af82888SYuanjiang Yu 1050195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 1051195ca170SBaolin Wang 105265c9fab7SBaolin Wang ret = sc27xx_fgu_calibration(data); 105365c9fab7SBaolin Wang if (ret) 105465c9fab7SBaolin Wang return ret; 105565c9fab7SBaolin Wang 1056195ca170SBaolin Wang /* Enable the FGU module */ 1057195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, 1058195ca170SBaolin Wang SC27XX_FGU_EN, SC27XX_FGU_EN); 1059195ca170SBaolin Wang if (ret) { 1060195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu\n"); 1061195ca170SBaolin Wang return ret; 1062195ca170SBaolin Wang } 1063195ca170SBaolin Wang 1064195ca170SBaolin Wang /* Enable the FGU RTC clock to make it work */ 1065195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0, 1066195ca170SBaolin Wang SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN); 1067195ca170SBaolin Wang if (ret) { 1068195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu RTC clock\n"); 1069195ca170SBaolin Wang goto disable_fgu; 1070195ca170SBaolin Wang } 1071195ca170SBaolin Wang 1072edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 1073edcb1c0aSYuanjiang Yu SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK); 1074edcb1c0aSYuanjiang Yu if (ret) { 1075edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to clear interrupt status\n"); 1076edcb1c0aSYuanjiang Yu goto disable_clk; 1077edcb1c0aSYuanjiang Yu } 1078edcb1c0aSYuanjiang Yu 1079edcb1c0aSYuanjiang Yu /* 1080edcb1c0aSYuanjiang Yu * Set the voltage low overload threshold, which means when the battery 1081edcb1c0aSYuanjiang Yu * voltage is lower than this threshold, the controller will generate 1082edcb1c0aSYuanjiang Yu * one interrupt to notify. 1083edcb1c0aSYuanjiang Yu */ 1084edcb1c0aSYuanjiang Yu alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 1085edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, 1086edcb1c0aSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc); 1087edcb1c0aSYuanjiang Yu if (ret) { 1088edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set fgu low overload\n"); 1089edcb1c0aSYuanjiang Yu goto disable_clk; 1090edcb1c0aSYuanjiang Yu } 1091edcb1c0aSYuanjiang Yu 1092edcb1c0aSYuanjiang Yu /* 1093edcb1c0aSYuanjiang Yu * Set the coulomb counter delta threshold, that means when the coulomb 1094edcb1c0aSYuanjiang Yu * counter change is multiples of the delta threshold, the controller 1095edcb1c0aSYuanjiang Yu * will generate one interrupt to notify the users to update the battery 1096edcb1c0aSYuanjiang Yu * capacity. Now we set the delta threshold as a counter value of 1% 1097edcb1c0aSYuanjiang Yu * capacity. 1098edcb1c0aSYuanjiang Yu */ 1099edcb1c0aSYuanjiang Yu delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1); 1100edcb1c0aSYuanjiang Yu 1101edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL, 1102edcb1c0aSYuanjiang Yu SC27XX_FGU_CLBCNT_MASK, delta_clbcnt); 1103edcb1c0aSYuanjiang Yu if (ret) { 1104edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set low delta coulomb counter\n"); 1105edcb1c0aSYuanjiang Yu goto disable_clk; 1106edcb1c0aSYuanjiang Yu } 1107edcb1c0aSYuanjiang Yu 1108edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH, 1109edcb1c0aSYuanjiang Yu SC27XX_FGU_CLBCNT_MASK, 1110edcb1c0aSYuanjiang Yu delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 1111edcb1c0aSYuanjiang Yu if (ret) { 1112edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set high delta coulomb counter\n"); 1113edcb1c0aSYuanjiang Yu goto disable_clk; 1114edcb1c0aSYuanjiang Yu } 1115edcb1c0aSYuanjiang Yu 1116195ca170SBaolin Wang /* 1117195ca170SBaolin Wang * Get the boot battery capacity when system powers on, which is used to 1118195ca170SBaolin Wang * initialize the coulomb counter. After that, we can read the coulomb 1119195ca170SBaolin Wang * counter to measure the battery capacity. 1120195ca170SBaolin Wang */ 1121195ca170SBaolin Wang ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap); 1122195ca170SBaolin Wang if (ret) { 1123195ca170SBaolin Wang dev_err(data->dev, "failed to get boot capacity\n"); 1124195ca170SBaolin Wang goto disable_clk; 1125195ca170SBaolin Wang } 1126195ca170SBaolin Wang 1127195ca170SBaolin Wang /* 1128195ca170SBaolin Wang * Convert battery capacity to the corresponding initial coulomb counter 1129195ca170SBaolin Wang * and set into coulomb counter registers. 1130195ca170SBaolin Wang */ 1131195ca170SBaolin Wang data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); 1132195ca170SBaolin Wang ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt); 1133195ca170SBaolin Wang if (ret) { 1134195ca170SBaolin Wang dev_err(data->dev, "failed to initialize coulomb counter\n"); 1135195ca170SBaolin Wang goto disable_clk; 1136195ca170SBaolin Wang } 1137195ca170SBaolin Wang 1138195ca170SBaolin Wang return 0; 1139195ca170SBaolin Wang 1140195ca170SBaolin Wang disable_clk: 1141195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 1142195ca170SBaolin Wang disable_fgu: 1143195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 1144195ca170SBaolin Wang 1145195ca170SBaolin Wang return ret; 1146195ca170SBaolin Wang } 1147195ca170SBaolin Wang 1148195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev) 1149195ca170SBaolin Wang { 115008614b40SFuqian Huang struct device *dev = &pdev->dev; 115108614b40SFuqian Huang struct device_node *np = dev->of_node; 1152195ca170SBaolin Wang struct power_supply_config fgu_cfg = { }; 1153195ca170SBaolin Wang struct sc27xx_fgu_data *data; 1154195ca170SBaolin Wang int ret, irq; 1155195ca170SBaolin Wang 115608614b40SFuqian Huang data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 1157195ca170SBaolin Wang if (!data) 1158195ca170SBaolin Wang return -ENOMEM; 1159195ca170SBaolin Wang 116008614b40SFuqian Huang data->regmap = dev_get_regmap(dev->parent, NULL); 1161195ca170SBaolin Wang if (!data->regmap) { 116208614b40SFuqian Huang dev_err(dev, "failed to get regmap\n"); 1163195ca170SBaolin Wang return -ENODEV; 1164195ca170SBaolin Wang } 1165195ca170SBaolin Wang 116608614b40SFuqian Huang ret = device_property_read_u32(dev, "reg", &data->base); 1167195ca170SBaolin Wang if (ret) { 116808614b40SFuqian Huang dev_err(dev, "failed to get fgu address\n"); 1169195ca170SBaolin Wang return ret; 1170195ca170SBaolin Wang } 1171195ca170SBaolin Wang 1172058d4256SBaolin Wang ret = device_property_read_u32(&pdev->dev, 1173058d4256SBaolin Wang "sprd,calib-resistance-micro-ohms", 1174058d4256SBaolin Wang &data->calib_resist); 1175058d4256SBaolin Wang if (ret) { 1176058d4256SBaolin Wang dev_err(&pdev->dev, 1177058d4256SBaolin Wang "failed to get fgu calibration resistance\n"); 1178058d4256SBaolin Wang return ret; 1179058d4256SBaolin Wang } 1180058d4256SBaolin Wang 118108614b40SFuqian Huang data->channel = devm_iio_channel_get(dev, "bat-temp"); 1182195ca170SBaolin Wang if (IS_ERR(data->channel)) { 118308614b40SFuqian Huang dev_err(dev, "failed to get IIO channel\n"); 1184195ca170SBaolin Wang return PTR_ERR(data->channel); 1185195ca170SBaolin Wang } 1186195ca170SBaolin Wang 118708614b40SFuqian Huang data->charge_chan = devm_iio_channel_get(dev, "charge-vol"); 11880a4f97a1SBaolin Wang if (IS_ERR(data->charge_chan)) { 118908614b40SFuqian Huang dev_err(dev, "failed to get charge IIO channel\n"); 11900a4f97a1SBaolin Wang return PTR_ERR(data->charge_chan); 11910a4f97a1SBaolin Wang } 11920a4f97a1SBaolin Wang 119308614b40SFuqian Huang data->gpiod = devm_gpiod_get(dev, "bat-detect", GPIOD_IN); 1194195ca170SBaolin Wang if (IS_ERR(data->gpiod)) { 119508614b40SFuqian Huang dev_err(dev, "failed to get battery detection GPIO\n"); 1196195ca170SBaolin Wang return PTR_ERR(data->gpiod); 1197195ca170SBaolin Wang } 1198195ca170SBaolin Wang 1199195ca170SBaolin Wang ret = gpiod_get_value_cansleep(data->gpiod); 1200195ca170SBaolin Wang if (ret < 0) { 120108614b40SFuqian Huang dev_err(dev, "failed to get gpio state\n"); 1202195ca170SBaolin Wang return ret; 1203195ca170SBaolin Wang } 1204195ca170SBaolin Wang 1205195ca170SBaolin Wang data->bat_present = !!ret; 1206195ca170SBaolin Wang mutex_init(&data->lock); 120708614b40SFuqian Huang data->dev = dev; 1208e2fb615bSYuanjiang Yu platform_set_drvdata(pdev, data); 1209195ca170SBaolin Wang 1210195ca170SBaolin Wang fgu_cfg.drv_data = data; 1211195ca170SBaolin Wang fgu_cfg.of_node = np; 121208614b40SFuqian Huang data->battery = devm_power_supply_register(dev, &sc27xx_fgu_desc, 1213195ca170SBaolin Wang &fgu_cfg); 1214195ca170SBaolin Wang if (IS_ERR(data->battery)) { 121508614b40SFuqian Huang dev_err(dev, "failed to register power supply\n"); 1216195ca170SBaolin Wang return PTR_ERR(data->battery); 1217195ca170SBaolin Wang } 1218195ca170SBaolin Wang 1219195ca170SBaolin Wang ret = sc27xx_fgu_hw_init(data); 1220195ca170SBaolin Wang if (ret) { 122108614b40SFuqian Huang dev_err(dev, "failed to initialize fgu hardware\n"); 1222195ca170SBaolin Wang return ret; 1223195ca170SBaolin Wang } 1224195ca170SBaolin Wang 122520420583SFuqian Huang ret = devm_add_action_or_reset(dev, sc27xx_fgu_disable, data); 1226195ca170SBaolin Wang if (ret) { 122708614b40SFuqian Huang dev_err(dev, "failed to add fgu disable action\n"); 1228195ca170SBaolin Wang return ret; 1229195ca170SBaolin Wang } 1230195ca170SBaolin Wang 1231edcb1c0aSYuanjiang Yu irq = platform_get_irq(pdev, 0); 1232edcb1c0aSYuanjiang Yu if (irq < 0) { 123308614b40SFuqian Huang dev_err(dev, "no irq resource specified\n"); 1234edcb1c0aSYuanjiang Yu return irq; 1235edcb1c0aSYuanjiang Yu } 1236edcb1c0aSYuanjiang Yu 1237edcb1c0aSYuanjiang Yu ret = devm_request_threaded_irq(data->dev, irq, NULL, 1238edcb1c0aSYuanjiang Yu sc27xx_fgu_interrupt, 1239edcb1c0aSYuanjiang Yu IRQF_NO_SUSPEND | IRQF_ONESHOT, 1240edcb1c0aSYuanjiang Yu pdev->name, data); 1241edcb1c0aSYuanjiang Yu if (ret) { 1242edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to request fgu IRQ\n"); 1243edcb1c0aSYuanjiang Yu return ret; 1244edcb1c0aSYuanjiang Yu } 1245edcb1c0aSYuanjiang Yu 1246195ca170SBaolin Wang irq = gpiod_to_irq(data->gpiod); 1247195ca170SBaolin Wang if (irq < 0) { 124808614b40SFuqian Huang dev_err(dev, "failed to translate GPIO to IRQ\n"); 1249195ca170SBaolin Wang return irq; 1250195ca170SBaolin Wang } 1251195ca170SBaolin Wang 125208614b40SFuqian Huang ret = devm_request_threaded_irq(dev, irq, NULL, 1253195ca170SBaolin Wang sc27xx_fgu_bat_detection, 1254195ca170SBaolin Wang IRQF_ONESHOT | IRQF_TRIGGER_RISING | 1255195ca170SBaolin Wang IRQF_TRIGGER_FALLING, 1256195ca170SBaolin Wang pdev->name, data); 1257195ca170SBaolin Wang if (ret) { 125808614b40SFuqian Huang dev_err(dev, "failed to request IRQ\n"); 1259195ca170SBaolin Wang return ret; 1260195ca170SBaolin Wang } 1261195ca170SBaolin Wang 1262195ca170SBaolin Wang return 0; 1263195ca170SBaolin Wang } 1264195ca170SBaolin Wang 1265e2fb615bSYuanjiang Yu #ifdef CONFIG_PM_SLEEP 1266e2fb615bSYuanjiang Yu static int sc27xx_fgu_resume(struct device *dev) 1267e2fb615bSYuanjiang Yu { 1268e2fb615bSYuanjiang Yu struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 1269e2fb615bSYuanjiang Yu int ret; 1270e2fb615bSYuanjiang Yu 1271e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1272e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT | 1273e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT, 0); 1274e2fb615bSYuanjiang Yu if (ret) { 1275e2fb615bSYuanjiang Yu dev_err(data->dev, "failed to disable fgu interrupts\n"); 1276e2fb615bSYuanjiang Yu return ret; 1277e2fb615bSYuanjiang Yu } 1278e2fb615bSYuanjiang Yu 1279e2fb615bSYuanjiang Yu return 0; 1280e2fb615bSYuanjiang Yu } 1281e2fb615bSYuanjiang Yu 1282e2fb615bSYuanjiang Yu static int sc27xx_fgu_suspend(struct device *dev) 1283e2fb615bSYuanjiang Yu { 1284e2fb615bSYuanjiang Yu struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 1285e2fb615bSYuanjiang Yu int ret, status, ocv; 1286e2fb615bSYuanjiang Yu 1287e2fb615bSYuanjiang Yu ret = sc27xx_fgu_get_status(data, &status); 1288e2fb615bSYuanjiang Yu if (ret) 1289e2fb615bSYuanjiang Yu return ret; 1290e2fb615bSYuanjiang Yu 1291e2fb615bSYuanjiang Yu /* 1292e2fb615bSYuanjiang Yu * If we are charging, then no need to enable the FGU interrupts to 1293e2fb615bSYuanjiang Yu * adjust the battery capacity. 1294e2fb615bSYuanjiang Yu */ 1295168e68d0SYuanjiang Yu if (status != POWER_SUPPLY_STATUS_NOT_CHARGING && 1296168e68d0SYuanjiang Yu status != POWER_SUPPLY_STATUS_DISCHARGING) 1297e2fb615bSYuanjiang Yu return 0; 1298e2fb615bSYuanjiang Yu 1299e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1300e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT, 1301e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT); 1302e2fb615bSYuanjiang Yu if (ret) { 1303e2fb615bSYuanjiang Yu dev_err(data->dev, "failed to enable low voltage interrupt\n"); 1304e2fb615bSYuanjiang Yu return ret; 1305e2fb615bSYuanjiang Yu } 1306e2fb615bSYuanjiang Yu 1307e2fb615bSYuanjiang Yu ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 1308e2fb615bSYuanjiang Yu if (ret) 1309e2fb615bSYuanjiang Yu goto disable_int; 1310e2fb615bSYuanjiang Yu 1311e2fb615bSYuanjiang Yu /* 1312e2fb615bSYuanjiang Yu * If current OCV is less than the minimum voltage, we should enable the 1313e2fb615bSYuanjiang Yu * coulomb counter threshold interrupt to notify events to adjust the 1314e2fb615bSYuanjiang Yu * battery capacity. 1315e2fb615bSYuanjiang Yu */ 1316e2fb615bSYuanjiang Yu if (ocv < data->min_volt) { 1317e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, 1318e2fb615bSYuanjiang Yu data->base + SC27XX_FGU_INT_EN, 1319e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT, 1320e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT); 1321e2fb615bSYuanjiang Yu if (ret) { 1322e2fb615bSYuanjiang Yu dev_err(data->dev, 1323e2fb615bSYuanjiang Yu "failed to enable coulomb threshold int\n"); 1324e2fb615bSYuanjiang Yu goto disable_int; 1325e2fb615bSYuanjiang Yu } 1326e2fb615bSYuanjiang Yu } 1327e2fb615bSYuanjiang Yu 1328e2fb615bSYuanjiang Yu return 0; 1329e2fb615bSYuanjiang Yu 1330e2fb615bSYuanjiang Yu disable_int: 1331e2fb615bSYuanjiang Yu regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1332e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT, 0); 1333e2fb615bSYuanjiang Yu return ret; 1334e2fb615bSYuanjiang Yu } 1335e2fb615bSYuanjiang Yu #endif 1336e2fb615bSYuanjiang Yu 1337e2fb615bSYuanjiang Yu static const struct dev_pm_ops sc27xx_fgu_pm_ops = { 1338e2fb615bSYuanjiang Yu SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume) 1339e2fb615bSYuanjiang Yu }; 1340e2fb615bSYuanjiang Yu 1341195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = { 1342195ca170SBaolin Wang { .compatible = "sprd,sc2731-fgu", }, 1343195ca170SBaolin Wang { } 1344195ca170SBaolin Wang }; 1345*603fcfb9SZou Wei MODULE_DEVICE_TABLE(of, sc27xx_fgu_of_match); 1346195ca170SBaolin Wang 1347195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = { 1348195ca170SBaolin Wang .probe = sc27xx_fgu_probe, 1349195ca170SBaolin Wang .driver = { 1350195ca170SBaolin Wang .name = "sc27xx-fgu", 1351195ca170SBaolin Wang .of_match_table = sc27xx_fgu_of_match, 1352e2fb615bSYuanjiang Yu .pm = &sc27xx_fgu_pm_ops, 1353195ca170SBaolin Wang } 1354195ca170SBaolin Wang }; 1355195ca170SBaolin Wang 1356195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver); 1357195ca170SBaolin Wang 1358195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver"); 1359195ca170SBaolin Wang MODULE_LICENSE("GPL v2"); 1360