summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDevesh Jhunjhunwala <deveshj@codeaurora.org>2016-06-02 11:59:58 -0700
committerKyle Yan <kyan@codeaurora.org>2016-07-11 18:56:23 -0700
commit94d2a9fd5dc8a51a4b43e82bd2271d209bd132c2 (patch)
tree9cafaade29e7d3659ccca0154781987d9fa511bb
parent01d202d756900e3f0f88bf9b184dbcad6874b41b (diff)
leds: qpnp-flash-v2: add regulator support
Add support to control regulators required for flash-led. The pmicobalt_bob regulator is one such regulator required by the flash-led device. CRs-Fixed: 1024187 Change-Id: I1515fba2fb04c0b4d21828af3cea6d322262ab14 Signed-off-by: Devesh Jhunjhunwala <deveshj@codeaurora.org>
-rw-r--r--Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt12
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c157
-rw-r--r--include/linux/leds-qpnp-flash-v2.h9
3 files changed, 176 insertions, 2 deletions
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
index d0fc0165e28e..1372e424927f 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
@@ -74,6 +74,14 @@ Optional properties:
be edge triggered. Otherwise, it is level triggered.
- qcom,hw-strobe-active-low : Boolean property to select strobe signal polarity. If defined, hw-strobe
signal polarity is set to active-low, else it is active-high.
+- reg<n> : reg<n> (<n> represents number. e.g. 0,1,2,..) subnode is to add support for
+ multiple power sources. This subnode should only be specified for switch nodes.
+ Required property inside regulator node:
+ - regulator-name : Name of the regulator which has to be used for this
+ switch node.
+ Optional property inside regulator node:
+ - max-voltage-uv : This specifies max voltage of regulator. Some switch
+ or boost regulator does not need this property.
Example:
qcom,leds@d300 {
@@ -177,6 +185,10 @@ Example:
qcom,led-name = "led:switch";
qcom,default-led-trigger =
"switch_trigger";
+ reg0 {
+ regulator-name = "pmicobalt_bob";
+ max-voltage-uv = <3600000>;
+ };
};
};
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 95bda28713f8..068c62a2f27f 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -20,6 +20,7 @@
#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/leds-qpnp-flash-v2.h>
#define FLASH_LED_REG_SAFETY_TMR(base) (base + 0x40)
@@ -196,7 +197,34 @@ static int qpnp_flash_led_hw_strobe_enable(struct flash_node_data *fnode,
static int qpnp_flash_led_regulator_enable(struct qpnp_flash_led *led,
struct flash_switch_data *snode, bool on)
{
- return 0;
+ int i, rc = 0;
+
+ if (snode->regulator_on == on)
+ return 0;
+
+ if (on == false) {
+ i = snode->num_regulators;
+ goto out;
+ }
+
+ for (i = 0; i < snode->num_regulators; i++) {
+ rc = regulator_enable(snode->reg_data[i].vreg);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "regulator enable failed, rc=%d\n", rc);
+ goto out;
+ }
+ }
+ snode->regulator_on = true;
+
+ return rc;
+
+out:
+ while (i--)
+ regulator_disable(snode->reg_data[i].vreg);
+
+ snode->regulator_on = false;
+ return rc;
}
static int qpnp_flash_led_get_max_avail_current(struct flash_switch_data *snode,
@@ -230,6 +258,10 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
if (!on)
goto leds_turn_off;
+ rc = qpnp_flash_led_regulator_enable(led, snode, true);
+ if (rc)
+ return rc;
+
val = 0;
for (i = 0; i < led->num_led_nodes; i++)
val |= led->fnode[i].ires << (led->fnode[i].id * 2);
@@ -351,6 +383,8 @@ leds_turn_off:
}
}
+ qpnp_flash_led_regulator_enable(led, snode, false);
+
return 0;
}
@@ -419,6 +453,104 @@ exit:
spin_unlock(&led->lock);
}
+static int qpnp_flash_led_regulator_setup(struct qpnp_flash_led *led,
+ struct flash_switch_data *snode, bool on)
+{
+ int i, rc = 0;
+
+ if (on == false) {
+ i = snode->num_regulators;
+ goto out;
+ }
+
+ for (i = 0; i < snode->num_regulators; i++) {
+ snode->reg_data[i].vreg = regulator_get(snode->cdev.dev,
+ snode->reg_data[i].reg_name);
+ if (IS_ERR(snode->reg_data[i].vreg)) {
+ rc = PTR_ERR(snode->reg_data[i].vreg);
+ dev_err(&led->pdev->dev,
+ "Failed to get regulator, rc=%d\n", rc);
+ goto out;
+ }
+
+ if (regulator_count_voltages(snode->reg_data[i].vreg) > 0) {
+ rc = regulator_set_voltage(snode->reg_data[i].vreg,
+ snode->reg_data[i].max_volt_uv,
+ snode->reg_data[i].max_volt_uv);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "regulator set voltage failed, rc=%d\n",
+ rc);
+ regulator_put(snode->reg_data[i].vreg);
+ goto out;
+ }
+ }
+ }
+
+ return rc;
+
+out:
+ while (i--) {
+ if (regulator_count_voltages(snode->reg_data[i].vreg) > 0)
+ regulator_set_voltage(snode->reg_data[i].vreg, 0,
+ snode->reg_data[i].max_volt_uv);
+
+ regulator_put(snode->reg_data[i].vreg);
+ }
+
+ return rc;
+}
+
+static int qpnp_flash_led_regulator_parse_dt(struct qpnp_flash_led *led,
+ struct flash_switch_data *snode,
+ struct device_node *node) {
+
+ int i = 0, rc = 0, num_regs = 0;
+ struct device_node *temp = NULL;
+ const char *temp_string;
+ u32 val;
+
+ while ((temp = of_get_next_available_child(node, temp))) {
+ if (of_find_property(temp, "regulator-name", NULL))
+ num_regs++;
+ }
+ snode->num_regulators = num_regs;
+
+ if (snode->num_regulators == 0)
+ return 0;
+
+ snode->reg_data = devm_kcalloc(&led->pdev->dev, snode->num_regulators,
+ sizeof(*snode->reg_data),
+ GFP_KERNEL);
+ if (!snode->reg_data)
+ return -ENOMEM;
+
+ for_each_available_child_of_node(node, temp) {
+ rc = of_property_read_string(temp, "regulator-name",
+ &temp_string);
+ if (!rc)
+ snode->reg_data[i].reg_name = temp_string;
+ else {
+ dev_err(&led->pdev->dev,
+ "Unable to read regulator name, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(temp, "max-voltage-uv", &val);
+ if (!rc) {
+ snode->reg_data[i].max_volt_uv = val;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev,
+ "Unable to read max voltage, rc=%d\n", rc);
+ return rc;
+ }
+
+ i++;
+ }
+
+ return 0;
+}
+
static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
struct flash_node_data *fnode, struct device_node *node)
{
@@ -636,7 +768,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led,
struct device_node *node)
{
- int rc;
+ int rc = 0;
rc = of_property_read_string(node, "qcom,led-name",
&led->snode->cdev.name);
@@ -652,6 +784,21 @@ static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led,
return rc;
}
+ rc = qpnp_flash_led_regulator_parse_dt(led, led->snode, node);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "Unable to parse regulator data, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (led->snode->num_regulators) {
+ rc = qpnp_flash_led_regulator_setup(led, led->snode, true);
+ if (rc) {
+ dev_err(&led->pdev->dev, "Unable to setup regulator\n");
+ return rc;
+ }
+ }
+
led->snode->pdev = led->pdev;
led->snode->cdev.brightness_set = qpnp_flash_led_brightness_set;
led->snode->cdev.brightness_get = qpnp_flash_led_brightness_get;
@@ -807,6 +954,12 @@ static int qpnp_flash_led_remove(struct platform_device *pdev)
struct qpnp_flash_led *led = dev_get_drvdata(&pdev->dev);
int i = led->num_led_nodes;
+ if (led->snode->num_regulators) {
+ if (led->snode->regulator_on)
+ qpnp_flash_led_regulator_enable(led, led->snode, false);
+ qpnp_flash_led_regulator_setup(led, led->snode, false);
+ }
+
led_classdev_unregister(&led->snode->cdev);
while (i > 0)
led_classdev_unregister(&led->fnode[--i].cdev);
diff --git a/include/linux/leds-qpnp-flash-v2.h b/include/linux/leds-qpnp-flash-v2.h
index 38d3c9887354..1ff8781d3837 100644
--- a/include/linux/leds-qpnp-flash-v2.h
+++ b/include/linux/leds-qpnp-flash-v2.h
@@ -19,6 +19,12 @@
#define ENABLE_REGULATOR BIT(0)
#define QUERY_MAX_CURRENT BIT(1)
+struct flash_regulator_data {
+ struct regulator *vreg;
+ const char *reg_name;
+ u32 max_volt_uv;
+};
+
/*
* Configurations for each individual LED
*/
@@ -47,6 +53,9 @@ struct flash_node_data {
struct flash_switch_data {
struct platform_device *pdev;
struct led_classdev cdev;
+ struct flash_regulator_data *reg_data;
+ u8 num_regulators;
+ bool regulator_on;
};
int qpnp_flash_led_prepare(struct led_classdev *led_cdev, int options);