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 65195ca170SBaolin Wang 66195ca170SBaolin Wang /* 67195ca170SBaolin Wang * struct sc27xx_fgu_data: describe the FGU device 68195ca170SBaolin Wang * @regmap: regmap for register access 69195ca170SBaolin Wang * @dev: platform device 70195ca170SBaolin Wang * @battery: battery power supply 71195ca170SBaolin Wang * @base: the base offset for the controller 72195ca170SBaolin Wang * @lock: protect the structure 73195ca170SBaolin Wang * @gpiod: GPIO for battery detection 74195ca170SBaolin Wang * @channel: IIO channel to get battery temperature 750a4f97a1SBaolin Wang * @charge_chan: IIO channel to get charge voltage 76195ca170SBaolin Wang * @internal_resist: the battery internal resistance in mOhm 77195ca170SBaolin Wang * @total_cap: the total capacity of the battery in mAh 78195ca170SBaolin Wang * @init_cap: the initial capacity of the battery in mAh 79edcb1c0aSYuanjiang Yu * @alarm_cap: the alarm capacity 80195ca170SBaolin Wang * @init_clbcnt: the initial coulomb counter 81195ca170SBaolin Wang * @max_volt: the maximum constant input voltage in millivolt 82edcb1c0aSYuanjiang Yu * @min_volt: the minimum drained battery voltage in microvolt 83195ca170SBaolin Wang * @table_len: the capacity table length 84*6af82888SYuanjiang Yu * @resist_table_len: the resistance table length 8565c9fab7SBaolin Wang * @cur_1000ma_adc: ADC value corresponding to 1000 mA 8665c9fab7SBaolin Wang * @vol_1000mv_adc: ADC value corresponding to 1000 mV 87195ca170SBaolin Wang * @cap_table: capacity table with corresponding ocv 88*6af82888SYuanjiang Yu * @resist_table: resistance percent table with corresponding temperature 89195ca170SBaolin Wang */ 90195ca170SBaolin Wang struct sc27xx_fgu_data { 91195ca170SBaolin Wang struct regmap *regmap; 92195ca170SBaolin Wang struct device *dev; 93195ca170SBaolin Wang struct power_supply *battery; 94195ca170SBaolin Wang u32 base; 95195ca170SBaolin Wang struct mutex lock; 96195ca170SBaolin Wang struct gpio_desc *gpiod; 97195ca170SBaolin Wang struct iio_channel *channel; 980a4f97a1SBaolin Wang struct iio_channel *charge_chan; 99195ca170SBaolin Wang bool bat_present; 100195ca170SBaolin Wang int internal_resist; 101195ca170SBaolin Wang int total_cap; 102195ca170SBaolin Wang int init_cap; 103edcb1c0aSYuanjiang Yu int alarm_cap; 104195ca170SBaolin Wang int init_clbcnt; 105195ca170SBaolin Wang int max_volt; 106edcb1c0aSYuanjiang Yu int min_volt; 107195ca170SBaolin Wang int table_len; 108*6af82888SYuanjiang Yu int resist_table_len; 10965c9fab7SBaolin Wang int cur_1000ma_adc; 11065c9fab7SBaolin Wang int vol_1000mv_adc; 111195ca170SBaolin Wang struct power_supply_battery_ocv_table *cap_table; 112*6af82888SYuanjiang Yu struct power_supply_resistance_temp_table *resist_table; 113195ca170SBaolin Wang }; 114195ca170SBaolin Wang 115edcb1c0aSYuanjiang Yu static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity); 11658066527SYuanjiang Yu static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, 11758066527SYuanjiang Yu int cap, bool int_mode); 1187cfd33d9SYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap); 119*6af82888SYuanjiang Yu static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp); 120edcb1c0aSYuanjiang Yu 121195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = { 122195ca170SBaolin Wang "sc2731_charger", 123195ca170SBaolin Wang "sc2720_charger", 124195ca170SBaolin Wang "sc2721_charger", 125195ca170SBaolin Wang "sc2723_charger", 126195ca170SBaolin Wang }; 127195ca170SBaolin Wang 12865c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc) 129195ca170SBaolin Wang { 13065c9fab7SBaolin Wang return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc); 131195ca170SBaolin Wang } 132195ca170SBaolin Wang 13365c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc) 134195ca170SBaolin Wang { 13565c9fab7SBaolin Wang return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); 136195ca170SBaolin Wang } 137195ca170SBaolin Wang 138edcb1c0aSYuanjiang Yu static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol) 139edcb1c0aSYuanjiang Yu { 140edcb1c0aSYuanjiang Yu return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000); 141edcb1c0aSYuanjiang Yu } 142edcb1c0aSYuanjiang Yu 1434a040e7cSYuanjiang Yu static bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data) 1444a040e7cSYuanjiang Yu { 1454a040e7cSYuanjiang Yu int ret, status, cap, mode; 1464a040e7cSYuanjiang Yu 1474a040e7cSYuanjiang Yu ret = regmap_read(data->regmap, 1484a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_STATUS, &status); 1494a040e7cSYuanjiang Yu if (ret) 1504a040e7cSYuanjiang Yu return false; 1514a040e7cSYuanjiang Yu 1524a040e7cSYuanjiang Yu /* 1534a040e7cSYuanjiang Yu * We use low 4 bits to save the last battery capacity and high 12 bits 1544a040e7cSYuanjiang Yu * to save the system boot mode. 1554a040e7cSYuanjiang Yu */ 1564a040e7cSYuanjiang Yu mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT; 1574a040e7cSYuanjiang Yu cap = status & SC27XX_FGU_CAP_AREA_MASK; 1584a040e7cSYuanjiang Yu 1594a040e7cSYuanjiang Yu /* 1604a040e7cSYuanjiang Yu * When FGU has been powered down, the user area registers became 1614a040e7cSYuanjiang Yu * default value (0xffff), which can be used to valid if the system is 1624a040e7cSYuanjiang Yu * first power on or not. 1634a040e7cSYuanjiang Yu */ 1644a040e7cSYuanjiang Yu if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP) 1654a040e7cSYuanjiang Yu return true; 1664a040e7cSYuanjiang Yu 1674a040e7cSYuanjiang Yu return false; 1684a040e7cSYuanjiang Yu } 1694a040e7cSYuanjiang Yu 1704a040e7cSYuanjiang Yu static int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data, 1714a040e7cSYuanjiang Yu int boot_mode) 1724a040e7cSYuanjiang Yu { 1734a040e7cSYuanjiang Yu int ret; 1744a040e7cSYuanjiang Yu 1754a040e7cSYuanjiang Yu ret = regmap_update_bits(data->regmap, 1764a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 1774a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 1784a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK); 1794a040e7cSYuanjiang Yu if (ret) 1804a040e7cSYuanjiang Yu return ret; 1814a040e7cSYuanjiang Yu 182d3e67c94SYuanjiang Yu /* 183d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 184d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 185d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 186d3e67c94SYuanjiang Yu * successfully according to the datasheet. 187d3e67c94SYuanjiang Yu */ 188d3e67c94SYuanjiang Yu udelay(200); 189d3e67c94SYuanjiang Yu 190d3e67c94SYuanjiang Yu ret = regmap_update_bits(data->regmap, 1914a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_SET, 1924a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 1934a040e7cSYuanjiang Yu boot_mode << SC27XX_FGU_MODE_AREA_SHIFT); 194d3e67c94SYuanjiang Yu if (ret) 195d3e67c94SYuanjiang Yu return ret; 196d3e67c94SYuanjiang Yu 197d3e67c94SYuanjiang Yu /* 198d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 199d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 200d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 201d3e67c94SYuanjiang Yu * successfully according to the datasheet. 202d3e67c94SYuanjiang Yu */ 203d3e67c94SYuanjiang Yu udelay(200); 204d3e67c94SYuanjiang Yu 205d3e67c94SYuanjiang Yu /* 206d3e67c94SYuanjiang Yu * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to 207d3e67c94SYuanjiang Yu * make the user area data available, otherwise we can not save the user 208d3e67c94SYuanjiang Yu * area data. 209d3e67c94SYuanjiang Yu */ 210d3e67c94SYuanjiang Yu return regmap_update_bits(data->regmap, 211d3e67c94SYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 212d3e67c94SYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 0); 2134a040e7cSYuanjiang Yu } 2144a040e7cSYuanjiang Yu 2154a040e7cSYuanjiang Yu static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap) 2164a040e7cSYuanjiang Yu { 2174a040e7cSYuanjiang Yu int ret; 2184a040e7cSYuanjiang Yu 2194a040e7cSYuanjiang Yu ret = regmap_update_bits(data->regmap, 2204a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 2214a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, 2224a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK); 2234a040e7cSYuanjiang Yu if (ret) 2244a040e7cSYuanjiang Yu return ret; 2254a040e7cSYuanjiang Yu 226d3e67c94SYuanjiang Yu /* 227d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 228d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 229d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 230d3e67c94SYuanjiang Yu * successfully according to the datasheet. 231d3e67c94SYuanjiang Yu */ 232d3e67c94SYuanjiang Yu udelay(200); 233d3e67c94SYuanjiang Yu 234d3e67c94SYuanjiang Yu ret = regmap_update_bits(data->regmap, 2354a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_SET, 2364a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, cap); 237d3e67c94SYuanjiang Yu if (ret) 238d3e67c94SYuanjiang Yu return ret; 239d3e67c94SYuanjiang Yu 240d3e67c94SYuanjiang Yu /* 241d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 242d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 243d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 244d3e67c94SYuanjiang Yu * successfully according to the datasheet. 245d3e67c94SYuanjiang Yu */ 246d3e67c94SYuanjiang Yu udelay(200); 247d3e67c94SYuanjiang Yu 248d3e67c94SYuanjiang Yu /* 249d3e67c94SYuanjiang Yu * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to 250d3e67c94SYuanjiang Yu * make the user area data available, otherwise we can not save the user 251d3e67c94SYuanjiang Yu * area data. 252d3e67c94SYuanjiang Yu */ 253d3e67c94SYuanjiang Yu return regmap_update_bits(data->regmap, 254d3e67c94SYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 255d3e67c94SYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, 0); 2564a040e7cSYuanjiang Yu } 2574a040e7cSYuanjiang Yu 2584a040e7cSYuanjiang Yu static int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap) 2594a040e7cSYuanjiang Yu { 2604a040e7cSYuanjiang Yu int ret, value; 2614a040e7cSYuanjiang Yu 2624a040e7cSYuanjiang Yu ret = regmap_read(data->regmap, 2634a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_STATUS, &value); 2644a040e7cSYuanjiang Yu if (ret) 2654a040e7cSYuanjiang Yu return ret; 2664a040e7cSYuanjiang Yu 2674a040e7cSYuanjiang Yu *cap = value & SC27XX_FGU_CAP_AREA_MASK; 2684a040e7cSYuanjiang Yu return 0; 2694a040e7cSYuanjiang Yu } 2704a040e7cSYuanjiang Yu 271195ca170SBaolin Wang /* 272195ca170SBaolin Wang * When system boots on, we can not read battery capacity from coulomb 273195ca170SBaolin Wang * registers, since now the coulomb registers are invalid. So we should 274195ca170SBaolin Wang * calculate the battery open circuit voltage, and get current battery 275195ca170SBaolin Wang * capacity according to the capacity table. 276195ca170SBaolin Wang */ 277195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) 278195ca170SBaolin Wang { 279195ca170SBaolin Wang int volt, cur, oci, ocv, ret; 2804a040e7cSYuanjiang Yu bool is_first_poweron = sc27xx_fgu_is_first_poweron(data); 2814a040e7cSYuanjiang Yu 2824a040e7cSYuanjiang Yu /* 2834a040e7cSYuanjiang Yu * If system is not the first power on, we should use the last saved 2844a040e7cSYuanjiang Yu * battery capacity as the initial battery capacity. Otherwise we should 2854a040e7cSYuanjiang Yu * re-calculate the initial battery capacity. 2864a040e7cSYuanjiang Yu */ 2874a040e7cSYuanjiang Yu if (!is_first_poweron) { 2884a040e7cSYuanjiang Yu ret = sc27xx_fgu_read_last_cap(data, cap); 2894a040e7cSYuanjiang Yu if (ret) 2904a040e7cSYuanjiang Yu return ret; 2914a040e7cSYuanjiang Yu 2924a040e7cSYuanjiang Yu return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 2934a040e7cSYuanjiang Yu } 294195ca170SBaolin Wang 295195ca170SBaolin Wang /* 296195ca170SBaolin Wang * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved 297195ca170SBaolin Wang * the first sampled open circuit current. 298195ca170SBaolin Wang */ 299195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL, 300195ca170SBaolin Wang &cur); 301195ca170SBaolin Wang if (ret) 302195ca170SBaolin Wang return ret; 303195ca170SBaolin Wang 304195ca170SBaolin Wang cur <<= 1; 30565c9fab7SBaolin Wang oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 306195ca170SBaolin Wang 307195ca170SBaolin Wang /* 308195ca170SBaolin Wang * Should get the OCV from SC27XX_FGU_POCV register at the system 309195ca170SBaolin Wang * beginning. It is ADC values reading from registers which need to 310195ca170SBaolin Wang * convert the corresponding voltage. 311195ca170SBaolin Wang */ 312195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt); 313195ca170SBaolin Wang if (ret) 314195ca170SBaolin Wang return ret; 315195ca170SBaolin Wang 31665c9fab7SBaolin Wang volt = sc27xx_fgu_adc_to_voltage(data, volt); 317195ca170SBaolin Wang ocv = volt * 1000 - oci * data->internal_resist; 318195ca170SBaolin Wang 319195ca170SBaolin Wang /* 320195ca170SBaolin Wang * Parse the capacity table to look up the correct capacity percent 321195ca170SBaolin Wang * according to current battery's corresponding OCV values. 322195ca170SBaolin Wang */ 323195ca170SBaolin Wang *cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len, 324195ca170SBaolin Wang ocv); 325195ca170SBaolin Wang 3264a040e7cSYuanjiang Yu ret = sc27xx_fgu_save_last_cap(data, *cap); 3274a040e7cSYuanjiang Yu if (ret) 3284a040e7cSYuanjiang Yu return ret; 3294a040e7cSYuanjiang Yu 3304a040e7cSYuanjiang Yu return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 331195ca170SBaolin Wang } 332195ca170SBaolin Wang 333195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt) 334195ca170SBaolin Wang { 335195ca170SBaolin Wang int ret; 336195ca170SBaolin Wang 337195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 338195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETL, 339195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, clbcnt); 340195ca170SBaolin Wang if (ret) 341195ca170SBaolin Wang return ret; 342195ca170SBaolin Wang 343195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 344195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETH, 345195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, 346195ca170SBaolin Wang clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 347195ca170SBaolin Wang if (ret) 348195ca170SBaolin Wang return ret; 349195ca170SBaolin Wang 350195ca170SBaolin Wang return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START, 351195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN, 352195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN); 353195ca170SBaolin Wang } 354195ca170SBaolin Wang 355195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt) 356195ca170SBaolin Wang { 357195ca170SBaolin Wang int ccl, cch, ret; 358195ca170SBaolin Wang 359195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL, 360195ca170SBaolin Wang &ccl); 361195ca170SBaolin Wang if (ret) 362195ca170SBaolin Wang return ret; 363195ca170SBaolin Wang 364195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH, 365195ca170SBaolin Wang &cch); 366195ca170SBaolin Wang if (ret) 367195ca170SBaolin Wang return ret; 368195ca170SBaolin Wang 369195ca170SBaolin Wang *clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK; 370195ca170SBaolin Wang *clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT; 371195ca170SBaolin Wang 372195ca170SBaolin Wang return 0; 373195ca170SBaolin Wang } 374195ca170SBaolin Wang 375195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap) 376195ca170SBaolin Wang { 377195ca170SBaolin Wang int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp; 378195ca170SBaolin Wang 379195ca170SBaolin Wang /* Get current coulomb counters firstly */ 380195ca170SBaolin Wang ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt); 381195ca170SBaolin Wang if (ret) 382195ca170SBaolin Wang return ret; 383195ca170SBaolin Wang 384195ca170SBaolin Wang delta_clbcnt = cur_clbcnt - data->init_clbcnt; 385195ca170SBaolin Wang 386195ca170SBaolin Wang /* 387195ca170SBaolin Wang * Convert coulomb counter to delta capacity (mAh), and set multiplier 3887384b0e7SYuanjiang Yu * as 10 to improve the precision. 389195ca170SBaolin Wang */ 3907384b0e7SYuanjiang Yu temp = DIV_ROUND_CLOSEST(delta_clbcnt * 10, 36 * SC27XX_FGU_SAMPLE_HZ); 3917384b0e7SYuanjiang Yu temp = sc27xx_fgu_adc_to_current(data, temp / 1000); 392195ca170SBaolin Wang 393195ca170SBaolin Wang /* 394195ca170SBaolin Wang * Convert to capacity percent of the battery total capacity, 395195ca170SBaolin Wang * and multiplier is 100 too. 396195ca170SBaolin Wang */ 397195ca170SBaolin Wang delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap); 398195ca170SBaolin Wang *cap = delta_cap + data->init_cap; 399195ca170SBaolin Wang 40058066527SYuanjiang Yu /* Calibrate the battery capacity in a normal range. */ 40158066527SYuanjiang Yu sc27xx_fgu_capacity_calibration(data, *cap, false); 40258066527SYuanjiang Yu 403195ca170SBaolin Wang return 0; 404195ca170SBaolin Wang } 405195ca170SBaolin Wang 406195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val) 407195ca170SBaolin Wang { 408195ca170SBaolin Wang int ret, vol; 409195ca170SBaolin Wang 410195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol); 411195ca170SBaolin Wang if (ret) 412195ca170SBaolin Wang return ret; 413195ca170SBaolin Wang 414195ca170SBaolin Wang /* 415195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 416195ca170SBaolin Wang * corresponding voltage values. 417195ca170SBaolin Wang */ 41865c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_voltage(data, vol); 419195ca170SBaolin Wang 420195ca170SBaolin Wang return 0; 421195ca170SBaolin Wang } 422195ca170SBaolin Wang 423195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val) 424195ca170SBaolin Wang { 425195ca170SBaolin Wang int ret, cur; 426195ca170SBaolin Wang 427195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur); 428195ca170SBaolin Wang if (ret) 429195ca170SBaolin Wang return ret; 430195ca170SBaolin Wang 431195ca170SBaolin Wang /* 432195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 433195ca170SBaolin Wang * corresponding current values. 434195ca170SBaolin Wang */ 43565c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 436195ca170SBaolin Wang 437195ca170SBaolin Wang return 0; 438195ca170SBaolin Wang } 439195ca170SBaolin Wang 440195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) 441195ca170SBaolin Wang { 442*6af82888SYuanjiang Yu int vol, cur, ret, temp, resistance; 443195ca170SBaolin Wang 444195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 445195ca170SBaolin Wang if (ret) 446195ca170SBaolin Wang return ret; 447195ca170SBaolin Wang 448195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &cur); 449195ca170SBaolin Wang if (ret) 450195ca170SBaolin Wang return ret; 451195ca170SBaolin Wang 452*6af82888SYuanjiang Yu resistance = data->internal_resist; 453*6af82888SYuanjiang Yu if (data->resist_table_len > 0) { 454*6af82888SYuanjiang Yu ret = sc27xx_fgu_get_temp(data, &temp); 455*6af82888SYuanjiang Yu if (ret) 456*6af82888SYuanjiang Yu return ret; 457*6af82888SYuanjiang Yu 458*6af82888SYuanjiang Yu resistance = power_supply_temp2resist_simple(data->resist_table, 459*6af82888SYuanjiang Yu data->resist_table_len, temp); 460*6af82888SYuanjiang Yu resistance = data->internal_resist * resistance / 100; 461*6af82888SYuanjiang Yu } 462*6af82888SYuanjiang Yu 463195ca170SBaolin Wang /* Return the battery OCV in micro volts. */ 464*6af82888SYuanjiang Yu *val = vol * 1000 - cur * resistance; 465195ca170SBaolin Wang 466195ca170SBaolin Wang return 0; 467195ca170SBaolin Wang } 468195ca170SBaolin Wang 4690a4f97a1SBaolin Wang static int sc27xx_fgu_get_charge_vol(struct sc27xx_fgu_data *data, int *val) 4700a4f97a1SBaolin Wang { 4710a4f97a1SBaolin Wang int ret, vol; 4720a4f97a1SBaolin Wang 4730a4f97a1SBaolin Wang ret = iio_read_channel_processed(data->charge_chan, &vol); 4740a4f97a1SBaolin Wang if (ret < 0) 4750a4f97a1SBaolin Wang return ret; 4760a4f97a1SBaolin Wang 4770a4f97a1SBaolin Wang *val = vol * 1000; 4780a4f97a1SBaolin Wang return 0; 4790a4f97a1SBaolin Wang } 4800a4f97a1SBaolin Wang 481195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp) 482195ca170SBaolin Wang { 483195ca170SBaolin Wang return iio_read_channel_processed(data->channel, temp); 484195ca170SBaolin Wang } 485195ca170SBaolin Wang 486195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health) 487195ca170SBaolin Wang { 488195ca170SBaolin Wang int ret, vol; 489195ca170SBaolin Wang 490195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 491195ca170SBaolin Wang if (ret) 492195ca170SBaolin Wang return ret; 493195ca170SBaolin Wang 494195ca170SBaolin Wang if (vol > data->max_volt) 495195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 496195ca170SBaolin Wang else 497195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_GOOD; 498195ca170SBaolin Wang 499195ca170SBaolin Wang return 0; 500195ca170SBaolin Wang } 501195ca170SBaolin Wang 502195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status) 503195ca170SBaolin Wang { 504195ca170SBaolin Wang union power_supply_propval val; 505195ca170SBaolin Wang struct power_supply *psy; 506195ca170SBaolin Wang int i, ret = -EINVAL; 507195ca170SBaolin Wang 508195ca170SBaolin Wang for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) { 509195ca170SBaolin Wang psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]); 510195ca170SBaolin Wang if (!psy) 511195ca170SBaolin Wang continue; 512195ca170SBaolin Wang 513195ca170SBaolin Wang ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, 514195ca170SBaolin Wang &val); 515195ca170SBaolin Wang power_supply_put(psy); 516195ca170SBaolin Wang if (ret) 517195ca170SBaolin Wang return ret; 518195ca170SBaolin Wang 519195ca170SBaolin Wang *status = val.intval; 520195ca170SBaolin Wang } 521195ca170SBaolin Wang 522195ca170SBaolin Wang return ret; 523195ca170SBaolin Wang } 524195ca170SBaolin Wang 525195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy, 526195ca170SBaolin Wang enum power_supply_property psp, 527195ca170SBaolin Wang union power_supply_propval *val) 528195ca170SBaolin Wang { 529195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 530195ca170SBaolin Wang int ret = 0; 531195ca170SBaolin Wang int value; 532195ca170SBaolin Wang 533195ca170SBaolin Wang mutex_lock(&data->lock); 534195ca170SBaolin Wang 535195ca170SBaolin Wang switch (psp) { 536195ca170SBaolin Wang case POWER_SUPPLY_PROP_STATUS: 537195ca170SBaolin Wang ret = sc27xx_fgu_get_status(data, &value); 538195ca170SBaolin Wang if (ret) 539195ca170SBaolin Wang goto error; 540195ca170SBaolin Wang 541195ca170SBaolin Wang val->intval = value; 542195ca170SBaolin Wang break; 543195ca170SBaolin Wang 544195ca170SBaolin Wang case POWER_SUPPLY_PROP_HEALTH: 545195ca170SBaolin Wang ret = sc27xx_fgu_get_health(data, &value); 546195ca170SBaolin Wang if (ret) 547195ca170SBaolin Wang goto error; 548195ca170SBaolin Wang 549195ca170SBaolin Wang val->intval = value; 550195ca170SBaolin Wang break; 551195ca170SBaolin Wang 552195ca170SBaolin Wang case POWER_SUPPLY_PROP_PRESENT: 553195ca170SBaolin Wang val->intval = data->bat_present; 554195ca170SBaolin Wang break; 555195ca170SBaolin Wang 556195ca170SBaolin Wang case POWER_SUPPLY_PROP_TEMP: 557195ca170SBaolin Wang ret = sc27xx_fgu_get_temp(data, &value); 558195ca170SBaolin Wang if (ret) 559195ca170SBaolin Wang goto error; 560195ca170SBaolin Wang 561195ca170SBaolin Wang val->intval = value; 562195ca170SBaolin Wang break; 563195ca170SBaolin Wang 564195ca170SBaolin Wang case POWER_SUPPLY_PROP_TECHNOLOGY: 565195ca170SBaolin Wang val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 566195ca170SBaolin Wang break; 567195ca170SBaolin Wang 568195ca170SBaolin Wang case POWER_SUPPLY_PROP_CAPACITY: 569195ca170SBaolin Wang ret = sc27xx_fgu_get_capacity(data, &value); 570195ca170SBaolin Wang if (ret) 571195ca170SBaolin Wang goto error; 572195ca170SBaolin Wang 573195ca170SBaolin Wang val->intval = value; 574195ca170SBaolin Wang break; 575195ca170SBaolin Wang 576195ca170SBaolin Wang case POWER_SUPPLY_PROP_VOLTAGE_NOW: 577195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &value); 578195ca170SBaolin Wang if (ret) 579195ca170SBaolin Wang goto error; 580195ca170SBaolin Wang 581195ca170SBaolin Wang val->intval = value * 1000; 582195ca170SBaolin Wang break; 583195ca170SBaolin Wang 584195ca170SBaolin Wang case POWER_SUPPLY_PROP_VOLTAGE_OCV: 585195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_ocv(data, &value); 586195ca170SBaolin Wang if (ret) 587195ca170SBaolin Wang goto error; 588195ca170SBaolin Wang 589195ca170SBaolin Wang val->intval = value; 590195ca170SBaolin Wang break; 591195ca170SBaolin Wang 5920a4f97a1SBaolin Wang case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 5930a4f97a1SBaolin Wang ret = sc27xx_fgu_get_charge_vol(data, &value); 5940a4f97a1SBaolin Wang if (ret) 5950a4f97a1SBaolin Wang goto error; 5960a4f97a1SBaolin Wang 5970a4f97a1SBaolin Wang val->intval = value; 5980a4f97a1SBaolin Wang break; 5990a4f97a1SBaolin Wang 600195ca170SBaolin Wang case POWER_SUPPLY_PROP_CURRENT_NOW: 601195ca170SBaolin Wang case POWER_SUPPLY_PROP_CURRENT_AVG: 602195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &value); 603195ca170SBaolin Wang if (ret) 604195ca170SBaolin Wang goto error; 605195ca170SBaolin Wang 606195ca170SBaolin Wang val->intval = value * 1000; 607195ca170SBaolin Wang break; 608195ca170SBaolin Wang 6097cff19b9SYuanjiang Yu case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 6107cff19b9SYuanjiang Yu val->intval = data->total_cap * 1000; 6117cff19b9SYuanjiang Yu break; 6127cff19b9SYuanjiang Yu 613195ca170SBaolin Wang default: 614195ca170SBaolin Wang ret = -EINVAL; 615195ca170SBaolin Wang break; 616195ca170SBaolin Wang } 617195ca170SBaolin Wang 618195ca170SBaolin Wang error: 619195ca170SBaolin Wang mutex_unlock(&data->lock); 620195ca170SBaolin Wang return ret; 621195ca170SBaolin Wang } 622195ca170SBaolin Wang 6234a040e7cSYuanjiang Yu static int sc27xx_fgu_set_property(struct power_supply *psy, 6244a040e7cSYuanjiang Yu enum power_supply_property psp, 6254a040e7cSYuanjiang Yu const union power_supply_propval *val) 6264a040e7cSYuanjiang Yu { 6274a040e7cSYuanjiang Yu struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 6284a040e7cSYuanjiang Yu int ret; 6294a040e7cSYuanjiang Yu 6304a040e7cSYuanjiang Yu mutex_lock(&data->lock); 6314a040e7cSYuanjiang Yu 6327cfd33d9SYuanjiang Yu switch (psp) { 6337cfd33d9SYuanjiang Yu case POWER_SUPPLY_PROP_CAPACITY: 6344a040e7cSYuanjiang Yu ret = sc27xx_fgu_save_last_cap(data, val->intval); 6354a040e7cSYuanjiang Yu if (ret < 0) 6364a040e7cSYuanjiang Yu dev_err(data->dev, "failed to save battery capacity\n"); 6377cfd33d9SYuanjiang Yu break; 6387cfd33d9SYuanjiang Yu 6397cfd33d9SYuanjiang Yu case POWER_SUPPLY_PROP_CALIBRATE: 6407cfd33d9SYuanjiang Yu sc27xx_fgu_adjust_cap(data, val->intval); 6417cfd33d9SYuanjiang Yu ret = 0; 6427cfd33d9SYuanjiang Yu break; 6437cfd33d9SYuanjiang Yu 6447cfd33d9SYuanjiang Yu default: 6457cfd33d9SYuanjiang Yu ret = -EINVAL; 6467cfd33d9SYuanjiang Yu } 6477cfd33d9SYuanjiang Yu 6487cfd33d9SYuanjiang Yu mutex_unlock(&data->lock); 6494a040e7cSYuanjiang Yu 6504a040e7cSYuanjiang Yu return ret; 6514a040e7cSYuanjiang Yu } 6524a040e7cSYuanjiang Yu 653195ca170SBaolin Wang static void sc27xx_fgu_external_power_changed(struct power_supply *psy) 654195ca170SBaolin Wang { 655195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 656195ca170SBaolin Wang 657195ca170SBaolin Wang power_supply_changed(data->battery); 658195ca170SBaolin Wang } 659195ca170SBaolin Wang 6604a040e7cSYuanjiang Yu static int sc27xx_fgu_property_is_writeable(struct power_supply *psy, 6614a040e7cSYuanjiang Yu enum power_supply_property psp) 6624a040e7cSYuanjiang Yu { 6637cfd33d9SYuanjiang Yu return psp == POWER_SUPPLY_PROP_CAPACITY || 6647cfd33d9SYuanjiang Yu psp == POWER_SUPPLY_PROP_CALIBRATE; 6654a040e7cSYuanjiang Yu } 6664a040e7cSYuanjiang Yu 667195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = { 668195ca170SBaolin Wang POWER_SUPPLY_PROP_STATUS, 669195ca170SBaolin Wang POWER_SUPPLY_PROP_HEALTH, 670195ca170SBaolin Wang POWER_SUPPLY_PROP_PRESENT, 671195ca170SBaolin Wang POWER_SUPPLY_PROP_TEMP, 672195ca170SBaolin Wang POWER_SUPPLY_PROP_TECHNOLOGY, 673195ca170SBaolin Wang POWER_SUPPLY_PROP_CAPACITY, 674195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_NOW, 675195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_OCV, 676195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_NOW, 677195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_AVG, 6780a4f97a1SBaolin Wang POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 6797cff19b9SYuanjiang Yu POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 6807cfd33d9SYuanjiang Yu POWER_SUPPLY_PROP_CALIBRATE, 681195ca170SBaolin Wang }; 682195ca170SBaolin Wang 683195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = { 684195ca170SBaolin Wang .name = "sc27xx-fgu", 685195ca170SBaolin Wang .type = POWER_SUPPLY_TYPE_BATTERY, 686195ca170SBaolin Wang .properties = sc27xx_fgu_props, 687195ca170SBaolin Wang .num_properties = ARRAY_SIZE(sc27xx_fgu_props), 688195ca170SBaolin Wang .get_property = sc27xx_fgu_get_property, 6894a040e7cSYuanjiang Yu .set_property = sc27xx_fgu_set_property, 690195ca170SBaolin Wang .external_power_changed = sc27xx_fgu_external_power_changed, 6914a040e7cSYuanjiang Yu .property_is_writeable = sc27xx_fgu_property_is_writeable, 692195ca170SBaolin Wang }; 693195ca170SBaolin Wang 694edcb1c0aSYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap) 695edcb1c0aSYuanjiang Yu { 69658066527SYuanjiang Yu int ret; 69758066527SYuanjiang Yu 698edcb1c0aSYuanjiang Yu data->init_cap = cap; 69958066527SYuanjiang Yu ret = sc27xx_fgu_get_clbcnt(data, &data->init_clbcnt); 70058066527SYuanjiang Yu if (ret) 70158066527SYuanjiang Yu dev_err(data->dev, "failed to get init coulomb counter\n"); 70258066527SYuanjiang Yu } 70358066527SYuanjiang Yu 70458066527SYuanjiang Yu static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, 70558066527SYuanjiang Yu int cap, bool int_mode) 70658066527SYuanjiang Yu { 70758066527SYuanjiang Yu int ret, ocv, chg_sts, adc; 70858066527SYuanjiang Yu 70958066527SYuanjiang Yu ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 71058066527SYuanjiang Yu if (ret) { 71158066527SYuanjiang Yu dev_err(data->dev, "get battery ocv error.\n"); 71258066527SYuanjiang Yu return; 71358066527SYuanjiang Yu } 71458066527SYuanjiang Yu 71558066527SYuanjiang Yu ret = sc27xx_fgu_get_status(data, &chg_sts); 71658066527SYuanjiang Yu if (ret) { 71758066527SYuanjiang Yu dev_err(data->dev, "get charger status error.\n"); 71858066527SYuanjiang Yu return; 71958066527SYuanjiang Yu } 72058066527SYuanjiang Yu 72158066527SYuanjiang Yu /* 72258066527SYuanjiang Yu * If we are in charging mode, then we do not need to calibrate the 72358066527SYuanjiang Yu * lower capacity. 72458066527SYuanjiang Yu */ 72558066527SYuanjiang Yu if (chg_sts == POWER_SUPPLY_STATUS_CHARGING) 72658066527SYuanjiang Yu return; 72758066527SYuanjiang Yu 72858066527SYuanjiang Yu if ((ocv > data->cap_table[0].ocv && cap < 100) || cap > 100) { 72958066527SYuanjiang Yu /* 73058066527SYuanjiang Yu * If current OCV value is larger than the max OCV value in 73158066527SYuanjiang Yu * OCV table, or the current capacity is larger than 100, 73258066527SYuanjiang Yu * we should force the inititial capacity to 100. 73358066527SYuanjiang Yu */ 73458066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, 100); 73558066527SYuanjiang Yu } else if (ocv <= data->cap_table[data->table_len - 1].ocv) { 73658066527SYuanjiang Yu /* 73758066527SYuanjiang Yu * If current OCV value is leass than the minimum OCV value in 73858066527SYuanjiang Yu * OCV table, we should force the inititial capacity to 0. 73958066527SYuanjiang Yu */ 74058066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, 0); 74158066527SYuanjiang Yu } else if ((ocv > data->cap_table[data->table_len - 1].ocv && cap <= 0) || 74258066527SYuanjiang Yu (ocv > data->min_volt && cap <= data->alarm_cap)) { 74358066527SYuanjiang Yu /* 74458066527SYuanjiang Yu * If current OCV value is not matchable with current capacity, 74558066527SYuanjiang Yu * we should re-calculate current capacity by looking up the 74658066527SYuanjiang Yu * OCV table. 74758066527SYuanjiang Yu */ 74858066527SYuanjiang Yu int cur_cap = power_supply_ocv2cap_simple(data->cap_table, 74958066527SYuanjiang Yu data->table_len, ocv); 75058066527SYuanjiang Yu 75158066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, cur_cap); 75258066527SYuanjiang Yu } else if (ocv <= data->min_volt) { 75358066527SYuanjiang Yu /* 75458066527SYuanjiang Yu * If current OCV value is less than the low alarm voltage, but 75558066527SYuanjiang Yu * current capacity is larger than the alarm capacity, we should 75658066527SYuanjiang Yu * adjust the inititial capacity to alarm capacity. 75758066527SYuanjiang Yu */ 75858066527SYuanjiang Yu if (cap > data->alarm_cap) { 75958066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, data->alarm_cap); 76058066527SYuanjiang Yu } else { 76158066527SYuanjiang Yu int cur_cap; 76258066527SYuanjiang Yu 76358066527SYuanjiang Yu /* 76458066527SYuanjiang Yu * If current capacity is equal with 0 or less than 0 76558066527SYuanjiang Yu * (some error occurs), we should adjust inititial 76658066527SYuanjiang Yu * capacity to the capacity corresponding to current OCV 76758066527SYuanjiang Yu * value. 76858066527SYuanjiang Yu */ 76958066527SYuanjiang Yu cur_cap = power_supply_ocv2cap_simple(data->cap_table, 77058066527SYuanjiang Yu data->table_len, 77158066527SYuanjiang Yu ocv); 77258066527SYuanjiang Yu sc27xx_fgu_adjust_cap(data, cur_cap); 77358066527SYuanjiang Yu } 77458066527SYuanjiang Yu 77558066527SYuanjiang Yu if (!int_mode) 77658066527SYuanjiang Yu return; 77758066527SYuanjiang Yu 77858066527SYuanjiang Yu /* 77958066527SYuanjiang Yu * After adjusting the battery capacity, we should set the 78058066527SYuanjiang Yu * lowest alarm voltage instead. 78158066527SYuanjiang Yu */ 78258066527SYuanjiang Yu data->min_volt = data->cap_table[data->table_len - 1].ocv; 78358066527SYuanjiang Yu data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, 78458066527SYuanjiang Yu data->table_len, 78558066527SYuanjiang Yu data->min_volt); 78658066527SYuanjiang Yu 78758066527SYuanjiang Yu adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 78858066527SYuanjiang Yu regmap_update_bits(data->regmap, 78958066527SYuanjiang Yu data->base + SC27XX_FGU_LOW_OVERLOAD, 79058066527SYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_MASK, adc); 79158066527SYuanjiang Yu } 792edcb1c0aSYuanjiang Yu } 793edcb1c0aSYuanjiang Yu 794edcb1c0aSYuanjiang Yu static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id) 795edcb1c0aSYuanjiang Yu { 796edcb1c0aSYuanjiang Yu struct sc27xx_fgu_data *data = dev_id; 79758066527SYuanjiang Yu int ret, cap; 798edcb1c0aSYuanjiang Yu u32 status; 799edcb1c0aSYuanjiang Yu 800edcb1c0aSYuanjiang Yu mutex_lock(&data->lock); 801edcb1c0aSYuanjiang Yu 802edcb1c0aSYuanjiang Yu ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS, 803edcb1c0aSYuanjiang Yu &status); 804edcb1c0aSYuanjiang Yu if (ret) 805edcb1c0aSYuanjiang Yu goto out; 806edcb1c0aSYuanjiang Yu 807edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 808edcb1c0aSYuanjiang Yu status, status); 809edcb1c0aSYuanjiang Yu if (ret) 810edcb1c0aSYuanjiang Yu goto out; 811edcb1c0aSYuanjiang Yu 812edcb1c0aSYuanjiang Yu /* 813edcb1c0aSYuanjiang Yu * When low overload voltage interrupt happens, we should calibrate the 814edcb1c0aSYuanjiang Yu * battery capacity in lower voltage stage. 815edcb1c0aSYuanjiang Yu */ 816edcb1c0aSYuanjiang Yu if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT)) 817edcb1c0aSYuanjiang Yu goto out; 818edcb1c0aSYuanjiang Yu 819edcb1c0aSYuanjiang Yu ret = sc27xx_fgu_get_capacity(data, &cap); 820edcb1c0aSYuanjiang Yu if (ret) 821edcb1c0aSYuanjiang Yu goto out; 822edcb1c0aSYuanjiang Yu 82358066527SYuanjiang Yu sc27xx_fgu_capacity_calibration(data, cap, true); 824edcb1c0aSYuanjiang Yu 825edcb1c0aSYuanjiang Yu out: 826edcb1c0aSYuanjiang Yu mutex_unlock(&data->lock); 827edcb1c0aSYuanjiang Yu 828edcb1c0aSYuanjiang Yu power_supply_changed(data->battery); 829edcb1c0aSYuanjiang Yu return IRQ_HANDLED; 830edcb1c0aSYuanjiang Yu } 831edcb1c0aSYuanjiang Yu 832195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id) 833195ca170SBaolin Wang { 834195ca170SBaolin Wang struct sc27xx_fgu_data *data = dev_id; 835195ca170SBaolin Wang int state; 836195ca170SBaolin Wang 837195ca170SBaolin Wang mutex_lock(&data->lock); 838195ca170SBaolin Wang 839195ca170SBaolin Wang state = gpiod_get_value_cansleep(data->gpiod); 840195ca170SBaolin Wang if (state < 0) { 841195ca170SBaolin Wang dev_err(data->dev, "failed to get gpio state\n"); 842195ca170SBaolin Wang mutex_unlock(&data->lock); 843195ca170SBaolin Wang return IRQ_RETVAL(state); 844195ca170SBaolin Wang } 845195ca170SBaolin Wang 846195ca170SBaolin Wang data->bat_present = !!state; 847195ca170SBaolin Wang 848195ca170SBaolin Wang mutex_unlock(&data->lock); 849195ca170SBaolin Wang 850195ca170SBaolin Wang power_supply_changed(data->battery); 851195ca170SBaolin Wang return IRQ_HANDLED; 852195ca170SBaolin Wang } 853195ca170SBaolin Wang 854195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data) 855195ca170SBaolin Wang { 856195ca170SBaolin Wang struct sc27xx_fgu_data *data = _data; 857195ca170SBaolin Wang 858195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 859195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 860195ca170SBaolin Wang } 861195ca170SBaolin Wang 862195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity) 863195ca170SBaolin Wang { 864195ca170SBaolin Wang /* 865195ca170SBaolin Wang * Get current capacity (mAh) = battery total capacity (mAh) * 866195ca170SBaolin Wang * current capacity percent (capacity / 100). 867195ca170SBaolin Wang */ 868195ca170SBaolin Wang int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100); 869195ca170SBaolin Wang 870195ca170SBaolin Wang /* 871195ca170SBaolin Wang * Convert current capacity (mAh) to coulomb counter according to the 872195ca170SBaolin Wang * formula: 1 mAh =3.6 coulomb. 873195ca170SBaolin Wang */ 8747384b0e7SYuanjiang Yu return DIV_ROUND_CLOSEST(cur_cap * 36 * data->cur_1000ma_adc * SC27XX_FGU_SAMPLE_HZ, 10); 875195ca170SBaolin Wang } 876195ca170SBaolin Wang 87765c9fab7SBaolin Wang static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) 87865c9fab7SBaolin Wang { 87965c9fab7SBaolin Wang struct nvmem_cell *cell; 88065c9fab7SBaolin Wang int calib_data, cal_4200mv; 88165c9fab7SBaolin Wang void *buf; 88265c9fab7SBaolin Wang size_t len; 88365c9fab7SBaolin Wang 88465c9fab7SBaolin Wang cell = nvmem_cell_get(data->dev, "fgu_calib"); 88565c9fab7SBaolin Wang if (IS_ERR(cell)) 88665c9fab7SBaolin Wang return PTR_ERR(cell); 88765c9fab7SBaolin Wang 88865c9fab7SBaolin Wang buf = nvmem_cell_read(cell, &len); 88965c9fab7SBaolin Wang nvmem_cell_put(cell); 89065c9fab7SBaolin Wang 89165c9fab7SBaolin Wang if (IS_ERR(buf)) 89265c9fab7SBaolin Wang return PTR_ERR(buf); 89365c9fab7SBaolin Wang 89465c9fab7SBaolin Wang memcpy(&calib_data, buf, min(len, sizeof(u32))); 89565c9fab7SBaolin Wang 89665c9fab7SBaolin Wang /* 89765c9fab7SBaolin Wang * Get the ADC value corresponding to 4200 mV from eFuse controller 89865c9fab7SBaolin Wang * according to below formula. Then convert to ADC values corresponding 89965c9fab7SBaolin Wang * to 1000 mV and 1000 mA. 90065c9fab7SBaolin Wang */ 90165c9fab7SBaolin Wang cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256; 90265c9fab7SBaolin Wang data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42); 90365c9fab7SBaolin Wang data->cur_1000ma_adc = data->vol_1000mv_adc * 4; 90465c9fab7SBaolin Wang 90565c9fab7SBaolin Wang kfree(buf); 90665c9fab7SBaolin Wang return 0; 90765c9fab7SBaolin Wang } 90865c9fab7SBaolin Wang 909195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) 910195ca170SBaolin Wang { 911195ca170SBaolin Wang struct power_supply_battery_info info = { }; 912195ca170SBaolin Wang struct power_supply_battery_ocv_table *table; 913edcb1c0aSYuanjiang Yu int ret, delta_clbcnt, alarm_adc; 914195ca170SBaolin Wang 915195ca170SBaolin Wang ret = power_supply_get_battery_info(data->battery, &info); 916195ca170SBaolin Wang if (ret) { 917195ca170SBaolin Wang dev_err(data->dev, "failed to get battery information\n"); 918195ca170SBaolin Wang return ret; 919195ca170SBaolin Wang } 920195ca170SBaolin Wang 921195ca170SBaolin Wang data->total_cap = info.charge_full_design_uah / 1000; 922195ca170SBaolin Wang data->max_volt = info.constant_charge_voltage_max_uv / 1000; 923195ca170SBaolin Wang data->internal_resist = info.factory_internal_resistance_uohm / 1000; 924edcb1c0aSYuanjiang Yu data->min_volt = info.voltage_min_design_uv; 925195ca170SBaolin Wang 926195ca170SBaolin Wang /* 927195ca170SBaolin Wang * For SC27XX fuel gauge device, we only use one ocv-capacity 928195ca170SBaolin Wang * table in normal temperature 20 Celsius. 929195ca170SBaolin Wang */ 930195ca170SBaolin Wang table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len); 931195ca170SBaolin Wang if (!table) 932195ca170SBaolin Wang return -EINVAL; 933195ca170SBaolin Wang 934195ca170SBaolin Wang data->cap_table = devm_kmemdup(data->dev, table, 935195ca170SBaolin Wang data->table_len * sizeof(*table), 936195ca170SBaolin Wang GFP_KERNEL); 937195ca170SBaolin Wang if (!data->cap_table) { 938195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 939195ca170SBaolin Wang return -ENOMEM; 940195ca170SBaolin Wang } 941195ca170SBaolin Wang 942edcb1c0aSYuanjiang Yu data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, 943edcb1c0aSYuanjiang Yu data->table_len, 944edcb1c0aSYuanjiang Yu data->min_volt); 945ff062d06SYuanjiang Yu if (!data->alarm_cap) 946ff062d06SYuanjiang Yu data->alarm_cap += 1; 947edcb1c0aSYuanjiang Yu 948*6af82888SYuanjiang Yu data->resist_table_len = info.resist_table_size; 949*6af82888SYuanjiang Yu if (data->resist_table_len > 0) { 950*6af82888SYuanjiang Yu data->resist_table = devm_kmemdup(data->dev, info.resist_table, 951*6af82888SYuanjiang Yu data->resist_table_len * 952*6af82888SYuanjiang Yu sizeof(struct power_supply_resistance_temp_table), 953*6af82888SYuanjiang Yu GFP_KERNEL); 954*6af82888SYuanjiang Yu if (!data->resist_table) { 955*6af82888SYuanjiang Yu power_supply_put_battery_info(data->battery, &info); 956*6af82888SYuanjiang Yu return -ENOMEM; 957*6af82888SYuanjiang Yu } 958*6af82888SYuanjiang Yu } 959*6af82888SYuanjiang Yu 960195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 961195ca170SBaolin Wang 96265c9fab7SBaolin Wang ret = sc27xx_fgu_calibration(data); 96365c9fab7SBaolin Wang if (ret) 96465c9fab7SBaolin Wang return ret; 96565c9fab7SBaolin Wang 966195ca170SBaolin Wang /* Enable the FGU module */ 967195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, 968195ca170SBaolin Wang SC27XX_FGU_EN, SC27XX_FGU_EN); 969195ca170SBaolin Wang if (ret) { 970195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu\n"); 971195ca170SBaolin Wang return ret; 972195ca170SBaolin Wang } 973195ca170SBaolin Wang 974195ca170SBaolin Wang /* Enable the FGU RTC clock to make it work */ 975195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0, 976195ca170SBaolin Wang SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN); 977195ca170SBaolin Wang if (ret) { 978195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu RTC clock\n"); 979195ca170SBaolin Wang goto disable_fgu; 980195ca170SBaolin Wang } 981195ca170SBaolin Wang 982edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 983edcb1c0aSYuanjiang Yu SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK); 984edcb1c0aSYuanjiang Yu if (ret) { 985edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to clear interrupt status\n"); 986edcb1c0aSYuanjiang Yu goto disable_clk; 987edcb1c0aSYuanjiang Yu } 988edcb1c0aSYuanjiang Yu 989edcb1c0aSYuanjiang Yu /* 990edcb1c0aSYuanjiang Yu * Set the voltage low overload threshold, which means when the battery 991edcb1c0aSYuanjiang Yu * voltage is lower than this threshold, the controller will generate 992edcb1c0aSYuanjiang Yu * one interrupt to notify. 993edcb1c0aSYuanjiang Yu */ 994edcb1c0aSYuanjiang Yu alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 995edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, 996edcb1c0aSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc); 997edcb1c0aSYuanjiang Yu if (ret) { 998edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set fgu low overload\n"); 999edcb1c0aSYuanjiang Yu goto disable_clk; 1000edcb1c0aSYuanjiang Yu } 1001edcb1c0aSYuanjiang Yu 1002edcb1c0aSYuanjiang Yu /* 1003edcb1c0aSYuanjiang Yu * Set the coulomb counter delta threshold, that means when the coulomb 1004edcb1c0aSYuanjiang Yu * counter change is multiples of the delta threshold, the controller 1005edcb1c0aSYuanjiang Yu * will generate one interrupt to notify the users to update the battery 1006edcb1c0aSYuanjiang Yu * capacity. Now we set the delta threshold as a counter value of 1% 1007edcb1c0aSYuanjiang Yu * capacity. 1008edcb1c0aSYuanjiang Yu */ 1009edcb1c0aSYuanjiang Yu delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1); 1010edcb1c0aSYuanjiang Yu 1011edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL, 1012edcb1c0aSYuanjiang Yu SC27XX_FGU_CLBCNT_MASK, delta_clbcnt); 1013edcb1c0aSYuanjiang Yu if (ret) { 1014edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set low delta coulomb counter\n"); 1015edcb1c0aSYuanjiang Yu goto disable_clk; 1016edcb1c0aSYuanjiang Yu } 1017edcb1c0aSYuanjiang Yu 1018edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH, 1019edcb1c0aSYuanjiang Yu SC27XX_FGU_CLBCNT_MASK, 1020edcb1c0aSYuanjiang Yu delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 1021edcb1c0aSYuanjiang Yu if (ret) { 1022edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set high delta coulomb counter\n"); 1023edcb1c0aSYuanjiang Yu goto disable_clk; 1024edcb1c0aSYuanjiang Yu } 1025edcb1c0aSYuanjiang Yu 1026195ca170SBaolin Wang /* 1027195ca170SBaolin Wang * Get the boot battery capacity when system powers on, which is used to 1028195ca170SBaolin Wang * initialize the coulomb counter. After that, we can read the coulomb 1029195ca170SBaolin Wang * counter to measure the battery capacity. 1030195ca170SBaolin Wang */ 1031195ca170SBaolin Wang ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap); 1032195ca170SBaolin Wang if (ret) { 1033195ca170SBaolin Wang dev_err(data->dev, "failed to get boot capacity\n"); 1034195ca170SBaolin Wang goto disable_clk; 1035195ca170SBaolin Wang } 1036195ca170SBaolin Wang 1037195ca170SBaolin Wang /* 1038195ca170SBaolin Wang * Convert battery capacity to the corresponding initial coulomb counter 1039195ca170SBaolin Wang * and set into coulomb counter registers. 1040195ca170SBaolin Wang */ 1041195ca170SBaolin Wang data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); 1042195ca170SBaolin Wang ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt); 1043195ca170SBaolin Wang if (ret) { 1044195ca170SBaolin Wang dev_err(data->dev, "failed to initialize coulomb counter\n"); 1045195ca170SBaolin Wang goto disable_clk; 1046195ca170SBaolin Wang } 1047195ca170SBaolin Wang 1048195ca170SBaolin Wang return 0; 1049195ca170SBaolin Wang 1050195ca170SBaolin Wang disable_clk: 1051195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 1052195ca170SBaolin Wang disable_fgu: 1053195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 1054195ca170SBaolin Wang 1055195ca170SBaolin Wang return ret; 1056195ca170SBaolin Wang } 1057195ca170SBaolin Wang 1058195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev) 1059195ca170SBaolin Wang { 106008614b40SFuqian Huang struct device *dev = &pdev->dev; 106108614b40SFuqian Huang struct device_node *np = dev->of_node; 1062195ca170SBaolin Wang struct power_supply_config fgu_cfg = { }; 1063195ca170SBaolin Wang struct sc27xx_fgu_data *data; 1064195ca170SBaolin Wang int ret, irq; 1065195ca170SBaolin Wang 106608614b40SFuqian Huang data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 1067195ca170SBaolin Wang if (!data) 1068195ca170SBaolin Wang return -ENOMEM; 1069195ca170SBaolin Wang 107008614b40SFuqian Huang data->regmap = dev_get_regmap(dev->parent, NULL); 1071195ca170SBaolin Wang if (!data->regmap) { 107208614b40SFuqian Huang dev_err(dev, "failed to get regmap\n"); 1073195ca170SBaolin Wang return -ENODEV; 1074195ca170SBaolin Wang } 1075195ca170SBaolin Wang 107608614b40SFuqian Huang ret = device_property_read_u32(dev, "reg", &data->base); 1077195ca170SBaolin Wang if (ret) { 107808614b40SFuqian Huang dev_err(dev, "failed to get fgu address\n"); 1079195ca170SBaolin Wang return ret; 1080195ca170SBaolin Wang } 1081195ca170SBaolin Wang 108208614b40SFuqian Huang data->channel = devm_iio_channel_get(dev, "bat-temp"); 1083195ca170SBaolin Wang if (IS_ERR(data->channel)) { 108408614b40SFuqian Huang dev_err(dev, "failed to get IIO channel\n"); 1085195ca170SBaolin Wang return PTR_ERR(data->channel); 1086195ca170SBaolin Wang } 1087195ca170SBaolin Wang 108808614b40SFuqian Huang data->charge_chan = devm_iio_channel_get(dev, "charge-vol"); 10890a4f97a1SBaolin Wang if (IS_ERR(data->charge_chan)) { 109008614b40SFuqian Huang dev_err(dev, "failed to get charge IIO channel\n"); 10910a4f97a1SBaolin Wang return PTR_ERR(data->charge_chan); 10920a4f97a1SBaolin Wang } 10930a4f97a1SBaolin Wang 109408614b40SFuqian Huang data->gpiod = devm_gpiod_get(dev, "bat-detect", GPIOD_IN); 1095195ca170SBaolin Wang if (IS_ERR(data->gpiod)) { 109608614b40SFuqian Huang dev_err(dev, "failed to get battery detection GPIO\n"); 1097195ca170SBaolin Wang return PTR_ERR(data->gpiod); 1098195ca170SBaolin Wang } 1099195ca170SBaolin Wang 1100195ca170SBaolin Wang ret = gpiod_get_value_cansleep(data->gpiod); 1101195ca170SBaolin Wang if (ret < 0) { 110208614b40SFuqian Huang dev_err(dev, "failed to get gpio state\n"); 1103195ca170SBaolin Wang return ret; 1104195ca170SBaolin Wang } 1105195ca170SBaolin Wang 1106195ca170SBaolin Wang data->bat_present = !!ret; 1107195ca170SBaolin Wang mutex_init(&data->lock); 110808614b40SFuqian Huang data->dev = dev; 1109e2fb615bSYuanjiang Yu platform_set_drvdata(pdev, data); 1110195ca170SBaolin Wang 1111195ca170SBaolin Wang fgu_cfg.drv_data = data; 1112195ca170SBaolin Wang fgu_cfg.of_node = np; 111308614b40SFuqian Huang data->battery = devm_power_supply_register(dev, &sc27xx_fgu_desc, 1114195ca170SBaolin Wang &fgu_cfg); 1115195ca170SBaolin Wang if (IS_ERR(data->battery)) { 111608614b40SFuqian Huang dev_err(dev, "failed to register power supply\n"); 1117195ca170SBaolin Wang return PTR_ERR(data->battery); 1118195ca170SBaolin Wang } 1119195ca170SBaolin Wang 1120195ca170SBaolin Wang ret = sc27xx_fgu_hw_init(data); 1121195ca170SBaolin Wang if (ret) { 112208614b40SFuqian Huang dev_err(dev, "failed to initialize fgu hardware\n"); 1123195ca170SBaolin Wang return ret; 1124195ca170SBaolin Wang } 1125195ca170SBaolin Wang 112620420583SFuqian Huang ret = devm_add_action_or_reset(dev, sc27xx_fgu_disable, data); 1127195ca170SBaolin Wang if (ret) { 112808614b40SFuqian Huang dev_err(dev, "failed to add fgu disable action\n"); 1129195ca170SBaolin Wang return ret; 1130195ca170SBaolin Wang } 1131195ca170SBaolin Wang 1132edcb1c0aSYuanjiang Yu irq = platform_get_irq(pdev, 0); 1133edcb1c0aSYuanjiang Yu if (irq < 0) { 113408614b40SFuqian Huang dev_err(dev, "no irq resource specified\n"); 1135edcb1c0aSYuanjiang Yu return irq; 1136edcb1c0aSYuanjiang Yu } 1137edcb1c0aSYuanjiang Yu 1138edcb1c0aSYuanjiang Yu ret = devm_request_threaded_irq(data->dev, irq, NULL, 1139edcb1c0aSYuanjiang Yu sc27xx_fgu_interrupt, 1140edcb1c0aSYuanjiang Yu IRQF_NO_SUSPEND | IRQF_ONESHOT, 1141edcb1c0aSYuanjiang Yu pdev->name, data); 1142edcb1c0aSYuanjiang Yu if (ret) { 1143edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to request fgu IRQ\n"); 1144edcb1c0aSYuanjiang Yu return ret; 1145edcb1c0aSYuanjiang Yu } 1146edcb1c0aSYuanjiang Yu 1147195ca170SBaolin Wang irq = gpiod_to_irq(data->gpiod); 1148195ca170SBaolin Wang if (irq < 0) { 114908614b40SFuqian Huang dev_err(dev, "failed to translate GPIO to IRQ\n"); 1150195ca170SBaolin Wang return irq; 1151195ca170SBaolin Wang } 1152195ca170SBaolin Wang 115308614b40SFuqian Huang ret = devm_request_threaded_irq(dev, irq, NULL, 1154195ca170SBaolin Wang sc27xx_fgu_bat_detection, 1155195ca170SBaolin Wang IRQF_ONESHOT | IRQF_TRIGGER_RISING | 1156195ca170SBaolin Wang IRQF_TRIGGER_FALLING, 1157195ca170SBaolin Wang pdev->name, data); 1158195ca170SBaolin Wang if (ret) { 115908614b40SFuqian Huang dev_err(dev, "failed to request IRQ\n"); 1160195ca170SBaolin Wang return ret; 1161195ca170SBaolin Wang } 1162195ca170SBaolin Wang 1163195ca170SBaolin Wang return 0; 1164195ca170SBaolin Wang } 1165195ca170SBaolin Wang 1166e2fb615bSYuanjiang Yu #ifdef CONFIG_PM_SLEEP 1167e2fb615bSYuanjiang Yu static int sc27xx_fgu_resume(struct device *dev) 1168e2fb615bSYuanjiang Yu { 1169e2fb615bSYuanjiang Yu struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 1170e2fb615bSYuanjiang Yu int ret; 1171e2fb615bSYuanjiang Yu 1172e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1173e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT | 1174e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT, 0); 1175e2fb615bSYuanjiang Yu if (ret) { 1176e2fb615bSYuanjiang Yu dev_err(data->dev, "failed to disable fgu interrupts\n"); 1177e2fb615bSYuanjiang Yu return ret; 1178e2fb615bSYuanjiang Yu } 1179e2fb615bSYuanjiang Yu 1180e2fb615bSYuanjiang Yu return 0; 1181e2fb615bSYuanjiang Yu } 1182e2fb615bSYuanjiang Yu 1183e2fb615bSYuanjiang Yu static int sc27xx_fgu_suspend(struct device *dev) 1184e2fb615bSYuanjiang Yu { 1185e2fb615bSYuanjiang Yu struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 1186e2fb615bSYuanjiang Yu int ret, status, ocv; 1187e2fb615bSYuanjiang Yu 1188e2fb615bSYuanjiang Yu ret = sc27xx_fgu_get_status(data, &status); 1189e2fb615bSYuanjiang Yu if (ret) 1190e2fb615bSYuanjiang Yu return ret; 1191e2fb615bSYuanjiang Yu 1192e2fb615bSYuanjiang Yu /* 1193e2fb615bSYuanjiang Yu * If we are charging, then no need to enable the FGU interrupts to 1194e2fb615bSYuanjiang Yu * adjust the battery capacity. 1195e2fb615bSYuanjiang Yu */ 1196168e68d0SYuanjiang Yu if (status != POWER_SUPPLY_STATUS_NOT_CHARGING && 1197168e68d0SYuanjiang Yu status != POWER_SUPPLY_STATUS_DISCHARGING) 1198e2fb615bSYuanjiang Yu return 0; 1199e2fb615bSYuanjiang Yu 1200e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1201e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT, 1202e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT); 1203e2fb615bSYuanjiang Yu if (ret) { 1204e2fb615bSYuanjiang Yu dev_err(data->dev, "failed to enable low voltage interrupt\n"); 1205e2fb615bSYuanjiang Yu return ret; 1206e2fb615bSYuanjiang Yu } 1207e2fb615bSYuanjiang Yu 1208e2fb615bSYuanjiang Yu ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 1209e2fb615bSYuanjiang Yu if (ret) 1210e2fb615bSYuanjiang Yu goto disable_int; 1211e2fb615bSYuanjiang Yu 1212e2fb615bSYuanjiang Yu /* 1213e2fb615bSYuanjiang Yu * If current OCV is less than the minimum voltage, we should enable the 1214e2fb615bSYuanjiang Yu * coulomb counter threshold interrupt to notify events to adjust the 1215e2fb615bSYuanjiang Yu * battery capacity. 1216e2fb615bSYuanjiang Yu */ 1217e2fb615bSYuanjiang Yu if (ocv < data->min_volt) { 1218e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, 1219e2fb615bSYuanjiang Yu data->base + SC27XX_FGU_INT_EN, 1220e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT, 1221e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT); 1222e2fb615bSYuanjiang Yu if (ret) { 1223e2fb615bSYuanjiang Yu dev_err(data->dev, 1224e2fb615bSYuanjiang Yu "failed to enable coulomb threshold int\n"); 1225e2fb615bSYuanjiang Yu goto disable_int; 1226e2fb615bSYuanjiang Yu } 1227e2fb615bSYuanjiang Yu } 1228e2fb615bSYuanjiang Yu 1229e2fb615bSYuanjiang Yu return 0; 1230e2fb615bSYuanjiang Yu 1231e2fb615bSYuanjiang Yu disable_int: 1232e2fb615bSYuanjiang Yu regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1233e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT, 0); 1234e2fb615bSYuanjiang Yu return ret; 1235e2fb615bSYuanjiang Yu } 1236e2fb615bSYuanjiang Yu #endif 1237e2fb615bSYuanjiang Yu 1238e2fb615bSYuanjiang Yu static const struct dev_pm_ops sc27xx_fgu_pm_ops = { 1239e2fb615bSYuanjiang Yu SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume) 1240e2fb615bSYuanjiang Yu }; 1241e2fb615bSYuanjiang Yu 1242195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = { 1243195ca170SBaolin Wang { .compatible = "sprd,sc2731-fgu", }, 1244195ca170SBaolin Wang { } 1245195ca170SBaolin Wang }; 1246195ca170SBaolin Wang 1247195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = { 1248195ca170SBaolin Wang .probe = sc27xx_fgu_probe, 1249195ca170SBaolin Wang .driver = { 1250195ca170SBaolin Wang .name = "sc27xx-fgu", 1251195ca170SBaolin Wang .of_match_table = sc27xx_fgu_of_match, 1252e2fb615bSYuanjiang Yu .pm = &sc27xx_fgu_pm_ops, 1253195ca170SBaolin Wang } 1254195ca170SBaolin Wang }; 1255195ca170SBaolin Wang 1256195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver); 1257195ca170SBaolin Wang 1258195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver"); 1259195ca170SBaolin Wang MODULE_LICENSE("GPL v2"); 1260