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

This commit is contained in:
Linux Build Service Account 2016-08-01 21:56:35 -07:00 committed by Gerrit - the friendly Code Review server
commit 25070b2a8d
7 changed files with 459 additions and 31 deletions

View file

@ -13,9 +13,25 @@ Required properties:
- reg : Base address and size for flash LED modules - reg : Base address and size for flash LED modules
Optional properties: Optional properties:
- interrupts : Specifies the interrupts associated with flash-led.
- interrupt-names : Specify the interrupt names associated with interrupts.
- qcom,hdrm-auto-mode : Boolean type to select headroom auto mode enabled or not - qcom,hdrm-auto-mode : Boolean type to select headroom auto mode enabled or not
- qcom,isc-delay : Integer type to specify short circuit delay. Valid values are 32, 64, - qcom,isc-delay-us : Integer type to specify short circuit delay. Valid values are 32, 64,
128, 192. Unit is us. 128, 192. Unit is uS.
- qcom,warmup-delay-us : Integer type to specify warm up delay. Valid values are 32, 64,
128, 192. Unit is uS.
- qcom,short-circuit-det : Boolean property which enables short circuit fault detection.
- qcom,open-circuit-det : Boolean property which enables open circuit fault detection.
- qcom,vph-droop-det : Boolean property which enables VPH droop detection.
- qcom,vph-droop-hys-mv : Integer property to specify VPH droop hysteresis. It is only used if
qcom,vph-droop-det is specified. Valid values are 0, 25, 50 and 75.
Unit is mV.
- qcom,vph-droop-thresh-mv : Integer property to specify VPH droop threshold. It is only used if
qcom,vph-droop-det is specified. Valid values are
2500 to 3200 with step size of 100. Unit is mV.
- qcom,vph-droop-debounce-us : Integer property to specify VPH droop debounce time. It is only used
if qcom,vph-droop-det is specified. Valid values are 0, 8, 16 and 26.
Unit is uS.
- qcom,hw-strobe-option : Integer type to specify hardware strobe option. Based on the specified - 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 value, additional GPIO configuration may be required to provide strobing
support. Supported values are: support. Supported values are:
@ -93,6 +109,22 @@ Example:
status = "okay"; status = "okay";
reg = <0xd300 0x100>; reg = <0xd300 0x100>;
label = "flash"; label = "flash";
interrupts = <0x3 0xd3 0x0 IRQ_TYPE_EDGE_BOTH>,
<0x3 0xd3 0x1 IRQ_TYPE_EDGE_BOTH>,
<0x3 0xd3 0x2 IRQ_TYPE_EDGE_BOTH>,
<0x3 0xd3 0x3 IRQ_TYPE_EDGE_BOTH>,
<0x3 0xd3 0x4 IRQ_TYPE_EDGE_BOTH>,
<0x3 0xd3 0x5 IRQ_TYPE_EDGE_BOTH>,
<0x3 0xd3 0x6 IRQ_TYPE_EDGE_BOTH>,
<0x3 0xd3 0x7 IRQ_TYPE_EDGE_BOTH>;
interrupt-names = "led-fault-irq",
"mitigation-irq",
"flash-timer-exp-irq",
"all-ramp-down-done-irq",
"all-ramp-up-done-irq",
"led3-ramp-up-done-irq",
"led2-ramp-up-done-irq",
"led1-ramp-up-done-irq";
qcom,hdrm-auto-mode; qcom,hdrm-auto-mode;
qcom,isc-delay = <192>; qcom,isc-delay = <192>;

View file

@ -634,6 +634,10 @@
status = "okay"; status = "okay";
reg = <0xd300 0x100>; reg = <0xd300 0x100>;
label = "flash"; label = "flash";
interrupts = <0x3 0xd3 0x3 IRQ_TYPE_EDGE_RISING>,
<0x3 0xd3 0x4 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "all-ramp-down-done-irq",
"all-ramp-up-done-irq";
qcom,hdrm-auto-mode; qcom,hdrm-auto-mode;
qcom,isc-delay = <192>; qcom,isc-delay = <192>;

