Loading Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt +2 −0 Original line number Diff line number Diff line Loading @@ -119,6 +119,8 @@ Optional properties: 2: Flash strobe is used for LED1; GPIO9 is used for LED2; GPIO10 is used for LED3 - switchX-supply : phandle of the regulator that needs to be used as a supply for flash switch_X device. - qcom,bst-pwm-ovrhd-uv : Charger flash VPH overhead. Applicable for PMI632 only. Supported values (in mV) are: 300, 400, 500, 600. Default is 300. Child node: Contains settings for each individual LED. Each LED channel needs a flash node and torch node for itself, and an individual switch node to serve as an overall switch. Loading drivers/leds/leds-qpnp-flash-v2.c +499 −121 Original line number Diff line number Diff line /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -34,82 +34,115 @@ #include "leds.h" #define FLASH_LED_REG_LED_STATUS1(base) (base + 0x08) #define FLASH_LED_REG_LED_STATUS2(base) (base + 0x09) #define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4) #define FLASH_LED_REG_INT_RT_STS(base) (base + 0x10) #define FLASH_LED_REG_SAFETY_TMR(base) (base + 0x40) #define FLASH_LED_SAFETY_TMR_ENABLE BIT(7) #define FLASH_LED_REG_TGR_CURRENT(base) (base + 0x43) #define FLASH_LED_REG_MOD_CTRL(base) (base + 0x46) #define FLASH_LED_MOD_CTRL_MASK BIT(7) #define FLASH_LED_MOD_ENABLE BIT(7) #define FLASH_LED_REG_IRES(base) (base + 0x47) #define FLASH_LED_REG_STROBE_CFG(base) (base + 0x48) #define FLASH_LED_STROBE_MASK GENMASK(1, 0) #define FLASH_LED_REG_STROBE_CTRL(base) (base + 0x49) #define FLASH_LED_HW_SW_STROBE_SEL_BIT BIT(2) #define FLASH_HW_STROBE_MASK GENMASK(2, 0) #define FLASH_LED_EN_LED_CTRL(base) (base + 0x4C) #define FLASH_LED_ENABLE BIT(0) #define FLASH_LED_REG_HDRM_PRGM(base) (base + 0x4D) #define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4) #define FLASH_LED_HDRM_VOL_SHIFT 4 #define FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(base) (base + 0x50) #define FLASH_LED_REG_WARMUP_DELAY(base) (base + 0x51) #define FLASH_LED_REG_ISC_DELAY(base) (base + 0x52) #define FLASH_LED_ISC_WARMUP_DELAY_MASK GENMASK(1, 0) #define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6 #define FLASH_LED_REG_THERMAL_RMP_DN_RATE(base) (base + 0x55) #define THERMAL_OTST1_RAMP_CTRL_MASK BIT(7) #define THERMAL_OTST1_RAMP_CTRL_SHIFT 7 #define THERMAL_DERATE_SLOW_SHIFT 4 #define THERMAL_DERATE_SLOW_MASK GENMASK(6, 4) #define THERMAL_DERATE_FAST_MASK GENMASK(2, 0) #define FLASH_LED_REG_THERMAL_THRSH1(base) (base + 0x56) #define FLASH_LED_THERMAL_THRSH_MASK GENMASK(2, 0) #define FLASH_LED_REG_THERMAL_THRSH2(base) (base + 0x57) #define FLASH_LED_REG_THERMAL_THRSH3(base) (base + 0x58) #define FLASH_LED_REG_THERMAL_HYSTERESIS(base) (base + 0x59) #define FLASH_LED_THERMAL_HYSTERESIS_MASK GENMASK(1, 0) #define FLASH_LED_REG_THERMAL_DEBOUNCE(base) (base + 0x5A) #define FLASH_LED_THERMAL_DEBOUNCE_MASK GENMASK(1, 0) #define FLASH_LED_REG_VPH_DROOP_THRESHOLD(base) (base + 0x61) #define FLASH_LED_VPH_DROOP_HYSTERESIS_MASK GENMASK(5, 4) #define FLASH_LED_VPH_DROOP_THRESHOLD_MASK GENMASK(2, 0) #define FLASH_LED_VPH_DROOP_HYST_SHIFT 4 #define FLASH_LED_REG_VPH_DROOP_DEBOUNCE(base) (base + 0x62) #define FLASH_LED_VPH_DROOP_DEBOUNCE_MASK GENMASK(1, 0) #define FLASH_LED_REG_ILED_GRT_THRSH(base) (base + 0x67) #define FLASH_LED_ILED_GRT_THRSH_MASK GENMASK(5, 0) #define FLASH_LED_REG_LED1N2_ICLAMP_LOW(base) (base + 0x68) #define FLASH_LED_REG_LED1N2_ICLAMP_MID(base) (base + 0x69) #define FLASH_LED_REG_LED3_ICLAMP_LOW(base) (base + 0x6A) #define FLASH_LED_REG_LED3_ICLAMP_MID(base) (base + 0x6B) #define FLASH_LED_REG_MITIGATION_SEL(base) (base + 0x6E) #define FLASH_LED_REG_MITIGATION_SW(base) (base + 0x6F) #define FLASH_LED_REG_LMH_LEVEL(base) (base + 0x70) #define FLASH_LED_REG_MULTI_STROBE_CTRL(base) (base + 0x71) #define FLASH_LED_REG_LPG_INPUT_CTRL(base) (base + 0x72) #define FLASH_LED_REG_CURRENT_DERATE_EN(base) (base + 0x76) #define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4) #define FLASH_LED_REG_LED3_ICLAMP_MID(base) (base + 0x6B) #define FLASH_LED_CURRENT_MASK GENMASK(6, 0) #define FLASH_LED_STROBE_MASK GENMASK(1, 0) #define FLASH_HW_STROBE_MASK GENMASK(2, 0) #define FLASH_LED_ISC_WARMUP_DELAY_MASK GENMASK(1, 0) #define FLASH_LED_CURRENT_DERATE_EN_MASK GENMASK(2, 0) #define FLASH_LED_VPH_DROOP_DEBOUNCE_MASK GENMASK(1, 0) #define FLASH_LED_REG_MITIGATION_SEL(base) (base + 0x6E) #define FLASH_LED_CHGR_MITIGATION_SEL_MASK GENMASK(5, 4) #define FLASH_LED_LMH_MITIGATION_SEL_MASK GENMASK(1, 0) #define FLASH_LED_ILED_GRT_THRSH_MASK GENMASK(5, 0) #define FLASH_LED_LMH_LEVEL_MASK GENMASK(1, 0) #define FLASH_LED_VPH_DROOP_HYSTERESIS_MASK GENMASK(5, 4) #define FLASH_LED_VPH_DROOP_THRESHOLD_MASK GENMASK(2, 0) #define FLASH_LED_THERMAL_HYSTERESIS_MASK GENMASK(1, 0) #define FLASH_LED_THERMAL_DEBOUNCE_MASK GENMASK(1, 0) #define FLASH_LED_THERMAL_THRSH_MASK GENMASK(2, 0) #define FLASH_LED_MOD_CTRL_MASK BIT(7) #define FLASH_LED_HW_SW_STROBE_SEL_BIT BIT(2) #define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4) #define FLASH_LED_REG_MITIGATION_SW(base) (base + 0x6F) #define FLASH_LED_LMH_MITIGATION_EN_MASK BIT(0) #define FLASH_LED_CHGR_MITIGATION_EN_MASK BIT(4) #define THERMAL_OTST1_RAMP_CTRL_MASK BIT(7) #define THERMAL_OTST1_RAMP_CTRL_SHIFT 7 #define THERMAL_DERATE_SLOW_SHIFT 4 #define THERMAL_DERATE_SLOW_MASK GENMASK(6, 4) #define THERMAL_DERATE_FAST_MASK GENMASK(2, 0) #define LED1N2_FLASH_ONCE_ONLY_BIT BIT(0) #define FLASH_LED_CHGR_MITIGATION_ENABLE BIT(4) #define FLASH_LED_REG_LMH_LEVEL(base) (base + 0x70) #define FLASH_LED_LMH_LEVEL_MASK GENMASK(1, 0) #define FLASH_LED_REG_MULTI_STROBE_CTRL(base) (base + 0x71) #define LED3_FLASH_ONCE_ONLY_BIT BIT(1) #define LED1N2_FLASH_ONCE_ONLY_BIT BIT(0) #define FLASH_LED_REG_LPG_INPUT_CTRL(base) (base + 0x72) #define LPG_INPUT_SEL_BIT BIT(0) #define FLASH_LED_REG_CURRENT_DERATE_EN(base) (base + 0x76) #define FLASH_LED_CURRENT_DERATE_EN_MASK GENMASK(2, 0) #define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8) #define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25) #define VPH_DROOP_THRESH_VAL_TO_UV(val) ((val + 25) * 100000) #define MITIGATION_THRSH_MA_TO_VAL(val_ma) (val_ma / 100) #define THERMAL_HYST_TEMP_TO_VAL(val, divisor) (val / divisor) #define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6 #define FLASH_LED_WARMUP_DELAY_DEFAULT 2 #define FLASH_LED_ISC_DELAY_DEFAULT 3 #define FLASH_LED_VPH_DROOP_DEBOUNCE_DEFAULT 2 #define FLASH_LED_VPH_DROOP_HYST_SHIFT 4 #define FLASH_LED_VPH_DROOP_HYST_DEFAULT 2 #define FLASH_LED_VPH_DROOP_THRESH_DEFAULT 5 #define BHARGER_FLASH_LED_VPH_DROOP_THRESH_DEFAULT 7 #define FLASH_LED_DEBOUNCE_MAX 3 #define FLASH_LED_HYSTERESIS_MAX 3 #define FLASH_LED_VPH_DROOP_THRESH_MAX 7 Loading @@ -123,11 +156,9 @@ #define FLASH_LED_VLED_MAX_DEFAULT_UV 3500000 #define FLASH_LED_IBATT_OCP_THRESH_DEFAULT_UA 4500000 #define FLASH_LED_RPARA_DEFAULT_UOHM 0 #define FLASH_LED_SAFETY_TMR_ENABLE BIT(7) #define FLASH_LED_LMH_LEVEL_DEFAULT 0 #define FLASH_LED_LMH_MITIGATION_ENABLE 1 #define FLASH_LED_LMH_MITIGATION_DISABLE 0 #define FLASH_LED_CHGR_MITIGATION_ENABLE BIT(4) #define FLASH_LED_CHGR_MITIGATION_DISABLE 0 #define FLASH_LED_LMH_MITIGATION_SEL_DEFAULT 2 #define FLASH_LED_MITIGATION_SEL_MAX 2 Loading @@ -141,7 +172,6 @@ #define FLASH_LED_IRES_MIN_UA 5000 #define FLASH_LED_IRES_DEFAULT_UA 12500 #define FLASH_LED_IRES_DEFAULT_VAL 0x00 #define FLASH_LED_HDRM_VOL_SHIFT 4 #define FLASH_LED_HDRM_VOL_DEFAULT_MV 0x80 #define FLASH_LED_HDRM_VOL_HI_LO_WIN_DEFAULT_MV 0x04 #define FLASH_LED_HDRM_VOL_BASE_MV 125 Loading @@ -150,8 +180,6 @@ #define FLASH_LED_HW_STROBE_OPTION_1 0x00 #define FLASH_LED_HW_STROBE_OPTION_2 0x01 #define FLASH_LED_HW_STROBE_OPTION_3 0x02 #define FLASH_LED_ENABLE BIT(0) #define FLASH_LED_MOD_ENABLE BIT(7) #define FLASH_LED_DISABLE 0x00 #define FLASH_LED_SAFETY_TMR_DISABLED 0x13 #define FLASH_LED_MAX_TOTAL_CURRENT_MA 3750 Loading @@ -160,6 +188,8 @@ #define FLASH_LED_IRES10P0_MAX_CURR_MA 1280 #define FLASH_LED_IRES12P5_MAX_CURR_MA 1600 #define MAX_IRES_LEVELS 4 #define FLASH_BST_PWM_OVRHD_MIN_UV 300000 #define FLASH_BST_PWM_OVRHD_MAX_UV 600000 /* notifier call chain for flash-led irqs */ static ATOMIC_NOTIFIER_HEAD(irq_notifier_list); Loading Loading @@ -259,6 +289,7 @@ struct flash_led_platform_data { u32 led1n2_iclamp_mid_ma; u32 led3_iclamp_low_ma; u32 led3_iclamp_mid_ma; u32 bst_pwm_ovrhd_uv; u8 isc_delay; u8 warmup_delay; u8 current_derate_en_cfg; Loading @@ -284,6 +315,8 @@ struct qpnp_flash_led { struct flash_node_data *fnode; struct flash_switch_data *snode; struct power_supply *bms_psy; struct power_supply *main_psy; struct power_supply *usb_psy; struct notifier_block nb; spinlock_t lock; int num_fnodes; Loading Loading @@ -739,9 +772,12 @@ static int get_property_from_fg(struct qpnp_flash_led *led, } #define VOLTAGE_HDRM_DEFAULT_MV 350 #define BHARGER_VOLTAGE_HDRM_DEFAULT_MV 400 #define BHARGER_HEADROOM_OFFSET_MV 50 static int qpnp_flash_led_get_voltage_headroom(struct qpnp_flash_led *led) { int i, voltage_hdrm_mv = 0, voltage_hdrm_max = 0; u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; for (i = 0; i < led->num_fnodes; i++) { if (led->fnode[i].led_on) { Loading @@ -765,13 +801,18 @@ static int qpnp_flash_led_get_voltage_headroom(struct qpnp_flash_led *led) voltage_hdrm_mv = 350; } if (pmic_subtype == PMI632_SUBTYPE) voltage_hdrm_mv += BHARGER_HEADROOM_OFFSET_MV; voltage_hdrm_max = max(voltage_hdrm_max, voltage_hdrm_mv); } } if (!voltage_hdrm_max) return VOLTAGE_HDRM_DEFAULT_MV; return (pmic_subtype == PMI632_SUBTYPE) ? BHARGER_VOLTAGE_HDRM_DEFAULT_MV : VOLTAGE_HDRM_DEFAULT_MV; return voltage_hdrm_max; } Loading Loading @@ -883,6 +924,179 @@ static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led, return 0; } static int is_main_psy_available(struct qpnp_flash_led *led) { if (!led->main_psy) { led->main_psy = power_supply_get_by_name("main"); if (!led->main_psy) { pr_err_ratelimited("Couldn't get main_psy\n"); return -ENODEV; } } return 0; } static int is_usb_psy_available(struct qpnp_flash_led *led) { if (!led->usb_psy) { led->usb_psy = power_supply_get_by_name("usb"); if (!led->usb_psy) { pr_err_ratelimited("Couldn't get usb_psy\n"); return -ENODEV; } } return 0; } #define CHGBST_EFFICIENCY 800LL #define CHGBST_FLASH_VDIP_MARGIN 10000 #define VIN_FLASH_UV 5000000 #define BHARGER_FLASH_LED_MAX_TOTAL_CURRENT_MA 1500 #define BHARGER_FLASH_LED_WITH_OTG_MAX_TOTAL_CURRENT_MA 1100 static int qpnp_flash_led_calc_bharger_max_current(struct qpnp_flash_led *led, int *max_current) { union power_supply_propval pval = {0, }; int ocv_uv, ibat_now, voltage_hdrm_mv, flash_led_max_total_curr_ma, rc; int rbatt_uohm = 0, usb_present, otg_enable; int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw; int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv, vph_flash_vdip; int64_t bst_pwm_ovrhd_uv; rc = is_usb_psy_available(led); if (rc < 0) return rc; rc = power_supply_get_property(led->usb_psy, POWER_SUPPLY_PROP_SCOPE, &pval); if (rc < 0) { pr_err("usb psy does not support usb present, rc=%d\n", rc); return rc; } otg_enable = pval.intval; /* RESISTANCE = esr_uohm + rslow_uohm */ rc = get_property_from_fg(led, POWER_SUPPLY_PROP_RESISTANCE, &rbatt_uohm); if (rc < 0) { pr_err("bms psy does not support resistance, rc=%d\n", rc); return rc; } /* If no battery is connected, return max possible flash current */ if (!rbatt_uohm) { *max_current = (otg_enable == POWER_SUPPLY_SCOPE_SYSTEM) ? BHARGER_FLASH_LED_WITH_OTG_MAX_TOTAL_CURRENT_MA : BHARGER_FLASH_LED_MAX_TOTAL_CURRENT_MA; return 0; } rc = get_property_from_fg(led, POWER_SUPPLY_PROP_VOLTAGE_OCV, &ocv_uv); if (rc < 0) { pr_err("bms psy does not support OCV, rc=%d\n", rc); return rc; } rc = get_property_from_fg(led, POWER_SUPPLY_PROP_CURRENT_NOW, &ibat_now); if (rc < 0) { pr_err("bms psy does not support current, rc=%d\n", rc); return rc; } bst_pwm_ovrhd_uv = led->pdata->bst_pwm_ovrhd_uv; rc = power_supply_get_property(led->usb_psy, POWER_SUPPLY_PROP_PRESENT, &pval); if (rc < 0) { pr_err("usb psy does not support usb present, rc=%d\n", rc); return rc; } usb_present = pval.intval; rbatt_uohm += led->pdata->rpara_uohm; voltage_hdrm_mv = qpnp_flash_led_get_voltage_headroom(led); vph_flash_vdip = VPH_DROOP_THRESH_VAL_TO_UV(led->pdata->vph_droop_threshold) + CHGBST_FLASH_VDIP_MARGIN; /* Check if LMH_MITIGATION needs to be triggered */ if (!led->trigger_lmh && (ocv_uv < led->pdata->lmh_ocv_threshold_uv || rbatt_uohm > led->pdata->lmh_rbatt_threshold_uohm)) { led->trigger_lmh = true; rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MITIGATION_SW(led->base), FLASH_LED_LMH_MITIGATION_EN_MASK, FLASH_LED_LMH_MITIGATION_ENABLE); if (rc < 0) { pr_err("trigger lmh mitigation failed, rc=%d\n", rc); return rc; } /* Wait for LMH mitigation to take effect */ udelay(100); return qpnp_flash_led_calc_bharger_max_current(led, max_current); } /* * Calculate the maximum current that can pulled out of the battery * before the battery voltage dips below a safe threshold. */ ibat_safe_ua = div_s64((ocv_uv - vph_flash_vdip) * UCONV, rbatt_uohm); if (ibat_safe_ua <= led->pdata->ibatt_ocp_threshold_ua) { /* * If the calculated current is below the OCP threshold, then * use it as the possible flash current. */ ibat_flash_ua = ibat_safe_ua - ibat_now; vph_flash_uv = vph_flash_vdip; } else { /* * If the calculated current is above the OCP threshold, then * use the ocp threshold instead. * * Any higher current will be tripping the battery OCP. */ ibat_flash_ua = led->pdata->ibatt_ocp_threshold_ua - ibat_now; vph_flash_uv = ocv_uv - div64_s64((int64_t)rbatt_uohm * led->pdata->ibatt_ocp_threshold_ua, UCONV); } /* when USB is present or OTG is enabled, VIN_FLASH is always at 5V */ if (usb_present || (otg_enable == POWER_SUPPLY_SCOPE_SYSTEM)) vin_flash_uv = VIN_FLASH_UV; else /* Calculate the input voltage of the flash module. */ vin_flash_uv = max((led->pdata->vled_max_uv + (voltage_hdrm_mv * MCONV)), vph_flash_uv + bst_pwm_ovrhd_uv); /* Calculate the available power for the flash module. */ avail_flash_power_fw = CHGBST_EFFICIENCY * vph_flash_uv * ibat_flash_ua; /* * Calculate the available amount of current the flash module can draw * before collapsing the battery. (available power/ flash input voltage) */ avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV); flash_led_max_total_curr_ma = otg_enable ? BHARGER_FLASH_LED_WITH_OTG_MAX_TOTAL_CURRENT_MA : BHARGER_FLASH_LED_MAX_TOTAL_CURRENT_MA; *max_current = min(flash_led_max_total_curr_ma, (int)(div64_s64(avail_flash_ua, MCONV))); pr_debug("avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d, trigger_lmh=%d max_current=%lld usb_present=%d otg_enable=%d\n", avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm, led->trigger_lmh, (*max_current * MCONV), usb_present, otg_enable); return 0; } static int qpnp_flash_led_calc_thermal_current_lim(struct qpnp_flash_led *led, int *thermal_current_lim) { Loading Loading @@ -974,9 +1188,16 @@ static int qpnp_flash_led_get_max_avail_current(struct qpnp_flash_led *led, int *max_avail_current) { int thermal_current_lim = 0, rc; u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; led->trigger_lmh = false; if (pmic_subtype == PMI632_SUBTYPE) rc = qpnp_flash_led_calc_bharger_max_current(led, max_avail_current); else rc = qpnp_flash_led_calc_max_current(led, max_avail_current); if (rc < 0) { pr_err("Couldn't calculate max_avail_current, rc=%d\n", rc); return rc; Loading Loading @@ -1018,6 +1239,7 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value) int prgm_current_ma = value; int min_ma = fnode->ires_ua / 1000; struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev); u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; if (value <= 0) prgm_current_ma = 0; Loading Loading @@ -1052,7 +1274,8 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value) if (prgm_current_ma) fnode->led_on = true; if (led->pdata->chgr_mitigation_sel == FLASH_SW_CHARGER_MITIGATION) { if (pmic_subtype != PMI632_SUBTYPE && led->pdata->chgr_mitigation_sel == FLASH_SW_CHARGER_MITIGATION) { qpnp_flash_led_aggregate_max_current(fnode); led->trigger_chgr = false; if (led->total_current_ma >= 1000) Loading Loading @@ -1082,7 +1305,7 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode) } } if (!led->trigger_chgr) { if (led->pdata->chgr_mitigation_sel && !led->trigger_chgr) { rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MITIGATION_SW(led->base), FLASH_LED_CHGR_MITIGATION_EN_MASK, Loading Loading @@ -1213,9 +1436,51 @@ static int qpnp_flash_led_symmetry_config(struct flash_switch_data *snode) return 0; } #define FLASH_LED_MODULE_EN_TIME_MS 300 static int qpnp_flash_poll_vreg_ok(struct qpnp_flash_led *led) { int rc, i; union power_supply_propval pval = {0, }; rc = is_main_psy_available(led); if (rc < 0) return rc; for (i = 0; i < 60; i++) { /* wait for the flash vreg_ok to be set */ mdelay(5); rc = power_supply_get_property(led->main_psy, POWER_SUPPLY_PROP_FLASH_TRIGGER, &pval); if (rc < 0) { pr_err("main psy doesn't support reading prop %d rc = %d\n", POWER_SUPPLY_PROP_FLASH_TRIGGER, rc); return rc; } if (pval.intval > 0) { pr_debug("Flash trigger set\n"); break; } if (pval.intval < 0) { pr_err("Error during flash trigger %d\n", pval.intval); return pval.intval; } } if (!pval.intval) { pr_err("Failed to enable the module\n"); return -ETIMEDOUT; } return 0; } static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) { struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev); u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; int rc, i, addr_offset; u8 val, mask; Loading Loading @@ -1310,6 +1575,19 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) FLASH_LED_MOD_CTRL_MASK, FLASH_LED_MOD_ENABLE); if (rc < 0) return rc; if (pmic_subtype == PMI632_SUBTYPE) { rc = qpnp_flash_poll_vreg_ok(led); if (rc < 0) { /* Disable the module */ qpnp_flash_led_masked_write(led, FLASH_LED_REG_MOD_CTRL(led->base), FLASH_LED_MOD_CTRL_MASK, FLASH_LED_DISABLE); return rc; } } } led->enable++; Loading @@ -1326,7 +1604,7 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) udelay(500); } if (led->trigger_chgr) { if (led->pdata->chgr_mitigation_sel && led->trigger_chgr) { rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MITIGATION_SW(led->base), FLASH_LED_CHGR_MITIGATION_EN_MASK, Loading @@ -1347,27 +1625,28 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) return 0; } int qpnp_flash_led_prepare(struct led_trigger *trig, int options, int *max_current) static int qpnp_flash_led_regulator_control(struct led_classdev *led_cdev, int options, int *max_current) { struct led_classdev *led_cdev; int rc; u8 pmic_subtype; struct flash_switch_data *snode; struct qpnp_flash_led *led; int rc; if (!trig) { pr_err("Invalid led_trigger provided\n"); return -EINVAL; } led_cdev = trigger_to_lcdev(trig); if (!led_cdev) { pr_err("Invalid led_cdev in trigger %s\n", trig->name); return -EINVAL; } union power_supply_propval ret = {0, }; snode = container_of(led_cdev, struct flash_switch_data, cdev); led = dev_get_drvdata(&snode->pdev->dev); pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; if (pmic_subtype == PMI632_SUBTYPE) { rc = is_main_psy_available(led); if (rc < 0) return rc; rc = is_usb_psy_available(led); if (rc < 0) return rc; } if (!(options & FLASH_LED_PREPARE_OPTIONS_MASK)) { pr_err("Invalid options %d\n", options); Loading @@ -1375,20 +1654,46 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options, } if (options & ENABLE_REGULATOR) { if (pmic_subtype == PMI632_SUBTYPE) { ret.intval = 1; rc = power_supply_set_property(led->main_psy, POWER_SUPPLY_PROP_FLASH_ACTIVE, &ret); if (rc < 0) { pr_err("Failed to set FLASH_ACTIVE on charger rc=%d\n", rc); return rc; } pr_debug("FLASH_ACTIVE = 1\n"); } else { rc = qpnp_flash_led_regulator_enable(led, snode, true); if (rc < 0) { pr_err("enable regulator failed, rc=%d\n", rc); return rc; } } } if (options & DISABLE_REGULATOR) { if (pmic_subtype == PMI632_SUBTYPE) { ret.intval = 0; rc = power_supply_set_property(led->main_psy, POWER_SUPPLY_PROP_FLASH_ACTIVE, &ret); if (rc < 0) { pr_err("Failed to set FLASH_ACTIVE on charger rc=%d\n", rc); return rc; } pr_debug("FLASH_ACTIVE = 0\n"); } else { rc = qpnp_flash_led_regulator_enable(led, snode, false); if (rc < 0) { pr_err("disable regulator failed, rc=%d\n", rc); return rc; } } } if (options & QUERY_MAX_AVAIL_CURRENT) { rc = qpnp_flash_led_get_max_avail_current(led, max_current); Loading @@ -1401,6 +1706,28 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options, return 0; } int qpnp_flash_led_prepare(struct led_trigger *trig, int options, int *max_current) { struct led_classdev *led_cdev; int rc; if (!trig) { pr_err("Invalid led_trigger provided\n"); return -EINVAL; } led_cdev = trigger_to_lcdev(trig); if (!led_cdev) { pr_err("Invalid led_cdev in trigger %s\n", trig->name); return -EINVAL; } rc = qpnp_flash_led_regulator_control(led_cdev, options, max_current); return rc; } static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) { Loading Loading @@ -1440,6 +1767,29 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev, spin_unlock(&led->lock); } static ssize_t qpnp_flash_led_prepare_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc, options, max_current; u32 val; struct led_classdev *led_cdev = dev_get_drvdata(dev); rc = kstrtouint(buf, 0, &val); if (rc < 0) return rc; if (val != 0 && val != 1) return count; options = val ? ENABLE_REGULATOR : DISABLE_REGULATOR; rc = qpnp_flash_led_regulator_control(led_cdev, options, &max_current); if (rc < 0) return rc; return count; } /* sysfs show function for flash_max_current */ static ssize_t qpnp_flash_led_max_current_show(struct device *dev, struct device_attribute *attr, char *buf) Loading @@ -1462,6 +1812,7 @@ static ssize_t qpnp_flash_led_max_current_show(struct device *dev, /* sysfs attributes exported by flash_led */ static struct device_attribute qpnp_flash_led_attrs[] = { __ATTR(max_current, 0664, qpnp_flash_led_max_current_show, NULL), __ATTR(enable, 0664, NULL, qpnp_flash_led_prepare_store), }; static int flash_led_psy_notifier_call(struct notifier_block *nb, Loading Loading @@ -1590,6 +1941,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, int rc, min_ma; u32 val; bool hw_strobe = 0, edge_trigger = 0, active_high = 0; u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; fnode->pdev = led->pdev; fnode->cdev.brightness_set = qpnp_flash_led_brightness_set; Loading Loading @@ -1619,6 +1971,11 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, rc = of_property_read_u32(node, "qcom,id", &val); if (!rc) { fnode->id = (u8)val; if (pmic_subtype == PMI632_SUBTYPE && fnode->id > LED2) { pr_err("Flash node id = %d not supported\n", fnode->id); return -EINVAL; } } else { pr_err("Unable to read flash LED ID\n"); return rc; Loading Loading @@ -1925,6 +2282,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, struct device_node *revid_node; int rc; u32 val; u8 pmic_subtype; bool short_circuit_det, open_circuit_det, vph_droop_det; revid_node = of_parse_phandle(node, "qcom,pmic-revid", 0); Loading @@ -1945,6 +2303,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return -EPROBE_DEFER; } pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; pr_debug("PMIC subtype %d Digital major %d\n", led->pdata->pmic_rev_id->pmic_subtype, led->pdata->pmic_rev_id->rev4); Loading Loading @@ -2059,7 +2418,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, led->pdata->thermal_hysteresis = -EINVAL; rc = of_property_read_u32(node, "qcom,thermal-hysteresis", &val); if (!rc) { if (led->pdata->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) if (pmic_subtype == PM660L_SUBTYPE) val = THERMAL_HYST_TEMP_TO_VAL(val, 20); else val = THERMAL_HYST_TEMP_TO_VAL(val, 15); Loading Loading @@ -2123,7 +2482,13 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return -EINVAL; } led->pdata->vph_droop_threshold = FLASH_LED_VPH_DROOP_THRESH_DEFAULT; if (pmic_subtype == PMI632_SUBTYPE) led->pdata->vph_droop_threshold = BHARGER_FLASH_LED_VPH_DROOP_THRESH_DEFAULT; else led->pdata->vph_droop_threshold = FLASH_LED_VPH_DROOP_THRESH_DEFAULT; rc = of_property_read_u32(node, "qcom,vph-droop-threshold-mv", &val); if (!rc) { led->pdata->vph_droop_threshold = Loading Loading @@ -2268,7 +2633,12 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return -EINVAL; } if (pmic_subtype == PMI632_SUBTYPE) led->pdata->chgr_mitigation_sel = FLASH_DISABLE_CHARGER_MITIGATION; else led->pdata->chgr_mitigation_sel = FLASH_SW_CHARGER_MITIGATION; rc = of_property_read_u32(node, "qcom,chgr-mitigation-sel", &val); if (!rc) { led->pdata->chgr_mitigation_sel = val; Loading Loading @@ -2296,6 +2666,14 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return -EINVAL; } led->pdata->bst_pwm_ovrhd_uv = FLASH_BST_PWM_OVRHD_MIN_UV; rc = of_property_read_u32(node, "qcom,bst-pwm-ovrhd-uv", &val); if (!rc) { if (val >= FLASH_BST_PWM_OVRHD_MIN_UV && val <= FLASH_BST_PWM_OVRHD_MAX_UV) led->pdata->bst_pwm_ovrhd_uv = val; } led->pdata->all_ramp_up_done_irq = of_irq_get_byname(node, "all-ramp-up-done-irq"); if (led->pdata->all_ramp_up_done_irq < 0) Loading Loading
Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt +2 −0 Original line number Diff line number Diff line Loading @@ -119,6 +119,8 @@ Optional properties: 2: Flash strobe is used for LED1; GPIO9 is used for LED2; GPIO10 is used for LED3 - switchX-supply : phandle of the regulator that needs to be used as a supply for flash switch_X device. - qcom,bst-pwm-ovrhd-uv : Charger flash VPH overhead. Applicable for PMI632 only. Supported values (in mV) are: 300, 400, 500, 600. Default is 300. Child node: Contains settings for each individual LED. Each LED channel needs a flash node and torch node for itself, and an individual switch node to serve as an overall switch. Loading
drivers/leds/leds-qpnp-flash-v2.c +499 −121 Original line number Diff line number Diff line /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. /* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -34,82 +34,115 @@ #include "leds.h" #define FLASH_LED_REG_LED_STATUS1(base) (base + 0x08) #define FLASH_LED_REG_LED_STATUS2(base) (base + 0x09) #define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4) #define FLASH_LED_REG_INT_RT_STS(base) (base + 0x10) #define FLASH_LED_REG_SAFETY_TMR(base) (base + 0x40) #define FLASH_LED_SAFETY_TMR_ENABLE BIT(7) #define FLASH_LED_REG_TGR_CURRENT(base) (base + 0x43) #define FLASH_LED_REG_MOD_CTRL(base) (base + 0x46) #define FLASH_LED_MOD_CTRL_MASK BIT(7) #define FLASH_LED_MOD_ENABLE BIT(7) #define FLASH_LED_REG_IRES(base) (base + 0x47) #define FLASH_LED_REG_STROBE_CFG(base) (base + 0x48) #define FLASH_LED_STROBE_MASK GENMASK(1, 0) #define FLASH_LED_REG_STROBE_CTRL(base) (base + 0x49) #define FLASH_LED_HW_SW_STROBE_SEL_BIT BIT(2) #define FLASH_HW_STROBE_MASK GENMASK(2, 0) #define FLASH_LED_EN_LED_CTRL(base) (base + 0x4C) #define FLASH_LED_ENABLE BIT(0) #define FLASH_LED_REG_HDRM_PRGM(base) (base + 0x4D) #define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4) #define FLASH_LED_HDRM_VOL_SHIFT 4 #define FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(base) (base + 0x50) #define FLASH_LED_REG_WARMUP_DELAY(base) (base + 0x51) #define FLASH_LED_REG_ISC_DELAY(base) (base + 0x52) #define FLASH_LED_ISC_WARMUP_DELAY_MASK GENMASK(1, 0) #define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6 #define FLASH_LED_REG_THERMAL_RMP_DN_RATE(base) (base + 0x55) #define THERMAL_OTST1_RAMP_CTRL_MASK BIT(7) #define THERMAL_OTST1_RAMP_CTRL_SHIFT 7 #define THERMAL_DERATE_SLOW_SHIFT 4 #define THERMAL_DERATE_SLOW_MASK GENMASK(6, 4) #define THERMAL_DERATE_FAST_MASK GENMASK(2, 0) #define FLASH_LED_REG_THERMAL_THRSH1(base) (base + 0x56) #define FLASH_LED_THERMAL_THRSH_MASK GENMASK(2, 0) #define FLASH_LED_REG_THERMAL_THRSH2(base) (base + 0x57) #define FLASH_LED_REG_THERMAL_THRSH3(base) (base + 0x58) #define FLASH_LED_REG_THERMAL_HYSTERESIS(base) (base + 0x59) #define FLASH_LED_THERMAL_HYSTERESIS_MASK GENMASK(1, 0) #define FLASH_LED_REG_THERMAL_DEBOUNCE(base) (base + 0x5A) #define FLASH_LED_THERMAL_DEBOUNCE_MASK GENMASK(1, 0) #define FLASH_LED_REG_VPH_DROOP_THRESHOLD(base) (base + 0x61) #define FLASH_LED_VPH_DROOP_HYSTERESIS_MASK GENMASK(5, 4) #define FLASH_LED_VPH_DROOP_THRESHOLD_MASK GENMASK(2, 0) #define FLASH_LED_VPH_DROOP_HYST_SHIFT 4 #define FLASH_LED_REG_VPH_DROOP_DEBOUNCE(base) (base + 0x62) #define FLASH_LED_VPH_DROOP_DEBOUNCE_MASK GENMASK(1, 0) #define FLASH_LED_REG_ILED_GRT_THRSH(base) (base + 0x67) #define FLASH_LED_ILED_GRT_THRSH_MASK GENMASK(5, 0) #define FLASH_LED_REG_LED1N2_ICLAMP_LOW(base) (base + 0x68) #define FLASH_LED_REG_LED1N2_ICLAMP_MID(base) (base + 0x69) #define FLASH_LED_REG_LED3_ICLAMP_LOW(base) (base + 0x6A) #define FLASH_LED_REG_LED3_ICLAMP_MID(base) (base + 0x6B) #define FLASH_LED_REG_MITIGATION_SEL(base) (base + 0x6E) #define FLASH_LED_REG_MITIGATION_SW(base) (base + 0x6F) #define FLASH_LED_REG_LMH_LEVEL(base) (base + 0x70) #define FLASH_LED_REG_MULTI_STROBE_CTRL(base) (base + 0x71) #define FLASH_LED_REG_LPG_INPUT_CTRL(base) (base + 0x72) #define FLASH_LED_REG_CURRENT_DERATE_EN(base) (base + 0x76) #define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4) #define FLASH_LED_REG_LED3_ICLAMP_MID(base) (base + 0x6B) #define FLASH_LED_CURRENT_MASK GENMASK(6, 0) #define FLASH_LED_STROBE_MASK GENMASK(1, 0) #define FLASH_HW_STROBE_MASK GENMASK(2, 0) #define FLASH_LED_ISC_WARMUP_DELAY_MASK GENMASK(1, 0) #define FLASH_LED_CURRENT_DERATE_EN_MASK GENMASK(2, 0) #define FLASH_LED_VPH_DROOP_DEBOUNCE_MASK GENMASK(1, 0) #define FLASH_LED_REG_MITIGATION_SEL(base) (base + 0x6E) #define FLASH_LED_CHGR_MITIGATION_SEL_MASK GENMASK(5, 4) #define FLASH_LED_LMH_MITIGATION_SEL_MASK GENMASK(1, 0) #define FLASH_LED_ILED_GRT_THRSH_MASK GENMASK(5, 0) #define FLASH_LED_LMH_LEVEL_MASK GENMASK(1, 0) #define FLASH_LED_VPH_DROOP_HYSTERESIS_MASK GENMASK(5, 4) #define FLASH_LED_VPH_DROOP_THRESHOLD_MASK GENMASK(2, 0) #define FLASH_LED_THERMAL_HYSTERESIS_MASK GENMASK(1, 0) #define FLASH_LED_THERMAL_DEBOUNCE_MASK GENMASK(1, 0) #define FLASH_LED_THERMAL_THRSH_MASK GENMASK(2, 0) #define FLASH_LED_MOD_CTRL_MASK BIT(7) #define FLASH_LED_HW_SW_STROBE_SEL_BIT BIT(2) #define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4) #define FLASH_LED_REG_MITIGATION_SW(base) (base + 0x6F) #define FLASH_LED_LMH_MITIGATION_EN_MASK BIT(0) #define FLASH_LED_CHGR_MITIGATION_EN_MASK BIT(4) #define THERMAL_OTST1_RAMP_CTRL_MASK BIT(7) #define THERMAL_OTST1_RAMP_CTRL_SHIFT 7 #define THERMAL_DERATE_SLOW_SHIFT 4 #define THERMAL_DERATE_SLOW_MASK GENMASK(6, 4) #define THERMAL_DERATE_FAST_MASK GENMASK(2, 0) #define LED1N2_FLASH_ONCE_ONLY_BIT BIT(0) #define FLASH_LED_CHGR_MITIGATION_ENABLE BIT(4) #define FLASH_LED_REG_LMH_LEVEL(base) (base + 0x70) #define FLASH_LED_LMH_LEVEL_MASK GENMASK(1, 0) #define FLASH_LED_REG_MULTI_STROBE_CTRL(base) (base + 0x71) #define LED3_FLASH_ONCE_ONLY_BIT BIT(1) #define LED1N2_FLASH_ONCE_ONLY_BIT BIT(0) #define FLASH_LED_REG_LPG_INPUT_CTRL(base) (base + 0x72) #define LPG_INPUT_SEL_BIT BIT(0) #define FLASH_LED_REG_CURRENT_DERATE_EN(base) (base + 0x76) #define FLASH_LED_CURRENT_DERATE_EN_MASK GENMASK(2, 0) #define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8) #define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25) #define VPH_DROOP_THRESH_VAL_TO_UV(val) ((val + 25) * 100000) #define MITIGATION_THRSH_MA_TO_VAL(val_ma) (val_ma / 100) #define THERMAL_HYST_TEMP_TO_VAL(val, divisor) (val / divisor) #define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6 #define FLASH_LED_WARMUP_DELAY_DEFAULT 2 #define FLASH_LED_ISC_DELAY_DEFAULT 3 #define FLASH_LED_VPH_DROOP_DEBOUNCE_DEFAULT 2 #define FLASH_LED_VPH_DROOP_HYST_SHIFT 4 #define FLASH_LED_VPH_DROOP_HYST_DEFAULT 2 #define FLASH_LED_VPH_DROOP_THRESH_DEFAULT 5 #define BHARGER_FLASH_LED_VPH_DROOP_THRESH_DEFAULT 7 #define FLASH_LED_DEBOUNCE_MAX 3 #define FLASH_LED_HYSTERESIS_MAX 3 #define FLASH_LED_VPH_DROOP_THRESH_MAX 7 Loading @@ -123,11 +156,9 @@ #define FLASH_LED_VLED_MAX_DEFAULT_UV 3500000 #define FLASH_LED_IBATT_OCP_THRESH_DEFAULT_UA 4500000 #define FLASH_LED_RPARA_DEFAULT_UOHM 0 #define FLASH_LED_SAFETY_TMR_ENABLE BIT(7) #define FLASH_LED_LMH_LEVEL_DEFAULT 0 #define FLASH_LED_LMH_MITIGATION_ENABLE 1 #define FLASH_LED_LMH_MITIGATION_DISABLE 0 #define FLASH_LED_CHGR_MITIGATION_ENABLE BIT(4) #define FLASH_LED_CHGR_MITIGATION_DISABLE 0 #define FLASH_LED_LMH_MITIGATION_SEL_DEFAULT 2 #define FLASH_LED_MITIGATION_SEL_MAX 2 Loading @@ -141,7 +172,6 @@ #define FLASH_LED_IRES_MIN_UA 5000 #define FLASH_LED_IRES_DEFAULT_UA 12500 #define FLASH_LED_IRES_DEFAULT_VAL 0x00 #define FLASH_LED_HDRM_VOL_SHIFT 4 #define FLASH_LED_HDRM_VOL_DEFAULT_MV 0x80 #define FLASH_LED_HDRM_VOL_HI_LO_WIN_DEFAULT_MV 0x04 #define FLASH_LED_HDRM_VOL_BASE_MV 125 Loading @@ -150,8 +180,6 @@ #define FLASH_LED_HW_STROBE_OPTION_1 0x00 #define FLASH_LED_HW_STROBE_OPTION_2 0x01 #define FLASH_LED_HW_STROBE_OPTION_3 0x02 #define FLASH_LED_ENABLE BIT(0) #define FLASH_LED_MOD_ENABLE BIT(7) #define FLASH_LED_DISABLE 0x00 #define FLASH_LED_SAFETY_TMR_DISABLED 0x13 #define FLASH_LED_MAX_TOTAL_CURRENT_MA 3750 Loading @@ -160,6 +188,8 @@ #define FLASH_LED_IRES10P0_MAX_CURR_MA 1280 #define FLASH_LED_IRES12P5_MAX_CURR_MA 1600 #define MAX_IRES_LEVELS 4 #define FLASH_BST_PWM_OVRHD_MIN_UV 300000 #define FLASH_BST_PWM_OVRHD_MAX_UV 600000 /* notifier call chain for flash-led irqs */ static ATOMIC_NOTIFIER_HEAD(irq_notifier_list); Loading Loading @@ -259,6 +289,7 @@ struct flash_led_platform_data { u32 led1n2_iclamp_mid_ma; u32 led3_iclamp_low_ma; u32 led3_iclamp_mid_ma; u32 bst_pwm_ovrhd_uv; u8 isc_delay; u8 warmup_delay; u8 current_derate_en_cfg; Loading @@ -284,6 +315,8 @@ struct qpnp_flash_led { struct flash_node_data *fnode; struct flash_switch_data *snode; struct power_supply *bms_psy; struct power_supply *main_psy; struct power_supply *usb_psy; struct notifier_block nb; spinlock_t lock; int num_fnodes; Loading Loading @@ -739,9 +772,12 @@ static int get_property_from_fg(struct qpnp_flash_led *led, } #define VOLTAGE_HDRM_DEFAULT_MV 350 #define BHARGER_VOLTAGE_HDRM_DEFAULT_MV 400 #define BHARGER_HEADROOM_OFFSET_MV 50 static int qpnp_flash_led_get_voltage_headroom(struct qpnp_flash_led *led) { int i, voltage_hdrm_mv = 0, voltage_hdrm_max = 0; u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; for (i = 0; i < led->num_fnodes; i++) { if (led->fnode[i].led_on) { Loading @@ -765,13 +801,18 @@ static int qpnp_flash_led_get_voltage_headroom(struct qpnp_flash_led *led) voltage_hdrm_mv = 350; } if (pmic_subtype == PMI632_SUBTYPE) voltage_hdrm_mv += BHARGER_HEADROOM_OFFSET_MV; voltage_hdrm_max = max(voltage_hdrm_max, voltage_hdrm_mv); } } if (!voltage_hdrm_max) return VOLTAGE_HDRM_DEFAULT_MV; return (pmic_subtype == PMI632_SUBTYPE) ? BHARGER_VOLTAGE_HDRM_DEFAULT_MV : VOLTAGE_HDRM_DEFAULT_MV; return voltage_hdrm_max; } Loading Loading @@ -883,6 +924,179 @@ static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led, return 0; } static int is_main_psy_available(struct qpnp_flash_led *led) { if (!led->main_psy) { led->main_psy = power_supply_get_by_name("main"); if (!led->main_psy) { pr_err_ratelimited("Couldn't get main_psy\n"); return -ENODEV; } } return 0; } static int is_usb_psy_available(struct qpnp_flash_led *led) { if (!led->usb_psy) { led->usb_psy = power_supply_get_by_name("usb"); if (!led->usb_psy) { pr_err_ratelimited("Couldn't get usb_psy\n"); return -ENODEV; } } return 0; } #define CHGBST_EFFICIENCY 800LL #define CHGBST_FLASH_VDIP_MARGIN 10000 #define VIN_FLASH_UV 5000000 #define BHARGER_FLASH_LED_MAX_TOTAL_CURRENT_MA 1500 #define BHARGER_FLASH_LED_WITH_OTG_MAX_TOTAL_CURRENT_MA 1100 static int qpnp_flash_led_calc_bharger_max_current(struct qpnp_flash_led *led, int *max_current) { union power_supply_propval pval = {0, }; int ocv_uv, ibat_now, voltage_hdrm_mv, flash_led_max_total_curr_ma, rc; int rbatt_uohm = 0, usb_present, otg_enable; int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw; int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv, vph_flash_vdip; int64_t bst_pwm_ovrhd_uv; rc = is_usb_psy_available(led); if (rc < 0) return rc; rc = power_supply_get_property(led->usb_psy, POWER_SUPPLY_PROP_SCOPE, &pval); if (rc < 0) { pr_err("usb psy does not support usb present, rc=%d\n", rc); return rc; } otg_enable = pval.intval; /* RESISTANCE = esr_uohm + rslow_uohm */ rc = get_property_from_fg(led, POWER_SUPPLY_PROP_RESISTANCE, &rbatt_uohm); if (rc < 0) { pr_err("bms psy does not support resistance, rc=%d\n", rc); return rc; } /* If no battery is connected, return max possible flash current */ if (!rbatt_uohm) { *max_current = (otg_enable == POWER_SUPPLY_SCOPE_SYSTEM) ? BHARGER_FLASH_LED_WITH_OTG_MAX_TOTAL_CURRENT_MA : BHARGER_FLASH_LED_MAX_TOTAL_CURRENT_MA; return 0; } rc = get_property_from_fg(led, POWER_SUPPLY_PROP_VOLTAGE_OCV, &ocv_uv); if (rc < 0) { pr_err("bms psy does not support OCV, rc=%d\n", rc); return rc; } rc = get_property_from_fg(led, POWER_SUPPLY_PROP_CURRENT_NOW, &ibat_now); if (rc < 0) { pr_err("bms psy does not support current, rc=%d\n", rc); return rc; } bst_pwm_ovrhd_uv = led->pdata->bst_pwm_ovrhd_uv; rc = power_supply_get_property(led->usb_psy, POWER_SUPPLY_PROP_PRESENT, &pval); if (rc < 0) { pr_err("usb psy does not support usb present, rc=%d\n", rc); return rc; } usb_present = pval.intval; rbatt_uohm += led->pdata->rpara_uohm; voltage_hdrm_mv = qpnp_flash_led_get_voltage_headroom(led); vph_flash_vdip = VPH_DROOP_THRESH_VAL_TO_UV(led->pdata->vph_droop_threshold) + CHGBST_FLASH_VDIP_MARGIN; /* Check if LMH_MITIGATION needs to be triggered */ if (!led->trigger_lmh && (ocv_uv < led->pdata->lmh_ocv_threshold_uv || rbatt_uohm > led->pdata->lmh_rbatt_threshold_uohm)) { led->trigger_lmh = true; rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MITIGATION_SW(led->base), FLASH_LED_LMH_MITIGATION_EN_MASK, FLASH_LED_LMH_MITIGATION_ENABLE); if (rc < 0) { pr_err("trigger lmh mitigation failed, rc=%d\n", rc); return rc; } /* Wait for LMH mitigation to take effect */ udelay(100); return qpnp_flash_led_calc_bharger_max_current(led, max_current); } /* * Calculate the maximum current that can pulled out of the battery * before the battery voltage dips below a safe threshold. */ ibat_safe_ua = div_s64((ocv_uv - vph_flash_vdip) * UCONV, rbatt_uohm); if (ibat_safe_ua <= led->pdata->ibatt_ocp_threshold_ua) { /* * If the calculated current is below the OCP threshold, then * use it as the possible flash current. */ ibat_flash_ua = ibat_safe_ua - ibat_now; vph_flash_uv = vph_flash_vdip; } else { /* * If the calculated current is above the OCP threshold, then * use the ocp threshold instead. * * Any higher current will be tripping the battery OCP. */ ibat_flash_ua = led->pdata->ibatt_ocp_threshold_ua - ibat_now; vph_flash_uv = ocv_uv - div64_s64((int64_t)rbatt_uohm * led->pdata->ibatt_ocp_threshold_ua, UCONV); } /* when USB is present or OTG is enabled, VIN_FLASH is always at 5V */ if (usb_present || (otg_enable == POWER_SUPPLY_SCOPE_SYSTEM)) vin_flash_uv = VIN_FLASH_UV; else /* Calculate the input voltage of the flash module. */ vin_flash_uv = max((led->pdata->vled_max_uv + (voltage_hdrm_mv * MCONV)), vph_flash_uv + bst_pwm_ovrhd_uv); /* Calculate the available power for the flash module. */ avail_flash_power_fw = CHGBST_EFFICIENCY * vph_flash_uv * ibat_flash_ua; /* * Calculate the available amount of current the flash module can draw * before collapsing the battery. (available power/ flash input voltage) */ avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV); flash_led_max_total_curr_ma = otg_enable ? BHARGER_FLASH_LED_WITH_OTG_MAX_TOTAL_CURRENT_MA : BHARGER_FLASH_LED_MAX_TOTAL_CURRENT_MA; *max_current = min(flash_led_max_total_curr_ma, (int)(div64_s64(avail_flash_ua, MCONV))); pr_debug("avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d, trigger_lmh=%d max_current=%lld usb_present=%d otg_enable=%d\n", avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm, led->trigger_lmh, (*max_current * MCONV), usb_present, otg_enable); return 0; } static int qpnp_flash_led_calc_thermal_current_lim(struct qpnp_flash_led *led, int *thermal_current_lim) { Loading Loading @@ -974,9 +1188,16 @@ static int qpnp_flash_led_get_max_avail_current(struct qpnp_flash_led *led, int *max_avail_current) { int thermal_current_lim = 0, rc; u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; led->trigger_lmh = false; if (pmic_subtype == PMI632_SUBTYPE) rc = qpnp_flash_led_calc_bharger_max_current(led, max_avail_current); else rc = qpnp_flash_led_calc_max_current(led, max_avail_current); if (rc < 0) { pr_err("Couldn't calculate max_avail_current, rc=%d\n", rc); return rc; Loading Loading @@ -1018,6 +1239,7 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value) int prgm_current_ma = value; int min_ma = fnode->ires_ua / 1000; struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev); u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; if (value <= 0) prgm_current_ma = 0; Loading Loading @@ -1052,7 +1274,8 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value) if (prgm_current_ma) fnode->led_on = true; if (led->pdata->chgr_mitigation_sel == FLASH_SW_CHARGER_MITIGATION) { if (pmic_subtype != PMI632_SUBTYPE && led->pdata->chgr_mitigation_sel == FLASH_SW_CHARGER_MITIGATION) { qpnp_flash_led_aggregate_max_current(fnode); led->trigger_chgr = false; if (led->total_current_ma >= 1000) Loading Loading @@ -1082,7 +1305,7 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode) } } if (!led->trigger_chgr) { if (led->pdata->chgr_mitigation_sel && !led->trigger_chgr) { rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MITIGATION_SW(led->base), FLASH_LED_CHGR_MITIGATION_EN_MASK, Loading Loading @@ -1213,9 +1436,51 @@ static int qpnp_flash_led_symmetry_config(struct flash_switch_data *snode) return 0; } #define FLASH_LED_MODULE_EN_TIME_MS 300 static int qpnp_flash_poll_vreg_ok(struct qpnp_flash_led *led) { int rc, i; union power_supply_propval pval = {0, }; rc = is_main_psy_available(led); if (rc < 0) return rc; for (i = 0; i < 60; i++) { /* wait for the flash vreg_ok to be set */ mdelay(5); rc = power_supply_get_property(led->main_psy, POWER_SUPPLY_PROP_FLASH_TRIGGER, &pval); if (rc < 0) { pr_err("main psy doesn't support reading prop %d rc = %d\n", POWER_SUPPLY_PROP_FLASH_TRIGGER, rc); return rc; } if (pval.intval > 0) { pr_debug("Flash trigger set\n"); break; } if (pval.intval < 0) { pr_err("Error during flash trigger %d\n", pval.intval); return pval.intval; } } if (!pval.intval) { pr_err("Failed to enable the module\n"); return -ETIMEDOUT; } return 0; } static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) { struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev); u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; int rc, i, addr_offset; u8 val, mask; Loading Loading @@ -1310,6 +1575,19 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) FLASH_LED_MOD_CTRL_MASK, FLASH_LED_MOD_ENABLE); if (rc < 0) return rc; if (pmic_subtype == PMI632_SUBTYPE) { rc = qpnp_flash_poll_vreg_ok(led); if (rc < 0) { /* Disable the module */ qpnp_flash_led_masked_write(led, FLASH_LED_REG_MOD_CTRL(led->base), FLASH_LED_MOD_CTRL_MASK, FLASH_LED_DISABLE); return rc; } } } led->enable++; Loading @@ -1326,7 +1604,7 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) udelay(500); } if (led->trigger_chgr) { if (led->pdata->chgr_mitigation_sel && led->trigger_chgr) { rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MITIGATION_SW(led->base), FLASH_LED_CHGR_MITIGATION_EN_MASK, Loading @@ -1347,27 +1625,28 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) return 0; } int qpnp_flash_led_prepare(struct led_trigger *trig, int options, int *max_current) static int qpnp_flash_led_regulator_control(struct led_classdev *led_cdev, int options, int *max_current) { struct led_classdev *led_cdev; int rc; u8 pmic_subtype; struct flash_switch_data *snode; struct qpnp_flash_led *led; int rc; if (!trig) { pr_err("Invalid led_trigger provided\n"); return -EINVAL; } led_cdev = trigger_to_lcdev(trig); if (!led_cdev) { pr_err("Invalid led_cdev in trigger %s\n", trig->name); return -EINVAL; } union power_supply_propval ret = {0, }; snode = container_of(led_cdev, struct flash_switch_data, cdev); led = dev_get_drvdata(&snode->pdev->dev); pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; if (pmic_subtype == PMI632_SUBTYPE) { rc = is_main_psy_available(led); if (rc < 0) return rc; rc = is_usb_psy_available(led); if (rc < 0) return rc; } if (!(options & FLASH_LED_PREPARE_OPTIONS_MASK)) { pr_err("Invalid options %d\n", options); Loading @@ -1375,20 +1654,46 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options, } if (options & ENABLE_REGULATOR) { if (pmic_subtype == PMI632_SUBTYPE) { ret.intval = 1; rc = power_supply_set_property(led->main_psy, POWER_SUPPLY_PROP_FLASH_ACTIVE, &ret); if (rc < 0) { pr_err("Failed to set FLASH_ACTIVE on charger rc=%d\n", rc); return rc; } pr_debug("FLASH_ACTIVE = 1\n"); } else { rc = qpnp_flash_led_regulator_enable(led, snode, true); if (rc < 0) { pr_err("enable regulator failed, rc=%d\n", rc); return rc; } } } if (options & DISABLE_REGULATOR) { if (pmic_subtype == PMI632_SUBTYPE) { ret.intval = 0; rc = power_supply_set_property(led->main_psy, POWER_SUPPLY_PROP_FLASH_ACTIVE, &ret); if (rc < 0) { pr_err("Failed to set FLASH_ACTIVE on charger rc=%d\n", rc); return rc; } pr_debug("FLASH_ACTIVE = 0\n"); } else { rc = qpnp_flash_led_regulator_enable(led, snode, false); if (rc < 0) { pr_err("disable regulator failed, rc=%d\n", rc); return rc; } } } if (options & QUERY_MAX_AVAIL_CURRENT) { rc = qpnp_flash_led_get_max_avail_current(led, max_current); Loading @@ -1401,6 +1706,28 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options, return 0; } int qpnp_flash_led_prepare(struct led_trigger *trig, int options, int *max_current) { struct led_classdev *led_cdev; int rc; if (!trig) { pr_err("Invalid led_trigger provided\n"); return -EINVAL; } led_cdev = trigger_to_lcdev(trig); if (!led_cdev) { pr_err("Invalid led_cdev in trigger %s\n", trig->name); return -EINVAL; } rc = qpnp_flash_led_regulator_control(led_cdev, options, max_current); return rc; } static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) { Loading Loading @@ -1440,6 +1767,29 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev, spin_unlock(&led->lock); } static ssize_t qpnp_flash_led_prepare_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc, options, max_current; u32 val; struct led_classdev *led_cdev = dev_get_drvdata(dev); rc = kstrtouint(buf, 0, &val); if (rc < 0) return rc; if (val != 0 && val != 1) return count; options = val ? ENABLE_REGULATOR : DISABLE_REGULATOR; rc = qpnp_flash_led_regulator_control(led_cdev, options, &max_current); if (rc < 0) return rc; return count; } /* sysfs show function for flash_max_current */ static ssize_t qpnp_flash_led_max_current_show(struct device *dev, struct device_attribute *attr, char *buf) Loading @@ -1462,6 +1812,7 @@ static ssize_t qpnp_flash_led_max_current_show(struct device *dev, /* sysfs attributes exported by flash_led */ static struct device_attribute qpnp_flash_led_attrs[] = { __ATTR(max_current, 0664, qpnp_flash_led_max_current_show, NULL), __ATTR(enable, 0664, NULL, qpnp_flash_led_prepare_store), }; static int flash_led_psy_notifier_call(struct notifier_block *nb, Loading Loading @@ -1590,6 +1941,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, int rc, min_ma; u32 val; bool hw_strobe = 0, edge_trigger = 0, active_high = 0; u8 pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; fnode->pdev = led->pdev; fnode->cdev.brightness_set = qpnp_flash_led_brightness_set; Loading Loading @@ -1619,6 +1971,11 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, rc = of_property_read_u32(node, "qcom,id", &val); if (!rc) { fnode->id = (u8)val; if (pmic_subtype == PMI632_SUBTYPE && fnode->id > LED2) { pr_err("Flash node id = %d not supported\n", fnode->id); return -EINVAL; } } else { pr_err("Unable to read flash LED ID\n"); return rc; Loading Loading @@ -1925,6 +2282,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, struct device_node *revid_node; int rc; u32 val; u8 pmic_subtype; bool short_circuit_det, open_circuit_det, vph_droop_det; revid_node = of_parse_phandle(node, "qcom,pmic-revid", 0); Loading @@ -1945,6 +2303,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return -EPROBE_DEFER; } pmic_subtype = led->pdata->pmic_rev_id->pmic_subtype; pr_debug("PMIC subtype %d Digital major %d\n", led->pdata->pmic_rev_id->pmic_subtype, led->pdata->pmic_rev_id->rev4); Loading Loading @@ -2059,7 +2418,7 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, led->pdata->thermal_hysteresis = -EINVAL; rc = of_property_read_u32(node, "qcom,thermal-hysteresis", &val); if (!rc) { if (led->pdata->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) if (pmic_subtype == PM660L_SUBTYPE) val = THERMAL_HYST_TEMP_TO_VAL(val, 20); else val = THERMAL_HYST_TEMP_TO_VAL(val, 15); Loading Loading @@ -2123,7 +2482,13 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return -EINVAL; } led->pdata->vph_droop_threshold = FLASH_LED_VPH_DROOP_THRESH_DEFAULT; if (pmic_subtype == PMI632_SUBTYPE) led->pdata->vph_droop_threshold = BHARGER_FLASH_LED_VPH_DROOP_THRESH_DEFAULT; else led->pdata->vph_droop_threshold = FLASH_LED_VPH_DROOP_THRESH_DEFAULT; rc = of_property_read_u32(node, "qcom,vph-droop-threshold-mv", &val); if (!rc) { led->pdata->vph_droop_threshold = Loading Loading @@ -2268,7 +2633,12 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return -EINVAL; } if (pmic_subtype == PMI632_SUBTYPE) led->pdata->chgr_mitigation_sel = FLASH_DISABLE_CHARGER_MITIGATION; else led->pdata->chgr_mitigation_sel = FLASH_SW_CHARGER_MITIGATION; rc = of_property_read_u32(node, "qcom,chgr-mitigation-sel", &val); if (!rc) { led->pdata->chgr_mitigation_sel = val; Loading Loading @@ -2296,6 +2666,14 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return -EINVAL; } led->pdata->bst_pwm_ovrhd_uv = FLASH_BST_PWM_OVRHD_MIN_UV; rc = of_property_read_u32(node, "qcom,bst-pwm-ovrhd-uv", &val); if (!rc) { if (val >= FLASH_BST_PWM_OVRHD_MIN_UV && val <= FLASH_BST_PWM_OVRHD_MAX_UV) led->pdata->bst_pwm_ovrhd_uv = val; } led->pdata->all_ramp_up_done_irq = of_irq_get_byname(node, "all-ramp-up-done-irq"); if (led->pdata->all_ramp_up_done_irq < 0) Loading