qpnp-smbcharger-mmi: Add PD charging in DLKM

PD3.0 with PPS is a Mandatory requirement from VZW,
Add PD charging(include PPS/Fixed) policy in DLKM for single
charge path(only pmic charging)

Change-Id: Ic0f2fcf45f00dea214602846bdbe43b56805a114
Signed-off-by: xuwt2 <xuwt2@lenovo.com>
Reviewed-on: https://gerrit.mot.com/1847093
SLTApproved: Slta Waiver
SME-Granted: SME Approvals Granted
Tested-by: Jira Key
Reviewed-by: Haijian Ma <mahj8@motorola.com>
Reviewed-by: Yonghua Yan <yanyh2@motorola.com>
Reviewed-by: Huosheng Liao <liaohs@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
xuwt2 2021-01-08 17:32:27 +08:00 • committed by Wentao Xu
commit b3ca118609
3 changed files with 352 additions and 1 deletions

View file

@ -1,6 +1,10 @@
DLKM_DIR := motorola/kernel/modules
LOCAL_PATH := $(call my-dir)
ifeq ($(MOTO_PD_CHARGER),true)
KERNEL_CFLAGS += CONFIG_PD_CHARGER_MMI=y
endif
include $(CLEAR_VARS)
LOCAL_MODULE := qpnp-smbcharger-mmi.ko
LOCAL_MODULE_TAGS := optional

View file

@ -2,4 +2,8 @@
EXTRA_CFLAGS += -Wall
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include
ifneq ($(filter m y,$(CONFIG_PD_CHARGER_MMI)),)
EXTRA_CFLAGS += -DCONFIG_PD_CHARGER_MMI
endif
obj-m += qpnp-smbcharger-mmi.o

View file

@ -28,6 +28,7 @@
#include <linux/string.h>
#include <linux/version.h>
#include <linux/mmi_wake_lock.h>
#include <linux/usb/usbpd.h>
#define MODULE_LOG "SMBMMI"
@ -296,12 +297,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_sm_params {
@ -331,6 +340,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;
@ -395,6 +405,12 @@ struct smb_mmi_charger {
bool *debug_enabled;
void *ipc_log;
struct usbpd *pd_handle;
#ifdef CONFIG_PD_CHARGER_MMI
struct usbpd_pdo_info mmi_pdo_info[PD_MAX_PDO_NUM];
#endif
int pd_power_max;
int hvdcp_power_max;
int inc_hvdcp_cnt;
int hb_startup_cnt;
@ -406,7 +422,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
#define AICL_RANGE2_OFFSET 16
@ -449,6 +465,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",
@ -487,6 +553,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 = {
@ -519,6 +593,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,
},
};
static struct smb_mmi_params smb2_pm660_params = {
@ -1395,6 +1477,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 = 0;
}
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;
@ -2047,6 +2143,238 @@ 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);
#ifdef CONFIG_PD_CHARGER_MMI
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 = power_supply_get_property(chg->usb_psy,
POWER_SUPPLY_PROP_PRESENT, &val);
if (rc) {
mmi_err(chg, "Unable to read USB PRESENT: %d\n", rc);
return;
}
vbus_present = val.intval;
rc = power_supply_get_property(chg->usb_psy,
POWER_SUPPLY_PROP_PD_ACTIVE, &val);
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 = power_supply_set_property(chg->usb_psy,
POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val);
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;
}
#endif
static void mmi_chrg_usb_vin_config(struct smb_mmi_charger *chg, int cur_mv)
{
int rc = -EINVAL;
@ -2112,6 +2440,11 @@ 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);
#ifdef CONFIG_PD_CHARGER_MMI
if (chg->pd_power_max)
mmi_chrg_usb_vin_pd_config(chg, stat->usb_mv);
#endif
}
static void mmi_weakcharger_work(struct work_struct *work)
@ -4103,6 +4436,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;
@ -4425,6 +4763,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);