Squashed backup commit (no shallow-clone history issues) of xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream commit 9eece662e) plus all fixes applied to get everpal_defconfig building clean against a modern (GCC 15.2.0) toolchain -- see the previous commit message on the lineage-23.2 branch (also in this repo history is not available here since this is a squashed snapshot; full incremental history lives in the local DL380 checkout and the upstream GitHub remote). Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
1620 lines
45 KiB
C
1620 lines
45 KiB
C
|
|
#define pr_fmt(fmt) "[USBPD-PM]: %s: " fmt, __func__
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/string.h>
|
|
#include <linux/power_supply.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/workqueue.h>
|
|
#include "pd_policy_manager.h"
|
|
#include <mt-plat/prop_chgalgo_class.h>
|
|
|
|
#define PCA_PPS_CMD_RETRY_COUNT 2
|
|
#define BATT_MAX_CHG_VOLT 4460
|
|
|
|
#define BATT_FAST_CHG_CURR 5800
|
|
#define BUS_OVP_THRESHOLD 10500
|
|
|
|
#define BUS_VOLT_INIT_UP 300
|
|
|
|
#define BAT_VOLT_LOOP_LMT BATT_MAX_CHG_VOLT
|
|
#define BAT_CURR_LOOP_LMT BATT_FAST_CHG_CURR
|
|
#define BUS_VOLT_LOOP_LMT BUS_OVP_THRESHOLD
|
|
|
|
#define PM_WORK_RUN_INTERVAL 200
|
|
|
|
#define CHG_BAT_TEMP_MIN 100
|
|
#define CHG_BAT_TEMP_15 150
|
|
#define FFC_BAT_TEMP_OFFSET 20
|
|
#define CHG_BAT_TEMP_MAX 450
|
|
#define BAT_TEMP_300 300
|
|
#define BAT_TEMP_340 340
|
|
#define BAT_TEMP_360 360
|
|
#define BAT_TEMP_370 370
|
|
#define BAT_TEMP_380 380
|
|
#define BAT_TEMP_390 390
|
|
#define BAT_TEMP_400 400
|
|
#define BAT_TEMP_410 410
|
|
#define BAT_TEMP_420 420
|
|
#define BAT_TEMP_430 430
|
|
#define BAT_TEMP_440 440
|
|
#define BAT_TEMP_460 460
|
|
#define BAT_CURR_6000MA 5800
|
|
#define BAT_CURR_5700MA 5700
|
|
#define BAT_CURR_5400MA 5050
|
|
#define BAT_CURR_5000MA 5300
|
|
#define BAT_CURR_4500MA 4400
|
|
#define BAT_CURR_4000MA 3900
|
|
#define BAT_CURR_3900MA 3600
|
|
#define BAT_CURR_3800MA 3700
|
|
#define BAT_CURR_3500MA 3500
|
|
#define BAT_CURR_3000MA 3000
|
|
#define BAT_CURR_2800MA 2800
|
|
#define BAT_CURR_2500MA 2500
|
|
#define BAT_CURR_2000MA 2000
|
|
#define BAT_CURR_1500MA 1500
|
|
#define BAT_CURR_1000MA 1000
|
|
#define BAT_CURR_100MA 100
|
|
#define CHG_CUR_VOLT 4240
|
|
#define CHG_CUR_VOLT2 4440
|
|
#define CHG_CUR_VOLT3 4470
|
|
#define CHG_TEMP_STEP1 1
|
|
#define CHG_TEMP_STEP2 2
|
|
#define CHG_TEMP_STEP3 3
|
|
#define CHG_TEMP_STEP4 4
|
|
#define CHG_TEMP_STEP5 5
|
|
#define CHG_TEMP_STEP6 6
|
|
#define CHG_TEMP_OFFSET 10
|
|
|
|
extern int get_jeita_lcd_on_off(void);
|
|
extern bool get_charging_call_state(void);
|
|
extern bool is_kernel_power_off_charging(void);
|
|
|
|
enum {
|
|
PM_ALGO_RET_OK,
|
|
PM_ALGO_RET_THERM_FAULT,
|
|
PM_ALGO_RET_OTHER_FAULT,
|
|
PM_ALGO_RET_CHG_DISABLED,
|
|
PM_ALGO_RET_TAPER_DONE,
|
|
};
|
|
|
|
enum {
|
|
SC8551_2_1_CHARGER_MODE = 0,
|
|
SC8551_1_1_CHARGER_MODE,
|
|
};
|
|
|
|
|
|
static const struct pdpm_config pm_config = {
|
|
.bat_volt_lp_lmt = BAT_VOLT_LOOP_LMT,
|
|
.bat_curr_lp_lmt = BAT_CURR_LOOP_LMT,
|
|
.bus_volt_lp_lmt = BUS_VOLT_LOOP_LMT,
|
|
.bus_curr_lp_lmt = (BAT_CURR_LOOP_LMT >> 1),
|
|
|
|
.fc2_taper_current = 2000,
|
|
.fc2_steps = 1,
|
|
|
|
.min_adapter_volt_required = 11000,
|
|
.min_adapter_curr_required = 2000,
|
|
|
|
.min_vbat_for_cp = 3500,
|
|
|
|
.cp_sec_enable = false,
|
|
.fc2_disable_sw = true,
|
|
};
|
|
|
|
static struct usbpd_pm *__pdpm;
|
|
|
|
static int fc2_taper_timer;
|
|
static int ibus_lmt_change_timer;
|
|
|
|
int pdpm_is_charge_pump_enable(void)
|
|
{
|
|
if(__pdpm)
|
|
return __pdpm->is_cp_ok;
|
|
return 0;
|
|
}
|
|
|
|
/*******************************PD API******************************/
|
|
static inline int check_typec_attached_snk(struct tcpc_device *tcpc)
|
|
{
|
|
if (tcpm_inquire_typec_attach_state(tcpc) != TYPEC_ATTACHED_SNK)
|
|
return -EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
static int usbpd_pps_enable_charging(struct usbpd_pm *pdpm, bool en,
|
|
u32 mV, u32 mA)
|
|
{
|
|
int ret, cnt = 0;
|
|
|
|
if (check_typec_attached_snk(pdpm->tcpc) < 0)
|
|
return -EINVAL;
|
|
pr_err("en = %d, %dmV, %dmA\n", en, mV, mA);
|
|
|
|
do {
|
|
if (en)
|
|
ret = tcpm_set_apdo_charging_policy(pdpm->tcpc,
|
|
DPM_CHARGING_POLICY_PPS, mV, mA, NULL);
|
|
else
|
|
ret = tcpm_reset_pd_charging_policy(pdpm->tcpc, NULL);
|
|
cnt++;
|
|
} while (ret != TCP_DPM_RET_SUCCESS && cnt < PCA_PPS_CMD_RETRY_COUNT);
|
|
|
|
if (ret != TCP_DPM_RET_SUCCESS)
|
|
pr_err("fail(%d)\n", ret);
|
|
return ret > 0 ? -ret : ret;
|
|
}
|
|
|
|
static bool usbpd_get_pps_status(struct usbpd_pm *pdpm)
|
|
{
|
|
int ret, apdo_idx = -1;
|
|
struct tcpm_power_cap_val apdo_cap = {0};
|
|
u8 cap_idx;
|
|
//u32 vta_meas, ita_meas, prog_mv;
|
|
|
|
pr_err("++\n");
|
|
|
|
if (check_typec_attached_snk(pdpm->tcpc) < 0)
|
|
return false;
|
|
|
|
|
|
if (!pdpm->is_pps_en_unlock) {
|
|
pr_err("pps en is locked\n");
|
|
return false;
|
|
}
|
|
|
|
if (!tcpm_inquire_pd_pe_ready(pdpm->tcpc)) {
|
|
pr_err("PD PE not ready\n");
|
|
return false;
|
|
}
|
|
|
|
/* select TA boundary */
|
|
cap_idx = 0;
|
|
while (1) {
|
|
ret = tcpm_inquire_pd_source_apdo(pdpm->tcpc,
|
|
TCPM_POWER_CAP_APDO_TYPE_PPS,
|
|
&cap_idx, &apdo_cap);
|
|
if (ret != TCP_DPM_RET_SUCCESS) {
|
|
pr_err("inquire pd apdo fail(%d)\n", ret);
|
|
break;
|
|
}
|
|
|
|
pr_err("cap_idx[%d], %d mv ~ %d mv, %d ma, pl: %d\n", cap_idx,
|
|
apdo_cap.min_mv, apdo_cap.max_mv, apdo_cap.ma,
|
|
apdo_cap.pwr_limit);
|
|
|
|
/*
|
|
* !(apdo_cap.min_mv <= data->vcap_min &&
|
|
* apdo_cap.max_mv >= data->vcap_max &&
|
|
* apdo_cap.ma >= data->icap_min)
|
|
*/
|
|
if (apdo_cap.max_mv < pm_config.min_adapter_volt_required ||
|
|
apdo_cap.ma < pm_config.min_adapter_curr_required)
|
|
continue;
|
|
if (apdo_idx == -1) {
|
|
apdo_idx = cap_idx;
|
|
pr_err("select potential cap_idx[%d]\n", cap_idx);
|
|
pdpm->apdo_max_volt = apdo_cap.max_mv;
|
|
pdpm->apdo_max_curr = apdo_cap.ma;
|
|
}
|
|
}
|
|
if (apdo_idx != -1){
|
|
ret = usbpd_pps_enable_charging(pdpm, true, 5000, 3000);
|
|
if (ret != TCP_DPM_RET_SUCCESS)
|
|
return false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int usbpd_select_pdo(struct usbpd_pm *pdpm, u32 mV, u32 mA)
|
|
{
|
|
int ret, cnt = 0;
|
|
|
|
if (check_typec_attached_snk(pdpm->tcpc) < 0)
|
|
return -EINVAL;
|
|
pr_err("%dmV, %dmA\n", mV, mA);
|
|
|
|
if (!tcpm_inquire_pd_connected(pdpm->tcpc)) {
|
|
pr_err("pd not connected\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
do {
|
|
ret = tcpm_dpm_pd_request(pdpm->tcpc, mV, mA, NULL);
|
|
cnt++;
|
|
} while (ret != TCP_DPM_RET_SUCCESS && cnt < PCA_PPS_CMD_RETRY_COUNT);
|
|
|
|
if (ret != TCP_DPM_RET_SUCCESS)
|
|
pr_err("fail(%d)\n", ret);
|
|
return ret > 0 ? -ret : ret;
|
|
}
|
|
|
|
static int pca_pps_tcp_notifier_call(struct notifier_block *nb,
|
|
unsigned long event, void *data)
|
|
{
|
|
//struct usbpd_pm *pdpm = container_of(nb, struct usbpd_pm, nb);
|
|
struct tcp_notify *noti = data;
|
|
|
|
switch (event) {
|
|
case TCP_NOTIFY_PD_STATE:
|
|
switch (noti->pd_state.connected) {
|
|
case PD_CONNECT_NONE:
|
|
pr_err("detached\n");
|
|
__pdpm->is_pps_en_unlock = false;
|
|
__pdpm->hrst_cnt = 0;
|
|
break;
|
|
case PD_CONNECT_HARD_RESET:
|
|
__pdpm->hrst_cnt++;
|
|
pr_err("pd hardreset, cnt = %d\n",
|
|
__pdpm->hrst_cnt);
|
|
__pdpm->is_pps_en_unlock = false;
|
|
break;
|
|
case PD_CONNECT_PE_READY_SNK_APDO:
|
|
if (__pdpm->hrst_cnt < 5) {
|
|
pr_err("en unlock\n");
|
|
__pdpm->is_pps_en_unlock = true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
power_supply_changed(__pdpm->usb_psy);
|
|
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
/************************6360 API***************************/
|
|
/*
|
|
* Set AICR & ICHG of switching charger
|
|
*
|
|
* @aicr: setting of AICR
|
|
* @ichg: setting of ICHG
|
|
*/
|
|
static int usbpd_pm_set_swchg_cap(struct usbpd_pm *pdpm, u32 aicr)
|
|
{
|
|
int ret;
|
|
u32 ichg;
|
|
|
|
ret = charger_dev_set_input_current(pdpm->sw_chg, aicr * 1000);
|
|
if (ret < 0) {
|
|
pr_err("set aicr fail(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
//set ichg
|
|
/* 90% charging efficiency */
|
|
ichg = (90 * pdpm->cp.vbus_volt * aicr / 100) / pdpm->cp.vbat_volt;
|
|
|
|
ret = charger_dev_set_charging_current(pdpm->sw_chg, ichg * 1000);
|
|
if (ret < 0) {
|
|
pr_err("set_ichg fail(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
pr_info("AICR = %dmA, ICHG = %dmA\n", aicr, ichg);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Enable charging of switching charger
|
|
* For divide by two algorithm, according to swchg_ichg to decide enable or not
|
|
*
|
|
* @en: enable/disable
|
|
*/
|
|
static int usbpd_pm_enable_sw(struct usbpd_pm *pdpm, bool en)
|
|
{
|
|
int ret;
|
|
pr_info("en = %d\n", en);
|
|
if (en) {
|
|
ret = charger_dev_enable(pdpm->sw_chg, true);
|
|
if (ret < 0) {
|
|
pr_err("en swchg fail(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
ret = charger_dev_enable_hz(pdpm->sw_chg, false);
|
|
if (ret < 0) {
|
|
pr_err("disable hz fail(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
} else {
|
|
ret = charger_dev_enable_hz(pdpm->sw_chg, true);
|
|
if (ret < 0) {
|
|
pr_err("disable hz fail(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
ret = charger_dev_enable(pdpm->sw_chg, false);
|
|
if (ret < 0) {
|
|
pr_err("en swchg fail(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
pdpm->sw.charge_enabled = en;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get ibus current of switching charger
|
|
*
|
|
*/
|
|
static int usbpd_pm_update_sw_status(struct usbpd_pm *pdpm)
|
|
{
|
|
int ret, ibus;
|
|
|
|
ret = charger_dev_get_adc(pdpm->sw_chg, ADC_CHANNEL_IBUS, &ibus,
|
|
&ibus);
|
|
if (ret < 0) {
|
|
pr_err("get swchg ibus fail(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
pdpm->sw.ibus_curr = ibus / 1000;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void usbpd_check_tcpc(struct usbpd_pm *pdpm)
|
|
{
|
|
if (!pdpm->tcpc) {
|
|
pdpm->tcpc = tcpc_dev_get_by_name("type_c_port0");
|
|
if (!pdpm->tcpc) {
|
|
pr_err("get tcpc dev fail\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void usbpd_check_pca_chg_swchg(struct usbpd_pm *pdpm)
|
|
{
|
|
if (!pdpm->sw_chg) {
|
|
pdpm->sw_chg = get_charger_by_name("primary_chg");
|
|
if (!pdpm->sw_chg) {
|
|
pr_err("get primary_chg fail\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void usbpd_check_charger_psy(struct usbpd_pm *pdpm)
|
|
{
|
|
if (!pdpm->usb_psy) {
|
|
pdpm->usb_psy = power_supply_get_by_name("charger");
|
|
if (!pdpm->usb_psy)
|
|
pr_err("usb psy not found!\n");
|
|
}
|
|
}
|
|
|
|
static void usbpd_check_battery_psy(struct usbpd_pm *pdpm)
|
|
{
|
|
if (!pdpm->battery_psy) {
|
|
pdpm->battery_psy = power_supply_get_by_name("battery");
|
|
if (!pdpm->battery_psy)
|
|
pr_err("battery psy not found!\n");
|
|
}
|
|
}
|
|
|
|
static void usbpd_check_apdo_psy(struct usbpd_pm *pdpm)
|
|
{
|
|
if (!pdpm->apdo_psy) {
|
|
pdpm->apdo_psy = power_supply_get_by_name("usb");
|
|
if (!pdpm->apdo_psy)
|
|
pr_err("apdo psy not found!\n");
|
|
}
|
|
}
|
|
|
|
static void usbpd_check_cp_psy(struct usbpd_pm *pdpm)
|
|
{
|
|
if (!pdpm->cp_psy) {
|
|
if (pm_config.cp_sec_enable)
|
|
pdpm->cp_psy = power_supply_get_by_name("sc8551-master");
|
|
else
|
|
pdpm->cp_psy = power_supply_get_by_name("sc8551-standalone");
|
|
if (pdpm->cp_psy)
|
|
return;
|
|
if (pm_config.cp_sec_enable)
|
|
pdpm->cp_psy = power_supply_get_by_name("ln8000-master");
|
|
else
|
|
pdpm->cp_psy = power_supply_get_by_name("ln8000-standalone");
|
|
if (!pdpm->cp_psy)
|
|
pr_err("cp_psy not found\n");
|
|
}
|
|
}
|
|
|
|
static void usbpd_check_cp_sec_psy(struct usbpd_pm *pdpm)
|
|
{
|
|
if (!pdpm->cp_sec_psy) {
|
|
pdpm->cp_sec_psy = power_supply_get_by_name("sc8551-slave");
|
|
if (pdpm->cp_psy)
|
|
return;
|
|
pdpm->cp_sec_psy = power_supply_get_by_name("ln8000-slave");
|
|
if (!pdpm->cp_sec_psy)
|
|
pr_err("cp_sec_psy not found\n");
|
|
}
|
|
}
|
|
|
|
static void usbpd_pm_update_cp_status(struct usbpd_pm *pdpm)
|
|
{
|
|
int ret;
|
|
union power_supply_propval val = {0,};
|
|
|
|
usbpd_check_cp_psy(pdpm);
|
|
|
|
if (!pdpm->cp_psy)
|
|
return;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_BATTERY_VOLTAGE, &val);
|
|
if (!ret)
|
|
pdpm->cp.vbat_volt = val.intval;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_BUS_CURRENT, &val);
|
|
if (!ret)
|
|
pdpm->cp.ibus_curr = val.intval;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_BUS_VOLTAGE, &val);
|
|
if (!ret)
|
|
pdpm->cp.vbus_volt = val.intval;
|
|
|
|
ret = power_supply_get_property(pdpm->battery_psy,
|
|
POWER_SUPPLY_PROP_CURRENT_NOW, &val);
|
|
if (!ret) {
|
|
pdpm->cp.battery_curr = val.intval/1000;
|
|
pdpm->cp.ibat_curr = pdpm->cp.battery_curr;
|
|
}
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_VBUS_ERROR_STATUS, &val);
|
|
if (!ret)
|
|
{
|
|
pr_err(">>>>vbus error state : %02x\n", val.intval);
|
|
pdpm->cp.vbus_error_low = (val.intval >> 5) & 0x01;
|
|
pdpm->cp.vbus_error_high = (val.intval >> 4) & 0x01;
|
|
}
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_BUS_TEMPERATURE, &val);
|
|
if (!ret)
|
|
pdpm->cp.bus_temp = val.intval;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_BATTERY_TEMPERATURE, &val);
|
|
if (!ret)
|
|
pdpm->cp.bat_temp = val.intval;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_DIE_TEMPERATURE, &val);
|
|
if (!ret)
|
|
pdpm->cp.die_temp = val.intval;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_BATTERY_PRESENT, &val);
|
|
if (!ret)
|
|
pdpm->cp.batt_pres = val.intval;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_VBUS_PRESENT, &val);
|
|
if (!ret)
|
|
pdpm->cp.vbus_pres = val.intval;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_CHARGING_ENABLED, &val);
|
|
if (!ret)
|
|
pdpm->cp.charge_enabled = val.intval;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_ALARM_STATUS, &val);
|
|
if (!ret) {
|
|
pdpm->cp.bat_ovp_alarm = !!(val.intval & BAT_OVP_ALARM_MASK);
|
|
pdpm->cp.bat_ocp_alarm = !!(val.intval & BAT_OCP_ALARM_MASK);
|
|
pdpm->cp.bus_ovp_alarm = !!(val.intval & BUS_OVP_ALARM_MASK);
|
|
pdpm->cp.bus_ocp_alarm = !!(val.intval & BUS_OCP_ALARM_MASK);
|
|
pdpm->cp.bat_ucp_alarm = !!(val.intval & BAT_UCP_ALARM_MASK);
|
|
pdpm->cp.bat_therm_alarm = !!(val.intval & BAT_THERM_ALARM_MASK);
|
|
pdpm->cp.bus_therm_alarm = !!(val.intval & BUS_THERM_ALARM_MASK);
|
|
pdpm->cp.die_therm_alarm = !!(val.intval & DIE_THERM_ALARM_MASK);
|
|
}
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_FAULT_STATUS, &val);
|
|
if (!ret) {
|
|
pdpm->cp.bat_ovp_fault = !!(val.intval & BAT_OVP_FAULT_MASK);
|
|
pdpm->cp.bat_ocp_fault = !!(val.intval & BAT_OCP_FAULT_MASK);
|
|
pdpm->cp.bus_ovp_fault = !!(val.intval & BUS_OVP_FAULT_MASK);
|
|
pdpm->cp.bus_ocp_fault = !!(val.intval & BUS_OCP_FAULT_MASK);
|
|
pdpm->cp.bat_therm_fault = !!(val.intval & BAT_THERM_FAULT_MASK);
|
|
pdpm->cp.bus_therm_fault = !!(val.intval & BUS_THERM_FAULT_MASK);
|
|
pdpm->cp.die_therm_fault = !!(val.intval & DIE_THERM_FAULT_MASK);
|
|
}
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_DIRECT_CHARGE, &val);
|
|
if (!ret) {
|
|
// pdpm->cp.direct_charge = val.intval;
|
|
pr_err("[%s] get direct_charge = %d", __func__, pdpm->cp.direct_charge);
|
|
}
|
|
|
|
}
|
|
|
|
static int usbpd_pm_set_cp_charge_mode(struct usbpd_pm *pdpm, u8 charge_mode)
|
|
{
|
|
int ret;
|
|
union power_supply_propval val = {0,};
|
|
|
|
usbpd_check_cp_psy(pdpm);
|
|
|
|
if (!pdpm->cp_psy)
|
|
return -ENODEV;
|
|
|
|
if(!charge_mode)
|
|
val.intval = SC8551_2_1_CHARGER_MODE;
|
|
else
|
|
val.intval = SC8551_1_1_CHARGER_MODE;
|
|
ret = power_supply_set_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_CHARGE_MODE, &val);
|
|
return ret;
|
|
}
|
|
|
|
static int usbpd_pm_check_cp_charge_mode(struct usbpd_pm *pdpm)
|
|
{
|
|
int ret;
|
|
union power_supply_propval val = {0,};
|
|
|
|
usbpd_check_cp_psy(pdpm);
|
|
|
|
if (!pdpm->cp_psy)
|
|
return -ENODEV;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_SC_CHARGE_MODE, &val);
|
|
if(!ret){
|
|
if(!val.intval)
|
|
pdpm->cp.charge_mode = SC8551_2_1_CHARGER_MODE;
|
|
else
|
|
pdpm->cp.charge_mode = SC8551_1_1_CHARGER_MODE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void usbpd_pm_update_cp_sec_status(struct usbpd_pm *pdpm)
|
|
{
|
|
int ret;
|
|
union power_supply_propval val = {0,};
|
|
|
|
if (!pm_config.cp_sec_enable)
|
|
return;
|
|
|
|
usbpd_check_cp_sec_psy(pdpm);
|
|
|
|
if (!pdpm->cp_sec_psy)
|
|
return;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_sec_psy,
|
|
POWER_SUPPLY_PROP_SC_BUS_CURRENT, &val);
|
|
if (!ret)
|
|
pdpm->cp_sec.ibus_curr = val.intval;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_sec_psy,
|
|
POWER_SUPPLY_PROP_CHARGING_ENABLED, &val);
|
|
if (!ret)
|
|
pdpm->cp_sec.charge_enabled = val.intval;
|
|
}
|
|
|
|
|
|
static int usbpd_pm_enable_cp(struct usbpd_pm *pdpm, bool enable)
|
|
{
|
|
int ret;
|
|
union power_supply_propval val = {0,};
|
|
|
|
usbpd_check_cp_psy(pdpm);
|
|
|
|
if (!pdpm->cp_psy)
|
|
return -ENODEV;
|
|
|
|
val.intval = enable;
|
|
ret = power_supply_set_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_CHARGING_ENABLED, &val);
|
|
|
|
return ret;
|
|
}
|
|
static int usbpd_pm_enable_cp_sec(struct usbpd_pm *pdpm, bool enable)
|
|
{
|
|
int ret;
|
|
union power_supply_propval val = {0,};
|
|
|
|
usbpd_check_cp_sec_psy(pdpm);
|
|
|
|
if (!pdpm->cp_sec_psy)
|
|
return -ENODEV;
|
|
|
|
val.intval = enable;
|
|
ret = power_supply_set_property(pdpm->cp_sec_psy,
|
|
POWER_SUPPLY_PROP_CHARGING_ENABLED, &val);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int usbpd_pm_check_cp_enabled(struct usbpd_pm *pdpm)
|
|
{
|
|
int ret;
|
|
union power_supply_propval val = {0,};
|
|
|
|
usbpd_check_cp_psy(pdpm);
|
|
|
|
if (!pdpm->cp_psy)
|
|
return -ENODEV;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_psy,
|
|
POWER_SUPPLY_PROP_CHARGING_ENABLED, &val);
|
|
if (!ret)
|
|
pdpm->cp.charge_enabled = !!val.intval;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int usbpd_pm_check_cp_sec_enabled(struct usbpd_pm *pdpm)
|
|
{
|
|
int ret;
|
|
union power_supply_propval val = {0,};
|
|
|
|
usbpd_check_cp_sec_psy(pdpm);
|
|
|
|
if (!pdpm->cp_sec_psy)
|
|
return -ENODEV;
|
|
|
|
ret = power_supply_get_property(pdpm->cp_sec_psy,
|
|
POWER_SUPPLY_PROP_CHARGING_ENABLED, &val);
|
|
if (!ret)
|
|
pdpm->cp_sec.charge_enabled = !!val.intval;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************************/
|
|
static void usbpd_pm_evaluate_src_caps(struct usbpd_pm *pdpm)
|
|
{
|
|
bool retValue;
|
|
union power_supply_propval pval = {0, };
|
|
struct power_supply *battery_psy;
|
|
|
|
retValue = usbpd_get_pps_status(pdpm);
|
|
if (retValue)
|
|
pdpm->pps_supported = true;
|
|
else
|
|
pdpm->pps_supported = false;
|
|
|
|
if (pdpm->pps_supported) {
|
|
pdpm->lcdon_curr_step = 0;
|
|
pdpm->lcdoff_curr_step = 0;
|
|
pr_notice("PPS supported, preferred APDO pos:%d, max volt:%d, current:%d\n",
|
|
pdpm->apdo_selected_pdo,
|
|
pdpm->apdo_max_volt,
|
|
pdpm->apdo_max_curr);
|
|
pval.intval = (pdpm->apdo_max_volt / 1000) * (pdpm->apdo_max_curr / 1000);
|
|
power_supply_set_property(pdpm->apdo_psy,
|
|
POWER_SUPPLY_PROP_APDO_MAX, &pval);
|
|
power_supply_changed(pdpm->apdo_psy);
|
|
battery_psy = power_supply_get_by_name("battery");
|
|
if (NULL != battery_psy)
|
|
power_supply_changed(battery_psy);
|
|
else
|
|
pr_err("battery_psy is null\n");
|
|
}
|
|
else
|
|
pr_notice("Not qualified PPS adapter\n");
|
|
|
|
}
|
|
|
|
|
|
#define TAPER_TIMEOUT (5000 / PM_WORK_RUN_INTERVAL)
|
|
#define IBUS_CHANGE_TIMEOUT (500 / PM_WORK_RUN_INTERVAL)
|
|
|
|
static int bat_step(struct usbpd_pm *pdpm, int cur) {
|
|
int step = 0;
|
|
|
|
if (cur <= BAT_CURR_3000MA)
|
|
pdpm->cp.direct_charge = 0;
|
|
else if (cur >= BAT_CURR_3500MA)
|
|
pdpm->cp.direct_charge = 0;
|
|
|
|
pr_err("[%s] curr = %d, direct_charge = %d, pdpm->pd_authen %d\n",
|
|
__func__, cur, pdpm->cp.direct_charge, pdpm->pd_authen);
|
|
|
|
#if 0
|
|
if (pdpm->cp.ibat_curr < cur)
|
|
step = pm_config.fc2_steps;
|
|
else if (pdpm->cp.ibat_curr > cur + BAT_CURR_100MA)
|
|
step = -pm_config.fc2_steps;
|
|
#else
|
|
if (pdpm->cp.battery_curr < cur)
|
|
step = pm_config.fc2_steps;
|
|
else if (pdpm->cp.battery_curr > cur + BAT_CURR_100MA)
|
|
step = -pm_config.fc2_steps;
|
|
#endif
|
|
return step;
|
|
}
|
|
|
|
static int bat_lcdon_temp(struct usbpd_pm *pdpm, int temp)
|
|
{
|
|
int bat_temp = temp;
|
|
int step_ibat = 0;
|
|
|
|
if (bat_temp < BAT_TEMP_300) {
|
|
if (!pdpm->lcdon_curr_step)
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3000MA);
|
|
else
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3000MA);
|
|
} else if (bat_temp >= BAT_TEMP_300 && bat_temp < BAT_TEMP_340) {
|
|
if (pdpm->lcdon_curr_step <= CHG_TEMP_STEP1) {
|
|
pdpm->lcdon_curr_step = CHG_TEMP_STEP1;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3000MA);
|
|
} else if (pdpm->lcdon_curr_step == CHG_TEMP_STEP2) {
|
|
if (bat_temp <= BAT_TEMP_340 - CHG_TEMP_OFFSET) {
|
|
pdpm->lcdon_curr_step = CHG_TEMP_STEP1;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3000MA);
|
|
} else {
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3000MA);
|
|
}
|
|
} else {
|
|
pdpm->lcdon_curr_step = CHG_TEMP_STEP2;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3000MA);
|
|
}
|
|
} else if (bat_temp >= BAT_TEMP_340 && bat_temp < BAT_TEMP_360) {
|
|
if (pdpm->lcdon_curr_step <= CHG_TEMP_STEP2) {
|
|
pdpm->lcdon_curr_step = CHG_TEMP_STEP2;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3000MA);
|
|
} else if (pdpm->lcdon_curr_step == CHG_TEMP_STEP3) {
|
|
if (bat_temp <= BAT_TEMP_360 - CHG_TEMP_OFFSET) {
|
|
pdpm->lcdon_curr_step = CHG_TEMP_STEP2;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3000MA);
|
|
} else {
|
|
step_ibat = bat_step(pdpm, BAT_CURR_2500MA);
|
|
}
|
|
} else {
|
|
pdpm->lcdon_curr_step = CHG_TEMP_STEP3;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_2500MA);
|
|
}
|
|
} else if (bat_temp >= BAT_TEMP_360 && bat_temp < BAT_TEMP_380) {
|
|
if (pdpm->lcdon_curr_step <= CHG_TEMP_STEP3) {
|
|
pdpm->lcdon_curr_step = CHG_TEMP_STEP3;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_2500MA);
|
|
} else if (pdpm->lcdon_curr_step == CHG_TEMP_STEP4) {
|
|
if (bat_temp <= BAT_TEMP_380 - CHG_TEMP_OFFSET) {
|
|
pdpm->lcdon_curr_step = CHG_TEMP_STEP3;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_2500MA);
|
|
} else {
|
|
step_ibat = bat_step(pdpm, BAT_CURR_2000MA);
|
|
}
|
|
} else {
|
|
pdpm->lcdon_curr_step = CHG_TEMP_STEP4;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_2000MA);
|
|
}
|
|
} else if (bat_temp >= BAT_TEMP_380 && bat_temp < BAT_TEMP_440) {
|
|
if (pdpm->lcdon_curr_step <= CHG_TEMP_STEP4) {
|
|
pdpm->lcdon_curr_step = CHG_TEMP_STEP4;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_2000MA);
|
|
} else if (pdpm->lcdon_curr_step == CHG_TEMP_STEP5) {
|
|
if (bat_temp <= BAT_TEMP_410 - CHG_TEMP_OFFSET) {
|
|
pdpm->lcdon_curr_step = CHG_TEMP_STEP4;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_2000MA);
|
|
} else {
|
|
step_ibat = bat_step(pdpm, BAT_CURR_1500MA);
|
|
}
|
|
} else {
|
|
pdpm->lcdon_curr_step = CHG_TEMP_STEP5;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_1500MA);
|
|
}
|
|
} else if (bat_temp >= BAT_TEMP_440) {
|
|
pdpm->lcdon_curr_step = CHG_TEMP_STEP5;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_1500MA);
|
|
}
|
|
pr_err("[%s]lcdon_curr_step=%d, direct_charge = %d",
|
|
__func__, pdpm->lcdon_curr_step, pdpm->cp.direct_charge);
|
|
return step_ibat;
|
|
}
|
|
|
|
static int bat_lcdoff_temp(struct usbpd_pm *pdpm, int temp)
|
|
{
|
|
int bat_temp = temp;
|
|
int step_ibat = 1;
|
|
|
|
if (bat_temp < BAT_TEMP_390) {
|
|
if (!pdpm->lcdoff_curr_step)
|
|
step_ibat = bat_step(pdpm, BAT_CURR_6000MA);
|
|
else
|
|
step_ibat = bat_step(pdpm, BAT_CURR_5000MA);
|
|
} else if (bat_temp >= BAT_TEMP_390 && bat_temp < BAT_TEMP_400) {
|
|
if (pdpm->lcdoff_curr_step <= CHG_TEMP_STEP1) {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP1;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_5000MA);
|
|
} else if (pdpm->lcdoff_curr_step == CHG_TEMP_STEP2) {
|
|
if (bat_temp <= BAT_TEMP_400 - CHG_TEMP_OFFSET) {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP1;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_5000MA);
|
|
} else {
|
|
step_ibat = bat_step(pdpm, BAT_CURR_4500MA);
|
|
}
|
|
} else {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP2;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_4500MA);
|
|
}
|
|
} else if (bat_temp >= BAT_TEMP_400 && bat_temp < BAT_TEMP_410) {
|
|
if (pdpm->lcdoff_curr_step <= CHG_TEMP_STEP2) {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP2;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_4500MA);
|
|
} else if (pdpm->lcdoff_curr_step == CHG_TEMP_STEP3) {
|
|
if (bat_temp <= BAT_TEMP_420 - CHG_TEMP_OFFSET) {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP2;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_4500MA);
|
|
} else {
|
|
step_ibat = bat_step(pdpm, BAT_CURR_4000MA);
|
|
}
|
|
} else {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP3;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_4000MA);
|
|
}
|
|
} else if (bat_temp >= BAT_TEMP_410 && bat_temp < BAT_TEMP_420) {
|
|
if (pdpm->lcdoff_curr_step <= CHG_TEMP_STEP3) {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP3;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_4000MA);
|
|
} else if (pdpm->lcdoff_curr_step == CHG_TEMP_STEP4) {
|
|
if (bat_temp <= BAT_TEMP_440 - CHG_TEMP_OFFSET) {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP3;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_4000MA);
|
|
} else {
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3500MA);
|
|
}
|
|
} else {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP4;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3500MA);
|
|
}
|
|
} else if (bat_temp >= BAT_TEMP_420 && bat_temp < BAT_TEMP_430) {
|
|
if (pdpm->lcdoff_curr_step <= CHG_TEMP_STEP4) {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP4;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3500MA);
|
|
} else if (pdpm->lcdoff_curr_step == CHG_TEMP_STEP5) {
|
|
if (bat_temp <= BAT_TEMP_410 - CHG_TEMP_OFFSET) {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP4;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3500MA);
|
|
} else {
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3000MA);
|
|
}
|
|
} else {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP5;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3000MA);
|
|
}
|
|
} else if (bat_temp >= BAT_TEMP_430 && bat_temp < BAT_TEMP_440) {
|
|
if (pdpm->lcdoff_curr_step <= CHG_TEMP_STEP5) {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP5;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3000MA);
|
|
} else if (pdpm->lcdoff_curr_step == CHG_TEMP_STEP6) {
|
|
if (bat_temp <= BAT_TEMP_410 - CHG_TEMP_OFFSET) {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP5;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3000MA);
|
|
} else {
|
|
step_ibat = bat_step(pdpm, BAT_CURR_2500MA);
|
|
}
|
|
} else {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP6;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_2500MA);
|
|
}
|
|
} else if (bat_temp >= BAT_TEMP_440) {
|
|
pdpm->lcdoff_curr_step = CHG_TEMP_STEP6;
|
|
step_ibat = bat_step(pdpm, BAT_CURR_2500MA);
|
|
}
|
|
pr_err("[%s]lcdoff_curr_step=%d, direct_charge = %d",
|
|
__func__, pdpm->lcdoff_curr_step, pdpm->cp.direct_charge);
|
|
return step_ibat;
|
|
}
|
|
|
|
static int get_battery_temp(void)
|
|
{
|
|
struct power_supply *battery_psy;
|
|
union power_supply_propval pval = {0, };
|
|
|
|
battery_psy = power_supply_get_by_name("battery");
|
|
if(battery_psy)
|
|
power_supply_get_property(battery_psy,
|
|
POWER_SUPPLY_PROP_TEMP, &pval);
|
|
return pval.intval;
|
|
}
|
|
|
|
extern int charger_manager_get_thermal_mitigation_current(void);
|
|
|
|
static int battery_sw_jeita(struct usbpd_pm *pdpm)
|
|
{
|
|
int step_temp = 0;
|
|
int step_ibat = 0;
|
|
int step_vbat = 0;
|
|
int bat_temp = 0;
|
|
int therm_curr = 0;
|
|
int step_therm = 0;
|
|
|
|
bat_temp = get_battery_temp();
|
|
therm_curr = charger_manager_get_thermal_mitigation_current() / 1000;
|
|
if (bat_temp >= CHG_BAT_TEMP_MIN && bat_temp <= CHG_BAT_TEMP_MAX) {
|
|
pdpm->pps_temp_flag = true;
|
|
if (pdpm->cp.vbat_volt < CHG_CUR_VOLT) {
|
|
if (pdpm->therm_flag)
|
|
step_vbat = bat_step(pdpm, BAT_CURR_6000MA);
|
|
else
|
|
step_vbat = bat_step(pdpm, BAT_CURR_6000MA);
|
|
} else if (pdpm->cp.vbat_volt >= CHG_CUR_VOLT && pdpm->cp.vbat_volt < CHG_CUR_VOLT2)
|
|
step_vbat = bat_step(pdpm, BAT_CURR_5400MA);
|
|
else
|
|
step_vbat = bat_step(pdpm, BAT_CURR_3900MA);
|
|
|
|
if (bat_temp <= CHG_BAT_TEMP_15)
|
|
step_vbat = bat_step(pdpm, BAT_CURR_3800MA);
|
|
|
|
if (!pdpm->pd_authen) {
|
|
if (pdpm->cp.vbat_volt <= CHG_CUR_VOLT2)
|
|
step_temp = bat_step(pdpm, BAT_CURR_5400MA);
|
|
else
|
|
step_temp = -pm_config.fc2_steps;
|
|
step_vbat = min(step_vbat, step_temp);
|
|
}
|
|
if(pdpm->therm_flag) {
|
|
if (get_jeita_lcd_on_off()) {
|
|
step_therm = bat_step(pdpm, therm_curr);
|
|
step_ibat = bat_lcdon_temp(pdpm, bat_temp);
|
|
} else {
|
|
step_ibat = bat_lcdoff_temp(pdpm, bat_temp);
|
|
step_therm = bat_step(pdpm, therm_curr);
|
|
}
|
|
step_ibat = min(step_therm, step_ibat);
|
|
} else {
|
|
step_therm = bat_step(pdpm, therm_curr);
|
|
if(get_jeita_lcd_on_off()) {
|
|
step_ibat = bat_step(pdpm, BAT_CURR_3000MA);
|
|
step_ibat = min(step_therm, step_ibat);
|
|
} else {
|
|
step_ibat = step_therm;
|
|
}
|
|
}
|
|
} else {
|
|
pdpm->pps_temp_flag = false;
|
|
}
|
|
pr_err(">>>>temp %d pdpm->cp.ibus_curr %d step_ibat %d, step_vbat %d, lcd_on %d, pd_authen %d, therm_curr %d, therm_flag %d\n",
|
|
bat_temp, pdpm->cp.ibus_curr, step_ibat, step_vbat, get_jeita_lcd_on_off(), pdpm->pd_authen, therm_curr, pdpm->therm_flag);
|
|
return min(step_vbat, step_ibat);
|
|
}
|
|
|
|
static int usbpd_pm_fc2_charge_algo(struct usbpd_pm *pdpm)
|
|
{
|
|
int steps;
|
|
int sw_ctrl_steps = 0;
|
|
int hw_ctrl_steps = 0;
|
|
int step_vbat = 0;
|
|
int step_ibus = 0;
|
|
int step_ibat = 0;
|
|
int step_bat_reg = 0;
|
|
int ibus_total = 0;
|
|
|
|
static int ibus_limit;
|
|
|
|
if (ibus_limit == 0)
|
|
ibus_limit = pm_config.bus_curr_lp_lmt;// + 400;
|
|
|
|
/* reduce bus current in cv loop */
|
|
if (pdpm->cp.vbat_volt > pm_config.bat_volt_lp_lmt - 50) {
|
|
if (ibus_lmt_change_timer++ > IBUS_CHANGE_TIMEOUT) {
|
|
ibus_lmt_change_timer = 0;
|
|
ibus_limit = pm_config.bus_curr_lp_lmt;// - 400;
|
|
}
|
|
} else if (pdpm->cp.vbat_volt < pm_config.bat_volt_lp_lmt - 250) {
|
|
ibus_limit = pm_config.bus_curr_lp_lmt;// + 400;
|
|
ibus_lmt_change_timer = 0;
|
|
} else {
|
|
ibus_lmt_change_timer = 0;
|
|
}
|
|
|
|
/* battery voltage loop*/
|
|
if (pdpm->cp.vbat_volt > pm_config.bat_volt_lp_lmt)
|
|
step_vbat = -pm_config.fc2_steps;
|
|
else if (pdpm->cp.vbat_volt < pm_config.bat_volt_lp_lmt - 7)
|
|
step_vbat = pm_config.fc2_steps;;
|
|
|
|
// pr_err("[%s] direct_charge = %d", __func__, pdpm->cp.direct_charge);
|
|
/* battery charge current loop*/
|
|
#if 0
|
|
if(!pdpm->cp.direct_charge){
|
|
if (pdpm->cp.ibat_curr < pm_config.bat_curr_lp_lmt )
|
|
step_ibat = pm_config.fc2_steps;
|
|
else if (pdpm->cp.ibat_curr > pm_config.bat_curr_lp_lmt + 100)
|
|
step_ibat = -pm_config.fc2_steps;
|
|
}
|
|
else{
|
|
if (pdpm->cp.ibat_curr < (pm_config.bat_curr_lp_lmt >> 1))
|
|
step_ibat = pm_config.fc2_steps;
|
|
else if (pdpm->cp.ibat_curr > (pm_config.bat_curr_lp_lmt + 100) >> 1)
|
|
step_ibat = -pm_config.fc2_steps;
|
|
}
|
|
#else
|
|
if(!pdpm->cp.direct_charge){
|
|
if (pdpm->cp.battery_curr < pm_config.bat_curr_lp_lmt )
|
|
step_ibat = pm_config.fc2_steps;
|
|
else if (pdpm->cp.battery_curr > pm_config.bat_curr_lp_lmt + 100)
|
|
step_ibat = -pm_config.fc2_steps;
|
|
}
|
|
else{
|
|
if (pdpm->cp.battery_curr < (pm_config.bat_curr_lp_lmt >> 1))
|
|
step_ibat = pm_config.fc2_steps;
|
|
else if (pdpm->cp.battery_curr > (pm_config.bat_curr_lp_lmt + 100) >> 1)
|
|
step_ibat = -pm_config.fc2_steps;
|
|
}
|
|
#endif
|
|
step_ibat = battery_sw_jeita(pdpm);
|
|
|
|
/* bus current loop*/
|
|
ibus_total = pdpm->cp.ibus_curr ;//+ pdpm->sw.ibus_curr;
|
|
if (pm_config.cp_sec_enable)
|
|
ibus_total += pdpm->cp_sec.ibus_curr;
|
|
|
|
if (ibus_total < ibus_limit - 50)
|
|
step_ibus = pm_config.fc2_steps;
|
|
else if (ibus_total > ibus_limit)
|
|
step_ibus = -pm_config.fc2_steps;
|
|
|
|
/* hardware regulation loop*/
|
|
/*if (pdpm->cp.vbat_reg || pdpm->cp.ibat_reg)
|
|
step_bat_reg = 5 * (-pm_config.fc2_steps);
|
|
else
|
|
step_bat_reg = pm_config.fc2_steps;*/
|
|
step_bat_reg = pm_config.fc2_steps;
|
|
|
|
sw_ctrl_steps = min(min(step_vbat, step_ibus), step_ibat);
|
|
sw_ctrl_steps = min(sw_ctrl_steps, step_bat_reg);
|
|
|
|
/* hardware alarm loop */
|
|
if (pdpm->cp.bat_ocp_alarm
|
|
|| pdpm->cp.bus_ocp_alarm || pdpm->cp.bus_ovp_alarm)
|
|
hw_ctrl_steps = -pm_config.fc2_steps;
|
|
else
|
|
hw_ctrl_steps = pm_config.fc2_steps;
|
|
|
|
usbpd_pm_check_cp_charge_mode(pdpm);
|
|
/* check if cp disabled due to other reason*/
|
|
usbpd_pm_check_cp_enabled(pdpm);
|
|
pr_err(">>>>cp enable bit %d\n", pdpm->cp.charge_enabled);
|
|
|
|
if (pm_config.cp_sec_enable) {
|
|
usbpd_pm_check_cp_sec_enabled(pdpm);
|
|
pr_err(">>>>cp sec enable bit %d\n", pdpm->cp_sec.charge_enabled);
|
|
}
|
|
|
|
if (pdpm->cp.bat_therm_fault ) { /* battery overheat, stop charge*/
|
|
pr_notice("bat_therm_fault:%d\n", pdpm->cp.bat_therm_fault);
|
|
return PM_ALGO_RET_THERM_FAULT;
|
|
} else if (pdpm->cp.bat_ocp_fault || pdpm->cp.bus_ocp_fault
|
|
|| pdpm->cp.bat_ovp_fault || pdpm->cp.bus_ovp_fault) {
|
|
pr_notice("bat_ocp_fault:%d, bus_ocp_fault:%d, bat_ovp_fault:%d, \
|
|
bus_ovp_fault:%d\n", pdpm->cp.bat_ocp_fault,
|
|
pdpm->cp.bus_ocp_fault, pdpm->cp.bat_ovp_fault,
|
|
pdpm->cp.bus_ovp_fault);
|
|
return PM_ALGO_RET_OTHER_FAULT; /* go to switch, and try to ramp up*/
|
|
} else if ( !pdpm->cp.charge_enabled ||(pdpm->cp.charge_enabled && (pdpm->cp.vbus_error_low
|
|
|| pdpm->cp.vbus_error_high)) || (pm_config.cp_sec_enable && !pdpm->cp_sec.charge_enabled && !pdpm->cp_sec_stopped)){
|
|
pr_notice("cp.charge_enabled:%d %d %d,cp_sec.charge_enabled:%d\n",
|
|
pdpm->cp.charge_enabled, pdpm->cp.vbus_error_low, pdpm->cp.vbus_error_high,pdpm->cp_sec.charge_enabled);
|
|
return PM_ALGO_RET_CHG_DISABLED;
|
|
}
|
|
if(pdpm->cp.direct_charge != pdpm->cp.charge_mode)
|
|
{
|
|
pr_err(">>>>SC8551 direct charge %d charge mode %d\n",
|
|
pdpm->cp.direct_charge, pdpm->cp.charge_mode);
|
|
usbpd_pm_enable_cp(pdpm, false);
|
|
usbpd_pm_check_cp_enabled(pdpm);
|
|
if(pdpm->cp.direct_charge)
|
|
usbpd_pm_set_cp_charge_mode(pdpm, SC8551_1_1_CHARGER_MODE);
|
|
else
|
|
usbpd_pm_set_cp_charge_mode(pdpm, SC8551_2_1_CHARGER_MODE);
|
|
pr_err(">>>>SC8551 direct charge %d charge mode %d end\n",
|
|
pdpm->cp.direct_charge, pdpm->cp.charge_mode);
|
|
return PM_ALGO_RET_CHG_DISABLED;
|
|
}
|
|
|
|
/* charge pump taper charge */
|
|
if (pdpm->cp.vbat_volt > pm_config.bat_volt_lp_lmt - 50
|
|
&& pdpm->cp.ibat_curr < pm_config.fc2_taper_current) {
|
|
if (fc2_taper_timer++ > TAPER_TIMEOUT) {
|
|
pr_notice("charge pump taper charging done\n");
|
|
fc2_taper_timer = 0;
|
|
return PM_ALGO_RET_TAPER_DONE;
|
|
}
|
|
} else {
|
|
fc2_taper_timer = 0;
|
|
}
|
|
|
|
/*TODO: customer can add hook here to check system level
|
|
* thermal mitigation*/
|
|
|
|
|
|
steps = min(sw_ctrl_steps, hw_ctrl_steps);
|
|
|
|
pr_err(">>>>>>%d %d %d sw %d hw %d all %d\n",
|
|
step_vbat, step_ibat, step_ibus, sw_ctrl_steps, hw_ctrl_steps, steps);
|
|
|
|
pdpm->request_voltage += steps * 20;
|
|
|
|
if (pdpm->request_voltage > pdpm->apdo_max_volt - 300)
|
|
pdpm->request_voltage = pdpm->apdo_max_volt - 300;
|
|
|
|
return PM_ALGO_RET_OK;
|
|
}
|
|
|
|
static const unsigned char *pm_str[] = {
|
|
"PD_PM_STATE_ENTRY",
|
|
"PD_PM_STATE_FC2_ENTRY",
|
|
"PD_PM_STATE_FC2_ENTRY_1",
|
|
"PD_PM_STATE_FC2_ENTRY_2",
|
|
"PD_PM_STATE_FC2_ENTRY_3",
|
|
"PD_PM_STATE_FC2_TUNE",
|
|
"PD_PM_STATE_FC2_EXIT",
|
|
};
|
|
|
|
static void usbpd_pm_move_state(struct usbpd_pm *pdpm, enum pm_state state)
|
|
{
|
|
#if 1
|
|
pr_err("state change:%s -> %s\n",
|
|
pm_str[pdpm->state], pm_str[state]);
|
|
#endif
|
|
pdpm->state = state;
|
|
}
|
|
|
|
static int usbpd_pm_sm(struct usbpd_pm *pdpm)
|
|
{
|
|
int ret;
|
|
int rc = 0;
|
|
static int tune_vbus_retry;
|
|
static bool stop_sw;
|
|
static bool recover;
|
|
int bat_temp;
|
|
|
|
bat_temp = get_battery_temp();
|
|
pr_err(">>>>>>>>>>>state phase :%d\n", pdpm->state);
|
|
pr_err(">>>>>vbus_vol %d vbat_vol %d vout %d\n", pdpm->cp.vbus_volt, pdpm->cp.vbat_volt, pdpm->cp.vout_volt);
|
|
pr_err(">>>>>ibus_curr %d ibat_curr %d mt6360_ibat_curr %d\n", pdpm->cp.ibus_curr + pdpm->cp_sec.ibus_curr, pdpm->cp.ibat_curr,pdpm->cp.battery_curr);
|
|
switch (pdpm->state) {
|
|
case PD_PM_STATE_ENTRY:
|
|
stop_sw = false;
|
|
recover = false;
|
|
|
|
if (pdpm->cp.vbat_volt < pm_config.min_vbat_for_cp) {
|
|
pr_notice("batt_volt-%d, waiting...\n", pdpm->cp.vbat_volt);
|
|
} else if (pdpm->cp.vbat_volt > pm_config.bat_volt_lp_lmt - 100) {
|
|
pr_notice("batt_volt-%d is too high for cp,\
|
|
charging with switch charger\n",
|
|
pdpm->cp.vbat_volt);
|
|
// usbpd_pm_move_state(pdpm, PD_PM_STATE_FC2_EXIT);
|
|
} else if ((bat_temp >= BAT_TEMP_460)
|
|
|| bat_temp <= CHG_BAT_TEMP_MIN
|
|
|| get_charging_call_state()) {
|
|
pr_notice("temp is %d, high or low, waiting...\n", bat_temp);
|
|
} else {
|
|
pr_notice("batt_volt-%d is ok, start flash charging\n",
|
|
pdpm->cp.vbat_volt);
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_FC2_ENTRY);
|
|
}
|
|
break;
|
|
|
|
case PD_PM_STATE_FC2_ENTRY:
|
|
if (pm_config.fc2_disable_sw) {
|
|
usbpd_pm_set_swchg_cap(pdpm, 3000);
|
|
usbpd_pm_enable_sw(pdpm, false);
|
|
pdpm->is_cp_ok = true;
|
|
if (!pdpm->sw.charge_enabled)
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_FC2_ENTRY_1);
|
|
} else {
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_FC2_ENTRY_1);
|
|
}
|
|
break;
|
|
|
|
case PD_PM_STATE_FC2_ENTRY_1:
|
|
usbpd_pm_check_cp_charge_mode(pdpm);
|
|
if (pdpm->cp.charge_mode) {
|
|
usbpd_pm_set_cp_charge_mode(pdpm, SC8551_1_1_CHARGER_MODE);
|
|
pdpm->request_voltage = pdpm->cp.vbat_volt + BUS_VOLT_INIT_UP/2;
|
|
} else {
|
|
usbpd_pm_set_cp_charge_mode(pdpm, SC8551_2_1_CHARGER_MODE);
|
|
pdpm->request_voltage = pdpm->cp.vbat_volt * 2 + BUS_VOLT_INIT_UP;
|
|
}
|
|
|
|
pdpm->request_current = min(pdpm->apdo_max_curr, pm_config.bus_curr_lp_lmt);
|
|
|
|
usbpd_select_pdo(pdpm,pdpm->request_voltage,
|
|
pdpm->request_current);
|
|
pr_err("request_voltage:%d, request_current:%d\n",
|
|
pdpm->request_voltage, pdpm->request_current);
|
|
|
|
pr_err(">>>>SC8551 direct charge %d charge mode %d\n",
|
|
pdpm->cp.direct_charge, pdpm->cp.charge_mode);
|
|
if(pdpm->cp.direct_charge == pdpm->cp.charge_mode)
|
|
{
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_FC2_ENTRY_2);
|
|
}
|
|
|
|
|
|
tune_vbus_retry = 0;
|
|
break;
|
|
|
|
case PD_PM_STATE_FC2_ENTRY_2:
|
|
pr_err("tune_vbus_retry %d\n", tune_vbus_retry);
|
|
if (pdpm->cp.vbus_error_low) {// || pdpm->cp.vbus_volt < pdpm->cp.vbat_volt * 213/100) {
|
|
tune_vbus_retry++;
|
|
pdpm->request_voltage += 20;
|
|
usbpd_select_pdo(pdpm,pdpm->request_voltage,
|
|
pdpm->request_current);
|
|
pr_err("request_voltage:%d, request_current:%d\n",
|
|
pdpm->request_voltage, pdpm->request_current);
|
|
} else if (pdpm->cp.vbus_error_high) {// || pdpm->cp.vbus_volt > pdpm->cp.vbat_volt * 217/100) {
|
|
tune_vbus_retry++;
|
|
pdpm->request_voltage -= 20;
|
|
usbpd_select_pdo(pdpm,pdpm->request_voltage,
|
|
pdpm->request_current);
|
|
pr_err("request_voltage:%d, request_current:%d\n",
|
|
pdpm->request_voltage, pdpm->request_current);
|
|
} else {
|
|
pr_notice("adapter volt tune ok, retry %d times\n", tune_vbus_retry);
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_FC2_ENTRY_3);
|
|
break;
|
|
}
|
|
|
|
if (tune_vbus_retry > 25) {
|
|
pr_notice("Failed to tune adapter volt into valid range, \
|
|
charge with switching charger\n");
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_FC2_EXIT);
|
|
}
|
|
break;
|
|
case PD_PM_STATE_FC2_ENTRY_3:
|
|
usbpd_pm_check_cp_enabled(pdpm);
|
|
if (!pdpm->cp.charge_enabled) {
|
|
usbpd_pm_enable_cp(pdpm, true);
|
|
usbpd_pm_check_cp_enabled(pdpm);
|
|
}
|
|
|
|
if (pm_config.cp_sec_enable) {
|
|
usbpd_pm_check_cp_sec_enabled(pdpm);
|
|
if (!pdpm->cp_sec.charge_enabled) {
|
|
usbpd_pm_enable_cp_sec(pdpm, true);
|
|
usbpd_pm_check_cp_sec_enabled(pdpm);
|
|
}
|
|
}
|
|
if (pdpm->cp.charge_enabled) {
|
|
if ((pm_config.cp_sec_enable && pdpm->cp_sec.charge_enabled)
|
|
|| !pm_config.cp_sec_enable) {
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_FC2_TUNE);
|
|
ibus_lmt_change_timer = 0;
|
|
fc2_taper_timer = 0;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case PD_PM_STATE_FC2_TUNE:
|
|
ret = usbpd_pm_fc2_charge_algo(pdpm);
|
|
if (ret == PM_ALGO_RET_THERM_FAULT) {
|
|
pr_notice("Move to stop charging:%d\n", ret);
|
|
stop_sw = true;
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_FC2_EXIT);
|
|
break;
|
|
} else if (ret == PM_ALGO_RET_OTHER_FAULT || ret == PM_ALGO_RET_TAPER_DONE) {
|
|
pr_notice("Move to switch charging:%d\n", ret);
|
|
stop_sw = false;
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_FC2_EXIT);
|
|
break;
|
|
} else if (ret == PM_ALGO_RET_CHG_DISABLED) {
|
|
pr_notice("Move to switch charging, will try to recover \
|
|
flash charging:%d\n", ret);
|
|
recover = true;
|
|
stop_sw = false;
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_FC2_EXIT);
|
|
break;
|
|
} else {
|
|
usbpd_select_pdo(pdpm,pdpm->request_voltage,
|
|
pdpm->request_current);
|
|
pr_err("request_voltage:%d, request_current:%d\n",
|
|
pdpm->request_voltage, pdpm->request_current);
|
|
}
|
|
|
|
if (!pdpm->pps_temp_flag
|
|
|| get_charging_call_state()) {
|
|
pr_notice("temp high or lower,stop charging\n");
|
|
stop_sw = false;
|
|
pdpm->sw.charge_enabled = false;
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_FC2_EXIT);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case PD_PM_STATE_FC2_EXIT:
|
|
pdpm->is_cp_ok = false;
|
|
/* select default 5V*/
|
|
usbpd_select_pdo(pdpm,5000,3000);
|
|
|
|
|
|
if (pdpm->cp.charge_enabled) {
|
|
usbpd_pm_enable_cp(pdpm, false);
|
|
usbpd_pm_check_cp_enabled(pdpm);
|
|
}
|
|
if (pm_config.cp_sec_enable && pdpm->cp_sec.charge_enabled) {
|
|
usbpd_pm_enable_cp_sec(pdpm, false);
|
|
usbpd_pm_check_cp_sec_enabled(pdpm);
|
|
}
|
|
|
|
pr_err(">>>sw state %d %d\n", stop_sw, pdpm->sw.charge_enabled);
|
|
if (stop_sw && pdpm->sw.charge_enabled) {
|
|
usbpd_pm_enable_sw(pdpm, false);
|
|
usbpd_pm_set_swchg_cap(pdpm, 3000);
|
|
}
|
|
else if (!stop_sw && !pdpm->sw.charge_enabled) {
|
|
usbpd_pm_enable_sw(pdpm, true);
|
|
usbpd_pm_set_swchg_cap(pdpm, 3000);
|
|
}
|
|
|
|
if (recover)
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_ENTRY);
|
|
else
|
|
{
|
|
usbpd_pps_enable_charging(pdpm,false,5000,3000);
|
|
rc = 1;
|
|
}
|
|
if (!pdpm->pps_temp_flag
|
|
|| get_charging_call_state()) {
|
|
pr_notice("temp is high or low waiting...\n");
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_ENTRY);
|
|
pdpm->pd_active = false;
|
|
schedule_work(&pdpm->usb_psy_change_work);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void usbpd_pm_workfunc(struct work_struct *work)
|
|
{
|
|
struct usbpd_pm *pdpm = container_of(work, struct usbpd_pm,
|
|
pm_work.work);
|
|
|
|
usbpd_pm_update_sw_status(pdpm);
|
|
usbpd_pm_update_cp_status(pdpm);
|
|
usbpd_pm_update_cp_sec_status(pdpm);
|
|
|
|
if (!usbpd_pm_sm(pdpm) && pdpm->pd_active)
|
|
schedule_delayed_work(&pdpm->pm_work,
|
|
msecs_to_jiffies(PM_WORK_RUN_INTERVAL));
|
|
}
|
|
|
|
static void usbpd_pm_disconnect(struct usbpd_pm *pdpm)
|
|
{
|
|
union power_supply_propval pval = {0, };
|
|
int ret = 0;
|
|
usbpd_pm_enable_cp(pdpm, false);
|
|
usbpd_pm_check_cp_enabled(pdpm);
|
|
if (pm_config.cp_sec_enable) {
|
|
usbpd_pm_enable_cp_sec(pdpm, false);
|
|
usbpd_pm_check_cp_sec_enabled(pdpm);
|
|
}
|
|
cancel_delayed_work(&pdpm->pm_work);
|
|
|
|
if (!pdpm->sw.charge_enabled) {
|
|
usbpd_pm_enable_sw(pdpm, true);
|
|
}
|
|
|
|
pdpm->pps_supported = false;
|
|
pdpm->lcdon_curr_step = 0;
|
|
pdpm->lcdoff_curr_step = 0;
|
|
pdpm->is_cp_ok = false;
|
|
pdpm->apdo_selected_pdo = 0;
|
|
pval.intval = 0;
|
|
ret = power_supply_set_property(pdpm->apdo_psy,
|
|
POWER_SUPPLY_PROP_APDO_MAX, &pval);
|
|
if (ret)
|
|
pr_err("%s:ret=%d\n",__func__,ret);
|
|
usbpd_pm_move_state(pdpm, PD_PM_STATE_ENTRY);
|
|
}
|
|
|
|
static int mi_pd_auth(struct usbpd_pm *pdpm)
|
|
{
|
|
union power_supply_propval val = {0,};
|
|
int ret = 0;
|
|
|
|
ret = power_supply_get_property(pdpm->apdo_psy,
|
|
POWER_SUPPLY_PROP_PD_AUTHENTICATION, &val);
|
|
if (ret)
|
|
pr_err("Failed to read typec power role\n");
|
|
else
|
|
pdpm->pd_authen = val.intval;
|
|
return pdpm->pd_authen;
|
|
}
|
|
|
|
static void usbpd_pd_contact(struct usbpd_pm *pdpm, bool connected)
|
|
{
|
|
pdpm->pd_active = connected;
|
|
pr_err("[SC manager] >> pd_active %d\n",
|
|
pdpm->pd_active);
|
|
|
|
if (connected) {
|
|
usbpd_pm_evaluate_src_caps(pdpm);
|
|
pr_err("[SC manager] >>start cp charging pps support %d\n", pdpm->pps_supported);
|
|
mi_pd_auth(pdpm);
|
|
if (pdpm->pps_supported)
|
|
schedule_delayed_work(&pdpm->pm_work, 0);
|
|
else
|
|
pdpm->pd_active = false;
|
|
} else {
|
|
usbpd_pm_disconnect(pdpm);
|
|
}
|
|
}
|
|
|
|
static void cp_psy_change_work(struct work_struct *work)
|
|
{
|
|
struct usbpd_pm *pdpm = container_of(work, struct usbpd_pm,
|
|
cp_psy_change_work);
|
|
|
|
pdpm->psy_change_running = false;
|
|
}
|
|
|
|
static void usb_psy_change_work(struct work_struct *work)
|
|
{
|
|
union power_supply_propval propval;
|
|
struct power_supply *batt_verify;
|
|
bool safe_batt_falg = false;
|
|
struct usbpd_pm *pdpm = container_of(work, struct usbpd_pm,
|
|
usb_psy_change_work);
|
|
|
|
batt_verify = power_supply_get_by_name("batt_verify");
|
|
if (batt_verify) {
|
|
power_supply_get_property(batt_verify,
|
|
POWER_SUPPLY_PROP_MI_BATTERY_ID, &propval);
|
|
if (propval.intval == 0x57 || propval.intval == 0x47)
|
|
safe_batt_falg = true;
|
|
pr_err("[SC manager] >> battery id %d safe flag = %d\n",
|
|
propval.intval, safe_batt_falg);
|
|
}
|
|
|
|
pr_err("[SC manager] >> usb change work, pdpm->pd_active=%d,"
|
|
"__pdpm->is_pps_en_unlock=%d\n", pdpm->pd_active,
|
|
__pdpm->is_pps_en_unlock);
|
|
|
|
if (!pdpm->pd_active && __pdpm->is_pps_en_unlock && safe_batt_falg)
|
|
usbpd_pd_contact(pdpm, true);
|
|
else if (pdpm->pd_active && !__pdpm->is_pps_en_unlock)
|
|
usbpd_pd_contact(pdpm, false);
|
|
|
|
pdpm->psy_change_running = false;
|
|
}
|
|
|
|
static int usbpd_check_plugout(struct usbpd_pm *pdpm)
|
|
{
|
|
int ret;
|
|
union power_supply_propval val = {0,};
|
|
|
|
ret = power_supply_get_property(pdpm->usb_psy,
|
|
POWER_SUPPLY_PROP_ONLINE, &val);
|
|
if (!ret) {
|
|
if (!val.intval) {
|
|
pdpm->is_cp_ok = false;
|
|
usbpd_pm_enable_cp(pdpm, false);
|
|
usbpd_pm_check_cp_enabled(pdpm);
|
|
if (pm_config.cp_sec_enable) {
|
|
usbpd_pm_enable_cp_sec(pdpm, false);
|
|
usbpd_pm_check_cp_sec_enabled(pdpm);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int usbpd_psy_notifier_cb(struct notifier_block *nb,
|
|
unsigned long event, void *data)
|
|
{
|
|
struct usbpd_pm *pdpm = container_of(nb, struct usbpd_pm, nb);
|
|
struct power_supply *psy = data;
|
|
unsigned long flags;
|
|
|
|
|
|
if (event != PSY_EVENT_PROP_CHANGED)
|
|
return NOTIFY_OK;
|
|
|
|
usbpd_check_cp_psy(pdpm);
|
|
if (pm_config.cp_sec_enable) {
|
|
usbpd_check_cp_sec_psy(pdpm);
|
|
}
|
|
usbpd_check_charger_psy(pdpm);
|
|
usbpd_check_battery_psy(pdpm);
|
|
usbpd_check_tcpc(pdpm);
|
|
usbpd_check_pca_chg_swchg(pdpm);
|
|
|
|
if (!pdpm->cp_psy || !pdpm->usb_psy
|
|
|| !pdpm->tcpc || !pdpm->sw_chg)
|
|
return NOTIFY_OK;
|
|
|
|
usbpd_check_plugout(pdpm);
|
|
|
|
if (psy == pdpm->cp_psy || psy == pdpm->usb_psy) {
|
|
spin_lock_irqsave(&pdpm->psy_change_lock, flags);
|
|
pr_err("[SC manager] >>>pdpm->psy_change_running : %d\n", pdpm->psy_change_running);
|
|
if (!pdpm->psy_change_running) {
|
|
pdpm->psy_change_running = true;
|
|
if (psy == pdpm->cp_psy)
|
|
schedule_work(&pdpm->cp_psy_change_work);
|
|
else
|
|
schedule_work(&pdpm->usb_psy_change_work);
|
|
}
|
|
spin_unlock_irqrestore(&pdpm->psy_change_lock, flags);
|
|
}
|
|
|
|
return NOTIFY_OK;
|
|
}
|
|
static int __init usbpd_pm_init(void)
|
|
{
|
|
struct usbpd_pm *pdpm;
|
|
int ret = 0;
|
|
|
|
pdpm = kzalloc(sizeof(*pdpm), GFP_KERNEL);
|
|
if (!pdpm)
|
|
return -ENOMEM;
|
|
|
|
__pdpm = pdpm;
|
|
|
|
INIT_WORK(&pdpm->cp_psy_change_work, cp_psy_change_work);
|
|
INIT_WORK(&pdpm->usb_psy_change_work, usb_psy_change_work);
|
|
|
|
spin_lock_init(&pdpm->psy_change_lock);
|
|
|
|
usbpd_check_cp_psy(pdpm);
|
|
if (pm_config.cp_sec_enable) {
|
|
usbpd_check_cp_sec_psy(pdpm);
|
|
}
|
|
usbpd_check_apdo_psy(pdpm);
|
|
usbpd_check_charger_psy(pdpm);
|
|
usbpd_check_battery_psy(pdpm);
|
|
usbpd_check_tcpc(pdpm);
|
|
usbpd_check_pca_chg_swchg(pdpm);
|
|
|
|
INIT_DELAYED_WORK(&pdpm->pm_work, usbpd_pm_workfunc);
|
|
|
|
/* register tcp notifier callback */
|
|
pdpm->tcp_nb.notifier_call = pca_pps_tcp_notifier_call;
|
|
ret = register_tcp_dev_notifier(pdpm->tcpc, &pdpm->tcp_nb,
|
|
TCP_NOTIFY_TYPE_USB);
|
|
if (ret < 0) {
|
|
pr_err("register tcpc notifier fail\n");
|
|
return ret;
|
|
}
|
|
|
|
if (is_kernel_power_off_charging())
|
|
pdpm->therm_flag = true;
|
|
else
|
|
pdpm->therm_flag = false;
|
|
|
|
pdpm->nb.notifier_call = usbpd_psy_notifier_cb;
|
|
power_supply_reg_notifier(&pdpm->nb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit usbpd_pm_exit(void)
|
|
{
|
|
power_supply_unreg_notifier(&__pdpm->nb);
|
|
cancel_delayed_work(&__pdpm->pm_work);
|
|
cancel_work_sync(&__pdpm->cp_psy_change_work);
|
|
cancel_work_sync(&__pdpm->usb_psy_change_work);
|
|
|
|
}
|
|
|
|
module_init(usbpd_pm_init);
|
|
module_exit(usbpd_pm_exit);
|
|
|
|
|