leds: qpnp-flash-v2: Add support for LMH mitigation

Based on the LMH trigger thresholds read from device tree
and the battery properties read from fuel gauge, trigger LMH
pre-emptive current mitigation if required.

CRs-Fixed: 1043718
Change-Id: I54e7670a880f862b6619d22f843c8fa13fd0b303
Signed-off-by: Devesh Jhunjhunwala <deveshj@codeaurora.org>
This commit is contained in:
Devesh Jhunjhunwala 2016-07-14 10:04:50 -07:00
parent 5ee78edab2
commit f816e044c2
2 changed files with 142 additions and 6 deletions

View file

@ -38,6 +38,18 @@ Optional properties:
Default value is 4500000 uA.
- qcom,rparasitic-uohm : Integer property for flash current predictive mitigation indicating
parasitic component of battery resistance. Default value is 0 uOhm.
- qcom,lmh-ocv-threshold-uv : Required property for flash current preemptive mitigation.
Default value is 3700000 uV.
- qcom,lmh-rbatt-threshold-uohm : Required property for flash current preemptive mitigation.
Default value is 400000 uOhm.
- qcom,lmh-mitigation-sel : Optional property to configure flash current preemptive mitigation.
Accepted values are:
0: MITIGATION_DISABLED
1: MITIGATION_BY_ILED_THRESHOLD
2: MITIGATION_BY_SW
Default value is 2.
- qcom,lmh-level : Optional property to configure flash current preemptive mitigation.
Accepted values are 0, 1, and 3. Default value is 0.
- qcom,hw-strobe-option : Integer type to specify hardware strobe option. Based on the specified
value, additional GPIO configuration may be required to provide strobing
support. Supported values are:

View file

