162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (C) 2018 Spreadtrum Communications Inc. 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 562306a36Sopenharmony_ci#include <linux/iio/consumer.h> 662306a36Sopenharmony_ci#include <linux/interrupt.h> 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/math64.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/nvmem-consumer.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/power_supply.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* PMIC global control registers definition */ 1862306a36Sopenharmony_ci#define SC27XX_MODULE_EN0 0xc08 1962306a36Sopenharmony_ci#define SC27XX_CLK_EN0 0xc18 2062306a36Sopenharmony_ci#define SC27XX_FGU_EN BIT(7) 2162306a36Sopenharmony_ci#define SC27XX_FGU_RTC_EN BIT(6) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* FGU registers definition */ 2462306a36Sopenharmony_ci#define SC27XX_FGU_START 0x0 2562306a36Sopenharmony_ci#define SC27XX_FGU_CONFIG 0x4 2662306a36Sopenharmony_ci#define SC27XX_FGU_ADC_CONFIG 0x8 2762306a36Sopenharmony_ci#define SC27XX_FGU_STATUS 0xc 2862306a36Sopenharmony_ci#define SC27XX_FGU_INT_EN 0x10 2962306a36Sopenharmony_ci#define SC27XX_FGU_INT_CLR 0x14 3062306a36Sopenharmony_ci#define SC27XX_FGU_INT_STS 0x1c 3162306a36Sopenharmony_ci#define SC27XX_FGU_VOLTAGE 0x20 3262306a36Sopenharmony_ci#define SC27XX_FGU_OCV 0x24 3362306a36Sopenharmony_ci#define SC27XX_FGU_POCV 0x28 3462306a36Sopenharmony_ci#define SC27XX_FGU_CURRENT 0x2c 3562306a36Sopenharmony_ci#define SC27XX_FGU_LOW_OVERLOAD 0x34 3662306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_SETH 0x50 3762306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_SETL 0x54 3862306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_DELTH 0x58 3962306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_DELTL 0x5c 4062306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_VALH 0x68 4162306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_VALL 0x6c 4262306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_QMAXL 0x74 4362306a36Sopenharmony_ci#define SC27XX_FGU_USER_AREA_SET 0xa0 4462306a36Sopenharmony_ci#define SC27XX_FGU_USER_AREA_CLEAR 0xa4 4562306a36Sopenharmony_ci#define SC27XX_FGU_USER_AREA_STATUS 0xa8 4662306a36Sopenharmony_ci#define SC27XX_FGU_VOLTAGE_BUF 0xd0 4762306a36Sopenharmony_ci#define SC27XX_FGU_CURRENT_BUF 0xf0 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define SC27XX_WRITE_SELCLB_EN BIT(0) 5062306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0) 5162306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_SHIFT 16 5262306a36Sopenharmony_ci#define SC27XX_FGU_LOW_OVERLOAD_MASK GENMASK(12, 0) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define SC27XX_FGU_INT_MASK GENMASK(9, 0) 5562306a36Sopenharmony_ci#define SC27XX_FGU_LOW_OVERLOAD_INT BIT(0) 5662306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_DELTA_INT BIT(2) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define SC27XX_FGU_MODE_AREA_MASK GENMASK(15, 12) 5962306a36Sopenharmony_ci#define SC27XX_FGU_CAP_AREA_MASK GENMASK(11, 0) 6062306a36Sopenharmony_ci#define SC27XX_FGU_MODE_AREA_SHIFT 12 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define SC27XX_FGU_FIRST_POWERTON GENMASK(3, 0) 6362306a36Sopenharmony_ci#define SC27XX_FGU_DEFAULT_CAP GENMASK(11, 0) 6462306a36Sopenharmony_ci#define SC27XX_FGU_NORMAIL_POWERTON 0x5 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define SC27XX_FGU_CUR_BASIC_ADC 8192 6762306a36Sopenharmony_ci#define SC27XX_FGU_SAMPLE_HZ 2 6862306a36Sopenharmony_ci/* micro Ohms */ 6962306a36Sopenharmony_ci#define SC27XX_FGU_IDEAL_RESISTANCE 20000 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* 7262306a36Sopenharmony_ci * struct sc27xx_fgu_data: describe the FGU device 7362306a36Sopenharmony_ci * @regmap: regmap for register access 7462306a36Sopenharmony_ci * @dev: platform device 7562306a36Sopenharmony_ci * @battery: battery power supply 7662306a36Sopenharmony_ci * @base: the base offset for the controller 7762306a36Sopenharmony_ci * @lock: protect the structure 7862306a36Sopenharmony_ci * @gpiod: GPIO for battery detection 7962306a36Sopenharmony_ci * @channel: IIO channel to get battery temperature 8062306a36Sopenharmony_ci * @charge_chan: IIO channel to get charge voltage 8162306a36Sopenharmony_ci * @internal_resist: the battery internal resistance in mOhm 8262306a36Sopenharmony_ci * @total_cap: the total capacity of the battery in mAh 8362306a36Sopenharmony_ci * @init_cap: the initial capacity of the battery in mAh 8462306a36Sopenharmony_ci * @alarm_cap: the alarm capacity 8562306a36Sopenharmony_ci * @init_clbcnt: the initial coulomb counter 8662306a36Sopenharmony_ci * @max_volt: the maximum constant input voltage in millivolt 8762306a36Sopenharmony_ci * @min_volt: the minimum drained battery voltage in microvolt 8862306a36Sopenharmony_ci * @boot_volt: the voltage measured during boot in microvolt 8962306a36Sopenharmony_ci * @table_len: the capacity table length 9062306a36Sopenharmony_ci * @resist_table_len: the resistance table length 9162306a36Sopenharmony_ci * @cur_1000ma_adc: ADC value corresponding to 1000 mA 9262306a36Sopenharmony_ci * @vol_1000mv_adc: ADC value corresponding to 1000 mV 9362306a36Sopenharmony_ci * @calib_resist: the real resistance of coulomb counter chip in uOhm 9462306a36Sopenharmony_ci * @cap_table: capacity table with corresponding ocv 9562306a36Sopenharmony_ci * @resist_table: resistance percent table with corresponding temperature 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistruct sc27xx_fgu_data { 9862306a36Sopenharmony_ci struct regmap *regmap; 9962306a36Sopenharmony_ci struct device *dev; 10062306a36Sopenharmony_ci struct power_supply *battery; 10162306a36Sopenharmony_ci u32 base; 10262306a36Sopenharmony_ci struct mutex lock; 10362306a36Sopenharmony_ci struct gpio_desc *gpiod; 10462306a36Sopenharmony_ci struct iio_channel *channel; 10562306a36Sopenharmony_ci struct iio_channel *charge_chan; 10662306a36Sopenharmony_ci bool bat_present; 10762306a36Sopenharmony_ci int internal_resist; 10862306a36Sopenharmony_ci int total_cap; 10962306a36Sopenharmony_ci int init_cap; 11062306a36Sopenharmony_ci int alarm_cap; 11162306a36Sopenharmony_ci int init_clbcnt; 11262306a36Sopenharmony_ci int max_volt; 11362306a36Sopenharmony_ci int min_volt; 11462306a36Sopenharmony_ci int boot_volt; 11562306a36Sopenharmony_ci int table_len; 11662306a36Sopenharmony_ci int resist_table_len; 11762306a36Sopenharmony_ci int cur_1000ma_adc; 11862306a36Sopenharmony_ci int vol_1000mv_adc; 11962306a36Sopenharmony_ci int calib_resist; 12062306a36Sopenharmony_ci struct power_supply_battery_ocv_table *cap_table; 12162306a36Sopenharmony_ci struct power_supply_resistance_temp_table *resist_table; 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity); 12562306a36Sopenharmony_cistatic void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, 12662306a36Sopenharmony_ci int cap, bool int_mode); 12762306a36Sopenharmony_cistatic void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap); 12862306a36Sopenharmony_cistatic int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic const char * const sc27xx_charger_supply_name[] = { 13162306a36Sopenharmony_ci "sc2731_charger", 13262306a36Sopenharmony_ci "sc2720_charger", 13362306a36Sopenharmony_ci "sc2721_charger", 13462306a36Sopenharmony_ci "sc2723_charger", 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, s64 adc) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci return DIV_S64_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, s64 adc) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci return DIV_S64_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci int ret, status, cap, mode; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ret = regmap_read(data->regmap, 15762306a36Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_STATUS, &status); 15862306a36Sopenharmony_ci if (ret) 15962306a36Sopenharmony_ci return false; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * We use low 4 bits to save the last battery capacity and high 12 bits 16362306a36Sopenharmony_ci * to save the system boot mode. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT; 16662306a36Sopenharmony_ci cap = status & SC27XX_FGU_CAP_AREA_MASK; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * When FGU has been powered down, the user area registers became 17062306a36Sopenharmony_ci * default value (0xffff), which can be used to valid if the system is 17162306a36Sopenharmony_ci * first power on or not. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP) 17462306a36Sopenharmony_ci return true; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return false; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data, 18062306a36Sopenharmony_ci int boot_mode) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci int ret; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, 18562306a36Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_CLEAR, 18662306a36Sopenharmony_ci SC27XX_FGU_MODE_AREA_MASK, 18762306a36Sopenharmony_ci SC27XX_FGU_MODE_AREA_MASK); 18862306a36Sopenharmony_ci if (ret) 18962306a36Sopenharmony_ci return ret; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* 19262306a36Sopenharmony_ci * Since the user area registers are put on power always-on region, 19362306a36Sopenharmony_ci * then these registers changing time will be a little long. Thus 19462306a36Sopenharmony_ci * here we should delay 200us to wait until values are updated 19562306a36Sopenharmony_ci * successfully according to the datasheet. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci udelay(200); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, 20062306a36Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_SET, 20162306a36Sopenharmony_ci SC27XX_FGU_MODE_AREA_MASK, 20262306a36Sopenharmony_ci boot_mode << SC27XX_FGU_MODE_AREA_SHIFT); 20362306a36Sopenharmony_ci if (ret) 20462306a36Sopenharmony_ci return ret; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * Since the user area registers are put on power always-on region, 20862306a36Sopenharmony_ci * then these registers changing time will be a little long. Thus 20962306a36Sopenharmony_ci * here we should delay 200us to wait until values are updated 21062306a36Sopenharmony_ci * successfully according to the datasheet. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci udelay(200); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* 21562306a36Sopenharmony_ci * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to 21662306a36Sopenharmony_ci * make the user area data available, otherwise we can not save the user 21762306a36Sopenharmony_ci * area data. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci return regmap_update_bits(data->regmap, 22062306a36Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_CLEAR, 22162306a36Sopenharmony_ci SC27XX_FGU_MODE_AREA_MASK, 0); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci int ret; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, 22962306a36Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_CLEAR, 23062306a36Sopenharmony_ci SC27XX_FGU_CAP_AREA_MASK, 23162306a36Sopenharmony_ci SC27XX_FGU_CAP_AREA_MASK); 23262306a36Sopenharmony_ci if (ret) 23362306a36Sopenharmony_ci return ret; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* 23662306a36Sopenharmony_ci * Since the user area registers are put on power always-on region, 23762306a36Sopenharmony_ci * then these registers changing time will be a little long. Thus 23862306a36Sopenharmony_ci * here we should delay 200us to wait until values are updated 23962306a36Sopenharmony_ci * successfully according to the datasheet. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci udelay(200); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, 24462306a36Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_SET, 24562306a36Sopenharmony_ci SC27XX_FGU_CAP_AREA_MASK, cap); 24662306a36Sopenharmony_ci if (ret) 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* 25062306a36Sopenharmony_ci * Since the user area registers are put on power always-on region, 25162306a36Sopenharmony_ci * then these registers changing time will be a little long. Thus 25262306a36Sopenharmony_ci * here we should delay 200us to wait until values are updated 25362306a36Sopenharmony_ci * successfully according to the datasheet. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci udelay(200); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* 25862306a36Sopenharmony_ci * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to 25962306a36Sopenharmony_ci * make the user area data available, otherwise we can not save the user 26062306a36Sopenharmony_ci * area data. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ci return regmap_update_bits(data->regmap, 26362306a36Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_CLEAR, 26462306a36Sopenharmony_ci SC27XX_FGU_CAP_AREA_MASK, 0); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci int ret, value; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci ret = regmap_read(data->regmap, 27262306a36Sopenharmony_ci data->base + SC27XX_FGU_USER_AREA_STATUS, &value); 27362306a36Sopenharmony_ci if (ret) 27462306a36Sopenharmony_ci return ret; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci *cap = value & SC27XX_FGU_CAP_AREA_MASK; 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci/* 28162306a36Sopenharmony_ci * When system boots on, we can not read battery capacity from coulomb 28262306a36Sopenharmony_ci * registers, since now the coulomb registers are invalid. So we should 28362306a36Sopenharmony_ci * calculate the battery open circuit voltage, and get current battery 28462306a36Sopenharmony_ci * capacity according to the capacity table. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_cistatic int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci int volt, cur, oci, ocv, ret; 28962306a36Sopenharmony_ci bool is_first_poweron = sc27xx_fgu_is_first_poweron(data); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * If system is not the first power on, we should use the last saved 29362306a36Sopenharmony_ci * battery capacity as the initial battery capacity. Otherwise we should 29462306a36Sopenharmony_ci * re-calculate the initial battery capacity. 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_ci if (!is_first_poweron) { 29762306a36Sopenharmony_ci ret = sc27xx_fgu_read_last_cap(data, cap); 29862306a36Sopenharmony_ci if (ret) 29962306a36Sopenharmony_ci return ret; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* 30562306a36Sopenharmony_ci * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved 30662306a36Sopenharmony_ci * the first sampled open circuit current. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL, 30962306a36Sopenharmony_ci &cur); 31062306a36Sopenharmony_ci if (ret) 31162306a36Sopenharmony_ci return ret; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci cur <<= 1; 31462306a36Sopenharmony_ci oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* 31762306a36Sopenharmony_ci * Should get the OCV from SC27XX_FGU_POCV register at the system 31862306a36Sopenharmony_ci * beginning. It is ADC values reading from registers which need to 31962306a36Sopenharmony_ci * convert the corresponding voltage. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt); 32262306a36Sopenharmony_ci if (ret) 32362306a36Sopenharmony_ci return ret; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci volt = sc27xx_fgu_adc_to_voltage(data, volt); 32662306a36Sopenharmony_ci ocv = volt * 1000 - oci * data->internal_resist; 32762306a36Sopenharmony_ci data->boot_volt = ocv; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* 33062306a36Sopenharmony_ci * Parse the capacity table to look up the correct capacity percent 33162306a36Sopenharmony_ci * according to current battery's corresponding OCV values. 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ci *cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len, 33462306a36Sopenharmony_ci ocv); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci ret = sc27xx_fgu_save_last_cap(data, *cap); 33762306a36Sopenharmony_ci if (ret) 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci int ret; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, 34862306a36Sopenharmony_ci data->base + SC27XX_FGU_CLBCNT_SETL, 34962306a36Sopenharmony_ci SC27XX_FGU_CLBCNT_MASK, clbcnt); 35062306a36Sopenharmony_ci if (ret) 35162306a36Sopenharmony_ci return ret; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, 35462306a36Sopenharmony_ci data->base + SC27XX_FGU_CLBCNT_SETH, 35562306a36Sopenharmony_ci SC27XX_FGU_CLBCNT_MASK, 35662306a36Sopenharmony_ci clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 35762306a36Sopenharmony_ci if (ret) 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START, 36162306a36Sopenharmony_ci SC27XX_WRITE_SELCLB_EN, 36262306a36Sopenharmony_ci SC27XX_WRITE_SELCLB_EN); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci int ccl, cch, ret; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL, 37062306a36Sopenharmony_ci &ccl); 37162306a36Sopenharmony_ci if (ret) 37262306a36Sopenharmony_ci return ret; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH, 37562306a36Sopenharmony_ci &cch); 37662306a36Sopenharmony_ci if (ret) 37762306a36Sopenharmony_ci return ret; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci *clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK; 38062306a36Sopenharmony_ci *clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int sc27xx_fgu_get_vol_now(struct sc27xx_fgu_data *data, int *val) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci int ret; 38862306a36Sopenharmony_ci u32 vol; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE_BUF, 39162306a36Sopenharmony_ci &vol); 39262306a36Sopenharmony_ci if (ret) 39362306a36Sopenharmony_ci return ret; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* 39662306a36Sopenharmony_ci * It is ADC values reading from registers which need to convert to 39762306a36Sopenharmony_ci * corresponding voltage values. 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_ci *val = sc27xx_fgu_adc_to_voltage(data, vol); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci return 0; 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic int sc27xx_fgu_get_cur_now(struct sc27xx_fgu_data *data, int *val) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci int ret; 40762306a36Sopenharmony_ci u32 cur; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT_BUF, 41062306a36Sopenharmony_ci &cur); 41162306a36Sopenharmony_ci if (ret) 41262306a36Sopenharmony_ci return ret; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* 41562306a36Sopenharmony_ci * It is ADC values reading from registers which need to convert to 41662306a36Sopenharmony_ci * corresponding current values. 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Get current coulomb counters firstly */ 42862306a36Sopenharmony_ci ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt); 42962306a36Sopenharmony_ci if (ret) 43062306a36Sopenharmony_ci return ret; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci delta_clbcnt = cur_clbcnt - data->init_clbcnt; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* 43562306a36Sopenharmony_ci * Convert coulomb counter to delta capacity (mAh), and set multiplier 43662306a36Sopenharmony_ci * as 10 to improve the precision. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci temp = DIV_ROUND_CLOSEST(delta_clbcnt * 10, 36 * SC27XX_FGU_SAMPLE_HZ); 43962306a36Sopenharmony_ci temp = sc27xx_fgu_adc_to_current(data, temp / 1000); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* 44262306a36Sopenharmony_ci * Convert to capacity percent of the battery total capacity, 44362306a36Sopenharmony_ci * and multiplier is 100 too. 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap); 44662306a36Sopenharmony_ci *cap = delta_cap + data->init_cap; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* Calibrate the battery capacity in a normal range. */ 44962306a36Sopenharmony_ci sc27xx_fgu_capacity_calibration(data, *cap, false); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci int ret, vol; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol); 45962306a36Sopenharmony_ci if (ret) 46062306a36Sopenharmony_ci return ret; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* 46362306a36Sopenharmony_ci * It is ADC values reading from registers which need to convert to 46462306a36Sopenharmony_ci * corresponding voltage values. 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ci *val = sc27xx_fgu_adc_to_voltage(data, vol); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return 0; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci int ret, cur; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur); 47662306a36Sopenharmony_ci if (ret) 47762306a36Sopenharmony_ci return ret; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* 48062306a36Sopenharmony_ci * It is ADC values reading from registers which need to convert to 48162306a36Sopenharmony_ci * corresponding current values. 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_ci *val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return 0; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci int vol, cur, ret, temp, resistance; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ret = sc27xx_fgu_get_vbat_vol(data, &vol); 49362306a36Sopenharmony_ci if (ret) 49462306a36Sopenharmony_ci return ret; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ret = sc27xx_fgu_get_current(data, &cur); 49762306a36Sopenharmony_ci if (ret) 49862306a36Sopenharmony_ci return ret; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci resistance = data->internal_resist; 50162306a36Sopenharmony_ci if (data->resist_table_len > 0) { 50262306a36Sopenharmony_ci ret = sc27xx_fgu_get_temp(data, &temp); 50362306a36Sopenharmony_ci if (ret) 50462306a36Sopenharmony_ci return ret; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci resistance = power_supply_temp2resist_simple(data->resist_table, 50762306a36Sopenharmony_ci data->resist_table_len, temp); 50862306a36Sopenharmony_ci resistance = data->internal_resist * resistance / 100; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* Return the battery OCV in micro volts. */ 51262306a36Sopenharmony_ci *val = vol * 1000 - cur * resistance; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int sc27xx_fgu_get_charge_vol(struct sc27xx_fgu_data *data, int *val) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci int ret, vol; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci ret = iio_read_channel_processed(data->charge_chan, &vol); 52262306a36Sopenharmony_ci if (ret < 0) 52362306a36Sopenharmony_ci return ret; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci *val = vol * 1000; 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci return iio_read_channel_processed(data->channel, temp); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci int ret, vol; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci ret = sc27xx_fgu_get_vbat_vol(data, &vol); 53962306a36Sopenharmony_ci if (ret) 54062306a36Sopenharmony_ci return ret; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (vol > data->max_volt) 54362306a36Sopenharmony_ci *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 54462306a36Sopenharmony_ci else 54562306a36Sopenharmony_ci *health = POWER_SUPPLY_HEALTH_GOOD; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci return 0; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci union power_supply_propval val; 55362306a36Sopenharmony_ci struct power_supply *psy; 55462306a36Sopenharmony_ci int i, ret = -EINVAL; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) { 55762306a36Sopenharmony_ci psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]); 55862306a36Sopenharmony_ci if (!psy) 55962306a36Sopenharmony_ci continue; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, 56262306a36Sopenharmony_ci &val); 56362306a36Sopenharmony_ci power_supply_put(psy); 56462306a36Sopenharmony_ci if (ret) 56562306a36Sopenharmony_ci return ret; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci *status = val.intval; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return ret; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic int sc27xx_fgu_get_property(struct power_supply *psy, 57462306a36Sopenharmony_ci enum power_supply_property psp, 57562306a36Sopenharmony_ci union power_supply_propval *val) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 57862306a36Sopenharmony_ci int ret = 0; 57962306a36Sopenharmony_ci int value; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci mutex_lock(&data->lock); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci switch (psp) { 58462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 58562306a36Sopenharmony_ci ret = sc27xx_fgu_get_status(data, &value); 58662306a36Sopenharmony_ci if (ret) 58762306a36Sopenharmony_ci goto error; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci val->intval = value; 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 59362306a36Sopenharmony_ci ret = sc27xx_fgu_get_health(data, &value); 59462306a36Sopenharmony_ci if (ret) 59562306a36Sopenharmony_ci goto error; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci val->intval = value; 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 60162306a36Sopenharmony_ci val->intval = data->bat_present; 60262306a36Sopenharmony_ci break; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 60562306a36Sopenharmony_ci ret = sc27xx_fgu_get_temp(data, &value); 60662306a36Sopenharmony_ci if (ret) 60762306a36Sopenharmony_ci goto error; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci val->intval = value; 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 61362306a36Sopenharmony_ci val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 61762306a36Sopenharmony_ci ret = sc27xx_fgu_get_capacity(data, &value); 61862306a36Sopenharmony_ci if (ret) 61962306a36Sopenharmony_ci goto error; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci val->intval = value; 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_AVG: 62562306a36Sopenharmony_ci ret = sc27xx_fgu_get_vbat_vol(data, &value); 62662306a36Sopenharmony_ci if (ret) 62762306a36Sopenharmony_ci goto error; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci val->intval = value * 1000; 63062306a36Sopenharmony_ci break; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_OCV: 63362306a36Sopenharmony_ci ret = sc27xx_fgu_get_vbat_ocv(data, &value); 63462306a36Sopenharmony_ci if (ret) 63562306a36Sopenharmony_ci goto error; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci val->intval = value; 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 64162306a36Sopenharmony_ci ret = sc27xx_fgu_get_charge_vol(data, &value); 64262306a36Sopenharmony_ci if (ret) 64362306a36Sopenharmony_ci goto error; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci val->intval = value; 64662306a36Sopenharmony_ci break; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_AVG: 64962306a36Sopenharmony_ci ret = sc27xx_fgu_get_current(data, &value); 65062306a36Sopenharmony_ci if (ret) 65162306a36Sopenharmony_ci goto error; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci val->intval = value * 1000; 65462306a36Sopenharmony_ci break; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 65762306a36Sopenharmony_ci val->intval = data->total_cap * 1000; 65862306a36Sopenharmony_ci break; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_NOW: 66162306a36Sopenharmony_ci ret = sc27xx_fgu_get_clbcnt(data, &value); 66262306a36Sopenharmony_ci if (ret) 66362306a36Sopenharmony_ci goto error; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci value = DIV_ROUND_CLOSEST(value * 10, 66662306a36Sopenharmony_ci 36 * SC27XX_FGU_SAMPLE_HZ); 66762306a36Sopenharmony_ci val->intval = sc27xx_fgu_adc_to_current(data, value); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 67262306a36Sopenharmony_ci ret = sc27xx_fgu_get_vol_now(data, &value); 67362306a36Sopenharmony_ci if (ret) 67462306a36Sopenharmony_ci goto error; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci val->intval = value * 1000; 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 68062306a36Sopenharmony_ci ret = sc27xx_fgu_get_cur_now(data, &value); 68162306a36Sopenharmony_ci if (ret) 68262306a36Sopenharmony_ci goto error; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci val->intval = value * 1000; 68562306a36Sopenharmony_ci break; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_BOOT: 68862306a36Sopenharmony_ci val->intval = data->boot_volt; 68962306a36Sopenharmony_ci break; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci default: 69262306a36Sopenharmony_ci ret = -EINVAL; 69362306a36Sopenharmony_ci break; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cierror: 69762306a36Sopenharmony_ci mutex_unlock(&data->lock); 69862306a36Sopenharmony_ci return ret; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int sc27xx_fgu_set_property(struct power_supply *psy, 70262306a36Sopenharmony_ci enum power_supply_property psp, 70362306a36Sopenharmony_ci const union power_supply_propval *val) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy); 70662306a36Sopenharmony_ci int ret; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci mutex_lock(&data->lock); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci switch (psp) { 71162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 71262306a36Sopenharmony_ci ret = sc27xx_fgu_save_last_cap(data, val->intval); 71362306a36Sopenharmony_ci if (ret < 0) 71462306a36Sopenharmony_ci dev_err(data->dev, "failed to save battery capacity\n"); 71562306a36Sopenharmony_ci break; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CALIBRATE: 71862306a36Sopenharmony_ci sc27xx_fgu_adjust_cap(data, val->intval); 71962306a36Sopenharmony_ci ret = 0; 72062306a36Sopenharmony_ci break; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 72362306a36Sopenharmony_ci data->total_cap = val->intval / 1000; 72462306a36Sopenharmony_ci ret = 0; 72562306a36Sopenharmony_ci break; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci default: 72862306a36Sopenharmony_ci ret = -EINVAL; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci mutex_unlock(&data->lock); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return ret; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic int sc27xx_fgu_property_is_writeable(struct power_supply *psy, 73762306a36Sopenharmony_ci enum power_supply_property psp) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci return psp == POWER_SUPPLY_PROP_CAPACITY || 74062306a36Sopenharmony_ci psp == POWER_SUPPLY_PROP_CALIBRATE || 74162306a36Sopenharmony_ci psp == POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic enum power_supply_property sc27xx_fgu_props[] = { 74562306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 74662306a36Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 74762306a36Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 74862306a36Sopenharmony_ci POWER_SUPPLY_PROP_TEMP, 74962306a36Sopenharmony_ci POWER_SUPPLY_PROP_TECHNOLOGY, 75062306a36Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 75162306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 75262306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_OCV, 75362306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_AVG, 75462306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_BOOT, 75562306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 75662306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_AVG, 75762306a36Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 75862306a36Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 75962306a36Sopenharmony_ci POWER_SUPPLY_PROP_CALIBRATE, 76062306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_NOW 76162306a36Sopenharmony_ci}; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic const struct power_supply_desc sc27xx_fgu_desc = { 76462306a36Sopenharmony_ci .name = "sc27xx-fgu", 76562306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 76662306a36Sopenharmony_ci .properties = sc27xx_fgu_props, 76762306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(sc27xx_fgu_props), 76862306a36Sopenharmony_ci .get_property = sc27xx_fgu_get_property, 76962306a36Sopenharmony_ci .set_property = sc27xx_fgu_set_property, 77062306a36Sopenharmony_ci .external_power_changed = power_supply_changed, 77162306a36Sopenharmony_ci .property_is_writeable = sc27xx_fgu_property_is_writeable, 77262306a36Sopenharmony_ci .no_thermal = true, 77362306a36Sopenharmony_ci}; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci int ret; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci data->init_cap = cap; 78062306a36Sopenharmony_ci ret = sc27xx_fgu_get_clbcnt(data, &data->init_clbcnt); 78162306a36Sopenharmony_ci if (ret) 78262306a36Sopenharmony_ci dev_err(data->dev, "failed to get init coulomb counter\n"); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, 78662306a36Sopenharmony_ci int cap, bool int_mode) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci int ret, ocv, chg_sts, adc; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 79162306a36Sopenharmony_ci if (ret) { 79262306a36Sopenharmony_ci dev_err(data->dev, "get battery ocv error.\n"); 79362306a36Sopenharmony_ci return; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci ret = sc27xx_fgu_get_status(data, &chg_sts); 79762306a36Sopenharmony_ci if (ret) { 79862306a36Sopenharmony_ci dev_err(data->dev, "get charger status error.\n"); 79962306a36Sopenharmony_ci return; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* 80362306a36Sopenharmony_ci * If we are in charging mode, then we do not need to calibrate the 80462306a36Sopenharmony_ci * lower capacity. 80562306a36Sopenharmony_ci */ 80662306a36Sopenharmony_ci if (chg_sts == POWER_SUPPLY_STATUS_CHARGING) 80762306a36Sopenharmony_ci return; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if ((ocv > data->cap_table[0].ocv && cap < 100) || cap > 100) { 81062306a36Sopenharmony_ci /* 81162306a36Sopenharmony_ci * If current OCV value is larger than the max OCV value in 81262306a36Sopenharmony_ci * OCV table, or the current capacity is larger than 100, 81362306a36Sopenharmony_ci * we should force the inititial capacity to 100. 81462306a36Sopenharmony_ci */ 81562306a36Sopenharmony_ci sc27xx_fgu_adjust_cap(data, 100); 81662306a36Sopenharmony_ci } else if (ocv <= data->cap_table[data->table_len - 1].ocv) { 81762306a36Sopenharmony_ci /* 81862306a36Sopenharmony_ci * If current OCV value is leass than the minimum OCV value in 81962306a36Sopenharmony_ci * OCV table, we should force the inititial capacity to 0. 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_ci sc27xx_fgu_adjust_cap(data, 0); 82262306a36Sopenharmony_ci } else if ((ocv > data->cap_table[data->table_len - 1].ocv && cap <= 0) || 82362306a36Sopenharmony_ci (ocv > data->min_volt && cap <= data->alarm_cap)) { 82462306a36Sopenharmony_ci /* 82562306a36Sopenharmony_ci * If current OCV value is not matchable with current capacity, 82662306a36Sopenharmony_ci * we should re-calculate current capacity by looking up the 82762306a36Sopenharmony_ci * OCV table. 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_ci int cur_cap = power_supply_ocv2cap_simple(data->cap_table, 83062306a36Sopenharmony_ci data->table_len, ocv); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci sc27xx_fgu_adjust_cap(data, cur_cap); 83362306a36Sopenharmony_ci } else if (ocv <= data->min_volt) { 83462306a36Sopenharmony_ci /* 83562306a36Sopenharmony_ci * If current OCV value is less than the low alarm voltage, but 83662306a36Sopenharmony_ci * current capacity is larger than the alarm capacity, we should 83762306a36Sopenharmony_ci * adjust the inititial capacity to alarm capacity. 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_ci if (cap > data->alarm_cap) { 84062306a36Sopenharmony_ci sc27xx_fgu_adjust_cap(data, data->alarm_cap); 84162306a36Sopenharmony_ci } else { 84262306a36Sopenharmony_ci int cur_cap; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* 84562306a36Sopenharmony_ci * If current capacity is equal with 0 or less than 0 84662306a36Sopenharmony_ci * (some error occurs), we should adjust inititial 84762306a36Sopenharmony_ci * capacity to the capacity corresponding to current OCV 84862306a36Sopenharmony_ci * value. 84962306a36Sopenharmony_ci */ 85062306a36Sopenharmony_ci cur_cap = power_supply_ocv2cap_simple(data->cap_table, 85162306a36Sopenharmony_ci data->table_len, 85262306a36Sopenharmony_ci ocv); 85362306a36Sopenharmony_ci sc27xx_fgu_adjust_cap(data, cur_cap); 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (!int_mode) 85762306a36Sopenharmony_ci return; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* 86062306a36Sopenharmony_ci * After adjusting the battery capacity, we should set the 86162306a36Sopenharmony_ci * lowest alarm voltage instead. 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_ci data->min_volt = data->cap_table[data->table_len - 1].ocv; 86462306a36Sopenharmony_ci data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, 86562306a36Sopenharmony_ci data->table_len, 86662306a36Sopenharmony_ci data->min_volt); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 86962306a36Sopenharmony_ci regmap_update_bits(data->regmap, 87062306a36Sopenharmony_ci data->base + SC27XX_FGU_LOW_OVERLOAD, 87162306a36Sopenharmony_ci SC27XX_FGU_LOW_OVERLOAD_MASK, adc); 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct sc27xx_fgu_data *data = dev_id; 87862306a36Sopenharmony_ci int ret, cap; 87962306a36Sopenharmony_ci u32 status; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci mutex_lock(&data->lock); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS, 88462306a36Sopenharmony_ci &status); 88562306a36Sopenharmony_ci if (ret) 88662306a36Sopenharmony_ci goto out; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 88962306a36Sopenharmony_ci status, status); 89062306a36Sopenharmony_ci if (ret) 89162306a36Sopenharmony_ci goto out; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci /* 89462306a36Sopenharmony_ci * When low overload voltage interrupt happens, we should calibrate the 89562306a36Sopenharmony_ci * battery capacity in lower voltage stage. 89662306a36Sopenharmony_ci */ 89762306a36Sopenharmony_ci if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT)) 89862306a36Sopenharmony_ci goto out; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci ret = sc27xx_fgu_get_capacity(data, &cap); 90162306a36Sopenharmony_ci if (ret) 90262306a36Sopenharmony_ci goto out; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci sc27xx_fgu_capacity_calibration(data, cap, true); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ciout: 90762306a36Sopenharmony_ci mutex_unlock(&data->lock); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci power_supply_changed(data->battery); 91062306a36Sopenharmony_ci return IRQ_HANDLED; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct sc27xx_fgu_data *data = dev_id; 91662306a36Sopenharmony_ci int state; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci mutex_lock(&data->lock); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci state = gpiod_get_value_cansleep(data->gpiod); 92162306a36Sopenharmony_ci if (state < 0) { 92262306a36Sopenharmony_ci dev_err(data->dev, "failed to get gpio state\n"); 92362306a36Sopenharmony_ci mutex_unlock(&data->lock); 92462306a36Sopenharmony_ci return IRQ_RETVAL(state); 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci data->bat_present = !!state; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci mutex_unlock(&data->lock); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci power_supply_changed(data->battery); 93262306a36Sopenharmony_ci return IRQ_HANDLED; 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic void sc27xx_fgu_disable(void *_data) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci struct sc27xx_fgu_data *data = _data; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 94062306a36Sopenharmony_ci regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci /* 94662306a36Sopenharmony_ci * Get current capacity (mAh) = battery total capacity (mAh) * 94762306a36Sopenharmony_ci * current capacity percent (capacity / 100). 94862306a36Sopenharmony_ci */ 94962306a36Sopenharmony_ci int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* 95262306a36Sopenharmony_ci * Convert current capacity (mAh) to coulomb counter according to the 95362306a36Sopenharmony_ci * formula: 1 mAh =3.6 coulomb. 95462306a36Sopenharmony_ci */ 95562306a36Sopenharmony_ci return DIV_ROUND_CLOSEST(cur_cap * 36 * data->cur_1000ma_adc * SC27XX_FGU_SAMPLE_HZ, 10); 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_cistatic int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) 95962306a36Sopenharmony_ci{ 96062306a36Sopenharmony_ci struct nvmem_cell *cell; 96162306a36Sopenharmony_ci int calib_data, cal_4200mv; 96262306a36Sopenharmony_ci void *buf; 96362306a36Sopenharmony_ci size_t len; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci cell = nvmem_cell_get(data->dev, "fgu_calib"); 96662306a36Sopenharmony_ci if (IS_ERR(cell)) 96762306a36Sopenharmony_ci return PTR_ERR(cell); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci buf = nvmem_cell_read(cell, &len); 97062306a36Sopenharmony_ci nvmem_cell_put(cell); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci if (IS_ERR(buf)) 97362306a36Sopenharmony_ci return PTR_ERR(buf); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci memcpy(&calib_data, buf, min(len, sizeof(u32))); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci /* 97862306a36Sopenharmony_ci * Get the ADC value corresponding to 4200 mV from eFuse controller 97962306a36Sopenharmony_ci * according to below formula. Then convert to ADC values corresponding 98062306a36Sopenharmony_ci * to 1000 mV and 1000 mA. 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_ci cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256; 98362306a36Sopenharmony_ci data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42); 98462306a36Sopenharmony_ci data->cur_1000ma_adc = 98562306a36Sopenharmony_ci DIV_ROUND_CLOSEST(data->vol_1000mv_adc * 4 * data->calib_resist, 98662306a36Sopenharmony_ci SC27XX_FGU_IDEAL_RESISTANCE); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci kfree(buf); 98962306a36Sopenharmony_ci return 0; 99062306a36Sopenharmony_ci} 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cistatic int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) 99362306a36Sopenharmony_ci{ 99462306a36Sopenharmony_ci struct power_supply_battery_info *info; 99562306a36Sopenharmony_ci struct power_supply_battery_ocv_table *table; 99662306a36Sopenharmony_ci int ret, delta_clbcnt, alarm_adc; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci ret = power_supply_get_battery_info(data->battery, &info); 99962306a36Sopenharmony_ci if (ret) { 100062306a36Sopenharmony_ci dev_err(data->dev, "failed to get battery information\n"); 100162306a36Sopenharmony_ci return ret; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci data->total_cap = info->charge_full_design_uah / 1000; 100562306a36Sopenharmony_ci data->max_volt = info->constant_charge_voltage_max_uv / 1000; 100662306a36Sopenharmony_ci data->internal_resist = info->factory_internal_resistance_uohm / 1000; 100762306a36Sopenharmony_ci data->min_volt = info->voltage_min_design_uv; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci /* 101062306a36Sopenharmony_ci * For SC27XX fuel gauge device, we only use one ocv-capacity 101162306a36Sopenharmony_ci * table in normal temperature 20 Celsius. 101262306a36Sopenharmony_ci */ 101362306a36Sopenharmony_ci table = power_supply_find_ocv2cap_table(info, 20, &data->table_len); 101462306a36Sopenharmony_ci if (!table) 101562306a36Sopenharmony_ci return -EINVAL; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci data->cap_table = devm_kmemdup(data->dev, table, 101862306a36Sopenharmony_ci data->table_len * sizeof(*table), 101962306a36Sopenharmony_ci GFP_KERNEL); 102062306a36Sopenharmony_ci if (!data->cap_table) { 102162306a36Sopenharmony_ci power_supply_put_battery_info(data->battery, info); 102262306a36Sopenharmony_ci return -ENOMEM; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, 102662306a36Sopenharmony_ci data->table_len, 102762306a36Sopenharmony_ci data->min_volt); 102862306a36Sopenharmony_ci if (!data->alarm_cap) 102962306a36Sopenharmony_ci data->alarm_cap += 1; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci data->resist_table_len = info->resist_table_size; 103262306a36Sopenharmony_ci if (data->resist_table_len > 0) { 103362306a36Sopenharmony_ci data->resist_table = devm_kmemdup(data->dev, info->resist_table, 103462306a36Sopenharmony_ci data->resist_table_len * 103562306a36Sopenharmony_ci sizeof(struct power_supply_resistance_temp_table), 103662306a36Sopenharmony_ci GFP_KERNEL); 103762306a36Sopenharmony_ci if (!data->resist_table) { 103862306a36Sopenharmony_ci power_supply_put_battery_info(data->battery, info); 103962306a36Sopenharmony_ci return -ENOMEM; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci power_supply_put_battery_info(data->battery, info); 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci ret = sc27xx_fgu_calibration(data); 104662306a36Sopenharmony_ci if (ret) 104762306a36Sopenharmony_ci return ret; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci /* Enable the FGU module */ 105062306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, 105162306a36Sopenharmony_ci SC27XX_FGU_EN, SC27XX_FGU_EN); 105262306a36Sopenharmony_ci if (ret) { 105362306a36Sopenharmony_ci dev_err(data->dev, "failed to enable fgu\n"); 105462306a36Sopenharmony_ci return ret; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci /* Enable the FGU RTC clock to make it work */ 105862306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0, 105962306a36Sopenharmony_ci SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN); 106062306a36Sopenharmony_ci if (ret) { 106162306a36Sopenharmony_ci dev_err(data->dev, "failed to enable fgu RTC clock\n"); 106262306a36Sopenharmony_ci goto disable_fgu; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR, 106662306a36Sopenharmony_ci SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK); 106762306a36Sopenharmony_ci if (ret) { 106862306a36Sopenharmony_ci dev_err(data->dev, "failed to clear interrupt status\n"); 106962306a36Sopenharmony_ci goto disable_clk; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci /* 107362306a36Sopenharmony_ci * Set the voltage low overload threshold, which means when the battery 107462306a36Sopenharmony_ci * voltage is lower than this threshold, the controller will generate 107562306a36Sopenharmony_ci * one interrupt to notify. 107662306a36Sopenharmony_ci */ 107762306a36Sopenharmony_ci alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); 107862306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, 107962306a36Sopenharmony_ci SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc); 108062306a36Sopenharmony_ci if (ret) { 108162306a36Sopenharmony_ci dev_err(data->dev, "failed to set fgu low overload\n"); 108262306a36Sopenharmony_ci goto disable_clk; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci /* 108662306a36Sopenharmony_ci * Set the coulomb counter delta threshold, that means when the coulomb 108762306a36Sopenharmony_ci * counter change is multiples of the delta threshold, the controller 108862306a36Sopenharmony_ci * will generate one interrupt to notify the users to update the battery 108962306a36Sopenharmony_ci * capacity. Now we set the delta threshold as a counter value of 1% 109062306a36Sopenharmony_ci * capacity. 109162306a36Sopenharmony_ci */ 109262306a36Sopenharmony_ci delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL, 109562306a36Sopenharmony_ci SC27XX_FGU_CLBCNT_MASK, delta_clbcnt); 109662306a36Sopenharmony_ci if (ret) { 109762306a36Sopenharmony_ci dev_err(data->dev, "failed to set low delta coulomb counter\n"); 109862306a36Sopenharmony_ci goto disable_clk; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH, 110262306a36Sopenharmony_ci SC27XX_FGU_CLBCNT_MASK, 110362306a36Sopenharmony_ci delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT); 110462306a36Sopenharmony_ci if (ret) { 110562306a36Sopenharmony_ci dev_err(data->dev, "failed to set high delta coulomb counter\n"); 110662306a36Sopenharmony_ci goto disable_clk; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci /* 111062306a36Sopenharmony_ci * Get the boot battery capacity when system powers on, which is used to 111162306a36Sopenharmony_ci * initialize the coulomb counter. After that, we can read the coulomb 111262306a36Sopenharmony_ci * counter to measure the battery capacity. 111362306a36Sopenharmony_ci */ 111462306a36Sopenharmony_ci ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap); 111562306a36Sopenharmony_ci if (ret) { 111662306a36Sopenharmony_ci dev_err(data->dev, "failed to get boot capacity\n"); 111762306a36Sopenharmony_ci goto disable_clk; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci /* 112162306a36Sopenharmony_ci * Convert battery capacity to the corresponding initial coulomb counter 112262306a36Sopenharmony_ci * and set into coulomb counter registers. 112362306a36Sopenharmony_ci */ 112462306a36Sopenharmony_ci data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); 112562306a36Sopenharmony_ci ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt); 112662306a36Sopenharmony_ci if (ret) { 112762306a36Sopenharmony_ci dev_err(data->dev, "failed to initialize coulomb counter\n"); 112862306a36Sopenharmony_ci goto disable_clk; 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci return 0; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_cidisable_clk: 113462306a36Sopenharmony_ci regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0); 113562306a36Sopenharmony_cidisable_fgu: 113662306a36Sopenharmony_ci regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci return ret; 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_cistatic int sc27xx_fgu_probe(struct platform_device *pdev) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 114462306a36Sopenharmony_ci struct device_node *np = dev->of_node; 114562306a36Sopenharmony_ci struct power_supply_config fgu_cfg = { }; 114662306a36Sopenharmony_ci struct sc27xx_fgu_data *data; 114762306a36Sopenharmony_ci int ret, irq; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 115062306a36Sopenharmony_ci if (!data) 115162306a36Sopenharmony_ci return -ENOMEM; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci data->regmap = dev_get_regmap(dev->parent, NULL); 115462306a36Sopenharmony_ci if (!data->regmap) { 115562306a36Sopenharmony_ci dev_err(dev, "failed to get regmap\n"); 115662306a36Sopenharmony_ci return -ENODEV; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci ret = device_property_read_u32(dev, "reg", &data->base); 116062306a36Sopenharmony_ci if (ret) { 116162306a36Sopenharmony_ci dev_err(dev, "failed to get fgu address\n"); 116262306a36Sopenharmony_ci return ret; 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci ret = device_property_read_u32(&pdev->dev, 116662306a36Sopenharmony_ci "sprd,calib-resistance-micro-ohms", 116762306a36Sopenharmony_ci &data->calib_resist); 116862306a36Sopenharmony_ci if (ret) { 116962306a36Sopenharmony_ci dev_err(&pdev->dev, 117062306a36Sopenharmony_ci "failed to get fgu calibration resistance\n"); 117162306a36Sopenharmony_ci return ret; 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci data->channel = devm_iio_channel_get(dev, "bat-temp"); 117562306a36Sopenharmony_ci if (IS_ERR(data->channel)) { 117662306a36Sopenharmony_ci dev_err(dev, "failed to get IIO channel\n"); 117762306a36Sopenharmony_ci return PTR_ERR(data->channel); 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci data->charge_chan = devm_iio_channel_get(dev, "charge-vol"); 118162306a36Sopenharmony_ci if (IS_ERR(data->charge_chan)) { 118262306a36Sopenharmony_ci dev_err(dev, "failed to get charge IIO channel\n"); 118362306a36Sopenharmony_ci return PTR_ERR(data->charge_chan); 118462306a36Sopenharmony_ci } 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci data->gpiod = devm_gpiod_get(dev, "bat-detect", GPIOD_IN); 118762306a36Sopenharmony_ci if (IS_ERR(data->gpiod)) { 118862306a36Sopenharmony_ci dev_err(dev, "failed to get battery detection GPIO\n"); 118962306a36Sopenharmony_ci return PTR_ERR(data->gpiod); 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci ret = gpiod_get_value_cansleep(data->gpiod); 119362306a36Sopenharmony_ci if (ret < 0) { 119462306a36Sopenharmony_ci dev_err(dev, "failed to get gpio state\n"); 119562306a36Sopenharmony_ci return ret; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci data->bat_present = !!ret; 119962306a36Sopenharmony_ci mutex_init(&data->lock); 120062306a36Sopenharmony_ci data->dev = dev; 120162306a36Sopenharmony_ci platform_set_drvdata(pdev, data); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci fgu_cfg.drv_data = data; 120462306a36Sopenharmony_ci fgu_cfg.of_node = np; 120562306a36Sopenharmony_ci data->battery = devm_power_supply_register(dev, &sc27xx_fgu_desc, 120662306a36Sopenharmony_ci &fgu_cfg); 120762306a36Sopenharmony_ci if (IS_ERR(data->battery)) { 120862306a36Sopenharmony_ci dev_err(dev, "failed to register power supply\n"); 120962306a36Sopenharmony_ci return PTR_ERR(data->battery); 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci ret = sc27xx_fgu_hw_init(data); 121362306a36Sopenharmony_ci if (ret) { 121462306a36Sopenharmony_ci dev_err(dev, "failed to initialize fgu hardware\n"); 121562306a36Sopenharmony_ci return ret; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci ret = devm_add_action_or_reset(dev, sc27xx_fgu_disable, data); 121962306a36Sopenharmony_ci if (ret) { 122062306a36Sopenharmony_ci dev_err(dev, "failed to add fgu disable action\n"); 122162306a36Sopenharmony_ci return ret; 122262306a36Sopenharmony_ci } 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 122562306a36Sopenharmony_ci if (irq < 0) 122662306a36Sopenharmony_ci return irq; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci ret = devm_request_threaded_irq(data->dev, irq, NULL, 122962306a36Sopenharmony_ci sc27xx_fgu_interrupt, 123062306a36Sopenharmony_ci IRQF_NO_SUSPEND | IRQF_ONESHOT, 123162306a36Sopenharmony_ci pdev->name, data); 123262306a36Sopenharmony_ci if (ret) { 123362306a36Sopenharmony_ci dev_err(data->dev, "failed to request fgu IRQ\n"); 123462306a36Sopenharmony_ci return ret; 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci irq = gpiod_to_irq(data->gpiod); 123862306a36Sopenharmony_ci if (irq < 0) { 123962306a36Sopenharmony_ci dev_err(dev, "failed to translate GPIO to IRQ\n"); 124062306a36Sopenharmony_ci return irq; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, irq, NULL, 124462306a36Sopenharmony_ci sc27xx_fgu_bat_detection, 124562306a36Sopenharmony_ci IRQF_ONESHOT | IRQF_TRIGGER_RISING | 124662306a36Sopenharmony_ci IRQF_TRIGGER_FALLING, 124762306a36Sopenharmony_ci pdev->name, data); 124862306a36Sopenharmony_ci if (ret) { 124962306a36Sopenharmony_ci dev_err(dev, "failed to request IRQ\n"); 125062306a36Sopenharmony_ci return ret; 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci return 0; 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 125762306a36Sopenharmony_cistatic int sc27xx_fgu_resume(struct device *dev) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 126062306a36Sopenharmony_ci int ret; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 126362306a36Sopenharmony_ci SC27XX_FGU_LOW_OVERLOAD_INT | 126462306a36Sopenharmony_ci SC27XX_FGU_CLBCNT_DELTA_INT, 0); 126562306a36Sopenharmony_ci if (ret) { 126662306a36Sopenharmony_ci dev_err(data->dev, "failed to disable fgu interrupts\n"); 126762306a36Sopenharmony_ci return ret; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci return 0; 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_cistatic int sc27xx_fgu_suspend(struct device *dev) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci struct sc27xx_fgu_data *data = dev_get_drvdata(dev); 127662306a36Sopenharmony_ci int ret, status, ocv; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci ret = sc27xx_fgu_get_status(data, &status); 127962306a36Sopenharmony_ci if (ret) 128062306a36Sopenharmony_ci return ret; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci /* 128362306a36Sopenharmony_ci * If we are charging, then no need to enable the FGU interrupts to 128462306a36Sopenharmony_ci * adjust the battery capacity. 128562306a36Sopenharmony_ci */ 128662306a36Sopenharmony_ci if (status != POWER_SUPPLY_STATUS_NOT_CHARGING && 128762306a36Sopenharmony_ci status != POWER_SUPPLY_STATUS_DISCHARGING) 128862306a36Sopenharmony_ci return 0; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 129162306a36Sopenharmony_ci SC27XX_FGU_LOW_OVERLOAD_INT, 129262306a36Sopenharmony_ci SC27XX_FGU_LOW_OVERLOAD_INT); 129362306a36Sopenharmony_ci if (ret) { 129462306a36Sopenharmony_ci dev_err(data->dev, "failed to enable low voltage interrupt\n"); 129562306a36Sopenharmony_ci return ret; 129662306a36Sopenharmony_ci } 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); 129962306a36Sopenharmony_ci if (ret) 130062306a36Sopenharmony_ci goto disable_int; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci /* 130362306a36Sopenharmony_ci * If current OCV is less than the minimum voltage, we should enable the 130462306a36Sopenharmony_ci * coulomb counter threshold interrupt to notify events to adjust the 130562306a36Sopenharmony_ci * battery capacity. 130662306a36Sopenharmony_ci */ 130762306a36Sopenharmony_ci if (ocv < data->min_volt) { 130862306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, 130962306a36Sopenharmony_ci data->base + SC27XX_FGU_INT_EN, 131062306a36Sopenharmony_ci SC27XX_FGU_CLBCNT_DELTA_INT, 131162306a36Sopenharmony_ci SC27XX_FGU_CLBCNT_DELTA_INT); 131262306a36Sopenharmony_ci if (ret) { 131362306a36Sopenharmony_ci dev_err(data->dev, 131462306a36Sopenharmony_ci "failed to enable coulomb threshold int\n"); 131562306a36Sopenharmony_ci goto disable_int; 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci return 0; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_cidisable_int: 132262306a36Sopenharmony_ci regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN, 132362306a36Sopenharmony_ci SC27XX_FGU_LOW_OVERLOAD_INT, 0); 132462306a36Sopenharmony_ci return ret; 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci#endif 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_cistatic const struct dev_pm_ops sc27xx_fgu_pm_ops = { 132962306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume) 133062306a36Sopenharmony_ci}; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_cistatic const struct of_device_id sc27xx_fgu_of_match[] = { 133362306a36Sopenharmony_ci { .compatible = "sprd,sc2731-fgu", }, 133462306a36Sopenharmony_ci { } 133562306a36Sopenharmony_ci}; 133662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sc27xx_fgu_of_match); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_cistatic struct platform_driver sc27xx_fgu_driver = { 133962306a36Sopenharmony_ci .probe = sc27xx_fgu_probe, 134062306a36Sopenharmony_ci .driver = { 134162306a36Sopenharmony_ci .name = "sc27xx-fgu", 134262306a36Sopenharmony_ci .of_match_table = sc27xx_fgu_of_match, 134362306a36Sopenharmony_ci .pm = &sc27xx_fgu_pm_ops, 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci}; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_cimodule_platform_driver(sc27xx_fgu_driver); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ciMODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver"); 135062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1351