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> 8195ca170SBaolin Wang #include <linux/module.h> 965c9fab7SBaolin Wang #include <linux/nvmem-consumer.h> 10195ca170SBaolin Wang #include <linux/of.h> 11195ca170SBaolin Wang #include <linux/platform_device.h> 12195ca170SBaolin Wang #include <linux/power_supply.h> 13195ca170SBaolin Wang #include <linux/regmap.h> 1465c9fab7SBaolin Wang #include <linux/slab.h> 15195ca170SBaolin Wang 16195ca170SBaolin Wang /* PMIC global control registers definition */ 17195ca170SBaolin Wang #define SC27XX_MODULE_EN0 0xc08 18195ca170SBaolin Wang #define SC27XX_CLK_EN0 0xc18 19195ca170SBaolin Wang #define SC27XX_FGU_EN BIT(7) 20195ca170SBaolin Wang #define SC27XX_FGU_RTC_EN BIT(6) 21195ca170SBaolin Wang 22195ca170SBaolin Wang /* FGU registers definition */ 23195ca170SBaolin Wang #define SC27XX_FGU_START 0x0 24195ca170SBaolin Wang #define SC27XX_FGU_CONFIG 0x4 25195ca170SBaolin Wang #define SC27XX_FGU_ADC_CONFIG 0x8 26195ca170SBaolin Wang #define SC27XX_FGU_STATUS 0xc 27195ca170SBaolin Wang #define SC27XX_FGU_INT_EN 0x10 28195ca170SBaolin Wang #define SC27XX_FGU_INT_CLR 0x14 29195ca170SBaolin Wang #define SC27XX_FGU_INT_STS 0x1c 30195ca170SBaolin Wang #define SC27XX_FGU_VOLTAGE 0x20 31195ca170SBaolin Wang #define SC27XX_FGU_OCV 0x24 32195ca170SBaolin Wang #define SC27XX_FGU_POCV 0x28 33195ca170SBaolin Wang #define SC27XX_FGU_CURRENT 0x2c 34edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD 0x34 35195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETH 0x50 36195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETL 0x54 37edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTH 0x58 38edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTL 0x5c 39195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALH 0x68 40195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALL 0x6c 41195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_QMAXL 0x74 424a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_SET 0xa0 434a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_CLEAR 0xa4 444a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_STATUS 0xa8 45195ca170SBaolin Wang 46195ca170SBaolin Wang #define SC27XX_WRITE_SELCLB_EN BIT(0) 47195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0) 48195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SHIFT 16 49edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD_MASK GENMASK(12, 0) 50edcb1c0aSYuanjiang Yu 51edcb1c0aSYuanjiang Yu #define SC27XX_FGU_INT_MASK GENMASK(9, 0) 52edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD_INT BIT(0) 53edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTA_INT BIT(2) 54195ca170SBaolin Wang 554a040e7cSYuanjiang Yu #define SC27XX_FGU_MODE_AREA_MASK GENMASK(15, 12) 564a040e7cSYuanjiang Yu #define SC27XX_FGU_CAP_AREA_MASK GENMASK(11, 0) 574a040e7cSYuanjiang Yu #define SC27XX_FGU_MODE_AREA_SHIFT 12 584a040e7cSYuanjiang Yu 594a040e7cSYuanjiang Yu #define SC27XX_FGU_FIRST_POWERTON GENMASK(3, 0) 604a040e7cSYuanjiang Yu #define SC27XX_FGU_DEFAULT_CAP GENMASK(11, 0) 614a040e7cSYuanjiang Yu #define SC27XX_FGU_NORMAIL_POWERTON 0x5 624a040e7cSYuanjiang Yu 63195ca170SBaolin Wang #define SC27XX_FGU_CUR_BASIC_ADC 8192 64195ca170SBaolin Wang #define SC27XX_FGU_SAMPLE_HZ 2 65058d4256SBaolin Wang /* micro Ohms */ 66058d4256SBaolin Wang #define SC27XX_FGU_IDEAL_RESISTANCE 20000 67195ca170SBaolin Wang 68195ca170SBaolin Wang /* 69195ca170SBaolin Wang * struct sc27xx_fgu_data: describe the FGU device 70195ca170SBaolin Wang * @regmap: regmap for register access 71195ca170SBaolin Wang * @dev: platform device 72195ca170SBaolin Wang * @battery: battery power supply 73195ca170SBaolin Wang * @base: the base offset for the controller 74195ca170SBaolin Wang * @lock: protect the structure 75195ca170SBaolin Wang * @gpiod: GPIO for battery detection 76195ca170SBaolin Wang * @channel: IIO channel to get battery temperature 770a4f97a1SBaolin Wang * @charge_chan: IIO channel to get charge voltage 78195ca170SBaolin Wang * @internal_resist: the battery internal resistance in mOhm 79195ca170SBaolin Wang * @total_cap: the total capacity of the battery in mAh 80195ca170SBaolin Wang * @init_cap: the initial capacity of the battery in mAh 81edcb1c0aSYuanjiang Yu * @alarm_cap: the alarm capacity 82195ca170SBaolin Wang * @init_clbcnt: the initial coulomb counter 83195ca170SBaolin Wang * @max_volt: the maximum constant input voltage in millivolt 84edcb1c0aSYuanjiang Yu * @min_volt: the minimum drained battery voltage in microvolt 85195ca170SBaolin Wang * @table_len: the capacity table length 866af82888SYuanjiang Yu * @resist_table_len: the resistance table length 8765c9fab7SBaolin Wang * @cur_1000ma_adc: ADC value corresponding to 1000 mA 8865c9fab7SBaolin Wang * @vol_1000mv_adc: ADC value corresponding to 1000 mV 89058d4256SBaolin Wang * @calib_resist: the real resistance of coulomb counter chip in uOhm 90195ca170SBaolin Wang * @cap_table: capacity table with corresponding ocv 916af82888SYuanjiang Yu * @resist_table: resistance percent table with corresponding temperature 92195ca170SBaolin Wang */ 93195ca170SBaolin Wang struct sc27xx_fgu_data { 94195ca170SBaolin Wang struct regmap *regmap; 95195ca170SBaolin Wang struct device *dev; 96195ca170SBaolin Wang struct power_supply *battery; 97195ca170SBaolin Wang u32 base; 98195ca170SBaolin Wang struct mutex lock; 99195ca170SBaolin Wang struct gpio_desc *gpiod; 100195ca170SBaolin Wang struct iio_channel *channel; 1010a4f97a1SBaolin Wang struct iio_channel *charge_chan; 102195ca170SBaolin Wang bool bat_present; 103195ca170SBaolin Wang int internal_resist; 104195ca170SBaolin Wang int total_cap; 105195ca170SBaolin Wang int init_cap; 106edcb1c0aSYuanjiang Yu int alarm_cap; 107195ca170SBaolin Wang int init_clbcnt; 108195ca170SBaolin Wang int max_volt; 109edcb1c0aSYuanjiang Yu int min_volt; 110195ca170SBaolin Wang int table_len; 1116af82888SYuanjiang Yu int resist_table_len; 11265c9fab7SBaolin Wang int cur_1000ma_adc; 11365c9fab7SBaolin Wang int vol_1000mv_adc; 114058d4256SBaolin Wang int calib_resist; 115195ca170SBaolin Wang struct power_supply_battery_ocv_table *cap_table; 1166af82888SYuanjiang Yu struct power_supply_resistance_temp_table *resist_table; 117195ca170SBaolin Wang }; 118195ca170SBaolin Wang 119edcb1c0aSYuanjiang Yu static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity); 12058066527SYuanjiang Yu static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, 12158066527SYuanjiang Yu int cap, bool int_mode); 1227cfd33d9SYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap); 1236af82888SYuanjiang Yu static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp); 124edcb1c0aSYuanjiang Yu 125195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = { 126195ca170SBaolin Wang "sc2731_charger", 127195ca170SBaolin Wang "sc2720_charger", 128195ca170SBaolin Wang "sc2721_charger", 129195ca170SBaolin Wang "sc2723_charger", 130195ca170SBaolin Wang }; 131195ca170SBaolin Wang 13265c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc) 133195ca170SBaolin Wang { 13465c9fab7SBaolin Wang return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc); 135195ca170SBaolin Wang } 136195ca170SBaolin Wang 13765c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc) 138195ca170SBaolin Wang { 13965c9fab7SBaolin Wang return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); 140195ca170SBaolin Wang } 141195ca170SBaolin Wang 142edcb1c0aSYuanjiang Yu static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol) 143edcb1c0aSYuanjiang Yu { 144edcb1c0aSYuanjiang Yu return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000); 145edcb1c0aSYuanjiang Yu } 146edcb1c0aSYuanjiang Yu 1474a040e7cSYuanjiang Yu static bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data) 1484a040e7cSYuanjiang Yu { 1494a040e7cSYuanjiang Yu int ret, status, cap, mode; 1504a040e7cSYuanjiang Yu 1514a040e7cSYuanjiang Yu ret = regmap_read(data->regmap, 1524a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_STATUS, &status); 1534a040e7cSYuanjiang Yu if (ret) 1544a040e7cSYuanjiang Yu return false; 1554a040e7cSYuanjiang Yu 1564a040e7cSYuanjiang Yu /* 1574a040e7cSYuanjiang Yu * We use low 4 bits to save the last battery capacity and high 12 bits 1584a040e7cSYuanjiang Yu * to save the system boot mode. 1594a040e7cSYuanjiang Yu */ 1604a040e7cSYuanjiang Yu mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT; 1614a040e7cSYuanjiang Yu cap = status & SC27XX_FGU_CAP_AREA_MASK; 1624a040e7cSYuanjiang Yu 1634a040e7cSYuanjiang Yu /* 1644a040e7cSYuanjiang Yu * When FGU has been powered down, the user area registers became 1654a040e7cSYuanjiang Yu * default value (0xffff), which can be used to valid if the system is 1664a040e7cSYuanjiang Yu * first power on or not. 1674a040e7cSYuanjiang Yu */ 1684a040e7cSYuanjiang Yu if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP) 1694a040e7cSYuanjiang Yu return true; 1704a040e7cSYuanjiang Yu 1714a040e7cSYuanjiang Yu return false; 1724a040e7cSYuanjiang Yu } 1734a040e7cSYuanjiang Yu 1744a040e7cSYuanjiang Yu static int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data, 1754a040e7cSYuanjiang Yu int boot_mode) 1764a040e7cSYuanjiang Yu { 1774a040e7cSYuanjiang Yu int ret; 1784a040e7cSYuanjiang Yu 1794a040e7cSYuanjiang Yu ret = regmap_update_bits(data->regmap, 1804a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 1814a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 1824a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK); 1834a040e7cSYuanjiang Yu if (ret) 1844a040e7cSYuanjiang Yu return ret; 1854a040e7cSYuanjiang Yu 186d3e67c94SYuanjiang Yu /* 187d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 188d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 189d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 190d3e67c94SYuanjiang Yu * successfully according to the datasheet. 191d3e67c94SYuanjiang Yu */ 192d3e67c94SYuanjiang Yu udelay(200); 193d3e67c94SYuanjiang Yu 194d3e67c94SYuanjiang Yu ret = regmap_update_bits(data->regmap, 1954a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_SET, 1964a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 1974a040e7cSYuanjiang Yu boot_mode << SC27XX_FGU_MODE_AREA_SHIFT); 198d3e67c94SYuanjiang Yu if (ret) 199d3e67c94SYuanjiang Yu return ret; 200d3e67c94SYuanjiang Yu 201d3e67c94SYuanjiang Yu /* 202d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 203d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 204d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 205d3e67c94SYuanjiang Yu * successfully according to the datasheet. 206d3e67c94SYuanjiang Yu */ 207d3e67c94SYuanjiang Yu udelay(200); 208d3e67c94SYuanjiang Yu 209d3e67c94SYuanjiang Yu /* 210d3e67c94SYuanjiang Yu * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to 211d3e67c94SYuanjiang Yu * make the user area data available, otherwise we can not save the user 212d3e67c94SYuanjiang Yu * area data. 213d3e67c94SYuanjiang Yu */ 214d3e67c94SYuanjiang Yu return regmap_update_bits(data->regmap, 215d3e67c94SYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 216d3e67c94SYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 0); 2174a040e7cSYuanjiang Yu } 2184a040e7cSYuanjiang Yu 2194a040e7cSYuanjiang Yu static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap) 2204a040e7cSYuanjiang Yu { 2214a040e7cSYuanjiang Yu int ret; 2224a040e7cSYuanjiang Yu 2234a040e7cSYuanjiang Yu ret = regmap_update_bits(data->regmap, 2244a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 2254a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, 2264a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK); 2274a040e7cSYuanjiang Yu if (ret) 2284a040e7cSYuanjiang Yu return ret; 2294a040e7cSYuanjiang Yu 230d3e67c94SYuanjiang Yu /* 231d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 232d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 233d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 234d3e67c94SYuanjiang Yu * successfully according to the datasheet. 235d3e67c94SYuanjiang Yu */ 236d3e67c94SYuanjiang Yu udelay(200); 237d3e67c94SYuanjiang Yu 238d3e67c94SYuanjiang Yu ret = regmap_update_bits(data->regmap, 2394a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_SET, 2404a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, cap); 241d3e67c94SYuanjiang Yu if (ret) 242d3e67c94SYuanjiang Yu return ret; 243d3e67c94SYuanjiang Yu 244d3e67c94SYuanjiang Yu /* 245d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 246d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 247d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 248d3e67c94SYuanjiang Yu * successfully according to the datasheet. 249d3e67c94SYuanjiang Yu */ 250d3e67c94SYuanjiang Yu udelay(200); 251d3e67c94SYuanjiang Yu 252d3e67c94SYuanjiang Yu /* 253d3e67c94SYuanjiang Yu * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to 254d3e67c94SYuanjiang Yu * make the user area data available, otherwise we can not save the user 255d3e67c94SYuanjiang Yu * area data. 256d3e67c94SYuanjiang Yu */ 257d3e67c94SYuanjiang Yu return regmap_update_bits(data->regmap, 258d3e67c94SYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 259d3e67c94SYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, 0); 2604a040e7cSYuanjiang Yu } 2614a040e7cSYuanjiang Yu 2624a040e7cSYuanjiang Yu static int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap) 2634a040e7cSYuanjiang Yu { 2644a040e7cSYuanjiang Yu int ret, value; 2654a040e7cSYuanjiang Yu 2664a040e7cSYuanjiang Yu ret = regmap_read(data->regmap, 2674a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_STATUS, &value); 2684a040e7cSYuanjiang Yu if (ret) 2694a040e7cSYuanjiang Yu return ret; 2704a040e7cSYuanjiang Yu 2714a040e7cSYuanjiang Yu *cap = value & SC27XX_FGU_CAP_AREA_MASK; 2724a040e7cSYuanjiang Yu return 0; 2734a040e7cSYuanjiang Yu } 2744a040e7cSYuanjiang Yu 275195ca170SBaolin Wang /* 276195ca170SBaolin Wang * When system boots on, we can not read battery capacity from coulomb 277195ca170SBaolin Wang * registers, since now the coulomb registers are invalid. So we should 278195ca170SBaolin Wang * calculate the battery open circuit voltage, and get current battery 279195ca170SBaolin Wang * capacity according to the capacity table. 280195ca170SBaolin Wang */ 281195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) 282195ca170SBaolin Wang { 283195ca170SBaolin Wang int volt, cur, oci, ocv, ret; 2844a040e7cSYuanjiang Yu bool is_first_poweron = sc27xx_fgu_is_first_poweron(data); 2854a040e7cSYuanjiang Yu 2864a040e7cSYuanjiang Yu /* 2874a040e7cSYuanjiang Yu * If system is not the first power on, we should use the last saved 2884a040e7cSYuanjiang Yu * battery capacity as the initial battery capacity. Otherwise we should 2894a040e7cSYuanjiang Yu * re-calculate the initial battery capacity. 2904a040e7cSYuanjiang Yu */ 2914a040e7cSYuanjiang Yu if (!is_first_poweron) { 2924a040e7cSYuanjiang Yu ret = sc27xx_fgu_read_last_cap(data, cap); 2934a040e7cSYuanjiang Yu if (ret) 2944a040e7cSYuanjiang Yu return ret; 2954a040e7cSYuanjiang Yu 2964a040e7cSYuanjiang Yu return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 2974a040e7cSYuanjiang Yu } 298195ca170SBaolin Wang 299195ca170SBaolin Wang /* 300195ca170SBaolin Wang * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved 301195ca170SBaolin Wang * the first sampled open circuit current. 302195ca170SBaolin Wang */ 303195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL, 304195ca170SBaolin Wang &cur); 305195ca170SBaolin Wang if (ret) 306195ca170SBaolin Wang return ret; 307195ca170SBaolin Wang 308195ca170SBaolin Wang cur <<= 1; 30965c9fab7SBaolin Wang oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 310195ca170SBaolin Wang 311195ca170SBaolin Wang /* 312195ca170SBaolin Wang * Should get the OCV from SC27XX_FGU_POCV register at the system 313195ca170SBaolin Wang * beginning. It is ADC values reading from registers which need to 314195ca170SBaolin Wang * convert the corresponding voltage. 315195ca170SBaolin Wang */ 316195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt); 317195ca170SBaolin Wang if (ret) 318195ca170SBaolin Wang return ret; 319195ca170SBaolin Wang 32065c9fab7SBaolin Wang volt = sc27xx_fgu_adc_to_voltage(data, volt); 321195ca170SBaolin Wang ocv = volt * 1000 - oci * data->internal_resist; 322195ca170SBaolin Wang 323195ca170SBaolin Wang /* 324195ca170SBaolin Wang * Parse the capacity table to look up the correct capacity percent 325195ca170SBaolin Wang * according to current battery's corresponding OCV values. 326195ca170SBaolin Wang */ 327195ca170SBaolin Wang *cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len, 328195ca170SBaolin Wang ocv); 329195ca170SBaolin Wang 3304a040e7cSYuanjiang Yu ret = sc27xx_fgu_save_last_cap(data, *cap); 3314a040e7cSYuanjiang Yu if (ret) 3324a040e7cSYuanjiang Yu return ret; 3334a040e7cSYuanjiang Yu 3344a040e7cSYuanjiang Yu return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 335195ca170SBaolin Wang } 336195ca170SBaolin Wang 337195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt) 338195ca170SBaolin Wang { 339195ca170SBaolin Wang int ret; 340195ca170SBaolin Wang 341195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 342195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETL, 343195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, clbcnt); 344195ca170SBaolin Wang if (ret) 345195ca170SBaolin Wang return ret; 346195ca170SBaolin Wang 347195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 348195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETH, 349195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, 350195ca170SBaolin Wang clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 351195ca170SBaolin Wang if (ret) 352195ca170SBaolin Wang return ret; 353195ca170SBaolin Wang 354195ca170SBaolin Wang return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START, 355195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN, 356195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN); 357195ca170SBaolin Wang } 358195ca170SBaolin Wang 359195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt) 360195ca170SBaolin Wang { 361195ca170SBaolin Wang int ccl, cch, ret; 362195ca170SBaolin Wang 363195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL, 364195ca170SBaolin Wang &ccl); 365195ca170SBaolin Wang if (ret) 366195ca170SBaolin Wang return ret; 367195ca170SBaolin Wang 368195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH, 369195ca170SBaolin Wang &cch); 370195ca170SBaolin Wang if (ret) 371195ca170SBaolin Wang return ret; 372195ca170SBaolin Wang 373195ca170SBaolin Wang *clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK; 374195ca170SBaolin Wang *clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT; 375195ca170SBaolin Wang 376195ca170SBaolin Wang return 0; 377195ca170SBaolin Wang } 378195ca170SBaolin Wang 379195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap) 380195ca170SBaolin Wang { 381195ca170SBaolin Wang int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp; 382195ca170SBaolin Wang 383195ca170SBaolin Wang /* Get current coulomb counters firstly */ 384195ca170SBaolin Wang ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt); 385195ca170SBaolin Wang if (ret) 386195ca170SBaolin Wang return ret; 387195ca170SBaolin Wang 388195ca170SBaolin Wang delta_clbcnt = cur_clbcnt - data->init_clbcnt; 389195ca170SBaolin Wang 390195ca170SBaolin Wang /* 391195ca170SBaolin Wang * Convert coulomb counter to delta capacity (mAh), and set multiplier 3927384b0e7SYuanjiang Yu * as 10 to improve the precision. 393195ca170SBaolin Wang */ 3947384b0e7SYuanjiang Yu temp = DIV_ROUND_CLOSEST(delta_clbcnt * 10, 36 * SC27XX_FGU_SAMPLE_HZ); 3957384b0e7SYuanjiang Yu temp = sc27xx_fgu_adc_to_current(data, temp / 1000); 396195ca170SBaolin Wang 397195ca170SBaolin Wang /* 398195ca170SBaolin Wang * Convert to capacity percent of the battery total capacity, 399195ca170SBaolin Wang * and multiplier is 100 too. 400195ca170SBaolin Wang */ 401195ca170SBaolin Wang delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap); 402195ca170SBaolin Wang *cap = delta_cap + data->init_cap; 403195ca170SBaolin Wang 40458066527SYuanjiang Yu /* Calibrate the battery capacity in a normal range. */ 40558066527SYuanjiang Yu sc27xx_fgu_capacity_calibration(data, *cap, false); 40658066527SYuanjiang Yu 407195ca170SBaolin Wang return 0; 408195ca170SBaolin Wang } 409195ca170SBaolin Wang 410195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val) 411195ca170SBaolin Wang { 412195ca170SBaolin Wang int ret, vol; 413195ca170SBaolin Wang 414195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol); 415195ca170SBaolin Wang if (ret) 416195ca170SBaolin Wang return ret; 417195ca170SBaolin Wang 418195ca170SBaolin Wang /* 419195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 420195ca170SBaolin Wang * corresponding voltage values. 421195ca170SBaolin Wang */ 42265c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_voltage(data, vol); 423195ca170SBaolin Wang 424195ca170SBaolin Wang return 0; 425195ca170SBaolin Wang } 426195ca170SBaolin Wang 427195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val) 428195ca170SBaolin Wang { 429195ca170SBaolin Wang int ret, cur; 430195ca170SBaolin Wang 431195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur); 432195ca170SBaolin Wang if (ret) 433195ca170SBaolin Wang return ret; 434195ca170SBaolin Wang 435195ca170SBaolin Wang /* 436195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 437195ca170SBaolin Wang * corresponding current values. 438195ca170SBaolin Wang */ 43965c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 440195ca170SBaolin Wang 441195ca170SBaolin Wang return 0; 442195ca170SBaolin Wang } 443195ca170SBaolin Wang 444195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) 445195ca170SBaolin Wang { 4466af82888SYuanjiang Yu int vol, cur, ret, temp, resistance; 447195ca170SBaolin Wang 448195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 449195ca170SBaolin Wang if (ret) 450195ca170SBaolin Wang return ret; 451195ca170SBaolin Wang 452195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &cur); 453195ca170SBaolin Wang if (ret) 454195ca170SBaolin Wang return ret; 455195ca170SBaolin Wang 4566af82888SYuanjiang Yu resistance = data->internal_resist; 4576af82888SYuanjiang Yu if (data->resist_table_len > 0) { 4586af82888SYuanjiang Yu ret = sc27xx_fgu_get_temp(data, &temp); 4596af82888SYuanjiang Yu if (ret) 4606af82888SYuanjiang Yu return ret; 4616af82888SYuanjiang Yu 4626af82888SYuanjiang Yu resistance = power_supply_temp2resist_simple(data->resist_table, 4636af82888SYuanjiang Yu data->resist_table_len, temp); 4646af82888SYuanjiang Yu resistance = data->internal_resist * resistance / 100; 4656af82888SYuanjiang Yu } 4666af82888SYuanjiang Yu 467195ca170SBaolin Wang /* Return the battery OCV in micro volts. */ 4686af82888SYuanjiang Yu *val = vol * 1000 - cur * resistance; 469195ca170SBaolin Wang 470195ca170SBaolin Wang return 0; 471195ca170SBaolin Wang } 472195ca170SBaolin Wang 4730a4f97a1SBaolin Wang static int sc27xx_fgu_get_charge_vol(struct sc27xx_fgu_data *data, int *val) 4740a4f97a1SBaolin Wang { 4750a4f97a1SBaolin Wang int ret, vol; 4760a4f97a1SBaolin Wang 4770a4f97a1SBaolin Wang ret = iio_read_channel_processed(data->charge_chan, &vol); 4780a4f97a1SBaolin Wang if (ret < 0) 4790a4f97a1SBaolin Wang return ret; 4800a4f97a1SBaolin Wang 4810a4f97a1SBaolin Wang *val = vol * 1000; 4820a4f97a1SBaolin Wang return 0; 4830a4f97a1SBaolin Wang } 4840a4f97a1SBaolin Wang 485195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp) 486195ca170SBaolin Wang { 487195ca170SBaolin Wang return iio_read_channel_processed(data->channel, temp); 488195ca170SBaolin Wang } 489195ca170SBaolin Wang 490195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health) 491195ca170SBaolin Wang { 492195ca170SBaolin Wang int ret, vol; 493195ca170SBaolin Wang 494195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 495195ca170SBaolin Wang if (ret) 496195ca170SBaolin Wang return ret; 497195ca170SBaolin Wang 498195ca170SBaolin Wang if (vol > data->max_volt) 499195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 500195ca170SBaolin Wang else 501195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_GOOD; 502195ca170SBaolin Wang 503195ca170SBaolin Wang return 0; 504195ca170SBaolin Wang } 505195ca170SBaolin Wang 506195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status) 507195ca170SBaolin Wang { 508195ca170SBaolin Wang union power_supply_propval val; 509195ca170SBaolin Wang struct power_supply *psy; 510195ca170SBaolin Wang int i, ret = -EINVAL; 511195ca170SBaolin Wang 512195ca170SBaolin Wang for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) { 513195ca170SBaolin Wang psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]); 514195ca170SBaolin Wang if (!psy) 515195ca170SBaolin Wang continue; 516195ca170SBaolin Wang 517195ca170SBaolin Wang ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, 518195ca170SBaolin Wang &val); 519195ca170SBaolin Wang power_supply_put(psy); 520195ca170SBaolin Wang if (ret) 521195ca170SBaolin Wang return ret; 522195ca170SBaolin Wang 523195ca170SBaolin Wang *status = val.intval; 524195ca170SBaolin Wang } 525195ca170SBaolin Wang 526195ca170SBaolin Wang return ret; 527195ca170SBaolin Wang } 528195ca170SBaolin Wang 529195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy, 530195ca170SBaolin Wang enum power_supply_property psp, 531195ca170SBaolin Wang union power_supply_propval *val) 532195ca170SBaolin Wang { 533195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 534195ca170SBaolin Wang int ret = 0; 535195ca170SBaolin Wang int value; 536195ca170SBaolin Wang 537195ca170SBaolin Wang mutex_lock(&data->lock); 538195ca170SBaolin Wang 539195ca170SBaolin Wang switch (psp) { 540195ca170SBaolin Wang case POWER_SUPPLY_PROP_STATUS: 541195ca170SBaolin Wang ret = sc27xx_fgu_get_status(data, &value); 542195ca170SBaolin Wang if (ret) 543195ca170SBaolin Wang goto error; 544195ca170SBaolin Wang 545195ca170SBaolin Wang val->intval = value; 546195ca170SBaolin Wang break; 547195ca170SBaolin Wang 548195ca170SBaolin Wang case POWER_SUPPLY_PROP_HEALTH: 549195ca170SBaolin Wang ret = sc27xx_fgu_get_health(data, &value); 550195ca170SBaolin Wang if (ret) 551195ca170SBaolin Wang goto error; 552195ca170SBaolin Wang 553195ca170SBaolin Wang val->intval = value; 554195ca170SBaolin Wang break; 555195ca170SBaolin Wang 556195ca170SBaolin Wang case POWER_SUPPLY_PROP_PRESENT: 557195ca170SBaolin Wang val->intval = data->bat_present; 558195ca170SBaolin Wang break; 559195ca170SBaolin Wang 560195ca170SBaolin Wang case POWER_SUPPLY_PROP_TEMP: 561195ca170SBaolin Wang ret = sc27xx_fgu_get_temp(data, &value); 562195ca170SBaolin Wang if (ret) 563195ca170SBaolin Wang goto error; 564195ca170SBaolin Wang 565195ca170SBaolin Wang val->intval = value; 566195ca170SBaolin Wang break; 567195ca170SBaolin Wang 568195ca170SBaolin Wang case POWER_SUPPLY_PROP_TECHNOLOGY: 569195ca170SBaolin Wang val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 570195ca170SBaolin Wang break; 571195ca170SBaolin Wang 572195ca170SBaolin Wang case POWER_SUPPLY_PROP_CAPACITY: 573195ca170SBaolin Wang ret = sc27xx_fgu_get_capacity(data, &value); 574195ca170SBaolin Wang if (ret) 575195ca170SBaolin Wang goto error; 576195ca170SBaolin Wang 577195ca170SBaolin Wang val->intval = value; 578195ca170SBaolin Wang break; 579195ca170SBaolin Wang 580195ca170SBaolin Wang case POWER_SUPPLY_PROP_VOLTAGE_NOW: 581195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &value); 582195ca170SBaolin Wang if (ret) 583195ca170SBaolin Wang goto error; 584195ca170SBaolin Wang 585195ca170SBaolin Wang val->intval = value * 1000; 586195ca170SBaolin Wang break; 587195ca170SBaolin Wang 588195ca170SBaolin Wang case POWER_SUPPLY_PROP_VOLTAGE_OCV: 589195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_ocv(data, &value); 590195ca170SBaolin Wang if (ret) 591195ca170SBaolin Wang goto error; 592195ca170SBaolin Wang 593195ca170SBaolin Wang val->intval = value; 594195ca170SBaolin Wang break; 595195ca170SBaolin Wang 5960a4f97a1SBaolin Wang case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 5970a4f97a1SBaolin Wang ret = sc27xx_fgu_get_charge_vol(data, &value); 5980a4f97a1SBaolin Wang if (ret) 5990a4f97a1SBaolin Wang goto error; 6000a4f97a1SBaolin Wang 6010a4f97a1SBaolin Wang val->intval = value; 6020a4f97a1SBaolin Wang break; 6030a4f97a1SBaolin Wang 604195ca170SBaolin Wang case POWER_SUPPLY_PROP_CURRENT_NOW: 605195ca170SBaolin Wang case POWER_SUPPLY_PROP_CURRENT_AVG: 606195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &value); 607195ca170SBaolin Wang if (ret) 608195ca170SBaolin Wang goto error; 609195ca170SBaolin Wang 610195ca170SBaolin Wang val->intval = value * 1000; 611195ca170SBaolin Wang break; 612195ca170SBaolin Wang 6137cff19b9SYuanjiang Yu case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 6147cff19b9SYuanjiang Yu val->intval = data->total_cap * 1000; 6157cff19b9SYuanjiang Yu break; 6167cff19b9SYuanjiang Yu 617*1c5dfc5eSBaolin Wang case POWER_SUPPLY_PROP_CHARGE_NOW: 618*1c5dfc5eSBaolin Wang ret = sc27xx_fgu_get_clbcnt(data, &value); 619*1c5dfc5eSBaolin Wang if (ret) 620*1c5dfc5eSBaolin Wang goto error; 621*1c5dfc5eSBaolin Wang 622*1c5dfc5eSBaolin Wang value = DIV_ROUND_CLOSEST(value * 10, 623*1c5dfc5eSBaolin Wang 36 * SC27XX_FGU_SAMPLE_HZ); 624*1c5dfc5eSBaolin Wang val->intval = sc27xx_fgu_adc_to_current(data, value); 625*1c5dfc5eSBaolin Wang 626*1c5dfc5eSBaolin Wang break; 627*1c5dfc5eSBaolin Wang 628195ca170SBaolin Wang default: 629195ca170SBaolin Wang ret = -EINVAL; 630195ca170SBaolin Wang break; 631195ca170SBaolin Wang } 632195ca170SBaolin Wang 633195ca170SBaolin Wang error: 634195ca170SBaolin Wang mutex_unlock(&data->lock); 635195ca170SBaolin Wang return ret; 636195ca170SBaolin Wang } 637195ca170SBaolin Wang 6384a040e7cSYuanjiang Yu static int sc27xx_fgu_set_property(struct power_supply *psy, 6394a040e7cSYuanjiang Yu enum power_supply_property psp, 6404a040e7cSYuanjiang Yu const union power_supply_propval *val) 6414a040e7cSYuanjiang Yu { 6424a040e7cSYuanjiang Yu struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 6434a040e7cSYuanjiang Yu int ret; 6444a040e7cSYuanjiang Yu 6454a040e7cSYuanjiang Yu mutex_lock(&data->lock); 6464a040e7cSYuanjiang Yu 6477cfd33d9SYuanjiang Yu switch (psp) { 6487cfd33d9SYuanjiang Yu case POWER_SUPPLY_PROP_CAPACITY: 6494a040e7cSYuanjiang Yu ret = sc27xx_fgu_save_last_cap(data, val->intval); 6504a040e7cSYuanjiang Yu if (ret < 0) 6514a040e7cSYuanjiang Yu dev_err(data->dev, "failed to save battery capacity\n"); 6527cfd33d9SYuanjiang Yu break; 6537cfd33d9SYuanjiang Yu 6547cfd33d9SYuanjiang Yu case POWER_SUPPLY_PROP_CALIBRATE: 6557cfd33d9SYuanjiang Yu sc27xx_fgu_adjust_cap(data, val->intval); 6567cfd33d9SYuanjiang Yu ret = 0; 6577cfd33d9SYuanjiang Yu break; 6587cfd33d9SYuanjiang Yu 6597cfd33d9SYuanjiang Yu default: 6607cfd33d9SYuanjiang Yu ret = -EINVAL; 6617cfd33d9SYuanjiang Yu } 6627cfd33d9SYuanjiang Yu 6637cfd33d9SYuanjiang Yu mutex_unlock(&data->lock); 6644a040e7cSYuanjiang Yu 6654a040e7cSYuanjiang Yu return ret; 6664a040e7cSYuanjiang Yu } 6674a040e7cSYuanjiang Yu 668195ca170SBaolin Wang static void sc27xx_fgu_external_power_changed(struct power_supply *psy) 669195ca170SBaolin Wang { 670195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 671195ca170SBaolin Wang 672195ca170SBaolin Wang power_supply_changed(data->battery); 673195ca170SBaolin Wang } 674195ca170SBaolin Wang 6754a040e7cSYuanjiang Yu static int sc27xx_fgu_property_is_writeable(struct power_supply *psy, 6764a040e7cSYuanjiang Yu enum power_supply_property psp) 6774a040e7cSYuanjiang Yu { 6787cfd33d9SYuanjiang Yu return psp == POWER_SUPPLY_PROP_CAPACITY || 6797cfd33d9SYuanjiang Yu psp == POWER_SUPPLY_PROP_CALIBRATE; 6804a040e7cSYuanjiang Yu } 6814a040e7cSYuanjiang Yu 682195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = { 683195ca170SBaolin Wang POWER_SUPPLY_PROP_STATUS, 684195ca170SBaolin Wang POWER_SUPPLY_PROP_HEALTH, 685195ca170SBaolin Wang POWER_SUPPLY_PROP_PRESENT, 686195ca170SBaolin Wang POWER_SUPPLY_PROP_TEMP, 687195ca170SBaolin Wang POWER_SUPPLY_PROP_TECHNOLOGY, 688195ca170SBaolin Wang POWER_SUPPLY_PROP_CAPACITY, 689195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_NOW, 690195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_OCV, 691195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_NOW, 692195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_AVG, 6930a4f97a1SBaolin Wang POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 6947cff19b9SYuanjiang Yu POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 6957cfd33d9SYuanjiang Yu POWER_SUPPLY_PROP_CALIBRATE, 696*1c5dfc5eSBaolin Wang POWER_SUPPLY_PROP_CHARGE_NOW 697195ca170SBaolin Wang }; 698195ca170SBaolin Wang 699195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = { 700195ca170SBaolin Wang .name = "sc27xx-fgu", 701195ca170SBaolin Wang .type = POWER_SUPPLY_TYPE_BATTERY, 702195ca170SBaolin Wang .properties = sc27xx_fgu_props, 703195ca170SBaolin Wang .num_properties = ARRAY_SIZE(sc27xx_fgu_props), 704195ca170SBaolin Wang .get_property = sc27xx_fgu_get_property, 7054a040e7cSYuanjiang Yu .set_property = sc27xx_fgu_set_property, 706195ca170SBaolin Wang .external_power_changed = sc27xx_fgu_external_power_changed, 7074a040e7cSYuanjiang Yu .property_is_writeable = sc27xx_fgu_property_is_writeable, 708195ca170SBaolin Wang }; 709195ca170SBaolin Wang 710edcb1c0aSYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap) 711edcb1c0aSYuanjiang Yu { 71258066527SYuanjiang Yu int ret; 71358066527SYuanjiang Yu 714edcb1c0aSYuanjiang Yu data->init_cap = cap; 71558066527SYuanjiang Yu ret = sc27xx_fgu_get_clbcnt(data, &data->init_clbcnt); 71658066527SYuanjiang Yu if (ret) 71758066527SYuanjiang Yu dev_err(data->dev, "failed to get init coulomb counter\n"); 71858066527SYuanjiang Yu } 71958066527SYuanjiang Yu 72058066527SYuanjiang Yu static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, 72158066527SYuanjiang Yu int cap, bool int_mode) 72258066527SYuanjiang Yu { 72358066527SYuanjiang Yu int ret, ocv, chg_sts, adc; 72458066527SYuanjiang Yu 72558066527SYuanjiang Yu ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 72658066527SYuanjiang Yu if (ret) { 72758066527SYuanjiang Yu dev_err(data->dev, "get battery ocv error.\n"); 72858066527SYuanjiang Yu return; 72958066527SYuanjiang Yu } 73058066527SYuanjiang Yu 73158066527SYuanjiang Yu ret = sc27xx_fgu_get_status(data, &chg_sts); 73258066527SYuanjiang Yu if (ret) { 73358066527SYuanjiang Yu dev_err(data->dev, "get charger status error.\n"); 73458066527SYuanjiang Yu return; 73558066527SYuanjiang Yu } 73658066527SYuanjiang Yu 73758066527SYuanjiang Yu /* 73858066527SYuanjiang Yu * If we are in charging mode, then we do not need to calibrate the 73958066527SYuanjiang Yu * lower capacity. 74058066527SYuanjiang Yu */ 74158066527SYuanjiang Yu if (chg_sts == POWER_SUPPLY_STATUS_CHARGING) 74258066527SYuanjiang Yu return; 74358066527SYuanjiang Yu 74458066527SYuanjiang Yu if ((ocv > data->cap_table[0].ocv && cap < 100) || cap > 100) { 74558066527SYuanjiang Yu /* 74658066527SYuanjiang Yu * If current OCV value is larger than the max OCV value in 74758066527SYuanjiang Yu * OCV table, or the current capacity is larger than 100, 74858066527SYuanjiang Yu * we should force the inititial capacity to 100. 74958066527SYuanjiang Yu */ 75058066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, 100); 75158066527SYuanjiang Yu } else if (ocv <= data->cap_table[data->table_len - 1].ocv) { 75258066527SYuanjiang Yu /* 75358066527SYuanjiang Yu * If current OCV value is leass than the minimum OCV value in 75458066527SYuanjiang Yu * OCV table, we should force the inititial capacity to 0. 75558066527SYuanjiang Yu */ 75658066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, 0); 75758066527SYuanjiang Yu } else if ((ocv > data->cap_table[data->table_len - 1].ocv && cap <= 0) || 75858066527SYuanjiang Yu (ocv > data->min_volt && cap <= data->alarm_cap)) { 75958066527SYuanjiang Yu /* 76058066527SYuanjiang Yu * If current OCV value is not matchable with current capacity, 76158066527SYuanjiang Yu * we should re-calculate current capacity by looking up the 76258066527SYuanjiang Yu * OCV table. 76358066527SYuanjiang Yu */ 76458066527SYuanjiang Yu int cur_cap = power_supply_ocv2cap_simple(data->cap_table, 76558066527SYuanjiang Yu data->table_len, ocv); 76658066527SYuanjiang Yu 76758066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, cur_cap); 76858066527SYuanjiang Yu } else if (ocv <= data->min_volt) { 76958066527SYuanjiang Yu /* 77058066527SYuanjiang Yu * If current OCV value is less than the low alarm voltage, but 77158066527SYuanjiang Yu * current capacity is larger than the alarm capacity, we should 77258066527SYuanjiang Yu * adjust the inititial capacity to alarm capacity. 77358066527SYuanjiang Yu */ 77458066527SYuanjiang Yu if (cap > data->alarm_cap) { 77558066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, data->alarm_cap); 77658066527SYuanjiang Yu } else { 77758066527SYuanjiang Yu int cur_cap; 77858066527SYuanjiang Yu 77958066527SYuanjiang Yu /* 78058066527SYuanjiang Yu * If current capacity is equal with 0 or less than 0 78158066527SYuanjiang Yu * (some error occurs), we should adjust inititial 78258066527SYuanjiang Yu * capacity to the capacity corresponding to current OCV 78358066527SYuanjiang Yu * value. 78458066527SYuanjiang Yu */ 78558066527SYuanjiang Yu cur_cap = power_supply_ocv2cap_simple(data->cap_table, 78658066527SYuanjiang Yu data->table_len, 78758066527SYuanjiang Yu ocv); 78858066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, cur_cap); 78958066527SYuanjiang Yu } 79058066527SYuanjiang Yu 79158066527SYuanjiang Yu if (!int_mode) 79258066527SYuanjiang Yu return; 79358066527SYuanjiang Yu 79458066527SYuanjiang Yu /* 79558066527SYuanjiang Yu * After adjusting the battery capacity, we should set the 79658066527SYuanjiang Yu * lowest alarm voltage instead. 79758066527SYuanjiang Yu */ 79858066527SYuanjiang Yu data->min_volt = data->cap_table[data->table_len - 1].ocv; 79958066527SYuanjiang Yu data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, 80058066527SYuanjiang Yu data->table_len, 80158066527SYuanjiang Yu data->min_volt); 80258066527SYuanjiang Yu 80358066527SYuanjiang Yu adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 80458066527SYuanjiang Yu regmap_update_bits(data->regmap, 80558066527SYuanjiang Yu data->base + SC27XX_FGU_LOW_OVERLOAD, 80658066527SYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_MASK, adc); 80758066527SYuanjiang Yu } 808edcb1c0aSYuanjiang Yu } 809edcb1c0aSYuanjiang Yu 810edcb1c0aSYuanjiang Yu static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id) 811edcb1c0aSYuanjiang Yu { 812edcb1c0aSYuanjiang Yu struct sc27xx_fgu_data *data = dev_id; 81358066527SYuanjiang Yu int ret, cap; 814edcb1c0aSYuanjiang Yu u32 status; 815edcb1c0aSYuanjiang Yu 816edcb1c0aSYuanjiang Yu mutex_lock(&data->lock); 817edcb1c0aSYuanjiang Yu 818edcb1c0aSYuanjiang Yu ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS, 819edcb1c0aSYuanjiang Yu &status); 820edcb1c0aSYuanjiang Yu if (ret) 821edcb1c0aSYuanjiang Yu goto out; 822edcb1c0aSYuanjiang Yu 823edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 824edcb1c0aSYuanjiang Yu status, status); 825edcb1c0aSYuanjiang Yu if (ret) 826edcb1c0aSYuanjiang Yu goto out; 827edcb1c0aSYuanjiang Yu 828edcb1c0aSYuanjiang Yu /* 829edcb1c0aSYuanjiang Yu * When low overload voltage interrupt happens, we should calibrate the 830edcb1c0aSYuanjiang Yu * battery capacity in lower voltage stage. 831edcb1c0aSYuanjiang Yu */ 832edcb1c0aSYuanjiang Yu if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT)) 833edcb1c0aSYuanjiang Yu goto out; 834edcb1c0aSYuanjiang Yu 835edcb1c0aSYuanjiang Yu ret = sc27xx_fgu_get_capacity(data, &cap); 836edcb1c0aSYuanjiang Yu if (ret) 837edcb1c0aSYuanjiang Yu goto out; 838edcb1c0aSYuanjiang Yu 83958066527SYuanjiang Yu sc27xx_fgu_capacity_calibration(data, cap, true); 840edcb1c0aSYuanjiang Yu 841edcb1c0aSYuanjiang Yu out: 842edcb1c0aSYuanjiang Yu mutex_unlock(&data->lock); 843edcb1c0aSYuanjiang Yu 844edcb1c0aSYuanjiang Yu power_supply_changed(data->battery); 845edcb1c0aSYuanjiang Yu return IRQ_HANDLED; 846edcb1c0aSYuanjiang Yu } 847edcb1c0aSYuanjiang Yu 848195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id) 849195ca170SBaolin Wang { 850195ca170SBaolin Wang struct sc27xx_fgu_data *data = dev_id; 851195ca170SBaolin Wang int state; 852195ca170SBaolin Wang 853195ca170SBaolin Wang mutex_lock(&data->lock); 854195ca170SBaolin Wang 855195ca170SBaolin Wang state = gpiod_get_value_cansleep(data->gpiod); 856195ca170SBaolin Wang if (state < 0) { 857195ca170SBaolin Wang dev_err(data->dev, "failed to get gpio state\n"); 858195ca170SBaolin Wang mutex_unlock(&data->lock); 859195ca170SBaolin Wang return IRQ_RETVAL(state); 860195ca170SBaolin Wang } 861195ca170SBaolin Wang 862195ca170SBaolin Wang data->bat_present = !!state; 863195ca170SBaolin Wang 864195ca170SBaolin Wang mutex_unlock(&data->lock); 865195ca170SBaolin Wang 866195ca170SBaolin Wang power_supply_changed(data->battery); 867195ca170SBaolin Wang return IRQ_HANDLED; 868195ca170SBaolin Wang } 869195ca170SBaolin Wang 870195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data) 871195ca170SBaolin Wang { 872195ca170SBaolin Wang struct sc27xx_fgu_data *data = _data; 873195ca170SBaolin Wang 874195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 875195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 876195ca170SBaolin Wang } 877195ca170SBaolin Wang 878195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity) 879195ca170SBaolin Wang { 880195ca170SBaolin Wang /* 881195ca170SBaolin Wang * Get current capacity (mAh) = battery total capacity (mAh) * 882195ca170SBaolin Wang * current capacity percent (capacity / 100). 883195ca170SBaolin Wang */ 884195ca170SBaolin Wang int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100); 885195ca170SBaolin Wang 886195ca170SBaolin Wang /* 887195ca170SBaolin Wang * Convert current capacity (mAh) to coulomb counter according to the 888195ca170SBaolin Wang * formula: 1 mAh =3.6 coulomb. 889195ca170SBaolin Wang */ 8907384b0e7SYuanjiang Yu return DIV_ROUND_CLOSEST(cur_cap * 36 * data->cur_1000ma_adc * SC27XX_FGU_SAMPLE_HZ, 10); 891195ca170SBaolin Wang } 892195ca170SBaolin Wang 89365c9fab7SBaolin Wang static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) 89465c9fab7SBaolin Wang { 89565c9fab7SBaolin Wang struct nvmem_cell *cell; 89665c9fab7SBaolin Wang int calib_data, cal_4200mv; 89765c9fab7SBaolin Wang void *buf; 89865c9fab7SBaolin Wang size_t len; 89965c9fab7SBaolin Wang 90065c9fab7SBaolin Wang cell = nvmem_cell_get(data->dev, "fgu_calib"); 90165c9fab7SBaolin Wang if (IS_ERR(cell)) 90265c9fab7SBaolin Wang return PTR_ERR(cell); 90365c9fab7SBaolin Wang 90465c9fab7SBaolin Wang buf = nvmem_cell_read(cell, &len); 90565c9fab7SBaolin Wang nvmem_cell_put(cell); 90665c9fab7SBaolin Wang 90765c9fab7SBaolin Wang if (IS_ERR(buf)) 90865c9fab7SBaolin Wang return PTR_ERR(buf); 90965c9fab7SBaolin Wang 91065c9fab7SBaolin Wang memcpy(&calib_data, buf, min(len, sizeof(u32))); 91165c9fab7SBaolin Wang 91265c9fab7SBaolin Wang /* 91365c9fab7SBaolin Wang * Get the ADC value corresponding to 4200 mV from eFuse controller 91465c9fab7SBaolin Wang * according to below formula. Then convert to ADC values corresponding 91565c9fab7SBaolin Wang * to 1000 mV and 1000 mA. 91665c9fab7SBaolin Wang */ 91765c9fab7SBaolin Wang cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256; 91865c9fab7SBaolin Wang data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42); 919058d4256SBaolin Wang data->cur_1000ma_adc = 920058d4256SBaolin Wang DIV_ROUND_CLOSEST(data->vol_1000mv_adc * 4 * data->calib_resist, 921058d4256SBaolin Wang SC27XX_FGU_IDEAL_RESISTANCE); 92265c9fab7SBaolin Wang 92365c9fab7SBaolin Wang kfree(buf); 92465c9fab7SBaolin Wang return 0; 92565c9fab7SBaolin Wang } 92665c9fab7SBaolin Wang 927195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) 928195ca170SBaolin Wang { 929195ca170SBaolin Wang struct power_supply_battery_info info = { }; 930195ca170SBaolin Wang struct power_supply_battery_ocv_table *table; 931edcb1c0aSYuanjiang Yu int ret, delta_clbcnt, alarm_adc; 932195ca170SBaolin Wang 933195ca170SBaolin Wang ret = power_supply_get_battery_info(data->battery, &info); 934195ca170SBaolin Wang if (ret) { 935195ca170SBaolin Wang dev_err(data->dev, "failed to get battery information\n"); 936195ca170SBaolin Wang return ret; 937195ca170SBaolin Wang } 938195ca170SBaolin Wang 939195ca170SBaolin Wang data->total_cap = info.charge_full_design_uah / 1000; 940195ca170SBaolin Wang data->max_volt = info.constant_charge_voltage_max_uv / 1000; 941195ca170SBaolin Wang data->internal_resist = info.factory_internal_resistance_uohm / 1000; 942edcb1c0aSYuanjiang Yu data->min_volt = info.voltage_min_design_uv; 943195ca170SBaolin Wang 944195ca170SBaolin Wang /* 945195ca170SBaolin Wang * For SC27XX fuel gauge device, we only use one ocv-capacity 946195ca170SBaolin Wang * table in normal temperature 20 Celsius. 947195ca170SBaolin Wang */ 948195ca170SBaolin Wang table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len); 949195ca170SBaolin Wang if (!table) 950195ca170SBaolin Wang return -EINVAL; 951195ca170SBaolin Wang 952195ca170SBaolin Wang data->cap_table = devm_kmemdup(data->dev, table, 953195ca170SBaolin Wang data->table_len * sizeof(*table), 954195ca170SBaolin Wang GFP_KERNEL); 955195ca170SBaolin Wang if (!data->cap_table) { 956195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 957195ca170SBaolin Wang return -ENOMEM; 958195ca170SBaolin Wang } 959195ca170SBaolin Wang 960edcb1c0aSYuanjiang Yu data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, 961edcb1c0aSYuanjiang Yu data->table_len, 962edcb1c0aSYuanjiang Yu data->min_volt); 963ff062d06SYuanjiang Yu if (!data->alarm_cap) 964ff062d06SYuanjiang Yu data->alarm_cap += 1; 965edcb1c0aSYuanjiang Yu 9666af82888SYuanjiang Yu data->resist_table_len = info.resist_table_size; 9676af82888SYuanjiang Yu if (data->resist_table_len > 0) { 9686af82888SYuanjiang Yu data->resist_table = devm_kmemdup(data->dev, info.resist_table, 9696af82888SYuanjiang Yu data->resist_table_len * 9706af82888SYuanjiang Yu sizeof(struct power_supply_resistance_temp_table), 9716af82888SYuanjiang Yu GFP_KERNEL); 9726af82888SYuanjiang Yu if (!data->resist_table) { 9736af82888SYuanjiang Yu power_supply_put_battery_info(data->battery, &info); 9746af82888SYuanjiang Yu return -ENOMEM; 9756af82888SYuanjiang Yu } 9766af82888SYuanjiang Yu } 9776af82888SYuanjiang Yu 978195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 979195ca170SBaolin Wang 98065c9fab7SBaolin Wang ret = sc27xx_fgu_calibration(data); 98165c9fab7SBaolin Wang if (ret) 98265c9fab7SBaolin Wang return ret; 98365c9fab7SBaolin Wang 984195ca170SBaolin Wang /* Enable the FGU module */ 985195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, 986195ca170SBaolin Wang SC27XX_FGU_EN, SC27XX_FGU_EN); 987195ca170SBaolin Wang if (ret) { 988195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu\n"); 989195ca170SBaolin Wang return ret; 990195ca170SBaolin Wang } 991195ca170SBaolin Wang 992195ca170SBaolin Wang /* Enable the FGU RTC clock to make it work */ 993195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0, 994195ca170SBaolin Wang SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN); 995195ca170SBaolin Wang if (ret) { 996195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu RTC clock\n"); 997195ca170SBaolin Wang goto disable_fgu; 998195ca170SBaolin Wang } 999195ca170SBaolin Wang 1000edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 1001edcb1c0aSYuanjiang Yu SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK); 1002edcb1c0aSYuanjiang Yu if (ret) { 1003edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to clear interrupt status\n"); 1004edcb1c0aSYuanjiang Yu goto disable_clk; 1005edcb1c0aSYuanjiang Yu } 1006edcb1c0aSYuanjiang Yu 1007edcb1c0aSYuanjiang Yu /* 1008edcb1c0aSYuanjiang Yu * Set the voltage low overload threshold, which means when the battery 1009edcb1c0aSYuanjiang Yu * voltage is lower than this threshold, the controller will generate 1010edcb1c0aSYuanjiang Yu * one interrupt to notify. 1011edcb1c0aSYuanjiang Yu */ 1012edcb1c0aSYuanjiang Yu alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 1013edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, 1014edcb1c0aSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc); 1015edcb1c0aSYuanjiang Yu if (ret) { 1016edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set fgu low overload\n"); 1017edcb1c0aSYuanjiang Yu goto disable_clk; 1018edcb1c0aSYuanjiang Yu } 1019edcb1c0aSYuanjiang Yu 1020edcb1c0aSYuanjiang Yu /* 1021edcb1c0aSYuanjiang Yu * Set the coulomb counter delta threshold, that means when the coulomb 1022edcb1c0aSYuanjiang Yu * counter change is multiples of the delta threshold, the controller 1023edcb1c0aSYuanjiang Yu * will generate one interrupt to notify the users to update the battery 1024edcb1c0aSYuanjiang Yu * capacity. Now we set the delta threshold as a counter value of 1% 1025edcb1c0aSYuanjiang Yu * capacity. 1026edcb1c0aSYuanjiang Yu */ 1027edcb1c0aSYuanjiang Yu delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1); 1028edcb1c0aSYuanjiang Yu 1029edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL, 1030edcb1c0aSYuanjiang Yu SC27XX_FGU_CLBCNT_MASK, delta_clbcnt); 1031edcb1c0aSYuanjiang Yu if (ret) { 1032edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set low delta coulomb counter\n"); 1033edcb1c0aSYuanjiang Yu goto disable_clk; 1034edcb1c0aSYuanjiang Yu } 1035edcb1c0aSYuanjiang Yu 1036edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH, 1037edcb1c0aSYuanjiang Yu SC27XX_FGU_CLBCNT_MASK, 1038edcb1c0aSYuanjiang Yu delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 1039edcb1c0aSYuanjiang Yu if (ret) { 1040edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set high delta coulomb counter\n"); 1041edcb1c0aSYuanjiang Yu goto disable_clk; 1042edcb1c0aSYuanjiang Yu } 1043edcb1c0aSYuanjiang Yu 1044195ca170SBaolin Wang /* 1045195ca170SBaolin Wang * Get the boot battery capacity when system powers on, which is used to 1046195ca170SBaolin Wang * initialize the coulomb counter. After that, we can read the coulomb 1047195ca170SBaolin Wang * counter to measure the battery capacity. 1048195ca170SBaolin Wang */ 1049195ca170SBaolin Wang ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap); 1050195ca170SBaolin Wang if (ret) { 1051195ca170SBaolin Wang dev_err(data->dev, "failed to get boot capacity\n"); 1052195ca170SBaolin Wang goto disable_clk; 1053195ca170SBaolin Wang } 1054195ca170SBaolin Wang 1055195ca170SBaolin Wang /* 1056195ca170SBaolin Wang * Convert battery capacity to the corresponding initial coulomb counter 1057195ca170SBaolin Wang * and set into coulomb counter registers. 1058195ca170SBaolin Wang */ 1059195ca170SBaolin Wang data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); 1060195ca170SBaolin Wang ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt); 1061195ca170SBaolin Wang if (ret) { 1062195ca170SBaolin Wang dev_err(data->dev, "failed to initialize coulomb counter\n"); 1063195ca170SBaolin Wang goto disable_clk; 1064195ca170SBaolin Wang } 1065195ca170SBaolin Wang 1066195ca170SBaolin Wang return 0; 1067195ca170SBaolin Wang 1068195ca170SBaolin Wang disable_clk: 1069195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 1070195ca170SBaolin Wang disable_fgu: 1071195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 1072195ca170SBaolin Wang 1073195ca170SBaolin Wang return ret; 1074195ca170SBaolin Wang } 1075195ca170SBaolin Wang 1076195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev) 1077195ca170SBaolin Wang { 107808614b40SFuqian Huang struct device *dev = &pdev->dev; 107908614b40SFuqian Huang struct device_node *np = dev->of_node; 1080195ca170SBaolin Wang struct power_supply_config fgu_cfg = { }; 1081195ca170SBaolin Wang struct sc27xx_fgu_data *data; 1082195ca170SBaolin Wang int ret, irq; 1083195ca170SBaolin Wang 108408614b40SFuqian Huang data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 1085195ca170SBaolin Wang if (!data) 1086195ca170SBaolin Wang return -ENOMEM; 1087195ca170SBaolin Wang 108808614b40SFuqian Huang data->regmap = dev_get_regmap(dev->parent, NULL); 1089195ca170SBaolin Wang if (!data->regmap) { 109008614b40SFuqian Huang dev_err(dev, "failed to get regmap\n"); 1091195ca170SBaolin Wang return -ENODEV; 1092195ca170SBaolin Wang } 1093195ca170SBaolin Wang 109408614b40SFuqian Huang ret = device_property_read_u32(dev, "reg", &data->base); 1095195ca170SBaolin Wang if (ret) { 109608614b40SFuqian Huang dev_err(dev, "failed to get fgu address\n"); 1097195ca170SBaolin Wang return ret; 1098195ca170SBaolin Wang } 1099195ca170SBaolin Wang 1100058d4256SBaolin Wang ret = device_property_read_u32(&pdev->dev, 1101058d4256SBaolin Wang "sprd,calib-resistance-micro-ohms", 1102058d4256SBaolin Wang &data->calib_resist); 1103058d4256SBaolin Wang if (ret) { 1104058d4256SBaolin Wang dev_err(&pdev->dev, 1105058d4256SBaolin Wang "failed to get fgu calibration resistance\n"); 1106058d4256SBaolin Wang return ret; 1107058d4256SBaolin Wang } 1108058d4256SBaolin Wang 110908614b40SFuqian Huang data->channel = devm_iio_channel_get(dev, "bat-temp"); 1110195ca170SBaolin Wang if (IS_ERR(data->channel)) { 111108614b40SFuqian Huang dev_err(dev, "failed to get IIO channel\n"); 1112195ca170SBaolin Wang return PTR_ERR(data->channel); 1113195ca170SBaolin Wang } 1114195ca170SBaolin Wang 111508614b40SFuqian Huang data->charge_chan = devm_iio_channel_get(dev, "charge-vol"); 11160a4f97a1SBaolin Wang if (IS_ERR(data->charge_chan)) { 111708614b40SFuqian Huang dev_err(dev, "failed to get charge IIO channel\n"); 11180a4f97a1SBaolin Wang return PTR_ERR(data->charge_chan); 11190a4f97a1SBaolin Wang } 11200a4f97a1SBaolin Wang 112108614b40SFuqian Huang data->gpiod = devm_gpiod_get(dev, "bat-detect", GPIOD_IN); 1122195ca170SBaolin Wang if (IS_ERR(data->gpiod)) { 112308614b40SFuqian Huang dev_err(dev, "failed to get battery detection GPIO\n"); 1124195ca170SBaolin Wang return PTR_ERR(data->gpiod); 1125195ca170SBaolin Wang } 1126195ca170SBaolin Wang 1127195ca170SBaolin Wang ret = gpiod_get_value_cansleep(data->gpiod); 1128195ca170SBaolin Wang if (ret < 0) { 112908614b40SFuqian Huang dev_err(dev, "failed to get gpio state\n"); 1130195ca170SBaolin Wang return ret; 1131195ca170SBaolin Wang } 1132195ca170SBaolin Wang 1133195ca170SBaolin Wang data->bat_present = !!ret; 1134195ca170SBaolin Wang mutex_init(&data->lock); 113508614b40SFuqian Huang data->dev = dev; 1136e2fb615bSYuanjiang Yu platform_set_drvdata(pdev, data); 1137195ca170SBaolin Wang 1138195ca170SBaolin Wang fgu_cfg.drv_data = data; 1139195ca170SBaolin Wang fgu_cfg.of_node = np; 114008614b40SFuqian Huang data->battery = devm_power_supply_register(dev, &sc27xx_fgu_desc, 1141195ca170SBaolin Wang &fgu_cfg); 1142195ca170SBaolin Wang if (IS_ERR(data->battery)) { 114308614b40SFuqian Huang dev_err(dev, "failed to register power supply\n"); 1144195ca170SBaolin Wang return PTR_ERR(data->battery); 1145195ca170SBaolin Wang } 1146195ca170SBaolin Wang 1147195ca170SBaolin Wang ret = sc27xx_fgu_hw_init(data); 1148195ca170SBaolin Wang if (ret) { 114908614b40SFuqian Huang dev_err(dev, "failed to initialize fgu hardware\n"); 1150195ca170SBaolin Wang return ret; 1151195ca170SBaolin Wang } 1152195ca170SBaolin Wang 115320420583SFuqian Huang ret = devm_add_action_or_reset(dev, sc27xx_fgu_disable, data); 1154195ca170SBaolin Wang if (ret) { 115508614b40SFuqian Huang dev_err(dev, "failed to add fgu disable action\n"); 1156195ca170SBaolin Wang return ret; 1157195ca170SBaolin Wang } 1158195ca170SBaolin Wang 1159edcb1c0aSYuanjiang Yu irq = platform_get_irq(pdev, 0); 1160edcb1c0aSYuanjiang Yu if (irq < 0) { 116108614b40SFuqian Huang dev_err(dev, "no irq resource specified\n"); 1162edcb1c0aSYuanjiang Yu return irq; 1163edcb1c0aSYuanjiang Yu } 1164edcb1c0aSYuanjiang Yu 1165edcb1c0aSYuanjiang Yu ret = devm_request_threaded_irq(data->dev, irq, NULL, 1166edcb1c0aSYuanjiang Yu sc27xx_fgu_interrupt, 1167edcb1c0aSYuanjiang Yu IRQF_NO_SUSPEND | IRQF_ONESHOT, 1168edcb1c0aSYuanjiang Yu pdev->name, data); 1169edcb1c0aSYuanjiang Yu if (ret) { 1170edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to request fgu IRQ\n"); 1171edcb1c0aSYuanjiang Yu return ret; 1172edcb1c0aSYuanjiang Yu } 1173edcb1c0aSYuanjiang Yu 1174195ca170SBaolin Wang irq = gpiod_to_irq(data->gpiod); 1175195ca170SBaolin Wang if (irq < 0) { 117608614b40SFuqian Huang dev_err(dev, "failed to translate GPIO to IRQ\n"); 1177195ca170SBaolin Wang return irq; 1178195ca170SBaolin Wang } 1179195ca170SBaolin Wang 118008614b40SFuqian Huang ret = devm_request_threaded_irq(dev, irq, NULL, 1181195ca170SBaolin Wang sc27xx_fgu_bat_detection, 1182195ca170SBaolin Wang IRQF_ONESHOT | IRQF_TRIGGER_RISING | 1183195ca170SBaolin Wang IRQF_TRIGGER_FALLING, 1184195ca170SBaolin Wang pdev->name, data); 1185195ca170SBaolin Wang if (ret) { 118608614b40SFuqian Huang dev_err(dev, "failed to request IRQ\n"); 1187195ca170SBaolin Wang return ret; 1188195ca170SBaolin Wang } 1189195ca170SBaolin Wang 1190195ca170SBaolin Wang return 0; 1191195ca170SBaolin Wang } 1192195ca170SBaolin Wang 1193e2fb615bSYuanjiang Yu #ifdef CONFIG_PM_SLEEP 1194e2fb615bSYuanjiang Yu static int sc27xx_fgu_resume(struct device *dev) 1195e2fb615bSYuanjiang Yu { 1196e2fb615bSYuanjiang Yu struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 1197e2fb615bSYuanjiang Yu int ret; 1198e2fb615bSYuanjiang Yu 1199e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1200e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT | 1201e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT, 0); 1202e2fb615bSYuanjiang Yu if (ret) { 1203e2fb615bSYuanjiang Yu dev_err(data->dev, "failed to disable fgu interrupts\n"); 1204e2fb615bSYuanjiang Yu return ret; 1205e2fb615bSYuanjiang Yu } 1206e2fb615bSYuanjiang Yu 1207e2fb615bSYuanjiang Yu return 0; 1208e2fb615bSYuanjiang Yu } 1209e2fb615bSYuanjiang Yu 1210e2fb615bSYuanjiang Yu static int sc27xx_fgu_suspend(struct device *dev) 1211e2fb615bSYuanjiang Yu { 1212e2fb615bSYuanjiang Yu struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 1213e2fb615bSYuanjiang Yu int ret, status, ocv; 1214e2fb615bSYuanjiang Yu 1215e2fb615bSYuanjiang Yu ret = sc27xx_fgu_get_status(data, &status); 1216e2fb615bSYuanjiang Yu if (ret) 1217e2fb615bSYuanjiang Yu return ret; 1218e2fb615bSYuanjiang Yu 1219e2fb615bSYuanjiang Yu /* 1220e2fb615bSYuanjiang Yu * If we are charging, then no need to enable the FGU interrupts to 1221e2fb615bSYuanjiang Yu * adjust the battery capacity. 1222e2fb615bSYuanjiang Yu */ 1223168e68d0SYuanjiang Yu if (status != POWER_SUPPLY_STATUS_NOT_CHARGING && 1224168e68d0SYuanjiang Yu status != POWER_SUPPLY_STATUS_DISCHARGING) 1225e2fb615bSYuanjiang Yu return 0; 1226e2fb615bSYuanjiang Yu 1227e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1228e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT, 1229e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT); 1230e2fb615bSYuanjiang Yu if (ret) { 1231e2fb615bSYuanjiang Yu dev_err(data->dev, "failed to enable low voltage interrupt\n"); 1232e2fb615bSYuanjiang Yu return ret; 1233e2fb615bSYuanjiang Yu } 1234e2fb615bSYuanjiang Yu 1235e2fb615bSYuanjiang Yu ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 1236e2fb615bSYuanjiang Yu if (ret) 1237e2fb615bSYuanjiang Yu goto disable_int; 1238e2fb615bSYuanjiang Yu 1239e2fb615bSYuanjiang Yu /* 1240e2fb615bSYuanjiang Yu * If current OCV is less than the minimum voltage, we should enable the 1241e2fb615bSYuanjiang Yu * coulomb counter threshold interrupt to notify events to adjust the 1242e2fb615bSYuanjiang Yu * battery capacity. 1243e2fb615bSYuanjiang Yu */ 1244e2fb615bSYuanjiang Yu if (ocv < data->min_volt) { 1245e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, 1246e2fb615bSYuanjiang Yu data->base + SC27XX_FGU_INT_EN, 1247e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT, 1248e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT); 1249e2fb615bSYuanjiang Yu if (ret) { 1250e2fb615bSYuanjiang Yu dev_err(data->dev, 1251e2fb615bSYuanjiang Yu "failed to enable coulomb threshold int\n"); 1252e2fb615bSYuanjiang Yu goto disable_int; 1253e2fb615bSYuanjiang Yu } 1254e2fb615bSYuanjiang Yu } 1255e2fb615bSYuanjiang Yu 1256e2fb615bSYuanjiang Yu return 0; 1257e2fb615bSYuanjiang Yu 1258e2fb615bSYuanjiang Yu disable_int: 1259e2fb615bSYuanjiang Yu regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1260e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT, 0); 1261e2fb615bSYuanjiang Yu return ret; 1262e2fb615bSYuanjiang Yu } 1263e2fb615bSYuanjiang Yu #endif 1264e2fb615bSYuanjiang Yu 1265e2fb615bSYuanjiang Yu static const struct dev_pm_ops sc27xx_fgu_pm_ops = { 1266e2fb615bSYuanjiang Yu SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume) 1267e2fb615bSYuanjiang Yu }; 1268e2fb615bSYuanjiang Yu 1269195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = { 1270195ca170SBaolin Wang { .compatible = "sprd,sc2731-fgu", }, 1271195ca170SBaolin Wang { } 1272195ca170SBaolin Wang }; 1273195ca170SBaolin Wang 1274195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = { 1275195ca170SBaolin Wang .probe = sc27xx_fgu_probe, 1276195ca170SBaolin Wang .driver = { 1277195ca170SBaolin Wang .name = "sc27xx-fgu", 1278195ca170SBaolin Wang .of_match_table = sc27xx_fgu_of_match, 1279e2fb615bSYuanjiang Yu .pm = &sc27xx_fgu_pm_ops, 1280195ca170SBaolin Wang } 1281195ca170SBaolin Wang }; 1282195ca170SBaolin Wang 1283195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver); 1284195ca170SBaolin Wang 1285195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver"); 1286195ca170SBaolin Wang MODULE_LICENSE("GPL v2"); 1287