charging: Add PD charging in DLKM

Add PD charging(include PPS/Fixed) policy in DLKM for single
charge path(only pmic charging)

refer to https://gerrit.mot.com/#/c/1847093/

Change-Id: I490a0628372626e43ec3c30af259ab5a97269927
Signed-off-by: mahj8 <mahj8@lenovo.com>
Reviewed-on: https://gerrit.mot.com/2029015
SME-Granted: SME Approvals Granted
SLTApproved: Slta Waiver
Tested-by: Jira Key
Reviewed-by: Lu Chai <chailu1@motorola.com>
Reviewed-by: Huosheng Liao <liaohs@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
mahj8 2021-07-30 15:42:49 +08:00 • committed by Haijian Ma
commit 73d3ecefa0

View file

@ -35,6 +35,7 @@
#include <linux/mmi_wake_lock.h>
#include <linux/iio/consumer.h>
#include <linux/qti_power_supply.h>
#include <linux/usb/usbpd.h>
#define MODULE_LOG "SMBMMI"
@ -320,12 +321,20 @@ struct smb_mmi_chg_param {
u8 *val_raw);
};
struct smb_mmi_chg_freq {
unsigned int freq_5V;
unsigned int freq_6V_8V;
unsigned int freq_9V;
unsigned int freq_12V;
};
struct smb_mmi_params {
struct smb_mmi_chg_param fcc;
struct smb_mmi_chg_param fv;
struct smb_mmi_chg_param usb_icl;
struct smb_mmi_chg_param dc_icl;
struct smb_mmi_chg_param aicl_cont_threshold;
struct smb_mmi_chg_param freq_switcher;
};
struct mmi_ffc_zone {
@ -362,6 +371,7 @@ struct smb_mmi_charger {
struct device *dev;
struct regmap *regmap;
struct smb_mmi_params param;
struct smb_mmi_chg_freq chg_freq;
char *name;
int smb_version;
@ -424,6 +434,10 @@ struct smb_mmi_charger {
bool *debug_enabled;
void *ipc_log;
struct usbpd *pd_handle;
struct usbpd_pdo_info mmi_pdo_info[PD_MAX_PDO_NUM];
int pd_power_max;
int hvdcp_power_max;
int inc_hvdcp_cnt;
int hb_startup_cnt;
@ -435,6 +449,7 @@ struct smb_mmi_charger {
#define USBIN_CURRENT_LIMIT_CFG_REG (USBIN_BASE + 0x70)
#define DCIN_CURRENT_LIMIT_CFG_REG (DCIN_BASE + 0x70)
#define USBIN_CONT_AICL_THRESHOLD_REG (USBIN_BASE + 0x84)
#define DCDC_FSW_SEL_REG (DCDC_BASE + 0x50)
#define AICL_RANGE2_MIN_MV 5600
#define AICL_RANGE2_STEP_DELTA_MV 200
@ -478,6 +493,56 @@ int smblib_set_aicl_cont_threshold(struct smb_mmi_chg_param *param,
return 0;
}
/********************
* REGISTER SETTERS *
********************/
struct smb_buck_boost_freq {
int freq_khz;
u8 val;
};
static const struct smb_buck_boost_freq chg_freq_list[] = {
[0] = {
.freq_khz = 2400,
.val = 7,
},
[1] = {
.freq_khz = 2100,
.val = 8,
},
[2] = {
.freq_khz = 1600,
.val = 11,
},
[3] = {
.freq_khz = 1200,
.val = 15,
},
};
int smblib_set_chg_freq(struct smb_mmi_chg_param *param,
int val_u, u8 *val_raw)
{
u8 i;
if (val_u > param->max_u || val_u < param->min_u)
return -EINVAL;
/* Charger FSW is the configured freqency / 2 */
val_u *= 2;
for (i = 0; i < ARRAY_SIZE(chg_freq_list); i++) {
if (chg_freq_list[i].freq_khz == val_u)
break;
}
if (i == ARRAY_SIZE(chg_freq_list)) {
pr_err("Invalid frequency %d Hz\n", val_u / 2);
return -EINVAL;
}
*val_raw = chg_freq_list[i].val;
return 0;
}
static struct smb_mmi_params smb5_pm8150b_params = {
.fcc = {
.name = "fast charge current",
@ -516,6 +581,14 @@ static struct smb_mmi_params smb5_pm8150b_params = {
.get_proc = smblib_get_aicl_cont_threshold,
.set_proc = smblib_set_aicl_cont_threshold,
},
.freq_switcher = {
.name = "switching frequency",
.reg = DCDC_FSW_SEL_REG,
.min_u = 600,
.max_u = 1200,
.step_u = 400,
.set_proc = smblib_set_chg_freq,
},
};
static struct smb_mmi_params smb5_pmi632_params = {
@ -548,6 +621,14 @@ static struct smb_mmi_params smb5_pmi632_params = {
.max_u = 3000000,
.step_u = 50000,
},
.freq_switcher = {
.name = "switching frequency",
.reg = DCDC_FSW_SEL_REG,
.min_u = 600,
.max_u = 1200,
.step_u = 400,
.set_proc = smblib_set_chg_freq,
},
};
enum smb_mmi_ext_iio_channels {
@ -562,6 +643,7 @@ enum smb_mmi_ext_iio_channels {
SMB5_USB_INPUT_CURRENT_SETTLED,
SMB5_RERUN_AICL,
SMB5_SW_JEITA_ENABLED,
SMB5_PD_CURRENT_MAX,
//bms
SMB5_QG_VOLTAGE_NOW,
SMB5_QG_CURRENT_NOW,
@ -584,6 +666,7 @@ static const char * const smb_mmi_ext_iio_chan_name[] = {
[SMB5_USB_INPUT_CURRENT_SETTLED] = "usb_input_current_settled",
[SMB5_RERUN_AICL] = "battery_rerun_aicl",
[SMB5_SW_JEITA_ENABLED] = "battery_sw_jeita_enabled",
[SMB5_PD_CURRENT_MAX] = "usb_pd_current_max",
//bms
[SMB5_QG_VOLTAGE_NOW] = "voltage_now",
[SMB5_QG_CURRENT_NOW] = "current_now",
@ -1550,6 +1633,20 @@ static void mmi_charger_power_support(struct smb_mmi_charger *chg)
const char *charger_ability = NULL;
int retval;
retval = of_property_read_u32(np, "qcom,pd-power-max",
&chg->pd_power_max);
if (retval) {
chg->pd_power_max = CHARGER_POWER_18W;
}
chg->pd_handle =
devm_usbpd_get_by_phandle(chg->dev, "qcom,usbpd-phandle");
if (IS_ERR_OR_NULL(chg->pd_handle)) {
dev_err(chg->dev, "Error getting the pd phandle %ld\n",
PTR_ERR(chg->pd_handle));
chg->pd_handle = NULL;
}
if (of_property_read_bool(np, "qcom,force-hvdcp-5v")) {
chg->hvdcp_power_max = CHARGER_POWER_15W;
return;
@ -2118,6 +2215,233 @@ static DEVICE_ATTR(force_hvdcp_power_max, 0644,
force_hvdcp_power_max_show,
force_hvdcp_power_max_store);
static ssize_t force_pd_power_max_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long r;
unsigned long power;
struct platform_device *pdev = to_platform_device(dev);
struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev);
r = kstrtoul(buf, 0, &power);
if (r) {
pr_err("SMBMMI: Invalid pd_power_max value = %lu\n", power);
return -EINVAL;
}
if (!mmi_chip) {
pr_err("SMBMMI: chip not valid\n");
return -ENODEV;
}
if ((power >= CHARGER_POWER_15W) &&
(power <= CHARGER_POWER_20W) &&
(mmi_chip->pd_power_max != power)) {
mmi_chip->pd_power_max = power;
cancel_delayed_work(&mmi_chip->heartbeat_work);
schedule_delayed_work(&mmi_chip->heartbeat_work,
msecs_to_jiffies(100));
mmi_info(mmi_chip, "Reset pd power max as %d, "
"Reschedule heartbeat\n",
mmi_chip->pd_power_max);
}
return r ? r : count;
}
static ssize_t force_pd_power_max_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int power;
struct platform_device *pdev = to_platform_device(dev);
struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev);
if (!mmi_chip) {
pr_err("SMBMMI: chip not valid\n");
return -ENODEV;
}
power = mmi_chip->pd_power_max;
return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", power);
}
static DEVICE_ATTR(force_pd_power_max, 0644,
force_pd_power_max_show,
force_pd_power_max_store);
static int smblib_set_opt_switcher_freq(struct smb_mmi_charger *chg, int fsw_khz)
{
int rc = 0;
rc = smblib_set_charge_param(chg, &chg->param.freq_switcher, fsw_khz);
if (rc < 0)
mmi_err(chg, "Error in setting freq_buck rc=%d\n", rc);
return rc;
}
#define MICRO_1V 1000000
#define MICRO_5V 5000000
#define MICRO_9V 9000000
#define MICRO_12V 12000000
static int smblib_set_usb_pd_fsw(struct smb_mmi_charger *chg, int voltage)
{
int rc = 0;
if (voltage == MICRO_5V)
rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_5V);
else if (voltage > MICRO_5V && voltage < MICRO_9V)
rc = smblib_set_opt_switcher_freq(chg,
chg->chg_freq.freq_6V_8V);
else if (voltage >= MICRO_9V && voltage < MICRO_12V)
rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_9V);
else if (voltage == MICRO_12V)
rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_12V);
else {
mmi_err(chg, "Couldn't set Fsw: invalid voltage %d\n",
voltage);
return -EINVAL;
}
return rc;
}
#define PD_SRC_PDO_TYPE_FIXED 0
#define PD_SRC_PDO_TYPE_BATTERY 1
#define PD_SRC_PDO_TYPE_VARIABLE 2
#define PD_SRC_PDO_TYPE_AUGMENTED 3
#define PPS_VOLT_MIN 5000000
#define PPS_CURR_MIN 2000000
#define PPS_CURR_MAX 3000000
static void mmi_chrg_usb_vin_pd_config(struct smb_mmi_charger *chg, int vbus_mv)
{
int rc = -EINVAL, i =0, req_pdo = -1, req_pd_volt = 0, req_pd_curr = 0;
int fixed_pd_volt = 0;
union power_supply_propval val = {0, };
bool pd_active = false, vbus_present = false, pps_active = false, fixed_active = false;
static bool fsw_setted = false, fixed_power_done = false;
if (!chg->usb_psy || !chg->pd_handle)
return;
rc = get_prop_usb_present(chg, &val);
if (rc) {
mmi_err(chg, "Unable to read USB PRESENT: %d\n", rc);
return;
}
vbus_present = val.intval;
rc = smb_mmi_read_iio_chan(chg, SMB5_PD_ACTIVE, &val.intval);
if (rc) {
mmi_err(chg, "Unable to read PD ACTIVE: %d\n", rc);
return;
}
pd_active = val.intval;
if (!pd_active || !vbus_present) {
fsw_setted = false;
fixed_power_done = false;
return;
}
memset(chg->mmi_pdo_info, 0,
sizeof(struct usbpd_pdo_info) * PD_MAX_PDO_NUM);
rc = usbpd_get_pdo_info(chg->pd_handle, chg->mmi_pdo_info, PD_MAX_PDO_NUM);
if (rc ) {
mmi_err(chg, "PD not ready, %d\n", rc);
return;
}
for (i = 0; i < PD_MAX_PDO_NUM; i++) {
if ((chg->mmi_pdo_info[i].type == PD_SRC_PDO_TYPE_AUGMENTED)
&& chg->mmi_pdo_info[i].uv_max >= PPS_VOLT_MIN
&& chg->mmi_pdo_info[i].ua >= PPS_CURR_MIN
&& (chg->mmi_pdo_info[i].uv_max / 1000) * (chg->mmi_pdo_info[i].ua / 1000) / 1000
>= chg->pd_power_max) {
req_pdo = chg->mmi_pdo_info[i].pdo_pos;
req_pd_curr = chg->mmi_pdo_info[i].ua;
if (req_pd_curr > PPS_CURR_MAX)
req_pd_curr = PPS_CURR_MAX;
req_pd_volt = (chg->pd_power_max * 1000 / (req_pd_curr / 1000)) * 1000 % 20000;
req_pd_volt = (chg->pd_power_max * 1000 / (req_pd_curr / 1000)) * 1000 - req_pd_volt;
req_pd_volt = max(req_pd_volt, PPS_VOLT_MIN);
req_pd_volt = min(req_pd_volt, chg->mmi_pdo_info[i].uv_max);
pps_active = true;
break;
}
}
if (!pps_active) {
if (chg->pd_power_max < CHARGER_POWER_18W)
fixed_pd_volt = MICRO_5V;
else
fixed_pd_volt = MICRO_9V;
for (i = 0; i < PD_MAX_PDO_NUM; i++) {
if ((chg->mmi_pdo_info[i].type ==
PD_SRC_PDO_TYPE_FIXED)
&& chg->mmi_pdo_info[i].uv_max <= fixed_pd_volt
&& chg->mmi_pdo_info[i].uv_max >= MICRO_5V) {
req_pdo = chg->mmi_pdo_info[i].pdo_pos;
req_pd_volt = chg->mmi_pdo_info[i].uv_max;
req_pd_curr = (chg->pd_power_max / (req_pd_volt / 1000)) * 1000;
fixed_active = true;
}
}
if (fixed_active == true && vbus_mv * 1000 < req_pd_volt - MICRO_1V)
fixed_power_done = false;
}
if (!pps_active && !fixed_active)
return;
else if (!fsw_setted){
smblib_set_usb_pd_fsw(chg, req_pd_volt);
fsw_setted = true;
}
if (!fixed_power_done && fixed_active) {
rc = usbpd_select_pdo(chg->pd_handle, req_pdo, req_pd_volt, req_pd_curr);
if (rc < 0) {
mmi_err(chg, "select pdo failed\n");
return;
}
val.intval = req_pd_curr * 1000; /* mA->uA */
rc = smb_mmi_write_iio_chan(chg, SMB5_PD_CURRENT_MAX, val.intval);
if (rc) {
mmi_err(chg, "set PD current MAX failed\n");
return;
}
fixed_power_done = true;
mmi_err(chg, "Request fixed power , re-vote USB_ICL %duA\n", val.intval);
} else if (pps_active){
rc = usbpd_select_pdo(chg->pd_handle, req_pdo, req_pd_volt, req_pd_curr);
if (rc < 0) {
mmi_err(chg, "select pdo failed\n");
return;
}
}
mmi_err(chg,
"Request PD power, fixed %d, pps %d, pdo %d, "
"req volt %dmV, req curr %dmA, vbus %dmV\n",
fixed_active,
pps_active,
req_pdo,
req_pd_volt,
req_pd_curr,
vbus_mv);
return;
}
static void mmi_chrg_usb_vin_config(struct smb_mmi_charger *chg, int cur_mv)
{
int rc = -EINVAL;
@ -2179,6 +2503,9 @@ static void mmi_chrg_input_config(struct smb_mmi_charger *chg,
{
if (chg->hvdcp_power_max)
mmi_chrg_usb_vin_config(chg, stat->usb_mv);
if (chg->pd_power_max)
mmi_chrg_usb_vin_pd_config(chg, stat->usb_mv);
}
static void mmi_weakcharger_work(struct work_struct *work)
@ -4088,6 +4415,11 @@ static int smb_mmi_chg_config_init(struct smb_mmi_charger *chip)
return -EINVAL;
}
chip->chg_freq.freq_5V = 600;
chip->chg_freq.freq_6V_8V = 800;
chip->chg_freq.freq_9V = 1050;
chip->chg_freq.freq_12V = 1200;
pr_err("SMBMMI: PMIC %d is %s\n", chip->smb_version, chip->name);
return 0;
@ -4417,6 +4749,11 @@ static int smb_mmi_probe(struct platform_device *pdev)
if (rc)
mmi_err(chip, "Couldn't create force_hvdcp_power_max\n");
rc = device_create_file(chip->dev,
&dev_attr_force_pd_power_max);
if (rc)
mmi_err(chip, "Couldn't create force_pd_power_max\n");
/* Register the notifier for the psy updates*/
chip->mmi_psy_notifier.notifier_call = mmi_psy_notifier_call;
rc = power_supply_reg_notifier(&chip->mmi_psy_notifier);