diff --git a/drivers/leds/leds-qti-flash.c b/drivers/leds/leds-qti-flash.c index 646a2aac058f..95b941d5d199 100644 --- a/drivers/leds/leds-qti-flash.c +++ b/drivers/leds/leds-qti-flash.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #define pr_fmt(fmt) "qti-flash: %s: " fmt, __func__ @@ -22,53 +23,52 @@ #include "leds.h" -#define FLASH_LED_REVISION1 0x00 +#define FLASH_LED_REVISION1 0x00 #define FLASH_LED_PERIPH_SUBTYPE 0x05 -#define FLASH_LED_STATUS1 0x06 +#define FLASH_LED_STATUS1 0x06 -#define FLASH_LED_STATUS2 0x07 -#define FLASH_LED_OTST1_STATUS BIT(5) -#define FLASH_LED_OTST2_STATUS BIT(4) -#define FLASH_LED_VPH_PWR_LOW BIT(0) +#define FLASH_LED_STATUS2 0x07 +#define FLASH_LED_OTST1_STATUS BIT(5) +#define FLASH_LED_OTST2_STATUS BIT(4) +#define FLASH_LED_VPH_PWR_LOW BIT(0) -#define FLASH_INT_RT_STS 0x10 -#define FLASH_LED_FAULT_RT_STS BIT(0) -#define FLASH_LED_ALL_RAMP_DN_DONE_RT_STS BIT(3) -#define FLASH_LED_ALL_RAMP_UP_DONE_RT_STS BIT(4) +#define FLASH_INT_RT_STS 0x10 +#define FLASH_LED_FAULT_RT_STS BIT(0) +#define FLASH_LED_ALL_RAMP_DN_DONE_RT_STS BIT(3) +#define FLASH_LED_ALL_RAMP_UP_DONE_RT_STS BIT(4) #define FLASH_LED_SAFETY_TIMER(id) (0x3E + id) #define FLASH_LED_SAFETY_TIMER_EN_MASK BIT(7) #define FLASH_LED_SAFETY_TIMER_EN BIT(7) #define SAFETY_TIMER_MAX_TIMEOUT_MS 1280 #define SAFETY_TIMER_MIN_TIMEOUT_MS 10 -#define SAFETY_TIMER_STEP_SIZE 10 -#define SAFETY_TIMER_DEFAULT_TIMEOUT_MS 200 +#define SAFETY_TIMER_STEP_SIZE 10 +#define SAFETY_TIMER_DEFAULT_TIMEOUT_MS 200 -#define FLASH_LED_ITARGET(id) (0x42 + id) -#define FLASH_LED_ITARGET_MASK GENMASK(6, 0) +#define FLASH_LED_ITARGET(id) (0x42 + id) +#define FLASH_LED_ITARGET_MASK GENMASK(6, 0) -#define FLASH_ENABLE_CONTROL 0x46 -#define FLASH_MODULE_ENABLE BIT(7) -#define FLASH_MODULE_DISABLE 0x0 +#define FLASH_ENABLE_CONTROL 0x46 +#define FLASH_MODULE_ENABLE BIT(7) +#define FLASH_MODULE_DISABLE 0x0 -#define FLASH_LED_IRESOLUTION 0x49 +#define FLASH_LED_IRESOLUTION 0x49 #define FLASH_LED_IRESOLUTION_MASK(id) BIT(id) -#define FLASH_LED_STROBE_CTRL(id) (0x4A + id) +#define FLASH_LED_STROBE_CTRL(id) (0x4A + id) #define FLASH_LED_STROBE_CFG_MASK GENMASK(6, 4) #define FLASH_LED_STROBE_CFG_SHIFT 4 #define FLASH_LED_HW_SW_STROBE_SEL BIT(2) #define FLASH_LED_STROBE_SEL_SHIFT 2 -#define FLASH_EN_LED_CTRL 0x4E +#define FLASH_EN_LED_CTRL 0x4E #define FLASH_LED_ENABLE(id) BIT(id) -#define FLASH_LED_DISABLE 0 +#define FLASH_LED_DISABLE 0 -#define FLASH_LED_MITIGATION_SEL 0x63 -#define FLASH_LED_PREEMPTIVE_LMH_MASK GENMASK(1, 0) -#define FLASH_LED_LMH_MITIGATION_SW 0x2 +#define FLASH_LED_MITIGATION_SW 0x65 +#define FLASH_LED_LMH_MITIGATION_SW_EN BIT(0) #define FLASH_LED_THERMAL_OTST2_CFG1 0x78 #define FLASH_LED_THERMAL_OTST1_CFG1 0x7A @@ -77,21 +77,20 @@ #define FLASH_LED_V2_OTST1_THRSH_MIN 0x10 #define FLASH_LED_OTST2_THRSH_MIN 0x30 -#define MAX_IRES_LEVELS 2 -#define IRES_12P5_MAX_CURR_MA 1500 -#define IRES_5P0_MAX_CURR_MA 640 -#define TORCH_MAX_CURR_MA 500 -#define IRES_12P5_UA 12500 -#define IRES_5P0_UA 5000 -#define IRES_DEFAULT_UA IRES_12P5_UA -#define MAX_FLASH_CURRENT_MA 2000 +#define MAX_IRES_LEVELS 2 +#define IRES_12P5_MAX_CURR_MA 1500 +#define IRES_5P0_MAX_CURR_MA 640 +#define TORCH_MAX_CURR_MA 500 +#define IRES_12P5_UA 12500 +#define IRES_5P0_UA 5000 +#define IRES_DEFAULT_UA IRES_12P5_UA +#define MAX_FLASH_CURRENT_MA 2000 #define IBATT_OCP_THRESH_DEFAULT_UA 4500000 -#define FLASH_THERMAL_LEVELS 2 -#define OTST1_IDX 0 -#define OTST2_IDX 1 -#define OTST1_CURR_LIM_MA 200 -#define OTST2_CURR_LIM_MA 500 -#define VLED_MAX_DEFAULT_UV 3500000 +#define OTST1_CURR_LIM_MA 200 +#define OTST2_CURR_LIM_MA 500 +#define VLED_MAX_DEFAULT_UV 3500000 + +#define LED_MASK_ALL(led) GENMASK(led->max_channels - 1, 0) enum flash_led_type { FLASH_LED_TYPE_UNKNOWN, @@ -112,11 +111,18 @@ enum strobe_type { HW_STROBE, }; +enum thermal_levels { + OTST1_IDX, + OTST2_IDX, + OTST_MAX, +}; + struct flash_node_data { struct qti_flash_led *led; - struct led_classdev_flash fdev; + struct led_classdev_flash fdev; u32 ires_ua; u32 default_ires_ua; + u32 user_current_ma; u32 current_ma; u32 max_current; u8 duration; @@ -133,8 +139,8 @@ struct flash_node_data { struct flash_switch_data { struct qti_flash_led *led; struct led_classdev cdev; - struct hrtimer on_timer; - struct hrtimer off_timer; + struct hrtimer on_timer; + struct hrtimer off_timer; u64 on_time_ms; u64 off_time_ms; u32 led_mask; @@ -144,50 +150,57 @@ struct flash_switch_data { /** * struct qti_flash_led: Main Flash LED data structure - * @pdev : Pointer for platform device - * @regmap : Pointer for regmap structure - * @fnode : Pointer for array of child LED devices - * @snode : Pointer for array of child switch devices - * @batt_psy : Pointer for battery power supply - * @lock : Spinlock to be used for critical section - * @num_fnodes : Number of flash/torch nodes defined in device tree - * @num_snodes : Number of switch nodes defined in device tree - * @hw_strobe_gpio : Pointer for array of GPIOs for HW strobing - * @all_ramp_up_done_irq : IRQ number for all ramp up interrupt - * @all_ramp_down_done_irq : IRQ number for all ramp down interrupt - * @led_fault_irq : IRQ number for LED fault interrupt - * @max_current : Maximum current available for flash - * @thermal_derate_current : Thermal derating current limits - * @base : Base address of the flash LED module - * @revision : Revision of the flash LED module - * @subtype : Peripheral subtype of the flash LED module - * @max_channels : Maximum number of channels supported by flash module - * @chan_en_map : Bit map of individual channel enable - * @module_en : Flag used to enable/disable flash LED module - * @trigger_lmh : Flag to enable lmh mitigation + * @pdev: Pointer for platform device + * @regmap: Pointer for regmap structure + * @fnode: Pointer for array of child LED devices + * @snode: Pointer for array of child switch devices + * @batt_psy: Pointer for battery power supply + * @lock: Spinlock to be used for critical section + * @num_fnodes: Number of flash/torch nodes defined in device + * tree + * @num_snodes: Number of switch nodes defined in device tree + * @hw_strobe_gpio: Pointer for array of GPIOs for HW strobing + * @all_ramp_up_done_irq: IRQ number for all ramp up interrupt + * @all_ramp_down_done_irq: IRQ number for all ramp down interrupt + * @led_fault_irq: IRQ number for LED fault interrupt + * @max_current: Maximum current available for flash + * @thermal_derate_current: Thermal derating current limits + * @base: Base address of the flash LED module + * @revision: Revision of the flash LED module + * @subtype: Peripheral subtype of the flash LED module + * @max_channels: Maximum number of channels supported by flash + * module + * @chan_en_map: Bit map of individual channel enable + * @module_en: Flag used to enable/disable flash LED module + * @trigger_lmh: Flag to enable lmh mitigation + * @non_all_mask_switch_present: Used in handling symmetry for all_mask switch + * @secure_vm: Flag indicating whether flash LED is used by + * secure VM */ struct qti_flash_led { struct platform_device *pdev; - struct regmap *regmap; + struct regmap *regmap; struct flash_node_data *fnode; - struct flash_switch_data *snode; + struct flash_switch_data *snode; struct power_supply *batt_psy; - spinlock_t lock; - u32 num_fnodes; - u32 num_snodes; - int *hw_strobe_gpio; - int all_ramp_up_done_irq; - int all_ramp_down_done_irq; - int led_fault_irq; - int max_current; - int thermal_derate_current[FLASH_THERMAL_LEVELS]; - u16 base; - u8 revision; - u8 subtype; - u8 max_channels; - u8 chan_en_map; - bool module_en; - bool trigger_lmh; + spinlock_t lock; + u32 num_fnodes; + u32 num_snodes; + int *hw_strobe_gpio; + int all_ramp_up_done_irq; + int all_ramp_down_done_irq; + int led_fault_irq; + int max_current; + int thermal_derate_current[OTST_MAX]; + u16 base; + u8 revision; + u8 subtype; + u8 max_channels; + u8 chan_en_map; + bool module_en; + bool trigger_lmh; + bool non_all_mask_switch_present; + bool secure_vm; }; struct flash_current_headroom { @@ -230,6 +243,22 @@ static int current_to_code(u32 target_curr_ma, u32 ires_ua) return DIV_ROUND_CLOSEST(target_curr_ma * 1000, ires_ua) - 1; } +static bool is_channel_configured(struct flash_node_data *fnode) +{ + int i; + + for (i = 0; i < fnode->led->num_fnodes; i++) { + if (fnode->led->fnode[i].id == fnode->id && + fnode->led->fnode[i].type != fnode->type && + fnode->led->fnode[i].configured) { + pr_debug("Channel %d for %s is already configured by %s\n", fnode->id, + fnode->fdev.led_cdev.name, fnode->led->fnode[i].fdev.led_cdev.name); + return true; + } + } + return false; +} + static int qti_flash_led_read(struct qti_flash_led *led, u16 offset, u8 *data, u8 len) { @@ -311,6 +340,28 @@ static int qti_flash_led_module_control(struct qti_flash_led *led, return rc; } +static int qti_flash_lmh_mitigation_config(struct qti_flash_led *led, + bool enable) +{ + u8 val = enable ? FLASH_LED_LMH_MITIGATION_SW_EN : 0; + int rc; + + if (enable == led->trigger_lmh) + return 0; + + rc = qti_flash_led_write(led, FLASH_LED_MITIGATION_SW, &val, 1); + if (rc < 0) { + pr_err("Failed to %s LMH mitigation, rc=%d\n", + enable ? "enable" : "disable", rc); + } else { + pr_debug("%s LMH mitigation\n", + enable ? "enabled" : "disabled"); + led->trigger_lmh = enable; + } + + return rc; +} + static int qti_flash_led_strobe(struct qti_flash_led *led, struct flash_switch_data *snode, u8 mask, u8 value) @@ -352,6 +403,12 @@ static int qti_flash_led_strobe(struct qti_flash_led *led, if (rc < 0) goto error; + if (led->trigger_lmh) { + rc = qti_flash_lmh_mitigation_config(led, false); + if (rc < 0) + goto error; + } + rc = qti_flash_led_module_control(led, enable); if (rc < 0) goto error; @@ -417,8 +474,10 @@ static int qti_flash_led_disable(struct flash_node_data *fnode) struct qti_flash_led *led = fnode->led; int rc; - if (!fnode->configured) - return -EINVAL; + if (!fnode->configured) { + pr_debug("%s is not configured\n", fnode->fdev.led_cdev.name); + return 0; + } spin_lock(&led->lock); if ((fnode->strobe_sel == HW_STROBE) && @@ -438,6 +497,7 @@ static int qti_flash_led_disable(struct flash_node_data *fnode) fnode->configured = false; fnode->current_ma = 0; + fnode->user_current_ma = 0; out: spin_unlock(&led->lock); @@ -450,10 +510,9 @@ static enum led_brightness qti_flash_led_brightness_get( return led_cdev->brightness; } -static void qti_flash_led_brightness_set(struct led_classdev *led_cdev, +static int __qti_flash_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { - struct qti_flash_led *led = NULL; struct flash_node_data *fnode = NULL; struct led_classdev_flash *fdev = NULL; int rc; @@ -462,21 +521,22 @@ static void qti_flash_led_brightness_set(struct led_classdev *led_cdev, fdev = container_of(led_cdev, struct led_classdev_flash, led_cdev); fnode = container_of(fdev, struct flash_node_data, fdev); - led = fnode->led; if (!brightness) { rc = qti_flash_led_strobe(fnode->led, NULL, FLASH_LED_ENABLE(fnode->id), 0); if (rc < 0) { pr_err("Failed to destrobe LED, rc=%d\n", rc); - return; + return rc; } rc = qti_flash_led_disable(fnode); if (rc < 0) pr_err("Failed to disable LED\n"); - return; + led_cdev->brightness = 0; + + return rc; } min_current_ma = DIV_ROUND_CLOSEST(fnode->ires_ua, 1000); @@ -501,14 +561,52 @@ static void qti_flash_led_brightness_set(struct led_classdev *led_cdev, rc = qti_flash_led_enable(fnode); if (rc < 0) pr_err("Failed to set brightness %d to LED\n", brightness); + + return rc; } -static int qti_flash_led_symmetry_config( - struct flash_switch_data *snode) +static int qti_flash_config_group_symmetry(struct qti_flash_led *led, + enum flash_led_type type, + u32 led_mask) +{ + int i, rc = 0, total_curr_ma = 0, symmetric_leds = 0, per_led_curr_ma; + + for (i = 0; i < led->num_fnodes; i++) { + if ((led_mask & BIT(led->fnode[i].id)) && + (led->fnode[i].type == type)) { + total_curr_ma += led->fnode[i].user_current_ma; + symmetric_leds++; + } + } + + if (!symmetric_leds) { + pr_err("led-mask %#x has zero symmetric leds\n", led_mask); + return -EINVAL; + } + + per_led_curr_ma = total_curr_ma / symmetric_leds; + + pr_debug("mask: %#x symmetric_leds: %d total: %d per_led_curr_ma: %d\n", + led_mask, symmetric_leds, total_curr_ma, per_led_curr_ma); + + for (i = 0; i < led->num_fnodes; i++) { + if (led_mask & BIT(led->fnode[i].id) && + led->fnode[i].type == type) { + rc = __qti_flash_led_brightness_set( + &led->fnode[i].fdev.led_cdev, per_led_curr_ma); + if (rc < 0) + return rc; + } + } + + return rc; +} + +static int qti_flash_led_symmetry_config(struct flash_switch_data *snode) { struct qti_flash_led *led = snode->led; - int i, total_curr_ma = 0, symmetric_leds = 0, per_led_curr_ma; enum flash_led_type type = FLASH_LED_TYPE_UNKNOWN; + int i, rc = 0; /* Determine which LED type has triggered switch ON */ for (i = 0; i < led->num_fnodes; i++) { @@ -522,45 +620,67 @@ static int qti_flash_led_symmetry_config( return 0; } - for (i = 0; i < led->num_fnodes; i++) { - if ((snode->led_mask & BIT(led->fnode[i].id)) && - (led->fnode[i].type == type)) { - total_curr_ma += led->fnode[i].current_ma; - symmetric_leds++; + if (snode->led_mask == LED_MASK_ALL(led) && + led->non_all_mask_switch_present) { + /* + * Gather masks from the other switches and configure symmetry + * accordingly. + */ + for (i = 0; i < led->num_snodes; i++) { + if (led->snode[i].led_mask != LED_MASK_ALL(led)) { + rc = qti_flash_config_group_symmetry(led, type, + led->snode[i].led_mask); + if (rc < 0) + return rc; + } } - } - - if (symmetric_leds > 0 && total_curr_ma > 0) { - per_led_curr_ma = total_curr_ma / symmetric_leds; } else { - pr_err("Incorrect configuration, symmetric_leds: %d total_curr_ma: %d\n", - symmetric_leds, total_curr_ma); - return -EINVAL; + rc = qti_flash_config_group_symmetry(led, type, + snode->led_mask); } - if (per_led_curr_ma == 0) { - pr_warn("per_led_curr_ma cannot be 0\n"); - return 0; - } + return rc; +} - pr_debug("symmetric_leds: %d total: %d per_led_curr_ma: %d\n", - symmetric_leds, total_curr_ma, per_led_curr_ma); +static void qti_flash_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct led_classdev_flash *fdev; + struct flash_node_data *fnode; + struct qti_flash_led *led; + int i, rc; - for (i = 0; i < led->num_fnodes; i++) { - if (snode->led_mask & BIT(led->fnode[i].id) && - led->fnode[i].type == type) { - qti_flash_led_brightness_set( - &led->fnode[i].fdev.led_cdev, per_led_curr_ma); + fdev = container_of(led_cdev, struct led_classdev_flash, led_cdev); + fnode = container_of(fdev, struct flash_node_data, fdev); + led = fnode->led; + + if (is_channel_configured(fnode)) + return; + + rc = __qti_flash_led_brightness_set(led_cdev, brightness); + if (!rc) + fnode->user_current_ma = brightness; + else + return; + + for (i = 0; i < led->num_snodes; i++) { + pr_debug("snode[%d] symm %d, enabled %d\n", i, + led->snode[i].symmetry_en, + led->snode[i].enabled); + if (led->snode[i].symmetry_en && led->snode[i].enabled) { + qti_flash_led_symmetry_config(&led->snode[i]); + break; } } - - return 0; } +#define FLASH_LMH_TRIGGER_LIMIT_MA 1000 + static int qti_flash_switch_enable(struct flash_switch_data *snode) { struct qti_flash_led *led = snode->led; - int rc = 0, i; + int rc = 0, total_curr_ma = 0, i; + enum flash_led_type type = FLASH_LED_TYPE_UNKNOWN; u8 led_en = 0; /* If symmetry enabled switch, then turn ON all its LEDs */ @@ -583,9 +703,32 @@ static int qti_flash_switch_enable(struct flash_switch_data *snode) !led->fnode[i].configured) continue; + /* + * For flash, LMH mitigation needs to be enabled + * if total current used is greater than or + * equal to 1A. + */ + + type = led->fnode[i].type; + if (type == FLASH_LED_TYPE_FLASH) + total_curr_ma += led->fnode[i].user_current_ma; + led_en |= (1 << led->fnode[i].id); } + if (total_curr_ma >= FLASH_LMH_TRIGGER_LIMIT_MA) { + rc = qti_flash_lmh_mitigation_config(led, true); + if (rc < 0) + return rc; + + /* Wait for lmh mitigation to take effect */ + udelay(500); + } else if (led->trigger_lmh) { + rc = qti_flash_lmh_mitigation_config(led, false); + if (rc < 0) + return rc; + } + return qti_flash_led_strobe(led, snode, snode->led_mask, led_en); } @@ -676,15 +819,15 @@ static struct led_classdev *trigger_to_lcdev(struct led_trigger *trig) { struct led_classdev *led_cdev; - read_lock(&trig->leddev_list_lock); - list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) { + rcu_read_lock(); + list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list) { if (!strcmp(led_cdev->default_trigger, trig->name)) { - read_unlock(&trig->leddev_list_lock); + rcu_read_unlock(); return led_cdev; } } + rcu_read_unlock(); - read_unlock(&trig->leddev_list_lock); return NULL; } @@ -834,23 +977,6 @@ static int qti_flash_led_calc_max_avail_current( voltage_hdrm_mv = qti_flash_led_get_voltage_headroom(led); vflash_vdip = VDIP_THRESH_DEFAULT_UV; - if (!led->trigger_lmh) { - rc = qti_flash_led_masked_write(led, FLASH_LED_MITIGATION_SEL, - FLASH_LED_PREEMPTIVE_LMH_MASK, - FLASH_LED_LMH_MITIGATION_SW); - if (rc < 0) { - pr_err("Failed to enable LMH mitigation, rc=%d\n", rc); - return rc; - } - - /* Wait for lmh mitigation to take effect */ - udelay(100); - - led->trigger_lmh = true; - return qti_flash_led_calc_max_avail_current(led, - max_current_ma); - } - ibatt_safe_ua = DIV_ROUND_CLOSEST((ocv_uv - (vflash_vdip + VFLASH_DIP_MARGIN_UV)) * UCONV, rbatt_uohm); @@ -927,7 +1053,11 @@ static int qti_flash_led_get_max_avail_current( { int thermal_current_limit = 0, rc; - led->trigger_lmh = false; + if (led->secure_vm) { + led->max_current = MAX_FLASH_CURRENT_MA; + return 0; + } + rc = qti_flash_led_calc_max_avail_current(led, max_current_ma); if (rc < 0) { pr_err("Failed to calculate max avail current, rc=%d\n", rc); @@ -1038,7 +1168,7 @@ static ssize_t qti_flash_on_time_show(struct device *dev, snode = container_of(led_cdev, struct flash_switch_data, cdev); - return scnprintf(buf, PAGE_SIZE, "%lu\n", snode->on_time_ms); + return scnprintf(buf, PAGE_SIZE, "%lu\n", snode->on_time_ms * 1000); } static ssize_t qti_flash_off_time_store(struct device *dev, @@ -1069,7 +1199,7 @@ static ssize_t qti_flash_off_time_show(struct device *dev, snode = container_of(led_cdev, struct flash_switch_data, cdev); - return scnprintf(buf, PAGE_SIZE, "%lu\n", snode->off_time_ms); + return scnprintf(buf, PAGE_SIZE, "%lu\n", snode->off_time_ms * 1000); } static struct device_attribute qti_flash_led_attrs[] = { @@ -1282,32 +1412,38 @@ static int qti_flash_led_register_interrupts(struct qti_flash_led *led) { int rc; - rc = devm_request_threaded_irq(&led->pdev->dev, - led->all_ramp_up_done_irq, NULL, qti_flash_led_irq_handler, - IRQF_ONESHOT, "flash_all_ramp_up", led); - if (rc < 0) { - pr_err("Failed to request all_ramp_up_done(%d) IRQ(err:%d)\n", - led->all_ramp_up_done_irq, rc); - return rc; + if (led->all_ramp_up_done_irq >= 0) { + rc = devm_request_threaded_irq(&led->pdev->dev, + led->all_ramp_up_done_irq, NULL, qti_flash_led_irq_handler, + IRQF_ONESHOT, "flash_all_ramp_up", led); + if (rc < 0) { + pr_err("Failed to request all_ramp_up_done(%d) IRQ(err:%d)\n", + led->all_ramp_up_done_irq, rc); + return rc; + } } - rc = devm_request_threaded_irq(&led->pdev->dev, - led->all_ramp_down_done_irq, NULL, qti_flash_led_irq_handler, - IRQF_ONESHOT, "flash_all_ramp_down", led); - if (rc < 0) { - pr_err("Failed to request all_ramp_down_done(%d) IRQ(err:%d)\n", - led->all_ramp_down_done_irq, - rc); - return rc; + if (led->all_ramp_down_done_irq >= 0) { + rc = devm_request_threaded_irq(&led->pdev->dev, + led->all_ramp_down_done_irq, NULL, qti_flash_led_irq_handler, + IRQF_ONESHOT, "flash_all_ramp_down", led); + if (rc < 0) { + pr_err("Failed to request all_ramp_down_done(%d) IRQ(err:%d)\n", + led->all_ramp_down_done_irq, + rc); + return rc; + } } - rc = devm_request_threaded_irq(&led->pdev->dev, - led->led_fault_irq, NULL, qti_flash_led_irq_handler, - IRQF_ONESHOT, "flash_fault", led); - if (rc < 0) { - pr_err("Failed to request led_fault(%d) IRQ(err:%d)\n", - led->led_fault_irq, rc); - return rc; + if (led->led_fault_irq >= 0) { + rc = devm_request_threaded_irq(&led->pdev->dev, + led->led_fault_irq, NULL, qti_flash_led_irq_handler, + IRQF_ONESHOT, "flash_fault", led); + if (rc < 0) { + pr_err("Failed to request led_fault(%d) IRQ(err:%d)\n", + led->led_fault_irq, rc); + return rc; + } } return 0; @@ -1337,10 +1473,11 @@ static int register_switch_device(struct qti_flash_led *led, pr_err("Failed to read led mask rc=%d\n", rc); return rc; } - if ((snode->led_mask > ((1 << led->max_channels) - 1))) { - pr_err("Error, Invalid value for led-mask mask=0x%x\n", - snode->led_mask); + if (!snode->led_mask || snode->led_mask > LED_MASK_ALL(led)) { + pr_err("led-mask %#x invalid\n", snode->led_mask); return -EINVAL; + } else if (snode->led_mask < LED_MASK_ALL(led)) { + led->non_all_mask_switch_present = true; } snode->symmetry_en = of_property_read_bool(node, "qcom,symmetry-en"); @@ -1579,6 +1716,8 @@ static int qti_flash_led_register_device(struct qti_flash_led *led, } + led->secure_vm = of_property_read_bool(node, "qcom,secure-vm"); + led->all_ramp_up_done_irq = of_irq_get_byname(node, "all-ramp-up-done-irq"); if (led->all_ramp_up_done_irq < 0)