From 35d006a926cdd33bbdac370d43892e27340d549f Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Tue, 15 Sep 2020 13:19:35 -0700 Subject: [PATCH 1/2] power: supply: qti_battery_charger: Configure ICL and FCC post SSR/PDR When the remote subsystem restarts (SSR) or the charger firmware restarts (PDR), resend the ICL/FCC votes from clients like USB driver, thermal SW once the subsystem is up. Change-Id: Ia0da3747fdb98c7873b65c948b76f6b6a286e31c Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/supply/qti_battery_charger.c | 57 +++++++++++++++++----- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index 3538310ae107..7219e6cc8339 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -234,6 +234,8 @@ struct battery_chg_dev { struct notifier_block reboot_notifier; u32 thermal_fcc_ua; u32 restrict_fcc_ua; + u32 last_fcc_ua; + u32 usb_icl_ua; bool restrict_chg_en; }; @@ -421,14 +423,6 @@ static void battery_chg_notify_enable(struct battery_chg_dev *bcdev) pr_err("Failed to enable notification rc=%d\n", rc); } -static void battery_chg_subsys_up_work(struct work_struct *work) -{ - struct battery_chg_dev *bcdev = container_of(work, - struct battery_chg_dev, subsys_up_work); - - battery_chg_notify_enable(bcdev); -} - static void battery_chg_state_cb(void *priv, enum pmic_glink_state state) { struct battery_chg_dev *bcdev = priv; @@ -636,6 +630,11 @@ static void battery_chg_update_usb_type_work(struct work_struct *work) return; } + /* Reset usb_icl_ua whenever USB adapter type changes */ + if (pst->prop[USB_ADAP_TYPE] != POWER_SUPPLY_USB_TYPE_SDP && + pst->prop[USB_ADAP_TYPE] != POWER_SUPPLY_USB_TYPE_PD) + bcdev->usb_icl_ua = 0; + pr_debug("usb_adap_type: %u\n", pst->prop[USB_ADAP_TYPE]); switch (pst->prop[USB_ADAP_TYPE]) { @@ -831,8 +830,10 @@ static int usb_psy_set_icl(struct battery_chg_dev *bcdev, u32 prop_id, int val) temp = UINT_MAX; rc = write_property_id(bcdev, pst, prop_id, temp); - if (!rc) + if (!rc) { pr_debug("Set ICL to %u\n", temp); + bcdev->usb_icl_ua = temp; + } return rc; } @@ -946,10 +947,12 @@ static int __battery_psy_set_charge_current(struct battery_chg_dev *bcdev, rc = write_property_id(bcdev, &bcdev->psy_list[PSY_TYPE_BATTERY], BATT_CHG_CTRL_LIM, fcc_ua); - if (rc < 0) + if (rc < 0) { pr_err("Failed to set FCC %u, rc=%d\n", fcc_ua, rc); - else + } else { pr_debug("Set FCC to %u uA\n", fcc_ua); + bcdev->last_fcc_ua = fcc_ua; + } return rc; } @@ -1128,6 +1131,38 @@ static int battery_chg_init_psy(struct battery_chg_dev *bcdev) return 0; } +static void battery_chg_subsys_up_work(struct work_struct *work) +{ + struct battery_chg_dev *bcdev = container_of(work, + struct battery_chg_dev, subsys_up_work); + int rc; + + battery_chg_notify_enable(bcdev); + + /* + * Give some time after enabling notification so that USB adapter type + * information can be obtained properly which is essential for setting + * USB ICL. + */ + msleep(200); + + if (bcdev->last_fcc_ua) { + rc = __battery_psy_set_charge_current(bcdev, + bcdev->last_fcc_ua); + if (rc < 0) + pr_err("Failed to set FCC (%u uA), rc=%d\n", + bcdev->last_fcc_ua, rc); + } + + if (bcdev->usb_icl_ua) { + rc = usb_psy_set_icl(bcdev, USB_INPUT_CURR_LIMIT, + bcdev->usb_icl_ua); + if (rc < 0) + pr_err("Failed to set ICL(%u uA), rc=%d\n", + bcdev->usb_icl_ua, rc); + } +} + static int wireless_fw_send_firmware(struct battery_chg_dev *bcdev, const struct firmware *fw) { From 653826192f972de2c324958077b51697f47e8eea Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Mon, 14 Sep 2020 13:04:55 +0530 Subject: [PATCH 2/2] power: supply: qti_battery_charger: Handle probe failure gracefully Due to a race condition between the probe and glink message callback, it is observed that the glink messages/notifications can be handled even after the probe failure which leads to an invalid memory access. Fix it by introducing "initialized" flag. While at it, add few error logs to identify the exact message failures. Change-Id: I18e61dea734efde6a11ee072793da8eead269b12 Signed-off-by: Kiran Gunda --- drivers/power/supply/qti_battery_charger.c | 30 ++++++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/power/supply/qti_battery_charger.c b/drivers/power/supply/qti_battery_charger.c index 7219e6cc8339..817e2ce65b02 100644 --- a/drivers/power/supply/qti_battery_charger.c +++ b/drivers/power/supply/qti_battery_charger.c @@ -237,6 +237,8 @@ struct battery_chg_dev { u32 last_fcc_ua; u32 usb_icl_ua; bool restrict_chg_en; + /* To track the driver initialization status */ + bool initialized; }; static const int battery_prop_map[BATT_PROP_MAX] = { @@ -719,6 +721,12 @@ static int battery_chg_callback(void *priv, void *data, size_t len) pr_debug("owner: %u type: %u opcode: %#x len: %zu\n", hdr->owner, hdr->type, hdr->opcode, len); + if (!bcdev->initialized) { + pr_debug("Driver initialization failed: Dropping glink callback message: state %d\n", + bcdev->state); + return 0; + } + if (hdr->opcode == BC_NOTIFY_IND) handle_notification(bcdev, data, len); else @@ -810,8 +818,10 @@ static int usb_psy_set_icl(struct battery_chg_dev *bcdev, u32 prop_id, int val) int rc; rc = read_property_id(bcdev, pst, USB_ADAP_TYPE); - if (rc < 0) + if (rc < 0) { + pr_err("Failed to read prop USB_ADAP_TYPE, rc=%d\n", rc); return rc; + } /* Allow this only for SDP or USB_PD and not for other charger types */ if (pst->prop[USB_ADAP_TYPE] != POWER_SUPPLY_USB_TYPE_SDP && @@ -830,7 +840,9 @@ static int usb_psy_set_icl(struct battery_chg_dev *bcdev, u32 prop_id, int val) temp = UINT_MAX; rc = write_property_id(bcdev, pst, prop_id, temp); - if (!rc) { + if (rc < 0) { + pr_err("Failed to set ICL (%u uA) rc=%d\n", temp, rc); + } else { pr_debug("Set ICL to %u\n", temp); bcdev->usb_icl_ua = temp; } @@ -1730,8 +1742,11 @@ static int battery_chg_parse_dt(struct battery_chg_dev *bcdev) len = rc; rc = read_property_id(bcdev, pst, BATT_CHG_CTRL_LIM_MAX); - if (rc < 0) + if (rc < 0) { + pr_err("Failed to read prop BATT_CHG_CTRL_LIM_MAX, rc=%d\n", + rc); return rc; + } prev = pst->prop[BATT_CHG_CTRL_LIM_MAX]; @@ -1862,13 +1877,16 @@ static int battery_chg_probe(struct platform_device *pdev) return rc; } + bcdev->initialized = true; bcdev->reboot_notifier.notifier_call = battery_chg_ship_mode; bcdev->reboot_notifier.priority = 255; register_reboot_notifier(&bcdev->reboot_notifier); rc = battery_chg_parse_dt(bcdev); - if (rc < 0) + if (rc < 0) { + dev_err(dev, "Failed to parse dt rc=%d\n", rc); goto error; + } bcdev->restrict_fcc_ua = DEFAULT_RESTRICT_FCC_UA; platform_set_drvdata(pdev, bcdev); @@ -1881,7 +1899,7 @@ static int battery_chg_probe(struct platform_device *pdev) bcdev->battery_class.class_groups = battery_class_groups; rc = class_register(&bcdev->battery_class); if (rc < 0) { - pr_err("Failed to create battery_class rc=%d\n", rc); + dev_err(dev, "Failed to create battery_class rc=%d\n", rc); goto error; } @@ -1891,6 +1909,8 @@ static int battery_chg_probe(struct platform_device *pdev) return 0; error: + bcdev->initialized = false; + complete(&bcdev->ack); pmic_glink_unregister_client(bcdev->client); unregister_reboot_notifier(&bcdev->reboot_notifier); return rc;