Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1c1e21ca authored by Kiran Gunda's avatar Kiran Gunda
Browse files

leds: qpnp-flash-v2: Add support for bharger flash



PMI632 flash is driven by the charger in reverse boost
(bharger) mode instead of the BoB regulator. Add the
necessary changes to support the same.

Change-Id: I9166f470f2fdffb12f1c90b1758f606ead2f131a
Signed-off-by: default avatarKiran Gunda <kgunda@codeaurora.org>
parent 901d889a
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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.
+497 −121
Original line number Diff line number Diff line
@@ -34,82 +34,114 @@
#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	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
@@ -123,11 +155,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
@@ -141,7 +171,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
@@ -150,8 +179,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
@@ -160,6 +187,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);
@@ -255,6 +284,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;
@@ -280,6 +310,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;
@@ -696,9 +728,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) {
@@ -722,13 +757,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;
}
@@ -840,6 +880,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)
{
@@ -931,9 +1144,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;
@@ -975,6 +1195,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;
@@ -1003,7 +1224,8 @@ static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
					fnode->ires_ua);
	fnode->led_on = prgm_current_ma != 0;

	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)
@@ -1033,7 +1255,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,
@@ -1158,9 +1380,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;

@@ -1255,6 +1519,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++;

@@ -1271,7 +1548,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,
@@ -1292,27 +1569,28 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
	return 0;
}

static int qpnp_flash_led_prepare_v2(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);
@@ -1320,20 +1598,46 @@ static int qpnp_flash_led_prepare_v2(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_CURRENT) {
		rc = qpnp_flash_led_get_max_avail_current(led, max_current);
@@ -1346,6 +1650,28 @@ static int qpnp_flash_led_prepare_v2(struct led_trigger *trig, int options,
	return 0;
}

static int qpnp_flash_led_prepare_v2(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)
{
@@ -1385,6 +1711,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)
@@ -1407,6 +1756,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,
@@ -1535,6 +1885,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;
@@ -1564,6 +1915,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 > 1) {
			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;
@@ -1870,6 +2226,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);
@@ -1890,10 +2247,10 @@ 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);

	led->pdata->hdrm_auto_mode_en = of_property_read_bool(node,
							"qcom,hdrm-auto-mode");

@@ -2001,7 +2358,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);
@@ -2065,7 +2422,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 =
@@ -2210,7 +2573,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;
@@ -2238,6 +2606,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)