18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (C) 2018 Spreadtrum Communications Inc. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 58c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h> 68c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/math64.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/nvmem-consumer.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 148c2ecf20Sopenharmony_ci#include <linux/regmap.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* PMIC global control registers definition */ 188c2ecf20Sopenharmony_ci#define SC27XX_MODULE_EN0 0xc08 198c2ecf20Sopenharmony_ci#define SC27XX_CLK_EN0 0xc18 208c2ecf20Sopenharmony_ci#define SC27XX_FGU_EN BIT(7) 218c2ecf20Sopenharmony_ci#define SC27XX_FGU_RTC_EN BIT(6) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* FGU registers definition */ 248c2ecf20Sopenharmony_ci#define SC27XX_FGU_START 0x0 258c2ecf20Sopenharmony_ci#define SC27XX_FGU_CONFIG 0x4 268c2ecf20Sopenharmony_ci#define SC27XX_FGU_ADC_CONFIG 0x8 278c2ecf20Sopenharmony_ci#define SC27XX_FGU_STATUS 0xc 288c2ecf20Sopenharmony_ci#define SC27XX_FGU_INT_EN 0x10 298c2ecf20Sopenharmony_ci#define SC27XX_FGU_INT_CLR 0x14 308c2ecf20Sopenharmony_ci#define SC27XX_FGU_INT_STS 0x1c 318c2ecf20Sopenharmony_ci#define SC27XX_FGU_VOLTAGE 0x20 328c2ecf20Sopenharmony_ci#define SC27XX_FGU_OCV 0x24 338c2ecf20Sopenharmony_ci#define SC27XX_FGU_POCV 0x28 348c2ecf20Sopenharmony_ci#define SC27XX_FGU_CURRENT 0x2c 358c2ecf20Sopenharmony_ci#define SC27XX_FGU_LOW_OVERLOAD 0x34 368c2ecf20Sopenharmony_ci#define SC27XX_FGU_CLBCNT_SETH 0x50 378c2ecf20Sopenharmony_ci#define SC27XX_FGU_CLBCNT_SETL 0x54 388c2ecf20Sopenharmony_ci#define SC27XX_FGU_CLBCNT_DELTH 0x58 398c2ecf20Sopenharmony_ci#define SC27XX_FGU_CLBCNT_DELTL 0x5c 408c2ecf20Sopenharmony_ci#define SC27XX_FGU_CLBCNT_VALH 0x68 418c2ecf20Sopenharmony_ci#define SC27XX_FGU_CLBCNT_VALL 0x6c 428c2ecf20Sopenharmony_ci#define SC27XX_FGU_CLBCNT_QMAXL 0x74 438c2ecf20Sopenharmony_ci#define SC27XX_FGU_USER_AREA_SET 0xa0 448c2ecf20Sopenharmony_ci#define SC27XX_FGU_USER_AREA_CLEAR 0xa4 458c2ecf20Sopenharmony_ci#define SC27XX_FGU_USER_AREA_STATUS 0xa8 468c2ecf20Sopenharmony_ci#define SC27XX_FGU_VOLTAGE_BUF 0xd0 478c2ecf20Sopenharmony_ci#define SC27XX_FGU_CURRENT_BUF 0xf0 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define SC27XX_WRITE_SELCLB_EN BIT(0) 508c2ecf20Sopenharmony_ci#define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0) 518c2ecf20Sopenharmony_ci#define SC27XX_FGU_CLBCNT_SHIFT 16 528c2ecf20Sopenharmony_ci#define SC27XX_FGU_LOW_OVERLOAD_MASK GENMASK(12, 0) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define SC27XX_FGU_INT_MASK GENMASK(9, 0) 558c2ecf20Sopenharmony_ci#define SC27XX_FGU_LOW_OVERLOAD_INT BIT(0) 568c2ecf20Sopenharmony_ci#define SC27XX_FGU_CLBCNT_DELTA_INT BIT(2) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define SC27XX_FGU_MODE_AREA_MASK GENMASK(15, 12) 598c2ecf20Sopenharmony_ci#define SC27XX_FGU_CAP_AREA_MASK GENMASK(11, 0) 608c2ecf20Sopenharmony_ci#define SC27XX_FGU_MODE_AREA_SHIFT 12 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define SC27XX_FGU_FIRST_POWERTON GENMASK(3, 0) 638c2ecf20Sopenharmony_ci#define SC27XX_FGU_DEFAULT_CAP GENMASK(11, 0) 648c2ecf20Sopenharmony_ci#define SC27XX_FGU_NORMAIL_POWERTON 0x5 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define SC27XX_FGU_CUR_BASIC_ADC 8192 678c2ecf20Sopenharmony_ci#define SC27XX_FGU_SAMPLE_HZ 2 688c2ecf20Sopenharmony_ci/* micro Ohms */ 698c2ecf20Sopenharmony_ci#define SC27XX_FGU_IDEAL_RESISTANCE 20000 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * struct sc27xx_fgu_data: describe the FGU device 738c2ecf20Sopenharmony_ci * @regmap: regmap for register access 748c2ecf20Sopenharmony_ci * @dev: platform device 758c2ecf20Sopenharmony_ci * @battery: battery power supply 768c2ecf20Sopenharmony_ci * @base: the base offset for the controller 778c2ecf20Sopenharmony_ci * @lock: protect the structure 788c2ecf20Sopenharmony_ci * @gpiod: GPIO for battery detection 798c2ecf20Sopenharmony_ci * @channel: IIO channel to get battery temperature 808c2ecf20Sopenharmony_ci * @charge_chan: IIO channel to get charge voltage 818c2ecf20Sopenharmony_ci * @internal_resist: the battery internal resistance in mOhm 828c2ecf20Sopenharmony_ci * @total_cap: the total capacity of the battery in mAh 838c2ecf20Sopenharmony_ci * @init_cap: the initial capacity of the battery in mAh 848c2ecf20Sopenharmony_ci * @alarm_cap: the alarm capacity 858c2ecf20Sopenharmony_ci * @init_clbcnt: the initial coulomb counter 868c2ecf20Sopenharmony_ci * @max_volt: the maximum constant input voltage in millivolt 878c2ecf20Sopenharmony_ci * @min_volt: the minimum drained battery voltage in microvolt 888c2ecf20Sopenharmony_ci * @boot_volt: the voltage measured during boot in microvolt 898c2ecf20Sopenharmony_ci * @table_len: the capacity table length 908c2ecf20Sopenharmony_ci * @resist_table_len: the resistance table length 918c2ecf20Sopenharmony_ci * @cur_1000ma_adc: ADC value corresponding to 1000 mA 928c2ecf20Sopenharmony_ci * @vol_1000mv_adc: ADC value corresponding to 1000 mV 938c2ecf20Sopenharmony_ci * @calib_resist: the real resistance of coulomb counter chip in uOhm 948c2ecf20Sopenharmony_ci * @cap_table: capacity table with corresponding ocv 958c2ecf20Sopenharmony_ci * @resist_table: resistance percent table with corresponding temperature 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistruct sc27xx_fgu_data { 988c2ecf20Sopenharmony_ci struct regmap *regmap; 998c2ecf20Sopenharmony_ci struct device *dev; 1008c2ecf20Sopenharmony_ci struct power_supply *battery; 1018c2ecf20Sopenharmony_ci u32 base; 1028c2ecf20Sopenharmony_ci struct mutex lock; 1038c2ecf20Sopenharmony_ci struct gpio_desc *gpiod; 1048c2ecf20Sopenharmony_ci struct iio_channel *channel; 1058c2ecf20Sopenharmony_ci struct iio_channel *charge_chan; 1068c2ecf20Sopenharmony_ci bool bat_present; 1078c2ecf20Sopenharmony_ci int internal_resist; 1088c2ecf20Sopenharmony_ci int total_cap; 1098c2ecf20Sopenharmony_ci int init_cap; 1108c2ecf20Sopenharmony_ci int alarm_cap; 1118c2ecf20Sopenharmony_ci int init_clbcnt; 1128c2ecf20Sopenharmony_ci int max_volt; 1138c2ecf20Sopenharmony_ci int min_volt; 1148c2ecf20Sopenharmony_ci int boot_volt; 1158c2ecf20Sopenharmony_ci int table_len; 1168c2ecf20Sopenharmony_ci int resist_table_len; 1178c2ecf20Sopenharmony_ci int cur_1000ma_adc; 1188c2ecf20Sopenharmony_ci int vol_1000mv_adc; 1198c2ecf20Sopenharmony_ci int calib_resist; 1208c2ecf20Sopenharmony_ci struct power_supply_battery_ocv_table *cap_table; 1218c2ecf20Sopenharmony_ci struct power_supply_resistance_temp_table *resist_table; 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity); 1258c2ecf20Sopenharmony_cistatic void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, 1268c2ecf20Sopenharmony_ci int cap, bool int_mode); 1278c2ecf20Sopenharmony_cistatic void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap); 1288c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic const char * const sc27xx_charger_supply_name[] = { 1318c2ecf20Sopenharmony_ci "sc2731_charger", 1328c2ecf20Sopenharmony_ci "sc2720_charger", 1338c2ecf20Sopenharmony_ci "sc2721_charger", 1348c2ecf20Sopenharmony_ci "sc2723_charger", 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, s64 adc) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci return DIV_S64_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, s64 adc) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci return DIV_S64_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci int ret, status, cap, mode; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, 1578c2ecf20Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_STATUS, &status); 1588c2ecf20Sopenharmony_ci if (ret) 1598c2ecf20Sopenharmony_ci return false; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* 1628c2ecf20Sopenharmony_ci * We use low 4 bits to save the last battery capacity and high 12 bits 1638c2ecf20Sopenharmony_ci * to save the system boot mode. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT; 1668c2ecf20Sopenharmony_ci cap = status & SC27XX_FGU_CAP_AREA_MASK; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* 1698c2ecf20Sopenharmony_ci * When FGU has been powered down, the user area registers became 1708c2ecf20Sopenharmony_ci * default value (0xffff), which can be used to valid if the system is 1718c2ecf20Sopenharmony_ci * first power on or not. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP) 1748c2ecf20Sopenharmony_ci return true; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return false; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data, 1808c2ecf20Sopenharmony_ci int boot_mode) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci int ret; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, 1858c2ecf20Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_CLEAR, 1868c2ecf20Sopenharmony_ci SC27XX_FGU_MODE_AREA_MASK, 1878c2ecf20Sopenharmony_ci SC27XX_FGU_MODE_AREA_MASK); 1888c2ecf20Sopenharmony_ci if (ret) 1898c2ecf20Sopenharmony_ci return ret; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* 1928c2ecf20Sopenharmony_ci * Since the user area registers are put on power always-on region, 1938c2ecf20Sopenharmony_ci * then these registers changing time will be a little long. Thus 1948c2ecf20Sopenharmony_ci * here we should delay 200us to wait until values are updated 1958c2ecf20Sopenharmony_ci * successfully according to the datasheet. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_ci udelay(200); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, 2008c2ecf20Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_SET, 2018c2ecf20Sopenharmony_ci SC27XX_FGU_MODE_AREA_MASK, 2028c2ecf20Sopenharmony_ci boot_mode << SC27XX_FGU_MODE_AREA_SHIFT); 2038c2ecf20Sopenharmony_ci if (ret) 2048c2ecf20Sopenharmony_ci return ret; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * Since the user area registers are put on power always-on region, 2088c2ecf20Sopenharmony_ci * then these registers changing time will be a little long. Thus 2098c2ecf20Sopenharmony_ci * here we should delay 200us to wait until values are updated 2108c2ecf20Sopenharmony_ci * successfully according to the datasheet. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci udelay(200); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to 2168c2ecf20Sopenharmony_ci * make the user area data available, otherwise we can not save the user 2178c2ecf20Sopenharmony_ci * area data. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci return regmap_update_bits(data->regmap, 2208c2ecf20Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_CLEAR, 2218c2ecf20Sopenharmony_ci SC27XX_FGU_MODE_AREA_MASK, 0); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci int ret; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, 2298c2ecf20Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_CLEAR, 2308c2ecf20Sopenharmony_ci SC27XX_FGU_CAP_AREA_MASK, 2318c2ecf20Sopenharmony_ci SC27XX_FGU_CAP_AREA_MASK); 2328c2ecf20Sopenharmony_ci if (ret) 2338c2ecf20Sopenharmony_ci return ret; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* 2368c2ecf20Sopenharmony_ci * Since the user area registers are put on power always-on region, 2378c2ecf20Sopenharmony_ci * then these registers changing time will be a little long. Thus 2388c2ecf20Sopenharmony_ci * here we should delay 200us to wait until values are updated 2398c2ecf20Sopenharmony_ci * successfully according to the datasheet. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci udelay(200); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, 2448c2ecf20Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_SET, 2458c2ecf20Sopenharmony_ci SC27XX_FGU_CAP_AREA_MASK, cap); 2468c2ecf20Sopenharmony_ci if (ret) 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* 2508c2ecf20Sopenharmony_ci * Since the user area registers are put on power always-on region, 2518c2ecf20Sopenharmony_ci * then these registers changing time will be a little long. Thus 2528c2ecf20Sopenharmony_ci * here we should delay 200us to wait until values are updated 2538c2ecf20Sopenharmony_ci * successfully according to the datasheet. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci udelay(200); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* 2588c2ecf20Sopenharmony_ci * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to 2598c2ecf20Sopenharmony_ci * make the user area data available, otherwise we can not save the user 2608c2ecf20Sopenharmony_ci * area data. 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_ci return regmap_update_bits(data->regmap, 2638c2ecf20Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_CLEAR, 2648c2ecf20Sopenharmony_ci SC27XX_FGU_CAP_AREA_MASK, 0); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci int ret, value; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, 2728c2ecf20Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_STATUS, &value); 2738c2ecf20Sopenharmony_ci if (ret) 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci *cap = value & SC27XX_FGU_CAP_AREA_MASK; 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/* 2818c2ecf20Sopenharmony_ci * When system boots on, we can not read battery capacity from coulomb 2828c2ecf20Sopenharmony_ci * registers, since now the coulomb registers are invalid. So we should 2838c2ecf20Sopenharmony_ci * calculate the battery open circuit voltage, and get current battery 2848c2ecf20Sopenharmony_ci * capacity according to the capacity table. 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci int volt, cur, oci, ocv, ret; 2898c2ecf20Sopenharmony_ci bool is_first_poweron = sc27xx_fgu_is_first_poweron(data); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* 2928c2ecf20Sopenharmony_ci * If system is not the first power on, we should use the last saved 2938c2ecf20Sopenharmony_ci * battery capacity as the initial battery capacity. Otherwise we should 2948c2ecf20Sopenharmony_ci * re-calculate the initial battery capacity. 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_ci if (!is_first_poweron) { 2978c2ecf20Sopenharmony_ci ret = sc27xx_fgu_read_last_cap(data, cap); 2988c2ecf20Sopenharmony_ci if (ret) 2998c2ecf20Sopenharmony_ci return ret; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* 3058c2ecf20Sopenharmony_ci * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved 3068c2ecf20Sopenharmony_ci * the first sampled open circuit current. 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL, 3098c2ecf20Sopenharmony_ci &cur); 3108c2ecf20Sopenharmony_ci if (ret) 3118c2ecf20Sopenharmony_ci return ret; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci cur <<= 1; 3148c2ecf20Sopenharmony_ci oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* 3178c2ecf20Sopenharmony_ci * Should get the OCV from SC27XX_FGU_POCV register at the system 3188c2ecf20Sopenharmony_ci * beginning. It is ADC values reading from registers which need to 3198c2ecf20Sopenharmony_ci * convert the corresponding voltage. 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt); 3228c2ecf20Sopenharmony_ci if (ret) 3238c2ecf20Sopenharmony_ci return ret; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci volt = sc27xx_fgu_adc_to_voltage(data, volt); 3268c2ecf20Sopenharmony_ci ocv = volt * 1000 - oci * data->internal_resist; 3278c2ecf20Sopenharmony_ci data->boot_volt = ocv; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* 3308c2ecf20Sopenharmony_ci * Parse the capacity table to look up the correct capacity percent 3318c2ecf20Sopenharmony_ci * according to current battery's corresponding OCV values. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci *cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len, 3348c2ecf20Sopenharmony_ci ocv); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci ret = sc27xx_fgu_save_last_cap(data, *cap); 3378c2ecf20Sopenharmony_ci if (ret) 3388c2ecf20Sopenharmony_ci return ret; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci int ret; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, 3488c2ecf20Sopenharmony_ci data->base + SC27XX_FGU_CLBCNT_SETL, 3498c2ecf20Sopenharmony_ci SC27XX_FGU_CLBCNT_MASK, clbcnt); 3508c2ecf20Sopenharmony_ci if (ret) 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, 3548c2ecf20Sopenharmony_ci data->base + SC27XX_FGU_CLBCNT_SETH, 3558c2ecf20Sopenharmony_ci SC27XX_FGU_CLBCNT_MASK, 3568c2ecf20Sopenharmony_ci clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 3578c2ecf20Sopenharmony_ci if (ret) 3588c2ecf20Sopenharmony_ci return ret; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START, 3618c2ecf20Sopenharmony_ci SC27XX_WRITE_SELCLB_EN, 3628c2ecf20Sopenharmony_ci SC27XX_WRITE_SELCLB_EN); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci int ccl, cch, ret; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL, 3708c2ecf20Sopenharmony_ci &ccl); 3718c2ecf20Sopenharmony_ci if (ret) 3728c2ecf20Sopenharmony_ci return ret; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH, 3758c2ecf20Sopenharmony_ci &cch); 3768c2ecf20Sopenharmony_ci if (ret) 3778c2ecf20Sopenharmony_ci return ret; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci *clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK; 3808c2ecf20Sopenharmony_ci *clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_vol_now(struct sc27xx_fgu_data *data, int *val) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci int ret; 3888c2ecf20Sopenharmony_ci u32 vol; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE_BUF, 3918c2ecf20Sopenharmony_ci &vol); 3928c2ecf20Sopenharmony_ci if (ret) 3938c2ecf20Sopenharmony_ci return ret; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* 3968c2ecf20Sopenharmony_ci * It is ADC values reading from registers which need to convert to 3978c2ecf20Sopenharmony_ci * corresponding voltage values. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci *val = sc27xx_fgu_adc_to_voltage(data, vol); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_cur_now(struct sc27xx_fgu_data *data, int *val) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci int ret; 4078c2ecf20Sopenharmony_ci u32 cur; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT_BUF, 4108c2ecf20Sopenharmony_ci &cur); 4118c2ecf20Sopenharmony_ci if (ret) 4128c2ecf20Sopenharmony_ci return ret; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* 4158c2ecf20Sopenharmony_ci * It is ADC values reading from registers which need to convert to 4168c2ecf20Sopenharmony_ci * corresponding current values. 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ci *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Get current coulomb counters firstly */ 4288c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt); 4298c2ecf20Sopenharmony_ci if (ret) 4308c2ecf20Sopenharmony_ci return ret; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci delta_clbcnt = cur_clbcnt - data->init_clbcnt; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* 4358c2ecf20Sopenharmony_ci * Convert coulomb counter to delta capacity (mAh), and set multiplier 4368c2ecf20Sopenharmony_ci * as 10 to improve the precision. 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_ci temp = DIV_ROUND_CLOSEST(delta_clbcnt * 10, 36 * SC27XX_FGU_SAMPLE_HZ); 4398c2ecf20Sopenharmony_ci temp = sc27xx_fgu_adc_to_current(data, temp / 1000); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* 4428c2ecf20Sopenharmony_ci * Convert to capacity percent of the battery total capacity, 4438c2ecf20Sopenharmony_ci * and multiplier is 100 too. 4448c2ecf20Sopenharmony_ci */ 4458c2ecf20Sopenharmony_ci delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap); 4468c2ecf20Sopenharmony_ci *cap = delta_cap + data->init_cap; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Calibrate the battery capacity in a normal range. */ 4498c2ecf20Sopenharmony_ci sc27xx_fgu_capacity_calibration(data, *cap, false); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return 0; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci int ret, vol; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol); 4598c2ecf20Sopenharmony_ci if (ret) 4608c2ecf20Sopenharmony_ci return ret; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* 4638c2ecf20Sopenharmony_ci * It is ADC values reading from registers which need to convert to 4648c2ecf20Sopenharmony_ci * corresponding voltage values. 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci *val = sc27xx_fgu_adc_to_voltage(data, vol); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return 0; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci int ret, cur; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur); 4768c2ecf20Sopenharmony_ci if (ret) 4778c2ecf20Sopenharmony_ci return ret; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* 4808c2ecf20Sopenharmony_ci * It is ADC values reading from registers which need to convert to 4818c2ecf20Sopenharmony_ci * corresponding current values. 4828c2ecf20Sopenharmony_ci */ 4838c2ecf20Sopenharmony_ci *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci return 0; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci int vol, cur, ret, temp, resistance; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_vbat_vol(data, &vol); 4938c2ecf20Sopenharmony_ci if (ret) 4948c2ecf20Sopenharmony_ci return ret; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_current(data, &cur); 4978c2ecf20Sopenharmony_ci if (ret) 4988c2ecf20Sopenharmony_ci return ret; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci resistance = data->internal_resist; 5018c2ecf20Sopenharmony_ci if (data->resist_table_len > 0) { 5028c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_temp(data, &temp); 5038c2ecf20Sopenharmony_ci if (ret) 5048c2ecf20Sopenharmony_ci return ret; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci resistance = power_supply_temp2resist_simple(data->resist_table, 5078c2ecf20Sopenharmony_ci data->resist_table_len, temp); 5088c2ecf20Sopenharmony_ci resistance = data->internal_resist * resistance / 100; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* Return the battery OCV in micro volts. */ 5128c2ecf20Sopenharmony_ci *val = vol * 1000 - cur * resistance; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_charge_vol(struct sc27xx_fgu_data *data, int *val) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci int ret, vol; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci ret = iio_read_channel_processed(data->charge_chan, &vol); 5228c2ecf20Sopenharmony_ci if (ret < 0) 5238c2ecf20Sopenharmony_ci return ret; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci *val = vol * 1000; 5268c2ecf20Sopenharmony_ci return 0; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci return iio_read_channel_processed(data->channel, temp); 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci int ret, vol; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_vbat_vol(data, &vol); 5398c2ecf20Sopenharmony_ci if (ret) 5408c2ecf20Sopenharmony_ci return ret; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (vol > data->max_volt) 5438c2ecf20Sopenharmony_ci *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 5448c2ecf20Sopenharmony_ci else 5458c2ecf20Sopenharmony_ci *health = POWER_SUPPLY_HEALTH_GOOD; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci union power_supply_propval val; 5538c2ecf20Sopenharmony_ci struct power_supply *psy; 5548c2ecf20Sopenharmony_ci int i, ret = -EINVAL; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) { 5578c2ecf20Sopenharmony_ci psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]); 5588c2ecf20Sopenharmony_ci if (!psy) 5598c2ecf20Sopenharmony_ci continue; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, 5628c2ecf20Sopenharmony_ci &val); 5638c2ecf20Sopenharmony_ci power_supply_put(psy); 5648c2ecf20Sopenharmony_ci if (ret) 5658c2ecf20Sopenharmony_ci return ret; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci *status = val.intval; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return ret; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic int sc27xx_fgu_get_property(struct power_supply *psy, 5748c2ecf20Sopenharmony_ci enum power_supply_property psp, 5758c2ecf20Sopenharmony_ci union power_supply_propval *val) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 5788c2ecf20Sopenharmony_ci int ret = 0; 5798c2ecf20Sopenharmony_ci int value; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci switch (psp) { 5848c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 5858c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_status(data, &value); 5868c2ecf20Sopenharmony_ci if (ret) 5878c2ecf20Sopenharmony_ci goto error; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci val->intval = value; 5908c2ecf20Sopenharmony_ci break; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 5938c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_health(data, &value); 5948c2ecf20Sopenharmony_ci if (ret) 5958c2ecf20Sopenharmony_ci goto error; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci val->intval = value; 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 6018c2ecf20Sopenharmony_ci val->intval = data->bat_present; 6028c2ecf20Sopenharmony_ci break; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 6058c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_temp(data, &value); 6068c2ecf20Sopenharmony_ci if (ret) 6078c2ecf20Sopenharmony_ci goto error; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci val->intval = value; 6108c2ecf20Sopenharmony_ci break; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 6138c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 6148c2ecf20Sopenharmony_ci break; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 6178c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_capacity(data, &value); 6188c2ecf20Sopenharmony_ci if (ret) 6198c2ecf20Sopenharmony_ci goto error; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci val->intval = value; 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_AVG: 6258c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_vbat_vol(data, &value); 6268c2ecf20Sopenharmony_ci if (ret) 6278c2ecf20Sopenharmony_ci goto error; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci val->intval = value * 1000; 6308c2ecf20Sopenharmony_ci break; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_OCV: 6338c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_vbat_ocv(data, &value); 6348c2ecf20Sopenharmony_ci if (ret) 6358c2ecf20Sopenharmony_ci goto error; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci val->intval = value; 6388c2ecf20Sopenharmony_ci break; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 6418c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_charge_vol(data, &value); 6428c2ecf20Sopenharmony_ci if (ret) 6438c2ecf20Sopenharmony_ci goto error; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci val->intval = value; 6468c2ecf20Sopenharmony_ci break; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_AVG: 6498c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_current(data, &value); 6508c2ecf20Sopenharmony_ci if (ret) 6518c2ecf20Sopenharmony_ci goto error; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci val->intval = value * 1000; 6548c2ecf20Sopenharmony_ci break; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 6578c2ecf20Sopenharmony_ci val->intval = data->total_cap * 1000; 6588c2ecf20Sopenharmony_ci break; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_NOW: 6618c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_clbcnt(data, &value); 6628c2ecf20Sopenharmony_ci if (ret) 6638c2ecf20Sopenharmony_ci goto error; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci value = DIV_ROUND_CLOSEST(value * 10, 6668c2ecf20Sopenharmony_ci 36 * SC27XX_FGU_SAMPLE_HZ); 6678c2ecf20Sopenharmony_ci val->intval = sc27xx_fgu_adc_to_current(data, value); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci break; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 6728c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_vol_now(data, &value); 6738c2ecf20Sopenharmony_ci if (ret) 6748c2ecf20Sopenharmony_ci goto error; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci val->intval = value * 1000; 6778c2ecf20Sopenharmony_ci break; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 6808c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_cur_now(data, &value); 6818c2ecf20Sopenharmony_ci if (ret) 6828c2ecf20Sopenharmony_ci goto error; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci val->intval = value * 1000; 6858c2ecf20Sopenharmony_ci break; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_BOOT: 6888c2ecf20Sopenharmony_ci val->intval = data->boot_volt; 6898c2ecf20Sopenharmony_ci break; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci default: 6928c2ecf20Sopenharmony_ci ret = -EINVAL; 6938c2ecf20Sopenharmony_ci break; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cierror: 6978c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 6988c2ecf20Sopenharmony_ci return ret; 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cistatic int sc27xx_fgu_set_property(struct power_supply *psy, 7028c2ecf20Sopenharmony_ci enum power_supply_property psp, 7038c2ecf20Sopenharmony_ci const union power_supply_propval *val) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 7068c2ecf20Sopenharmony_ci int ret; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci switch (psp) { 7118c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 7128c2ecf20Sopenharmony_ci ret = sc27xx_fgu_save_last_cap(data, val->intval); 7138c2ecf20Sopenharmony_ci if (ret < 0) 7148c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to save battery capacity\n"); 7158c2ecf20Sopenharmony_ci break; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CALIBRATE: 7188c2ecf20Sopenharmony_ci sc27xx_fgu_adjust_cap(data, val->intval); 7198c2ecf20Sopenharmony_ci ret = 0; 7208c2ecf20Sopenharmony_ci break; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 7238c2ecf20Sopenharmony_ci data->total_cap = val->intval / 1000; 7248c2ecf20Sopenharmony_ci ret = 0; 7258c2ecf20Sopenharmony_ci break; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci default: 7288c2ecf20Sopenharmony_ci ret = -EINVAL; 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci return ret; 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic int sc27xx_fgu_property_is_writeable(struct power_supply *psy, 7378c2ecf20Sopenharmony_ci enum power_supply_property psp) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci return psp == POWER_SUPPLY_PROP_CAPACITY || 7408c2ecf20Sopenharmony_ci psp == POWER_SUPPLY_PROP_CALIBRATE || 7418c2ecf20Sopenharmony_ci psp == POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_cistatic enum power_supply_property sc27xx_fgu_props[] = { 7458c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 7468c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 7478c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 7488c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TEMP, 7498c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TECHNOLOGY, 7508c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 7518c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 7528c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_OCV, 7538c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_AVG, 7548c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_BOOT, 7558c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 7568c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_AVG, 7578c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 7588c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 7598c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CALIBRATE, 7608c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_NOW 7618c2ecf20Sopenharmony_ci}; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_cistatic const struct power_supply_desc sc27xx_fgu_desc = { 7648c2ecf20Sopenharmony_ci .name = "sc27xx-fgu", 7658c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 7668c2ecf20Sopenharmony_ci .properties = sc27xx_fgu_props, 7678c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(sc27xx_fgu_props), 7688c2ecf20Sopenharmony_ci .get_property = sc27xx_fgu_get_property, 7698c2ecf20Sopenharmony_ci .set_property = sc27xx_fgu_set_property, 7708c2ecf20Sopenharmony_ci .external_power_changed = power_supply_changed, 7718c2ecf20Sopenharmony_ci .property_is_writeable = sc27xx_fgu_property_is_writeable, 7728c2ecf20Sopenharmony_ci .no_thermal = true, 7738c2ecf20Sopenharmony_ci}; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci int ret; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci data->init_cap = cap; 7808c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_clbcnt(data, &data->init_clbcnt); 7818c2ecf20Sopenharmony_ci if (ret) 7828c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to get init coulomb counter\n"); 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, 7868c2ecf20Sopenharmony_ci int cap, bool int_mode) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci int ret, ocv, chg_sts, adc; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 7918c2ecf20Sopenharmony_ci if (ret) { 7928c2ecf20Sopenharmony_ci dev_err(data->dev, "get battery ocv error.\n"); 7938c2ecf20Sopenharmony_ci return; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_status(data, &chg_sts); 7978c2ecf20Sopenharmony_ci if (ret) { 7988c2ecf20Sopenharmony_ci dev_err(data->dev, "get charger status error.\n"); 7998c2ecf20Sopenharmony_ci return; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* 8038c2ecf20Sopenharmony_ci * If we are in charging mode, then we do not need to calibrate the 8048c2ecf20Sopenharmony_ci * lower capacity. 8058c2ecf20Sopenharmony_ci */ 8068c2ecf20Sopenharmony_ci if (chg_sts == POWER_SUPPLY_STATUS_CHARGING) 8078c2ecf20Sopenharmony_ci return; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if ((ocv > data->cap_table[0].ocv && cap < 100) || cap > 100) { 8108c2ecf20Sopenharmony_ci /* 8118c2ecf20Sopenharmony_ci * If current OCV value is larger than the max OCV value in 8128c2ecf20Sopenharmony_ci * OCV table, or the current capacity is larger than 100, 8138c2ecf20Sopenharmony_ci * we should force the inititial capacity to 100. 8148c2ecf20Sopenharmony_ci */ 8158c2ecf20Sopenharmony_ci sc27xx_fgu_adjust_cap(data, 100); 8168c2ecf20Sopenharmony_ci } else if (ocv <= data->cap_table[data->table_len - 1].ocv) { 8178c2ecf20Sopenharmony_ci /* 8188c2ecf20Sopenharmony_ci * If current OCV value is leass than the minimum OCV value in 8198c2ecf20Sopenharmony_ci * OCV table, we should force the inititial capacity to 0. 8208c2ecf20Sopenharmony_ci */ 8218c2ecf20Sopenharmony_ci sc27xx_fgu_adjust_cap(data, 0); 8228c2ecf20Sopenharmony_ci } else if ((ocv > data->cap_table[data->table_len - 1].ocv && cap <= 0) || 8238c2ecf20Sopenharmony_ci (ocv > data->min_volt && cap <= data->alarm_cap)) { 8248c2ecf20Sopenharmony_ci /* 8258c2ecf20Sopenharmony_ci * If current OCV value is not matchable with current capacity, 8268c2ecf20Sopenharmony_ci * we should re-calculate current capacity by looking up the 8278c2ecf20Sopenharmony_ci * OCV table. 8288c2ecf20Sopenharmony_ci */ 8298c2ecf20Sopenharmony_ci int cur_cap = power_supply_ocv2cap_simple(data->cap_table, 8308c2ecf20Sopenharmony_ci data->table_len, ocv); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci sc27xx_fgu_adjust_cap(data, cur_cap); 8338c2ecf20Sopenharmony_ci } else if (ocv <= data->min_volt) { 8348c2ecf20Sopenharmony_ci /* 8358c2ecf20Sopenharmony_ci * If current OCV value is less than the low alarm voltage, but 8368c2ecf20Sopenharmony_ci * current capacity is larger than the alarm capacity, we should 8378c2ecf20Sopenharmony_ci * adjust the inititial capacity to alarm capacity. 8388c2ecf20Sopenharmony_ci */ 8398c2ecf20Sopenharmony_ci if (cap > data->alarm_cap) { 8408c2ecf20Sopenharmony_ci sc27xx_fgu_adjust_cap(data, data->alarm_cap); 8418c2ecf20Sopenharmony_ci } else { 8428c2ecf20Sopenharmony_ci int cur_cap; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* 8458c2ecf20Sopenharmony_ci * If current capacity is equal with 0 or less than 0 8468c2ecf20Sopenharmony_ci * (some error occurs), we should adjust inititial 8478c2ecf20Sopenharmony_ci * capacity to the capacity corresponding to current OCV 8488c2ecf20Sopenharmony_ci * value. 8498c2ecf20Sopenharmony_ci */ 8508c2ecf20Sopenharmony_ci cur_cap = power_supply_ocv2cap_simple(data->cap_table, 8518c2ecf20Sopenharmony_ci data->table_len, 8528c2ecf20Sopenharmony_ci ocv); 8538c2ecf20Sopenharmony_ci sc27xx_fgu_adjust_cap(data, cur_cap); 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if (!int_mode) 8578c2ecf20Sopenharmony_ci return; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* 8608c2ecf20Sopenharmony_ci * After adjusting the battery capacity, we should set the 8618c2ecf20Sopenharmony_ci * lowest alarm voltage instead. 8628c2ecf20Sopenharmony_ci */ 8638c2ecf20Sopenharmony_ci data->min_volt = data->cap_table[data->table_len - 1].ocv; 8648c2ecf20Sopenharmony_ci data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, 8658c2ecf20Sopenharmony_ci data->table_len, 8668c2ecf20Sopenharmony_ci data->min_volt); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 8698c2ecf20Sopenharmony_ci regmap_update_bits(data->regmap, 8708c2ecf20Sopenharmony_ci data->base + SC27XX_FGU_LOW_OVERLOAD, 8718c2ecf20Sopenharmony_ci SC27XX_FGU_LOW_OVERLOAD_MASK, adc); 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci struct sc27xx_fgu_data *data = dev_id; 8788c2ecf20Sopenharmony_ci int ret, cap; 8798c2ecf20Sopenharmony_ci u32 status; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS, 8848c2ecf20Sopenharmony_ci &status); 8858c2ecf20Sopenharmony_ci if (ret) 8868c2ecf20Sopenharmony_ci goto out; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 8898c2ecf20Sopenharmony_ci status, status); 8908c2ecf20Sopenharmony_ci if (ret) 8918c2ecf20Sopenharmony_ci goto out; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* 8948c2ecf20Sopenharmony_ci * When low overload voltage interrupt happens, we should calibrate the 8958c2ecf20Sopenharmony_ci * battery capacity in lower voltage stage. 8968c2ecf20Sopenharmony_ci */ 8978c2ecf20Sopenharmony_ci if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT)) 8988c2ecf20Sopenharmony_ci goto out; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_capacity(data, &cap); 9018c2ecf20Sopenharmony_ci if (ret) 9028c2ecf20Sopenharmony_ci goto out; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci sc27xx_fgu_capacity_calibration(data, cap, true); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ciout: 9078c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci power_supply_changed(data->battery); 9108c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci struct sc27xx_fgu_data *data = dev_id; 9168c2ecf20Sopenharmony_ci int state; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci state = gpiod_get_value_cansleep(data->gpiod); 9218c2ecf20Sopenharmony_ci if (state < 0) { 9228c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to get gpio state\n"); 9238c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 9248c2ecf20Sopenharmony_ci return IRQ_RETVAL(state); 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci data->bat_present = !!state; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci power_supply_changed(data->battery); 9328c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic void sc27xx_fgu_disable(void *_data) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct sc27xx_fgu_data *data = _data; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 9408c2ecf20Sopenharmony_ci regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_cistatic int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci /* 9468c2ecf20Sopenharmony_ci * Get current capacity (mAh) = battery total capacity (mAh) * 9478c2ecf20Sopenharmony_ci * current capacity percent (capacity / 100). 9488c2ecf20Sopenharmony_ci */ 9498c2ecf20Sopenharmony_ci int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci /* 9528c2ecf20Sopenharmony_ci * Convert current capacity (mAh) to coulomb counter according to the 9538c2ecf20Sopenharmony_ci * formula: 1 mAh =3.6 coulomb. 9548c2ecf20Sopenharmony_ci */ 9558c2ecf20Sopenharmony_ci return DIV_ROUND_CLOSEST(cur_cap * 36 * data->cur_1000ma_adc * SC27XX_FGU_SAMPLE_HZ, 10); 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_cistatic int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) 9598c2ecf20Sopenharmony_ci{ 9608c2ecf20Sopenharmony_ci struct nvmem_cell *cell; 9618c2ecf20Sopenharmony_ci int calib_data, cal_4200mv; 9628c2ecf20Sopenharmony_ci void *buf; 9638c2ecf20Sopenharmony_ci size_t len; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci cell = nvmem_cell_get(data->dev, "fgu_calib"); 9668c2ecf20Sopenharmony_ci if (IS_ERR(cell)) 9678c2ecf20Sopenharmony_ci return PTR_ERR(cell); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci buf = nvmem_cell_read(cell, &len); 9708c2ecf20Sopenharmony_ci nvmem_cell_put(cell); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (IS_ERR(buf)) 9738c2ecf20Sopenharmony_ci return PTR_ERR(buf); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci memcpy(&calib_data, buf, min(len, sizeof(u32))); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci /* 9788c2ecf20Sopenharmony_ci * Get the ADC value corresponding to 4200 mV from eFuse controller 9798c2ecf20Sopenharmony_ci * according to below formula. Then convert to ADC values corresponding 9808c2ecf20Sopenharmony_ci * to 1000 mV and 1000 mA. 9818c2ecf20Sopenharmony_ci */ 9828c2ecf20Sopenharmony_ci cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256; 9838c2ecf20Sopenharmony_ci data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42); 9848c2ecf20Sopenharmony_ci data->cur_1000ma_adc = 9858c2ecf20Sopenharmony_ci DIV_ROUND_CLOSEST(data->vol_1000mv_adc * 4 * data->calib_resist, 9868c2ecf20Sopenharmony_ci SC27XX_FGU_IDEAL_RESISTANCE); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci kfree(buf); 9898c2ecf20Sopenharmony_ci return 0; 9908c2ecf20Sopenharmony_ci} 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cistatic int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) 9938c2ecf20Sopenharmony_ci{ 9948c2ecf20Sopenharmony_ci struct power_supply_battery_info info = { }; 9958c2ecf20Sopenharmony_ci struct power_supply_battery_ocv_table *table; 9968c2ecf20Sopenharmony_ci int ret, delta_clbcnt, alarm_adc; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci ret = power_supply_get_battery_info(data->battery, &info); 9998c2ecf20Sopenharmony_ci if (ret) { 10008c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to get battery information\n"); 10018c2ecf20Sopenharmony_ci return ret; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci data->total_cap = info.charge_full_design_uah / 1000; 10058c2ecf20Sopenharmony_ci data->max_volt = info.constant_charge_voltage_max_uv / 1000; 10068c2ecf20Sopenharmony_ci data->internal_resist = info.factory_internal_resistance_uohm / 1000; 10078c2ecf20Sopenharmony_ci data->min_volt = info.voltage_min_design_uv; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci /* 10108c2ecf20Sopenharmony_ci * For SC27XX fuel gauge device, we only use one ocv-capacity 10118c2ecf20Sopenharmony_ci * table in normal temperature 20 Celsius. 10128c2ecf20Sopenharmony_ci */ 10138c2ecf20Sopenharmony_ci table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len); 10148c2ecf20Sopenharmony_ci if (!table) 10158c2ecf20Sopenharmony_ci return -EINVAL; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci data->cap_table = devm_kmemdup(data->dev, table, 10188c2ecf20Sopenharmony_ci data->table_len * sizeof(*table), 10198c2ecf20Sopenharmony_ci GFP_KERNEL); 10208c2ecf20Sopenharmony_ci if (!data->cap_table) { 10218c2ecf20Sopenharmony_ci power_supply_put_battery_info(data->battery, &info); 10228c2ecf20Sopenharmony_ci return -ENOMEM; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, 10268c2ecf20Sopenharmony_ci data->table_len, 10278c2ecf20Sopenharmony_ci data->min_volt); 10288c2ecf20Sopenharmony_ci if (!data->alarm_cap) 10298c2ecf20Sopenharmony_ci data->alarm_cap += 1; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci data->resist_table_len = info.resist_table_size; 10328c2ecf20Sopenharmony_ci if (data->resist_table_len > 0) { 10338c2ecf20Sopenharmony_ci data->resist_table = devm_kmemdup(data->dev, info.resist_table, 10348c2ecf20Sopenharmony_ci data->resist_table_len * 10358c2ecf20Sopenharmony_ci sizeof(struct power_supply_resistance_temp_table), 10368c2ecf20Sopenharmony_ci GFP_KERNEL); 10378c2ecf20Sopenharmony_ci if (!data->resist_table) { 10388c2ecf20Sopenharmony_ci power_supply_put_battery_info(data->battery, &info); 10398c2ecf20Sopenharmony_ci return -ENOMEM; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci power_supply_put_battery_info(data->battery, &info); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci ret = sc27xx_fgu_calibration(data); 10468c2ecf20Sopenharmony_ci if (ret) 10478c2ecf20Sopenharmony_ci return ret; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci /* Enable the FGU module */ 10508c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, 10518c2ecf20Sopenharmony_ci SC27XX_FGU_EN, SC27XX_FGU_EN); 10528c2ecf20Sopenharmony_ci if (ret) { 10538c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to enable fgu\n"); 10548c2ecf20Sopenharmony_ci return ret; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* Enable the FGU RTC clock to make it work */ 10588c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0, 10598c2ecf20Sopenharmony_ci SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN); 10608c2ecf20Sopenharmony_ci if (ret) { 10618c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to enable fgu RTC clock\n"); 10628c2ecf20Sopenharmony_ci goto disable_fgu; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 10668c2ecf20Sopenharmony_ci SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK); 10678c2ecf20Sopenharmony_ci if (ret) { 10688c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to clear interrupt status\n"); 10698c2ecf20Sopenharmony_ci goto disable_clk; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci /* 10738c2ecf20Sopenharmony_ci * Set the voltage low overload threshold, which means when the battery 10748c2ecf20Sopenharmony_ci * voltage is lower than this threshold, the controller will generate 10758c2ecf20Sopenharmony_ci * one interrupt to notify. 10768c2ecf20Sopenharmony_ci */ 10778c2ecf20Sopenharmony_ci alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 10788c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, 10798c2ecf20Sopenharmony_ci SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc); 10808c2ecf20Sopenharmony_ci if (ret) { 10818c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to set fgu low overload\n"); 10828c2ecf20Sopenharmony_ci goto disable_clk; 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci /* 10868c2ecf20Sopenharmony_ci * Set the coulomb counter delta threshold, that means when the coulomb 10878c2ecf20Sopenharmony_ci * counter change is multiples of the delta threshold, the controller 10888c2ecf20Sopenharmony_ci * will generate one interrupt to notify the users to update the battery 10898c2ecf20Sopenharmony_ci * capacity. Now we set the delta threshold as a counter value of 1% 10908c2ecf20Sopenharmony_ci * capacity. 10918c2ecf20Sopenharmony_ci */ 10928c2ecf20Sopenharmony_ci delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL, 10958c2ecf20Sopenharmony_ci SC27XX_FGU_CLBCNT_MASK, delta_clbcnt); 10968c2ecf20Sopenharmony_ci if (ret) { 10978c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to set low delta coulomb counter\n"); 10988c2ecf20Sopenharmony_ci goto disable_clk; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH, 11028c2ecf20Sopenharmony_ci SC27XX_FGU_CLBCNT_MASK, 11038c2ecf20Sopenharmony_ci delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 11048c2ecf20Sopenharmony_ci if (ret) { 11058c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to set high delta coulomb counter\n"); 11068c2ecf20Sopenharmony_ci goto disable_clk; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci /* 11108c2ecf20Sopenharmony_ci * Get the boot battery capacity when system powers on, which is used to 11118c2ecf20Sopenharmony_ci * initialize the coulomb counter. After that, we can read the coulomb 11128c2ecf20Sopenharmony_ci * counter to measure the battery capacity. 11138c2ecf20Sopenharmony_ci */ 11148c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap); 11158c2ecf20Sopenharmony_ci if (ret) { 11168c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to get boot capacity\n"); 11178c2ecf20Sopenharmony_ci goto disable_clk; 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci /* 11218c2ecf20Sopenharmony_ci * Convert battery capacity to the corresponding initial coulomb counter 11228c2ecf20Sopenharmony_ci * and set into coulomb counter registers. 11238c2ecf20Sopenharmony_ci */ 11248c2ecf20Sopenharmony_ci data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); 11258c2ecf20Sopenharmony_ci ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt); 11268c2ecf20Sopenharmony_ci if (ret) { 11278c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to initialize coulomb counter\n"); 11288c2ecf20Sopenharmony_ci goto disable_clk; 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci return 0; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_cidisable_clk: 11348c2ecf20Sopenharmony_ci regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 11358c2ecf20Sopenharmony_cidisable_fgu: 11368c2ecf20Sopenharmony_ci regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci return ret; 11398c2ecf20Sopenharmony_ci} 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_cistatic int sc27xx_fgu_probe(struct platform_device *pdev) 11428c2ecf20Sopenharmony_ci{ 11438c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 11448c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 11458c2ecf20Sopenharmony_ci struct power_supply_config fgu_cfg = { }; 11468c2ecf20Sopenharmony_ci struct sc27xx_fgu_data *data; 11478c2ecf20Sopenharmony_ci int ret, irq; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 11508c2ecf20Sopenharmony_ci if (!data) 11518c2ecf20Sopenharmony_ci return -ENOMEM; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci data->regmap = dev_get_regmap(dev->parent, NULL); 11548c2ecf20Sopenharmony_ci if (!data->regmap) { 11558c2ecf20Sopenharmony_ci dev_err(dev, "failed to get regmap\n"); 11568c2ecf20Sopenharmony_ci return -ENODEV; 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci ret = device_property_read_u32(dev, "reg", &data->base); 11608c2ecf20Sopenharmony_ci if (ret) { 11618c2ecf20Sopenharmony_ci dev_err(dev, "failed to get fgu address\n"); 11628c2ecf20Sopenharmony_ci return ret; 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci ret = device_property_read_u32(&pdev->dev, 11668c2ecf20Sopenharmony_ci "sprd,calib-resistance-micro-ohms", 11678c2ecf20Sopenharmony_ci &data->calib_resist); 11688c2ecf20Sopenharmony_ci if (ret) { 11698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 11708c2ecf20Sopenharmony_ci "failed to get fgu calibration resistance\n"); 11718c2ecf20Sopenharmony_ci return ret; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci data->channel = devm_iio_channel_get(dev, "bat-temp"); 11758c2ecf20Sopenharmony_ci if (IS_ERR(data->channel)) { 11768c2ecf20Sopenharmony_ci dev_err(dev, "failed to get IIO channel\n"); 11778c2ecf20Sopenharmony_ci return PTR_ERR(data->channel); 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci data->charge_chan = devm_iio_channel_get(dev, "charge-vol"); 11818c2ecf20Sopenharmony_ci if (IS_ERR(data->charge_chan)) { 11828c2ecf20Sopenharmony_ci dev_err(dev, "failed to get charge IIO channel\n"); 11838c2ecf20Sopenharmony_ci return PTR_ERR(data->charge_chan); 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci data->gpiod = devm_gpiod_get(dev, "bat-detect", GPIOD_IN); 11878c2ecf20Sopenharmony_ci if (IS_ERR(data->gpiod)) { 11888c2ecf20Sopenharmony_ci dev_err(dev, "failed to get battery detection GPIO\n"); 11898c2ecf20Sopenharmony_ci return PTR_ERR(data->gpiod); 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci ret = gpiod_get_value_cansleep(data->gpiod); 11938c2ecf20Sopenharmony_ci if (ret < 0) { 11948c2ecf20Sopenharmony_ci dev_err(dev, "failed to get gpio state\n"); 11958c2ecf20Sopenharmony_ci return ret; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci data->bat_present = !!ret; 11998c2ecf20Sopenharmony_ci mutex_init(&data->lock); 12008c2ecf20Sopenharmony_ci data->dev = dev; 12018c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci fgu_cfg.drv_data = data; 12048c2ecf20Sopenharmony_ci fgu_cfg.of_node = np; 12058c2ecf20Sopenharmony_ci data->battery = devm_power_supply_register(dev, &sc27xx_fgu_desc, 12068c2ecf20Sopenharmony_ci &fgu_cfg); 12078c2ecf20Sopenharmony_ci if (IS_ERR(data->battery)) { 12088c2ecf20Sopenharmony_ci dev_err(dev, "failed to register power supply\n"); 12098c2ecf20Sopenharmony_ci return PTR_ERR(data->battery); 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci ret = sc27xx_fgu_hw_init(data); 12138c2ecf20Sopenharmony_ci if (ret) { 12148c2ecf20Sopenharmony_ci dev_err(dev, "failed to initialize fgu hardware\n"); 12158c2ecf20Sopenharmony_ci return ret; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(dev, sc27xx_fgu_disable, data); 12198c2ecf20Sopenharmony_ci if (ret) { 12208c2ecf20Sopenharmony_ci dev_err(dev, "failed to add fgu disable action\n"); 12218c2ecf20Sopenharmony_ci return ret; 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 12258c2ecf20Sopenharmony_ci if (irq < 0) { 12268c2ecf20Sopenharmony_ci dev_err(dev, "no irq resource specified\n"); 12278c2ecf20Sopenharmony_ci return irq; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(data->dev, irq, NULL, 12318c2ecf20Sopenharmony_ci sc27xx_fgu_interrupt, 12328c2ecf20Sopenharmony_ci IRQF_NO_SUSPEND | IRQF_ONESHOT, 12338c2ecf20Sopenharmony_ci pdev->name, data); 12348c2ecf20Sopenharmony_ci if (ret) { 12358c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to request fgu IRQ\n"); 12368c2ecf20Sopenharmony_ci return ret; 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci irq = gpiod_to_irq(data->gpiod); 12408c2ecf20Sopenharmony_ci if (irq < 0) { 12418c2ecf20Sopenharmony_ci dev_err(dev, "failed to translate GPIO to IRQ\n"); 12428c2ecf20Sopenharmony_ci return irq; 12438c2ecf20Sopenharmony_ci } 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(dev, irq, NULL, 12468c2ecf20Sopenharmony_ci sc27xx_fgu_bat_detection, 12478c2ecf20Sopenharmony_ci IRQF_ONESHOT | IRQF_TRIGGER_RISING | 12488c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING, 12498c2ecf20Sopenharmony_ci pdev->name, data); 12508c2ecf20Sopenharmony_ci if (ret) { 12518c2ecf20Sopenharmony_ci dev_err(dev, "failed to request IRQ\n"); 12528c2ecf20Sopenharmony_ci return ret; 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci return 0; 12568c2ecf20Sopenharmony_ci} 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 12598c2ecf20Sopenharmony_cistatic int sc27xx_fgu_resume(struct device *dev) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 12628c2ecf20Sopenharmony_ci int ret; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 12658c2ecf20Sopenharmony_ci SC27XX_FGU_LOW_OVERLOAD_INT | 12668c2ecf20Sopenharmony_ci SC27XX_FGU_CLBCNT_DELTA_INT, 0); 12678c2ecf20Sopenharmony_ci if (ret) { 12688c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to disable fgu interrupts\n"); 12698c2ecf20Sopenharmony_ci return ret; 12708c2ecf20Sopenharmony_ci } 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci return 0; 12738c2ecf20Sopenharmony_ci} 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_cistatic int sc27xx_fgu_suspend(struct device *dev) 12768c2ecf20Sopenharmony_ci{ 12778c2ecf20Sopenharmony_ci struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 12788c2ecf20Sopenharmony_ci int ret, status, ocv; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_status(data, &status); 12818c2ecf20Sopenharmony_ci if (ret) 12828c2ecf20Sopenharmony_ci return ret; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci /* 12858c2ecf20Sopenharmony_ci * If we are charging, then no need to enable the FGU interrupts to 12868c2ecf20Sopenharmony_ci * adjust the battery capacity. 12878c2ecf20Sopenharmony_ci */ 12888c2ecf20Sopenharmony_ci if (status != POWER_SUPPLY_STATUS_NOT_CHARGING && 12898c2ecf20Sopenharmony_ci status != POWER_SUPPLY_STATUS_DISCHARGING) 12908c2ecf20Sopenharmony_ci return 0; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 12938c2ecf20Sopenharmony_ci SC27XX_FGU_LOW_OVERLOAD_INT, 12948c2ecf20Sopenharmony_ci SC27XX_FGU_LOW_OVERLOAD_INT); 12958c2ecf20Sopenharmony_ci if (ret) { 12968c2ecf20Sopenharmony_ci dev_err(data->dev, "failed to enable low voltage interrupt\n"); 12978c2ecf20Sopenharmony_ci return ret; 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 13018c2ecf20Sopenharmony_ci if (ret) 13028c2ecf20Sopenharmony_ci goto disable_int; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci /* 13058c2ecf20Sopenharmony_ci * If current OCV is less than the minimum voltage, we should enable the 13068c2ecf20Sopenharmony_ci * coulomb counter threshold interrupt to notify events to adjust the 13078c2ecf20Sopenharmony_ci * battery capacity. 13088c2ecf20Sopenharmony_ci */ 13098c2ecf20Sopenharmony_ci if (ocv < data->min_volt) { 13108c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, 13118c2ecf20Sopenharmony_ci data->base + SC27XX_FGU_INT_EN, 13128c2ecf20Sopenharmony_ci SC27XX_FGU_CLBCNT_DELTA_INT, 13138c2ecf20Sopenharmony_ci SC27XX_FGU_CLBCNT_DELTA_INT); 13148c2ecf20Sopenharmony_ci if (ret) { 13158c2ecf20Sopenharmony_ci dev_err(data->dev, 13168c2ecf20Sopenharmony_ci "failed to enable coulomb threshold int\n"); 13178c2ecf20Sopenharmony_ci goto disable_int; 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci return 0; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_cidisable_int: 13248c2ecf20Sopenharmony_ci regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 13258c2ecf20Sopenharmony_ci SC27XX_FGU_LOW_OVERLOAD_INT, 0); 13268c2ecf20Sopenharmony_ci return ret; 13278c2ecf20Sopenharmony_ci} 13288c2ecf20Sopenharmony_ci#endif 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_cistatic const struct dev_pm_ops sc27xx_fgu_pm_ops = { 13318c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume) 13328c2ecf20Sopenharmony_ci}; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_cistatic const struct of_device_id sc27xx_fgu_of_match[] = { 13358c2ecf20Sopenharmony_ci { .compatible = "sprd,sc2731-fgu", }, 13368c2ecf20Sopenharmony_ci { } 13378c2ecf20Sopenharmony_ci}; 13388c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sc27xx_fgu_of_match); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistatic struct platform_driver sc27xx_fgu_driver = { 13418c2ecf20Sopenharmony_ci .probe = sc27xx_fgu_probe, 13428c2ecf20Sopenharmony_ci .driver = { 13438c2ecf20Sopenharmony_ci .name = "sc27xx-fgu", 13448c2ecf20Sopenharmony_ci .of_match_table = sc27xx_fgu_of_match, 13458c2ecf20Sopenharmony_ci .pm = &sc27xx_fgu_pm_ops, 13468c2ecf20Sopenharmony_ci } 13478c2ecf20Sopenharmony_ci}; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_cimodule_platform_driver(sc27xx_fgu_driver); 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver"); 13528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1353