@ -14,6 +14,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@ -44,6 +45,9 @@
#define FLASH_LED_REG_ISC_DELAY(base) (base + 0x52)
#define FLASH_LED_REG_VPH_DROOP_THRESHOLD(base) (base + 0x61)
#define FLASH_LED_REG_VPH_DROOP_DEBOUNCE(base) (base + 0x62)
#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_CURRENT_DERATE_EN(base) (base + 0x76)
#define FLASH_LED_HDRM_MODE_PRGM_MASK GENMASK(7, 0)
@ -55,11 +59,14 @@
#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_LMH_MITIGATION_SEL_MASK GENMASK(1, 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_MOD_CTRL_MASK BIT(7)
#define FLASH_LED_HW_SW_STROBE_SEL_MASK BIT(2)
#define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4)
#define FLASH_LED_LMH_MITIGATION_EN_MASK BIT(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)
@ -81,6 +88,13 @@
#define FLASH_LED_SAFETY_TMR_VAL_OFFSET 1
#define FLASH_LED_SAFETY_TMR_VAL_DIVISOR 10
#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_LMH_MITIGATION_SEL_DEFAULT 2
#define FLASH_LED_LMH_MITIGATION_SEL_MAX 2
#define FLASH_LED_LMH_OCV_THRESH_DEFAULT_UV 3700000
#define FLASH_LED_LMH_RBATT_THRESH_DEFAULT_UOHM 400000
#define FLASH_LED_IRES_BASE 3
#define FLASH_LED_IRES_DIVISOR 2500
#define FLASH_LED_IRES_MIN_UA 5000
@ -167,12 +181,16 @@ struct flash_led_platform_data {
int ibatt_ocp_threshold_ua;
int vled_max_uv;
int rpara_uohm;
int lmh_rbatt_threshold_uohm;
int lmh_ocv_threshold_uv;
u8 isc_delay;
u8 warmup_delay;
u8 current_derate_en_cfg;
u8 vph_droop_threshold;
u8 vph_droop_hysteresis;
u8 vph_droop_debounce;
u8 lmh_mitigation_sel;
u8 lmh_level;
u8 hw_strobe_option;
bool hdrm_auto_mode_en;
};
@ -193,6 +211,7 @@ struct qpnp_flash_led {
int num_snodes;
int enable;
u16 base;
bool trigger_lmh;
};
static int
@ -301,6 +320,20 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
if (rc < 0)
return rc;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_MITIGATION_SEL(led->base),
FLASH_LED_LMH_MITIGATION_SEL_MASK,
led->pdata->lmh_mitigation_sel);
if (rc < 0)
return rc;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LMH_LEVEL(led->base),
FLASH_LED_LMH_LEVEL_MASK,
led->pdata->lmh_level);
if (rc < 0)
return rc;
return 0;
}
@ -440,6 +473,26 @@ static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led)
VPH_DROOP_THRESH_VAL_TO_UV(led->pdata->vph_droop_threshold)
+ 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) {
dev_err(&led->pdev->dev, "trigger lmh mitigation failed, rc=%d\n",
rc);
return rc;
}
/* Wait for LMH mitigation to take effect */
udelay(100);
return qpnp_flash_led_calc_max_current(led);
}
/*
* Calculate the maximum current that can pulled out of the battery
* before the battery voltage dips below a safe threshold.
@ -475,16 +528,16 @@ static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led)
* before collapsing the battery. (available power/ flash input voltage)
*/
avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV);
dev_dbg(&led->pdev->dev,
"avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d\n",
avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm);
dev_dbg(&led->pdev->dev, "avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d, trigger_lmh=%d\n",
avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm,
led->trigger_lmh);
return min(FLASH_LED_MAX_TOTAL_CURRENT_MA,
(int)(avail_flash_ua / MCONV));
}
static int qpnp_flash_led_get_max_avail_current(struct flash_switch_data *snode,
struct qpnp_flash_led *led)
static int qpnp_flash_led_get_max_avail_current(struct qpnp_flash_led *led)
{
led->trigger_lmh = false;
return qpnp_flash_led_calc_max_current(led);
}
@ -515,6 +568,18 @@ static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode)
if (rc < 0)
return rc;
if (led->trigger_lmh) {
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_MITIGATION_SW(led->base),
FLASH_LED_LMH_MITIGATION_EN_MASK,
FLASH_LED_LMH_MITIGATION_DISABLE);
if (rc < 0) {
dev_err(&led->pdev->dev, "disable lmh mitigation failed, rc=%d\n",
rc);
return rc;
}
}
led->enable--;
if (led->enable == 0) {
rc = qpnp_flash_led_masked_write(led,
@ -661,6 +726,18 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
}
led->enable++;
if (led->trigger_lmh) {
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) {
dev_err(&led->pdev->dev, "trigger lmh mitigation failed, rc=%d\n",
rc);
return rc;
}
}
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_EN_LED_CTRL(led->base),
snode->led_mask, val);
@ -701,7 +778,7 @@ int qpnp_flash_led_prepare(struct led_trigger *trig, int options)
}
if (options & QUERY_MAX_CURRENT) {
val = qpnp_flash_led_get_max_avail_current(snode, led);
val = qpnp_flash_led_get_max_avail_current(led);
if (val < 0) {
dev_err(&led->pdev->dev,
"query max current failed, rc=%d\n", val);
@ -1353,6 +1430,53 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return rc;
}
led->pdata->lmh_ocv_threshold_uv =
FLASH_LED_LMH_OCV_THRESH_DEFAULT_UV;
rc = of_property_read_u32(node, "qcom,lmh-ocv-threshold-uv", &val);
if (!rc) {
led->pdata->lmh_ocv_threshold_uv = val;
} else if (rc != -EINVAL) {
dev_err(&led->pdev->dev, "Unable to parse lmh ocv threshold, rc=%d\n",
rc);
return rc;
}
led->pdata->lmh_rbatt_threshold_uohm =
FLASH_LED_LMH_RBATT_THRESH_DEFAULT_UOHM;
rc = of_property_read_u32(node, "qcom,lmh-rbatt-threshold-uohm", &val);
if (!rc) {
led->pdata->lmh_rbatt_threshold_uohm = val;
} else if (rc != -EINVAL) {
dev_err(&led->pdev->dev, "Unable to parse lmh rbatt threshold, rc=%d\n",
rc);
return rc;
}
led->pdata->lmh_level = FLASH_LED_LMH_LEVEL_DEFAULT;
rc = of_property_read_u32(node, "qcom,lmh-level", &val);
if (!rc) {
led->pdata->lmh_level = val;
} else if (rc != -EINVAL) {
dev_err(&led->pdev->dev, "Unable to parse lmh_level, rc=%d\n",
rc);
return rc;
}
led->pdata->lmh_mitigation_sel = FLASH_LED_LMH_MITIGATION_SEL_DEFAULT;
rc = of_property_read_u32(node, "qcom,lmh-mitigation-sel", &val);
if (!rc) {
led->pdata->lmh_mitigation_sel = val;
} else if (rc != -EINVAL) {
dev_err(&led->pdev->dev, "Unable to parse lmh_mitigation_sel, rc=%d\n",
rc);
return rc;
}
if (led->pdata->lmh_mitigation_sel > FLASH_LED_LMH_MITIGATION_SEL_MAX) {
dev_err(&led->pdev->dev, "Invalid lmh_mitigation_sel specified\n");
return -EINVAL;
}
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)