Merge "leds: qpnp-flash-v2: Add support for led_fault_irq"
This commit is contained in:
commit
25070b2a8d
7 changed files with 459 additions and 31 deletions
|
@ -13,9 +13,25 @@ Required properties:
|
|||
- reg : Base address and size for flash LED modules
|
||||
|
||||
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,isc-delay : Integer type to specify short circuit delay. Valid values are 32, 64,
|
||||
128, 192. Unit is us.
|
||||
- qcom,isc-delay-us : Integer type to specify short circuit delay. Valid values are 32, 64,
|
||||
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
|
||||
value, additional GPIO configuration may be required to provide strobing
|
||||
support. Supported values are:
|
||||
|
@ -93,6 +109,22 @@ Example:
|
|||
status = "okay";
|
||||
reg = <0xd300 0x100>;
|
||||
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,isc-delay = <192>;
|
||||
|
|
|
@ -634,6 +634,10 @@
|
|||
status = "okay";
|
||||
reg = <0xd300 0x100>;
|
||||
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,isc-delay = <192>;
|
||||
|
||||
|
|
|
@ -16,13 +16,20 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/leds-qpnp-flash.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_TGR_CURRENT(base) (base + 0x43)
|
||||
#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_REG_HDRM_PRGM(base) (base + 0x4D)
|
||||
#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_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_VOL_MASK GENMASK(7, 4)
|
||||
#define FLASH_LED_CURRENT_MASK GENMASK(6, 0)
|
||||
#define FLASH_LED_ENABLE_MASK GENMASK(2, 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_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 FLASH_LED_ISC_DELAY_SHIFT 6
|
||||
#define FLASH_LED_ISC_DELAY_DEFAULT_US 3
|
||||
#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_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_DIVISOR 10
|
||||
#define FLASH_LED_SAFETY_TMR_ENABLE BIT(7)
|
||||
|
@ -69,6 +96,9 @@
|
|||
#define FLASH_LED_SAFETY_TMR_DISABLED 0x13
|
||||
#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 {
|
||||
FLASH_LED_TYPE_FLASH,
|
||||
FLASH_LED_TYPE_TORCH,
|
||||
|
@ -125,7 +155,15 @@ struct flash_switch_data {
|
|||
* Flash LED configuration read from device tree
|
||||
*/
|
||||
struct flash_led_platform_data {
|
||||
u8 isc_delay_us;
|
||||
int all_ramp_up_done_irq;
|
||||
int all_ramp_down_done_irq;
|
||||
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,6 +184,24 @@ struct qpnp_flash_led {
|
|||
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
|
||||
qpnp_flash_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask,
|
||||
u8 val)
|
||||
|
@ -154,11 +210,10 @@ qpnp_flash_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask,
|
|||
|
||||
rc = regmap_update_bits(led->regmap, addr, mask, val);
|
||||
if (rc < 0)
|
||||
dev_err(&led->pdev->dev,
|
||||
"Unable to update bits from 0x%02X, rc = %d\n",
|
||||
dev_err(&led->pdev->dev, "Unable to update bits from 0x%04X, rc = %d\n",
|
||||
addr, rc);
|
||||
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);
|
||||
|
||||
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,
|
||||
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)
|
||||
return rc;
|
||||
|
||||
|
@ -459,13 +550,21 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
|
|||
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 =
|
||||
container_of(led_cdev, struct flash_switch_data, cdev);
|
||||
struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev);
|
||||
struct led_classdev *led_cdev = trigger_to_lcdev(trig);
|
||||
struct flash_switch_data *snode;
|
||||
struct qpnp_flash_led *led;
|
||||
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))) {
|
||||
dev_err(&led->pdev->dev, "Invalid options %d\n", options);
|
||||
return -EINVAL;
|
||||
|
@ -521,6 +620,77 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
|
|||
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,
|
||||
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;
|
||||
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,
|
||||
"qcom,hdrm-auto-mode");
|
||||
|
||||
led->pdata->isc_delay_us = FLASH_LED_ISC_DELAY_DEFAULT_US;
|
||||
rc = of_property_read_u32(node, "qcom,isc-delay", &val);
|
||||
led->pdata->isc_delay = FLASH_LED_ISC_DELAY_DEFAULT;
|
||||
rc = of_property_read_u32(node, "qcom,isc-delay-us", &val);
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!rc) {
|
||||
led->pdata->hw_strobe_option = (u8)val;
|
||||
} 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
if (rc < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
|
|
|
@ -26,11 +26,12 @@
|
|||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/leds-qpnp-flash.h>
|
||||
#include <linux/qpnp/qpnp-adc.h>
|
||||
#include <linux/qpnp/qpnp-revid.h>
|
||||
#include "leds.h"
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include "leds.h"
|
||||
|
||||
#define FLASH_LED_PERIPHERAL_SUBTYPE(base) (base + 0x05)
|
||||
#define FLASH_SAFETY_TIMER(base) (base + 0x40)
|
||||
|
@ -1154,6 +1155,47 @@ error_regulator_enable:
|
|||
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)
|
||||
{
|
||||
struct flash_node_data *flash_node = container_of(work,
|
||||
|
|
|
@ -44,6 +44,22 @@ static inline int led_get_brightness(struct led_classdev *led_cdev)
|
|||
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_stop_software_blink(struct led_classdev *led_cdev);
|
||||
|
||||
|
|
|
@ -14,11 +14,21 @@
|
|||
#define __LEDS_QPNP_FLASH_V2_H
|
||||
|
||||
#include <linux/leds.h>
|
||||
#include "leds.h"
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#define ENABLE_REGULATOR BIT(0)
|
||||
#define QUERY_MAX_CURRENT BIT(1)
|
||||
enum flash_led_irq_type {
|
||||
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
|
||||
|
|
23
include/linux/leds-qpnp-flash.h
Normal file
23
include/linux/leds-qpnp-flash.h
Normal 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
|
Loading…
Add table
Reference in a new issue