Loading Documentation/devicetree/bindings/power/qpnp-charger.txt +2 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,8 @@ Parent node optional properties: DC and USB OVP FETs. Please note that this should only be enabled in board designs with PM8941 which have DC_IN and USB_IN connected via a short. - qcom,ext-ovp-isns-enable-gpio External OVP enable GPIO. - qcom,ext-ovp-isns-r-ohm External ISNS OVP resistance in ohm. Sub node required structure: - A qcom,chg node must be a child of an SPMI node that has specified Loading drivers/power/qpnp-charger.c +133 −5 Original line number Diff line number Diff line Loading @@ -34,6 +34,9 @@ #include <linux/alarmtimer.h> #include <linux/time.h> #include <linux/spinlock.h> #include <linux/gpio.h> #include <linux/of_gpio.h> #include <linux/qpnp/pin.h> /* Interrupt offsets */ #define INT_RT_STS(base) (base + 0x10) Loading Loading @@ -392,6 +395,9 @@ struct qpnp_chg_chip { struct work_struct reduce_power_stage_work; bool power_stage_workaround_running; bool power_stage_workaround_enable; bool ext_ovp_ic_gpio_enabled; unsigned int ext_ovp_isns_gpio; unsigned int usb_trim_default; }; static void Loading Loading @@ -434,6 +440,81 @@ enum usbin_health { USBIN_OVP, }; static int ext_ovp_isns_present; module_param(ext_ovp_isns_present, int, 0444); static int ext_ovp_isns_r; module_param(ext_ovp_isns_r, int, 0444); static bool ext_ovp_isns_online; static long ext_ovp_isns_ua; #define MAX_CURRENT_LENGTH_9A 10 #define ISNS_CURRENT_RATIO 2500 static int ext_ovp_isns_read(char *buffer, const struct kernel_param *kp) { int rc; struct qpnp_vadc_result results; struct power_supply *batt_psy = power_supply_get_by_name("battery"); struct qpnp_chg_chip *chip = container_of(batt_psy, struct qpnp_chg_chip, batt_psy); if (!ext_ovp_isns_present) return 0; rc = qpnp_vadc_read(chip->vadc_dev, P_MUX7_1_1, &results); if (rc) { pr_err("Unable to read vbat rc=%d\n", rc); return 0; } pr_debug("voltage %lld uV, current: %d\n mA", results.physical, ((int) results.physical / (ext_ovp_isns_r / ISNS_CURRENT_RATIO))); return snprintf(buffer, MAX_CURRENT_LENGTH_9A, "%d\n", ((int)results.physical / (ext_ovp_isns_r / ISNS_CURRENT_RATIO))); } static int ext_ovp_isns_enable(const char *val, const struct kernel_param *kp) { int rc; struct power_supply *batt_psy = power_supply_get_by_name("battery"); struct qpnp_chg_chip *chip = container_of(batt_psy, struct qpnp_chg_chip, batt_psy); rc = param_set_bool(val, kp); if (rc) { pr_err("Unable to set gpio en: %d\n", rc); return rc; } if (*(bool *)kp->arg) { gpio_direction_output( chip->ext_ovp_isns_gpio, 1); chip->ext_ovp_ic_gpio_enabled = 1; pr_debug("enabled GPIO\n"); } else { gpio_direction_output( chip->ext_ovp_isns_gpio, 0); chip->ext_ovp_ic_gpio_enabled = 0; pr_debug("disabled GPIO\n"); } return rc; } static struct kernel_param_ops ext_ovp_isns_ops = { .get = ext_ovp_isns_read, }; module_param_cb(ext_ovp_isns_ua, &ext_ovp_isns_ops, &ext_ovp_isns_ua, 0644); static struct kernel_param_ops ext_ovp_en_ops = { .set = ext_ovp_isns_enable, .get = param_get_bool, }; module_param_cb(ext_ovp_isns_online, &ext_ovp_en_ops, &ext_ovp_isns_online, 0664); static inline int get_bpd(const char *name) { Loading Loading @@ -890,6 +971,7 @@ qpnp_chg_iusb_trim_set(struct qpnp_chg_chip *chip, int trim) return rc; } #define IOVP_USB_WALL_TRSH_MA 150 static int qpnp_chg_iusbmax_set(struct qpnp_chg_chip *chip, int mA) { Loading Loading @@ -1323,7 +1405,13 @@ qpnp_chg_usb_chg_gone_irq_handler(int irq, void *_chip) if ((qpnp_chg_is_usb_chg_plugged_in(chip) || qpnp_chg_is_dc_chg_plugged_in(chip)) && (usb_sts & CHG_GONE_IRQ)) { if (ext_ovp_isns_present) { pr_debug("EXT OVP IC ISNS disabled due to ARB WA\n"); gpio_direction_output(chip->ext_ovp_isns_gpio, 0); } qpnp_chg_charge_en(chip, 0); qpnp_chg_force_run_on_batt(chip, 1); schedule_delayed_work(&chip->arb_stop_work, msecs_to_jiffies(ARB_STOP_WORK_MS)); Loading Loading @@ -1656,6 +1744,7 @@ qpnp_chg_usb_usbin_valid_irq_handler(int irq, void *_chip) qpnp_chg_usb_suspend_enable(chip, 0); qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100); qpnp_chg_iusb_trim_set(chip, chip->usb_trim_default); chip->prev_usb_max_ma = -EINVAL; chip->aicl_settled = false; } else { Loading Loading @@ -1986,6 +2075,13 @@ qpnp_chg_chgr_chg_fastchg_irq_handler(int irq, void *_chip) } if (chip->parallel_ovp_mode) switch_parallel_ovp_mode(chip, 1); if (ext_ovp_isns_present && chip->ext_ovp_ic_gpio_enabled) { pr_debug("EXT OVP IC ISNS enabled\n"); gpio_direction_output( chip->ext_ovp_isns_gpio, 1); } } else { if (chip->parallel_ovp_mode) switch_parallel_ovp_mode(chip, 0); Loading Loading @@ -2599,12 +2695,18 @@ qpnp_batt_external_power_changed(struct power_supply *psy) if (!qpnp_is_dc_higher_prio(chip)) qpnp_chg_idcmax_set(chip, QPNP_CHG_I_MAX_MIN_100); if (!ext_ovp_present) qpnp_chg_iusbmax_set(chip, USB_WALL_THRESHOLD_MA); else if (unlikely(ext_ovp_present)) { qpnp_chg_iusbmax_set(chip, OVP_USB_WALL_TRSH_MA); } else if (unlikely( ext_ovp_isns_present)) { qpnp_chg_iusb_trim_set(chip, 0); qpnp_chg_iusbmax_set(chip, IOVP_USB_WALL_TRSH_MA); } else { qpnp_chg_iusbmax_set(chip, USB_WALL_THRESHOLD_MA); } } else { qpnp_chg_iusbmax_set(chip, ret.intval / 1000); } Loading Loading @@ -3146,6 +3248,12 @@ qpnp_chg_regulator_boost_enable(struct regulator_dev *rdev) if (qpnp_chg_is_usb_chg_plugged_in(chip) && (chip->flags & BOOST_FLASH_WA)) { if (ext_ovp_isns_present && chip->ext_ovp_ic_gpio_enabled) { pr_debug("EXT OVP IC ISNS disabled\n"); gpio_direction_output(chip->ext_ovp_isns_gpio, 0); } qpnp_chg_usb_suspend_enable(chip, 1); rc = qpnp_chg_masked_write(chip, Loading Loading @@ -3263,6 +3371,11 @@ qpnp_chg_regulator_boost_disable(struct regulator_dev *rdev) qpnp_chg_usb_suspend_enable(chip, 0); } if (ext_ovp_isns_present && chip->ext_ovp_ic_gpio_enabled) { pr_debug("EXT OVP IC ISNS enable\n"); gpio_direction_output(chip->ext_ovp_isns_gpio, 1); } return rc; } Loading Loading @@ -4832,6 +4945,17 @@ qpnp_charger_read_dt_props(struct qpnp_chg_chip *chip) ext_ovp_present = of_property_read_bool(chip->spmi->dev.of_node, "qcom,ext-ovp-present"); /* Check if external IOVP part is configured */ chip->ext_ovp_isns_gpio = of_get_named_gpio(chip->spmi->dev.of_node, "qcom,ext-ovp-isns-enable-gpio", 0); if (gpio_is_valid(chip->ext_ovp_isns_gpio)) { ext_ovp_isns_present = true; rc = of_property_read_u32(chip->spmi->dev.of_node, "qcom,ext-ovp-isns-r-ohm", &ext_ovp_isns_r); if (rc) return rc; } /* Get the charging-disabled property */ chip->charging_disabled = of_property_read_bool(chip->spmi->dev.of_node, "qcom,charging-disabled"); Loading Loading @@ -4939,6 +5063,9 @@ qpnp_charger_probe(struct spmi_device *spmi) if (rc) return rc; if (ext_ovp_isns_present) chip->ext_ovp_ic_gpio_enabled = 0; /* * Check if bat_if is set in DT and make sure VADC is present * Also try loading the battery data profile if bat_if exists Loading Loading @@ -5220,6 +5347,7 @@ qpnp_charger_probe(struct spmi_device *spmi) goto unregister_dc_psy; } chip->usb_trim_default = qpnp_chg_iusb_trim_get(chip); qpnp_chg_charge_en(chip, !chip->charging_disabled); qpnp_chg_force_run_on_batt(chip, chip->charging_disabled); qpnp_chg_set_appropriate_vddmax(chip); Loading Loading
Documentation/devicetree/bindings/power/qpnp-charger.txt +2 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,8 @@ Parent node optional properties: DC and USB OVP FETs. Please note that this should only be enabled in board designs with PM8941 which have DC_IN and USB_IN connected via a short. - qcom,ext-ovp-isns-enable-gpio External OVP enable GPIO. - qcom,ext-ovp-isns-r-ohm External ISNS OVP resistance in ohm. Sub node required structure: - A qcom,chg node must be a child of an SPMI node that has specified Loading
drivers/power/qpnp-charger.c +133 −5 Original line number Diff line number Diff line Loading @@ -34,6 +34,9 @@ #include <linux/alarmtimer.h> #include <linux/time.h> #include <linux/spinlock.h> #include <linux/gpio.h> #include <linux/of_gpio.h> #include <linux/qpnp/pin.h> /* Interrupt offsets */ #define INT_RT_STS(base) (base + 0x10) Loading Loading @@ -392,6 +395,9 @@ struct qpnp_chg_chip { struct work_struct reduce_power_stage_work; bool power_stage_workaround_running; bool power_stage_workaround_enable; bool ext_ovp_ic_gpio_enabled; unsigned int ext_ovp_isns_gpio; unsigned int usb_trim_default; }; static void Loading Loading @@ -434,6 +440,81 @@ enum usbin_health { USBIN_OVP, }; static int ext_ovp_isns_present; module_param(ext_ovp_isns_present, int, 0444); static int ext_ovp_isns_r; module_param(ext_ovp_isns_r, int, 0444); static bool ext_ovp_isns_online; static long ext_ovp_isns_ua; #define MAX_CURRENT_LENGTH_9A 10 #define ISNS_CURRENT_RATIO 2500 static int ext_ovp_isns_read(char *buffer, const struct kernel_param *kp) { int rc; struct qpnp_vadc_result results; struct power_supply *batt_psy = power_supply_get_by_name("battery"); struct qpnp_chg_chip *chip = container_of(batt_psy, struct qpnp_chg_chip, batt_psy); if (!ext_ovp_isns_present) return 0; rc = qpnp_vadc_read(chip->vadc_dev, P_MUX7_1_1, &results); if (rc) { pr_err("Unable to read vbat rc=%d\n", rc); return 0; } pr_debug("voltage %lld uV, current: %d\n mA", results.physical, ((int) results.physical / (ext_ovp_isns_r / ISNS_CURRENT_RATIO))); return snprintf(buffer, MAX_CURRENT_LENGTH_9A, "%d\n", ((int)results.physical / (ext_ovp_isns_r / ISNS_CURRENT_RATIO))); } static int ext_ovp_isns_enable(const char *val, const struct kernel_param *kp) { int rc; struct power_supply *batt_psy = power_supply_get_by_name("battery"); struct qpnp_chg_chip *chip = container_of(batt_psy, struct qpnp_chg_chip, batt_psy); rc = param_set_bool(val, kp); if (rc) { pr_err("Unable to set gpio en: %d\n", rc); return rc; } if (*(bool *)kp->arg) { gpio_direction_output( chip->ext_ovp_isns_gpio, 1); chip->ext_ovp_ic_gpio_enabled = 1; pr_debug("enabled GPIO\n"); } else { gpio_direction_output( chip->ext_ovp_isns_gpio, 0); chip->ext_ovp_ic_gpio_enabled = 0; pr_debug("disabled GPIO\n"); } return rc; } static struct kernel_param_ops ext_ovp_isns_ops = { .get = ext_ovp_isns_read, }; module_param_cb(ext_ovp_isns_ua, &ext_ovp_isns_ops, &ext_ovp_isns_ua, 0644); static struct kernel_param_ops ext_ovp_en_ops = { .set = ext_ovp_isns_enable, .get = param_get_bool, }; module_param_cb(ext_ovp_isns_online, &ext_ovp_en_ops, &ext_ovp_isns_online, 0664); static inline int get_bpd(const char *name) { Loading Loading @@ -890,6 +971,7 @@ qpnp_chg_iusb_trim_set(struct qpnp_chg_chip *chip, int trim) return rc; } #define IOVP_USB_WALL_TRSH_MA 150 static int qpnp_chg_iusbmax_set(struct qpnp_chg_chip *chip, int mA) { Loading Loading @@ -1323,7 +1405,13 @@ qpnp_chg_usb_chg_gone_irq_handler(int irq, void *_chip) if ((qpnp_chg_is_usb_chg_plugged_in(chip) || qpnp_chg_is_dc_chg_plugged_in(chip)) && (usb_sts & CHG_GONE_IRQ)) { if (ext_ovp_isns_present) { pr_debug("EXT OVP IC ISNS disabled due to ARB WA\n"); gpio_direction_output(chip->ext_ovp_isns_gpio, 0); } qpnp_chg_charge_en(chip, 0); qpnp_chg_force_run_on_batt(chip, 1); schedule_delayed_work(&chip->arb_stop_work, msecs_to_jiffies(ARB_STOP_WORK_MS)); Loading Loading @@ -1656,6 +1744,7 @@ qpnp_chg_usb_usbin_valid_irq_handler(int irq, void *_chip) qpnp_chg_usb_suspend_enable(chip, 0); qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100); qpnp_chg_iusb_trim_set(chip, chip->usb_trim_default); chip->prev_usb_max_ma = -EINVAL; chip->aicl_settled = false; } else { Loading Loading @@ -1986,6 +2075,13 @@ qpnp_chg_chgr_chg_fastchg_irq_handler(int irq, void *_chip) } if (chip->parallel_ovp_mode) switch_parallel_ovp_mode(chip, 1); if (ext_ovp_isns_present && chip->ext_ovp_ic_gpio_enabled) { pr_debug("EXT OVP IC ISNS enabled\n"); gpio_direction_output( chip->ext_ovp_isns_gpio, 1); } } else { if (chip->parallel_ovp_mode) switch_parallel_ovp_mode(chip, 0); Loading Loading @@ -2599,12 +2695,18 @@ qpnp_batt_external_power_changed(struct power_supply *psy) if (!qpnp_is_dc_higher_prio(chip)) qpnp_chg_idcmax_set(chip, QPNP_CHG_I_MAX_MIN_100); if (!ext_ovp_present) qpnp_chg_iusbmax_set(chip, USB_WALL_THRESHOLD_MA); else if (unlikely(ext_ovp_present)) { qpnp_chg_iusbmax_set(chip, OVP_USB_WALL_TRSH_MA); } else if (unlikely( ext_ovp_isns_present)) { qpnp_chg_iusb_trim_set(chip, 0); qpnp_chg_iusbmax_set(chip, IOVP_USB_WALL_TRSH_MA); } else { qpnp_chg_iusbmax_set(chip, USB_WALL_THRESHOLD_MA); } } else { qpnp_chg_iusbmax_set(chip, ret.intval / 1000); } Loading Loading @@ -3146,6 +3248,12 @@ qpnp_chg_regulator_boost_enable(struct regulator_dev *rdev) if (qpnp_chg_is_usb_chg_plugged_in(chip) && (chip->flags & BOOST_FLASH_WA)) { if (ext_ovp_isns_present && chip->ext_ovp_ic_gpio_enabled) { pr_debug("EXT OVP IC ISNS disabled\n"); gpio_direction_output(chip->ext_ovp_isns_gpio, 0); } qpnp_chg_usb_suspend_enable(chip, 1); rc = qpnp_chg_masked_write(chip, Loading Loading @@ -3263,6 +3371,11 @@ qpnp_chg_regulator_boost_disable(struct regulator_dev *rdev) qpnp_chg_usb_suspend_enable(chip, 0); } if (ext_ovp_isns_present && chip->ext_ovp_ic_gpio_enabled) { pr_debug("EXT OVP IC ISNS enable\n"); gpio_direction_output(chip->ext_ovp_isns_gpio, 1); } return rc; } Loading Loading @@ -4832,6 +4945,17 @@ qpnp_charger_read_dt_props(struct qpnp_chg_chip *chip) ext_ovp_present = of_property_read_bool(chip->spmi->dev.of_node, "qcom,ext-ovp-present"); /* Check if external IOVP part is configured */ chip->ext_ovp_isns_gpio = of_get_named_gpio(chip->spmi->dev.of_node, "qcom,ext-ovp-isns-enable-gpio", 0); if (gpio_is_valid(chip->ext_ovp_isns_gpio)) { ext_ovp_isns_present = true; rc = of_property_read_u32(chip->spmi->dev.of_node, "qcom,ext-ovp-isns-r-ohm", &ext_ovp_isns_r); if (rc) return rc; } /* Get the charging-disabled property */ chip->charging_disabled = of_property_read_bool(chip->spmi->dev.of_node, "qcom,charging-disabled"); Loading Loading @@ -4939,6 +5063,9 @@ qpnp_charger_probe(struct spmi_device *spmi) if (rc) return rc; if (ext_ovp_isns_present) chip->ext_ovp_ic_gpio_enabled = 0; /* * Check if bat_if is set in DT and make sure VADC is present * Also try loading the battery data profile if bat_if exists Loading Loading @@ -5220,6 +5347,7 @@ qpnp_charger_probe(struct spmi_device *spmi) goto unregister_dc_psy; } chip->usb_trim_default = qpnp_chg_iusb_trim_get(chip); qpnp_chg_charge_en(chip, !chip->charging_disabled); qpnp_chg_force_run_on_batt(chip, chip->charging_disabled); qpnp_chg_set_appropriate_vddmax(chip); Loading