View file

@ -16,13 +16,20 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/leds-qpnp-flash.h>
#include <linux/leds-qpnp-flash-v2.h> #include <linux/leds-qpnp-flash-v2.h>
#include "leds.h"
#define FLASH_LED_REG_LED_STATUS1(base) (base + 0x08)
#define FLASH_LED_REG_LED_STATUS2(base) (base + 0x09)
#define FLASH_LED_REG_INT_RT_STS(base) (base + 0x10)
#define FLASH_LED_REG_SAFETY_TMR(base) (base + 0x40) #define FLASH_LED_REG_SAFETY_TMR(base) (base + 0x40)
#define FLASH_LED_REG_TGR_CURRENT(base) (base + 0x43) #define FLASH_LED_REG_TGR_CURRENT(base) (base + 0x43)
#define FLASH_LED_REG_MOD_CTRL(base) (base + 0x46) #define FLASH_LED_REG_MOD_CTRL(base) (base + 0x46)
@ -32,20 +39,40 @@
#define FLASH_LED_EN_LED_CTRL(base) (base + 0x4C) #define FLASH_LED_EN_LED_CTRL(base) (base + 0x4C)
#define FLASH_LED_REG_HDRM_PRGM(base) (base + 0x4D) #define FLASH_LED_REG_HDRM_PRGM(base) (base + 0x4D)
#define FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(base) (base + 0x50) #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_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_CURRENT_DERATE_EN(base) (base + 0x76)
#define FLASH_LED_HDRM_MODE_PRGM_MASK GENMASK(7, 0) #define FLASH_LED_HDRM_MODE_PRGM_MASK GENMASK(7, 0)
#define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4) #define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4)
#define FLASH_LED_CURRENT_MASK GENMASK(6, 0) #define FLASH_LED_CURRENT_MASK GENMASK(6, 0)
#define FLASH_LED_ENABLE_MASK GENMASK(2, 0) #define FLASH_LED_ENABLE_MASK GENMASK(2, 0)
#define FLASH_LED_SAFETY_TMR_MASK GENMASK(7, 0) #define FLASH_LED_SAFETY_TMR_MASK GENMASK(7, 0)
#define FLASH_LED_ISC_DELAY_MASK GENMASK(1, 0) #define FLASH_LED_INT_RT_STS_MASK GENMASK(7, 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_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_MOD_CTRL_MASK BIT(7)
#define FLASH_LED_HW_SW_STROBE_SEL_MASK BIT(2) #define FLASH_LED_HW_SW_STROBE_SEL_MASK BIT(2)
#define FLASH_LED_VPH_DROOP_FAULT_MASK BIT(4)
#define FLASH_LED_HEADROOM_AUTO_MODE_ENABLED true #define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8)
#define FLASH_LED_ISC_DELAY_SHIFT 6 #define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25)
#define FLASH_LED_ISC_DELAY_DEFAULT_US 3 #define VPH_DROOP_THRESH_MV_TO_VAL(val_mv) ((val_mv / 100) - 25)
#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_DEFAULT 2
#define FLASH_LED_VPH_DROOP_THRESH_DEFAULT 5
#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_SAFETY_TMR_VAL_OFFSET 1 #define FLASH_LED_SAFETY_TMR_VAL_OFFSET 1
#define FLASH_LED_SAFETY_TMR_VAL_DIVISOR 10 #define FLASH_LED_SAFETY_TMR_VAL_DIVISOR 10
#define FLASH_LED_SAFETY_TMR_ENABLE BIT(7) #define FLASH_LED_SAFETY_TMR_ENABLE BIT(7)
@ -69,6 +96,9 @@
#define FLASH_LED_SAFETY_TMR_DISABLED 0x13 #define FLASH_LED_SAFETY_TMR_DISABLED 0x13
#define FLASH_LED_MIN_CURRENT_MA 25 #define FLASH_LED_MIN_CURRENT_MA 25
/* notifier call chain for flash-led irqs */
static ATOMIC_NOTIFIER_HEAD(irq_notifier_list);
enum flash_led_type { enum flash_led_type {
FLASH_LED_TYPE_FLASH, FLASH_LED_TYPE_FLASH,
FLASH_LED_TYPE_TORCH, FLASH_LED_TYPE_TORCH,
@ -125,9 +155,17 @@ struct flash_switch_data {
* Flash LED configuration read from device tree * Flash LED configuration read from device tree
*/ */
struct flash_led_platform_data { struct flash_led_platform_data {
u8 isc_delay_us; int all_ramp_up_done_irq;
u8 hw_strobe_option; int all_ramp_down_done_irq;
bool hdrm_auto_mode_en; int led_fault_irq;
u8 isc_delay;
u8 warmup_delay;
u8 current_derate_en_cfg;
u8 vph_droop_threshold;
u8 vph_droop_hysteresis;
u8 vph_droop_debounce;
u8 hw_strobe_option;
bool hdrm_auto_mode_en;
}; };
/* /*
@ -146,20 +184,37 @@ struct qpnp_flash_led {
u16 base; u16 base;
}; };
static int
qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data)
{
int rc;
uint val;
rc = regmap_read(led->regmap, addr, &val);
if (rc < 0)
dev_err(&led->pdev->dev, "Unable to read from 0x%04X rc = %d\n",
addr, rc);
else
dev_dbg(&led->pdev->dev, "Read 0x%02X from addr 0x%04X\n",
val, addr);
*data = (u8)val;
return rc;
}
static int static int
qpnp_flash_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask, qpnp_flash_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask,
u8 val) u8 val)
{ {
int rc; int rc;
rc = regmap_update_bits(led->regmap, addr, mask, val); rc = regmap_update_bits(led->regmap, addr, mask, val);
if (rc < 0) if (rc < 0)
dev_err(&led->pdev->dev, dev_err(&led->pdev->dev, "Unable to update bits from 0x%04X, rc = %d\n",
"Unable to update bits from 0x%02X, rc = %d\n", addr, rc);
addr, rc);
else else
dev_dbg(&led->pdev->dev, "Wrote 0x%02X to addr 0x%02X\n", dev_dbg(&led->pdev->dev, "Wrote 0x%02X to addr 0x%04X\n",
val, addr); val, addr);
return rc; return rc;
} }
@ -195,7 +250,43 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
rc = qpnp_flash_led_masked_write(led, rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_ISC_DELAY(led->base), FLASH_LED_REG_ISC_DELAY(led->base),
FLASH_LED_ISC_DELAY_MASK, led->pdata->isc_delay_us); FLASH_LED_ISC_WARMUP_DELAY_MASK,
led->pdata->isc_delay);
if (rc < 0)
return rc;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_WARMUP_DELAY(led->base),
FLASH_LED_ISC_WARMUP_DELAY_MASK,
led->pdata->warmup_delay);
if (rc < 0)
return rc;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_CURRENT_DERATE_EN(led->base),
FLASH_LED_CURRENT_DERATE_EN_MASK,
led->pdata->current_derate_en_cfg);
if (rc < 0)
return rc;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_VPH_DROOP_DEBOUNCE(led->base),
FLASH_LED_VPH_DROOP_DEBOUNCE_MASK,
led->pdata->vph_droop_debounce);
if (rc < 0)
return rc;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_VPH_DROOP_THRESHOLD(led->base),
FLASH_LED_VPH_DROOP_THRESHOLD_MASK,
led->pdata->vph_droop_threshold);
if (rc < 0)
return rc;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_VPH_DROOP_THRESHOLD(led->base),
FLASH_LED_VPH_DROOP_HYSTERESIS_MASK,
led->pdata->vph_droop_hysteresis);
if (rc < 0) if (rc < 0)
return rc; return rc;
@ -459,13 +550,21 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
return 0; return 0;
} }
int qpnp_flash_led_prepare(struct led_classdev *led_cdev, int options) int qpnp_flash_led_prepare(struct led_trigger *trig, int options)
{ {
struct flash_switch_data *snode = struct led_classdev *led_cdev = trigger_to_lcdev(trig);
container_of(led_cdev, struct flash_switch_data, cdev); struct flash_switch_data *snode;
struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev); struct qpnp_flash_led *led;
int rc, val = 0; int rc, val = 0;
if (!led_cdev) {
pr_err("Invalid led_trigger provided\n");
return -EINVAL;
}
snode = container_of(led_cdev, struct flash_switch_data, cdev);
led = dev_get_drvdata(&snode->pdev->dev);
if (!(options & (ENABLE_REGULATOR | QUERY_MAX_CURRENT))) { if (!(options & (ENABLE_REGULATOR | QUERY_MAX_CURRENT))) {
dev_err(&led->pdev->dev, "Invalid options %d\n", options); dev_err(&led->pdev->dev, "Invalid options %d\n", options);
return -EINVAL; return -EINVAL;
@ -521,6 +620,77 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
spin_unlock(&led->lock); spin_unlock(&led->lock);
} }
/* irq handler */
static irqreturn_t qpnp_flash_led_irq_handler(int irq, void *_led)
{
struct qpnp_flash_led *led = _led;
enum flash_led_irq_type irq_type = INVALID_IRQ;
int rc;
u8 irq_status, led_status1, led_status2;
dev_dbg(&led->pdev->dev, "irq received, irq=%d\n", irq);
rc = qpnp_flash_led_read(led,
FLASH_LED_REG_INT_RT_STS(led->base), &irq_status);
if (rc < 0) {
dev_err(&led->pdev->dev, "Failed to read interrupt status reg, rc=%d\n",
rc);
goto exit;
}
if (irq == led->pdata->all_ramp_up_done_irq)
irq_type = ALL_RAMP_UP_DONE_IRQ;
else if (irq == led->pdata->all_ramp_down_done_irq)
irq_type = ALL_RAMP_DOWN_DONE_IRQ;
else if (irq == led->pdata->led_fault_irq)
irq_type = LED_FAULT_IRQ;
if (irq_type == ALL_RAMP_UP_DONE_IRQ)
atomic_notifier_call_chain(&irq_notifier_list,
irq_type, NULL);
if (irq_type == LED_FAULT_IRQ) {
rc = qpnp_flash_led_read(led,
FLASH_LED_REG_LED_STATUS1(led->base), &led_status1);
if (rc < 0) {
dev_err(&led->pdev->dev, "Failed to read led_status1 reg, rc=%d\n",
rc);
goto exit;
}
rc = qpnp_flash_led_read(led,
FLASH_LED_REG_LED_STATUS2(led->base), &led_status2);
if (rc < 0) {
dev_err(&led->pdev->dev, "Failed to read led_status2 reg, rc=%d\n",
rc);
goto exit;
}
if (led_status1)
dev_emerg(&led->pdev->dev, "led short/open fault detected! led_status1=%x\n",
led_status1);
if (led_status2 & FLASH_LED_VPH_DROOP_FAULT_MASK)
dev_emerg(&led->pdev->dev, "led vph_droop fault detected!\n");
}
dev_dbg(&led->pdev->dev, "irq handled, irq_type=%x, irq_status=%x\n",
irq_type, irq_status);
exit:
return IRQ_HANDLED;
}
int qpnp_flash_led_register_irq_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_register(&irq_notifier_list, nb);
}
int qpnp_flash_led_unregister_irq_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_unregister(&irq_notifier_list, nb);
}
static int qpnp_flash_led_regulator_setup(struct qpnp_flash_led *led, static int qpnp_flash_led_regulator_setup(struct qpnp_flash_led *led,
struct flash_switch_data *snode, bool on) struct flash_switch_data *snode, bool on)
{ {
@ -901,28 +1071,116 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
{ {
int rc; int rc;
u32 val; u32 val;
bool short_circuit_det, open_circuit_det, vph_droop_det;
led->pdata->hdrm_auto_mode_en = FLASH_LED_HEADROOM_AUTO_MODE_ENABLED;
led->pdata->hdrm_auto_mode_en = of_property_read_bool(node, led->pdata->hdrm_auto_mode_en = of_property_read_bool(node,
"qcom,hdrm-auto-mode"); "qcom,hdrm-auto-mode");
led->pdata->isc_delay_us = FLASH_LED_ISC_DELAY_DEFAULT_US; led->pdata->isc_delay = FLASH_LED_ISC_DELAY_DEFAULT;
rc = of_property_read_u32(node, "qcom,isc-delay", &val); rc = of_property_read_u32(node, "qcom,isc-delay-us", &val);
if (!rc) { if (!rc) {
led->pdata->isc_delay_us = val >> FLASH_LED_ISC_DELAY_SHIFT; led->pdata->isc_delay =
val >> FLASH_LED_ISC_WARMUP_DELAY_SHIFT;
} else if (rc != -EINVAL) { } else if (rc != -EINVAL) {
dev_err(&led->pdev->dev, "Unable to read ISC delay\n"); dev_err(&led->pdev->dev,
"Unable to read ISC delay, rc=%d\n", rc);
return rc; return rc;
} }
led->pdata->warmup_delay = FLASH_LED_WARMUP_DELAY_DEFAULT;
rc = of_property_read_u32(node, "qcom,warmup-delay-us", &val);
if (!rc) {
led->pdata->warmup_delay =
val >> FLASH_LED_ISC_WARMUP_DELAY_SHIFT;
} else if (rc != -EINVAL) {
dev_err(&led->pdev->dev,
"Unable to read WARMUP delay, rc=%d\n", rc);
return rc;
}
short_circuit_det =
of_property_read_bool(node, "qcom,short-circuit-det");
open_circuit_det = of_property_read_bool(node, "qcom,open-circuit-det");
vph_droop_det = of_property_read_bool(node, "qcom,vph-droop-det");
led->pdata->current_derate_en_cfg = (vph_droop_det << 2) |
(open_circuit_det << 1) | short_circuit_det;
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) {
led->pdata->vph_droop_debounce =
VPH_DROOP_DEBOUNCE_US_TO_VAL(val);
} else if (rc != -EINVAL) {
dev_err(&led->pdev->dev,
"Unable to read VPH droop debounce, rc=%d\n", rc);
return rc;
}
if (led->pdata->vph_droop_debounce > FLASH_LED_VPH_DROOP_DEBOUNCE_MAX) {
dev_err(&led->pdev->dev,
"Invalid VPH droop debounce specified");
return -EINVAL;
}
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 =
VPH_DROOP_THRESH_MV_TO_VAL(val);
} else if (rc != -EINVAL) {
dev_err(&led->pdev->dev,
"Unable to read VPH droop threshold, rc=%d\n", rc);
return rc;
}
if (led->pdata->vph_droop_threshold > FLASH_LED_VPH_DROOP_THRESH_MAX) {
dev_err(&led->pdev->dev,
"Invalid VPH droop threshold specified");
return -EINVAL;
}
led->pdata->vph_droop_hysteresis =
FLASH_LED_VPH_DROOP_HYST_DEFAULT;
rc = of_property_read_u32(node, "qcom,vph-droop-hysteresis-mv", &val);
if (!rc) {
led->pdata->vph_droop_hysteresis =
VPH_DROOP_HYST_MV_TO_VAL(val);
} else if (rc != -EINVAL) {
dev_err(&led->pdev->dev,
"Unable to read VPH droop hysteresis, rc=%d\n", rc);
return rc;
}
if (led->pdata->vph_droop_hysteresis > FLASH_LED_VPH_DROOP_HYST_MAX) {
dev_err(&led->pdev->dev,
"Invalid VPH droop hysteresis specified");
return -EINVAL;
}
rc = of_property_read_u32(node, "qcom,hw-strobe-option", &val); rc = of_property_read_u32(node, "qcom,hw-strobe-option", &val);
if (!rc) { if (!rc) {
led->pdata->hw_strobe_option = (u8)val; led->pdata->hw_strobe_option = (u8)val;
} else if (rc != -EINVAL) { } else if (rc != -EINVAL) {
dev_err(&led->pdev->dev, "Unable to parse hw strobe option\n"); dev_err(&led->pdev->dev,
"Unable to parse hw strobe option, rc=%d\n", rc);
return rc; return rc;
} }
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)
dev_dbg(&led->pdev->dev, "all-ramp-up-done-irq not used\n");
led->pdata->all_ramp_down_done_irq =
of_irq_get_byname(node, "all-ramp-down-done-irq");
if (led->pdata->all_ramp_down_done_irq < 0)
dev_dbg(&led->pdev->dev, "all-ramp-down-done-irq not used\n");
led->pdata->led_fault_irq =
of_irq_get_byname(node, "led-fault-irq");
if (led->pdata->led_fault_irq < 0)
dev_dbg(&led->pdev->dev, "led-fault-irq not used\n");
return 0; return 0;
} }
@ -1033,6 +1291,49 @@ static int qpnp_flash_led_probe(struct platform_device *pdev)
} }
} }
/* setup irqs */
if (led->pdata->all_ramp_up_done_irq >= 0) {
rc = devm_request_threaded_irq(&led->pdev->dev,
led->pdata->all_ramp_up_done_irq,
NULL, qpnp_flash_led_irq_handler,
IRQF_ONESHOT,
"qpnp_flash_led_all_ramp_up_done_irq", led);
if (rc < 0) {
dev_err(&pdev->dev,
"Unable to request all_ramp_up_done(%d) IRQ(err:%d)\n",
led->pdata->all_ramp_up_done_irq, rc);
return rc;
}
}
if (led->pdata->all_ramp_down_done_irq >= 0) {
rc = devm_request_threaded_irq(&led->pdev->dev,
led->pdata->all_ramp_down_done_irq,
NULL, qpnp_flash_led_irq_handler,
IRQF_ONESHOT,
"qpnp_flash_led_all_ramp_down_done_irq", led);
if (rc < 0) {
dev_err(&pdev->dev,
"Unable to request all_ramp_down_done(%d) IRQ(err:%d)\n",
led->pdata->all_ramp_down_done_irq, rc);
return rc;
}
}
if (led->pdata->led_fault_irq >= 0) {
rc = devm_request_threaded_irq(&led->pdev->dev,
led->pdata->led_fault_irq,
NULL, qpnp_flash_led_irq_handler,
IRQF_ONESHOT,
"qpnp_flash_led_fault_irq", led);
if (rc < 0) {
dev_err(&pdev->dev,
"Unable to request led_fault(%d) IRQ(err:%d)\n",
led->pdata->led_fault_irq, rc);
return rc;
}
}
rc = qpnp_flash_led_init_settings(led); rc = qpnp_flash_led_init_settings(led);
if (rc < 0) { if (rc < 0) {
dev_err(&pdev->dev, dev_err(&pdev->dev,

View file

@ -26,11 +26,12 @@
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/leds-qpnp-flash.h>
#include <linux/qpnp/qpnp-adc.h> #include <linux/qpnp/qpnp-adc.h>
#include <linux/qpnp/qpnp-revid.h> #include <linux/qpnp/qpnp-revid.h>
#include "leds.h"
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include "leds.h"
#define FLASH_LED_PERIPHERAL_SUBTYPE(base) (base + 0x05) #define FLASH_LED_PERIPHERAL_SUBTYPE(base) (base + 0x05)
#define FLASH_SAFETY_TIMER(base) (base + 0x40) #define FLASH_SAFETY_TIMER(base) (base + 0x40)
@ -1154,6 +1155,47 @@ error_regulator_enable:
return rc; return rc;
} }
int qpnp_flash_led_prepare(struct led_trigger *trig, int options)
{
struct led_classdev *led_cdev = trigger_to_lcdev(trig);
struct flash_node_data *flash_node;
struct qpnp_flash_led *led;
int rc, val = 0;
if (!led_cdev) {
pr_err("Invalid led_trigger provided\n");
return -EINVAL;
}
flash_node = container_of(led_cdev, struct flash_node_data, cdev);
led = dev_get_drvdata(&flash_node->pdev->dev);
if (!(options & (ENABLE_REGULATOR | QUERY_MAX_CURRENT))) {
dev_err(&led->pdev->dev, "Invalid options %d\n", options);
return -EINVAL;
}
if (options & ENABLE_REGULATOR) {
rc = flash_regulator_enable(led, flash_node, true);
if (rc < 0) {
dev_err(&led->pdev->dev,
"enable regulator failed, rc=%d\n", rc);
return rc;
}
}
if (options & QUERY_MAX_CURRENT) {
val = qpnp_flash_led_get_max_avail_current(flash_node, led);
if (val < 0) {
dev_err(&led->pdev->dev,
"query max current failed, rc=%d\n", val);
return val;
}
}
return val;
}
static void qpnp_flash_led_work(struct work_struct *work) static void qpnp_flash_led_work(struct work_struct *work)
{ {
struct flash_node_data *flash_node = container_of(work, struct flash_node_data *flash_node = container_of(work,

View file

@ -44,6 +44,22 @@ static inline int led_get_brightness(struct led_classdev *led_cdev)
return led_cdev->brightness; return led_cdev->brightness;
} }
static inline 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) {
if (!strcmp(led_cdev->default_trigger, trig->name)) {
read_unlock(&trig->leddev_list_lock);
return led_cdev;
}
}
read_unlock(&trig->leddev_list_lock);
return NULL;
}
void led_init_core(struct led_classdev *led_cdev); void led_init_core(struct led_classdev *led_cdev);
void led_stop_software_blink(struct led_classdev *led_cdev); void led_stop_software_blink(struct led_classdev *led_cdev);

View file

@ -14,11 +14,21 @@
#define __LEDS_QPNP_FLASH_V2_H #define __LEDS_QPNP_FLASH_V2_H
#include <linux/leds.h> #include <linux/leds.h>
#include "leds.h" #include <linux/notifier.h>
#define ENABLE_REGULATOR BIT(0) enum flash_led_irq_type {
#define QUERY_MAX_CURRENT BIT(1) LED_FAULT_IRQ = BIT(0),
MITIGATION_IRQ = BIT(1),
FLASH_TIMER_EXP_IRQ = BIT(2),
ALL_RAMP_DOWN_DONE_IRQ = BIT(3),
ALL_RAMP_UP_DONE_IRQ = BIT(4),
LED3_RAMP_UP_DONE_IRQ = BIT(5),
LED2_RAMP_UP_DONE_IRQ = BIT(6),
LED1_RAMP_UP_DONE_IRQ = BIT(7),
INVALID_IRQ = BIT(8),
};
int qpnp_flash_led_prepare(struct led_classdev *led_cdev, int options); int qpnp_flash_led_register_irq_notifier(struct notifier_block *nb);
int qpnp_flash_led_unregister_irq_notifier(struct notifier_block *nb);
#endif #endif

View file

@ -0,0 +1,23 @@
/* Copyright (c) 2016, 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __LEDS_QPNP_FLASH_H
#define __LEDS_QPNP_FLASH_H
#include <linux/leds.h>
#define ENABLE_REGULATOR BIT(0)
#define QUERY_MAX_CURRENT BIT(1)
int qpnp_flash_led_prepare(struct led_trigger *trig, int options);
#endif