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> 9*65c9fab7SBaolin 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> 14*65c9fab7SBaolin 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 34195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETH 0x50 35195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETL 0x54 36195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALH 0x68 37195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALL 0x6c 38195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_QMAXL 0x74 39195ca170SBaolin Wang 40195ca170SBaolin Wang #define SC27XX_WRITE_SELCLB_EN BIT(0) 41195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0) 42195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SHIFT 16 43195ca170SBaolin Wang 44195ca170SBaolin Wang #define SC27XX_FGU_CUR_BASIC_ADC 8192 45195ca170SBaolin Wang #define SC27XX_FGU_SAMPLE_HZ 2 46195ca170SBaolin Wang 47195ca170SBaolin Wang /* 48195ca170SBaolin Wang * struct sc27xx_fgu_data: describe the FGU device 49195ca170SBaolin Wang * @regmap: regmap for register access 50195ca170SBaolin Wang * @dev: platform device 51195ca170SBaolin Wang * @battery: battery power supply 52195ca170SBaolin Wang * @base: the base offset for the controller 53195ca170SBaolin Wang * @lock: protect the structure 54195ca170SBaolin Wang * @gpiod: GPIO for battery detection 55195ca170SBaolin Wang * @channel: IIO channel to get battery temperature 56195ca170SBaolin Wang * @internal_resist: the battery internal resistance in mOhm 57195ca170SBaolin Wang * @total_cap: the total capacity of the battery in mAh 58195ca170SBaolin Wang * @init_cap: the initial capacity of the battery in mAh 59195ca170SBaolin Wang * @init_clbcnt: the initial coulomb counter 60195ca170SBaolin Wang * @max_volt: the maximum constant input voltage in millivolt 61195ca170SBaolin Wang * @table_len: the capacity table length 62*65c9fab7SBaolin Wang * @cur_1000ma_adc: ADC value corresponding to 1000 mA 63*65c9fab7SBaolin Wang * @vol_1000mv_adc: ADC value corresponding to 1000 mV 64195ca170SBaolin Wang * @cap_table: capacity table with corresponding ocv 65195ca170SBaolin Wang */ 66195ca170SBaolin Wang struct sc27xx_fgu_data { 67195ca170SBaolin Wang struct regmap *regmap; 68195ca170SBaolin Wang struct device *dev; 69195ca170SBaolin Wang struct power_supply *battery; 70195ca170SBaolin Wang u32 base; 71195ca170SBaolin Wang struct mutex lock; 72195ca170SBaolin Wang struct gpio_desc *gpiod; 73195ca170SBaolin Wang struct iio_channel *channel; 74195ca170SBaolin Wang bool bat_present; 75195ca170SBaolin Wang int internal_resist; 76195ca170SBaolin Wang int total_cap; 77195ca170SBaolin Wang int init_cap; 78195ca170SBaolin Wang int init_clbcnt; 79195ca170SBaolin Wang int max_volt; 80195ca170SBaolin Wang int table_len; 81*65c9fab7SBaolin Wang int cur_1000ma_adc; 82*65c9fab7SBaolin Wang int vol_1000mv_adc; 83195ca170SBaolin Wang struct power_supply_battery_ocv_table *cap_table; 84195ca170SBaolin Wang }; 85195ca170SBaolin Wang 86195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = { 87195ca170SBaolin Wang "sc2731_charger", 88195ca170SBaolin Wang "sc2720_charger", 89195ca170SBaolin Wang "sc2721_charger", 90195ca170SBaolin Wang "sc2723_charger", 91195ca170SBaolin Wang }; 92195ca170SBaolin Wang 93*65c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc) 94195ca170SBaolin Wang { 95*65c9fab7SBaolin Wang return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc); 96195ca170SBaolin Wang } 97195ca170SBaolin Wang 98*65c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc) 99195ca170SBaolin Wang { 100*65c9fab7SBaolin Wang return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); 101195ca170SBaolin Wang } 102195ca170SBaolin Wang 103195ca170SBaolin Wang /* 104195ca170SBaolin Wang * When system boots on, we can not read battery capacity from coulomb 105195ca170SBaolin Wang * registers, since now the coulomb registers are invalid. So we should 106195ca170SBaolin Wang * calculate the battery open circuit voltage, and get current battery 107195ca170SBaolin Wang * capacity according to the capacity table. 108195ca170SBaolin Wang */ 109195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) 110195ca170SBaolin Wang { 111195ca170SBaolin Wang int volt, cur, oci, ocv, ret; 112195ca170SBaolin Wang 113195ca170SBaolin Wang /* 114195ca170SBaolin Wang * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved 115195ca170SBaolin Wang * the first sampled open circuit current. 116195ca170SBaolin Wang */ 117195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL, 118195ca170SBaolin Wang &cur); 119195ca170SBaolin Wang if (ret) 120195ca170SBaolin Wang return ret; 121195ca170SBaolin Wang 122195ca170SBaolin Wang cur <<= 1; 123*65c9fab7SBaolin Wang oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 124195ca170SBaolin Wang 125195ca170SBaolin Wang /* 126195ca170SBaolin Wang * Should get the OCV from SC27XX_FGU_POCV register at the system 127195ca170SBaolin Wang * beginning. It is ADC values reading from registers which need to 128195ca170SBaolin Wang * convert the corresponding voltage. 129195ca170SBaolin Wang */ 130195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt); 131195ca170SBaolin Wang if (ret) 132195ca170SBaolin Wang return ret; 133195ca170SBaolin Wang 134*65c9fab7SBaolin Wang volt = sc27xx_fgu_adc_to_voltage(data, volt); 135195ca170SBaolin Wang ocv = volt * 1000 - oci * data->internal_resist; 136195ca170SBaolin Wang 137195ca170SBaolin Wang /* 138195ca170SBaolin Wang * Parse the capacity table to look up the correct capacity percent 139195ca170SBaolin Wang * according to current battery's corresponding OCV values. 140195ca170SBaolin Wang */ 141195ca170SBaolin Wang *cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len, 142195ca170SBaolin Wang ocv); 143195ca170SBaolin Wang 144195ca170SBaolin Wang return 0; 145195ca170SBaolin Wang } 146195ca170SBaolin Wang 147195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt) 148195ca170SBaolin Wang { 149195ca170SBaolin Wang int ret; 150195ca170SBaolin Wang 151195ca170SBaolin Wang clbcnt *= SC27XX_FGU_SAMPLE_HZ; 152195ca170SBaolin Wang 153195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 154195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETL, 155195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, clbcnt); 156195ca170SBaolin Wang if (ret) 157195ca170SBaolin Wang return ret; 158195ca170SBaolin Wang 159195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, 160195ca170SBaolin Wang data->base + SC27XX_FGU_CLBCNT_SETH, 161195ca170SBaolin Wang SC27XX_FGU_CLBCNT_MASK, 162195ca170SBaolin Wang clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 163195ca170SBaolin Wang if (ret) 164195ca170SBaolin Wang return ret; 165195ca170SBaolin Wang 166195ca170SBaolin Wang return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START, 167195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN, 168195ca170SBaolin Wang SC27XX_WRITE_SELCLB_EN); 169195ca170SBaolin Wang } 170195ca170SBaolin Wang 171195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt) 172195ca170SBaolin Wang { 173195ca170SBaolin Wang int ccl, cch, ret; 174195ca170SBaolin Wang 175195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL, 176195ca170SBaolin Wang &ccl); 177195ca170SBaolin Wang if (ret) 178195ca170SBaolin Wang return ret; 179195ca170SBaolin Wang 180195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH, 181195ca170SBaolin Wang &cch); 182195ca170SBaolin Wang if (ret) 183195ca170SBaolin Wang return ret; 184195ca170SBaolin Wang 185195ca170SBaolin Wang *clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK; 186195ca170SBaolin Wang *clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT; 187195ca170SBaolin Wang *clb_cnt /= SC27XX_FGU_SAMPLE_HZ; 188195ca170SBaolin Wang 189195ca170SBaolin Wang return 0; 190195ca170SBaolin Wang } 191195ca170SBaolin Wang 192195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap) 193195ca170SBaolin Wang { 194195ca170SBaolin Wang int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp; 195195ca170SBaolin Wang 196195ca170SBaolin Wang /* Get current coulomb counters firstly */ 197195ca170SBaolin Wang ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt); 198195ca170SBaolin Wang if (ret) 199195ca170SBaolin Wang return ret; 200195ca170SBaolin Wang 201195ca170SBaolin Wang delta_clbcnt = cur_clbcnt - data->init_clbcnt; 202195ca170SBaolin Wang 203195ca170SBaolin Wang /* 204195ca170SBaolin Wang * Convert coulomb counter to delta capacity (mAh), and set multiplier 205195ca170SBaolin Wang * as 100 to improve the precision. 206195ca170SBaolin Wang */ 207195ca170SBaolin Wang temp = DIV_ROUND_CLOSEST(delta_clbcnt, 360); 208*65c9fab7SBaolin Wang temp = sc27xx_fgu_adc_to_current(data, temp); 209195ca170SBaolin Wang 210195ca170SBaolin Wang /* 211195ca170SBaolin Wang * Convert to capacity percent of the battery total capacity, 212195ca170SBaolin Wang * and multiplier is 100 too. 213195ca170SBaolin Wang */ 214195ca170SBaolin Wang delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap); 215195ca170SBaolin Wang *cap = delta_cap + data->init_cap; 216195ca170SBaolin Wang 217195ca170SBaolin Wang return 0; 218195ca170SBaolin Wang } 219195ca170SBaolin Wang 220195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val) 221195ca170SBaolin Wang { 222195ca170SBaolin Wang int ret, vol; 223195ca170SBaolin Wang 224195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol); 225195ca170SBaolin Wang if (ret) 226195ca170SBaolin Wang return ret; 227195ca170SBaolin Wang 228195ca170SBaolin Wang /* 229195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 230195ca170SBaolin Wang * corresponding voltage values. 231195ca170SBaolin Wang */ 232*65c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_voltage(data, vol); 233195ca170SBaolin Wang 234195ca170SBaolin Wang return 0; 235195ca170SBaolin Wang } 236195ca170SBaolin Wang 237195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val) 238195ca170SBaolin Wang { 239195ca170SBaolin Wang int ret, cur; 240195ca170SBaolin Wang 241195ca170SBaolin Wang ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur); 242195ca170SBaolin Wang if (ret) 243195ca170SBaolin Wang return ret; 244195ca170SBaolin Wang 245195ca170SBaolin Wang /* 246195ca170SBaolin Wang * It is ADC values reading from registers which need to convert to 247195ca170SBaolin Wang * corresponding current values. 248195ca170SBaolin Wang */ 249*65c9fab7SBaolin Wang *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 250195ca170SBaolin Wang 251195ca170SBaolin Wang return 0; 252195ca170SBaolin Wang } 253195ca170SBaolin Wang 254195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) 255195ca170SBaolin Wang { 256195ca170SBaolin Wang int vol, cur, ret; 257195ca170SBaolin Wang 258195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 259195ca170SBaolin Wang if (ret) 260195ca170SBaolin Wang return ret; 261195ca170SBaolin Wang 262195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &cur); 263195ca170SBaolin Wang if (ret) 264195ca170SBaolin Wang return ret; 265195ca170SBaolin Wang 266195ca170SBaolin Wang /* Return the battery OCV in micro volts. */ 267195ca170SBaolin Wang *val = vol * 1000 - cur * data->internal_resist; 268195ca170SBaolin Wang 269195ca170SBaolin Wang return 0; 270195ca170SBaolin Wang } 271195ca170SBaolin Wang 272195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp) 273195ca170SBaolin Wang { 274195ca170SBaolin Wang return iio_read_channel_processed(data->channel, temp); 275195ca170SBaolin Wang } 276195ca170SBaolin Wang 277195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health) 278195ca170SBaolin Wang { 279195ca170SBaolin Wang int ret, vol; 280195ca170SBaolin Wang 281195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &vol); 282195ca170SBaolin Wang if (ret) 283195ca170SBaolin Wang return ret; 284195ca170SBaolin Wang 285195ca170SBaolin Wang if (vol > data->max_volt) 286195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 287195ca170SBaolin Wang else 288195ca170SBaolin Wang *health = POWER_SUPPLY_HEALTH_GOOD; 289195ca170SBaolin Wang 290195ca170SBaolin Wang return 0; 291195ca170SBaolin Wang } 292195ca170SBaolin Wang 293195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status) 294195ca170SBaolin Wang { 295195ca170SBaolin Wang union power_supply_propval val; 296195ca170SBaolin Wang struct power_supply *psy; 297195ca170SBaolin Wang int i, ret = -EINVAL; 298195ca170SBaolin Wang 299195ca170SBaolin Wang for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) { 300195ca170SBaolin Wang psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]); 301195ca170SBaolin Wang if (!psy) 302195ca170SBaolin Wang continue; 303195ca170SBaolin Wang 304195ca170SBaolin Wang ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, 305195ca170SBaolin Wang &val); 306195ca170SBaolin Wang power_supply_put(psy); 307195ca170SBaolin Wang if (ret) 308195ca170SBaolin Wang return ret; 309195ca170SBaolin Wang 310195ca170SBaolin Wang *status = val.intval; 311195ca170SBaolin Wang } 312195ca170SBaolin Wang 313195ca170SBaolin Wang return ret; 314195ca170SBaolin Wang } 315195ca170SBaolin Wang 316195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy, 317195ca170SBaolin Wang enum power_supply_property psp, 318195ca170SBaolin Wang union power_supply_propval *val) 319195ca170SBaolin Wang { 320195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 321195ca170SBaolin Wang int ret = 0; 322195ca170SBaolin Wang int value; 323195ca170SBaolin Wang 324195ca170SBaolin Wang mutex_lock(&data->lock); 325195ca170SBaolin Wang 326195ca170SBaolin Wang switch (psp) { 327195ca170SBaolin Wang case POWER_SUPPLY_PROP_STATUS: 328195ca170SBaolin Wang ret = sc27xx_fgu_get_status(data, &value); 329195ca170SBaolin Wang if (ret) 330195ca170SBaolin Wang goto error; 331195ca170SBaolin Wang 332195ca170SBaolin Wang val->intval = value; 333195ca170SBaolin Wang break; 334195ca170SBaolin Wang 335195ca170SBaolin Wang case POWER_SUPPLY_PROP_HEALTH: 336195ca170SBaolin Wang ret = sc27xx_fgu_get_health(data, &value); 337195ca170SBaolin Wang if (ret) 338195ca170SBaolin Wang goto error; 339195ca170SBaolin Wang 340195ca170SBaolin Wang val->intval = value; 341195ca170SBaolin Wang break; 342195ca170SBaolin Wang 343195ca170SBaolin Wang case POWER_SUPPLY_PROP_PRESENT: 344195ca170SBaolin Wang val->intval = data->bat_present; 345195ca170SBaolin Wang break; 346195ca170SBaolin Wang 347195ca170SBaolin Wang case POWER_SUPPLY_PROP_TEMP: 348195ca170SBaolin Wang ret = sc27xx_fgu_get_temp(data, &value); 349195ca170SBaolin Wang if (ret) 350195ca170SBaolin Wang goto error; 351195ca170SBaolin Wang 352195ca170SBaolin Wang val->intval = value; 353195ca170SBaolin Wang break; 354195ca170SBaolin Wang 355195ca170SBaolin Wang case POWER_SUPPLY_PROP_TECHNOLOGY: 356195ca170SBaolin Wang val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 357195ca170SBaolin Wang break; 358195ca170SBaolin Wang 359195ca170SBaolin Wang case POWER_SUPPLY_PROP_CAPACITY: 360195ca170SBaolin Wang ret = sc27xx_fgu_get_capacity(data, &value); 361195ca170SBaolin Wang if (ret) 362195ca170SBaolin Wang goto error; 363195ca170SBaolin Wang 364195ca170SBaolin Wang val->intval = value; 365195ca170SBaolin Wang break; 366195ca170SBaolin Wang 367195ca170SBaolin Wang case POWER_SUPPLY_PROP_VOLTAGE_NOW: 368195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_vol(data, &value); 369195ca170SBaolin Wang if (ret) 370195ca170SBaolin Wang goto error; 371195ca170SBaolin Wang 372195ca170SBaolin Wang val->intval = value * 1000; 373195ca170SBaolin Wang break; 374195ca170SBaolin Wang 375195ca170SBaolin Wang case POWER_SUPPLY_PROP_VOLTAGE_OCV: 376195ca170SBaolin Wang ret = sc27xx_fgu_get_vbat_ocv(data, &value); 377195ca170SBaolin Wang if (ret) 378195ca170SBaolin Wang goto error; 379195ca170SBaolin Wang 380195ca170SBaolin Wang val->intval = value; 381195ca170SBaolin Wang break; 382195ca170SBaolin Wang 383195ca170SBaolin Wang case POWER_SUPPLY_PROP_CURRENT_NOW: 384195ca170SBaolin Wang case POWER_SUPPLY_PROP_CURRENT_AVG: 385195ca170SBaolin Wang ret = sc27xx_fgu_get_current(data, &value); 386195ca170SBaolin Wang if (ret) 387195ca170SBaolin Wang goto error; 388195ca170SBaolin Wang 389195ca170SBaolin Wang val->intval = value * 1000; 390195ca170SBaolin Wang break; 391195ca170SBaolin Wang 392195ca170SBaolin Wang default: 393195ca170SBaolin Wang ret = -EINVAL; 394195ca170SBaolin Wang break; 395195ca170SBaolin Wang } 396195ca170SBaolin Wang 397195ca170SBaolin Wang error: 398195ca170SBaolin Wang mutex_unlock(&data->lock); 399195ca170SBaolin Wang return ret; 400195ca170SBaolin Wang } 401195ca170SBaolin Wang 402195ca170SBaolin Wang static void sc27xx_fgu_external_power_changed(struct power_supply *psy) 403195ca170SBaolin Wang { 404195ca170SBaolin Wang struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 405195ca170SBaolin Wang 406195ca170SBaolin Wang power_supply_changed(data->battery); 407195ca170SBaolin Wang } 408195ca170SBaolin Wang 409195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = { 410195ca170SBaolin Wang POWER_SUPPLY_PROP_STATUS, 411195ca170SBaolin Wang POWER_SUPPLY_PROP_HEALTH, 412195ca170SBaolin Wang POWER_SUPPLY_PROP_PRESENT, 413195ca170SBaolin Wang POWER_SUPPLY_PROP_TEMP, 414195ca170SBaolin Wang POWER_SUPPLY_PROP_TECHNOLOGY, 415195ca170SBaolin Wang POWER_SUPPLY_PROP_CAPACITY, 416195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_NOW, 417195ca170SBaolin Wang POWER_SUPPLY_PROP_VOLTAGE_OCV, 418195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_NOW, 419195ca170SBaolin Wang POWER_SUPPLY_PROP_CURRENT_AVG, 420195ca170SBaolin Wang }; 421195ca170SBaolin Wang 422195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = { 423195ca170SBaolin Wang .name = "sc27xx-fgu", 424195ca170SBaolin Wang .type = POWER_SUPPLY_TYPE_BATTERY, 425195ca170SBaolin Wang .properties = sc27xx_fgu_props, 426195ca170SBaolin Wang .num_properties = ARRAY_SIZE(sc27xx_fgu_props), 427195ca170SBaolin Wang .get_property = sc27xx_fgu_get_property, 428195ca170SBaolin Wang .external_power_changed = sc27xx_fgu_external_power_changed, 429195ca170SBaolin Wang }; 430195ca170SBaolin Wang 431195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id) 432195ca170SBaolin Wang { 433195ca170SBaolin Wang struct sc27xx_fgu_data *data = dev_id; 434195ca170SBaolin Wang int state; 435195ca170SBaolin Wang 436195ca170SBaolin Wang mutex_lock(&data->lock); 437195ca170SBaolin Wang 438195ca170SBaolin Wang state = gpiod_get_value_cansleep(data->gpiod); 439195ca170SBaolin Wang if (state < 0) { 440195ca170SBaolin Wang dev_err(data->dev, "failed to get gpio state\n"); 441195ca170SBaolin Wang mutex_unlock(&data->lock); 442195ca170SBaolin Wang return IRQ_RETVAL(state); 443195ca170SBaolin Wang } 444195ca170SBaolin Wang 445195ca170SBaolin Wang data->bat_present = !!state; 446195ca170SBaolin Wang 447195ca170SBaolin Wang mutex_unlock(&data->lock); 448195ca170SBaolin Wang 449195ca170SBaolin Wang power_supply_changed(data->battery); 450195ca170SBaolin Wang return IRQ_HANDLED; 451195ca170SBaolin Wang } 452195ca170SBaolin Wang 453195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data) 454195ca170SBaolin Wang { 455195ca170SBaolin Wang struct sc27xx_fgu_data *data = _data; 456195ca170SBaolin Wang 457195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 458195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 459195ca170SBaolin Wang } 460195ca170SBaolin Wang 461195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity) 462195ca170SBaolin Wang { 463195ca170SBaolin Wang /* 464195ca170SBaolin Wang * Get current capacity (mAh) = battery total capacity (mAh) * 465195ca170SBaolin Wang * current capacity percent (capacity / 100). 466195ca170SBaolin Wang */ 467195ca170SBaolin Wang int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100); 468195ca170SBaolin Wang 469195ca170SBaolin Wang /* 470195ca170SBaolin Wang * Convert current capacity (mAh) to coulomb counter according to the 471195ca170SBaolin Wang * formula: 1 mAh =3.6 coulomb. 472195ca170SBaolin Wang */ 473195ca170SBaolin Wang return DIV_ROUND_CLOSEST(cur_cap * 36, 10); 474195ca170SBaolin Wang } 475195ca170SBaolin Wang 476*65c9fab7SBaolin Wang static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) 477*65c9fab7SBaolin Wang { 478*65c9fab7SBaolin Wang struct nvmem_cell *cell; 479*65c9fab7SBaolin Wang int calib_data, cal_4200mv; 480*65c9fab7SBaolin Wang void *buf; 481*65c9fab7SBaolin Wang size_t len; 482*65c9fab7SBaolin Wang 483*65c9fab7SBaolin Wang cell = nvmem_cell_get(data->dev, "fgu_calib"); 484*65c9fab7SBaolin Wang if (IS_ERR(cell)) 485*65c9fab7SBaolin Wang return PTR_ERR(cell); 486*65c9fab7SBaolin Wang 487*65c9fab7SBaolin Wang buf = nvmem_cell_read(cell, &len); 488*65c9fab7SBaolin Wang nvmem_cell_put(cell); 489*65c9fab7SBaolin Wang 490*65c9fab7SBaolin Wang if (IS_ERR(buf)) 491*65c9fab7SBaolin Wang return PTR_ERR(buf); 492*65c9fab7SBaolin Wang 493*65c9fab7SBaolin Wang memcpy(&calib_data, buf, min(len, sizeof(u32))); 494*65c9fab7SBaolin Wang 495*65c9fab7SBaolin Wang /* 496*65c9fab7SBaolin Wang * Get the ADC value corresponding to 4200 mV from eFuse controller 497*65c9fab7SBaolin Wang * according to below formula. Then convert to ADC values corresponding 498*65c9fab7SBaolin Wang * to 1000 mV and 1000 mA. 499*65c9fab7SBaolin Wang */ 500*65c9fab7SBaolin Wang cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256; 501*65c9fab7SBaolin Wang data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42); 502*65c9fab7SBaolin Wang data->cur_1000ma_adc = data->vol_1000mv_adc * 4; 503*65c9fab7SBaolin Wang 504*65c9fab7SBaolin Wang kfree(buf); 505*65c9fab7SBaolin Wang return 0; 506*65c9fab7SBaolin Wang } 507*65c9fab7SBaolin Wang 508195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) 509195ca170SBaolin Wang { 510195ca170SBaolin Wang struct power_supply_battery_info info = { }; 511195ca170SBaolin Wang struct power_supply_battery_ocv_table *table; 512195ca170SBaolin Wang int ret; 513195ca170SBaolin Wang 514195ca170SBaolin Wang ret = power_supply_get_battery_info(data->battery, &info); 515195ca170SBaolin Wang if (ret) { 516195ca170SBaolin Wang dev_err(data->dev, "failed to get battery information\n"); 517195ca170SBaolin Wang return ret; 518195ca170SBaolin Wang } 519195ca170SBaolin Wang 520195ca170SBaolin Wang data->total_cap = info.charge_full_design_uah / 1000; 521195ca170SBaolin Wang data->max_volt = info.constant_charge_voltage_max_uv / 1000; 522195ca170SBaolin Wang data->internal_resist = info.factory_internal_resistance_uohm / 1000; 523195ca170SBaolin Wang 524195ca170SBaolin Wang /* 525195ca170SBaolin Wang * For SC27XX fuel gauge device, we only use one ocv-capacity 526195ca170SBaolin Wang * table in normal temperature 20 Celsius. 527195ca170SBaolin Wang */ 528195ca170SBaolin Wang table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len); 529195ca170SBaolin Wang if (!table) 530195ca170SBaolin Wang return -EINVAL; 531195ca170SBaolin Wang 532195ca170SBaolin Wang data->cap_table = devm_kmemdup(data->dev, table, 533195ca170SBaolin Wang data->table_len * sizeof(*table), 534195ca170SBaolin Wang GFP_KERNEL); 535195ca170SBaolin Wang if (!data->cap_table) { 536195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 537195ca170SBaolin Wang return -ENOMEM; 538195ca170SBaolin Wang } 539195ca170SBaolin Wang 540195ca170SBaolin Wang power_supply_put_battery_info(data->battery, &info); 541195ca170SBaolin Wang 542*65c9fab7SBaolin Wang ret = sc27xx_fgu_calibration(data); 543*65c9fab7SBaolin Wang if (ret) 544*65c9fab7SBaolin Wang return ret; 545*65c9fab7SBaolin Wang 546195ca170SBaolin Wang /* Enable the FGU module */ 547195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, 548195ca170SBaolin Wang SC27XX_FGU_EN, SC27XX_FGU_EN); 549195ca170SBaolin Wang if (ret) { 550195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu\n"); 551195ca170SBaolin Wang return ret; 552195ca170SBaolin Wang } 553195ca170SBaolin Wang 554195ca170SBaolin Wang /* Enable the FGU RTC clock to make it work */ 555195ca170SBaolin Wang ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0, 556195ca170SBaolin Wang SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN); 557195ca170SBaolin Wang if (ret) { 558195ca170SBaolin Wang dev_err(data->dev, "failed to enable fgu RTC clock\n"); 559195ca170SBaolin Wang goto disable_fgu; 560195ca170SBaolin Wang } 561195ca170SBaolin Wang 562195ca170SBaolin Wang /* 563195ca170SBaolin Wang * Get the boot battery capacity when system powers on, which is used to 564195ca170SBaolin Wang * initialize the coulomb counter. After that, we can read the coulomb 565195ca170SBaolin Wang * counter to measure the battery capacity. 566195ca170SBaolin Wang */ 567195ca170SBaolin Wang ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap); 568195ca170SBaolin Wang if (ret) { 569195ca170SBaolin Wang dev_err(data->dev, "failed to get boot capacity\n"); 570195ca170SBaolin Wang goto disable_clk; 571195ca170SBaolin Wang } 572195ca170SBaolin Wang 573195ca170SBaolin Wang /* 574195ca170SBaolin Wang * Convert battery capacity to the corresponding initial coulomb counter 575195ca170SBaolin Wang * and set into coulomb counter registers. 576195ca170SBaolin Wang */ 577195ca170SBaolin Wang data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); 578195ca170SBaolin Wang ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt); 579195ca170SBaolin Wang if (ret) { 580195ca170SBaolin Wang dev_err(data->dev, "failed to initialize coulomb counter\n"); 581195ca170SBaolin Wang goto disable_clk; 582195ca170SBaolin Wang } 583195ca170SBaolin Wang 584195ca170SBaolin Wang return 0; 585195ca170SBaolin Wang 586195ca170SBaolin Wang disable_clk: 587195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 588195ca170SBaolin Wang disable_fgu: 589195ca170SBaolin Wang regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 590195ca170SBaolin Wang 591195ca170SBaolin Wang return ret; 592195ca170SBaolin Wang } 593195ca170SBaolin Wang 594195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev) 595195ca170SBaolin Wang { 596195ca170SBaolin Wang struct device_node *np = pdev->dev.of_node; 597195ca170SBaolin Wang struct power_supply_config fgu_cfg = { }; 598195ca170SBaolin Wang struct sc27xx_fgu_data *data; 599195ca170SBaolin Wang int ret, irq; 600195ca170SBaolin Wang 601195ca170SBaolin Wang data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 602195ca170SBaolin Wang if (!data) 603195ca170SBaolin Wang return -ENOMEM; 604195ca170SBaolin Wang 605195ca170SBaolin Wang data->regmap = dev_get_regmap(pdev->dev.parent, NULL); 606195ca170SBaolin Wang if (!data->regmap) { 607195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get regmap\n"); 608195ca170SBaolin Wang return -ENODEV; 609195ca170SBaolin Wang } 610195ca170SBaolin Wang 611195ca170SBaolin Wang ret = device_property_read_u32(&pdev->dev, "reg", &data->base); 612195ca170SBaolin Wang if (ret) { 613195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get fgu address\n"); 614195ca170SBaolin Wang return ret; 615195ca170SBaolin Wang } 616195ca170SBaolin Wang 617195ca170SBaolin Wang data->channel = devm_iio_channel_get(&pdev->dev, "bat-temp"); 618195ca170SBaolin Wang if (IS_ERR(data->channel)) { 619195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get IIO channel\n"); 620195ca170SBaolin Wang return PTR_ERR(data->channel); 621195ca170SBaolin Wang } 622195ca170SBaolin Wang 623195ca170SBaolin Wang data->gpiod = devm_gpiod_get(&pdev->dev, "bat-detect", GPIOD_IN); 624195ca170SBaolin Wang if (IS_ERR(data->gpiod)) { 625195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get battery detection GPIO\n"); 626195ca170SBaolin Wang return PTR_ERR(data->gpiod); 627195ca170SBaolin Wang } 628195ca170SBaolin Wang 629195ca170SBaolin Wang ret = gpiod_get_value_cansleep(data->gpiod); 630195ca170SBaolin Wang if (ret < 0) { 631195ca170SBaolin Wang dev_err(&pdev->dev, "failed to get gpio state\n"); 632195ca170SBaolin Wang return ret; 633195ca170SBaolin Wang } 634195ca170SBaolin Wang 635195ca170SBaolin Wang data->bat_present = !!ret; 636195ca170SBaolin Wang mutex_init(&data->lock); 637195ca170SBaolin Wang data->dev = &pdev->dev; 638195ca170SBaolin Wang 639195ca170SBaolin Wang fgu_cfg.drv_data = data; 640195ca170SBaolin Wang fgu_cfg.of_node = np; 641195ca170SBaolin Wang data->battery = devm_power_supply_register(&pdev->dev, &sc27xx_fgu_desc, 642195ca170SBaolin Wang &fgu_cfg); 643195ca170SBaolin Wang if (IS_ERR(data->battery)) { 644195ca170SBaolin Wang dev_err(&pdev->dev, "failed to register power supply\n"); 645195ca170SBaolin Wang return PTR_ERR(data->battery); 646195ca170SBaolin Wang } 647195ca170SBaolin Wang 648195ca170SBaolin Wang ret = sc27xx_fgu_hw_init(data); 649195ca170SBaolin Wang if (ret) { 650195ca170SBaolin Wang dev_err(&pdev->dev, "failed to initialize fgu hardware\n"); 651195ca170SBaolin Wang return ret; 652195ca170SBaolin Wang } 653195ca170SBaolin Wang 654195ca170SBaolin Wang ret = devm_add_action(&pdev->dev, sc27xx_fgu_disable, data); 655195ca170SBaolin Wang if (ret) { 656195ca170SBaolin Wang sc27xx_fgu_disable(data); 657195ca170SBaolin Wang dev_err(&pdev->dev, "failed to add fgu disable action\n"); 658195ca170SBaolin Wang return ret; 659195ca170SBaolin Wang } 660195ca170SBaolin Wang 661195ca170SBaolin Wang irq = gpiod_to_irq(data->gpiod); 662195ca170SBaolin Wang if (irq < 0) { 663195ca170SBaolin Wang dev_err(&pdev->dev, "failed to translate GPIO to IRQ\n"); 664195ca170SBaolin Wang return irq; 665195ca170SBaolin Wang } 666195ca170SBaolin Wang 667195ca170SBaolin Wang ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, 668195ca170SBaolin Wang sc27xx_fgu_bat_detection, 669195ca170SBaolin Wang IRQF_ONESHOT | IRQF_TRIGGER_RISING | 670195ca170SBaolin Wang IRQF_TRIGGER_FALLING, 671195ca170SBaolin Wang pdev->name, data); 672195ca170SBaolin Wang if (ret) { 673195ca170SBaolin Wang dev_err(&pdev->dev, "failed to request IRQ\n"); 674195ca170SBaolin Wang return ret; 675195ca170SBaolin Wang } 676195ca170SBaolin Wang 677195ca170SBaolin Wang return 0; 678195ca170SBaolin Wang } 679195ca170SBaolin Wang 680195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = { 681195ca170SBaolin Wang { .compatible = "sprd,sc2731-fgu", }, 682195ca170SBaolin Wang { } 683195ca170SBaolin Wang }; 684195ca170SBaolin Wang 685195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = { 686195ca170SBaolin Wang .probe = sc27xx_fgu_probe, 687195ca170SBaolin Wang .driver = { 688195ca170SBaolin Wang .name = "sc27xx-fgu", 689195ca170SBaolin Wang .of_match_table = sc27xx_fgu_of_match, 690195ca170SBaolin Wang } 691195ca170SBaolin Wang }; 692195ca170SBaolin Wang 693195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver); 694195ca170SBaolin Wang 695195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver"); 696195ca170SBaolin Wang MODULE_LICENSE("GPL v2"); 697