From 813d9bf09b5ed82f3b1e7b9254f64ff44ecd1d4e Mon Sep 17 00:00:00 2001 From: Bernie Moreno-BERNIEM Date: Thu, 8 Apr 2021 20:42:32 -0500 Subject: [PATCH] power:fet_control: Batt flip dischg fet ctl, chg cur_en Add flip batt path fet switching control logic for discharge. Low-Z battplus_en path is enabled when flip batt & main batt is closely matched in charge (voltage). Otherwise enable R-balance flip path. Also add logic for vbus charge current OCP in case of short condition esp for IEC62368 cert test. Change-Id: I94022c5491ed3c7776c868bdf77d34cfe3e98478 Signed-off-by: Bernie Moreno-BERNIEM Reviewed-on: https://gerrit.mot.com/1920346 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Ling Jin Submit-Approved: Jira Key --- drivers/power/fet-control/fet_control.c | 288 +++++++++++++++++++----- 1 file changed, 237 insertions(+), 51 deletions(-) diff --git a/drivers/power/fet-control/fet_control.c b/drivers/power/fet-control/fet_control.c index c3aec44cb78e..597fe50a2e7f 100755 --- a/drivers/power/fet-control/fet_control.c +++ b/drivers/power/fet-control/fet_control.c @@ -36,20 +36,32 @@ #include #include +#define RBALANCE_VDIFF_MV 100 +#define GPIO_SET_DELAY 50 +#define CHRG_FULLCURR_EN 1 +#define CHRG_FULLCURR_DIS 0 +#define HBDLY_DISCHARGE_MS 30000 +#define HBDLY_CHARGE_MS 6000 + static struct fet_control_data { struct device *dev; struct power_supply *flip_batt_psy; struct power_supply *main_batt_psy; struct power_supply *usb_psy; - struct notifier_block ps_notif; - struct work_struct update; - bool init_done; + struct notifier_block ps_notif; + struct delayed_work update; + bool init_done; + bool ps_is_present; + int flip_chg_curr_max; + int main_chg_curr_max; int battplus_en_gpio; int balance_en_n_gpio; + int chrg_fullcurr_en_gpio; } fet_control_data; int battplus_state = -1; int balance_state = -1; +int fullcurr_state = -1; static int get_ps_int_prop(struct power_supply *psy, enum power_supply_property prop) { @@ -69,6 +81,26 @@ static int get_ps_int_prop(struct power_supply *psy, enum power_supply_property return val.intval; } +static int update_state_gpio(int gpio, int val) +{ + int readval; + + gpio_set_value(gpio, val); + udelay(GPIO_SET_DELAY); + + readval = gpio_get_value(gpio); + + if (readval != val) { + pr_err("FAIL set fet-ctrl gpio[%d], wrote:%d, read:%d\n", + gpio, + val, + readval); + return -EINVAL; + } + + return 0; +} + static int set_battplus_state(const char *val, const struct kernel_param *kp) { int rc; @@ -81,18 +113,13 @@ static int set_battplus_state(const char *val, const struct kernel_param *kp) if (rc) return rc; - gpio_set_value(fet_control_data.battplus_en_gpio, !!mode); - udelay(500); - - if (gpio_get_value(fet_control_data.battplus_en_gpio) != !!mode) { - pr_err("FAIL Set flip_battplus gpio[%d], wrote:%d, read:%d\n", - fet_control_data.battplus_en_gpio, - !!mode, - gpio_get_value(fet_control_data.battplus_en_gpio)); - return -EINVAL; + rc = update_state_gpio(fet_control_data.battplus_en_gpio, !!mode); + if (rc) { + pr_err("Update fet-ctl gpio [%d] failed\n", fet_control_data.battplus_en_gpio); + return rc; } else { battplus_state = !!mode; - pr_info("Set using flip_battplus gpio[%d], VAL:[%d]\n", + pr_debug("Set using flip_battplus gpio[%d], VAL:[%d]\n", fet_control_data.battplus_en_gpio, gpio_get_value(fet_control_data.battplus_en_gpio)); } @@ -112,18 +139,13 @@ static int set_balance_state(const char *val, const struct kernel_param *kp) if (rc) return rc; - gpio_set_value(fet_control_data.balance_en_n_gpio, !!mode); - udelay(500); - - if (gpio_get_value(fet_control_data.balance_en_n_gpio) != !!mode) { - pr_err("FAIL set flip_balance gpio[%d], wrote:%d, read:%d\n", - fet_control_data.balance_en_n_gpio, - !!mode, - gpio_get_value(fet_control_data.balance_en_n_gpio)); - return -EINVAL; + rc = update_state_gpio(fet_control_data.balance_en_n_gpio, !!mode); + if (rc) { + pr_err("Update fet-ctl gpio [%d] failed\n", fet_control_data.balance_en_n_gpio); + return rc; } else { balance_state = !!mode; - pr_info("Set using flip_bal_en_n gpio[%d], VAL:[%d]\n", + pr_debug("Set using flip_balance_enn gpio[%d], VAL:[%d]\n", fet_control_data.balance_en_n_gpio, gpio_get_value(fet_control_data.balance_en_n_gpio)); } @@ -131,28 +153,28 @@ static int set_balance_state(const char *val, const struct kernel_param *kp) return 0; } -static void update_work(struct work_struct *w) +static int set_fullcurr_state(const char *val, const struct kernel_param *kp) { - struct fet_control_data *data = container_of(w, struct fet_control_data, update); + int rc; + long mode; - //unused for now - (void)data; - (void)get_ps_int_prop; -} + if (!fet_control_data.init_done) + return -ENODEV; -static int ps_notify_callback(struct notifier_block *nb, - unsigned long event, void *p) -{ - struct fet_control_data *data = container_of(nb, struct fet_control_data, ps_notif); - struct power_supply *psy = p; + rc = kstrtol(val, 0, &mode); + if (rc) + return rc; - //unused for now - (void)data; - if (event == PSY_EVENT_PROP_CHANGED && - psy && psy->desc->get_property && psy->desc->name && - !strncmp(psy->desc->name, "battery", sizeof("battery"))) { - //schedule_work(&data->update); + rc = update_state_gpio(fet_control_data.chrg_fullcurr_en_gpio, !!mode); + if (rc) { + pr_err("Update fet-ctl gpio [%d] failed\n", fet_control_data.chrg_fullcurr_en_gpio); + return rc; + } else { + fullcurr_state = !!mode; + pr_debug("Set using Chrg fullcurr_en gpio[%d], VAL:[%d]\n", + fet_control_data.chrg_fullcurr_en_gpio, + gpio_get_value(fet_control_data.chrg_fullcurr_en_gpio)); } return 0; @@ -170,6 +192,12 @@ static struct kernel_param_ops balance_ops = .get = param_get_int, }; +static struct kernel_param_ops fullcurr_ops = +{ + .set = &set_fullcurr_state, + .get = param_get_int, +}; + module_param_cb(battplus_state, &battplus_ops, &battplus_state, @@ -182,6 +210,139 @@ module_param_cb(balance_state, S_IRUGO | S_IWUSR ); +module_param_cb(fullcurr_state, + &fullcurr_ops, + &fullcurr_state, + S_IRUGO | S_IWUSR +); + +static void update_work(struct work_struct *work) +{ + struct fet_control_data *data = container_of(work, struct fet_control_data, update.work); + int flip_curr, main_curr, flip_mv, main_mv; + int flip_dischg = 1; + int main_dischg = 1; + int hb_sched_time; + + flip_mv = get_ps_int_prop(data->flip_batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW); + flip_mv /= 1000; + + main_mv = get_ps_int_prop(data->main_batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW); + main_mv /= 1000; + + flip_curr = get_ps_int_prop(data->flip_batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW); + flip_curr /= 1000; + if (flip_curr < 0) { + pr_debug("Charging Flip\n"); + flip_dischg = -1; + } + + /* Main current sysfs already in mA */ + main_curr = get_ps_int_prop(data->main_batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW); + if (main_curr < 0) { + pr_debug("Charging Main\n"); + main_dischg = -1; + } + + /* Max Allowed Charge Curr= 1C */ + data->flip_chg_curr_max = get_ps_int_prop(data->flip_batt_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN); + data->flip_chg_curr_max /= 1000; + if (data->flip_chg_curr_max < 0) { + pr_err("Failed to get Flip Max chrg curr\n"); + return; + } + + data->main_chg_curr_max = get_ps_int_prop(data->main_batt_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN); + data->main_chg_curr_max /= 1000; + if (data->main_chg_curr_max < 0) { + pr_err("Failed to get Main Max chrg curr\n"); + return; + } + + if (flip_dischg * flip_curr > data->flip_chg_curr_max || + main_dischg * main_curr > data->main_chg_curr_max) { + fullcurr_state = CHRG_FULLCURR_DIS; + } else { + fullcurr_state = CHRG_FULLCURR_EN; + } + + pr_info("Flip_curr:%d, flip_Ichg_MAX:%d, fullcurr state:%d\n", + flip_curr, data->flip_chg_curr_max, fullcurr_state); + pr_info("Main_curr:%d, main_Ichg_MAX:%d\n", + main_curr, data->main_chg_curr_max); + pr_info("Flip-Vbatt:%d, Main-Vbatt: %d\n", flip_mv, main_mv); + + /* FET paths set in Batt discharge state */ + if (flip_dischg ==1 && main_dischg ==1) { + hb_sched_time = HBDLY_DISCHARGE_MS; + /* Leave Balance dflt-en & toggle in/out parallel low-Z battplus fet */ + balance_state = 0; + if ((main_mv - flip_mv) < RBALANCE_VDIFF_MV) { + battplus_state = 1; + pr_debug("DISCHG-battplus-EN: %d\n", battplus_state); + } else { + battplus_state = 0; + pr_debug("DISCHG-battplus-DIS: %d\n", battplus_state); + } + } else { + hb_sched_time = HBDLY_CHARGE_MS; + battplus_state = 0; + balance_state = 1; + } + + /* Update GPIO controls */ + update_state_gpio(fet_control_data.battplus_en_gpio, battplus_state); + update_state_gpio(fet_control_data.balance_en_n_gpio, balance_state); + update_state_gpio(fet_control_data.chrg_fullcurr_en_gpio, fullcurr_state); + + schedule_delayed_work(&data->update, msecs_to_jiffies(hb_sched_time)); +} + +static int ps_notify_callback(struct notifier_block *nb, + unsigned long event, void *p) +{ + struct fet_control_data *data = container_of(nb, struct fet_control_data, ps_notif); + struct power_supply *psy = p; + union power_supply_propval pval = {0}; + int retval; + bool present_ps; + + if ((event == PSY_EVENT_PROP_CHANGED) && + psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb")) && data) { + pr_info("psy notif: event = %lu\n", event); + + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, + &pval); + if (retval) { + pr_err("%s psy get property failed\n", psy->desc->name); + return retval; + } + present_ps = (pval.intval) ? true : false; + pr_info("%s is %s\n", psy->desc->name, + (present_ps) ? "present" : "not present"); + + if (event == PSY_EVENT_PROP_CHANGED) { + if (data->ps_is_present == present_ps) { + pr_info("ps present state unchanged\n"); + return 0; + } + } + data->ps_is_present = present_ps; + + cancel_delayed_work(&data->update); + schedule_delayed_work(&data->update, msecs_to_jiffies(1)); + } + + return 0; +} + static int parse_dt(struct device_node *node) { int rc = 0; @@ -212,6 +373,13 @@ static int parse_dt(struct device_node *node) return -ENODEV; } + fet_control_data.chrg_fullcurr_en_gpio = of_get_named_gpio(node, + "mmi,chrg-fullcurr-en-gpio", 0); + if (!gpio_is_valid(fet_control_data.chrg_fullcurr_en_gpio)) { + pr_err("chrg-fullcurr-en-gpio is not valid!\n"); + return -ENODEV; + } + return rc; } @@ -247,8 +415,11 @@ static int fet_control_probe(struct platform_device *pdev) goto fail; } - pr_info("Flip_battplus_en_GPIO: [%d]\n", fet_control_data.battplus_en_gpio); - pr_info("Flip_balance_en_n_GPIO: [%d]\n", fet_control_data.balance_en_n_gpio); + rc = gpio_request(fet_control_data.chrg_fullcurr_en_gpio, "mmi,chrg-fullcurr-en-gpio"); + if (rc) { + pr_err("Failed request chrg-fullcurr-en-gpio\n"); + goto fail; + } /* Set default Flip Battery Path FETs: battplus path disable*/ rc = gpio_direction_output(fet_control_data.battplus_en_gpio, 0); //Path Disabled @@ -266,24 +437,35 @@ static int fet_control_probe(struct platform_device *pdev) } balance_state = 0; - pr_info("Flip_battplus_en SET GPIO:[%d], VAL:[%d]\n", + /* Set charger fullcurr enable */ + rc = gpio_direction_output(fet_control_data.chrg_fullcurr_en_gpio, 0); // ChrgFullCurr dflt Disable + if (rc) { + pr_err("Unable to set DIR/VAL chrg_fullcurr_en_gpio [%d]\n", fet_control_data.chrg_fullcurr_en_gpio); + goto fail; + } + fullcurr_state = 0; + + pr_info("Flip_battplus_en Init GPIO:[%d], VAL:[%d]\n", fet_control_data.battplus_en_gpio, gpio_get_value(fet_control_data.battplus_en_gpio) ); - pr_info("Flip_balance_en_n SET GPIO:[%d], VAL:[%d]\n", + pr_info("Flip_balance_en_n Init GPIO:[%d], VAL:[%d]\n", fet_control_data.balance_en_n_gpio, gpio_get_value(fet_control_data.balance_en_n_gpio) ); + pr_info("chrg_fullcurr Init GPIO:[%d], VAL:[%d]\n", + fet_control_data.chrg_fullcurr_en_gpio, + gpio_get_value(fet_control_data.chrg_fullcurr_en_gpio) ); - // TBD notify or run every X seconds? - // Notify on plug/unplug? + + // Notify on plug/unplug fet_control_data.ps_notif.notifier_call = ps_notify_callback; if (power_supply_reg_notifier(&fet_control_data.ps_notif)) { pr_err("Failed to register notifier\n"); goto fail; } - // Work to update the fet state. Maybe delayed work that runs on heartbeat? - INIT_WORK(&fet_control_data.update, update_work); - schedule_work(&fet_control_data.update); + // Work to update the fet & charge current states. + INIT_DELAYED_WORK(&fet_control_data.update, update_work); + schedule_delayed_work(&fet_control_data.update, 1); fet_control_data.init_done = true; return 0; @@ -293,14 +475,18 @@ fail: static int fet_control_remove(struct platform_device *pdev) { + if (fet_control_data.init_done) { - cancel_work_sync(&fet_control_data.update); + cancel_delayed_work(&fet_control_data.update); power_supply_unreg_notifier(&fet_control_data.ps_notif); + gpio_free(fet_control_data.battplus_en_gpio); + gpio_free(fet_control_data.balance_en_n_gpio); + gpio_free(fet_control_data.chrg_fullcurr_en_gpio); } + return 0; } - static const struct of_device_id match_table[] = { { .compatible = "mmi,fet-control", }, { },