// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2018 Spreadtrum Communications Inc.
#include <linux/gpio/consumer.h>
#include <linux/iio/consumer.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/slab.h>
/* PMIC global control registers definition */
#define SC27XX_MODULE_EN0 0xc08
#define SC27XX_CLK_EN0 0xc18
#define SC27XX_FGU_EN BIT(7)
#define SC27XX_FGU_RTC_EN BIT(6)
/* FGU registers definition */
#define SC27XX_FGU_START 0x0
#define SC27XX_FGU_CONFIG 0x4
#define SC27XX_FGU_ADC_CONFIG 0x8
#define SC27XX_FGU_STATUS 0xc
#define SC27XX_FGU_INT_EN 0x10
#define SC27XX_FGU_INT_CLR 0x14
#define SC27XX_FGU_INT_STS 0x1c
#define SC27XX_FGU_VOLTAGE 0x20
#define SC27XX_FGU_OCV 0x24
#define SC27XX_FGU_POCV 0x28
#define SC27XX_FGU_CURRENT 0x2c
#define SC27XX_FGU_LOW_OVERLOAD 0x34
#define SC27XX_FGU_CLBCNT_SETH 0x50
#define SC27XX_FGU_CLBCNT_SETL 0x54
#define SC27XX_FGU_CLBCNT_DELTH 0x58
#define SC27XX_FGU_CLBCNT_DELTL 0x5c
#define SC27XX_FGU_CLBCNT_VALH 0x68
#define SC27XX_FGU_CLBCNT_VALL 0x6c
#define SC27XX_FGU_CLBCNT_QMAXL 0x74
#define SC27XX_FGU_USER_AREA_SET 0xa0
#define SC27XX_FGU_USER_AREA_CLEAR 0xa4
#define SC27XX_FGU_USER_AREA_STATUS 0xa8
#define SC27XX_WRITE_SELCLB_EN BIT(0)
#define SC27XX_FGU_CLBCNT_MASK GENMASK(15, 0)
#define SC27XX_FGU_CLBCNT_SHIFT 16
#define SC27XX_FGU_LOW_OVERLOAD_MASK GENMASK(12, 0)
#define SC27XX_FGU_INT_MASK GENMASK(9, 0)
#define SC27XX_FGU_LOW_OVERLOAD_INT BIT(0)
#define SC27XX_FGU_CLBCNT_DELTA_INT BIT(2)
#define SC27XX_FGU_MODE_AREA_MASK GENMASK(15, 12)
#define SC27XX_FGU_CAP_AREA_MASK GENMASK(11, 0)
#define SC27XX_FGU_MODE_AREA_SHIFT 12
#define SC27XX_FGU_FIRST_POWERTON GENMASK(3, 0)
#define SC27XX_FGU_DEFAULT_CAP GENMASK(11, 0)
#define SC27XX_FGU_NORMAIL_POWERTON 0x5
#define SC27XX_FGU_CUR_BASIC_ADC 8192
#define SC27XX_FGU_SAMPLE_HZ 2
/*
* struct sc27xx_fgu_data: describe the FGU device
* @regmap: regmap for register access
* @dev: platform device
* @battery: battery power supply
* @base: the base offset for the controller
* @lock: protect the structure
* @gpiod: GPIO for battery detection
* @channel: IIO channel to get battery temperature
* @charge_chan: IIO channel to get charge voltage
* @internal_resist: the battery internal resistance in mOhm
* @total_cap: the total capacity of the battery in mAh
* @init_cap: the initial capacity of the battery in mAh
* @alarm_cap: the alarm capacity
* @init_clbcnt: the initial coulomb counter
* @max_volt: the maximum constant input voltage in millivolt
* @min_volt: the minimum drained battery voltage in microvolt
* @table_len: the capacity table length
* @cur_1000ma_adc: ADC value corresponding to 1000 mA
* @vol_1000mv_adc: ADC value corresponding to 1000 mV
* @cap_table: capacity table with corresponding ocv
*/
struct sc27xx_fgu_data {
struct regmap *regmap;
struct device *dev;
struct power_supply *battery;
u32 base;
struct mutex lock;
struct gpio_desc *gpiod;
struct iio_channel *channel;
struct iio_channel *charge_chan;
bool bat_present;
int internal_resist;
int total_cap;
int init_cap;
int alarm_cap;
int init_clbcnt;
int max_volt;
int min_volt;
int table_len;
int cur_1000ma_adc;
int vol_1000mv_adc;
struct power_supply_battery_ocv_table *cap_table;
};
static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity);
static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data,
int cap, bool int_mode);
static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap);
static const char * const sc27xx_charger_supply_name[] = {
"sc2731_charger",
"sc2720_charger",
"sc2721_charger",
"sc2723_charger",
};
static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc)
{
return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc);
}
static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc)
{
return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc);
}
static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol)
{
return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc,<