diff options
author | Devesh Jhunjhunwala <deveshj@codeaurora.org> | 2016-06-02 11:59:58 -0700 |
---|---|---|
committer | Kyle Yan <kyan@codeaurora.org> | 2016-07-11 18:56:23 -0700 |
commit | 94d2a9fd5dc8a51a4b43e82bd2271d209bd132c2 (patch) | |
tree | 9cafaade29e7d3659ccca0154781987d9fa511bb | |
parent | 01d202d756900e3f0f88bf9b184dbcad6874b41b (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.txt | 12 | ||||
-rw-r--r-- | drivers/leds/leds-qpnp-flash-v2.c | 157 | ||||
-rw-r--r-- | include/linux/leds-qpnp-flash-v2.h | 9 |
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); |