From b3ca1186097acf74126eefdc4664f21f9b0ff542 Mon Sep 17 00:00:00 2001 From: xuwt2 Date: Fri, 8 Jan 2021 17:32:27 +0800 Subject: [PATCH] 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 Reviewed-on: https://gerrit.mot.com/1847093 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Haijian Ma Reviewed-by: Yonghua Yan Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- drivers/power/qpnp-smbcharger-mmi/Android.mk | 4 + drivers/power/qpnp-smbcharger-mmi/Kbuild | 4 + .../qpnp-smbcharger-mmi/qpnp-smbcharger-mmi.c | 345 +++++++++++++++++- 3 files changed, 352 insertions(+), 1 deletion(-) diff --git a/drivers/power/qpnp-smbcharger-mmi/Android.mk b/drivers/power/qpnp-smbcharger-mmi/Android.mk index 875aa1a291a8..35ff12fe93bd 100755 --- a/drivers/power/qpnp-smbcharger-mmi/Android.mk +++ b/drivers/power/qpnp-smbcharger-mmi/Android.mk @@ -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 diff --git a/drivers/power/qpnp-smbcharger-mmi/Kbuild b/drivers/power/qpnp-smbcharger-mmi/Kbuild index f1eab7724f22..8906bafe9fea 100755 --- a/drivers/power/qpnp-smbcharger-mmi/Kbuild +++ b/drivers/power/qpnp-smbcharger-mmi/Kbuild @@ -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 diff --git a/drivers/power/qpnp-smbcharger-mmi/qpnp-smbcharger-mmi.c b/drivers/power/qpnp-smbcharger-mmi/qpnp-smbcharger-mmi.c index 0284065318b6..38315137c796 100755 --- a/drivers/power/qpnp-smbcharger-mmi/qpnp-smbcharger-mmi.c +++ b/drivers/power/qpnp-smbcharger-mmi/qpnp-smbcharger-mmi.c @@ -28,6 +28,7 @@ #include #include #include +#include #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);