oleavr-rgl-a500-mini-linux-.../drivers/power/axp_power/axp-charger.c
Ole André Vadla Ravnås 169c65d57e Initial commit
2022-05-07 01:01:45 +02:00

1107 lines
33 KiB
C

#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 bool battery_initialized;
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);
void axp_charger_update_state(struct axp_charger_dev *chg_dev)
{
u8 val;
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;
axp_regmap_read(map, ac->det_offset, &val);
spin_lock(&chg_dev->charger_lock);
chg_dev->ac_det = (val & 1 << ac->det_bit) ? 1 : 0;
spin_unlock(&chg_dev->charger_lock);
if (usb->det_unused == 0) {
axp_regmap_read(map, usb->det_offset, &val);
spin_lock(&chg_dev->charger_lock);
chg_dev->usb_det = (val & 1 << usb->det_bit) ? 1 : 0;
spin_unlock(&chg_dev->charger_lock);
} else if (usb->det_unused == 1) {
chg_dev->usb_det = 0;
}
axp_regmap_read(map, ac->valid_offset, &val);
spin_lock(&chg_dev->charger_lock);
chg_dev->ac_valid = (val & 1 << ac->valid_bit) ? 1 : 0;
spin_unlock(&chg_dev->charger_lock);
if (usb->det_unused == 0) {
axp_regmap_read(map, usb->valid_offset, &val);
spin_lock(&chg_dev->charger_lock);
chg_dev->usb_valid = (val & 1 << usb->valid_bit) ? 1 : 0;
spin_unlock(&chg_dev->charger_lock);
} else if (usb->det_unused == 1) {
chg_dev->usb_valid = 0;
}
chg_dev->ext_valid = (chg_dev->ac_det || chg_dev->usb_det);
axp_regmap_read(map, batt->det_offset, &val);
spin_lock(&chg_dev->charger_lock);
if (batt->det_unused == 0) {
if (batt->det_valid == 1) {
if ((chg_dev->ac_det && chg_dev->ac_valid)
|| (chg_dev->usb_det && chg_dev->usb_valid)) {
if ((val & (1 << batt->det_bit))
&& (val & (1 << batt->det_valid_bit)))
chg_dev->bat_det = 1;
else
chg_dev->bat_det = 0;
} else {
if (val & (1 << batt->det_bit))
chg_dev->bat_det = 1;
else
chg_dev->bat_det = 0;
}
} 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;
}
spin_unlock(&chg_dev->charger_lock);
axp_regmap_read(map, ac->in_short_offset, &val);
spin_lock(&chg_dev->charger_lock);
chg_dev->in_short = (val & 1 << ac->in_short_bit) ? 1 : 0;
if (!chg_dev->in_short)
chg_dev->ac_charging = chg_dev->ac_valid;
spin_unlock(&chg_dev->charger_lock);
axp_regmap_read(map, batt->cur_direction_offset, &val);
spin_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;
spin_unlock(&chg_dev->charger_lock);
axp_regmap_read(map, batt->chgstat_offset, &val);
spin_lock(&chg_dev->charger_lock);
chg_dev->charging = (val & 1 << batt->chgstat_bit) ? 1 : 0;
spin_unlock(&chg_dev->charger_lock);
}
static void axp_charger_get_bat_temp(struct axp_charger_dev *chg_dev)
{
struct axp_battery_info *batt = chg_dev->spy_info->batt;
int bat_temp = 30;
if (batt->get_bat_temp)
bat_temp = batt->get_bat_temp(chg_dev);
chg_dev->bat_temp = bat_temp;
}
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;
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);
axp_charger_get_bat_temp(chg_dev);
spin_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;
spin_unlock(&chg_dev->charger_lock);
}
int axp_charger_vts_to_temp(int data, struct axp_config_info *axp_config)
{
int temp;
if (data < 80)
temp = 30;
else if (data < axp_config->pmu_bat_temp_para16)
temp = 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 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 (chg_dev->usb_adapter_charging) {
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);
spin_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;
}
spin_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_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,
};
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) {
if (chg_dev->rest_vol == 100)
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 = container_of(psy,
struct axp_charger_dev, batt);
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_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->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:
axp_charger_get_bat_temp(chg_dev);
val->intval = chg_dev->bat_temp;
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 = container_of(psy,
struct axp_charger_dev, ac);
s32 ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = psy->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 = container_of(psy,
struct axp_charger_dev, usb);
s32 ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = psy->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;
}
static char *supply_list[] = {
"battery",
};
static void axp_battery_setup_psy(struct axp_charger_dev *chg_dev)
{
struct power_supply *batt = &chg_dev->batt;
struct power_supply *ac = &chg_dev->ac;
struct power_supply *usb = &chg_dev->usb;
struct power_supply_info *info = chg_dev->battery_info;
batt->name = "battery";
batt->use_for_apm = info->use_for_apm;
batt->type = POWER_SUPPLY_TYPE_BATTERY;
batt->get_property = axp_battery_get_property;
batt->properties = axp_battery_props;
batt->num_properties = ARRAY_SIZE(axp_battery_props);
ac->name = "ac";
ac->type = POWER_SUPPLY_TYPE_MAINS;
ac->get_property = axp_ac_get_property;
ac->supplied_to = supply_list,
ac->num_supplicants = ARRAY_SIZE(supply_list),
ac->properties = axp_ac_props;
ac->num_properties = ARRAY_SIZE(axp_ac_props);
usb->name = "usb";
usb->type = POWER_SUPPLY_TYPE_USB;
usb->get_property = axp_usb_get_property;
usb->supplied_to = supply_list,
usb->num_supplicants = ARRAY_SIZE(supply_list),
usb->properties = axp_usb_props;
usb->num_properties = ARRAY_SIZE(axp_usb_props);
}
static void axp_charging_monitor(struct work_struct *work)
{
struct axp_charger_dev *chg_dev = container_of(work,
struct axp_charger_dev, work.work);
static s32 pre_rest_vol;
static bool pre_bat_curr_dir;
axp_charger_update_state(chg_dev);
axp_charger_update_value(chg_dev);
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 (chg_dev->private_debug)
chg_dev->private_debug(chg_dev);
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);
/* this is about 3.5s,
* while the flag set in usb drivers after usb plugged
*/
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)
{
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) {
power_supply_register(chg_dev->dev, &chg_dev->batt);
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);
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;
}
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)
{
int ret;
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;
axp_battery_setup_psy(chg_dev);
axp_charger_update_state(chg_dev);
if (chg_dev->bat_det) {
ret = power_supply_register(dev, &chg_dev->batt);
if (ret)
goto err_ps_register;
battery_initialized = true;
}
ret = power_supply_register(dev, &chg_dev->ac);
if (ret) {
if (chg_dev->bat_det) {
power_supply_unregister(&chg_dev->batt);
goto err_ps_register;
}
}
ret = power_supply_register(dev, &chg_dev->usb);
if (ret) {
power_supply_unregister(&chg_dev->ac);
if (chg_dev->bat_det) {
power_supply_unregister(&chg_dev->batt);
goto err_ps_register;
}
}
spin_lock_init(&chg_dev->charger_lock);
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);
spin_lock(&chg_dev->charger_lock);
chg_dev->interval = msecs_to_jiffies(10 * 1000);
spin_unlock(&chg_dev->charger_lock);
INIT_DELAYED_WORK(&chg_dev->work, axp_charging_monitor);
if (chg_dev->bat_det) {
schedule_delayed_work(&chg_dev->work, chg_dev->interval);
schedule_delayed_work(&(chg_dev->usbwork),
msecs_to_jiffies(30 * 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);
power_supply_unregister(&chg_dev->ac);
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);
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_discharge_ltf, 3200);
AXP_OF_PROP_READ(pmu_discharge_htf, 237);
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);
return 0;
}
EXPORT_SYMBOL_GPL(axp_charger_dt_parse);