summaryrefslogtreecommitdiff
path: root/drivers/leds
diff options
context:
space:
mode:
authorDevesh Jhunjhunwala <deveshj@codeaurora.org>2016-07-20 12:06:57 -0700
committerDevesh Jhunjhunwala <deveshj@codeaurora.org>2016-08-16 07:54:02 -0700
commit49d96e8bc7ce6a2ee09ad34071f77d2524468d0a (patch)
tree4daf1a5fdf9b5a2afe8c0588c708be1c7eedd613 /drivers/leds
parentf816e044c2fa3c3064d506a44570fc258c69346a (diff)
leds: qpnp-flash-v2: Add support for thermal current mitigation
Based on the thermal thresholds triggered, use the LUT to limit the maximum available flash current. CRs-Fixed: 1043718 Change-Id: I456d6491987183e07eba921edc826ff801fdeee3 Signed-off-by: Devesh Jhunjhunwala <deveshj@codeaurora.org>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c144
1 files changed, 143 insertions, 1 deletions
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index b6c23b809e25..203859fd1bff 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -43,6 +43,9 @@
#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_REG_THERMAL_THRSH1(base) (base + 0x56)
+#define FLASH_LED_REG_THERMAL_THRSH2(base) (base + 0x57)
+#define FLASH_LED_REG_THERMAL_THRSH3(base) (base + 0x58)
#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)
@@ -63,6 +66,8 @@
#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_THRSH_MASK GENMASK(2, 0)
+#define FLASH_LED_THERMAL_OTST_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)
@@ -82,6 +87,8 @@
#define FLASH_LED_VPH_DROOP_DEBOUNCE_MAX 3
#define FLASH_LED_VPH_DROOP_HYST_MAX 3
#define FLASH_LED_VPH_DROOP_THRESH_MAX 7
+#define FLASH_LED_THERMAL_THRSH_MIN 3
+#define FLASH_LED_THERMAL_OTST_LEVELS 3
#define FLASH_LED_VLED_MAX_DEFAULT_UV 3500000
#define FLASH_LED_IBATT_OCP_THRESH_DEFAULT_UA 4500000
#define FLASH_LED_RPARA_DEFAULT_UOHM 0
@@ -175,6 +182,7 @@ struct flash_switch_data {
* Flash LED configuration read from device tree
*/
struct flash_led_platform_data {
+ int *thermal_derate_current;
int all_ramp_up_done_irq;
int all_ramp_down_done_irq;
int led_fault_irq;
@@ -193,6 +201,7 @@ struct flash_led_platform_data {
u8 lmh_level;
u8 hw_strobe_option;
bool hdrm_auto_mode_en;
+ bool thermal_derate_en;
};
/*
@@ -233,6 +242,20 @@ qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data)
}
static int
+qpnp_flash_led_masked_read(struct qpnp_flash_led *led, u16 addr, u8 mask,
+ u8 *val)
+{
+ int rc;
+
+ rc = qpnp_flash_led_read(led, addr, val);
+ if (rc < 0)
+ return rc;
+
+ *val &= mask;
+ return rc;
+}
+
+static int
qpnp_flash_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask,
u8 val)
{
@@ -535,10 +558,107 @@ static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led)
(int)(avail_flash_ua / MCONV));
}
+static int qpnp_flash_led_calc_thermal_current_lim(struct qpnp_flash_led *led)
+{
+ int thermal_current_lim = 0;
+ int rc;
+ u8 thermal_thrsh1, thermal_thrsh2, thermal_thrsh3, otst_status;
+
+ /* Store THERMAL_THRSHx register values */
+ rc = qpnp_flash_led_masked_read(led,
+ FLASH_LED_REG_THERMAL_THRSH1(led->base),
+ FLASH_LED_THERMAL_THRSH_MASK,
+ &thermal_thrsh1);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_read(led,
+ FLASH_LED_REG_THERMAL_THRSH2(led->base),
+ FLASH_LED_THERMAL_THRSH_MASK,
+ &thermal_thrsh2);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_read(led,
+ FLASH_LED_REG_THERMAL_THRSH3(led->base),
+ FLASH_LED_THERMAL_THRSH_MASK,
+ &thermal_thrsh3);
+ if (rc < 0)
+ return rc;
+
+ /* Lower THERMAL_THRSHx thresholds to minimum */
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_THERMAL_THRSH1(led->base),
+ FLASH_LED_THERMAL_THRSH_MASK,
+ FLASH_LED_THERMAL_THRSH_MIN);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_THERMAL_THRSH2(led->base),
+ FLASH_LED_THERMAL_THRSH_MASK,
+ FLASH_LED_THERMAL_THRSH_MIN);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_THERMAL_THRSH3(led->base),
+ FLASH_LED_THERMAL_THRSH_MASK,
+ FLASH_LED_THERMAL_THRSH_MIN);
+ if (rc < 0)
+ return rc;
+
+ /* Check THERMAL_OTST status */
+ rc = qpnp_flash_led_read(led,
+ FLASH_LED_REG_LED_STATUS2(led->base),
+ &otst_status);
+ if (rc < 0)
+ return rc;
+
+ /* Look up current limit based on THERMAL_OTST status */
+ if (otst_status)
+ thermal_current_lim =
+ led->pdata->thermal_derate_current[otst_status >> 1];
+
+ /* Restore THERMAL_THRESHx registers to original values */
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_THERMAL_THRSH1(led->base),
+ FLASH_LED_THERMAL_THRSH_MASK,
+ thermal_thrsh1);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_THERMAL_THRSH2(led->base),
+ FLASH_LED_THERMAL_THRSH_MASK,
+ thermal_thrsh2);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_THERMAL_THRSH3(led->base),
+ FLASH_LED_THERMAL_THRSH_MASK,
+ thermal_thrsh3);
+ if (rc < 0)
+ return rc;
+
+ return thermal_current_lim;
+}
+
static int qpnp_flash_led_get_max_avail_current(struct qpnp_flash_led *led)
{
+ int max_avail_current, thermal_current_lim = 0;
+
led->trigger_lmh = false;
- return qpnp_flash_led_calc_max_current(led);
+ max_avail_current = qpnp_flash_led_calc_max_current(led);
+ if (led->pdata->thermal_derate_en)
+ thermal_current_lim =
+ qpnp_flash_led_calc_thermal_current_lim(led);
+
+ if (thermal_current_lim)
+ max_avail_current = min(max_avail_current, thermal_current_lim);
+
+ return max_avail_current;
}
static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
@@ -1338,6 +1458,28 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
led->pdata->current_derate_en_cfg = (vph_droop_det << 2) |
(open_circuit_det << 1) | short_circuit_det;
+ led->pdata->thermal_derate_en =
+ of_property_read_bool(node, "qcom,thermal-derate-en");
+
+ if (led->pdata->thermal_derate_en) {
+ led->pdata->thermal_derate_current =
+ devm_kcalloc(&led->pdev->dev,
+ FLASH_LED_THERMAL_OTST_LEVELS,
+ sizeof(int), GFP_KERNEL);
+ if (!led->pdata->thermal_derate_current)
+ return -ENOMEM;
+
+ rc = of_property_read_u32_array(node,
+ "qcom,thermal-derate-current",
+ led->pdata->thermal_derate_current,
+ FLASH_LED_THERMAL_OTST_LEVELS);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev, "Unable to read thermal current limits, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
led->pdata->vph_droop_debounce = FLASH_LED_VPH_DROOP_DEBOUNCE_DEFAULT;
rc = of_property_read_u32(node, "qcom,vph-droop-debounce-us", &val);
if (!rc) {