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

337 lines
9 KiB
C

/*
* Battery charger driver for AW-POWERS
*
* Copyright (C) 2014 ALLWINNERTECH.
* Ming Li <liming@allwinnertech.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/power_supply.h>
#include "axp-cfg.h"
#include <linux/mfd/axp-mfd.h>
#ifdef CONFIG_AW_AXP81X
#include "axp81x/axp81x-sply.h"
#include "axp81x/axp81x-common.h"
static const struct axp_config_info *axp_config = &axp81x_config;
#endif
static aw_charge_type axp_usbcurflag = CHARGE_AC;
static aw_charge_type axp_usbvolflag = CHARGE_AC;
int axp_usb_connect;
s32 axp_usbvol(aw_charge_type type)
{
axp_usbvolflag = type;
return 0;
}
EXPORT_SYMBOL_GPL(axp_usbvol);
s32 axp_usbcur(aw_charge_type type)
{
axp_usbcurflag = type;
return 0;
}
EXPORT_SYMBOL_GPL(axp_usbcur);
s32 axp_usb_det(void)
{
u8 ret = 0;
if (axp_charger == NULL || axp_charger->master == NULL)
return ret;
axp_read(axp_charger->master, AXP_CHARGE_STATUS, &ret);
if (ret & 0x10)
axp_usb_connect = 1;
else
axp_usb_connect = 0;
return axp_usb_connect;
}
EXPORT_SYMBOL_GPL(axp_usb_det);
int axp_usb_is_connected(void)
{
return axp_usb_connect;
}
EXPORT_SYMBOL_GPL(axp_usb_is_connected);
s32 axp_usb_vbus_output(int high)
{
s32 ret = -1;
if (axp_charger == NULL || axp_charger->master == NULL)
return ret;
ret = axp_clr_bits_sync(axp_charger->master, AXP_HOTOVER_CTL, 0x10);
if (ret)
return ret;
if (high)
return axp_set_bits_sync(axp_charger->master,
AXP_IPS_SET, 0x04);
else
return axp_clr_bits_sync(axp_charger->master,
AXP_IPS_SET, 0x04);
}
EXPORT_SYMBOL_GPL(axp_usb_vbus_output);
static void axp_usb_ac_check_status(struct axp_charger *charger)
{
if (!axp_config->pmu_init_bc_en) {
charger->usb_valid = (((CHARGE_USB_20 == axp_usbcurflag)
|| (CHARGE_USB_30 == axp_usbcurflag))
&& charger->ext_valid);
charger->usb_adapter_valid = ((0 == charger->ac_valid)
&& (CHARGE_USB_20 != axp_usbcurflag)
&& (CHARGE_USB_30 != axp_usbcurflag)
&& (charger->ext_valid));
if (charger->in_short) {
charger->ac_valid = ((charger->usb_adapter_valid == 0)
&& (charger->usb_valid == 0)
&& (charger->ext_valid));
}
} else {
charger->usb_adapter_valid = 0;
}
power_supply_changed(&charger->ac);
power_supply_changed(&charger->usb);
DBG_PSY_MSG(DEBUG_CHG,
"usb_valid=%d ac_valid=%d usb_adapter_valid=%d\n",
charger->usb_valid, charger->ac_valid,
charger->usb_adapter_valid);
DBG_PSY_MSG(DEBUG_CHG, "usb_det=%d ac_det=%d \n",
charger->usb_det, charger->ac_det);
}
s32 axp_read_ac_chg(void)
{
s32 ac_chg = 0;
if (axp_charger->ac_valid || axp_charger->usb_adapter_valid)
ac_chg = 1;
else
ac_chg = 0;
return ac_chg;
}
EXPORT_SYMBOL_GPL(axp_read_ac_chg);
static void axp_charger_update_usb_state(unsigned long data)
{
struct axp_charger *charger = (struct axp_charger *)data;
axp_usb_ac_check_status(charger);
schedule_delayed_work(&(charger->usbwork), 0);
}
static void axp_usb(struct work_struct *work)
{
u8 val = 0;
DBG_PSY_MSG(DEBUG_CHG, "[axp_usb]axp_usbcurflag = %d\n", axp_usbcurflag);
axp_read(axp_charger->master, AXP_CHARGE_STATUS, &val);
if ((val & 0x02)) {
/* usb and ac in short*/
if ((val & 0x10) == 0x00) {
/*usb or usb adapter can not be used*/
DBG_PSY_MSG(DEBUG_CHG, "USB not insert!\n");
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_USB_20, 500);
} else if (CHARGE_USB_20 == axp_usbcurflag) {
DBG_PSY_MSG(DEBUG_CHG, "set usbpc_cur %d mA\n",
axp_config->pmu_usbpc_cur);
if (axp_config->pmu_usbpc_cur)
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_USB_20, axp_config->pmu_usbpc_cur);
else
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_USB_20, 500);
} else if (CHARGE_USB_30 == axp_usbcurflag) {
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_USB_30, 900);
} else {
DBG_PSY_MSG(DEBUG_CHG, "set usbcur %d mA\n",
axp_config->pmu_ac_cur);
if (axp_config->pmu_ac_cur) {
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_USB_20, axp_config->pmu_ac_cur);
} else {
DBG_PSY_MSG(DEBUG_CHG,
"%s: %d,set usbcur 2500 mA\n",
__func__, __LINE__);
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_USB_20, 2500);
}
}
if (!vbus_curr_limit_debug) {
DBG_PSY_MSG(DEBUG_CHG,
"vbus_curr_limit_debug = %d\n",
vbus_curr_limit_debug);
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_USB_20, 2500);
}
if (CHARGE_USB_20 == axp_usbvolflag) {
DBG_PSY_MSG(DEBUG_CHG, "set usbpc_vol %d mV\n",
axp_config->pmu_usbpc_vol);
if (axp_config->pmu_usbpc_vol)
axp_charger->chg_usb_ac_vol_set(axp_charger,
CHARGE_USB_20, axp_config->pmu_usbpc_vol);
} else if (CHARGE_USB_30 == axp_usbvolflag) {
axp_charger->chg_usb_ac_vol_set(axp_charger,
CHARGE_USB_30, 4700);
} else {
DBG_PSY_MSG(DEBUG_CHG, "set usbvol %d mV\n",
axp_config->pmu_ac_vol);
if (axp_config->pmu_ac_vol)
axp_charger->chg_usb_ac_vol_set(axp_charger,
CHARGE_USB_20, axp_config->pmu_ac_vol);
}
} else {
if ((val & 0x50) == 0x00) {/*usb and ac can not be used*/
DBG_PSY_MSG(DEBUG_CHG, "USB not insert!\n");
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_USB_20, 500);
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_AC, 1500);
} else if (CHARGE_USB_20 == axp_usbcurflag) {
DBG_PSY_MSG(DEBUG_CHG, "set usbpc_cur %d mA\n",
axp_config->pmu_usbpc_cur);
if (axp_config->pmu_usbpc_cur)
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_USB_20, axp_config->pmu_usbpc_cur);
else
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_USB_20, 500);
} else if (CHARGE_USB_30 == axp_usbcurflag)
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_USB_30, 900);
if (!vbus_curr_limit_debug) {
DBG_PSY_MSG(DEBUG_CHG,
"vbus_curr_limit_debug = %d\n",
vbus_curr_limit_debug);
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_AC, 2500);
}
if (CHARGE_USB_20 == axp_usbvolflag) {
DBG_PSY_MSG(DEBUG_CHG,
"set usbpc_vol %d mV\n",
axp_config->pmu_usbpc_vol);
if (axp_config->pmu_usbpc_vol) {
axp_charger->chg_usb_ac_vol_set(axp_charger,
CHARGE_USB_20, axp_config->pmu_usbpc_vol);
}
} else if (CHARGE_USB_30 == axp_usbvolflag) {
axp_charger->chg_usb_ac_vol_set(axp_charger, CHARGE_USB_30, 4700);
}
}
}
void axp_usbac_checkst(struct axp_charger *charger)
{
u8 val;
if (axp_config->pmu_init_bc_en) {
if (charger->in_short) {
axp_read(axp_charger->master, AXP_BC_DET_STATUS, &val);
spin_lock(&charger->charger_lock);
if (0x20 == (val&0xe0))
charger->usb_valid = 1;
else
charger->usb_valid = 0;
if (0x60 == (val&0xe0))
charger->ac_valid = 1;
else
charger->ac_valid = 0;
spin_unlock(&charger->charger_lock);
}
}
}
void axp_usbac_in(struct axp_charger *charger)
{
axp_usbcur(CHARGE_AC);
axp_usbvol(CHARGE_AC);
if (!axp_config->pmu_init_bc_en) {
DBG_PSY_MSG(DEBUG_CHG, "axp ac/usb in!\n");
if (timer_pending(&charger->usb_status_timer))
del_timer_sync(&charger->usb_status_timer);
/* must limit the current now,
* and will again fix it while usb/ac detect finished!
*/
if (axp_config->pmu_usbpc_cur)
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_USB_20, axp_config->pmu_usbpc_cur);
else
axp_charger->chg_usb_ac_current_set(axp_charger,
CHARGE_USB_20, 500);
/* this is about 3.5s,
* while the flag set in usb drivers after usb plugged
*/
mod_timer(&charger->usb_status_timer,
jiffies + msecs_to_jiffies(5000));
}
axp_usb_ac_check_status(charger);
}
void axp_usbac_out(struct axp_charger *charger)
{
if (!axp_config->pmu_init_bc_en) {
DBG_PSY_MSG(DEBUG_CHG, "axp22 ac/usb out!\n");
if (timer_pending(&charger->usb_status_timer))
del_timer_sync(&charger->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(&charger->usb_status_timer,
jiffies + msecs_to_jiffies(2000));
}
axp_usb_ac_check_status(charger);
}
s32 axp_chg_init(struct axp_charger *charger)
{
s32 ret = 0;
if (axp_config->pmu_init_bc_en) {
/* enable BC */
axp_set_bits(charger->master, AXP_BC_SET, 0x01);
} else {
setup_timer(&charger->usb_status_timer,
axp_charger_update_usb_state, (unsigned long)charger);
/* set usb cur-vol limit*/
INIT_DELAYED_WORK(&(charger->usbwork), axp_usb);
axp_usb_ac_check_status(charger);
power_supply_changed(&charger->ac);
power_supply_changed(&charger->usb);
schedule_delayed_work(&(charger->usbwork),
msecs_to_jiffies(30 * 1000));
}
return ret;
}
void axp_chg_exit(struct axp_charger *charger)
{
if (axp_config->pmu_init_bc_en) {
/* enable BC */
axp_clr_bits(charger->master, AXP_BC_SET, 0x01);
} else {
cancel_delayed_work_sync(&(charger->usbwork));
del_timer_sync(&(charger->usb_status_timer));
}
}