sdk-hwV1.3/lichee/linux-4.9/drivers/power/supply/axp2585_battery.c

1162 lines
34 KiB
C

#define pr_fmt(x) KBUILD_MODNAME ": " x "\n"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include "linux/irq.h"
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/param.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/of.h>
#include <linux/timekeeping.h>
#include <linux/types.h>
#include <linux/string.h>
#include <asm/irq.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/err.h>
#include "linux/mfd/axp2101.h"
#include "axp2101_charger.h"
#define AXP2585_VBAT_MAX (8000)
#define AXP2585_VBAT_MIN (2000)
#define AXP2585_SOC_MAX (100)
#define AXP2585_SOC_MIN (0)
#define AXP2585_MASK_VBUS_STATE BIT(1)
#define AXP2585_MODE_RSTGAUGE BIT(3)
#define AXP2585_MODE_RSTMCU BIT(2)
#define AXP2585_MAX_PARAM 512
#define AXP2585_BROMUP_EN BIT(0)
#define AXP2585_CFG_UPDATE_MARK BIT(4)
#define AXP2585_CHARGING_TRI (0)
#define AXP2585_CHARGING_PRE (1)
#define AXP2585_CHARGING_CC (2)
#define AXP2585_CHARGING_CV (3)
#define AXP2585_CHARGING_DONE (4)
#define AXP2585_CHARGING_NCHG (5)
#define AXP2585_MANUFACTURER "xpower,axp2585"
#define AXP2585_ADC_BATVOL_ENABLE (1 << 4)
#define AXP2585_ADC_TSVOL_ENABLE (1 << 5)
#define AXP2585_ADC_BATCUR_ENABLE (1 << 6)
#define AXP2585_ADC_DIETMP_ENABLE (1 << 7)
struct axp2585_device_info {
char *name;
struct device *dev;
struct regmap *regmap;
struct power_supply *bat;
struct delayed_work bat_chk;
struct axp_config_info dts_info;
bool axp2585_version_d;
struct notifier_block notifier;
};
RAW_NOTIFIER_HEAD(axp2585_notifier_list);
#ifdef CONFIG_AXP2585_LOW_WARNING_LED
#define LOW_WARNING_EVENT 0x5A
#endif
static int axp2585_get_rest_vol(struct axp2585_device_info *di)
{
u8 temp_val[2];
u8 ocv_percent = 0;
u8 coulomb_percent = 0;
int batt_max_cap, coulumb_counter;
int rest_vol;
struct regmap *map = di->regmap;
u32 val, tmp[2];
bool bat_charging;
regmap_read(map, AXP2585_CAP, &val);
if (!(val & 0x80))
return 0;
rest_vol = (int) (val & 0x7F);
regmap_read(map, 0xe4, &tmp[0]);
if (tmp[0] & 0x80)
ocv_percent = tmp[0] & 0x7f;
regmap_read(map, 0xe5, &tmp[0]);
if (tmp[0] & 0x80)
coulomb_percent = tmp[0] & 0x7f;
regmap_read(map, AXP2585_STATUS, &val);
bat_charging = ((((val & (7 << 2)) > 0))
&& ((val & (7 << 2)) < 0x14)) ? 1 : 0;
if (ocv_percent == 100 && bat_charging == 0 && rest_vol == 99
&& (val & BIT(1))) {
regmap_update_bits(map, AXP2585_COULOMB_CTL, 0x00, 0x80);
regmap_update_bits(map, AXP2585_COULOMB_CTL, 0x80, 0x80);
rest_vol = 100;
}
regmap_bulk_read(map, 0xe2, temp_val, 2);
coulumb_counter = (((temp_val[0] & 0x7f) << 8) + temp_val[1])
* 1456 / 1000;
regmap_bulk_read(map, 0xe0, temp_val, 2);
batt_max_cap = (((temp_val[0] & 0x7f) << 8) + temp_val[1])
* 1456 / 1000;
return rest_vol;
}
static int axp2585_get_coulumb_counter(struct axp2585_device_info *di)
{
u8 temp_val[2];
struct regmap *map = di->regmap;
int coulumb_counter;
regmap_bulk_read(map, 0xe2, temp_val, 2);
coulumb_counter = (((temp_val[0] & 0x7f) << 8) + temp_val[1])
* 1456;
return coulumb_counter;
}
static int axp2585_get_bat(struct axp2585_device_info *di)
{
u32 val;
struct regmap *regmap = di->regmap;
if (di->dts_info.pmu_bat_unused == 0) {
if (di->axp2585_version_d) {
regmap_read(regmap, 0x00, &val);
if (!(val & 0x02))
return 1;
regmap_read(regmap, 0xbc, &val);
if (val & 0xff)
return 1;
regmap_read(regmap, 0xbd, &val);
if (val & 0xff)
return 1;
return 0;
} else {
regmap_read(regmap, 0x02, &val);
if ((val & (3 << 3)) == 0x18)
return 1;
else
return 0;
}
} else {
return 0;
}
}
static int axp2585_get_direction(struct axp2585_device_info *di)
{
u32 val;
struct regmap *regmap = di->regmap;
regmap_read(regmap, 0x02, &val);
if (val & 0x01)
return 1;
else
return 0;
}
static int axp2585_get_bat_status(struct power_supply *psy,
union power_supply_propval *val)
{
struct axp2585_device_info *di = power_supply_get_drvdata(psy);
struct regmap *regmap = di->regmap;
unsigned int data;
int ret;
bool bat_det, bat_charging;
unsigned int rest_vol;
bool usb_valid;
ret = regmap_read(regmap, AXP2585_STATUS, &data);
if (ret < 0) {
dev_dbg(&psy->dev, "error read AXP210X_COM_STAT1\n");
return ret;
}
bat_charging = ((((data & (7 << 2)) > 0))
&& ((data & (7 << 2)) < 0x14)) ? 1 : 0;
usb_valid = data & (1 << 1);
bat_det = axp2585_get_bat(di);
rest_vol = axp2585_get_rest_vol(di);
if (bat_det) {
if (rest_vol == 100)
val->intval = POWER_SUPPLY_STATUS_FULL;
else if (bat_charging)
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
} else {
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
}
return 0;
}
static int axp2585_get_bat_health(void)
{
return POWER_SUPPLY_HEALTH_GOOD;
}
static inline int axp2585_icharge_to_mA(u32 reg)
{
return (int)((((reg >> 8) << 4) | (reg & 0x000f)) << 1);
}
static int axp2585_get_ibat(struct axp2585_device_info *di)
{
struct regmap *map = di->regmap;
u8 tmp[2];
u32 res;
int ret;
ret = regmap_bulk_read(map, AXP2585_IBATH_REG, tmp, 2);
if (ret < 0)
return ret;
res = (tmp[0] << 8) | tmp[1];
return axp2585_icharge_to_mA(res);
}
static inline int axp2585_vbat_to_mV(u32 reg)
{
return ((int)(((reg >> 8) << 4) | (reg & 0x000F))) * 1200 / 1000;
}
static int axp2585_get_vbat(struct axp2585_device_info *di)
{
struct regmap *map = di->regmap;
u8 tmp[2];
u32 res;
int ret;
ret = regmap_bulk_read(map, AXP2585_VBATH_REG, tmp, 2);
if (ret < 0)
return ret;
res = (tmp[0] << 8) | tmp[1];
return axp2585_vbat_to_mV(res);
}
static inline int axp2585_ibat_to_mA(u32 reg)
{
return (int)((((reg >> 8) << 4) | (reg & 0x000f)) << 1);
}
static int axp2585_get_disibat(struct axp2585_device_info *di)
{
struct regmap *map = di->regmap;
u8 tmp[2];
u32 res;
int ret;
ret = regmap_bulk_read(map, AXP2585_DISIBATH_REG, tmp, 2);
if (ret < 0)
return ret;
res = (tmp[0] << 8) | tmp[1];
return axp2585_ibat_to_mA(res);
}
static inline int axp_vts_to_mV(u16 reg)
{
return ((int)(((reg >> 8) << 4) | (reg & 0x000F))) * 800 / 1000;
}
static inline int axp_vts_to_temp(int data,
const struct axp_config_info *axp_config)
{
int temp;
if (data < 80 || axp_config->pmu_bat_temp_enable != 0)
return 30;
else if (data < axp_config->pmu_bat_temp_para16)
return 80;
else if (data <= axp_config->pmu_bat_temp_para15) {
temp = 70 + (axp_config->pmu_bat_temp_para15-data)*10/
(axp_config->pmu_bat_temp_para15 - axp_config->pmu_bat_temp_para16);
} else if (data <= axp_config->pmu_bat_temp_para14) {
temp = 60 + (axp_config->pmu_bat_temp_para14-data)*10/
(axp_config->pmu_bat_temp_para14 - axp_config->pmu_bat_temp_para15);
} else if (data <= axp_config->pmu_bat_temp_para13) {
temp = 55 + (axp_config->pmu_bat_temp_para13-data)*5/
(axp_config->pmu_bat_temp_para13 - axp_config->pmu_bat_temp_para14);
} else if (data <= axp_config->pmu_bat_temp_para12) {
temp = 50 + (axp_config->pmu_bat_temp_para12-data)*5/
(axp_config->pmu_bat_temp_para12 - axp_config->pmu_bat_temp_para13);
} else if (data <= axp_config->pmu_bat_temp_para11) {
temp = 45 + (axp_config->pmu_bat_temp_para11-data)*5/
(axp_config->pmu_bat_temp_para11 - axp_config->pmu_bat_temp_para12);
} else if (data <= axp_config->pmu_bat_temp_para10) {
temp = 40 + (axp_config->pmu_bat_temp_para10-data)*5/
(axp_config->pmu_bat_temp_para10 - axp_config->pmu_bat_temp_para11);
} else if (data <= axp_config->pmu_bat_temp_para9) {
temp = 30 + (axp_config->pmu_bat_temp_para9-data)*10/
(axp_config->pmu_bat_temp_para9 - axp_config->pmu_bat_temp_para10);
} else if (data <= axp_config->pmu_bat_temp_para8) {
temp = 20 + (axp_config->pmu_bat_temp_para8-data)*10/
(axp_config->pmu_bat_temp_para8 - axp_config->pmu_bat_temp_para9);
} else if (data <= axp_config->pmu_bat_temp_para7) {
temp = 10 + (axp_config->pmu_bat_temp_para7-data)*10/
(axp_config->pmu_bat_temp_para7 - axp_config->pmu_bat_temp_para8);
} else if (data <= axp_config->pmu_bat_temp_para6) {
temp = 5 + (axp_config->pmu_bat_temp_para6-data)*5/
(axp_config->pmu_bat_temp_para6 - axp_config->pmu_bat_temp_para7);
} else if (data <= axp_config->pmu_bat_temp_para5) {
temp = 0 + (axp_config->pmu_bat_temp_para5-data)*5/
(axp_config->pmu_bat_temp_para5 - axp_config->pmu_bat_temp_para6);
} else if (data <= axp_config->pmu_bat_temp_para4) {
temp = -5 + (axp_config->pmu_bat_temp_para4-data)*5/
(axp_config->pmu_bat_temp_para4 - axp_config->pmu_bat_temp_para5);
} else if (data <= axp_config->pmu_bat_temp_para3) {
temp = -10 + (axp_config->pmu_bat_temp_para3-data)*5/
(axp_config->pmu_bat_temp_para3 - axp_config->pmu_bat_temp_para4);
} else if (data <= axp_config->pmu_bat_temp_para2) {
temp = -15 + (axp_config->pmu_bat_temp_para2-data)*5/
(axp_config->pmu_bat_temp_para2 - axp_config->pmu_bat_temp_para3);
} else if (data <= axp_config->pmu_bat_temp_para1) {
temp = -25 + (axp_config->pmu_bat_temp_para1-data)*10/
(axp_config->pmu_bat_temp_para1 - axp_config->pmu_bat_temp_para2);
} else
temp = -25;
return temp;
}
static int axp2585_get_bat_temp(struct axp2585_device_info *di)
{
unsigned char temp_val[2];
unsigned short ts_res;
int bat_temp_mv, bat_temp;
int ret = 0;
struct regmap *regmap = di->regmap;
ret = regmap_bulk_read(regmap, AXP803_VTS_RES, temp_val, 2);
if (ret < 0)
return ret;
ts_res = ((unsigned short) temp_val[0] << 8) | temp_val[1];
bat_temp_mv = axp_vts_to_mV(ts_res);
bat_temp = axp_vts_to_temp(bat_temp_mv, &di->dts_info);
pr_debug("bat_temp: %d\n", bat_temp);
return bat_temp;
}
static enum power_supply_property axp2585_props[] = {
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_TEMP,
};
static int axp2585_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
int ret = 0;
struct axp2585_device_info *di = power_supply_get_drvdata(psy);
u8 temp_val[2];
switch (psp) {
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = psy->desc->name;
break;
case POWER_SUPPLY_PROP_STATUS:
ret = axp2585_get_bat_status(psy, val);
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = axp2585_get_bat_health();
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
val->intval = axp2585_get_coulumb_counter(di);
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
val->intval = di->dts_info.pmu_init_chgvol;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
val->intval = di->dts_info.pmu_pwroff_vol;
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = axp2585_get_direction(di);
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = axp2585_get_bat(di);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = axp2585_get_vbat(di) * 1000;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = (axp2585_get_ibat(di) - axp2585_get_disibat(di)) * 1000;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
ret = regmap_bulk_read(di->regmap, AXP2585_BATCAP0, temp_val, 2);
if (ret < 0)
return ret;
val->intval = (((temp_val[0] & 0x7f) << 8) + temp_val[1]) * 1456;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = axp2585_get_rest_vol(di);
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = axp2585_get_bat_temp(di) * 10;
break;
default:
return -EINVAL;
}
return ret;
}
static int axp2585_register_battery(struct axp2585_device_info *di)
{
int ret = 0;
struct power_supply_desc *psy_desc;
struct power_supply_config psy_cfg = {
.of_node = di->dev->of_node,
.drv_data = di,
};
psy_desc = devm_kzalloc(di->dev, sizeof(*psy_desc), GFP_KERNEL);
if (!psy_desc)
return -ENOMEM;
psy_desc->name = "battery";
psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
psy_desc->properties = axp2585_props;
psy_desc->num_properties = ARRAY_SIZE(axp2585_props);
psy_desc->get_property = axp2585_get_property;
psy_desc->use_for_apm = 1;
di->bat = power_supply_register(di->dev, psy_desc, &psy_cfg);
if (IS_ERR(di->bat)) {
dev_err(di->dev, "failed to register battery\n");
ret = PTR_ERR(di->bat);
goto err1;
}
return ret;
err1:
power_supply_unregister(di->bat);
return ret;
}
static void axp2585_teardown_battery(struct axp2585_device_info *di)
{
if (di->bat)
power_supply_unregister(di->bat);
}
/* show pwm-led when battery capacity low. */
#ifdef CONFIG_AXP2585_LOW_WARNING_LED
irqreturn_t axp2585_irq_handler_low_warn1(int irq, void *data)
{
struct axp2585_device_info *di = data;
pr_debug("%s: enter interrupt %d\n", __func__, irq);
pr_debug("==========battery capacity low warning!!==========\n");
pr_debug("=============need to charge!!=============\n");
power_supply_changed(di->bat);
/* call notifier to show pwm-led*/
raw_notifier_call_chain(&axp2585_notifier_list, LOW_WARNING_EVENT, NULL);
return IRQ_HANDLED;
}
#endif
static irqreturn_t axp2585_irq_handler_thread(int irq, void *data)
{
struct axp2585_device_info *di = data;
pr_debug("%s: enter interrupt %d\n", __func__, irq);
power_supply_changed(di->bat);
return IRQ_HANDLED;
}
enum axp2585_virq_index {
AXP2585_VIRQ_BAT_IN,
AXP2585_VIRQ_BAT_OUT,
AXP2585_VIRQ_CHARGING,
AXP2585_VIRQ_CHARGE_OVER,
AXP2585_VIRQ_LOW_WARNING1,
AXP2585_VIRQ_LOW_WARNING2,
AXP2585_VIRQ_TC_IN,
AXP2585_VIRQ_TC_OUT,
AXP2585_VIRQ_BAT_UNTEMP_WORK,
AXP2585_VIRQ_BAT_OVTEMP_WORK,
AXP2585_VIRQ_BAT_UNTEMP_CHG,
AXP2585_VIRQ_BAT_OVTEMP_CHG,
AXP2585_VIRQ_MAX_VIRQ,
};
static struct axp_interrupts axp_charger_irq[] = {
[AXP2585_VIRQ_BAT_IN] = { "bat in", axp2585_irq_handler_thread },
[AXP2585_VIRQ_BAT_OUT] = { "bat out", axp2585_irq_handler_thread },
[AXP2585_VIRQ_CHARGING] = { "charging", axp2585_irq_handler_thread },
[AXP2585_VIRQ_CHARGE_OVER] = { "charge over",
axp2585_irq_handler_thread },
#ifdef CONFIG_AXP2585_LOW_WARNING_LED
[AXP2585_VIRQ_LOW_WARNING1] = { "low warning1",
axp2585_irq_handler_low_warn1 },
#else
[AXP2585_VIRQ_LOW_WARNING1] = { "low warning1",
axp2585_irq_handler_thread },
#endif
[AXP2585_VIRQ_LOW_WARNING2] = { "low warning2",
axp2585_irq_handler_thread },
[AXP2585_VIRQ_TC_IN] = { "tc in", axp2585_irq_handler_thread },
[AXP2585_VIRQ_TC_OUT] = { "tc out", axp2585_irq_handler_thread },
[AXP2585_VIRQ_BAT_UNTEMP_WORK] = { "bat untemp work",
axp2585_irq_handler_thread },
[AXP2585_VIRQ_BAT_OVTEMP_WORK] = { "bat ovtemp work",
axp2585_irq_handler_thread },
[AXP2585_VIRQ_BAT_UNTEMP_CHG] = { "bat untemp chg",
axp2585_irq_handler_thread },
[AXP2585_VIRQ_BAT_OVTEMP_CHG] = { "bat ovtemp chg",
axp2585_irq_handler_thread },
};
static int axp2585_notifier_event(struct notifier_block *nb, unsigned long event, void *v)
{
switch (event) {
#ifdef CONFIG_AXP2585_LOW_WARNING_LED
int axp_leds_show(void);
case LOW_WARNING_EVENT:
axp_leds_show();
break;
#endif
default:
pr_err("axp2585: unknow notifier event\n");
break;
}
return NOTIFY_DONE;
}
static int axp2585_notifier_init(struct axp2585_device_info *di)
{
di->notifier.notifier_call = axp2585_notifier_event;
raw_notifier_chain_register(&axp2585_notifier_list, &di->notifier);
return 0;
}
static void axp2585_set_chg_cur(struct regmap *regmap, u32 cur)
{
u8 tmp = 0;
tmp = cur / 64;
if (tmp > 0x3f)
tmp = 0x3f;
regmap_update_bits(regmap, 0x8b, 0x3f, tmp);
}
static void axp2585_charger_init(struct axp2585_device_info *di)
{
unsigned char ocv_cap[32];
unsigned int val;
int cur_coulomb_counter, rdc;
int i;
int update_min_times[8] = {30, 60, 120, 164, 0, 5, 10, 20};
int ocv_cou_adjust_time[4] = {60, 120, 15, 30};
struct regmap *regmap = di->regmap;
struct axp_config_info *dinfo = &di->dts_info;
/* set chg time */
if (dinfo->pmu_init_chg_pretime < 40)
dinfo->pmu_init_chg_pretime = 40;
val = (dinfo->pmu_init_chg_pretime - 40)/10;
if (val >= 3)
val = 3;
val = 0x80 + (val << 5);
regmap_update_bits(regmap, 0x8e, 0xe0, val);
if (dinfo->pmu_init_chg_csttime <= 60 * 5)
val = 0;
else if (dinfo->pmu_init_chg_csttime <= 60 * 8)
val = 1;
else if (dinfo->pmu_init_chg_csttime <= 60 * 12)
val = 2;
else if (dinfo->pmu_init_chg_csttime <= 60 * 20)
val = 3;
else
val = 3;
val = (val << 1) + 0x01;
regmap_update_bits(regmap, 0x8d, 0x07, val);
/* adc set */
val = AXP2585_ADC_BATVOL_ENABLE | AXP2585_ADC_BATCUR_ENABLE;
if (dinfo->pmu_bat_temp_enable != 0)
val = val | AXP2585_ADC_TSVOL_ENABLE;
regmap_update_bits(regmap, AXP2585_ADC_CONTROL,
AXP2585_ADC_BATVOL_ENABLE
| AXP2585_ADC_BATCUR_ENABLE
| AXP2585_ADC_TSVOL_ENABLE,
val);
regmap_read(regmap, AXP2585_TS_PIN_CONTROL, &val);
switch (dinfo->pmu_init_adc_freq / 100) {
case 1:
val &= ~(3 << 5);
break;
case 2:
val &= ~(3 << 5);
val |= 1 << 5;
break;
case 4:
val &= ~(3 << 5);
val |= 2 << 5;
break;
case 8:
val &= ~(3 << 5);
val |= 3 << 5;
break;
default:
break;
}
if (dinfo->pmu_bat_temp_enable != 0)
val &= ~(1 << 7);
regmap_write(regmap, AXP2585_TS_PIN_CONTROL, val);
/* bat para */
regmap_write(regmap, AXP2585_WARNING_LEVEL,
((dinfo->pmu_battery_warning_level1 - 5) << 4)
+ dinfo->pmu_battery_warning_level2);
if (dinfo->pmu_init_chgvol < 3840)
dinfo->pmu_init_chgvol = 3840;
val = (dinfo->pmu_init_chgvol - 3840)/16;
if (val > 0x30)
val = 0x30;
val <<= 2;
regmap_update_bits(regmap, AXP2585_CHARGE_CONTROL2, 0xfc, val);
ocv_cap[0] = dinfo->pmu_bat_para1;
ocv_cap[1] = dinfo->pmu_bat_para2;
ocv_cap[2] = dinfo->pmu_bat_para3;
ocv_cap[3] = dinfo->pmu_bat_para4;
ocv_cap[4] = dinfo->pmu_bat_para5;
ocv_cap[5] = dinfo->pmu_bat_para6;
ocv_cap[6] = dinfo->pmu_bat_para7;
ocv_cap[7] = dinfo->pmu_bat_para8;
ocv_cap[8] = dinfo->pmu_bat_para9;
ocv_cap[9] = dinfo->pmu_bat_para10;
ocv_cap[10] = dinfo->pmu_bat_para11;
ocv_cap[11] = dinfo->pmu_bat_para12;
ocv_cap[12] = dinfo->pmu_bat_para13;
ocv_cap[13] = dinfo->pmu_bat_para14;
ocv_cap[14] = dinfo->pmu_bat_para15;
ocv_cap[15] = dinfo->pmu_bat_para16;
ocv_cap[16] = dinfo->pmu_bat_para17;
ocv_cap[17] = dinfo->pmu_bat_para18;
ocv_cap[18] = dinfo->pmu_bat_para19;
ocv_cap[19] = dinfo->pmu_bat_para20;
ocv_cap[20] = dinfo->pmu_bat_para21;
ocv_cap[21] = dinfo->pmu_bat_para22;
ocv_cap[22] = dinfo->pmu_bat_para23;
ocv_cap[23] = dinfo->pmu_bat_para24;
ocv_cap[24] = dinfo->pmu_bat_para25;
ocv_cap[25] = dinfo->pmu_bat_para26;
ocv_cap[26] = dinfo->pmu_bat_para27;
ocv_cap[27] = dinfo->pmu_bat_para28;
ocv_cap[28] = dinfo->pmu_bat_para29;
ocv_cap[29] = dinfo->pmu_bat_para30;
ocv_cap[30] = dinfo->pmu_bat_para31;
ocv_cap[31] = dinfo->pmu_bat_para32;
regmap_bulk_write(regmap, 0xC0, ocv_cap, 32);
/* Init CHGLED function */
if (dinfo->pmu_chgled_func)
regmap_update_bits(regmap, 0x90, 0x80, 0x80);
else
regmap_update_bits(regmap, 0x90, 0x80, 0x00);
regmap_update_bits(regmap, 0x90, 0x07, dinfo->pmu_chgled_type);
/* init battery capacity correct function */
if (dinfo->pmu_batt_cap_correct)
regmap_update_bits(regmap, 0xb8, 0x20, 0x20);
else
regmap_update_bits(regmap, 0xb8, 0x20, 0x0);
/* battery detect enable */
if (!(di->axp2585_version_d)) {
if (dinfo->pmu_batdeten)
regmap_update_bits(regmap, 0x8e, 0x08, 0x08);
else
regmap_update_bits(regmap, 0x8e, 0x08, 0x0);
}
/* RDC initial */
regmap_read(regmap, AXP2585_RDC0, &val);
if ((dinfo->pmu_battery_rdc) && (!(val & 0x40))) {
rdc = (dinfo->pmu_battery_rdc * 10000 + 5371) / 10742;
regmap_write(regmap, AXP2585_RDC0, ((rdc >> 8)& 0x1F)|0x80);
regmap_write(regmap, AXP2585_RDC1, rdc & 0x00FF);
}
regmap_read(regmap, AXP2585_BATCAP0, &val);
if ((dinfo->pmu_battery_cap) && (!(val & 0x80))) {
cur_coulomb_counter = dinfo->pmu_battery_cap
* 1000 / 1456;
regmap_write(regmap, AXP2585_BATCAP0,
((cur_coulomb_counter >> 8) | 0x80));
regmap_write(regmap, AXP2585_BATCAP1,
cur_coulomb_counter & 0x00FF);
} else if (!dinfo->pmu_battery_cap) {
regmap_write(regmap, AXP2585_BATCAP0, 0x00);
regmap_write(regmap, AXP2585_BATCAP1, 0x00);
}
if (dinfo->pmu_bat_temp_enable != 0) {
regmap_write(regmap, AXP2585_VLTF_CHARGE,
dinfo->pmu_bat_charge_ltf * 10 / 128);
regmap_write(regmap, AXP2585_VHTF_CHARGE,
dinfo->pmu_bat_charge_htf * 10 / 128);
regmap_write(regmap, AXP2585_VLTF_WORK,
dinfo->pmu_bat_shutdown_ltf * 10 / 128);
regmap_write(regmap, AXP2585_VHTF_WORK,
dinfo->pmu_bat_shutdown_htf * 10 / 128);
}
/*enable fast charge */
regmap_update_bits(regmap, 0x31, 0x04, 0x04);
/*set POR time as 16s*/
regmap_update_bits(regmap, AXP2585_POK_SET, 0x30, 0x30);
/* avoid the timer counter error*/
regmap_update_bits(regmap, AXP2585_TIMER2_SET, 0x10, 0x0);
for (i = 0; i < ARRAY_SIZE(update_min_times); i++) {
if (update_min_times[i] == dinfo->pmu_update_min_time)
break;
}
regmap_update_bits(regmap, AXP2585_ADJUST_PARA, 0x7, i);
/*initial the ocv_cou_adjust_time*/
for (i = 0; i < ARRAY_SIZE(ocv_cou_adjust_time); i++) {
if (ocv_cou_adjust_time[i] == dinfo->pmu_ocv_cou_adjust_time)
break;
}
i <<= 6;
regmap_update_bits(regmap, AXP2585_ADJUST_PARA1, 0xc0, i);
/* init the chg runtime_cur */
axp2585_set_chg_cur(regmap, dinfo->pmu_runtime_chgcur);
/*type-c cc logic init*/
#ifdef CONFIG_TYPE_C
regmap_update_bits(regmap, AXP2585_CC_EN, 0x02, 0x02);
regmap_update_bits(regmap, AXP2585_CC_LOW_POWER_CTRL, 0x04, 0x00);
regmap_update_bits(regmap, AXP2585_CC_MODE_CTRL, 0x03, 0x03);
#endif
}
#define AXP_OF_PROP_READ(name, def_value)\
do {\
if (of_property_read_u32(node, #name, &axp_config->name))\
axp_config->name = def_value;\
} while (0)
int axp2585_charger_dt_parse(struct device_node *node,
struct axp_config_info *axp_config)
{
if (!of_device_is_available(node)) {
pr_err("%s: failed\n", __func__);
return -1;
}
AXP_OF_PROP_READ(pmu_battery_rdc, BATRDC);
AXP_OF_PROP_READ(pmu_battery_cap, 4000);
AXP_OF_PROP_READ(pmu_batdeten, 1);
AXP_OF_PROP_READ(pmu_chg_ic_temp, 0);
AXP_OF_PROP_READ(pmu_runtime_chgcur, INTCHGCUR / 1000);
AXP_OF_PROP_READ(pmu_suspend_chgcur, 1200);
AXP_OF_PROP_READ(pmu_shutdown_chgcur, 1200);
AXP_OF_PROP_READ(pmu_init_chgvol, INTCHGVOL / 1000);
AXP_OF_PROP_READ(pmu_init_chgend_rate, INTCHGENDRATE);
AXP_OF_PROP_READ(pmu_init_chg_enabled, 1);
AXP_OF_PROP_READ(pmu_init_bc_en, 0);
AXP_OF_PROP_READ(pmu_init_adc_freq, INTADCFREQ);
AXP_OF_PROP_READ(pmu_init_adcts_freq, INTADCFREQC);
AXP_OF_PROP_READ(pmu_init_chg_pretime, INTCHGPRETIME);
AXP_OF_PROP_READ(pmu_init_chg_csttime, INTCHGCSTTIME);
AXP_OF_PROP_READ(pmu_batt_cap_correct, 1);
AXP_OF_PROP_READ(pmu_chg_end_on_en, 0);
AXP_OF_PROP_READ(ocv_coulumb_100, 0);
AXP_OF_PROP_READ(pmu_bat_para1, OCVREG0);
AXP_OF_PROP_READ(pmu_bat_para2, OCVREG1);
AXP_OF_PROP_READ(pmu_bat_para3, OCVREG2);
AXP_OF_PROP_READ(pmu_bat_para4, OCVREG3);
AXP_OF_PROP_READ(pmu_bat_para5, OCVREG4);
AXP_OF_PROP_READ(pmu_bat_para6, OCVREG5);
AXP_OF_PROP_READ(pmu_bat_para7, OCVREG6);
AXP_OF_PROP_READ(pmu_bat_para8, OCVREG7);
AXP_OF_PROP_READ(pmu_bat_para9, OCVREG8);
AXP_OF_PROP_READ(pmu_bat_para10, OCVREG9);
AXP_OF_PROP_READ(pmu_bat_para11, OCVREGA);
AXP_OF_PROP_READ(pmu_bat_para12, OCVREGB);
AXP_OF_PROP_READ(pmu_bat_para13, OCVREGC);
AXP_OF_PROP_READ(pmu_bat_para14, OCVREGD);
AXP_OF_PROP_READ(pmu_bat_para15, OCVREGE);
AXP_OF_PROP_READ(pmu_bat_para16, OCVREGF);
AXP_OF_PROP_READ(pmu_bat_para17, OCVREG10);
AXP_OF_PROP_READ(pmu_bat_para18, OCVREG11);
AXP_OF_PROP_READ(pmu_bat_para19, OCVREG12);
AXP_OF_PROP_READ(pmu_bat_para20, OCVREG13);
AXP_OF_PROP_READ(pmu_bat_para21, OCVREG14);
AXP_OF_PROP_READ(pmu_bat_para22, OCVREG15);
AXP_OF_PROP_READ(pmu_bat_para23, OCVREG16);
AXP_OF_PROP_READ(pmu_bat_para24, OCVREG17);
AXP_OF_PROP_READ(pmu_bat_para25, OCVREG18);
AXP_OF_PROP_READ(pmu_bat_para26, OCVREG19);
AXP_OF_PROP_READ(pmu_bat_para27, OCVREG1A);
AXP_OF_PROP_READ(pmu_bat_para28, OCVREG1B);
AXP_OF_PROP_READ(pmu_bat_para29, OCVREG1C);
AXP_OF_PROP_READ(pmu_bat_para30, OCVREG1D);
AXP_OF_PROP_READ(pmu_bat_para31, OCVREG1E);
AXP_OF_PROP_READ(pmu_bat_para32, OCVREG1F);
AXP_OF_PROP_READ(pmu_ac_vol, 4400);
AXP_OF_PROP_READ(pmu_ac_cur, 0);
AXP_OF_PROP_READ(pmu_pwroff_vol, 3300);
AXP_OF_PROP_READ(pmu_pwron_vol, 2900);
AXP_OF_PROP_READ(pmu_battery_warning_level1, 15);
AXP_OF_PROP_READ(pmu_battery_warning_level2, 0);
AXP_OF_PROP_READ(pmu_restvol_adjust_time, 30);
AXP_OF_PROP_READ(pmu_ocv_cou_adjust_time, 60);
AXP_OF_PROP_READ(pmu_chgled_func, 0);
AXP_OF_PROP_READ(pmu_chgled_type, 0);
AXP_OF_PROP_READ(pmu_bat_temp_enable, 0);
AXP_OF_PROP_READ(pmu_bat_charge_ltf, 0xA5);
AXP_OF_PROP_READ(pmu_bat_charge_htf, 0x1F);
AXP_OF_PROP_READ(pmu_bat_shutdown_ltf, 0xFC);
AXP_OF_PROP_READ(pmu_bat_shutdown_htf, 0x16);
AXP_OF_PROP_READ(pmu_bat_temp_para1, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para2, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para3, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para4, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para5, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para6, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para7, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para8, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para9, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para10, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para11, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para12, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para13, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para14, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para15, 0);
AXP_OF_PROP_READ(pmu_bat_temp_para16, 0);
AXP_OF_PROP_READ(pmu_bat_unused, 0);
AXP_OF_PROP_READ(power_start, 0);
AXP_OF_PROP_READ(pmu_ocv_en, 1);
AXP_OF_PROP_READ(pmu_cou_en, 1);
AXP_OF_PROP_READ(pmu_update_min_time, UPDATEMINTIME);
axp_config->wakeup_bat_in =
of_property_read_bool(node, "wakeup_bat_in");
axp_config->wakeup_bat_out =
of_property_read_bool(node, "wakeup_bat_out");
axp_config->wakeup_bat_charging =
of_property_read_bool(node, "wakeup_bat_charging");
axp_config->wakeup_bat_charge_over =
of_property_read_bool(node, "wakeup_bat_charge_over");
axp_config->wakeup_low_warning1 =
of_property_read_bool(node, "wakeup_low_warning1");
axp_config->wakeup_low_warning2 =
of_property_read_bool(node, "wakeup_low_warning2");
axp_config->wakeup_bat_untemp_work =
of_property_read_bool(node, "wakeup_bat_untemp_work");
axp_config->wakeup_bat_ovtemp_work =
of_property_read_bool(node, "wakeup_bat_ovtemp_work");
axp_config->wakeup_untemp_chg =
of_property_read_bool(node, "wakeup_bat_untemp_chg");
axp_config->wakeup_ovtemp_chg =
of_property_read_bool(node, "wakeup_bat_ovtemp_chg");
// axp_config->wakeup_tc_in =
// of_property_read_bool(node, "wakeup_tc_in");
// axp_config->wakeup_tc_out =
// of_property_read_bool(node, "wakeup_tc_out");
return 0;
}
static void axp2585_parse_device_tree(struct axp2585_device_info *di)
{
int ret;
struct axp_config_info *cfg;
/* set input current limit */
if (!di->dev->of_node) {
pr_info("can not find device tree\n");
return;
}
cfg = &di->dts_info;
ret = axp2585_charger_dt_parse(di->dev->of_node, cfg);
if (ret) {
pr_info("can not parse device tree err\n");
return;
}
}
static void axp2585_bat_power_monitor(struct work_struct *work)
{
struct axp2585_device_info *di =
container_of(work, typeof(*di), bat_chk.work);
power_supply_changed(di->bat);
schedule_delayed_work(&di->bat_chk, msecs_to_jiffies(10*1000));
}
static int axp2585_battery_probe(struct platform_device *pdev)
{
int ret = 0;
int i = 0, irq;
unsigned int val;
struct axp2585_device_info *di;
struct axp20x_dev *axp_dev = dev_get_drvdata(pdev->dev.parent);
struct device_node *node = pdev->dev.of_node;
if (!of_device_is_available(node)) {
pr_err("axp2585-battery device is not configed\n");
return -ENODEV;
}
if (!axp_dev->irq) {
pr_err("can not register axp2585-charger without irq\n");
return -EINVAL;
}
di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
if (di == NULL) {
pr_err("axp2585_device_info alloc failed\n");
ret = -ENOMEM;
goto err;
}
di->name = "axp2585_power";
di->dev = &pdev->dev;
di->regmap = axp_dev->regmap;
/* for device tree parse */
axp2585_parse_device_tree(di);
/* check bmu1760 d or c version */
regmap_read(di->regmap, 0x03, &val);
di->axp2585_version_d = (val & 0x20);
axp2585_charger_init(di);
ret = axp2585_register_battery(di);
if (ret < 0) {
dev_err(di->dev, "axp210x register battery dev fail!\n");
goto err;
}
for (i = 0; i < ARRAY_SIZE(axp_charger_irq); i++) {
irq = platform_get_irq_byname(pdev, axp_charger_irq[i].name);
if (irq < 0)
continue;
irq = regmap_irq_get_virq(axp_dev->regmap_irqc, irq);
if (irq < 0) {
dev_err(&pdev->dev, "can not get irq\n");
return irq;
}
/* we use this variable to suspend irq */
axp_charger_irq[i].irq = irq;
ret = devm_request_any_context_irq(&pdev->dev, irq,
axp_charger_irq[i].isr, 0,
axp_charger_irq[i].name, di);
if (ret < 0) {
dev_err(&pdev->dev, "failed to request %s IRQ %d: %d\n",
axp_charger_irq[i].name, irq, ret);
return ret;
} else {
ret = 0;
}
dev_dbg(&pdev->dev, "Requested %s IRQ %d: %d\n",
axp_charger_irq[i].name, irq, ret);
}
platform_set_drvdata(pdev, di);
INIT_DELAYED_WORK(&di->bat_chk, axp2585_bat_power_monitor);
schedule_delayed_work(&di->bat_chk, msecs_to_jiffies(10 * 1000));
axp2585_notifier_init(di);
return ret;
err:
pr_err("%s,probe fail, ret = %d\n", __func__, ret);
return ret;
}
static int axp2585_charger_remove(struct platform_device *pdev)
{
struct axp2585_device_info *di = platform_get_drvdata(pdev);
dev_dbg(&pdev->dev, "==============AXP2585 unegister==============\n");
axp2585_teardown_battery(di);
dev_dbg(&pdev->dev, "axp210x teardown battery dev\n");
return 0;
}
static void axp2585_icchg_set(struct axp2585_device_info *di, int mA)
{
mA = mA / 64;
if (mA > 0x3f)
mA = 0x3f;
/* bit 5:0 is the ctrl bit */
regmap_update_bits(di->regmap, AXP2585_ICC_CFG, GENMASK(5, 0), mA);
}
static inline void axp2585_irq_set(unsigned int irq, bool enable)
{
if (enable)
enable_irq(irq);
else
disable_irq(irq);
}
static void axp2585_virq_dts_set(struct axp2585_device_info *di, bool enable)
{
struct axp_config_info *dts_info = &di->dts_info;
if (!dts_info->wakeup_bat_in)
axp2585_irq_set(axp_charger_irq[AXP2585_VIRQ_BAT_IN].irq,
enable);
if (!dts_info->wakeup_bat_out)
axp2585_irq_set(axp_charger_irq[AXP2585_VIRQ_BAT_OUT].irq,
enable);
if (!dts_info->wakeup_bat_charging)
axp2585_irq_set(axp_charger_irq[AXP2585_VIRQ_CHARGING].irq,
enable);
if (!dts_info->wakeup_bat_charge_over)
axp2585_irq_set(axp_charger_irq[AXP2585_VIRQ_CHARGE_OVER].irq,
enable);
if (!dts_info->wakeup_low_warning1)
axp2585_irq_set(axp_charger_irq[AXP2585_VIRQ_LOW_WARNING1].irq,
enable);
if (!dts_info->wakeup_low_warning2)
axp2585_irq_set(axp_charger_irq[AXP2585_VIRQ_LOW_WARNING2].irq,
enable);
// if (!dts_info->wakeup_tc_in)
// axp2585_irq_set(axp_charger_irq[AXP2585_VIRQ_TC_IN].irq,
// enable);
// if (!dts_info->wakeup_tc_out)
// axp2585_irq_set(axp_charger_irq[AXP2585_VIRQ_TC_OUT].irq,
// enable);
if (!dts_info->wakeup_bat_untemp_work)
axp2585_irq_set(
axp_charger_irq[AXP2585_VIRQ_BAT_UNTEMP_WORK].irq,
enable);
if (!dts_info->wakeup_bat_ovtemp_work)
axp2585_irq_set(
axp_charger_irq[AXP2585_VIRQ_BAT_OVTEMP_WORK].irq,
enable);
if (!dts_info->wakeup_untemp_chg)
axp2585_irq_set(
axp_charger_irq[AXP2585_VIRQ_BAT_UNTEMP_CHG].irq,
enable);
if (!dts_info->wakeup_ovtemp_chg)
axp2585_irq_set(
axp_charger_irq[AXP2585_VIRQ_BAT_OVTEMP_CHG].irq,
enable);
}
static void axp2585_shutdown(struct platform_device *p)
{
struct axp2585_device_info *di = platform_get_drvdata(p);
cancel_delayed_work_sync(&di->bat_chk);
axp2585_icchg_set(di, di->dts_info.pmu_shutdown_chgcur);
}
static int axp2585_suspend(struct platform_device *p, pm_message_t state)
{
struct axp2585_device_info *di = platform_get_drvdata(p);
cancel_delayed_work_sync(&di->bat_chk);
axp2585_icchg_set(di, di->dts_info.pmu_suspend_chgcur);
axp2585_virq_dts_set(di, false);
return 0;
}
static int axp2585_resume(struct platform_device *p)
{
struct axp2585_device_info *di = platform_get_drvdata(p);
schedule_delayed_work(&di->bat_chk, 0);
axp2585_icchg_set(di, di->dts_info.pmu_runtime_chgcur);
axp2585_virq_dts_set(di, true);
return 0;
}
static const struct of_device_id axp2585_power_of_match[] = {
{ .compatible = "x-powers,axp2585-battery-power-supply", },
{}
};
MODULE_DEVICE_TABLE(of, axp2585_power_of_match);
static struct platform_driver axp2585_charger_driver = {
.driver = {
.name = "axp2585-battery-power-supply",
.of_match_table = axp2585_power_of_match,
},
.probe = axp2585_battery_probe,
.remove = axp2585_charger_remove,
.shutdown = axp2585_shutdown,
.suspend = axp2585_suspend,
.resume = axp2585_resume,
};
module_platform_driver(axp2585_charger_driver);
MODULE_AUTHOR("wangxiaoliang <wangxiaoliang@x-powers.com>");
MODULE_DESCRIPTION("axp2585 i2c driver");
MODULE_LICENSE("GPL");