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 42195ca170SBaolin Wang 43195ca170SBaolin Wang #define SC27XX_WRITE_SELCLB_EN BIT(0) 44195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0) 45195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SHIFT 16 46edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD_MASK GENMASK(12, 0) 47edcb1c0aSYuanjiang Yu 48edcb1c0aSYuanjiang Yu #define SC27XX_FGU_INT_MASK GENMASK(9, 0) 49edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD_INT BIT(0) 50edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTA_INT BIT(2) 51195ca170SBaolin Wang 52195ca170SBaolin Wang #define SC27XX_FGU_CUR_BASIC_ADC 8192 53195ca170SBaolin Wang #define SC27XX_FGU_SAMPLE_HZ 2 54195ca170SBaolin Wang 55195ca170SBaolin Wang /* 56195ca170SBaolin Wang * struct sc27xx_fgu_data: describe the FGU device 57195ca170SBaolin Wang * @regmap: regmap for register access 58195ca170SBaolin Wang * @dev: platform device 59195ca170SBaolin Wang * @battery: battery power supply 60195ca170SBaolin Wang * @base: the base offset for the controller 61195ca170SBaolin Wang * @lock: protect the structure 62195ca170SBaolin Wang * @gpiod: GPIO for battery detection 63195ca170SBaolin Wang * @channel: IIO channel to get battery temperature 64195ca170SBaolin Wang * @internal_resist: the battery internal resistance in mOhm 65195ca170SBaolin Wang * @total_cap: the total capacity of the battery in mAh 66195ca170SBaolin Wang * @init_cap: the initial capacity of the battery in mAh 67edcb1c0aSYuanjiang Yu * @alarm_cap: the alarm capacity 68195ca170SBaolin Wang * @init_clbcnt: the initial coulomb counter 69195ca170SBaolin Wang * @max_volt: the maximum constant input voltage in millivolt 70edcb1c0aSYuanjiang Yu * @min_volt: the minimum drained battery voltage in microvolt 71195ca170SBaolin Wang * @table_len: the capacity table length 7265c9fab7SBaolin Wang * @cur_1000ma_adc: ADC value corresponding to 1000 mA 7365c9fab7SBaolin Wang * @vol_1000mv_adc: ADC value corresponding to 1000 mV 74195ca170SBaolin Wang * @cap_table: capacity table with corresponding ocv 75195ca170SBaolin Wang */ 76195ca170SBaolin Wang struct sc27xx_fgu_data { 77195ca170SBaolin Wang struct regmap *regmap; 78195ca170SBaolin Wang struct device *dev; 79195ca170SBaolin Wang struct power_supply *battery; 80195ca170SBaolin Wang u32 base; 81195ca170SBaolin Wang struct mutex lock; 82195ca170SBaolin Wang struct gpio_desc *gpiod; 83195ca170SBaolin Wang struct iio_channel *channel; 84195ca170SBaolin Wang bool bat_present; 85195ca170SBaolin Wang int internal_resist; 86195ca170SBaolin Wang int total_cap; 87195ca170SBaolin Wang int init_cap; 88edcb1c0aSYuanjiang Yu int alarm_cap; 89195ca170SBaolin Wang int init_clbcnt; 90195ca170SBaolin Wang int max_volt; 91edcb1c0aSYuanjiang Yu int min_volt; 92195ca170SBaolin Wang int table_len; 9365c9fab7SBaolin Wang int cur_1000ma_adc; 9465c9fab7SBaolin Wang int vol_1000mv_adc; 95195ca170SBaolin Wang struct power_supply_battery_ocv_table *cap_table; 96195ca170SBaolin Wang }; 97195ca170SBaolin Wang 98edcb1c0aSYuanjiang Yu static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity); 99edcb1c0aSYuanjiang Yu 100195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = { 101195ca170SBaolin Wang "sc2731_charger", 102195ca170SBaolin Wang "sc2720_charger", 103195ca170SBaolin Wang "sc2721_charger", 104195ca170SBaolin Wang "sc2723_charger", 105195ca170SBaolin Wang }; 106195ca170SBaolin Wang 10765c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc) 108195ca170SBaolin Wang { 10965c9fab7SBaolin Wang return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc); 110195ca170SBaolin Wang } 111195ca170SBaolin Wang 11265c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc) 113195ca170SBaolin Wang { 11465c9fab7SBaolin Wang return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); 115195ca170SBaolin Wang } 116195ca170SBaolin Wang 117edcb1c0aSYuanjiang Yu static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol) 118edcb1c0aSYuanjiang Yu { 119edcb1c0aSYuanjiang Yu return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000); 120edcb1c0aSYuanjiang Yu } 121edcb1c0aSYuanjiang Yu 122195ca170SBaolin Wang /* 123195ca170SBaolin Wang * When system boots on, we can not read battery capacity from coulomb 124195ca170SBaolin Wang * registers, since now the coulomb registers are invalid. So we should 125195ca170SBaolin Wang * calculate the battery open circuit voltage, and get current battery 126195ca170SBaolin Wang * capacity according to the capacity table. 127195ca170SBaolin Wang */ 128195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) 129195ca170SBaolin Wang { 130195ca170SBaolin Wang int volt, cur, oci, ocv, ret; 131195ca170SBaolin Wang 132195ca170SBaolin Wang /* 133195ca170SBaolin Wang * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved 134195ca170SBaolin Wang * the first sampled open circuit current. 135195ca170SBaolin Wang */ 136195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL, 137195ca170SBaolin Wang &cur); 138195ca170SBaolin Wang if (ret) 139195ca170SBaolin Wang return ret; 140195ca170SBaolin Wang 141195ca170SBaolin Wang cur <<= 1; 14265c9fab7SBaolin Wang oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 143195ca170SBaolin Wang 144195ca170SBaolin Wang /* 145195ca170SBaolin Wang * Should get the OCV from SC27XX_FGU_POCV register at the system 146195ca170SBaolin Wang * beginning. It is ADC values reading from registers which need to 147195ca170SBaolin Wang * convert the corresponding voltage. 148195ca170SBaolin Wang */ 149195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt); 150195ca170SBaolin Wang if (ret) 151195ca170SBaolin Wang return ret; 152195ca170SBaolin Wang 15365c9fab7SBaolin Wang volt = sc27xx_fgu_adc_to_voltage(data, volt); 154195ca170SBaolin Wang ocv = volt * 1000 - oci * data->internal_resist; 155195ca170SBaolin Wang 156195ca170SBaolin Wang /* 157195ca170SBaolin Wang * Parse the capacity table to look up the correct capacity percent 158195ca170SBaolin Wang * according to current battery's corresponding OCV values. 159195ca170SBaolin Wang */ 160195ca170SBaolin Wang *cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len, 161195ca170SBaolin Wang ocv); 162195ca170SBaolin Wang 163195ca170SBaolin Wang return 0; 164195ca170SBaolin Wang } 165195ca170SBaolin Wang 166195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt) 167195ca170SBaolin Wang { 168195ca170SBaolin Wang int ret; 169195ca170SBaolin Wang 170195ca170SBaolin Wang clbcnt *= SC27XX_FGU_SAMPLE_HZ; 171195ca170SBaolin Wang 172195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 173195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETL, 174195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, clbcnt); 175195ca170SBaolin Wang if (ret) 176195ca170SBaolin Wang return ret; 177195ca170SBaolin Wang 178195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 179195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETH, 180195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, 181195ca170SBaolin Wang clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 182195ca170SBaolin Wang if (ret) 183195ca170SBaolin Wang return ret; 184195ca170SBaolin Wang 185195ca170SBaolin Wang return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START, 186195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN, 187195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN); 188195ca170SBaolin Wang } 189195ca170SBaolin Wang 190195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt) 191195ca170SBaolin Wang { 192195ca170SBaolin Wang int ccl, cch, ret; 193195ca170SBaolin Wang 194195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL, 195195ca170SBaolin Wang &ccl); 196195ca170SBaolin Wang if (ret) 197195ca170SBaolin Wang return ret; 198195ca170SBaolin Wang 199195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH, 200195ca170SBaolin Wang &cch); 201195ca170SBaolin Wang if (ret) 202195ca170SBaolin Wang return ret; 203195ca170SBaolin Wang 204195ca170SBaolin Wang *clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK; 205195ca170SBaolin Wang *clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT; 206195ca170SBaolin Wang *clb_cnt /= SC27XX_FGU_SAMPLE_HZ; 207195ca170SBaolin Wang 208195ca170SBaolin Wang return 0; 209195ca170SBaolin Wang } 210195ca170SBaolin Wang 211195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap) 212195ca170SBaolin Wang { 213195ca170SBaolin Wang int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp; 214195ca170SBaolin Wang 215195ca170SBaolin Wang /* Get current coulomb counters firstly */ 216195ca170SBaolin Wang ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt); 217195ca170SBaolin Wang if (ret) 218195ca170SBaolin Wang return ret; 219195ca170SBaolin Wang 220195ca170SBaolin Wang delta_clbcnt = cur_clbcnt - data->init_clbcnt; 221195ca170SBaolin Wang 222195ca170SBaolin Wang /* 223195ca170SBaolin Wang * Convert coulomb counter to delta capacity (mAh), and set multiplier 224195ca170SBaolin Wang * as 100 to improve the precision. 225195ca170SBaolin Wang */ 226195ca170SBaolin Wang temp = DIV_ROUND_CLOSEST(delta_clbcnt, 360); 22765c9fab7SBaolin Wang temp = sc27xx_fgu_adc_to_current(data, temp); 228195ca170SBaolin Wang 229195ca170SBaolin Wang /* 230195ca170SBaolin Wang * Convert to capacity percent of the battery total capacity, 231195ca170SBaolin Wang * and multiplier is 100 too. 232195ca170SBaolin Wang */ 233195ca170SBaolin Wang delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap); 234195ca170SBaolin Wang *cap = delta_cap + data->init_cap; 235195ca170SBaolin Wang 236195ca170SBaolin Wang return 0; 237195ca170SBaolin Wang } 238195ca170SBaolin Wang 239195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val) 240195ca170SBaolin Wang { 241195ca170SBaolin Wang int ret, vol; 242195ca170SBaolin Wang 243195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol); 244195ca170SBaolin Wang if (ret) 245195ca170SBaolin Wang return ret; 246195ca170SBaolin Wang 247195ca170SBaolin Wang /* 248195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 249195ca170SBaolin Wang * corresponding voltage values. 250195ca170SBaolin Wang */ 25165c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_voltage(data, vol); 252195ca170SBaolin Wang 253195ca170SBaolin Wang return 0; 254195ca170SBaolin Wang } 255195ca170SBaolin Wang 256195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val) 257195ca170SBaolin Wang { 258195ca170SBaolin Wang int ret, cur; 259195ca170SBaolin Wang 260195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur); 261195ca170SBaolin Wang if (ret) 262195ca170SBaolin Wang return ret; 263195ca170SBaolin Wang 264195ca170SBaolin Wang /* 265195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 266195ca170SBaolin Wang * corresponding current values. 267195ca170SBaolin Wang */ 26865c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 269195ca170SBaolin Wang 270195ca170SBaolin Wang return 0; 271195ca170SBaolin Wang } 272195ca170SBaolin Wang 273195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) 274195ca170SBaolin Wang { 275195ca170SBaolin Wang int vol, cur, ret; 276195ca170SBaolin Wang 277195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 278195ca170SBaolin Wang if (ret) 279195ca170SBaolin Wang return ret; 280195ca170SBaolin Wang 281195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &cur); 282195ca170SBaolin Wang if (ret) 283195ca170SBaolin Wang return ret; 284195ca170SBaolin Wang 285195ca170SBaolin Wang /* Return the battery OCV in micro volts. */ 286195ca170SBaolin Wang *val = vol * 1000 - cur * data->internal_resist; 287195ca170SBaolin Wang 288195ca170SBaolin Wang return 0; 289195ca170SBaolin Wang } 290195ca170SBaolin Wang 291195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp) 292195ca170SBaolin Wang { 293195ca170SBaolin Wang return iio_read_channel_processed(data->channel, temp); 294195ca170SBaolin Wang } 295195ca170SBaolin Wang 296195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health) 297195ca170SBaolin Wang { 298195ca170SBaolin Wang int ret, vol; 299195ca170SBaolin Wang 300195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 301195ca170SBaolin Wang if (ret) 302195ca170SBaolin Wang return ret; 303195ca170SBaolin Wang 304195ca170SBaolin Wang if (vol > data->max_volt) 305195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 306195ca170SBaolin Wang else 307195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_GOOD; 308195ca170SBaolin Wang 309195ca170SBaolin Wang return 0; 310195ca170SBaolin Wang } 311195ca170SBaolin Wang 312195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status) 313195ca170SBaolin Wang { 314195ca170SBaolin Wang union power_supply_propval val; 315195ca170SBaolin Wang struct power_supply *psy; 316195ca170SBaolin Wang int i, ret = -EINVAL; 317195ca170SBaolin Wang 318195ca170SBaolin Wang for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) { 319195ca170SBaolin Wang psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]); 320195ca170SBaolin Wang if (!psy) 321195ca170SBaolin Wang continue; 322195ca170SBaolin Wang 323195ca170SBaolin Wang ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, 324195ca170SBaolin Wang &val); 325195ca170SBaolin Wang power_supply_put(psy); 326195ca170SBaolin Wang if (ret) 327195ca170SBaolin Wang return ret; 328195ca170SBaolin Wang 329195ca170SBaolin Wang *status = val.intval; 330195ca170SBaolin Wang } 331195ca170SBaolin Wang 332195ca170SBaolin Wang return ret; 333195ca170SBaolin Wang } 334195ca170SBaolin Wang 335195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy, 336195ca170SBaolin Wang enum power_supply_property psp, 337195ca170SBaolin Wang union power_supply_propval *val) 338195ca170SBaolin Wang { 339195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 340195ca170SBaolin Wang int ret = 0; 341195ca170SBaolin Wang int value; 342195ca170SBaolin Wang 343195ca170SBaolin Wang mutex_lock(&data->lock); 344195ca170SBaolin Wang 345195ca170SBaolin Wang switch (psp) { 346195ca170SBaolin Wang case POWER_SUPPLY_PROP_STATUS: 347195ca170SBaolin Wang ret = sc27xx_fgu_get_status(data, &value); 348195ca170SBaolin Wang if (ret) 349195ca170SBaolin Wang goto error; 350195ca170SBaolin Wang 351195ca170SBaolin Wang val->intval = value; 352195ca170SBaolin Wang break; 353195ca170SBaolin Wang 354195ca170SBaolin Wang case POWER_SUPPLY_PROP_HEALTH: 355195ca170SBaolin Wang ret = sc27xx_fgu_get_health(data, &value); 356195ca170SBaolin Wang if (ret) 357195ca170SBaolin Wang goto error; 358195ca170SBaolin Wang 359195ca170SBaolin Wang val->intval = value; 360195ca170SBaolin Wang break; 361195ca170SBaolin Wang 362195ca170SBaolin Wang case POWER_SUPPLY_PROP_PRESENT: 363195ca170SBaolin Wang val->intval = data->bat_present; 364195ca170SBaolin Wang break; 365195ca170SBaolin Wang 366195ca170SBaolin Wang case POWER_SUPPLY_PROP_TEMP: 367195ca170SBaolin Wang ret = sc27xx_fgu_get_temp(data, &value); 368195ca170SBaolin Wang if (ret) 369195ca170SBaolin Wang goto error; 370195ca170SBaolin Wang 371195ca170SBaolin Wang val->intval = value; 372195ca170SBaolin Wang break; 373195ca170SBaolin Wang 374195ca170SBaolin Wang case POWER_SUPPLY_PROP_TECHNOLOGY: 375195ca170SBaolin Wang val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 376195ca170SBaolin Wang break; 377195ca170SBaolin Wang 378195ca170SBaolin Wang case POWER_SUPPLY_PROP_CAPACITY: 379195ca170SBaolin Wang ret = sc27xx_fgu_get_capacity(data, &value); 380195ca170SBaolin Wang if (ret) 381195ca170SBaolin Wang goto error; 382195ca170SBaolin Wang 383195ca170SBaolin Wang val->intval = value; 384195ca170SBaolin Wang break; 385195ca170SBaolin Wang 386195ca170SBaolin Wang case POWER_SUPPLY_PROP_VOLTAGE_NOW: 387195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &value); 388195ca170SBaolin Wang if (ret) 389195ca170SBaolin Wang goto error; 390195ca170SBaolin Wang 391195ca170SBaolin Wang val->intval = value * 1000; 392195ca170SBaolin Wang break; 393195ca170SBaolin Wang 394195ca170SBaolin Wang case POWER_SUPPLY_PROP_VOLTAGE_OCV: 395195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_ocv(data, &value); 396195ca170SBaolin Wang if (ret) 397195ca170SBaolin Wang goto error; 398195ca170SBaolin Wang 399195ca170SBaolin Wang val->intval = value; 400195ca170SBaolin Wang break; 401195ca170SBaolin Wang 402195ca170SBaolin Wang case POWER_SUPPLY_PROP_CURRENT_NOW: 403195ca170SBaolin Wang case POWER_SUPPLY_PROP_CURRENT_AVG: 404195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &value); 405195ca170SBaolin Wang if (ret) 406195ca170SBaolin Wang goto error; 407195ca170SBaolin Wang 408195ca170SBaolin Wang val->intval = value * 1000; 409195ca170SBaolin Wang break; 410195ca170SBaolin Wang 411195ca170SBaolin Wang default: 412195ca170SBaolin Wang ret = -EINVAL; 413195ca170SBaolin Wang break; 414195ca170SBaolin Wang } 415195ca170SBaolin Wang 416195ca170SBaolin Wang error: 417195ca170SBaolin Wang mutex_unlock(&data->lock); 418195ca170SBaolin Wang return ret; 419195ca170SBaolin Wang } 420195ca170SBaolin Wang 421195ca170SBaolin Wang static void sc27xx_fgu_external_power_changed(struct power_supply *psy) 422195ca170SBaolin Wang { 423195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 424195ca170SBaolin Wang 425195ca170SBaolin Wang power_supply_changed(data->battery); 426195ca170SBaolin Wang } 427195ca170SBaolin Wang 428195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = { 429195ca170SBaolin Wang POWER_SUPPLY_PROP_STATUS, 430195ca170SBaolin Wang POWER_SUPPLY_PROP_HEALTH, 431195ca170SBaolin Wang POWER_SUPPLY_PROP_PRESENT, 432195ca170SBaolin Wang POWER_SUPPLY_PROP_TEMP, 433195ca170SBaolin Wang POWER_SUPPLY_PROP_TECHNOLOGY, 434195ca170SBaolin Wang POWER_SUPPLY_PROP_CAPACITY, 435195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_NOW, 436195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_OCV, 437195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_NOW, 438195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_AVG, 439195ca170SBaolin Wang }; 440195ca170SBaolin Wang 441195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = { 442195ca170SBaolin Wang .name = "sc27xx-fgu", 443195ca170SBaolin Wang .type = POWER_SUPPLY_TYPE_BATTERY, 444195ca170SBaolin Wang .properties = sc27xx_fgu_props, 445195ca170SBaolin Wang .num_properties = ARRAY_SIZE(sc27xx_fgu_props), 446195ca170SBaolin Wang .get_property = sc27xx_fgu_get_property, 447195ca170SBaolin Wang .external_power_changed = sc27xx_fgu_external_power_changed, 448195ca170SBaolin Wang }; 449195ca170SBaolin Wang 450edcb1c0aSYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap) 451edcb1c0aSYuanjiang Yu { 452edcb1c0aSYuanjiang Yu data->init_cap = cap; 453edcb1c0aSYuanjiang Yu data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); 454edcb1c0aSYuanjiang Yu } 455edcb1c0aSYuanjiang Yu 456edcb1c0aSYuanjiang Yu static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id) 457edcb1c0aSYuanjiang Yu { 458edcb1c0aSYuanjiang Yu struct sc27xx_fgu_data *data = dev_id; 459edcb1c0aSYuanjiang Yu int ret, cap, ocv, adc; 460edcb1c0aSYuanjiang Yu u32 status; 461edcb1c0aSYuanjiang Yu 462edcb1c0aSYuanjiang Yu mutex_lock(&data->lock); 463edcb1c0aSYuanjiang Yu 464edcb1c0aSYuanjiang Yu ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS, 465edcb1c0aSYuanjiang Yu &status); 466edcb1c0aSYuanjiang Yu if (ret) 467edcb1c0aSYuanjiang Yu goto out; 468edcb1c0aSYuanjiang Yu 469edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 470edcb1c0aSYuanjiang Yu status, status); 471edcb1c0aSYuanjiang Yu if (ret) 472edcb1c0aSYuanjiang Yu goto out; 473edcb1c0aSYuanjiang Yu 474edcb1c0aSYuanjiang Yu /* 475edcb1c0aSYuanjiang Yu * When low overload voltage interrupt happens, we should calibrate the 476edcb1c0aSYuanjiang Yu * battery capacity in lower voltage stage. 477edcb1c0aSYuanjiang Yu */ 478edcb1c0aSYuanjiang Yu if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT)) 479edcb1c0aSYuanjiang Yu goto out; 480edcb1c0aSYuanjiang Yu 481edcb1c0aSYuanjiang Yu ret = sc27xx_fgu_get_capacity(data, &cap); 482edcb1c0aSYuanjiang Yu if (ret) 483edcb1c0aSYuanjiang Yu goto out; 484edcb1c0aSYuanjiang Yu 485edcb1c0aSYuanjiang Yu ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 486edcb1c0aSYuanjiang Yu if (ret) 487edcb1c0aSYuanjiang Yu goto out; 488edcb1c0aSYuanjiang Yu 489edcb1c0aSYuanjiang Yu /* 490edcb1c0aSYuanjiang Yu * If current OCV value is less than the minimum OCV value in OCV table, 491edcb1c0aSYuanjiang Yu * which means now battery capacity is 0%, and we should adjust the 492edcb1c0aSYuanjiang Yu * inititial capacity to 0. 493edcb1c0aSYuanjiang Yu */ 494edcb1c0aSYuanjiang Yu if (ocv <= data->cap_table[data->table_len - 1].ocv) { 495edcb1c0aSYuanjiang Yu sc27xx_fgu_adjust_cap(data, 0); 496edcb1c0aSYuanjiang Yu } else if (ocv <= data->min_volt) { 497edcb1c0aSYuanjiang Yu /* 498edcb1c0aSYuanjiang Yu * If current OCV value is less than the low alarm voltage, but 499edcb1c0aSYuanjiang Yu * current capacity is larger than the alarm capacity, we should 500edcb1c0aSYuanjiang Yu * adjust the inititial capacity to alarm capacity. 501edcb1c0aSYuanjiang Yu */ 502edcb1c0aSYuanjiang Yu if (cap > data->alarm_cap) { 503edcb1c0aSYuanjiang Yu sc27xx_fgu_adjust_cap(data, data->alarm_cap); 504edcb1c0aSYuanjiang Yu } else if (cap <= 0) { 505edcb1c0aSYuanjiang Yu int cur_cap; 506edcb1c0aSYuanjiang Yu 507edcb1c0aSYuanjiang Yu /* 508edcb1c0aSYuanjiang Yu * If current capacity is equal with 0 or less than 0 509edcb1c0aSYuanjiang Yu * (some error occurs), we should adjust inititial 510edcb1c0aSYuanjiang Yu * capacity to the capacity corresponding to current OCV 511edcb1c0aSYuanjiang Yu * value. 512edcb1c0aSYuanjiang Yu */ 513edcb1c0aSYuanjiang Yu cur_cap = power_supply_ocv2cap_simple(data->cap_table, 514edcb1c0aSYuanjiang Yu data->table_len, 515edcb1c0aSYuanjiang Yu ocv); 516edcb1c0aSYuanjiang Yu sc27xx_fgu_adjust_cap(data, cur_cap); 517edcb1c0aSYuanjiang Yu } 518edcb1c0aSYuanjiang Yu 519edcb1c0aSYuanjiang Yu /* 520edcb1c0aSYuanjiang Yu * After adjusting the battery capacity, we should set the 521edcb1c0aSYuanjiang Yu * lowest alarm voltage instead. 522edcb1c0aSYuanjiang Yu */ 523edcb1c0aSYuanjiang Yu data->min_volt = data->cap_table[data->table_len - 1].ocv; 524edcb1c0aSYuanjiang Yu adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 525edcb1c0aSYuanjiang Yu regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, 526edcb1c0aSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_MASK, adc); 527edcb1c0aSYuanjiang Yu } 528edcb1c0aSYuanjiang Yu 529edcb1c0aSYuanjiang Yu out: 530edcb1c0aSYuanjiang Yu mutex_unlock(&data->lock); 531edcb1c0aSYuanjiang Yu 532edcb1c0aSYuanjiang Yu power_supply_changed(data->battery); 533edcb1c0aSYuanjiang Yu return IRQ_HANDLED; 534edcb1c0aSYuanjiang Yu } 535edcb1c0aSYuanjiang Yu 536195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id) 537195ca170SBaolin Wang { 538195ca170SBaolin Wang struct sc27xx_fgu_data *data = dev_id; 539195ca170SBaolin Wang int state; 540195ca170SBaolin Wang 541195ca170SBaolin Wang mutex_lock(&data->lock); 542195ca170SBaolin Wang 543195ca170SBaolin Wang state = gpiod_get_value_cansleep(data->gpiod); 544195ca170SBaolin Wang if (state < 0) { 545195ca170SBaolin Wang dev_err(data->dev, "failed to get gpio state\n"); 546195ca170SBaolin Wang mutex_unlock(&data->lock); 547195ca170SBaolin Wang return IRQ_RETVAL(state); 548195ca170SBaolin Wang } 549195ca170SBaolin Wang 550195ca170SBaolin Wang data->bat_present = !!state; 551195ca170SBaolin Wang 552195ca170SBaolin Wang mutex_unlock(&data->lock); 553195ca170SBaolin Wang 554195ca170SBaolin Wang power_supply_changed(data->battery); 555195ca170SBaolin Wang return IRQ_HANDLED; 556195ca170SBaolin Wang } 557195ca170SBaolin Wang 558195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data) 559195ca170SBaolin Wang { 560195ca170SBaolin Wang struct sc27xx_fgu_data *data = _data; 561195ca170SBaolin Wang 562195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 563195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 564195ca170SBaolin Wang } 565195ca170SBaolin Wang 566195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity) 567195ca170SBaolin Wang { 568195ca170SBaolin Wang /* 569195ca170SBaolin Wang * Get current capacity (mAh) = battery total capacity (mAh) * 570195ca170SBaolin Wang * current capacity percent (capacity / 100). 571195ca170SBaolin Wang */ 572195ca170SBaolin Wang int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100); 573195ca170SBaolin Wang 574195ca170SBaolin Wang /* 575195ca170SBaolin Wang * Convert current capacity (mAh) to coulomb counter according to the 576195ca170SBaolin Wang * formula: 1 mAh =3.6 coulomb. 577195ca170SBaolin Wang */ 578195ca170SBaolin Wang return DIV_ROUND_CLOSEST(cur_cap * 36, 10); 579195ca170SBaolin Wang } 580195ca170SBaolin Wang 58165c9fab7SBaolin Wang static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) 58265c9fab7SBaolin Wang { 58365c9fab7SBaolin Wang struct nvmem_cell *cell; 58465c9fab7SBaolin Wang int calib_data, cal_4200mv; 58565c9fab7SBaolin Wang void *buf; 58665c9fab7SBaolin Wang size_t len; 58765c9fab7SBaolin Wang 58865c9fab7SBaolin Wang cell = nvmem_cell_get(data->dev, "fgu_calib"); 58965c9fab7SBaolin Wang if (IS_ERR(cell)) 59065c9fab7SBaolin Wang return PTR_ERR(cell); 59165c9fab7SBaolin Wang 59265c9fab7SBaolin Wang buf = nvmem_cell_read(cell, &len); 59365c9fab7SBaolin Wang nvmem_cell_put(cell); 59465c9fab7SBaolin Wang 59565c9fab7SBaolin Wang if (IS_ERR(buf)) 59665c9fab7SBaolin Wang return PTR_ERR(buf); 59765c9fab7SBaolin Wang 59865c9fab7SBaolin Wang memcpy(&calib_data, buf, min(len, sizeof(u32))); 59965c9fab7SBaolin Wang 60065c9fab7SBaolin Wang /* 60165c9fab7SBaolin Wang * Get the ADC value corresponding to 4200 mV from eFuse controller 60265c9fab7SBaolin Wang * according to below formula. Then convert to ADC values corresponding 60365c9fab7SBaolin Wang * to 1000 mV and 1000 mA. 60465c9fab7SBaolin Wang */ 60565c9fab7SBaolin Wang cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256; 60665c9fab7SBaolin Wang data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42); 60765c9fab7SBaolin Wang data->cur_1000ma_adc = data->vol_1000mv_adc * 4; 60865c9fab7SBaolin Wang 60965c9fab7SBaolin Wang kfree(buf); 61065c9fab7SBaolin Wang return 0; 61165c9fab7SBaolin Wang } 61265c9fab7SBaolin Wang 613195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) 614195ca170SBaolin Wang { 615195ca170SBaolin Wang struct power_supply_battery_info info = { }; 616195ca170SBaolin Wang struct power_supply_battery_ocv_table *table; 617edcb1c0aSYuanjiang Yu int ret, delta_clbcnt, alarm_adc; 618195ca170SBaolin Wang 619195ca170SBaolin Wang ret = power_supply_get_battery_info(data->battery, &info); 620195ca170SBaolin Wang if (ret) { 621195ca170SBaolin Wang dev_err(data->dev, "failed to get battery information\n"); 622195ca170SBaolin Wang return ret; 623195ca170SBaolin Wang } 624195ca170SBaolin Wang 625195ca170SBaolin Wang data->total_cap = info.charge_full_design_uah / 1000; 626195ca170SBaolin Wang data->max_volt = info.constant_charge_voltage_max_uv / 1000; 627195ca170SBaolin Wang data->internal_resist = info.factory_internal_resistance_uohm / 1000; 628edcb1c0aSYuanjiang Yu data->min_volt = info.voltage_min_design_uv; 629195ca170SBaolin Wang 630195ca170SBaolin Wang /* 631195ca170SBaolin Wang * For SC27XX fuel gauge device, we only use one ocv-capacity 632195ca170SBaolin Wang * table in normal temperature 20 Celsius. 633195ca170SBaolin Wang */ 634195ca170SBaolin Wang table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len); 635195ca170SBaolin Wang if (!table) 636195ca170SBaolin Wang return -EINVAL; 637195ca170SBaolin Wang 638195ca170SBaolin Wang data->cap_table = devm_kmemdup(data->dev, table, 639195ca170SBaolin Wang data->table_len * sizeof(*table), 640195ca170SBaolin Wang GFP_KERNEL); 641195ca170SBaolin Wang if (!data->cap_table) { 642195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 643195ca170SBaolin Wang return -ENOMEM; 644195ca170SBaolin Wang } 645195ca170SBaolin Wang 646edcb1c0aSYuanjiang Yu data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, 647edcb1c0aSYuanjiang Yu data->table_len, 648edcb1c0aSYuanjiang Yu data->min_volt); 649edcb1c0aSYuanjiang Yu 650195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 651195ca170SBaolin Wang 65265c9fab7SBaolin Wang ret = sc27xx_fgu_calibration(data); 65365c9fab7SBaolin Wang if (ret) 65465c9fab7SBaolin Wang return ret; 65565c9fab7SBaolin Wang 656195ca170SBaolin Wang /* Enable the FGU module */ 657195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, 658195ca170SBaolin Wang SC27XX_FGU_EN, SC27XX_FGU_EN); 659195ca170SBaolin Wang if (ret) { 660195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu\n"); 661195ca170SBaolin Wang return ret; 662195ca170SBaolin Wang } 663195ca170SBaolin Wang 664195ca170SBaolin Wang /* Enable the FGU RTC clock to make it work */ 665195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0, 666195ca170SBaolin Wang SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN); 667195ca170SBaolin Wang if (ret) { 668195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu RTC clock\n"); 669195ca170SBaolin Wang goto disable_fgu; 670195ca170SBaolin Wang } 671195ca170SBaolin Wang 672edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 673edcb1c0aSYuanjiang Yu SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK); 674edcb1c0aSYuanjiang Yu if (ret) { 675edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to clear interrupt status\n"); 676edcb1c0aSYuanjiang Yu goto disable_clk; 677edcb1c0aSYuanjiang Yu } 678edcb1c0aSYuanjiang Yu 679edcb1c0aSYuanjiang Yu /* 680edcb1c0aSYuanjiang Yu * Set the voltage low overload threshold, which means when the battery 681edcb1c0aSYuanjiang Yu * voltage is lower than this threshold, the controller will generate 682edcb1c0aSYuanjiang Yu * one interrupt to notify. 683edcb1c0aSYuanjiang Yu */ 684edcb1c0aSYuanjiang Yu alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 685edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, 686edcb1c0aSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc); 687edcb1c0aSYuanjiang Yu if (ret) { 688edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set fgu low overload\n"); 689edcb1c0aSYuanjiang Yu goto disable_clk; 690edcb1c0aSYuanjiang Yu } 691edcb1c0aSYuanjiang Yu 692edcb1c0aSYuanjiang Yu /* 693edcb1c0aSYuanjiang Yu * Set the coulomb counter delta threshold, that means when the coulomb 694edcb1c0aSYuanjiang Yu * counter change is multiples of the delta threshold, the controller 695edcb1c0aSYuanjiang Yu * will generate one interrupt to notify the users to update the battery 696edcb1c0aSYuanjiang Yu * capacity. Now we set the delta threshold as a counter value of 1% 697edcb1c0aSYuanjiang Yu * capacity. 698edcb1c0aSYuanjiang Yu */ 699edcb1c0aSYuanjiang Yu delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1); 700edcb1c0aSYuanjiang Yu 701edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL, 702edcb1c0aSYuanjiang Yu SC27XX_FGU_CLBCNT_MASK, delta_clbcnt); 703edcb1c0aSYuanjiang Yu if (ret) { 704edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set low delta coulomb counter\n"); 705edcb1c0aSYuanjiang Yu goto disable_clk; 706edcb1c0aSYuanjiang Yu } 707edcb1c0aSYuanjiang Yu 708edcb1c0aSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH, 709edcb1c0aSYuanjiang Yu SC27XX_FGU_CLBCNT_MASK, 710edcb1c0aSYuanjiang Yu delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 711edcb1c0aSYuanjiang Yu if (ret) { 712edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to set high delta coulomb counter\n"); 713edcb1c0aSYuanjiang Yu goto disable_clk; 714edcb1c0aSYuanjiang Yu } 715edcb1c0aSYuanjiang Yu 716195ca170SBaolin Wang /* 717195ca170SBaolin Wang * Get the boot battery capacity when system powers on, which is used to 718195ca170SBaolin Wang * initialize the coulomb counter. After that, we can read the coulomb 719195ca170SBaolin Wang * counter to measure the battery capacity. 720195ca170SBaolin Wang */ 721195ca170SBaolin Wang ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap); 722195ca170SBaolin Wang if (ret) { 723195ca170SBaolin Wang dev_err(data->dev, "failed to get boot capacity\n"); 724195ca170SBaolin Wang goto disable_clk; 725195ca170SBaolin Wang } 726195ca170SBaolin Wang 727195ca170SBaolin Wang /* 728195ca170SBaolin Wang * Convert battery capacity to the corresponding initial coulomb counter 729195ca170SBaolin Wang * and set into coulomb counter registers. 730195ca170SBaolin Wang */ 731195ca170SBaolin Wang data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); 732195ca170SBaolin Wang ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt); 733195ca170SBaolin Wang if (ret) { 734195ca170SBaolin Wang dev_err(data->dev, "failed to initialize coulomb counter\n"); 735195ca170SBaolin Wang goto disable_clk; 736195ca170SBaolin Wang } 737195ca170SBaolin Wang 738195ca170SBaolin Wang return 0; 739195ca170SBaolin Wang 740195ca170SBaolin Wang disable_clk: 741195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 742195ca170SBaolin Wang disable_fgu: 743195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 744195ca170SBaolin Wang 745195ca170SBaolin Wang return ret; 746195ca170SBaolin Wang } 747195ca170SBaolin Wang 748195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev) 749195ca170SBaolin Wang { 750195ca170SBaolin Wang struct device_node *np = pdev->dev.of_node; 751195ca170SBaolin Wang struct power_supply_config fgu_cfg = { }; 752195ca170SBaolin Wang struct sc27xx_fgu_data *data; 753195ca170SBaolin Wang int ret, irq; 754195ca170SBaolin Wang 755195ca170SBaolin Wang data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 756195ca170SBaolin Wang if (!data) 757195ca170SBaolin Wang return -ENOMEM; 758195ca170SBaolin Wang 759195ca170SBaolin Wang data->regmap = dev_get_regmap(pdev->dev.parent, NULL); 760195ca170SBaolin Wang if (!data->regmap) { 761195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get regmap\n"); 762195ca170SBaolin Wang return -ENODEV; 763195ca170SBaolin Wang } 764195ca170SBaolin Wang 765195ca170SBaolin Wang ret = device_property_read_u32(&pdev->dev, "reg", &data->base); 766195ca170SBaolin Wang if (ret) { 767195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get fgu address\n"); 768195ca170SBaolin Wang return ret; 769195ca170SBaolin Wang } 770195ca170SBaolin Wang 771195ca170SBaolin Wang data->channel = devm_iio_channel_get(&pdev->dev, "bat-temp"); 772195ca170SBaolin Wang if (IS_ERR(data->channel)) { 773195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get IIO channel\n"); 774195ca170SBaolin Wang return PTR_ERR(data->channel); 775195ca170SBaolin Wang } 776195ca170SBaolin Wang 777195ca170SBaolin Wang data->gpiod = devm_gpiod_get(&pdev->dev, "bat-detect", GPIOD_IN); 778195ca170SBaolin Wang if (IS_ERR(data->gpiod)) { 779195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get battery detection GPIO\n"); 780195ca170SBaolin Wang return PTR_ERR(data->gpiod); 781195ca170SBaolin Wang } 782195ca170SBaolin Wang 783195ca170SBaolin Wang ret = gpiod_get_value_cansleep(data->gpiod); 784195ca170SBaolin Wang if (ret < 0) { 785195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get gpio state\n"); 786195ca170SBaolin Wang return ret; 787195ca170SBaolin Wang } 788195ca170SBaolin Wang 789195ca170SBaolin Wang data->bat_present = !!ret; 790195ca170SBaolin Wang mutex_init(&data->lock); 791195ca170SBaolin Wang data->dev = &pdev->dev; 792*e2fb615bSYuanjiang Yu platform_set_drvdata(pdev, data); 793195ca170SBaolin Wang 794195ca170SBaolin Wang fgu_cfg.drv_data = data; 795195ca170SBaolin Wang fgu_cfg.of_node = np; 796195ca170SBaolin Wang data->battery = devm_power_supply_register(&pdev->dev, &sc27xx_fgu_desc, 797195ca170SBaolin Wang &fgu_cfg); 798195ca170SBaolin Wang if (IS_ERR(data->battery)) { 799195ca170SBaolin Wang dev_err(&pdev->dev, "failed to register power supply\n"); 800195ca170SBaolin Wang return PTR_ERR(data->battery); 801195ca170SBaolin Wang } 802195ca170SBaolin Wang 803195ca170SBaolin Wang ret = sc27xx_fgu_hw_init(data); 804195ca170SBaolin Wang if (ret) { 805195ca170SBaolin Wang dev_err(&pdev->dev, "failed to initialize fgu hardware\n"); 806195ca170SBaolin Wang return ret; 807195ca170SBaolin Wang } 808195ca170SBaolin Wang 809195ca170SBaolin Wang ret = devm_add_action(&pdev->dev, sc27xx_fgu_disable, data); 810195ca170SBaolin Wang if (ret) { 811195ca170SBaolin Wang sc27xx_fgu_disable(data); 812195ca170SBaolin Wang dev_err(&pdev->dev, "failed to add fgu disable action\n"); 813195ca170SBaolin Wang return ret; 814195ca170SBaolin Wang } 815195ca170SBaolin Wang 816edcb1c0aSYuanjiang Yu irq = platform_get_irq(pdev, 0); 817edcb1c0aSYuanjiang Yu if (irq < 0) { 818edcb1c0aSYuanjiang Yu dev_err(&pdev->dev, "no irq resource specified\n"); 819edcb1c0aSYuanjiang Yu return irq; 820edcb1c0aSYuanjiang Yu } 821edcb1c0aSYuanjiang Yu 822edcb1c0aSYuanjiang Yu ret = devm_request_threaded_irq(data->dev, irq, NULL, 823edcb1c0aSYuanjiang Yu sc27xx_fgu_interrupt, 824edcb1c0aSYuanjiang Yu IRQF_NO_SUSPEND | IRQF_ONESHOT, 825edcb1c0aSYuanjiang Yu pdev->name, data); 826edcb1c0aSYuanjiang Yu if (ret) { 827edcb1c0aSYuanjiang Yu dev_err(data->dev, "failed to request fgu IRQ\n"); 828edcb1c0aSYuanjiang Yu return ret; 829edcb1c0aSYuanjiang Yu } 830edcb1c0aSYuanjiang Yu 831195ca170SBaolin Wang irq = gpiod_to_irq(data->gpiod); 832195ca170SBaolin Wang if (irq < 0) { 833195ca170SBaolin Wang dev_err(&pdev->dev, "failed to translate GPIO to IRQ\n"); 834195ca170SBaolin Wang return irq; 835195ca170SBaolin Wang } 836195ca170SBaolin Wang 837195ca170SBaolin Wang ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 838195ca170SBaolin Wang sc27xx_fgu_bat_detection, 839195ca170SBaolin Wang IRQF_ONESHOT | IRQF_TRIGGER_RISING | 840195ca170SBaolin Wang IRQF_TRIGGER_FALLING, 841195ca170SBaolin Wang pdev->name, data); 842195ca170SBaolin Wang if (ret) { 843195ca170SBaolin Wang dev_err(&pdev->dev, "failed to request IRQ\n"); 844195ca170SBaolin Wang return ret; 845195ca170SBaolin Wang } 846195ca170SBaolin Wang 847195ca170SBaolin Wang return 0; 848195ca170SBaolin Wang } 849195ca170SBaolin Wang 850*e2fb615bSYuanjiang Yu #ifdef CONFIG_PM_SLEEP 851*e2fb615bSYuanjiang Yu static int sc27xx_fgu_resume(struct device *dev) 852*e2fb615bSYuanjiang Yu { 853*e2fb615bSYuanjiang Yu struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 854*e2fb615bSYuanjiang Yu int ret; 855*e2fb615bSYuanjiang Yu 856*e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 857*e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT | 858*e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT, 0); 859*e2fb615bSYuanjiang Yu if (ret) { 860*e2fb615bSYuanjiang Yu dev_err(data->dev, "failed to disable fgu interrupts\n"); 861*e2fb615bSYuanjiang Yu return ret; 862*e2fb615bSYuanjiang Yu } 863*e2fb615bSYuanjiang Yu 864*e2fb615bSYuanjiang Yu return 0; 865*e2fb615bSYuanjiang Yu } 866*e2fb615bSYuanjiang Yu 867*e2fb615bSYuanjiang Yu static int sc27xx_fgu_suspend(struct device *dev) 868*e2fb615bSYuanjiang Yu { 869*e2fb615bSYuanjiang Yu struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 870*e2fb615bSYuanjiang Yu int ret, status, ocv; 871*e2fb615bSYuanjiang Yu 872*e2fb615bSYuanjiang Yu ret = sc27xx_fgu_get_status(data, &status); 873*e2fb615bSYuanjiang Yu if (ret) 874*e2fb615bSYuanjiang Yu return ret; 875*e2fb615bSYuanjiang Yu 876*e2fb615bSYuanjiang Yu /* 877*e2fb615bSYuanjiang Yu * If we are charging, then no need to enable the FGU interrupts to 878*e2fb615bSYuanjiang Yu * adjust the battery capacity. 879*e2fb615bSYuanjiang Yu */ 880*e2fb615bSYuanjiang Yu if (status != POWER_SUPPLY_STATUS_NOT_CHARGING) 881*e2fb615bSYuanjiang Yu return 0; 882*e2fb615bSYuanjiang Yu 883*e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 884*e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT, 885*e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT); 886*e2fb615bSYuanjiang Yu if (ret) { 887*e2fb615bSYuanjiang Yu dev_err(data->dev, "failed to enable low voltage interrupt\n"); 888*e2fb615bSYuanjiang Yu return ret; 889*e2fb615bSYuanjiang Yu } 890*e2fb615bSYuanjiang Yu 891*e2fb615bSYuanjiang Yu ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 892*e2fb615bSYuanjiang Yu if (ret) 893*e2fb615bSYuanjiang Yu goto disable_int; 894*e2fb615bSYuanjiang Yu 895*e2fb615bSYuanjiang Yu /* 896*e2fb615bSYuanjiang Yu * If current OCV is less than the minimum voltage, we should enable the 897*e2fb615bSYuanjiang Yu * coulomb counter threshold interrupt to notify events to adjust the 898*e2fb615bSYuanjiang Yu * battery capacity. 899*e2fb615bSYuanjiang Yu */ 900*e2fb615bSYuanjiang Yu if (ocv < data->min_volt) { 901*e2fb615bSYuanjiang Yu ret = regmap_update_bits(data->regmap, 902*e2fb615bSYuanjiang Yu data->base + SC27XX_FGU_INT_EN, 903*e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT, 904*e2fb615bSYuanjiang Yu SC27XX_FGU_CLBCNT_DELTA_INT); 905*e2fb615bSYuanjiang Yu if (ret) { 906*e2fb615bSYuanjiang Yu dev_err(data->dev, 907*e2fb615bSYuanjiang Yu "failed to enable coulomb threshold int\n"); 908*e2fb615bSYuanjiang Yu goto disable_int; 909*e2fb615bSYuanjiang Yu } 910*e2fb615bSYuanjiang Yu } 911*e2fb615bSYuanjiang Yu 912*e2fb615bSYuanjiang Yu return 0; 913*e2fb615bSYuanjiang Yu 914*e2fb615bSYuanjiang Yu disable_int: 915*e2fb615bSYuanjiang Yu regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 916*e2fb615bSYuanjiang Yu SC27XX_FGU_LOW_OVERLOAD_INT, 0); 917*e2fb615bSYuanjiang Yu return ret; 918*e2fb615bSYuanjiang Yu } 919*e2fb615bSYuanjiang Yu #endif 920*e2fb615bSYuanjiang Yu 921*e2fb615bSYuanjiang Yu static const struct dev_pm_ops sc27xx_fgu_pm_ops = { 922*e2fb615bSYuanjiang Yu SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume) 923*e2fb615bSYuanjiang Yu }; 924*e2fb615bSYuanjiang Yu 925195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = { 926195ca170SBaolin Wang { .compatible = "sprd,sc2731-fgu", }, 927195ca170SBaolin Wang { } 928195ca170SBaolin Wang }; 929195ca170SBaolin Wang 930195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = { 931195ca170SBaolin Wang .probe = sc27xx_fgu_probe, 932195ca170SBaolin Wang .driver = { 933195ca170SBaolin Wang .name = "sc27xx-fgu", 934195ca170SBaolin Wang .of_match_table = sc27xx_fgu_of_match, 935*e2fb615bSYuanjiang Yu .pm = &sc27xx_fgu_pm_ops, 936195ca170SBaolin Wang } 937195ca170SBaolin Wang }; 938195ca170SBaolin Wang 939195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver); 940195ca170SBaolin Wang 941195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver"); 942195ca170SBaolin Wang MODULE_LICENSE("GPL v2"); 943