SmartAudio/lichee/linux-4.9/drivers/power/supply/axp/axp-charger.c

1345 lines
39 KiB
C

/*
* drivers/power/axp/axp-charger.c
* (C) Copyright 2010-2016
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Pannan <pannan@allwinnertech.com>
*
* axp charger APIs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/power_supply.h>
#include <linux/of_device.h>
#include <linux/interrupt.h>
#include "axp-core.h"
#include "axp-charger.h"
static int axp_power_key;
static enum AW_CHARGE_TYPE axp_usbcurflag = CHARGE_AC;
static enum AW_CHARGE_TYPE axp_usbvolflag = CHARGE_AC;
static struct axp_adc_res adc;
static bool battery_initialized;
static struct axp_config_info *axp_config_obj;
static int plug_debounce;
static DEFINE_SPINLOCK(axp_powerkey_lock);
void axp_powerkey_set(int value)
{
spin_lock(&axp_powerkey_lock);
axp_power_key = value;
spin_unlock(&axp_powerkey_lock);
}
EXPORT_SYMBOL_GPL(axp_powerkey_set);
int axp_powerkey_get(void)
{
int value;
spin_lock(&axp_powerkey_lock);
value = axp_power_key;
spin_unlock(&axp_powerkey_lock);
return value;
}
EXPORT_SYMBOL_GPL(axp_powerkey_get);
int axp_usbvol(enum AW_CHARGE_TYPE type)
{
axp_usbvolflag = type;
return 0;
}
EXPORT_SYMBOL_GPL(axp_usbvol);
int axp_usbcur(enum AW_CHARGE_TYPE type)
{
axp_usbcurflag = type;
return 0;
}
EXPORT_SYMBOL_GPL(axp_usbcur);
static inline void axp_read_adc(struct axp_charger_dev *chg_dev,
struct axp_adc_res *adc)
{
u8 tmp[2];
struct axp_regmap *map = chg_dev->chip->regmap;
axp_regmap_reads(map, chg_dev->spy_info->batt->bat_temp_offset,
2, tmp);
adc->ts_res = ((u16) tmp[0] << 8) | tmp[1];
}
static inline s32 axp_vts_to_temp(s32 data,
const struct axp_config_info *axp_config)
{
s32 temp;
if (data < 80 || !axp_config->pmu_bat_temp_enable)
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 inline s32 axp_vts_to_mV(u16 reg)
{
return ((s32)(((reg >> 8) << 4) | (reg & 0x000F))) * 800 / 1000;
}
static inline void axp_update_ictemp_status(struct axp_charger_dev *chg_dev)
{
u16 tmp;
u8 temp_val[2];
struct axp_regmap *map = chg_dev->chip->regmap;
axp_regmap_reads(map, chg_dev->pmic_temp_offset, 2, temp_val);
tmp = (temp_val[0] << 4) + (temp_val[1] & 0x0F);
chg_dev->ic_temp = (s32) tmp * 1063 / 10000 - 2667 / 10;
}
static inline void axp_update_temp_status(struct axp_charger_dev *chg_dev)
{
u16 tmp;
u8 temp_val[2];
s32 bat_temp_mv;
struct axp_regmap *map = chg_dev->chip->regmap;
chg_dev->adc = &adc;
axp_read_adc(chg_dev, &adc);
axp_regmap_reads(map, chg_dev->pmic_temp_offset, 2, temp_val);
tmp = (temp_val[0] << 4) + (temp_val[1] & 0x0F);
chg_dev->ic_temp = (s32) tmp * 1063 / 10000 - 2667 / 10;
tmp = chg_dev->adc->ts_res;
bat_temp_mv = axp_vts_to_mV(tmp);
chg_dev->bat_temp = axp_vts_to_temp(bat_temp_mv, axp_config_obj);
}
/*
* acin not presence + vbus no presence -> battery presence
*/
static int pwrsrc_parse_bat_det(struct axp_battery_info *batt, u8 val)
{
if (!(val & ((1 << batt->acpresent_bit) | (1 << batt->vbuspresent_bit))))
return 1;
else
return 0;
}
static int det_parse_bat_det(struct axp_battery_info *batt, u8 val)
{
if ((val & (1 << batt->det_bit)) && (val & (1 << batt->det_valid_bit)))
return 1;
else
return 0;
}
void axp_charger_update_state(struct axp_charger_dev *chg_dev)
{
u8 val;
u8 pwrsrc;
struct axp_ac_info *ac = chg_dev->spy_info->ac;
struct axp_usb_info *usb = chg_dev->spy_info->usb;
struct axp_battery_info *batt = chg_dev->spy_info->batt;
struct axp_regmap *map = chg_dev->chip->regmap;
#ifdef TYPE_C
struct axp_tc_info *tc = chg_dev->spy_info->tc;
#endif
/*sleep 10ms for adapter stable*/
msleep(10);
axp_regmap_read(map, batt->det_offset, &val);
axp_regmap_read(map, batt->pwrsrc_offset, &pwrsrc);
mutex_lock(&chg_dev->charger_lock);
if (batt->det_unused == 0) {
if (batt->det_valid == 1) {
chg_dev->bat_det = pwrsrc_parse_bat_det(batt, pwrsrc);
if (chg_dev->bat_det == 0)
chg_dev->bat_det = det_parse_bat_det(batt, val);
} else if (batt->det_valid == 0) {
chg_dev->bat_det = (val & 1 << batt->det_bit) ? 1 : 0;
}
} else if (batt->det_unused == 1) {
chg_dev->bat_det = 0;
}
mutex_unlock(&chg_dev->charger_lock);
axp_regmap_read(map, ac->det_offset, &val);
mutex_lock(&chg_dev->charger_lock);
chg_dev->ac_det = (val & 1 << ac->det_bit) ? 1 : 0;
mutex_unlock(&chg_dev->charger_lock);
if (usb->det_unused == 0) {
axp_regmap_read(map, usb->det_offset, &val);
mutex_lock(&chg_dev->charger_lock);
chg_dev->usb_det = (val & 1 << usb->det_bit) ? 1 : 0;
mutex_unlock(&chg_dev->charger_lock);
} else if (usb->det_unused == 1) {
chg_dev->usb_det = 0;
}
axp_regmap_read(map, ac->valid_offset, &val);
mutex_lock(&chg_dev->charger_lock);
chg_dev->ac_valid = (val & 1 << ac->valid_bit) ? 1 : 0;
mutex_unlock(&chg_dev->charger_lock);
if (usb->det_unused == 0) {
axp_regmap_read(map, usb->valid_offset, &val);
mutex_lock(&chg_dev->charger_lock);
chg_dev->usb_valid = (val & 1 << usb->valid_bit) ? 1 : 0;
mutex_unlock(&chg_dev->charger_lock);
} else if (usb->det_unused == 1) {
chg_dev->usb_valid = 0;
}
#ifdef TYPE_C
if (tc->det_unused == 0) {
axp_regmap_read(map, tc->det_offset, &val);
mutex_lock(&chg_dev->charger_lock);
chg_dev->tc_det = (val & 1 << tc->det_bit) ? 1 : 0;
mutex_unlock(&chg_dev->charger_lock);
} else if (tc->det_unused == 1) {
chg_dev->tc_det = 0;
}
if (tc->det_unused == 0) {
axp_regmap_read(map, tc->valid_offset, &val);
mutex_lock(&chg_dev->charger_lock);
chg_dev->tc_valid = (val & 1 << tc->valid_bit) ? 1 : 0;
mutex_unlock(&chg_dev->charger_lock);
} else if (tc->det_unused == 1) {
chg_dev->tc_valid = 0;
}
chg_dev->ext_valid = (chg_dev->ac_det ||
chg_dev->usb_det || chg_dev->tc_det);
#else
chg_dev->ext_valid = (chg_dev->ac_det || chg_dev->usb_det);
#endif
axp_regmap_read(map, ac->in_short_offset, &val);
mutex_lock(&chg_dev->charger_lock);
#ifdef AXP2585
chg_dev->in_short = (val & 1 << ac->in_short_bit) ? 1 : 0;
#else
chg_dev->in_short = 1;
#endif
if (!chg_dev->in_short)
chg_dev->ac_charging = chg_dev->ac_valid;
mutex_unlock(&chg_dev->charger_lock);
axp_regmap_read(map, batt->cur_direction_offset, &val);
mutex_lock(&chg_dev->charger_lock);
if (val & 1 << batt->cur_direction_bit)
chg_dev->bat_current_direction = 1;
else
chg_dev->bat_current_direction = 0;
mutex_unlock(&chg_dev->charger_lock);
axp_regmap_read(map, batt->chgstat_offset, &val);
mutex_lock(&chg_dev->charger_lock);
#ifdef AXP2585
chg_dev->charging = (((val & (7 << batt->chgstat_bit)) > 0) &&
((val & (7 << batt->chgstat_bit)) < 0x14)) ? 1 : 0;
#else
chg_dev->charging = (val & 1 << batt->chgstat_bit) ? 1 : 0;
#endif
mutex_unlock(&chg_dev->charger_lock);
}
void axp_charger_update_value(struct axp_charger_dev *chg_dev)
{
struct axp_ac_info *ac = chg_dev->spy_info->ac;
struct axp_usb_info *usb = chg_dev->spy_info->usb;
struct axp_battery_info *batt = chg_dev->spy_info->batt;
int bat_vol, bat_cur, bat_discur, ac_vol, ac_cur, usb_vol, usb_cur;
#ifdef TYPE_C
/* struct axp_tc_info *tc = chg_dev->spy_info->tc;
int tc_vol, tc_cur;
*/
#endif
bat_vol = batt->get_vbat(chg_dev);
bat_cur = batt->get_ibat(chg_dev);
bat_discur = batt->get_disibat(chg_dev);
ac_vol = ac->get_ac_voltage(chg_dev);
ac_cur = ac->get_ac_current(chg_dev);
usb_vol = usb->get_usb_voltage(chg_dev);
usb_cur = usb->get_usb_current(chg_dev);
#ifdef TYPE_C
/*tc_vol = tc->get_tc_voltage(chg_dev);
tc_cur = tc->get_tc_current(chg_dev);*/
#endif
mutex_lock(&chg_dev->charger_lock);
chg_dev->bat_vol = bat_vol;
chg_dev->bat_cur = bat_cur;
chg_dev->bat_discur = bat_discur;
chg_dev->ac_vol = ac_vol;
chg_dev->ac_cur = ac_cur;
chg_dev->usb_vol = usb_vol;
chg_dev->usb_cur = usb_cur;
#ifdef TYPE_C
/* chg_dev->tc_vol = tc_vol;
chg_dev->tc_cur = tc_cur;
*/
#endif
mutex_unlock(&chg_dev->charger_lock);
}
static void axp_usb_ac_check_status(struct axp_charger_dev *chg_dev)
{
chg_dev->usb_pc_charging = (((CHARGE_USB_20 == axp_usbcurflag)
|| (CHARGE_USB_30 == axp_usbcurflag))
&& (chg_dev->ext_valid));
chg_dev->usb_adapter_charging = ((0 == chg_dev->ac_valid)
&& (CHARGE_USB_20 != axp_usbcurflag)
&& (CHARGE_USB_30 != axp_usbcurflag)
&& (chg_dev->ext_valid));
if (chg_dev->in_short)
chg_dev->ac_charging = ((chg_dev->usb_adapter_charging == 0)
&& (chg_dev->usb_pc_charging == 0)
&& (chg_dev->ext_valid));
else
chg_dev->ac_charging = chg_dev->ac_valid;
power_supply_changed(chg_dev->ac);
power_supply_changed(chg_dev->usb);
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"ac_charging=%d\n", chg_dev->ac_charging);
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"usb_pc_charging=%d\n", chg_dev->usb_pc_charging);
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"usb_adapter_charging=%d\n",
chg_dev->usb_adapter_charging);
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"usb_det=%d ac_det=%d\n",
chg_dev->usb_det, chg_dev->ac_det);
}
static void axp_charger_update_usb_state(unsigned long data)
{
struct axp_charger_dev *chg_dev = (struct axp_charger_dev *)data;
axp_usb_ac_check_status(chg_dev);
if (chg_dev->bat_det)
schedule_delayed_work(&(chg_dev->usbwork), 0);
}
static void axp_usb(struct work_struct *work)
{
struct axp_charger_dev *chg_dev = container_of(work,
struct axp_charger_dev, usbwork.work);
struct axp_usb_info *usb = chg_dev->spy_info->usb;
struct axp_ac_info *ac = chg_dev->spy_info->ac;
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"[axp_usb] axp_usbcurflag = %d\n",
axp_usbcurflag);
axp_charger_update_state(chg_dev);
if (chg_dev->in_short) {
/* usb and ac in short*/
if (!chg_dev->usb_valid) {
/*usb or usb adapter can not be used*/
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"USB not insert!\n");
usb->set_usb_ihold(chg_dev, 500);
} else if (CHARGE_USB_20 == axp_usbcurflag) {
if (usb->usb_pc_cur) {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set usb_pc_cur %d mA\n",
usb->usb_pc_cur);
usb->set_usb_ihold(chg_dev, usb->usb_pc_cur);
} else {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set usb_pc_cur 500 mA\n");
usb->set_usb_ihold(chg_dev, 500);
}
} else if (CHARGE_USB_30 == axp_usbcurflag) {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set usb_pc_cur 900 mA\n");
usb->set_usb_ihold(chg_dev, 900);
} else {
/* usb adapter */
if (usb->usb_ad_cur) {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set usb_ad_cur %d mA\n",
usb->usb_ad_cur);
} else {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set usb_ad_cur no limit\n");
}
usb->set_usb_ihold(chg_dev, usb->usb_ad_cur);
}
if (CHARGE_USB_20 == axp_usbvolflag) {
if (usb->usb_pc_vol) {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set usb_pc_vol %d mV\n",
usb->usb_pc_vol);
usb->set_usb_vhold(chg_dev, usb->usb_pc_vol);
}
} else if (CHARGE_USB_30 == axp_usbvolflag) {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set usb_pc_vol 4700 mV\n");
usb->set_usb_vhold(chg_dev, 4700);
} else {
if (usb->usb_ad_vol) {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set usb_ad_vol %d mV\n",
usb->usb_ad_vol);
usb->set_usb_vhold(chg_dev, usb->usb_ad_vol);
}
}
} else {
if (!chg_dev->ac_valid && !chg_dev->usb_valid) {
/*usb and ac can not be used*/
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"AC and USB not insert!\n");
usb->set_usb_ihold(chg_dev, 500);
} else if (CHARGE_USB_20 == axp_usbcurflag) {
if (usb->usb_pc_cur) {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set usb_pc_cur %d mA\n",
usb->usb_pc_cur);
usb->set_usb_ihold(chg_dev, usb->usb_pc_cur);
} else {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set usb_pc_cur 500 mA\n");
usb->set_usb_ihold(chg_dev, 500);
}
} else if (CHARGE_USB_30 == axp_usbcurflag) {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set usb_pc_cur 900 mA\n");
usb->set_usb_ihold(chg_dev, 900);
} else {
if ((usb->usb_ad_cur)) {
AXP_DEBUG(AXP_CHG,
chg_dev->chip->pmu_num,
"set adapter cur %d mA\n",
usb->usb_ad_cur);
} else {
AXP_DEBUG(AXP_CHG,
chg_dev->chip->pmu_num,
"set adapter cur no limit\n");
}
usb->set_usb_ihold(chg_dev, usb->usb_ad_cur);
}
if (CHARGE_USB_20 == axp_usbvolflag) {
if (usb->usb_pc_vol) {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set usb_pc_vol %d mV\n",
usb->usb_pc_vol);
usb->set_usb_vhold(chg_dev, usb->usb_pc_vol);
}
} else if (CHARGE_USB_30 == axp_usbvolflag) {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set usb_pc_vol 4700 mV\n");
usb->set_usb_vhold(chg_dev, 4700);
} else {
if (ac->ac_vol) {
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num,
"set ac_vol %d mV\n",
ac->ac_vol);
ac->set_ac_vhold(chg_dev, ac->ac_vol);
}
}
}
}
void axp_battery_update_vol(struct axp_charger_dev *chg_dev)
{
s32 rest_vol = 0;
struct axp_battery_info *batt = chg_dev->spy_info->batt;
rest_vol = batt->get_rest_cap(chg_dev);
mutex_lock(&chg_dev->charger_lock);
if (rest_vol > 100) {
AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num,
"AXP rest_vol = %d\n", rest_vol);
chg_dev->rest_vol = 100;
} else {
chg_dev->rest_vol = rest_vol;
}
mutex_unlock(&chg_dev->charger_lock);
AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num,
"charger->rest_vol = %d\n", chg_dev->rest_vol);
}
static enum power_supply_property axp_battery_props[] = {
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
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_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_TEMP,
};
static enum power_supply_property axp_ac_props[] = {
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
};
static enum power_supply_property axp_usb_props[] = {
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
};
#ifdef TYPE_C
static enum power_supply_property axp_tc_props[] = {
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
};
#endif
static void axp_battery_check_status(struct axp_charger_dev *chg_dev,
union power_supply_propval *val)
{
if (chg_dev->bat_det) {
if (chg_dev->ext_valid) {
#ifdef TYPE_C
if (chg_dev->rest_vol == 96)
#else
if (chg_dev->rest_vol == 100)
#endif
val->intval = POWER_SUPPLY_STATUS_FULL;
else if (chg_dev->charging)
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
} else {
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
}
} else {
val->intval = POWER_SUPPLY_STATUS_FULL;
}
}
static void axp_battery_check_health(struct axp_charger_dev *chg_dev,
union power_supply_propval *val)
{
struct axp_battery_info *batt = chg_dev->spy_info->batt;
val->intval = batt->get_bat_health(chg_dev);
}
static s32 axp_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct axp_charger_dev *chg_dev = power_supply_get_drvdata(psy);
s32 ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
axp_battery_check_status(chg_dev, val);
break;
case POWER_SUPPLY_PROP_HEALTH:
axp_battery_check_health(chg_dev, val);
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = chg_dev->battery_info->technology;
break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
val->intval = chg_dev->coulumb_counter * 1000;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
val->intval = chg_dev->battery_info->voltage_max_design;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
val->intval = chg_dev->battery_info->voltage_min_design;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = chg_dev->bat_vol * 1000;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = (chg_dev->bat_cur - chg_dev->bat_discur) * 1000;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = psy->desc->name;
break;
case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
val->intval = chg_dev->battery_info->energy_full_design;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = chg_dev->rest_vol;
break;
case POWER_SUPPLY_PROP_ONLINE: {
/* in order to get hardware state,
* we must update charger state now.
* by sunny at 2012-12-23 11:06:15.
*/
axp_charger_update_state(chg_dev);
val->intval = !chg_dev->bat_current_direction;
break;
}
case POWER_SUPPLY_PROP_PRESENT:
val->intval = chg_dev->bat_det;
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = chg_dev->bat_temp * 10;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static s32 axp_ac_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct axp_charger_dev *chg_dev = power_supply_get_drvdata(psy);
s32 ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = psy->desc->name;
break;
case POWER_SUPPLY_PROP_PRESENT:
case POWER_SUPPLY_PROP_ONLINE:
val->intval = (chg_dev->ac_charging
|| chg_dev->usb_adapter_charging);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = chg_dev->ac_vol * 1000;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = chg_dev->ac_cur * 1000;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static s32 axp_usb_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct axp_charger_dev *chg_dev = power_supply_get_drvdata(psy);
s32 ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = psy->desc->name;
break;
case POWER_SUPPLY_PROP_PRESENT:
case POWER_SUPPLY_PROP_ONLINE:
val->intval = chg_dev->usb_pc_charging;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = chg_dev->usb_vol * 1000;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = chg_dev->usb_cur * 1000;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
#ifdef TYPE_C
static s32 axp_tc_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct axp_charger_dev *chg_dev = power_supply_get_drvdata(psy);
s32 ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = psy->desc->name;
break;
case POWER_SUPPLY_PROP_PRESENT:
case POWER_SUPPLY_PROP_ONLINE:
val->intval = chg_dev->tc_valid;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = chg_dev->tc_vol * 1000;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = chg_dev->tc_cur * 1000;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
#endif
static char *supply_list[] = {
"battery",
};
static const struct power_supply_desc batt_desc = {
.name = "battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.get_property = axp_battery_get_property,
.properties = axp_battery_props,
.num_properties = ARRAY_SIZE(axp_battery_props),
.use_for_apm = 1,
};
static const struct power_supply_desc ac_desc = {
.name = "ac",
.type = POWER_SUPPLY_TYPE_MAINS,
.get_property = axp_ac_get_property,
.properties = axp_ac_props,
.num_properties = ARRAY_SIZE(axp_ac_props),
};
static const struct power_supply_desc usb_desc = {
.name = "usb",
.type = POWER_SUPPLY_TYPE_USB,
.get_property = axp_usb_get_property,
.properties = axp_usb_props,
.num_properties = ARRAY_SIZE(axp_usb_props),
};
#ifdef TYPE_C
static const struct power_supply_desc tc_desc = {
.name = "tc",
.type = POWER_SUPPLY_TYPE_USB_TYPE_C,
.get_property = axp_tc_get_property,
.properties = axp_tc_props,
.num_properties = ARRAY_SIZE(axp_tc_props),
};
#endif
static struct power_supply_config psy_cfg = {
.supplied_to = supply_list,
.num_supplicants = ARRAY_SIZE(supply_list),
};
static void axp_charging_monitor(struct work_struct *work)
{
struct axp_charger_dev *chg_dev = container_of(work,
struct axp_charger_dev, work.work);
struct power_supply_config psy_cfg = {};
static s32 pre_rest_vol;
static bool pre_bat_curr_dir;
axp_charger_update_state(chg_dev);
/* if no battery exist, then return */
if (!chg_dev->bat_det) {
axp_update_ictemp_status(chg_dev);
AXP_DEBUG(AXP_MISC, chg_dev->chip->pmu_num,
"charger->ic_temp = %d\n", chg_dev->ic_temp);
schedule_delayed_work(&chg_dev->work, chg_dev->interval);
return;
}
/* if battery hadn't been detectd before, register it as power supply
* now */
if (!battery_initialized) {
psy_cfg.drv_data = chg_dev;
chg_dev->batt = power_supply_register(chg_dev->dev,
&batt_desc, &psy_cfg);
battery_initialized = true;
}
axp_charger_update_value(chg_dev);
axp_update_temp_status(chg_dev);
AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num,
"charger->ic_temp = %d\n", chg_dev->ic_temp);
AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num,
"charger->bat_temp = %d\n", chg_dev->bat_temp);
AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num,
"charger->bat_vol = %d\n", chg_dev->bat_vol);
AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num,
"charger->bat_cur = %d\n", chg_dev->bat_cur);
AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num,
"charger->bat_discur = %d\n", chg_dev->bat_discur);
AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num,
"charger->is_charging = %d\n", chg_dev->charging);
AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num,
"charger->bat_current_direction = %d\n",
chg_dev->bat_current_direction);
AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num,
"charger->ext_valid = %d\n", chg_dev->ext_valid);
if (!plug_debounce) {
if (chg_dev->private_debug)
chg_dev->private_debug(chg_dev);
} else {
plug_debounce = 0;
}
axp_battery_update_vol(chg_dev);
/* if battery volume changed, inform uevent */
if ((chg_dev->rest_vol - pre_rest_vol)
|| (chg_dev->bat_current_direction != pre_bat_curr_dir)
) {
AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num,
"battery vol change: %d->%d\n",
pre_rest_vol, chg_dev->rest_vol);
pre_rest_vol = chg_dev->rest_vol;
pre_bat_curr_dir = chg_dev->bat_current_direction;
power_supply_changed(chg_dev->batt);
}
/* reschedule for the next time */
schedule_delayed_work(&chg_dev->work, chg_dev->interval);
}
void axp_change(struct axp_charger_dev *chg_dev)
{
AXP_DEBUG(AXP_INT, chg_dev->chip->pmu_num, "battery state change\n");
axp_charger_update_state(chg_dev);
axp_charger_update_value(chg_dev);
if (chg_dev->bat_det && battery_initialized)
power_supply_changed(chg_dev->batt);
}
EXPORT_SYMBOL_GPL(axp_change);
void axp_usbac_in(struct axp_charger_dev *chg_dev)
{
struct axp_usb_info *usb = chg_dev->spy_info->usb;
axp_usbcur(CHARGE_AC);
axp_usbvol(CHARGE_AC);
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "axp ac/usb in!\n");
if (timer_pending(&chg_dev->usb_status_timer))
del_timer_sync(&chg_dev->usb_status_timer);
/* must limit the current now,
* and will again fix it while usb/ac detect finished!
*/
if (usb->usb_pc_cur)
usb->set_usb_ihold(chg_dev, usb->usb_pc_cur);
else
usb->set_usb_ihold(chg_dev, 500);
plug_debounce = 1;
/* this is about 3.5s,
* while the flag set in usb drivers after usb plugged
*/
axp_charger_update_state(chg_dev);
mod_timer(&chg_dev->usb_status_timer,
jiffies + msecs_to_jiffies(5000));
axp_usb_ac_check_status(chg_dev);
}
EXPORT_SYMBOL_GPL(axp_usbac_in);
void axp_usbac_out(struct axp_charger_dev *chg_dev)
{
AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "axp ac/usb out!\n");
if (timer_pending(&chg_dev->usb_status_timer))
del_timer_sync(&chg_dev->usb_status_timer);
/* if we plugged usb & ac at the same time,
* then unpluged ac quickly while the usb driver
* do not finished detecting,
* the charger type is error!So delay the charger type report 2s
*/
mod_timer(&chg_dev->usb_status_timer,
jiffies + msecs_to_jiffies(2000));
axp_usb_ac_check_status(chg_dev);
}
EXPORT_SYMBOL_GPL(axp_usbac_out);
void axp_capchange(struct axp_charger_dev *chg_dev)
{
struct power_supply_config psy_cfg = {};
AXP_DEBUG(AXP_INT, chg_dev->chip->pmu_num, "battery change\n");
axp_charger_update_state(chg_dev);
axp_charger_update_value(chg_dev);
axp_battery_update_vol(chg_dev);
if (chg_dev->bat_det) {
AXP_DEBUG(AXP_INT, chg_dev->chip->pmu_num, "rest_vol = %d\n",
chg_dev->rest_vol);
if (!battery_initialized) {
psy_cfg.drv_data = chg_dev;
chg_dev->batt = power_supply_register(chg_dev->dev,
&batt_desc, &psy_cfg);
schedule_delayed_work(&chg_dev->usbwork, 0);
schedule_delayed_work(&chg_dev->work, 0);
power_supply_changed(chg_dev->batt);
battery_initialized = true;
}
} else {
if (battery_initialized) {
cancel_delayed_work_sync(&chg_dev->work);
cancel_delayed_work_sync(&chg_dev->usbwork);
power_supply_unregister(chg_dev->batt);
chg_dev->batt = NULL;
battery_initialized = false;
}
}
}
EXPORT_SYMBOL_GPL(axp_capchange);
irqreturn_t axp_usb_in_isr(int irq, void *data)
{
struct axp_charger_dev *chg_dev = data;
axp_usb_connect = 1;
axp_change(chg_dev);
axp_usbac_in(chg_dev);
return IRQ_HANDLED;
}
irqreturn_t axp_usb_out_isr(int irq, void *data)
{
struct axp_charger_dev *chg_dev = data;
axp_usb_connect = 0;
axp_change(chg_dev);
axp_usbac_out(chg_dev);
return IRQ_HANDLED;
}
irqreturn_t axp_ac_in_isr(int irq, void *data)
{
struct axp_charger_dev *chg_dev = data;
axp_change(chg_dev);
axp_usbac_in(chg_dev);
return IRQ_HANDLED;
}
irqreturn_t axp_ac_out_isr(int irq, void *data)
{
struct axp_charger_dev *chg_dev = data;
axp_change(chg_dev);
axp_usbac_out(chg_dev);
return IRQ_HANDLED;
}
irqreturn_t axp_capchange_isr(int irq, void *data)
{
struct axp_charger_dev *chg_dev = data;
axp_capchange(chg_dev);
return IRQ_HANDLED;
}
irqreturn_t axp_change_isr(int irq, void *data)
{
struct axp_charger_dev *chg_dev = data;
axp_change(chg_dev);
return IRQ_HANDLED;
}
irqreturn_t axp_low_warning1_isr(int irq, void *data)
{
struct axp_charger_dev *chg_dev = data;
axp_change(chg_dev);
return IRQ_HANDLED;
}
irqreturn_t axp_low_warning2_isr(int irq, void *data)
{
struct axp_charger_dev *chg_dev = data;
axp_change(chg_dev);
return IRQ_HANDLED;
}
#ifdef TYPE_C
irqreturn_t axp_tc_in_isr(int irq, void *data)
{
struct axp_charger_dev *chg_dev = data;
axp_change(chg_dev);
/*axp_usbac_in(chg_dev);*/
return IRQ_HANDLED;
}
irqreturn_t axp_tc_out_isr(int irq, void *data)
{
struct axp_charger_dev *chg_dev = data;
axp_change(chg_dev);
/*axp_usbac_out(chg_dev);*/
return IRQ_HANDLED;
}
#endif
void axp_charger_suspend(struct axp_charger_dev *chg_dev)
{
struct axp_battery_info *batt = chg_dev->spy_info->batt;
axp_charger_update_state(chg_dev);
if (chg_dev->bat_det) {
schedule_delayed_work(&chg_dev->usbwork, 0);
flush_delayed_work(&chg_dev->usbwork);
cancel_delayed_work_sync(&chg_dev->work);
cancel_delayed_work_sync(&chg_dev->usbwork);
batt->set_chg_cur(chg_dev, batt->suspend_chgcur);
}
}
EXPORT_SYMBOL_GPL(axp_charger_suspend);
void axp_charger_resume(struct axp_charger_dev *chg_dev)
{
struct axp_battery_info *batt = chg_dev->spy_info->batt;
axp_charger_update_state(chg_dev);
axp_charger_update_value(chg_dev);
axp_battery_update_vol(chg_dev);
batt->set_chg_cur(chg_dev, batt->runtime_chgcur);
power_supply_changed(chg_dev->ac);
power_supply_changed(chg_dev->usb);
if (chg_dev->bat_det) {
power_supply_changed(chg_dev->batt);
schedule_delayed_work(&chg_dev->work, chg_dev->interval);
schedule_delayed_work(&chg_dev->usbwork,
msecs_to_jiffies(7 * 1000));
}
}
EXPORT_SYMBOL_GPL(axp_charger_resume);
void axp_charger_shutdown(struct axp_charger_dev *chg_dev)
{
struct axp_battery_info *batt = chg_dev->spy_info->batt;
axp_charger_update_state(chg_dev);
if (chg_dev->bat_det) {
schedule_delayed_work(&chg_dev->usbwork, 0);
flush_delayed_work(&chg_dev->usbwork);
cancel_delayed_work_sync(&chg_dev->work);
cancel_delayed_work_sync(&chg_dev->usbwork);
batt->set_chg_cur(chg_dev, batt->shutdown_chgcur);
}
}
EXPORT_SYMBOL_GPL(axp_charger_shutdown);
struct axp_charger_dev *axp_power_supply_register(struct device *dev,
struct axp_dev *axp_dev,
struct power_supply_info *battery_info,
struct axp_supply_info *info)
{
struct axp_charger_dev *chg_dev;
chg_dev = devm_kzalloc(dev, sizeof(*chg_dev), GFP_KERNEL);
if (chg_dev == NULL)
return NULL;
chg_dev->dev = dev;
chg_dev->spy_info = info;
chg_dev->chip = axp_dev;
chg_dev->battery_info = battery_info;
psy_cfg.drv_data = chg_dev;
mutex_init(&chg_dev->charger_lock);
axp_charger_update_state(chg_dev);
if (chg_dev->bat_det) {
chg_dev->batt = power_supply_register(dev,
&batt_desc, &psy_cfg);
if (IS_ERR(chg_dev->batt))
goto err_ps_register;
battery_initialized = true;
}
chg_dev->ac = power_supply_register(dev, &ac_desc, &psy_cfg);
if (IS_ERR(chg_dev->ac)) {
if (chg_dev->bat_det) {
power_supply_unregister(chg_dev->batt);
chg_dev->batt = NULL;
goto err_ps_register;
}
}
chg_dev->usb = power_supply_register(dev, &usb_desc, &psy_cfg);
if (IS_ERR(chg_dev->usb)) {
power_supply_unregister(chg_dev->ac);
chg_dev->ac = NULL;
if (chg_dev->bat_det) {
power_supply_unregister(chg_dev->batt);
chg_dev->batt = NULL;
goto err_ps_register;
}
}
#ifdef TYPE_C
chg_dev->tc = power_supply_register(dev, &tc_desc, &psy_cfg);
if (IS_ERR(chg_dev->tc)) {
power_supply_unregister(chg_dev->ac);
power_supply_unregister(chg_dev->usb);
chg_dev->ac = NULL;
chg_dev->usb = NULL;
if (chg_dev->bat_det) {
power_supply_unregister(chg_dev->batt);
chg_dev->batt = NULL;
goto err_ps_register;
}
}
if (info->tc->tc_vol && info->tc->set_tc_vhold)
info->tc->set_tc_vhold(chg_dev, info->tc->tc_vol);
#endif
if (info->ac->ac_vol && info->ac->set_ac_vhold)
info->ac->set_ac_vhold(chg_dev, info->ac->ac_vol);
if (info->usb->usb_pc_vol && info->usb->set_usb_vhold)
info->usb->set_usb_vhold(chg_dev, info->usb->usb_pc_vol);
if (info->batt->runtime_chgcur && info->batt->set_chg_cur)
info->batt->set_chg_cur(chg_dev, info->batt->runtime_chgcur);
setup_timer(&chg_dev->usb_status_timer,
axp_charger_update_usb_state, (unsigned long)chg_dev);
INIT_DELAYED_WORK(&(chg_dev->usbwork), axp_usb);
axp_usb_ac_check_status(chg_dev);
axp_battery_update_vol(chg_dev);
chg_dev->interval = msecs_to_jiffies(10 * 1000);
INIT_DELAYED_WORK(&chg_dev->work, axp_charging_monitor);
schedule_delayed_work(&chg_dev->work, chg_dev->interval);
if (timer_pending(&chg_dev->usb_status_timer))
del_timer_sync(&chg_dev->usb_status_timer);
mod_timer(&chg_dev->usb_status_timer,
jiffies + msecs_to_jiffies(20 * 1000));
return chg_dev;
err_ps_register:
return NULL;
}
EXPORT_SYMBOL_GPL(axp_power_supply_register);
void axp_power_supply_unregister(struct axp_charger_dev *chg_dev)
{
del_timer_sync(&chg_dev->usb_status_timer);
power_supply_unregister(chg_dev->usb);
chg_dev->batt = NULL;
power_supply_unregister(chg_dev->ac);
chg_dev->ac = NULL;
if (chg_dev->bat_det && battery_initialized) {
cancel_delayed_work_sync(&chg_dev->work);
cancel_delayed_work_sync(&chg_dev->usbwork);
power_supply_unregister(chg_dev->batt);
chg_dev->batt = NULL;
battery_initialized = false;
}
}
EXPORT_SYMBOL_GPL(axp_power_supply_unregister);
int axp_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_usbpc_vol, 4400);
AXP_OF_PROP_READ(pmu_ac_cur, 0);
AXP_OF_PROP_READ(pmu_usbpc_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_obj = axp_config;
return 0;
}
EXPORT_SYMBOL_GPL(axp_charger_dt_parse);
MODULE_DESCRIPTION("ALLWINNERTECH axp charger");
MODULE_AUTHOR("pannan");
MODULE_LICENSE("GPL");