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 8465c9fab7SBaolin Wang * @cur_1000ma_adc: ADC value corresponding to 1000 mA 8565c9fab7SBaolin Wang * @vol_1000mv_adc: ADC value corresponding to 1000 mV 86195ca170SBaolin Wang * @cap_table: capacity table with corresponding ocv 87195ca170SBaolin Wang */ 88195ca170SBaolin Wang struct sc27xx_fgu_data { 89195ca170SBaolin Wang struct regmap *regmap; 90195ca170SBaolin Wang struct device *dev; 91195ca170SBaolin Wang struct power_supply *battery; 92195ca170SBaolin Wang u32 base; 93195ca170SBaolin Wang struct mutex lock; 94195ca170SBaolin Wang struct gpio_desc *gpiod; 95195ca170SBaolin Wang struct iio_channel *channel; 960a4f97a1SBaolin Wang struct iio_channel *charge_chan; 97195ca170SBaolin Wang bool bat_present; 98195ca170SBaolin Wang int internal_resist; 99195ca170SBaolin Wang int total_cap; 100195ca170SBaolin Wang int init_cap; 101edcb1c0aSYuanjiang Yu int alarm_cap; 102195ca170SBaolin Wang int init_clbcnt; 103195ca170SBaolin Wang int max_volt; 104edcb1c0aSYuanjiang Yu int min_volt; 105195ca170SBaolin Wang int table_len; 10665c9fab7SBaolin Wang int cur_1000ma_adc; 10765c9fab7SBaolin Wang int vol_1000mv_adc; 108195ca170SBaolin Wang struct power_supply_battery_ocv_table *cap_table; 109195ca170SBaolin Wang }; 110195ca170SBaolin Wang 111edcb1c0aSYuanjiang Yu static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity); 112edcb1c0aSYuanjiang Yu 113195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = { 114195ca170SBaolin Wang "sc2731_charger", 115195ca170SBaolin Wang "sc2720_charger", 116195ca170SBaolin Wang "sc2721_charger", 117195ca170SBaolin Wang "sc2723_charger", 118195ca170SBaolin Wang }; 119195ca170SBaolin Wang 12065c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc) 121195ca170SBaolin Wang { 12265c9fab7SBaolin Wang return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc); 123195ca170SBaolin Wang } 124195ca170SBaolin Wang 12565c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc) 126195ca170SBaolin Wang { 12765c9fab7SBaolin Wang return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); 128195ca170SBaolin Wang } 129195ca170SBaolin Wang 130edcb1c0aSYuanjiang Yu static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol) 131edcb1c0aSYuanjiang Yu { 132edcb1c0aSYuanjiang Yu return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000); 133edcb1c0aSYuanjiang Yu } 134edcb1c0aSYuanjiang Yu 1354a040e7cSYuanjiang Yu static bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data) 1364a040e7cSYuanjiang Yu { 1374a040e7cSYuanjiang Yu int ret, status, cap, mode; 1384a040e7cSYuanjiang Yu 1394a040e7cSYuanjiang Yu ret = regmap_read(data->regmap, 1404a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_STATUS, &status); 1414a040e7cSYuanjiang Yu if (ret) 1424a040e7cSYuanjiang Yu return false; 1434a040e7cSYuanjiang Yu 1444a040e7cSYuanjiang Yu /* 1454a040e7cSYuanjiang Yu * We use low 4 bits to save the last battery capacity and high 12 bits 1464a040e7cSYuanjiang Yu * to save the system boot mode. 1474a040e7cSYuanjiang Yu */ 1484a040e7cSYuanjiang Yu mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT; 1494a040e7cSYuanjiang Yu cap = status & SC27XX_FGU_CAP_AREA_MASK; 1504a040e7cSYuanjiang Yu 1514a040e7cSYuanjiang Yu /* 1524a040e7cSYuanjiang Yu * When FGU has been powered down, the user area registers became 1534a040e7cSYuanjiang Yu * default value (0xffff), which can be used to valid if the system is 1544a040e7cSYuanjiang Yu * first power on or not. 1554a040e7cSYuanjiang Yu */ 1564a040e7cSYuanjiang Yu if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP) 1574a040e7cSYuanjiang Yu return true; 1584a040e7cSYuanjiang Yu 1594a040e7cSYuanjiang Yu return false; 1604a040e7cSYuanjiang Yu } 1614a040e7cSYuanjiang Yu 1624a040e7cSYuanjiang Yu static int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data, 1634a040e7cSYuanjiang Yu int boot_mode) 1644a040e7cSYuanjiang Yu { 1654a040e7cSYuanjiang Yu int ret; 1664a040e7cSYuanjiang Yu 1674a040e7cSYuanjiang Yu ret = regmap_update_bits(data->regmap, 1684a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 1694a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 1704a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK); 1714a040e7cSYuanjiang Yu if (ret) 1724a040e7cSYuanjiang Yu return ret; 1734a040e7cSYuanjiang Yu 174d3e67c94SYuanjiang Yu /* 175d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 176d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 177d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 178d3e67c94SYuanjiang Yu * successfully according to the datasheet. 179d3e67c94SYuanjiang Yu */ 180d3e67c94SYuanjiang Yu udelay(200); 181d3e67c94SYuanjiang Yu 182d3e67c94SYuanjiang Yu ret = regmap_update_bits(data->regmap, 1834a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_SET, 1844a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 1854a040e7cSYuanjiang Yu boot_mode << SC27XX_FGU_MODE_AREA_SHIFT); 186d3e67c94SYuanjiang Yu if (ret) 187d3e67c94SYuanjiang Yu return ret; 188d3e67c94SYuanjiang Yu 189d3e67c94SYuanjiang Yu /* 190d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 191d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 192d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 193d3e67c94SYuanjiang Yu * successfully according to the datasheet. 194d3e67c94SYuanjiang Yu */ 195d3e67c94SYuanjiang Yu udelay(200); 196d3e67c94SYuanjiang Yu 197d3e67c94SYuanjiang Yu /* 198d3e67c94SYuanjiang Yu * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to 199d3e67c94SYuanjiang Yu * make the user area data available, otherwise we can not save the user 200d3e67c94SYuanjiang Yu * area data. 201d3e67c94SYuanjiang Yu */ 202d3e67c94SYuanjiang Yu return regmap_update_bits(data->regmap, 203d3e67c94SYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 204d3e67c94SYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 0); 2054a040e7cSYuanjiang Yu } 2064a040e7cSYuanjiang Yu 2074a040e7cSYuanjiang Yu static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap) 2084a040e7cSYuanjiang Yu { 2094a040e7cSYuanjiang Yu int ret; 2104a040e7cSYuanjiang Yu 2114a040e7cSYuanjiang Yu ret = regmap_update_bits(data->regmap, 2124a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 2134a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, 2144a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK); 2154a040e7cSYuanjiang Yu if (ret) 2164a040e7cSYuanjiang Yu return ret; 2174a040e7cSYuanjiang Yu 218d3e67c94SYuanjiang Yu /* 219d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 220d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 221d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 222d3e67c94SYuanjiang Yu * successfully according to the datasheet. 223d3e67c94SYuanjiang Yu */ 224d3e67c94SYuanjiang Yu udelay(200); 225d3e67c94SYuanjiang Yu 226d3e67c94SYuanjiang Yu ret = regmap_update_bits(data->regmap, 2274a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_SET, 2284a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, cap); 229d3e67c94SYuanjiang Yu if (ret) 230d3e67c94SYuanjiang Yu return ret; 231d3e67c94SYuanjiang Yu 232d3e67c94SYuanjiang Yu /* 233d3e67c94SYuanjiang Yu * Since the user area registers are put on power always-on region, 234d3e67c94SYuanjiang Yu * then these registers changing time will be a little long. Thus 235d3e67c94SYuanjiang Yu * here we should delay 200us to wait until values are updated 236d3e67c94SYuanjiang Yu * successfully according to the datasheet. 237d3e67c94SYuanjiang Yu */ 238d3e67c94SYuanjiang Yu udelay(200); 239d3e67c94SYuanjiang Yu 240d3e67c94SYuanjiang Yu /* 241d3e67c94SYuanjiang Yu * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to 242d3e67c94SYuanjiang Yu * make the user area data available, otherwise we can not save the user 243d3e67c94SYuanjiang Yu * area data. 244d3e67c94SYuanjiang Yu */ 245d3e67c94SYuanjiang Yu return regmap_update_bits(data->regmap, 246d3e67c94SYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 247d3e67c94SYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, 0); 2484a040e7cSYuanjiang Yu } 2494a040e7cSYuanjiang Yu 2504a040e7cSYuanjiang Yu static int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap) 2514a040e7cSYuanjiang Yu { 2524a040e7cSYuanjiang Yu int ret, value; 2534a040e7cSYuanjiang Yu 2544a040e7cSYuanjiang Yu ret = regmap_read(data->regmap, 2554a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_STATUS, &value); 2564a040e7cSYuanjiang Yu if (ret) 2574a040e7cSYuanjiang Yu return ret; 2584a040e7cSYuanjiang Yu 2594a040e7cSYuanjiang Yu *cap = value & SC27XX_FGU_CAP_AREA_MASK; 2604a040e7cSYuanjiang Yu return 0; 2614a040e7cSYuanjiang Yu } 2624a040e7cSYuanjiang Yu 263195ca170SBaolin Wang /* 264195ca170SBaolin Wang * When system boots on, we can not read battery capacity from coulomb 265195ca170SBaolin Wang * registers, since now the coulomb registers are invalid. So we should 266195ca170SBaolin Wang * calculate the battery open circuit voltage, and get current battery 267195ca170SBaolin Wang * capacity according to the capacity table. 268195ca170SBaolin Wang */ 269195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) 270195ca170SBaolin Wang { 271195ca170SBaolin Wang int volt, cur, oci, ocv, ret; 2724a040e7cSYuanjiang Yu bool is_first_poweron = sc27xx_fgu_is_first_poweron(data); 2734a040e7cSYuanjiang Yu 2744a040e7cSYuanjiang Yu /* 2754a040e7cSYuanjiang Yu * If system is not the first power on, we should use the last saved 2764a040e7cSYuanjiang Yu * battery capacity as the initial battery capacity. Otherwise we should 2774a040e7cSYuanjiang Yu * re-calculate the initial battery capacity. 2784a040e7cSYuanjiang Yu */ 2794a040e7cSYuanjiang Yu if (!is_first_poweron) { 2804a040e7cSYuanjiang Yu ret = sc27xx_fgu_read_last_cap(data, cap); 2814a040e7cSYuanjiang Yu if (ret) 2824a040e7cSYuanjiang Yu return ret; 2834a040e7cSYuanjiang Yu 2844a040e7cSYuanjiang Yu return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 2854a040e7cSYuanjiang Yu } 286195ca170SBaolin Wang 287195ca170SBaolin Wang /* 288195ca170SBaolin Wang * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved 289195ca170SBaolin Wang * the first sampled open circuit current. 290195ca170SBaolin Wang */ 291195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL, 292195ca170SBaolin Wang &cur); 293195ca170SBaolin Wang if (ret) 294195ca170SBaolin Wang return ret; 295195ca170SBaolin Wang 296195ca170SBaolin Wang cur <<= 1; 29765c9fab7SBaolin Wang oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 298195ca170SBaolin Wang 299195ca170SBaolin Wang /* 300195ca170SBaolin Wang * Should get the OCV from SC27XX_FGU_POCV register at the system 301195ca170SBaolin Wang * beginning. It is ADC values reading from registers which need to 302195ca170SBaolin Wang * convert the corresponding voltage. 303195ca170SBaolin Wang */ 304195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt); 305195ca170SBaolin Wang if (ret) 306195ca170SBaolin Wang return ret; 307195ca170SBaolin Wang 30865c9fab7SBaolin Wang volt = sc27xx_fgu_adc_to_voltage(data, volt); 309195ca170SBaolin Wang ocv = volt * 1000 - oci * data->internal_resist; 310195ca170SBaolin Wang 311195ca170SBaolin Wang /* 312195ca170SBaolin Wang * Parse the capacity table to look up the correct capacity percent 313195ca170SBaolin Wang * according to current battery's corresponding OCV values. 314195ca170SBaolin Wang */ 315195ca170SBaolin Wang *cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len, 316195ca170SBaolin Wang ocv); 317195ca170SBaolin Wang 3184a040e7cSYuanjiang Yu ret = sc27xx_fgu_save_last_cap(data, *cap); 3194a040e7cSYuanjiang Yu if (ret) 3204a040e7cSYuanjiang Yu return ret; 3214a040e7cSYuanjiang Yu 3224a040e7cSYuanjiang Yu return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 323195ca170SBaolin Wang } 324195ca170SBaolin Wang 325195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt) 326195ca170SBaolin Wang { 327195ca170SBaolin Wang int ret; 328195ca170SBaolin Wang 329195ca170SBaolin Wang clbcnt *= SC27XX_FGU_SAMPLE_HZ; 330195ca170SBaolin Wang 331195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 332195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETL, 333195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, clbcnt); 334195ca170SBaolin Wang if (ret) 335195ca170SBaolin Wang return ret; 336195ca170SBaolin Wang 337195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 338195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETH, 339195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, 340195ca170SBaolin Wang clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 341195ca170SBaolin Wang if (ret) 342195ca170SBaolin Wang return ret; 343195ca170SBaolin Wang 344195ca170SBaolin Wang return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START, 345195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN, 346195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN); 347195ca170SBaolin Wang } 348195ca170SBaolin Wang 349195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt) 350195ca170SBaolin Wang { 351195ca170SBaolin Wang int ccl, cch, ret; 352195ca170SBaolin Wang 353195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL, 354195ca170SBaolin Wang &ccl); 355195ca170SBaolin Wang if (ret) 356195ca170SBaolin Wang return ret; 357195ca170SBaolin Wang 358195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH, 359195ca170SBaolin Wang &cch); 360195ca170SBaolin Wang if (ret) 361195ca170SBaolin Wang return ret; 362195ca170SBaolin Wang 363195ca170SBaolin Wang *clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK; 364195ca170SBaolin Wang *clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT; 365195ca170SBaolin Wang *clb_cnt /= SC27XX_FGU_SAMPLE_HZ; 366195ca170SBaolin Wang 367195ca170SBaolin Wang return 0; 368195ca170SBaolin Wang } 369195ca170SBaolin Wang 370195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap) 371195ca170SBaolin Wang { 372195ca170SBaolin Wang int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp; 373195ca170SBaolin Wang 374195ca170SBaolin Wang /* Get current coulomb counters firstly */ 375195ca170SBaolin Wang ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt); 376195ca170SBaolin Wang if (ret) 377195ca170SBaolin Wang return ret; 378195ca170SBaolin Wang 379195ca170SBaolin Wang delta_clbcnt = cur_clbcnt - data->init_clbcnt; 380195ca170SBaolin Wang 381195ca170SBaolin Wang /* 382195ca170SBaolin Wang * Convert coulomb counter to delta capacity (mAh), and set multiplier 383195ca170SBaolin Wang * as 100 to improve the precision. 384195ca170SBaolin Wang */ 385195ca170SBaolin Wang temp = DIV_ROUND_CLOSEST(delta_clbcnt, 360); 38665c9fab7SBaolin Wang temp = sc27xx_fgu_adc_to_current(data, temp); 387195ca170SBaolin Wang 388195ca170SBaolin Wang /* 389195ca170SBaolin Wang * Convert to capacity percent of the battery total capacity, 390195ca170SBaolin Wang * and multiplier is 100 too. 391195ca170SBaolin Wang */ 392195ca170SBaolin Wang delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap); 393195ca170SBaolin Wang *cap = delta_cap + data->init_cap; 394195ca170SBaolin Wang 395195ca170SBaolin Wang return 0; 396195ca170SBaolin Wang } 397195ca170SBaolin Wang 398195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val) 399195ca170SBaolin Wang { 400195ca170SBaolin Wang int ret, vol; 401195ca170SBaolin Wang 402195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol); 403195ca170SBaolin Wang if (ret) 404195ca170SBaolin Wang return ret; 405195ca170SBaolin Wang 406195ca170SBaolin Wang /* 407195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 408195ca170SBaolin Wang * corresponding voltage values. 409195ca170SBaolin Wang */ 41065c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_voltage(data, vol); 411195ca170SBaolin Wang 412195ca170SBaolin Wang return 0; 413195ca170SBaolin Wang } 414195ca170SBaolin Wang 415195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val) 416195ca170SBaolin Wang { 417195ca170SBaolin Wang int ret, cur; 418195ca170SBaolin Wang 419195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur); 420195ca170SBaolin Wang if (ret) 421195ca170SBaolin Wang return ret; 422195ca170SBaolin Wang 423195ca170SBaolin Wang /* 424195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 425195ca170SBaolin Wang * corresponding current values. 426195ca170SBaolin Wang */ 42765c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 428195ca170SBaolin Wang 429195ca170SBaolin Wang return 0; 430195ca170SBaolin Wang } 431195ca170SBaolin Wang 432195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) 433195ca170SBaolin Wang { 434195ca170SBaolin Wang int vol, cur, ret; 435195ca170SBaolin Wang 436195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 437195ca170SBaolin Wang if (ret) 438195ca170SBaolin Wang return ret; 439195ca170SBaolin Wang 440195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &cur); 441195ca170SBaolin Wang if (ret) 442195ca170SBaolin Wang return ret; 443195ca170SBaolin Wang 444195ca170SBaolin Wang /* Return the battery OCV in micro volts. */ 445195ca170SBaolin Wang *val = vol * 1000 - cur * data->internal_resist; 446195ca170SBaolin Wang 447195ca170SBaolin Wang return 0; 448195ca170SBaolin Wang } 449195ca170SBaolin Wang 4500a4f97a1SBaolin Wang static int sc27xx_fgu_get_charge_vol(struct sc27xx_fgu_data *data, int *val) 4510a4f97a1SBaolin Wang { 4520a4f97a1SBaolin Wang int ret, vol; 4530a4f97a1SBaolin Wang 4540a4f97a1SBaolin Wang ret = iio_read_channel_processed(data->charge_chan, &vol); 4550a4f97a1SBaolin Wang if (ret < 0) 4560a4f97a1SBaolin Wang return ret; 4570a4f97a1SBaolin Wang 4580a4f97a1SBaolin Wang *val = vol * 1000; 4590a4f97a1SBaolin Wang return 0; 4600a4f97a1SBaolin Wang } 4610a4f97a1SBaolin Wang 462195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp) 463195ca170SBaolin Wang { 464195ca170SBaolin Wang return iio_read_channel_processed(data->channel, temp); 465195ca170SBaolin Wang } 466195ca170SBaolin Wang 467195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health) 468195ca170SBaolin Wang { 469195ca170SBaolin Wang int ret, vol; 470195ca170SBaolin Wang 471195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 472195ca170SBaolin Wang if (ret) 473195ca170SBaolin Wang return ret; 474195ca170SBaolin Wang 475195ca170SBaolin Wang if (vol > data->max_volt) 476195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 477195ca170SBaolin Wang else 478195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_GOOD; 479195ca170SBaolin Wang 480195ca170SBaolin Wang return 0; 481195ca170SBaolin Wang } 482195ca170SBaolin Wang 483195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status) 484195ca170SBaolin Wang { 485195ca170SBaolin Wang union power_supply_propval val; 486195ca170SBaolin Wang struct power_supply *psy; 487195ca170SBaolin Wang int i, ret = -EINVAL; 488195ca170SBaolin Wang 489195ca170SBaolin Wang for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) { 490195ca170SBaolin Wang psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]); 491195ca170SBaolin Wang if (!psy) 492195ca170SBaolin Wang continue; 493195ca170SBaolin Wang 494195ca170SBaolin Wang ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, 495195ca170SBaolin Wang &val); 496195ca170SBaolin Wang power_supply_put(psy); 497195ca170SBaolin Wang if (ret) 498195ca170SBaolin Wang return ret; 499195ca170SBaolin Wang 500195ca170SBaolin Wang *status = val.intval; 501195ca170SBaolin Wang } 502195ca170SBaolin Wang 503195ca170SBaolin Wang return ret; 504195ca170SBaolin Wang } 505195ca170SBaolin Wang 506195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy, 507195ca170SBaolin Wang enum power_supply_property psp, 508195ca170SBaolin Wang union power_supply_propval *val) 509195ca170SBaolin Wang { 510195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 511195ca170SBaolin Wang int ret = 0; 512195ca170SBaolin Wang int value; 513195ca170SBaolin Wang 514195ca170SBaolin Wang mutex_lock(&data->lock); 515195ca170SBaolin Wang 516195ca170SBaolin Wang switch (psp) { 517195ca170SBaolin Wang case POWER_SUPPLY_PROP_STATUS: 518195ca170SBaolin Wang ret = sc27xx_fgu_get_status(data, &value); 519195ca170SBaolin Wang if (ret) 520195ca170SBaolin Wang goto error; 521195ca170SBaolin Wang 522195ca170SBaolin Wang val->intval = value; 523195ca170SBaolin Wang break; 524195ca170SBaolin Wang 525195ca170SBaolin Wang case POWER_SUPPLY_PROP_HEALTH: 526195ca170SBaolin Wang ret = sc27xx_fgu_get_health(data, &value); 527195ca170SBaolin Wang if (ret) 528195ca170SBaolin Wang goto error; 529195ca170SBaolin Wang 530195ca170SBaolin Wang val->intval = value; 531195ca170SBaolin Wang break; 532195ca170SBaolin Wang 533195ca170SBaolin Wang case POWER_SUPPLY_PROP_PRESENT: 534195ca170SBaolin Wang val->intval = data->bat_present; 535195ca170SBaolin Wang break; 536195ca170SBaolin Wang 537195ca170SBaolin Wang case POWER_SUPPLY_PROP_TEMP: 538195ca170SBaolin Wang ret = sc27xx_fgu_get_temp(data, &value); 539195ca170SBaolin Wang if (ret) 540195ca170SBaolin Wang goto error; 541195ca170SBaolin Wang 542195ca170SBaolin Wang val->intval = value; 543195ca170SBaolin Wang break; 544195ca170SBaolin Wang 545195ca170SBaolin Wang case POWER_SUPPLY_PROP_TECHNOLOGY: 546195ca170SBaolin Wang val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 547195ca170SBaolin Wang break; 548195ca170SBaolin Wang 549195ca170SBaolin Wang case POWER_SUPPLY_PROP_CAPACITY: 550195ca170SBaolin Wang ret = sc27xx_fgu_get_capacity(data, &value); 551195ca170SBaolin Wang if (ret) 552195ca170SBaolin Wang goto error; 553195ca170SBaolin Wang 554195ca170SBaolin Wang val->intval = value; 555195ca170SBaolin Wang break; 556195ca170SBaolin Wang 557195ca170SBaolin Wang case POWER_SUPPLY_PROP_VOLTAGE_NOW: 558195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &value); 559195ca170SBaolin Wang if (ret) 560195ca170SBaolin Wang goto error; 561195ca170SBaolin Wang 562195ca170SBaolin Wang val->intval = value * 1000; 563195ca170SBaolin Wang break; 564195ca170SBaolin Wang 565195ca170SBaolin Wang case POWER_SUPPLY_PROP_VOLTAGE_OCV: 566195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_ocv(data, &value); 567195ca170SBaolin Wang if (ret) 568195ca170SBaolin Wang goto error; 569195ca170SBaolin Wang 570195ca170SBaolin Wang val->intval = value; 571195ca170SBaolin Wang break; 572195ca170SBaolin Wang 5730a4f97a1SBaolin Wang case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 5740a4f97a1SBaolin Wang ret = sc27xx_fgu_get_charge_vol(data, &value); 5750a4f97a1SBaolin Wang if (ret) 5760a4f97a1SBaolin Wang goto error; 5770a4f97a1SBaolin Wang 5780a4f97a1SBaolin Wang val->intval = value; 5790a4f97a1SBaolin Wang break; 5800a4f97a1SBaolin Wang 581195ca170SBaolin Wang case POWER_SUPPLY_PROP_CURRENT_NOW: 582195ca170SBaolin Wang case POWER_SUPPLY_PROP_CURRENT_AVG: 583195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &value); 584195ca170SBaolin Wang if (ret) 585195ca170SBaolin Wang goto error; 586195ca170SBaolin Wang 587195ca170SBaolin Wang val->intval = value * 1000; 588195ca170SBaolin Wang break; 589195ca170SBaolin Wang 590*7cff19b9SYuanjiang Yu case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 591*7cff19b9SYuanjiang Yu val->intval = data->total_cap * 1000; 592*7cff19b9SYuanjiang Yu break; 593*7cff19b9SYuanjiang Yu 594195ca170SBaolin Wang default: 595195ca170SBaolin Wang ret = -EINVAL; 596195ca170SBaolin Wang break; 597195ca170SBaolin Wang } 598195ca170SBaolin Wang 599195ca170SBaolin Wang error: 600195ca170SBaolin Wang mutex_unlock(&data->lock); 601195ca170SBaolin Wang return ret; 602195ca170SBaolin Wang } 603195ca170SBaolin Wang 6044a040e7cSYuanjiang Yu static int sc27xx_fgu_set_property(struct power_supply *psy, 6054a040e7cSYuanjiang Yu enum power_supply_property psp, 6064a040e7cSYuanjiang Yu const union power_supply_propval *val) 6074a040e7cSYuanjiang Yu { 6084a040e7cSYuanjiang Yu struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 6094a040e7cSYuanjiang Yu int ret; 6104a040e7cSYuanjiang Yu 6114a040e7cSYuanjiang Yu if (psp != POWER_SUPPLY_PROP_CAPACITY) 6124a040e7cSYuanjiang Yu return -EINVAL; 6134a040e7cSYuanjiang Yu 6144a040e7cSYuanjiang Yu mutex_lock(&data->lock); 6154a040e7cSYuanjiang Yu 6164a040e7cSYuanjiang Yu ret = sc27xx_fgu_save_last_cap(data, val->intval); 6174a040e7cSYuanjiang Yu 6184a040e7cSYuanjiang Yu mutex_unlock(&data->lock); 6194a040e7cSYuanjiang Yu 6204a040e7cSYuanjiang Yu if (ret < 0) 6214a040e7cSYuanjiang Yu dev_err(data->dev, "failed to save battery capacity\n"); 6224a040e7cSYuanjiang Yu 6234a040e7cSYuanjiang Yu return ret; 6244a040e7cSYuanjiang Yu } 6254a040e7cSYuanjiang Yu 626195ca170SBaolin Wang static void sc27xx_fgu_external_power_changed(struct power_supply *psy) 627195ca170SBaolin Wang { 628195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 629195ca170SBaolin Wang 630195ca170SBaolin Wang power_supply_changed(data->battery); 631195ca170SBaolin Wang } 632195ca170SBaolin Wang 6334a040e7cSYuanjiang Yu static int sc27xx_fgu_property_is_writeable(struct power_supply *psy, 6344a040e7cSYuanjiang Yu enum power_supply_property psp) 6354a040e7cSYuanjiang Yu { 6364a040e7cSYuanjiang Yu return psp == POWER_SUPPLY_PROP_CAPACITY; 6374a040e7cSYuanjiang Yu } 6384a040e7cSYuanjiang Yu 639195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = { 640195ca170SBaolin Wang POWER_SUPPLY_PROP_STATUS, 641195ca170SBaolin Wang POWER_SUPPLY_PROP_HEALTH, 642195ca170SBaolin Wang POWER_SUPPLY_PROP_PRESENT, 643195ca170SBaolin Wang POWER_SUPPLY_PROP_TEMP, 644195ca170SBaolin Wang POWER_SUPPLY_PROP_TECHNOLOGY, 645195ca170SBaolin Wang POWER_SUPPLY_PROP_CAPACITY, 646195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_NOW, 647195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_OCV, 648195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_NOW, 649195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_AVG, 6500a4f97a1SBaolin Wang POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 651*7cff19b9SYuanjiang Yu POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 652195ca170SBaolin Wang }; 653195ca170SBaolin Wang 654195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = { 655195ca170SBaolin Wang .name = "sc27xx-fgu", 656195ca170SBaolin Wang .type = POWER_SUPPLY_TYPE_BATTERY, 657195ca170SBaolin Wang .properties = sc27xx_fgu_props, 658195ca170SBaolin Wang .num_properties = ARRAY_SIZE(sc27xx_fgu_props), 659195ca170SBaolin Wang .get_property = sc27xx_fgu_get_property, 6604a040e7cSYuanjiang Yu .set_property = sc27xx_fgu_set_property, 661195ca170SBaolin Wang .external_power_changed = sc27xx_fgu_external_power_changed, 6624a040e7cSYuanjiang Yu .property_is_writeable = sc27xx_fgu_property_is_writeable, 663195ca170SBaolin Wang }; 664195ca170SBaolin Wang 665edcb1c0aSYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap) 666edcb1c0aSYuanjiang Yu { 667edcb1c0aSYuanjiang Yu data->init_cap = cap; 668edcb1c0aSYuanjiang Yu data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); 669edcb1c0aSYuanjiang Yu } 670edcb1c0aSYuanjiang Yu 671edcb1c0aSYuanjiang Yu static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id) 672edcb1c0aSYuanjiang Yu { 673edcb1c0aSYuanjiang Yu struct sc27xx_fgu_data *data = dev_id; 674edcb1c0aSYuanjiang Yu int ret, cap, ocv, adc; 675edcb1c0aSYuanjiang Yu u32 status; 676edcb1c0aSYuanjiang Yu 677edcb1c0aSYuanjiang Yu mutex_lock(&data->lock); 678edcb1c0aSYuanjiang Yu 679edcb1c0aSYuanjiang Yu ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS, 680edcb1c0aSYuanjiang Yu &status); 681edcb1c0aSYuanjiang Yu if (ret) 682edcb1c0aSYuanjiang Yu goto out; 683edcb1c0aSYuanjiang Yu 684edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 685edcb1c0aSYuanjiang Yu status, status); 686edcb1c0aSYuanjiang Yu if (ret) 687edcb1c0aSYuanjiang Yu goto out; 688edcb1c0aSYuanjiang Yu 689edcb1c0aSYuanjiang Yu /* 690edcb1c0aSYuanjiang Yu * When low overload voltage interrupt happens, we should calibrate the 691edcb1c0aSYuanjiang Yu * battery capacity in lower voltage stage. 692edcb1c0aSYuanjiang Yu */ 693edcb1c0aSYuanjiang Yu if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT)) 694edcb1c0aSYuanjiang Yu goto out; 695edcb1c0aSYuanjiang Yu 696edcb1c0aSYuanjiang Yu ret = sc27xx_fgu_get_capacity(data, &cap); 697edcb1c0aSYuanjiang Yu if (ret) 698edcb1c0aSYuanjiang Yu goto out; 699edcb1c0aSYuanjiang Yu 700edcb1c0aSYuanjiang Yu ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 701edcb1c0aSYuanjiang Yu if (ret) 702edcb1c0aSYuanjiang Yu goto out; 703edcb1c0aSYuanjiang Yu 704edcb1c0aSYuanjiang Yu /* 705edcb1c0aSYuanjiang Yu * If current OCV value is less than the minimum OCV value in OCV table, 706edcb1c0aSYuanjiang Yu * which means now battery capacity is 0%, and we should adjust the 707edcb1c0aSYuanjiang Yu * inititial capacity to 0. 708edcb1c0aSYuanjiang Yu */ 709edcb1c0aSYuanjiang Yu if (ocv <= data->cap_table[data->table_len - 1].ocv) { 710edcb1c0aSYuanjiang Yu sc27xx_fgu_adjust_cap(data, 0); 711edcb1c0aSYuanjiang Yu } else if (ocv <= data->min_volt) { 712edcb1c0aSYuanjiang Yu /* 713edcb1c0aSYuanjiang Yu * If current OCV value is less than the low alarm voltage, but 714edcb1c0aSYuanjiang Yu * current capacity is larger than the alarm capacity, we should 715edcb1c0aSYuanjiang Yu * adjust the inititial capacity to alarm capacity. 716edcb1c0aSYuanjiang Yu */ 717edcb1c0aSYuanjiang Yu if (cap > data->alarm_cap) { 718edcb1c0aSYuanjiang Yu sc27xx_fgu_adjust_cap(data, data->alarm_cap); 719edcb1c0aSYuanjiang Yu } else if (cap <= 0) { 720edcb1c0aSYuanjiang Yu int cur_cap; 721edcb1c0aSYuanjiang Yu 722edcb1c0aSYuanjiang Yu /* 723edcb1c0aSYuanjiang Yu * If current capacity is equal with 0 or less than 0 724edcb1c0aSYuanjiang Yu * (some error occurs), we should adjust inititial 725edcb1c0aSYuanjiang Yu * capacity to the capacity corresponding to current OCV 726edcb1c0aSYuanjiang Yu * value. 727edcb1c0aSYuanjiang Yu */ 728edcb1c0aSYuanjiang Yu cur_cap = power_supply_ocv2cap_simple(data->cap_table, 729edcb1c0aSYuanjiang Yu data->table_len, 730edcb1c0aSYuanjiang Yu ocv); 731edcb1c0aSYuanjiang Yu sc27xx_fgu_adjust_cap(data, cur_cap); 732edcb1c0aSYuanjiang Yu } 733edcb1c0aSYuanjiang Yu 734edcb1c0aSYuanjiang Yu /* 735edcb1c0aSYuanjiang Yu * After adjusting the battery capacity, we should set the 736edcb1c0aSYuanjiang Yu * lowest alarm voltage instead. 737edcb1c0aSYuanjiang Yu */ 738edcb1c0aSYuanjiang Yu data->min_volt = data->cap_table[data->table_len - 1].ocv; 739edcb1c0aSYuanjiang Yu adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 740edcb1c0aSYuanjiang Yu regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, 741edcb1c0aSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_MASK, adc); 742edcb1c0aSYuanjiang Yu } 743edcb1c0aSYuanjiang Yu 744edcb1c0aSYuanjiang Yu out: 745edcb1c0aSYuanjiang Yu mutex_unlock(&data->lock); 746edcb1c0aSYuanjiang Yu 747edcb1c0aSYuanjiang Yu power_supply_changed(data->battery); 748edcb1c0aSYuanjiang Yu return IRQ_HANDLED; 749edcb1c0aSYuanjiang Yu } 750edcb1c0aSYuanjiang Yu 751195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id) 752195ca170SBaolin Wang { 753195ca170SBaolin Wang struct sc27xx_fgu_data *data = dev_id; 754195ca170SBaolin Wang int state; 755195ca170SBaolin Wang 756195ca170SBaolin Wang mutex_lock(&data->lock); 757195ca170SBaolin Wang 758195ca170SBaolin Wang state = gpiod_get_value_cansleep(data->gpiod); 759195ca170SBaolin Wang if (state < 0) { 760195ca170SBaolin Wang dev_err(data->dev, "failed to get gpio state\n"); 761195ca170SBaolin Wang mutex_unlock(&data->lock); 762195ca170SBaolin Wang return IRQ_RETVAL(state); 763195ca170SBaolin Wang } 764195ca170SBaolin Wang 765195ca170SBaolin Wang data->bat_present = !!state; 766195ca170SBaolin Wang 767195ca170SBaolin Wang mutex_unlock(&data->lock); 768195ca170SBaolin Wang 769195ca170SBaolin Wang power_supply_changed(data->battery); 770195ca170SBaolin Wang return IRQ_HANDLED; 771195ca170SBaolin Wang } 772195ca170SBaolin Wang 773195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data) 774195ca170SBaolin Wang { 775195ca170SBaolin Wang struct sc27xx_fgu_data *data = _data; 776195ca170SBaolin Wang 777195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 778195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 779195ca170SBaolin Wang } 780195ca170SBaolin Wang 781195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity) 782195ca170SBaolin Wang { 783195ca170SBaolin Wang /* 784195ca170SBaolin Wang * Get current capacity (mAh) = battery total capacity (mAh) * 785195ca170SBaolin Wang * current capacity percent (capacity / 100). 786195ca170SBaolin Wang */ 787195ca170SBaolin Wang int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100); 788195ca170SBaolin Wang 789195ca170SBaolin Wang /* 790195ca170SBaolin Wang * Convert current capacity (mAh) to coulomb counter according to the 791195ca170SBaolin Wang * formula: 1 mAh =3.6 coulomb. 792195ca170SBaolin Wang */ 7933a28f203SBaolin Wang return DIV_ROUND_CLOSEST(cur_cap * 36 * data->cur_1000ma_adc, 10); 794195ca170SBaolin Wang } 795195ca170SBaolin Wang 79665c9fab7SBaolin Wang static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) 79765c9fab7SBaolin Wang { 79865c9fab7SBaolin Wang struct nvmem_cell *cell; 79965c9fab7SBaolin Wang int calib_data, cal_4200mv; 80065c9fab7SBaolin Wang void *buf; 80165c9fab7SBaolin Wang size_t len; 80265c9fab7SBaolin Wang 80365c9fab7SBaolin Wang cell = nvmem_cell_get(data->dev, "fgu_calib"); 80465c9fab7SBaolin Wang if (IS_ERR(cell)) 80565c9fab7SBaolin Wang return PTR_ERR(cell); 80665c9fab7SBaolin Wang 80765c9fab7SBaolin Wang buf = nvmem_cell_read(cell, &len); 80865c9fab7SBaolin Wang nvmem_cell_put(cell); 80965c9fab7SBaolin Wang 81065c9fab7SBaolin Wang if (IS_ERR(buf)) 81165c9fab7SBaolin Wang return PTR_ERR(buf); 81265c9fab7SBaolin Wang 81365c9fab7SBaolin Wang memcpy(&calib_data, buf, min(len, sizeof(u32))); 81465c9fab7SBaolin Wang 81565c9fab7SBaolin Wang /* 81665c9fab7SBaolin Wang * Get the ADC value corresponding to 4200 mV from eFuse controller 81765c9fab7SBaolin Wang * according to below formula. Then convert to ADC values corresponding 81865c9fab7SBaolin Wang * to 1000 mV and 1000 mA. 81965c9fab7SBaolin Wang */ 82065c9fab7SBaolin Wang cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256; 82165c9fab7SBaolin Wang data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42); 82265c9fab7SBaolin Wang data->cur_1000ma_adc = data->vol_1000mv_adc * 4; 82365c9fab7SBaolin Wang 82465c9fab7SBaolin Wang kfree(buf); 82565c9fab7SBaolin Wang return 0; 82665c9fab7SBaolin Wang } 82765c9fab7SBaolin Wang 828195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) 829195ca170SBaolin Wang { 830195ca170SBaolin Wang struct power_supply_battery_info info = { }; 831195ca170SBaolin Wang struct power_supply_battery_ocv_table *table; 832edcb1c0aSYuanjiang Yu int ret, delta_clbcnt, alarm_adc; 833195ca170SBaolin Wang 834195ca170SBaolin Wang ret = power_supply_get_battery_info(data->battery, &info); 835195ca170SBaolin Wang if (ret) { 836195ca170SBaolin Wang dev_err(data->dev, "failed to get battery information\n"); 837195ca170SBaolin Wang return ret; 838195ca170SBaolin Wang } 839195ca170SBaolin Wang 840195ca170SBaolin Wang data->total_cap = info.charge_full_design_uah / 1000; 841195ca170SBaolin Wang data->max_volt = info.constant_charge_voltage_max_uv / 1000; 842195ca170SBaolin Wang data->internal_resist = info.factory_internal_resistance_uohm / 1000; 843edcb1c0aSYuanjiang Yu data->min_volt = info.voltage_min_design_uv; 844195ca170SBaolin Wang 845195ca170SBaolin Wang /* 846195ca170SBaolin Wang * For SC27XX fuel gauge device, we only use one ocv-capacity 847195ca170SBaolin Wang * table in normal temperature 20 Celsius. 848195ca170SBaolin Wang */ 849195ca170SBaolin Wang table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len); 850195ca170SBaolin Wang if (!table) 851195ca170SBaolin Wang return -EINVAL; 852195ca170SBaolin Wang 853195ca170SBaolin Wang data->cap_table = devm_kmemdup(data->dev, table, 854195ca170SBaolin Wang data->table_len * sizeof(*table), 855195ca170SBaolin Wang GFP_KERNEL); 856195ca170SBaolin Wang if (!data->cap_table) { 857195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 858195ca170SBaolin Wang return -ENOMEM; 859195ca170SBaolin Wang } 860195ca170SBaolin Wang 861edcb1c0aSYuanjiang Yu data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, 862edcb1c0aSYuanjiang Yu data->table_len, 863edcb1c0aSYuanjiang Yu data->min_volt); 864edcb1c0aSYuanjiang Yu 865195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 866195ca170SBaolin Wang 86765c9fab7SBaolin Wang ret = sc27xx_fgu_calibration(data); 86865c9fab7SBaolin Wang if (ret) 86965c9fab7SBaolin Wang return ret; 87065c9fab7SBaolin Wang 871195ca170SBaolin Wang /* Enable the FGU module */ 872195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, 873195ca170SBaolin Wang SC27XX_FGU_EN, SC27XX_FGU_EN); 874195ca170SBaolin Wang if (ret) { 875195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu\n"); 876195ca170SBaolin Wang return ret; 877195ca170SBaolin Wang } 878195ca170SBaolin Wang 879195ca170SBaolin Wang /* Enable the FGU RTC clock to make it work */ 880195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0, 881195ca170SBaolin Wang SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN); 882195ca170SBaolin Wang if (ret) { 883195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu RTC clock\n"); 884195ca170SBaolin Wang goto disable_fgu; 885195ca170SBaolin Wang } 886195ca170SBaolin Wang 887edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 888edcb1c0aSYuanjiang Yu SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK); 889edcb1c0aSYuanjiang Yu if (ret) { 890edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to clear interrupt status\n"); 891edcb1c0aSYuanjiang Yu goto disable_clk; 892edcb1c0aSYuanjiang Yu } 893edcb1c0aSYuanjiang Yu 894edcb1c0aSYuanjiang Yu /* 895edcb1c0aSYuanjiang Yu * Set the voltage low overload threshold, which means when the battery 896edcb1c0aSYuanjiang Yu * voltage is lower than this threshold, the controller will generate 897edcb1c0aSYuanjiang Yu * one interrupt to notify. 898edcb1c0aSYuanjiang Yu */ 899edcb1c0aSYuanjiang Yu alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 900edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, 901edcb1c0aSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc); 902edcb1c0aSYuanjiang Yu if (ret) { 903edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set fgu low overload\n"); 904edcb1c0aSYuanjiang Yu goto disable_clk; 905edcb1c0aSYuanjiang Yu } 906edcb1c0aSYuanjiang Yu 907edcb1c0aSYuanjiang Yu /* 908edcb1c0aSYuanjiang Yu * Set the coulomb counter delta threshold, that means when the coulomb 909edcb1c0aSYuanjiang Yu * counter change is multiples of the delta threshold, the controller 910edcb1c0aSYuanjiang Yu * will generate one interrupt to notify the users to update the battery 911edcb1c0aSYuanjiang Yu * capacity. Now we set the delta threshold as a counter value of 1% 912edcb1c0aSYuanjiang Yu * capacity. 913edcb1c0aSYuanjiang Yu */ 914edcb1c0aSYuanjiang Yu delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1); 915edcb1c0aSYuanjiang Yu 916edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL, 917edcb1c0aSYuanjiang Yu SC27XX_FGU_CLBCNT_MASK, delta_clbcnt); 918edcb1c0aSYuanjiang Yu if (ret) { 919edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set low delta coulomb counter\n"); 920edcb1c0aSYuanjiang Yu goto disable_clk; 921edcb1c0aSYuanjiang Yu } 922edcb1c0aSYuanjiang Yu 923edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH, 924edcb1c0aSYuanjiang Yu SC27XX_FGU_CLBCNT_MASK, 925edcb1c0aSYuanjiang Yu delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 926edcb1c0aSYuanjiang Yu if (ret) { 927edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set high delta coulomb counter\n"); 928edcb1c0aSYuanjiang Yu goto disable_clk; 929edcb1c0aSYuanjiang Yu } 930edcb1c0aSYuanjiang Yu 931195ca170SBaolin Wang /* 932195ca170SBaolin Wang * Get the boot battery capacity when system powers on, which is used to 933195ca170SBaolin Wang * initialize the coulomb counter. After that, we can read the coulomb 934195ca170SBaolin Wang * counter to measure the battery capacity. 935195ca170SBaolin Wang */ 936195ca170SBaolin Wang ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap); 937195ca170SBaolin Wang if (ret) { 938195ca170SBaolin Wang dev_err(data->dev, "failed to get boot capacity\n"); 939195ca170SBaolin Wang goto disable_clk; 940195ca170SBaolin Wang } 941195ca170SBaolin Wang 942195ca170SBaolin Wang /* 943195ca170SBaolin Wang * Convert battery capacity to the corresponding initial coulomb counter 944195ca170SBaolin Wang * and set into coulomb counter registers. 945195ca170SBaolin Wang */ 946195ca170SBaolin Wang data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); 947195ca170SBaolin Wang ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt); 948195ca170SBaolin Wang if (ret) { 949195ca170SBaolin Wang dev_err(data->dev, "failed to initialize coulomb counter\n"); 950195ca170SBaolin Wang goto disable_clk; 951195ca170SBaolin Wang } 952195ca170SBaolin Wang 953195ca170SBaolin Wang return 0; 954195ca170SBaolin Wang 955195ca170SBaolin Wang disable_clk: 956195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 957195ca170SBaolin Wang disable_fgu: 958195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 959195ca170SBaolin Wang 960195ca170SBaolin Wang return ret; 961195ca170SBaolin Wang } 962195ca170SBaolin Wang 963195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev) 964195ca170SBaolin Wang { 96508614b40SFuqian Huang struct device *dev = &pdev->dev; 96608614b40SFuqian Huang struct device_node *np = dev->of_node; 967195ca170SBaolin Wang struct power_supply_config fgu_cfg = { }; 968195ca170SBaolin Wang struct sc27xx_fgu_data *data; 969195ca170SBaolin Wang int ret, irq; 970195ca170SBaolin Wang 97108614b40SFuqian Huang data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 972195ca170SBaolin Wang if (!data) 973195ca170SBaolin Wang return -ENOMEM; 974195ca170SBaolin Wang 97508614b40SFuqian Huang data->regmap = dev_get_regmap(dev->parent, NULL); 976195ca170SBaolin Wang if (!data->regmap) { 97708614b40SFuqian Huang dev_err(dev, "failed to get regmap\n"); 978195ca170SBaolin Wang return -ENODEV; 979195ca170SBaolin Wang } 980195ca170SBaolin Wang 98108614b40SFuqian Huang ret = device_property_read_u32(dev, "reg", &data->base); 982195ca170SBaolin Wang if (ret) { 98308614b40SFuqian Huang dev_err(dev, "failed to get fgu address\n"); 984195ca170SBaolin Wang return ret; 985195ca170SBaolin Wang } 986195ca170SBaolin Wang 98708614b40SFuqian Huang data->channel = devm_iio_channel_get(dev, "bat-temp"); 988195ca170SBaolin Wang if (IS_ERR(data->channel)) { 98908614b40SFuqian Huang dev_err(dev, "failed to get IIO channel\n"); 990195ca170SBaolin Wang return PTR_ERR(data->channel); 991195ca170SBaolin Wang } 992195ca170SBaolin Wang 99308614b40SFuqian Huang data->charge_chan = devm_iio_channel_get(dev, "charge-vol"); 9940a4f97a1SBaolin Wang if (IS_ERR(data->charge_chan)) { 99508614b40SFuqian Huang dev_err(dev, "failed to get charge IIO channel\n"); 9960a4f97a1SBaolin Wang return PTR_ERR(data->charge_chan); 9970a4f97a1SBaolin Wang } 9980a4f97a1SBaolin Wang 99908614b40SFuqian Huang data->gpiod = devm_gpiod_get(dev, "bat-detect", GPIOD_IN); 1000195ca170SBaolin Wang if (IS_ERR(data->gpiod)) { 100108614b40SFuqian Huang dev_err(dev, "failed to get battery detection GPIO\n"); 1002195ca170SBaolin Wang return PTR_ERR(data->gpiod); 1003195ca170SBaolin Wang } 1004195ca170SBaolin Wang 1005195ca170SBaolin Wang ret = gpiod_get_value_cansleep(data->gpiod); 1006195ca170SBaolin Wang if (ret < 0) { 100708614b40SFuqian Huang dev_err(dev, "failed to get gpio state\n"); 1008195ca170SBaolin Wang return ret; 1009195ca170SBaolin Wang } 1010195ca170SBaolin Wang 1011195ca170SBaolin Wang data->bat_present = !!ret; 1012195ca170SBaolin Wang mutex_init(&data->lock); 101308614b40SFuqian Huang data->dev = dev; 1014e2fb615bSYuanjiang Yu platform_set_drvdata(pdev, data); 1015195ca170SBaolin Wang 1016195ca170SBaolin Wang fgu_cfg.drv_data = data; 1017195ca170SBaolin Wang fgu_cfg.of_node = np; 101808614b40SFuqian Huang data->battery = devm_power_supply_register(dev, &sc27xx_fgu_desc, 1019195ca170SBaolin Wang &fgu_cfg); 1020195ca170SBaolin Wang if (IS_ERR(data->battery)) { 102108614b40SFuqian Huang dev_err(dev, "failed to register power supply\n"); 1022195ca170SBaolin Wang return PTR_ERR(data->battery); 1023195ca170SBaolin Wang } 1024195ca170SBaolin Wang 1025195ca170SBaolin Wang ret = sc27xx_fgu_hw_init(data); 1026195ca170SBaolin Wang if (ret) { 102708614b40SFuqian Huang dev_err(dev, "failed to initialize fgu hardware\n"); 1028195ca170SBaolin Wang return ret; 1029195ca170SBaolin Wang } 1030195ca170SBaolin Wang 103120420583SFuqian Huang ret = devm_add_action_or_reset(dev, sc27xx_fgu_disable, data); 1032195ca170SBaolin Wang if (ret) { 103308614b40SFuqian Huang dev_err(dev, "failed to add fgu disable action\n"); 1034195ca170SBaolin Wang return ret; 1035195ca170SBaolin Wang } 1036195ca170SBaolin Wang 1037edcb1c0aSYuanjiang Yu irq = platform_get_irq(pdev, 0); 1038edcb1c0aSYuanjiang Yu if (irq < 0) { 103908614b40SFuqian Huang dev_err(dev, "no irq resource specified\n"); 1040edcb1c0aSYuanjiang Yu return irq; 1041edcb1c0aSYuanjiang Yu } 1042edcb1c0aSYuanjiang Yu 1043edcb1c0aSYuanjiang Yu ret = devm_request_threaded_irq(data->dev, irq, NULL, 1044edcb1c0aSYuanjiang Yu sc27xx_fgu_interrupt, 1045edcb1c0aSYuanjiang Yu IRQF_NO_SUSPEND | IRQF_ONESHOT, 1046edcb1c0aSYuanjiang Yu pdev->name, data); 1047edcb1c0aSYuanjiang Yu if (ret) { 1048edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to request fgu IRQ\n"); 1049edcb1c0aSYuanjiang Yu return ret; 1050edcb1c0aSYuanjiang Yu } 1051edcb1c0aSYuanjiang Yu 1052195ca170SBaolin Wang irq = gpiod_to_irq(data->gpiod); 1053195ca170SBaolin Wang if (irq < 0) { 105408614b40SFuqian Huang dev_err(dev, "failed to translate GPIO to IRQ\n"); 1055195ca170SBaolin Wang return irq; 1056195ca170SBaolin Wang } 1057195ca170SBaolin Wang 105808614b40SFuqian Huang ret = devm_request_threaded_irq(dev, irq, NULL, 1059195ca170SBaolin Wang sc27xx_fgu_bat_detection, 1060195ca170SBaolin Wang IRQF_ONESHOT | IRQF_TRIGGER_RISING | 1061195ca170SBaolin Wang IRQF_TRIGGER_FALLING, 1062195ca170SBaolin Wang pdev->name, data); 1063195ca170SBaolin Wang if (ret) { 106408614b40SFuqian Huang dev_err(dev, "failed to request IRQ\n"); 1065195ca170SBaolin Wang return ret; 1066195ca170SBaolin Wang } 1067195ca170SBaolin Wang 1068195ca170SBaolin Wang return 0; 1069195ca170SBaolin Wang } 1070195ca170SBaolin Wang 1071e2fb615bSYuanjiang Yu #ifdef CONFIG_PM_SLEEP 1072e2fb615bSYuanjiang Yu static int sc27xx_fgu_resume(struct device *dev) 1073e2fb615bSYuanjiang Yu { 1074e2fb615bSYuanjiang Yu struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 1075e2fb615bSYuanjiang Yu int ret; 1076e2fb615bSYuanjiang Yu 1077e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1078e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT | 1079e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT, 0); 1080e2fb615bSYuanjiang Yu if (ret) { 1081e2fb615bSYuanjiang Yu dev_err(data->dev, "failed to disable fgu interrupts\n"); 1082e2fb615bSYuanjiang Yu return ret; 1083e2fb615bSYuanjiang Yu } 1084e2fb615bSYuanjiang Yu 1085e2fb615bSYuanjiang Yu return 0; 1086e2fb615bSYuanjiang Yu } 1087e2fb615bSYuanjiang Yu 1088e2fb615bSYuanjiang Yu static int sc27xx_fgu_suspend(struct device *dev) 1089e2fb615bSYuanjiang Yu { 1090e2fb615bSYuanjiang Yu struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 1091e2fb615bSYuanjiang Yu int ret, status, ocv; 1092e2fb615bSYuanjiang Yu 1093e2fb615bSYuanjiang Yu ret = sc27xx_fgu_get_status(data, &status); 1094e2fb615bSYuanjiang Yu if (ret) 1095e2fb615bSYuanjiang Yu return ret; 1096e2fb615bSYuanjiang Yu 1097e2fb615bSYuanjiang Yu /* 1098e2fb615bSYuanjiang Yu * If we are charging, then no need to enable the FGU interrupts to 1099e2fb615bSYuanjiang Yu * adjust the battery capacity. 1100e2fb615bSYuanjiang Yu */ 1101e2fb615bSYuanjiang Yu if (status != POWER_SUPPLY_STATUS_NOT_CHARGING) 1102e2fb615bSYuanjiang Yu return 0; 1103e2fb615bSYuanjiang Yu 1104e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1105e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT, 1106e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT); 1107e2fb615bSYuanjiang Yu if (ret) { 1108e2fb615bSYuanjiang Yu dev_err(data->dev, "failed to enable low voltage interrupt\n"); 1109e2fb615bSYuanjiang Yu return ret; 1110e2fb615bSYuanjiang Yu } 1111e2fb615bSYuanjiang Yu 1112e2fb615bSYuanjiang Yu ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 1113e2fb615bSYuanjiang Yu if (ret) 1114e2fb615bSYuanjiang Yu goto disable_int; 1115e2fb615bSYuanjiang Yu 1116e2fb615bSYuanjiang Yu /* 1117e2fb615bSYuanjiang Yu * If current OCV is less than the minimum voltage, we should enable the 1118e2fb615bSYuanjiang Yu * coulomb counter threshold interrupt to notify events to adjust the 1119e2fb615bSYuanjiang Yu * battery capacity. 1120e2fb615bSYuanjiang Yu */ 1121e2fb615bSYuanjiang Yu if (ocv < data->min_volt) { 1122e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, 1123e2fb615bSYuanjiang Yu data->base + SC27XX_FGU_INT_EN, 1124e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT, 1125e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT); 1126e2fb615bSYuanjiang Yu if (ret) { 1127e2fb615bSYuanjiang Yu dev_err(data->dev, 1128e2fb615bSYuanjiang Yu "failed to enable coulomb threshold int\n"); 1129e2fb615bSYuanjiang Yu goto disable_int; 1130e2fb615bSYuanjiang Yu } 1131e2fb615bSYuanjiang Yu } 1132e2fb615bSYuanjiang Yu 1133e2fb615bSYuanjiang Yu return 0; 1134e2fb615bSYuanjiang Yu 1135e2fb615bSYuanjiang Yu disable_int: 1136e2fb615bSYuanjiang Yu regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1137e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT, 0); 1138e2fb615bSYuanjiang Yu return ret; 1139e2fb615bSYuanjiang Yu } 1140e2fb615bSYuanjiang Yu #endif 1141e2fb615bSYuanjiang Yu 1142e2fb615bSYuanjiang Yu static const struct dev_pm_ops sc27xx_fgu_pm_ops = { 1143e2fb615bSYuanjiang Yu SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume) 1144e2fb615bSYuanjiang Yu }; 1145e2fb615bSYuanjiang Yu 1146195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = { 1147195ca170SBaolin Wang { .compatible = "sprd,sc2731-fgu", }, 1148195ca170SBaolin Wang { } 1149195ca170SBaolin Wang }; 1150195ca170SBaolin Wang 1151195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = { 1152195ca170SBaolin Wang .probe = sc27xx_fgu_probe, 1153195ca170SBaolin Wang .driver = { 1154195ca170SBaolin Wang .name = "sc27xx-fgu", 1155195ca170SBaolin Wang .of_match_table = sc27xx_fgu_of_match, 1156e2fb615bSYuanjiang Yu .pm = &sc27xx_fgu_pm_ops, 1157195ca170SBaolin Wang } 1158195ca170SBaolin Wang }; 1159195ca170SBaolin Wang 1160195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver); 1161195ca170SBaolin Wang 1162195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver"); 1163195ca170SBaolin Wang MODULE_LICENSE("GPL v2"); 1164