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

Commit 7e801ca9 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

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

parents 36efe3d7 aab10c39
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.
+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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
@@ -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;
@@ -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;
@@ -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) {
@@ -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;
}
@@ -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)
{
@@ -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;
@@ -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;
@@ -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)
@@ -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,
@@ -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;

@@ -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++;

@@ -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,
@@ -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);
@@ -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);
@@ -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)
{
@@ -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)
@@ -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,
@@ -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;
@@ -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;
@@ -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);
@@ -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);
@@ -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);
@@ -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 =
@@ -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;
@@ -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)