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 42*4a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_SET 0xa0 43*4a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_CLEAR 0xa4 44*4a040e7cSYuanjiang 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 55*4a040e7cSYuanjiang Yu #define SC27XX_FGU_MODE_AREA_MASK GENMASK(15, 12) 56*4a040e7cSYuanjiang Yu #define SC27XX_FGU_CAP_AREA_MASK GENMASK(11, 0) 57*4a040e7cSYuanjiang Yu #define SC27XX_FGU_MODE_AREA_SHIFT 12 58*4a040e7cSYuanjiang Yu 59*4a040e7cSYuanjiang Yu #define SC27XX_FGU_FIRST_POWERTON GENMASK(3, 0) 60*4a040e7cSYuanjiang Yu #define SC27XX_FGU_DEFAULT_CAP GENMASK(11, 0) 61*4a040e7cSYuanjiang Yu #define SC27XX_FGU_NORMAIL_POWERTON 0x5 62*4a040e7cSYuanjiang 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 75195ca170SBaolin Wang * @internal_resist: the battery internal resistance in mOhm 76195ca170SBaolin Wang * @total_cap: the total capacity of the battery in mAh 77195ca170SBaolin Wang * @init_cap: the initial capacity of the battery in mAh 78edcb1c0aSYuanjiang Yu * @alarm_cap: the alarm capacity 79195ca170SBaolin Wang * @init_clbcnt: the initial coulomb counter 80195ca170SBaolin Wang * @max_volt: the maximum constant input voltage in millivolt 81edcb1c0aSYuanjiang Yu * @min_volt: the minimum drained battery voltage in microvolt 82195ca170SBaolin Wang * @table_len: the capacity table length 8365c9fab7SBaolin Wang * @cur_1000ma_adc: ADC value corresponding to 1000 mA 8465c9fab7SBaolin Wang * @vol_1000mv_adc: ADC value corresponding to 1000 mV 85195ca170SBaolin Wang * @cap_table: capacity table with corresponding ocv 86195ca170SBaolin Wang */ 87195ca170SBaolin Wang struct sc27xx_fgu_data { 88195ca170SBaolin Wang struct regmap *regmap; 89195ca170SBaolin Wang struct device *dev; 90195ca170SBaolin Wang struct power_supply *battery; 91195ca170SBaolin Wang u32 base; 92195ca170SBaolin Wang struct mutex lock; 93195ca170SBaolin Wang struct gpio_desc *gpiod; 94195ca170SBaolin Wang struct iio_channel *channel; 95195ca170SBaolin Wang bool bat_present; 96195ca170SBaolin Wang int internal_resist; 97195ca170SBaolin Wang int total_cap; 98195ca170SBaolin Wang int init_cap; 99edcb1c0aSYuanjiang Yu int alarm_cap; 100195ca170SBaolin Wang int init_clbcnt; 101195ca170SBaolin Wang int max_volt; 102edcb1c0aSYuanjiang Yu int min_volt; 103195ca170SBaolin Wang int table_len; 10465c9fab7SBaolin Wang int cur_1000ma_adc; 10565c9fab7SBaolin Wang int vol_1000mv_adc; 106195ca170SBaolin Wang struct power_supply_battery_ocv_table *cap_table; 107195ca170SBaolin Wang }; 108195ca170SBaolin Wang 109edcb1c0aSYuanjiang Yu static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity); 110edcb1c0aSYuanjiang Yu 111195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = { 112195ca170SBaolin Wang "sc2731_charger", 113195ca170SBaolin Wang "sc2720_charger", 114195ca170SBaolin Wang "sc2721_charger", 115195ca170SBaolin Wang "sc2723_charger", 116195ca170SBaolin Wang }; 117195ca170SBaolin Wang 11865c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc) 119195ca170SBaolin Wang { 12065c9fab7SBaolin Wang return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc); 121195ca170SBaolin Wang } 122195ca170SBaolin Wang 12365c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc) 124195ca170SBaolin Wang { 12565c9fab7SBaolin Wang return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); 126195ca170SBaolin Wang } 127195ca170SBaolin Wang 128edcb1c0aSYuanjiang Yu static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol) 129edcb1c0aSYuanjiang Yu { 130edcb1c0aSYuanjiang Yu return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000); 131edcb1c0aSYuanjiang Yu } 132edcb1c0aSYuanjiang Yu 133*4a040e7cSYuanjiang Yu static bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data) 134*4a040e7cSYuanjiang Yu { 135*4a040e7cSYuanjiang Yu int ret, status, cap, mode; 136*4a040e7cSYuanjiang Yu 137*4a040e7cSYuanjiang Yu ret = regmap_read(data->regmap, 138*4a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_STATUS, &status); 139*4a040e7cSYuanjiang Yu if (ret) 140*4a040e7cSYuanjiang Yu return false; 141*4a040e7cSYuanjiang Yu 142*4a040e7cSYuanjiang Yu /* 143*4a040e7cSYuanjiang Yu * We use low 4 bits to save the last battery capacity and high 12 bits 144*4a040e7cSYuanjiang Yu * to save the system boot mode. 145*4a040e7cSYuanjiang Yu */ 146*4a040e7cSYuanjiang Yu mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT; 147*4a040e7cSYuanjiang Yu cap = status & SC27XX_FGU_CAP_AREA_MASK; 148*4a040e7cSYuanjiang Yu 149*4a040e7cSYuanjiang Yu /* 150*4a040e7cSYuanjiang Yu * When FGU has been powered down, the user area registers became 151*4a040e7cSYuanjiang Yu * default value (0xffff), which can be used to valid if the system is 152*4a040e7cSYuanjiang Yu * first power on or not. 153*4a040e7cSYuanjiang Yu */ 154*4a040e7cSYuanjiang Yu if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP) 155*4a040e7cSYuanjiang Yu return true; 156*4a040e7cSYuanjiang Yu 157*4a040e7cSYuanjiang Yu return false; 158*4a040e7cSYuanjiang Yu } 159*4a040e7cSYuanjiang Yu 160*4a040e7cSYuanjiang Yu static int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data, 161*4a040e7cSYuanjiang Yu int boot_mode) 162*4a040e7cSYuanjiang Yu { 163*4a040e7cSYuanjiang Yu int ret; 164*4a040e7cSYuanjiang Yu 165*4a040e7cSYuanjiang Yu ret = regmap_update_bits(data->regmap, 166*4a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 167*4a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 168*4a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK); 169*4a040e7cSYuanjiang Yu if (ret) 170*4a040e7cSYuanjiang Yu return ret; 171*4a040e7cSYuanjiang Yu 172*4a040e7cSYuanjiang Yu return regmap_update_bits(data->regmap, 173*4a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_SET, 174*4a040e7cSYuanjiang Yu SC27XX_FGU_MODE_AREA_MASK, 175*4a040e7cSYuanjiang Yu boot_mode << SC27XX_FGU_MODE_AREA_SHIFT); 176*4a040e7cSYuanjiang Yu } 177*4a040e7cSYuanjiang Yu 178*4a040e7cSYuanjiang Yu static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap) 179*4a040e7cSYuanjiang Yu { 180*4a040e7cSYuanjiang Yu int ret; 181*4a040e7cSYuanjiang Yu 182*4a040e7cSYuanjiang Yu ret = regmap_update_bits(data->regmap, 183*4a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_CLEAR, 184*4a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, 185*4a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK); 186*4a040e7cSYuanjiang Yu if (ret) 187*4a040e7cSYuanjiang Yu return ret; 188*4a040e7cSYuanjiang Yu 189*4a040e7cSYuanjiang Yu return regmap_update_bits(data->regmap, 190*4a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_SET, 191*4a040e7cSYuanjiang Yu SC27XX_FGU_CAP_AREA_MASK, cap); 192*4a040e7cSYuanjiang Yu } 193*4a040e7cSYuanjiang Yu 194*4a040e7cSYuanjiang Yu static int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap) 195*4a040e7cSYuanjiang Yu { 196*4a040e7cSYuanjiang Yu int ret, value; 197*4a040e7cSYuanjiang Yu 198*4a040e7cSYuanjiang Yu ret = regmap_read(data->regmap, 199*4a040e7cSYuanjiang Yu data->base + SC27XX_FGU_USER_AREA_STATUS, &value); 200*4a040e7cSYuanjiang Yu if (ret) 201*4a040e7cSYuanjiang Yu return ret; 202*4a040e7cSYuanjiang Yu 203*4a040e7cSYuanjiang Yu *cap = value & SC27XX_FGU_CAP_AREA_MASK; 204*4a040e7cSYuanjiang Yu return 0; 205*4a040e7cSYuanjiang Yu } 206*4a040e7cSYuanjiang Yu 207195ca170SBaolin Wang /* 208195ca170SBaolin Wang * When system boots on, we can not read battery capacity from coulomb 209195ca170SBaolin Wang * registers, since now the coulomb registers are invalid. So we should 210195ca170SBaolin Wang * calculate the battery open circuit voltage, and get current battery 211195ca170SBaolin Wang * capacity according to the capacity table. 212195ca170SBaolin Wang */ 213195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) 214195ca170SBaolin Wang { 215195ca170SBaolin Wang int volt, cur, oci, ocv, ret; 216*4a040e7cSYuanjiang Yu bool is_first_poweron = sc27xx_fgu_is_first_poweron(data); 217*4a040e7cSYuanjiang Yu 218*4a040e7cSYuanjiang Yu /* 219*4a040e7cSYuanjiang Yu * If system is not the first power on, we should use the last saved 220*4a040e7cSYuanjiang Yu * battery capacity as the initial battery capacity. Otherwise we should 221*4a040e7cSYuanjiang Yu * re-calculate the initial battery capacity. 222*4a040e7cSYuanjiang Yu */ 223*4a040e7cSYuanjiang Yu if (!is_first_poweron) { 224*4a040e7cSYuanjiang Yu ret = sc27xx_fgu_read_last_cap(data, cap); 225*4a040e7cSYuanjiang Yu if (ret) 226*4a040e7cSYuanjiang Yu return ret; 227*4a040e7cSYuanjiang Yu 228*4a040e7cSYuanjiang Yu return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 229*4a040e7cSYuanjiang Yu } 230195ca170SBaolin Wang 231195ca170SBaolin Wang /* 232195ca170SBaolin Wang * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved 233195ca170SBaolin Wang * the first sampled open circuit current. 234195ca170SBaolin Wang */ 235195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL, 236195ca170SBaolin Wang &cur); 237195ca170SBaolin Wang if (ret) 238195ca170SBaolin Wang return ret; 239195ca170SBaolin Wang 240195ca170SBaolin Wang cur <<= 1; 24165c9fab7SBaolin Wang oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 242195ca170SBaolin Wang 243195ca170SBaolin Wang /* 244195ca170SBaolin Wang * Should get the OCV from SC27XX_FGU_POCV register at the system 245195ca170SBaolin Wang * beginning. It is ADC values reading from registers which need to 246195ca170SBaolin Wang * convert the corresponding voltage. 247195ca170SBaolin Wang */ 248195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt); 249195ca170SBaolin Wang if (ret) 250195ca170SBaolin Wang return ret; 251195ca170SBaolin Wang 25265c9fab7SBaolin Wang volt = sc27xx_fgu_adc_to_voltage(data, volt); 253195ca170SBaolin Wang ocv = volt * 1000 - oci * data->internal_resist; 254195ca170SBaolin Wang 255195ca170SBaolin Wang /* 256195ca170SBaolin Wang * Parse the capacity table to look up the correct capacity percent 257195ca170SBaolin Wang * according to current battery's corresponding OCV values. 258195ca170SBaolin Wang */ 259195ca170SBaolin Wang *cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len, 260195ca170SBaolin Wang ocv); 261195ca170SBaolin Wang 262*4a040e7cSYuanjiang Yu ret = sc27xx_fgu_save_last_cap(data, *cap); 263*4a040e7cSYuanjiang Yu if (ret) 264*4a040e7cSYuanjiang Yu return ret; 265*4a040e7cSYuanjiang Yu 266*4a040e7cSYuanjiang Yu return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 267195ca170SBaolin Wang } 268195ca170SBaolin Wang 269195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt) 270195ca170SBaolin Wang { 271195ca170SBaolin Wang int ret; 272195ca170SBaolin Wang 273195ca170SBaolin Wang clbcnt *= SC27XX_FGU_SAMPLE_HZ; 274195ca170SBaolin Wang 275195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 276195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETL, 277195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, clbcnt); 278195ca170SBaolin Wang if (ret) 279195ca170SBaolin Wang return ret; 280195ca170SBaolin Wang 281195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 282195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETH, 283195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, 284195ca170SBaolin Wang clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 285195ca170SBaolin Wang if (ret) 286195ca170SBaolin Wang return ret; 287195ca170SBaolin Wang 288195ca170SBaolin Wang return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START, 289195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN, 290195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN); 291195ca170SBaolin Wang } 292195ca170SBaolin Wang 293195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt) 294195ca170SBaolin Wang { 295195ca170SBaolin Wang int ccl, cch, ret; 296195ca170SBaolin Wang 297195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL, 298195ca170SBaolin Wang &ccl); 299195ca170SBaolin Wang if (ret) 300195ca170SBaolin Wang return ret; 301195ca170SBaolin Wang 302195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH, 303195ca170SBaolin Wang &cch); 304195ca170SBaolin Wang if (ret) 305195ca170SBaolin Wang return ret; 306195ca170SBaolin Wang 307195ca170SBaolin Wang *clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK; 308195ca170SBaolin Wang *clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT; 309195ca170SBaolin Wang *clb_cnt /= SC27XX_FGU_SAMPLE_HZ; 310195ca170SBaolin Wang 311195ca170SBaolin Wang return 0; 312195ca170SBaolin Wang } 313195ca170SBaolin Wang 314195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap) 315195ca170SBaolin Wang { 316195ca170SBaolin Wang int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp; 317195ca170SBaolin Wang 318195ca170SBaolin Wang /* Get current coulomb counters firstly */ 319195ca170SBaolin Wang ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt); 320195ca170SBaolin Wang if (ret) 321195ca170SBaolin Wang return ret; 322195ca170SBaolin Wang 323195ca170SBaolin Wang delta_clbcnt = cur_clbcnt - data->init_clbcnt; 324195ca170SBaolin Wang 325195ca170SBaolin Wang /* 326195ca170SBaolin Wang * Convert coulomb counter to delta capacity (mAh), and set multiplier 327195ca170SBaolin Wang * as 100 to improve the precision. 328195ca170SBaolin Wang */ 329195ca170SBaolin Wang temp = DIV_ROUND_CLOSEST(delta_clbcnt, 360); 33065c9fab7SBaolin Wang temp = sc27xx_fgu_adc_to_current(data, temp); 331195ca170SBaolin Wang 332195ca170SBaolin Wang /* 333195ca170SBaolin Wang * Convert to capacity percent of the battery total capacity, 334195ca170SBaolin Wang * and multiplier is 100 too. 335195ca170SBaolin Wang */ 336195ca170SBaolin Wang delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap); 337195ca170SBaolin Wang *cap = delta_cap + data->init_cap; 338195ca170SBaolin Wang 339195ca170SBaolin Wang return 0; 340195ca170SBaolin Wang } 341195ca170SBaolin Wang 342195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val) 343195ca170SBaolin Wang { 344195ca170SBaolin Wang int ret, vol; 345195ca170SBaolin Wang 346195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol); 347195ca170SBaolin Wang if (ret) 348195ca170SBaolin Wang return ret; 349195ca170SBaolin Wang 350195ca170SBaolin Wang /* 351195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 352195ca170SBaolin Wang * corresponding voltage values. 353195ca170SBaolin Wang */ 35465c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_voltage(data, vol); 355195ca170SBaolin Wang 356195ca170SBaolin Wang return 0; 357195ca170SBaolin Wang } 358195ca170SBaolin Wang 359195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val) 360195ca170SBaolin Wang { 361195ca170SBaolin Wang int ret, cur; 362195ca170SBaolin Wang 363195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur); 364195ca170SBaolin Wang if (ret) 365195ca170SBaolin Wang return ret; 366195ca170SBaolin Wang 367195ca170SBaolin Wang /* 368195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 369195ca170SBaolin Wang * corresponding current values. 370195ca170SBaolin Wang */ 37165c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 372195ca170SBaolin Wang 373195ca170SBaolin Wang return 0; 374195ca170SBaolin Wang } 375195ca170SBaolin Wang 376195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) 377195ca170SBaolin Wang { 378195ca170SBaolin Wang int vol, cur, ret; 379195ca170SBaolin Wang 380195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 381195ca170SBaolin Wang if (ret) 382195ca170SBaolin Wang return ret; 383195ca170SBaolin Wang 384195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &cur); 385195ca170SBaolin Wang if (ret) 386195ca170SBaolin Wang return ret; 387195ca170SBaolin Wang 388195ca170SBaolin Wang /* Return the battery OCV in micro volts. */ 389195ca170SBaolin Wang *val = vol * 1000 - cur * data->internal_resist; 390195ca170SBaolin Wang 391195ca170SBaolin Wang return 0; 392195ca170SBaolin Wang } 393195ca170SBaolin Wang 394195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp) 395195ca170SBaolin Wang { 396195ca170SBaolin Wang return iio_read_channel_processed(data->channel, temp); 397195ca170SBaolin Wang } 398195ca170SBaolin Wang 399195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health) 400195ca170SBaolin Wang { 401195ca170SBaolin Wang int ret, vol; 402195ca170SBaolin Wang 403195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 404195ca170SBaolin Wang if (ret) 405195ca170SBaolin Wang return ret; 406195ca170SBaolin Wang 407195ca170SBaolin Wang if (vol > data->max_volt) 408195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 409195ca170SBaolin Wang else 410195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_GOOD; 411195ca170SBaolin Wang 412195ca170SBaolin Wang return 0; 413195ca170SBaolin Wang } 414195ca170SBaolin Wang 415195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status) 416195ca170SBaolin Wang { 417195ca170SBaolin Wang union power_supply_propval val; 418195ca170SBaolin Wang struct power_supply *psy; 419195ca170SBaolin Wang int i, ret = -EINVAL; 420195ca170SBaolin Wang 421195ca170SBaolin Wang for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) { 422195ca170SBaolin Wang psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]); 423195ca170SBaolin Wang if (!psy) 424195ca170SBaolin Wang continue; 425195ca170SBaolin Wang 426195ca170SBaolin Wang ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, 427195ca170SBaolin Wang &val); 428195ca170SBaolin Wang power_supply_put(psy); 429195ca170SBaolin Wang if (ret) 430195ca170SBaolin Wang return ret; 431195ca170SBaolin Wang 432195ca170SBaolin Wang *status = val.intval; 433195ca170SBaolin Wang } 434195ca170SBaolin Wang 435195ca170SBaolin Wang return ret; 436195ca170SBaolin Wang } 437195ca170SBaolin Wang 438195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy, 439195ca170SBaolin Wang enum power_supply_property psp, 440195ca170SBaolin Wang union power_supply_propval *val) 441195ca170SBaolin Wang { 442195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 443195ca170SBaolin Wang int ret = 0; 444195ca170SBaolin Wang int value; 445195ca170SBaolin Wang 446195ca170SBaolin Wang mutex_lock(&data->lock); 447195ca170SBaolin Wang 448195ca170SBaolin Wang switch (psp) { 449195ca170SBaolin Wang case POWER_SUPPLY_PROP_STATUS: 450195ca170SBaolin Wang ret = sc27xx_fgu_get_status(data, &value); 451195ca170SBaolin Wang if (ret) 452195ca170SBaolin Wang goto error; 453195ca170SBaolin Wang 454195ca170SBaolin Wang val->intval = value; 455195ca170SBaolin Wang break; 456195ca170SBaolin Wang 457195ca170SBaolin Wang case POWER_SUPPLY_PROP_HEALTH: 458195ca170SBaolin Wang ret = sc27xx_fgu_get_health(data, &value); 459195ca170SBaolin Wang if (ret) 460195ca170SBaolin Wang goto error; 461195ca170SBaolin Wang 462195ca170SBaolin Wang val->intval = value; 463195ca170SBaolin Wang break; 464195ca170SBaolin Wang 465195ca170SBaolin Wang case POWER_SUPPLY_PROP_PRESENT: 466195ca170SBaolin Wang val->intval = data->bat_present; 467195ca170SBaolin Wang break; 468195ca170SBaolin Wang 469195ca170SBaolin Wang case POWER_SUPPLY_PROP_TEMP: 470195ca170SBaolin Wang ret = sc27xx_fgu_get_temp(data, &value); 471195ca170SBaolin Wang if (ret) 472195ca170SBaolin Wang goto error; 473195ca170SBaolin Wang 474195ca170SBaolin Wang val->intval = value; 475195ca170SBaolin Wang break; 476195ca170SBaolin Wang 477195ca170SBaolin Wang case POWER_SUPPLY_PROP_TECHNOLOGY: 478195ca170SBaolin Wang val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 479195ca170SBaolin Wang break; 480195ca170SBaolin Wang 481195ca170SBaolin Wang case POWER_SUPPLY_PROP_CAPACITY: 482195ca170SBaolin Wang ret = sc27xx_fgu_get_capacity(data, &value); 483195ca170SBaolin Wang if (ret) 484195ca170SBaolin Wang goto error; 485195ca170SBaolin Wang 486195ca170SBaolin Wang val->intval = value; 487195ca170SBaolin Wang break; 488195ca170SBaolin Wang 489195ca170SBaolin Wang case POWER_SUPPLY_PROP_VOLTAGE_NOW: 490195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &value); 491195ca170SBaolin Wang if (ret) 492195ca170SBaolin Wang goto error; 493195ca170SBaolin Wang 494195ca170SBaolin Wang val->intval = value * 1000; 495195ca170SBaolin Wang break; 496195ca170SBaolin Wang 497195ca170SBaolin Wang case POWER_SUPPLY_PROP_VOLTAGE_OCV: 498195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_ocv(data, &value); 499195ca170SBaolin Wang if (ret) 500195ca170SBaolin Wang goto error; 501195ca170SBaolin Wang 502195ca170SBaolin Wang val->intval = value; 503195ca170SBaolin Wang break; 504195ca170SBaolin Wang 505195ca170SBaolin Wang case POWER_SUPPLY_PROP_CURRENT_NOW: 506195ca170SBaolin Wang case POWER_SUPPLY_PROP_CURRENT_AVG: 507195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &value); 508195ca170SBaolin Wang if (ret) 509195ca170SBaolin Wang goto error; 510195ca170SBaolin Wang 511195ca170SBaolin Wang val->intval = value * 1000; 512195ca170SBaolin Wang break; 513195ca170SBaolin Wang 514195ca170SBaolin Wang default: 515195ca170SBaolin Wang ret = -EINVAL; 516195ca170SBaolin Wang break; 517195ca170SBaolin Wang } 518195ca170SBaolin Wang 519195ca170SBaolin Wang error: 520195ca170SBaolin Wang mutex_unlock(&data->lock); 521195ca170SBaolin Wang return ret; 522195ca170SBaolin Wang } 523195ca170SBaolin Wang 524*4a040e7cSYuanjiang Yu static int sc27xx_fgu_set_property(struct power_supply *psy, 525*4a040e7cSYuanjiang Yu enum power_supply_property psp, 526*4a040e7cSYuanjiang Yu const union power_supply_propval *val) 527*4a040e7cSYuanjiang Yu { 528*4a040e7cSYuanjiang Yu struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 529*4a040e7cSYuanjiang Yu int ret; 530*4a040e7cSYuanjiang Yu 531*4a040e7cSYuanjiang Yu if (psp != POWER_SUPPLY_PROP_CAPACITY) 532*4a040e7cSYuanjiang Yu return -EINVAL; 533*4a040e7cSYuanjiang Yu 534*4a040e7cSYuanjiang Yu mutex_lock(&data->lock); 535*4a040e7cSYuanjiang Yu 536*4a040e7cSYuanjiang Yu ret = sc27xx_fgu_save_last_cap(data, val->intval); 537*4a040e7cSYuanjiang Yu 538*4a040e7cSYuanjiang Yu mutex_unlock(&data->lock); 539*4a040e7cSYuanjiang Yu 540*4a040e7cSYuanjiang Yu if (ret < 0) 541*4a040e7cSYuanjiang Yu dev_err(data->dev, "failed to save battery capacity\n"); 542*4a040e7cSYuanjiang Yu 543*4a040e7cSYuanjiang Yu return ret; 544*4a040e7cSYuanjiang Yu } 545*4a040e7cSYuanjiang Yu 546195ca170SBaolin Wang static void sc27xx_fgu_external_power_changed(struct power_supply *psy) 547195ca170SBaolin Wang { 548195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 549195ca170SBaolin Wang 550195ca170SBaolin Wang power_supply_changed(data->battery); 551195ca170SBaolin Wang } 552195ca170SBaolin Wang 553*4a040e7cSYuanjiang Yu static int sc27xx_fgu_property_is_writeable(struct power_supply *psy, 554*4a040e7cSYuanjiang Yu enum power_supply_property psp) 555*4a040e7cSYuanjiang Yu { 556*4a040e7cSYuanjiang Yu return psp == POWER_SUPPLY_PROP_CAPACITY; 557*4a040e7cSYuanjiang Yu } 558*4a040e7cSYuanjiang Yu 559195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = { 560195ca170SBaolin Wang POWER_SUPPLY_PROP_STATUS, 561195ca170SBaolin Wang POWER_SUPPLY_PROP_HEALTH, 562195ca170SBaolin Wang POWER_SUPPLY_PROP_PRESENT, 563195ca170SBaolin Wang POWER_SUPPLY_PROP_TEMP, 564195ca170SBaolin Wang POWER_SUPPLY_PROP_TECHNOLOGY, 565195ca170SBaolin Wang POWER_SUPPLY_PROP_CAPACITY, 566195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_NOW, 567195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_OCV, 568195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_NOW, 569195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_AVG, 570195ca170SBaolin Wang }; 571195ca170SBaolin Wang 572195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = { 573195ca170SBaolin Wang .name = "sc27xx-fgu", 574195ca170SBaolin Wang .type = POWER_SUPPLY_TYPE_BATTERY, 575195ca170SBaolin Wang .properties = sc27xx_fgu_props, 576195ca170SBaolin Wang .num_properties = ARRAY_SIZE(sc27xx_fgu_props), 577195ca170SBaolin Wang .get_property = sc27xx_fgu_get_property, 578*4a040e7cSYuanjiang Yu .set_property = sc27xx_fgu_set_property, 579195ca170SBaolin Wang .external_power_changed = sc27xx_fgu_external_power_changed, 580*4a040e7cSYuanjiang Yu .property_is_writeable = sc27xx_fgu_property_is_writeable, 581195ca170SBaolin Wang }; 582195ca170SBaolin Wang 583edcb1c0aSYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap) 584edcb1c0aSYuanjiang Yu { 585edcb1c0aSYuanjiang Yu data->init_cap = cap; 586edcb1c0aSYuanjiang Yu data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); 587edcb1c0aSYuanjiang Yu } 588edcb1c0aSYuanjiang Yu 589edcb1c0aSYuanjiang Yu static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id) 590edcb1c0aSYuanjiang Yu { 591edcb1c0aSYuanjiang Yu struct sc27xx_fgu_data *data = dev_id; 592edcb1c0aSYuanjiang Yu int ret, cap, ocv, adc; 593edcb1c0aSYuanjiang Yu u32 status; 594edcb1c0aSYuanjiang Yu 595edcb1c0aSYuanjiang Yu mutex_lock(&data->lock); 596edcb1c0aSYuanjiang Yu 597edcb1c0aSYuanjiang Yu ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS, 598edcb1c0aSYuanjiang Yu &status); 599edcb1c0aSYuanjiang Yu if (ret) 600edcb1c0aSYuanjiang Yu goto out; 601edcb1c0aSYuanjiang Yu 602edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 603edcb1c0aSYuanjiang Yu status, status); 604edcb1c0aSYuanjiang Yu if (ret) 605edcb1c0aSYuanjiang Yu goto out; 606edcb1c0aSYuanjiang Yu 607edcb1c0aSYuanjiang Yu /* 608edcb1c0aSYuanjiang Yu * When low overload voltage interrupt happens, we should calibrate the 609edcb1c0aSYuanjiang Yu * battery capacity in lower voltage stage. 610edcb1c0aSYuanjiang Yu */ 611edcb1c0aSYuanjiang Yu if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT)) 612edcb1c0aSYuanjiang Yu goto out; 613edcb1c0aSYuanjiang Yu 614edcb1c0aSYuanjiang Yu ret = sc27xx_fgu_get_capacity(data, &cap); 615edcb1c0aSYuanjiang Yu if (ret) 616edcb1c0aSYuanjiang Yu goto out; 617edcb1c0aSYuanjiang Yu 618edcb1c0aSYuanjiang Yu ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 619edcb1c0aSYuanjiang Yu if (ret) 620edcb1c0aSYuanjiang Yu goto out; 621edcb1c0aSYuanjiang Yu 622edcb1c0aSYuanjiang Yu /* 623edcb1c0aSYuanjiang Yu * If current OCV value is less than the minimum OCV value in OCV table, 624edcb1c0aSYuanjiang Yu * which means now battery capacity is 0%, and we should adjust the 625edcb1c0aSYuanjiang Yu * inititial capacity to 0. 626edcb1c0aSYuanjiang Yu */ 627edcb1c0aSYuanjiang Yu if (ocv <= data->cap_table[data->table_len - 1].ocv) { 628edcb1c0aSYuanjiang Yu sc27xx_fgu_adjust_cap(data, 0); 629edcb1c0aSYuanjiang Yu } else if (ocv <= data->min_volt) { 630edcb1c0aSYuanjiang Yu /* 631edcb1c0aSYuanjiang Yu * If current OCV value is less than the low alarm voltage, but 632edcb1c0aSYuanjiang Yu * current capacity is larger than the alarm capacity, we should 633edcb1c0aSYuanjiang Yu * adjust the inititial capacity to alarm capacity. 634edcb1c0aSYuanjiang Yu */ 635edcb1c0aSYuanjiang Yu if (cap > data->alarm_cap) { 636edcb1c0aSYuanjiang Yu sc27xx_fgu_adjust_cap(data, data->alarm_cap); 637edcb1c0aSYuanjiang Yu } else if (cap <= 0) { 638edcb1c0aSYuanjiang Yu int cur_cap; 639edcb1c0aSYuanjiang Yu 640edcb1c0aSYuanjiang Yu /* 641edcb1c0aSYuanjiang Yu * If current capacity is equal with 0 or less than 0 642edcb1c0aSYuanjiang Yu * (some error occurs), we should adjust inititial 643edcb1c0aSYuanjiang Yu * capacity to the capacity corresponding to current OCV 644edcb1c0aSYuanjiang Yu * value. 645edcb1c0aSYuanjiang Yu */ 646edcb1c0aSYuanjiang Yu cur_cap = power_supply_ocv2cap_simple(data->cap_table, 647edcb1c0aSYuanjiang Yu data->table_len, 648edcb1c0aSYuanjiang Yu ocv); 649edcb1c0aSYuanjiang Yu sc27xx_fgu_adjust_cap(data, cur_cap); 650edcb1c0aSYuanjiang Yu } 651edcb1c0aSYuanjiang Yu 652edcb1c0aSYuanjiang Yu /* 653edcb1c0aSYuanjiang Yu * After adjusting the battery capacity, we should set the 654edcb1c0aSYuanjiang Yu * lowest alarm voltage instead. 655edcb1c0aSYuanjiang Yu */ 656edcb1c0aSYuanjiang Yu data->min_volt = data->cap_table[data->table_len - 1].ocv; 657edcb1c0aSYuanjiang Yu adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 658edcb1c0aSYuanjiang Yu regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, 659edcb1c0aSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_MASK, adc); 660edcb1c0aSYuanjiang Yu } 661edcb1c0aSYuanjiang Yu 662edcb1c0aSYuanjiang Yu out: 663edcb1c0aSYuanjiang Yu mutex_unlock(&data->lock); 664edcb1c0aSYuanjiang Yu 665edcb1c0aSYuanjiang Yu power_supply_changed(data->battery); 666edcb1c0aSYuanjiang Yu return IRQ_HANDLED; 667edcb1c0aSYuanjiang Yu } 668edcb1c0aSYuanjiang Yu 669195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id) 670195ca170SBaolin Wang { 671195ca170SBaolin Wang struct sc27xx_fgu_data *data = dev_id; 672195ca170SBaolin Wang int state; 673195ca170SBaolin Wang 674195ca170SBaolin Wang mutex_lock(&data->lock); 675195ca170SBaolin Wang 676195ca170SBaolin Wang state = gpiod_get_value_cansleep(data->gpiod); 677195ca170SBaolin Wang if (state < 0) { 678195ca170SBaolin Wang dev_err(data->dev, "failed to get gpio state\n"); 679195ca170SBaolin Wang mutex_unlock(&data->lock); 680195ca170SBaolin Wang return IRQ_RETVAL(state); 681195ca170SBaolin Wang } 682195ca170SBaolin Wang 683195ca170SBaolin Wang data->bat_present = !!state; 684195ca170SBaolin Wang 685195ca170SBaolin Wang mutex_unlock(&data->lock); 686195ca170SBaolin Wang 687195ca170SBaolin Wang power_supply_changed(data->battery); 688195ca170SBaolin Wang return IRQ_HANDLED; 689195ca170SBaolin Wang } 690195ca170SBaolin Wang 691195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data) 692195ca170SBaolin Wang { 693195ca170SBaolin Wang struct sc27xx_fgu_data *data = _data; 694195ca170SBaolin Wang 695195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 696195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 697195ca170SBaolin Wang } 698195ca170SBaolin Wang 699195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity) 700195ca170SBaolin Wang { 701195ca170SBaolin Wang /* 702195ca170SBaolin Wang * Get current capacity (mAh) = battery total capacity (mAh) * 703195ca170SBaolin Wang * current capacity percent (capacity / 100). 704195ca170SBaolin Wang */ 705195ca170SBaolin Wang int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100); 706195ca170SBaolin Wang 707195ca170SBaolin Wang /* 708195ca170SBaolin Wang * Convert current capacity (mAh) to coulomb counter according to the 709195ca170SBaolin Wang * formula: 1 mAh =3.6 coulomb. 710195ca170SBaolin Wang */ 711195ca170SBaolin Wang return DIV_ROUND_CLOSEST(cur_cap * 36, 10); 712195ca170SBaolin Wang } 713195ca170SBaolin Wang 71465c9fab7SBaolin Wang static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) 71565c9fab7SBaolin Wang { 71665c9fab7SBaolin Wang struct nvmem_cell *cell; 71765c9fab7SBaolin Wang int calib_data, cal_4200mv; 71865c9fab7SBaolin Wang void *buf; 71965c9fab7SBaolin Wang size_t len; 72065c9fab7SBaolin Wang 72165c9fab7SBaolin Wang cell = nvmem_cell_get(data->dev, "fgu_calib"); 72265c9fab7SBaolin Wang if (IS_ERR(cell)) 72365c9fab7SBaolin Wang return PTR_ERR(cell); 72465c9fab7SBaolin Wang 72565c9fab7SBaolin Wang buf = nvmem_cell_read(cell, &len); 72665c9fab7SBaolin Wang nvmem_cell_put(cell); 72765c9fab7SBaolin Wang 72865c9fab7SBaolin Wang if (IS_ERR(buf)) 72965c9fab7SBaolin Wang return PTR_ERR(buf); 73065c9fab7SBaolin Wang 73165c9fab7SBaolin Wang memcpy(&calib_data, buf, min(len, sizeof(u32))); 73265c9fab7SBaolin Wang 73365c9fab7SBaolin Wang /* 73465c9fab7SBaolin Wang * Get the ADC value corresponding to 4200 mV from eFuse controller 73565c9fab7SBaolin Wang * according to below formula. Then convert to ADC values corresponding 73665c9fab7SBaolin Wang * to 1000 mV and 1000 mA. 73765c9fab7SBaolin Wang */ 73865c9fab7SBaolin Wang cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256; 73965c9fab7SBaolin Wang data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42); 74065c9fab7SBaolin Wang data->cur_1000ma_adc = data->vol_1000mv_adc * 4; 74165c9fab7SBaolin Wang 74265c9fab7SBaolin Wang kfree(buf); 74365c9fab7SBaolin Wang return 0; 74465c9fab7SBaolin Wang } 74565c9fab7SBaolin Wang 746195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) 747195ca170SBaolin Wang { 748195ca170SBaolin Wang struct power_supply_battery_info info = { }; 749195ca170SBaolin Wang struct power_supply_battery_ocv_table *table; 750edcb1c0aSYuanjiang Yu int ret, delta_clbcnt, alarm_adc; 751195ca170SBaolin Wang 752195ca170SBaolin Wang ret = power_supply_get_battery_info(data->battery, &info); 753195ca170SBaolin Wang if (ret) { 754195ca170SBaolin Wang dev_err(data->dev, "failed to get battery information\n"); 755195ca170SBaolin Wang return ret; 756195ca170SBaolin Wang } 757195ca170SBaolin Wang 758195ca170SBaolin Wang data->total_cap = info.charge_full_design_uah / 1000; 759195ca170SBaolin Wang data->max_volt = info.constant_charge_voltage_max_uv / 1000; 760195ca170SBaolin Wang data->internal_resist = info.factory_internal_resistance_uohm / 1000; 761edcb1c0aSYuanjiang Yu data->min_volt = info.voltage_min_design_uv; 762195ca170SBaolin Wang 763195ca170SBaolin Wang /* 764195ca170SBaolin Wang * For SC27XX fuel gauge device, we only use one ocv-capacity 765195ca170SBaolin Wang * table in normal temperature 20 Celsius. 766195ca170SBaolin Wang */ 767195ca170SBaolin Wang table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len); 768195ca170SBaolin Wang if (!table) 769195ca170SBaolin Wang return -EINVAL; 770195ca170SBaolin Wang 771195ca170SBaolin Wang data->cap_table = devm_kmemdup(data->dev, table, 772195ca170SBaolin Wang data->table_len * sizeof(*table), 773195ca170SBaolin Wang GFP_KERNEL); 774195ca170SBaolin Wang if (!data->cap_table) { 775195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 776195ca170SBaolin Wang return -ENOMEM; 777195ca170SBaolin Wang } 778195ca170SBaolin Wang 779edcb1c0aSYuanjiang Yu data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, 780edcb1c0aSYuanjiang Yu data->table_len, 781edcb1c0aSYuanjiang Yu data->min_volt); 782edcb1c0aSYuanjiang Yu 783195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 784195ca170SBaolin Wang 78565c9fab7SBaolin Wang ret = sc27xx_fgu_calibration(data); 78665c9fab7SBaolin Wang if (ret) 78765c9fab7SBaolin Wang return ret; 78865c9fab7SBaolin Wang 789195ca170SBaolin Wang /* Enable the FGU module */ 790195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, 791195ca170SBaolin Wang SC27XX_FGU_EN, SC27XX_FGU_EN); 792195ca170SBaolin Wang if (ret) { 793195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu\n"); 794195ca170SBaolin Wang return ret; 795195ca170SBaolin Wang } 796195ca170SBaolin Wang 797195ca170SBaolin Wang /* Enable the FGU RTC clock to make it work */ 798195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0, 799195ca170SBaolin Wang SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN); 800195ca170SBaolin Wang if (ret) { 801195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu RTC clock\n"); 802195ca170SBaolin Wang goto disable_fgu; 803195ca170SBaolin Wang } 804195ca170SBaolin Wang 805edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 806edcb1c0aSYuanjiang Yu SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK); 807edcb1c0aSYuanjiang Yu if (ret) { 808edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to clear interrupt status\n"); 809edcb1c0aSYuanjiang Yu goto disable_clk; 810edcb1c0aSYuanjiang Yu } 811edcb1c0aSYuanjiang Yu 812edcb1c0aSYuanjiang Yu /* 813edcb1c0aSYuanjiang Yu * Set the voltage low overload threshold, which means when the battery 814edcb1c0aSYuanjiang Yu * voltage is lower than this threshold, the controller will generate 815edcb1c0aSYuanjiang Yu * one interrupt to notify. 816edcb1c0aSYuanjiang Yu */ 817edcb1c0aSYuanjiang Yu alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 818edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, 819edcb1c0aSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc); 820edcb1c0aSYuanjiang Yu if (ret) { 821edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set fgu low overload\n"); 822edcb1c0aSYuanjiang Yu goto disable_clk; 823edcb1c0aSYuanjiang Yu } 824edcb1c0aSYuanjiang Yu 825edcb1c0aSYuanjiang Yu /* 826edcb1c0aSYuanjiang Yu * Set the coulomb counter delta threshold, that means when the coulomb 827edcb1c0aSYuanjiang Yu * counter change is multiples of the delta threshold, the controller 828edcb1c0aSYuanjiang Yu * will generate one interrupt to notify the users to update the battery 829edcb1c0aSYuanjiang Yu * capacity. Now we set the delta threshold as a counter value of 1% 830edcb1c0aSYuanjiang Yu * capacity. 831edcb1c0aSYuanjiang Yu */ 832edcb1c0aSYuanjiang Yu delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1); 833edcb1c0aSYuanjiang Yu 834edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL, 835edcb1c0aSYuanjiang Yu SC27XX_FGU_CLBCNT_MASK, delta_clbcnt); 836edcb1c0aSYuanjiang Yu if (ret) { 837edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set low delta coulomb counter\n"); 838edcb1c0aSYuanjiang Yu goto disable_clk; 839edcb1c0aSYuanjiang Yu } 840edcb1c0aSYuanjiang Yu 841edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH, 842edcb1c0aSYuanjiang Yu SC27XX_FGU_CLBCNT_MASK, 843edcb1c0aSYuanjiang Yu delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 844edcb1c0aSYuanjiang Yu if (ret) { 845edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set high delta coulomb counter\n"); 846edcb1c0aSYuanjiang Yu goto disable_clk; 847edcb1c0aSYuanjiang Yu } 848edcb1c0aSYuanjiang Yu 849195ca170SBaolin Wang /* 850195ca170SBaolin Wang * Get the boot battery capacity when system powers on, which is used to 851195ca170SBaolin Wang * initialize the coulomb counter. After that, we can read the coulomb 852195ca170SBaolin Wang * counter to measure the battery capacity. 853195ca170SBaolin Wang */ 854195ca170SBaolin Wang ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap); 855195ca170SBaolin Wang if (ret) { 856195ca170SBaolin Wang dev_err(data->dev, "failed to get boot capacity\n"); 857195ca170SBaolin Wang goto disable_clk; 858195ca170SBaolin Wang } 859195ca170SBaolin Wang 860195ca170SBaolin Wang /* 861195ca170SBaolin Wang * Convert battery capacity to the corresponding initial coulomb counter 862195ca170SBaolin Wang * and set into coulomb counter registers. 863195ca170SBaolin Wang */ 864195ca170SBaolin Wang data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); 865195ca170SBaolin Wang ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt); 866195ca170SBaolin Wang if (ret) { 867195ca170SBaolin Wang dev_err(data->dev, "failed to initialize coulomb counter\n"); 868195ca170SBaolin Wang goto disable_clk; 869195ca170SBaolin Wang } 870195ca170SBaolin Wang 871195ca170SBaolin Wang return 0; 872195ca170SBaolin Wang 873195ca170SBaolin Wang disable_clk: 874195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 875195ca170SBaolin Wang disable_fgu: 876195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 877195ca170SBaolin Wang 878195ca170SBaolin Wang return ret; 879195ca170SBaolin Wang } 880195ca170SBaolin Wang 881195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev) 882195ca170SBaolin Wang { 883195ca170SBaolin Wang struct device_node *np = pdev->dev.of_node; 884195ca170SBaolin Wang struct power_supply_config fgu_cfg = { }; 885195ca170SBaolin Wang struct sc27xx_fgu_data *data; 886195ca170SBaolin Wang int ret, irq; 887195ca170SBaolin Wang 888195ca170SBaolin Wang data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 889195ca170SBaolin Wang if (!data) 890195ca170SBaolin Wang return -ENOMEM; 891195ca170SBaolin Wang 892195ca170SBaolin Wang data->regmap = dev_get_regmap(pdev->dev.parent, NULL); 893195ca170SBaolin Wang if (!data->regmap) { 894195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get regmap\n"); 895195ca170SBaolin Wang return -ENODEV; 896195ca170SBaolin Wang } 897195ca170SBaolin Wang 898195ca170SBaolin Wang ret = device_property_read_u32(&pdev->dev, "reg", &data->base); 899195ca170SBaolin Wang if (ret) { 900195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get fgu address\n"); 901195ca170SBaolin Wang return ret; 902195ca170SBaolin Wang } 903195ca170SBaolin Wang 904195ca170SBaolin Wang data->channel = devm_iio_channel_get(&pdev->dev, "bat-temp"); 905195ca170SBaolin Wang if (IS_ERR(data->channel)) { 906195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get IIO channel\n"); 907195ca170SBaolin Wang return PTR_ERR(data->channel); 908195ca170SBaolin Wang } 909195ca170SBaolin Wang 910195ca170SBaolin Wang data->gpiod = devm_gpiod_get(&pdev->dev, "bat-detect", GPIOD_IN); 911195ca170SBaolin Wang if (IS_ERR(data->gpiod)) { 912195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get battery detection GPIO\n"); 913195ca170SBaolin Wang return PTR_ERR(data->gpiod); 914195ca170SBaolin Wang } 915195ca170SBaolin Wang 916195ca170SBaolin Wang ret = gpiod_get_value_cansleep(data->gpiod); 917195ca170SBaolin Wang if (ret < 0) { 918195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get gpio state\n"); 919195ca170SBaolin Wang return ret; 920195ca170SBaolin Wang } 921195ca170SBaolin Wang 922195ca170SBaolin Wang data->bat_present = !!ret; 923195ca170SBaolin Wang mutex_init(&data->lock); 924195ca170SBaolin Wang data->dev = &pdev->dev; 925e2fb615bSYuanjiang Yu platform_set_drvdata(pdev, data); 926195ca170SBaolin Wang 927195ca170SBaolin Wang fgu_cfg.drv_data = data; 928195ca170SBaolin Wang fgu_cfg.of_node = np; 929195ca170SBaolin Wang data->battery = devm_power_supply_register(&pdev->dev, &sc27xx_fgu_desc, 930195ca170SBaolin Wang &fgu_cfg); 931195ca170SBaolin Wang if (IS_ERR(data->battery)) { 932195ca170SBaolin Wang dev_err(&pdev->dev, "failed to register power supply\n"); 933195ca170SBaolin Wang return PTR_ERR(data->battery); 934195ca170SBaolin Wang } 935195ca170SBaolin Wang 936195ca170SBaolin Wang ret = sc27xx_fgu_hw_init(data); 937195ca170SBaolin Wang if (ret) { 938195ca170SBaolin Wang dev_err(&pdev->dev, "failed to initialize fgu hardware\n"); 939195ca170SBaolin Wang return ret; 940195ca170SBaolin Wang } 941195ca170SBaolin Wang 942195ca170SBaolin Wang ret = devm_add_action(&pdev->dev, sc27xx_fgu_disable, data); 943195ca170SBaolin Wang if (ret) { 944195ca170SBaolin Wang sc27xx_fgu_disable(data); 945195ca170SBaolin Wang dev_err(&pdev->dev, "failed to add fgu disable action\n"); 946195ca170SBaolin Wang return ret; 947195ca170SBaolin Wang } 948195ca170SBaolin Wang 949edcb1c0aSYuanjiang Yu irq = platform_get_irq(pdev, 0); 950edcb1c0aSYuanjiang Yu if (irq < 0) { 951edcb1c0aSYuanjiang Yu dev_err(&pdev->dev, "no irq resource specified\n"); 952edcb1c0aSYuanjiang Yu return irq; 953edcb1c0aSYuanjiang Yu } 954edcb1c0aSYuanjiang Yu 955edcb1c0aSYuanjiang Yu ret = devm_request_threaded_irq(data->dev, irq, NULL, 956edcb1c0aSYuanjiang Yu sc27xx_fgu_interrupt, 957edcb1c0aSYuanjiang Yu IRQF_NO_SUSPEND | IRQF_ONESHOT, 958edcb1c0aSYuanjiang Yu pdev->name, data); 959edcb1c0aSYuanjiang Yu if (ret) { 960edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to request fgu IRQ\n"); 961edcb1c0aSYuanjiang Yu return ret; 962edcb1c0aSYuanjiang Yu } 963edcb1c0aSYuanjiang Yu 964195ca170SBaolin Wang irq = gpiod_to_irq(data->gpiod); 965195ca170SBaolin Wang if (irq < 0) { 966195ca170SBaolin Wang dev_err(&pdev->dev, "failed to translate GPIO to IRQ\n"); 967195ca170SBaolin Wang return irq; 968195ca170SBaolin Wang } 969195ca170SBaolin Wang 970195ca170SBaolin Wang ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 971195ca170SBaolin Wang sc27xx_fgu_bat_detection, 972195ca170SBaolin Wang IRQF_ONESHOT | IRQF_TRIGGER_RISING | 973195ca170SBaolin Wang IRQF_TRIGGER_FALLING, 974195ca170SBaolin Wang pdev->name, data); 975195ca170SBaolin Wang if (ret) { 976195ca170SBaolin Wang dev_err(&pdev->dev, "failed to request IRQ\n"); 977195ca170SBaolin Wang return ret; 978195ca170SBaolin Wang } 979195ca170SBaolin Wang 980195ca170SBaolin Wang return 0; 981195ca170SBaolin Wang } 982195ca170SBaolin Wang 983e2fb615bSYuanjiang Yu #ifdef CONFIG_PM_SLEEP 984e2fb615bSYuanjiang Yu static int sc27xx_fgu_resume(struct device *dev) 985e2fb615bSYuanjiang Yu { 986e2fb615bSYuanjiang Yu struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 987e2fb615bSYuanjiang Yu int ret; 988e2fb615bSYuanjiang Yu 989e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 990e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT | 991e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT, 0); 992e2fb615bSYuanjiang Yu if (ret) { 993e2fb615bSYuanjiang Yu dev_err(data->dev, "failed to disable fgu interrupts\n"); 994e2fb615bSYuanjiang Yu return ret; 995e2fb615bSYuanjiang Yu } 996e2fb615bSYuanjiang Yu 997e2fb615bSYuanjiang Yu return 0; 998e2fb615bSYuanjiang Yu } 999e2fb615bSYuanjiang Yu 1000e2fb615bSYuanjiang Yu static int sc27xx_fgu_suspend(struct device *dev) 1001e2fb615bSYuanjiang Yu { 1002e2fb615bSYuanjiang Yu struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 1003e2fb615bSYuanjiang Yu int ret, status, ocv; 1004e2fb615bSYuanjiang Yu 1005e2fb615bSYuanjiang Yu ret = sc27xx_fgu_get_status(data, &status); 1006e2fb615bSYuanjiang Yu if (ret) 1007e2fb615bSYuanjiang Yu return ret; 1008e2fb615bSYuanjiang Yu 1009e2fb615bSYuanjiang Yu /* 1010e2fb615bSYuanjiang Yu * If we are charging, then no need to enable the FGU interrupts to 1011e2fb615bSYuanjiang Yu * adjust the battery capacity. 1012e2fb615bSYuanjiang Yu */ 1013e2fb615bSYuanjiang Yu if (status != POWER_SUPPLY_STATUS_NOT_CHARGING) 1014e2fb615bSYuanjiang Yu return 0; 1015e2fb615bSYuanjiang Yu 1016e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1017e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT, 1018e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT); 1019e2fb615bSYuanjiang Yu if (ret) { 1020e2fb615bSYuanjiang Yu dev_err(data->dev, "failed to enable low voltage interrupt\n"); 1021e2fb615bSYuanjiang Yu return ret; 1022e2fb615bSYuanjiang Yu } 1023e2fb615bSYuanjiang Yu 1024e2fb615bSYuanjiang Yu ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 1025e2fb615bSYuanjiang Yu if (ret) 1026e2fb615bSYuanjiang Yu goto disable_int; 1027e2fb615bSYuanjiang Yu 1028e2fb615bSYuanjiang Yu /* 1029e2fb615bSYuanjiang Yu * If current OCV is less than the minimum voltage, we should enable the 1030e2fb615bSYuanjiang Yu * coulomb counter threshold interrupt to notify events to adjust the 1031e2fb615bSYuanjiang Yu * battery capacity. 1032e2fb615bSYuanjiang Yu */ 1033e2fb615bSYuanjiang Yu if (ocv < data->min_volt) { 1034e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, 1035e2fb615bSYuanjiang Yu data->base + SC27XX_FGU_INT_EN, 1036e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT, 1037e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT); 1038e2fb615bSYuanjiang Yu if (ret) { 1039e2fb615bSYuanjiang Yu dev_err(data->dev, 1040e2fb615bSYuanjiang Yu "failed to enable coulomb threshold int\n"); 1041e2fb615bSYuanjiang Yu goto disable_int; 1042e2fb615bSYuanjiang Yu } 1043e2fb615bSYuanjiang Yu } 1044e2fb615bSYuanjiang Yu 1045e2fb615bSYuanjiang Yu return 0; 1046e2fb615bSYuanjiang Yu 1047e2fb615bSYuanjiang Yu disable_int: 1048e2fb615bSYuanjiang Yu regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 1049e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT, 0); 1050e2fb615bSYuanjiang Yu return ret; 1051e2fb615bSYuanjiang Yu } 1052e2fb615bSYuanjiang Yu #endif 1053e2fb615bSYuanjiang Yu 1054e2fb615bSYuanjiang Yu static const struct dev_pm_ops sc27xx_fgu_pm_ops = { 1055e2fb615bSYuanjiang Yu SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume) 1056e2fb615bSYuanjiang Yu }; 1057e2fb615bSYuanjiang Yu 1058195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = { 1059195ca170SBaolin Wang { .compatible = "sprd,sc2731-fgu", }, 1060195ca170SBaolin Wang { } 1061195ca170SBaolin Wang }; 1062195ca170SBaolin Wang 1063195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = { 1064195ca170SBaolin Wang .probe = sc27xx_fgu_probe, 1065195ca170SBaolin Wang .driver = { 1066195ca170SBaolin Wang .name = "sc27xx-fgu", 1067195ca170SBaolin Wang .of_match_table = sc27xx_fgu_of_match, 1068e2fb615bSYuanjiang Yu .pm = &sc27xx_fgu_pm_ops, 1069195ca170SBaolin Wang } 1070195ca170SBaolin Wang }; 1071195ca170SBaolin Wang 1072195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver); 1073195ca170SBaolin Wang 1074195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver"); 1075195ca170SBaolin Wang MODULE_LICENSE("GPL v2"); 1076