diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2016-11-15 04:07:25 -0800 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-11-15 04:07:25 -0800 |
commit | 4c014824b993b00f29eae7c8b43544d6b739bc85 (patch) | |
tree | 2f148fa84826f895965ffd16baac3ca61874f720 | |
parent | d05c65bf95abfb889d13c4be599306f1c04110d8 (diff) | |
parent | 561bfca197b2870ad3eeec764cce749e11995841 (diff) |
Merge "leds: qpnp-flash-v2: Add support for programming led clamp currents"
-rw-r--r-- | Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt | 32 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi | 8 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi | 8 | ||||
-rw-r--r-- | drivers/leds/leds-qpnp-flash-v2.c | 368 |
4 files changed, 218 insertions, 198 deletions
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt index 8365762e520f..8844a816052e 100644 --- a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt +++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt @@ -32,6 +32,18 @@ Optional properties: - 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,led1n2-iclamp-low-ma : Integer property to specify current clamp low + level for mitigation. Unit is mA. Allowed + values are same as under qcom,max-current. +- qcom,led1n2-iclamp-mid-ma : Integer property to specify current clamp mid + level for mitigation. Unit is mA. Allowed + values are same as under qcom,max-current. +- qcom,led3-iclamp-low-ma : Integer property to specify current clamp low + level for mitigation. Unit is mA. Allowed + values are same as under qcom,max-current. +- qcom,led3-iclamp-mid-ma : Integer property to specify current clamp mid + level for mitigation. Unit is mA. Allowed + values are same as under qcom,max-current. - qcom,vled-max-uv : Integer property for flash current predictive mitigation. Default value is 3500000 uV. - qcom,ibatt-ocp-threshold-ua : Integer property for flash current predictive mitigation. @@ -70,6 +82,8 @@ Optional properties: 0: Flash strobe is used for LED1, LED2, LED3 1: Flash strobe is used for LED1, LED2 and GPIO10 is used for LED3 2: Flash strobe is used for LED1; GPIO9 is used for LED2; GPIO10 is used for LED3 +- switchX-supply : phandle of the regulator that needs to be used + as a supply for flash switch_X device. Child node: Contains settings for each individual LED. Each LED channel needs a flash node and torch node for itself, and an individual switch node to serve as an overall switch. @@ -126,15 +140,6 @@ 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 { compatible = "qcom,qpnp-flash-led-v2"; @@ -160,6 +165,7 @@ Example: qcom,hdrm-auto-mode; qcom,isc-delay = <192>; + switch0-supply = <&pmicobalt_bob>; pmi8998_flash0: qcom,flash_0 { label = "flash"; @@ -254,10 +260,6 @@ Example: qcom,led-mask = <3>; qcom,default-led-trigger = "switch0_trigger"; - reg0 { - regulator-name = "pmicobalt_bob"; - max-voltage-uv = <3600000>; - }; }; pmi8998_switch1: qcom,led_switch_1 { @@ -266,10 +268,6 @@ Example: qcom,led-mask = <4>; qcom,default-led-trigger = "switch1_trigger"; - reg0 { - regulator-name = "pmicobalt_bob"; - max-voltage-uv = <3600000>; - }; }; }; diff --git a/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi b/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi index 41589d02f6fc..0e5a999e4642 100644 --- a/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi @@ -357,10 +357,6 @@ qcom,led-name = "led:switch_0"; qcom,led-mask = <3>; qcom,default-led-trigger = "switch0_trigger"; - reg0 { - regulator-name = "pmfalcon_bob"; - max-voltage-uv = <3600000>; - }; }; pm2falcon_switch1: qcom,led_switch_1 { @@ -368,10 +364,6 @@ qcom,led-name = "led:switch_1"; qcom,led-mask = <4>; qcom,default-led-trigger = "switch1_trigger"; - reg0 { - regulator-name = "pmfalcon_bob"; - max-voltage-uv = <3600000>; - }; }; }; }; diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi index b88b0e28c948..640aa53364a5 100644 --- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi @@ -744,10 +744,6 @@ qcom,led-name = "led:switch_0"; qcom,led-mask = <3>; qcom,default-led-trigger = "switch0_trigger"; - reg0 { - regulator-name = "pmicobalt_bob"; - max-voltage-uv = <3600000>; - }; }; pmicobalt_switch1: qcom,led_switch_1 { @@ -755,10 +751,6 @@ qcom,led-name = "led:switch_1"; qcom,led-mask = <4>; qcom,default-led-trigger = "switch1_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 51fd79f101c8..ea3d0eed47e1 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -49,6 +49,10 @@ #define FLASH_LED_REG_VPH_DROOP_THRESHOLD(base) (base + 0x61) #define FLASH_LED_REG_VPH_DROOP_DEBOUNCE(base) (base + 0x62) #define FLASH_LED_REG_ILED_GRT_THRSH(base) (base + 0x67) +#define FLASH_LED_REG_LED1N2_ICLAMP_LOW(base) (base + 0x68) +#define FLASH_LED_REG_LED1N2_ICLAMP_MID(base) (base + 0x69) +#define FLASH_LED_REG_LED3_ICLAMP_LOW(base) (base + 0x6A) +#define FLASH_LED_REG_LED3_ICLAMP_MID(base) (base + 0x6B) #define FLASH_LED_REG_MITIGATION_SEL(base) (base + 0x6E) #define FLASH_LED_REG_MITIGATION_SW(base) (base + 0x6F) #define FLASH_LED_REG_LMH_LEVEL(base) (base + 0x70) @@ -173,18 +177,12 @@ struct flash_node_data { bool led_on; }; -struct flash_regulator_data { - struct regulator *vreg; - const char *reg_name; - u32 max_volt_uv; -}; struct flash_switch_data { struct platform_device *pdev; + struct regulator *vreg; struct led_classdev cdev; - struct flash_regulator_data *reg_data; int led_mask; - int num_regulators; bool regulator_on; bool enabled; }; @@ -202,6 +200,10 @@ struct flash_led_platform_data { int rpara_uohm; int lmh_rbatt_threshold_uohm; int lmh_ocv_threshold_uv; + u32 led1n2_iclamp_low_ma; + u32 led1n2_iclamp_mid_ma; + u32 led3_iclamp_low_ma; + u32 led3_iclamp_mid_ma; u8 isc_delay; u8 warmup_delay; u8 current_derate_en_cfg; @@ -385,6 +387,46 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) if (rc < 0) return rc; + if (led->pdata->led1n2_iclamp_low_ma) { + val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_low_ma, + led->fnode[0].ires_ua); + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_LED1N2_ICLAMP_LOW(led->base), + FLASH_LED_CURRENT_MASK, val); + if (rc < 0) + return rc; + } + + if (led->pdata->led1n2_iclamp_mid_ma) { + val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_mid_ma, + led->fnode[0].ires_ua); + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_LED1N2_ICLAMP_MID(led->base), + FLASH_LED_CURRENT_MASK, val); + if (rc < 0) + return rc; + } + + if (led->pdata->led3_iclamp_low_ma) { + val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_low_ma, + led->fnode[3].ires_ua); + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_LED3_ICLAMP_LOW(led->base), + FLASH_LED_CURRENT_MASK, val); + if (rc < 0) + return rc; + } + + if (led->pdata->led3_iclamp_mid_ma) { + val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_mid_ma, + led->fnode[3].ires_ua); + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_LED3_ICLAMP_MID(led->base), + FLASH_LED_CURRENT_MASK, val); + if (rc < 0) + return rc; + } + return 0; } @@ -426,34 +468,27 @@ 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) { - int i, rc = 0; + int rc = 0; + + if (!snode || !snode->vreg) + return 0; if (snode->regulator_on == on) return 0; - if (on == false) { - i = snode->num_regulators; - goto out; - } + if (on) + rc = regulator_enable(snode->vreg); + else + rc = regulator_disable(snode->vreg); - 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; - } + if (rc < 0) { + dev_err(&led->pdev->dev, "regulator_%s failed, rc=%d\n", + on ? "enable" : "disable", rc); + return rc; } - snode->regulator_on = true; - return rc; - -out: - while (i--) - regulator_disable(snode->reg_data[i].vreg); - - snode->regulator_on = false; - return rc; + snode->regulator_on = on ? true : false; + return 0; } static int get_property_from_fg(struct qpnp_flash_led *led, @@ -835,7 +870,7 @@ static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on) u8 val, mask; if (snode->enabled == on) { - dev_warn(&led->pdev->dev, "Switch node is already %s!\n", + dev_dbg(&led->pdev->dev, "Switch node is already %s!\n", on ? "enabled" : "disabled"); return 0; } @@ -1050,6 +1085,32 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev, spin_unlock(&led->lock); } +/* sysfs show function for flash_max_current */ +static ssize_t qpnp_flash_led_max_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc; + struct flash_switch_data *snode; + struct qpnp_flash_led *led; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + snode = container_of(led_cdev, struct flash_switch_data, cdev); + led = dev_get_drvdata(&snode->pdev->dev); + + rc = qpnp_flash_led_get_max_avail_current(led); + if (rc < 0) + dev_err(&led->pdev->dev, "query max current failed, rc=%d\n", + rc); + + return snprintf(buf, PAGE_SIZE, "%d\n", rc); +} + +/* sysfs attributes exported by flash_led */ +static struct device_attribute qpnp_flash_led_attrs[] = { + __ATTR(max_current, (S_IRUGO | S_IWUSR | S_IWGRP), + qpnp_flash_led_max_current_show, NULL), +}; + static int flash_led_psy_notifier_call(struct notifier_block *nb, unsigned long ev, void *v) { @@ -1156,104 +1217,6 @@ 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) -{ - 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) { @@ -1440,7 +1403,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led, fnode->pinctrl = devm_pinctrl_get(fnode->cdev.dev); if (IS_ERR_OR_NULL(fnode->pinctrl)) { - dev_warn(&led->pdev->dev, "No pinctrl defined\n"); + dev_dbg(&led->pdev->dev, "No pinctrl defined\n"); fnode->pinctrl = NULL; } else { fnode->gpio_state_active = @@ -1471,7 +1434,8 @@ static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led, struct flash_switch_data *snode, struct device_node *node) { - int rc = 0; + int rc = 0, num; + char reg_name[16], reg_sup_name[16]; rc = of_property_read_string(node, "qcom,led-name", &snode->cdev.name); if (rc < 0) { @@ -1480,6 +1444,12 @@ static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led, return rc; } + rc = sscanf(snode->cdev.name, "led:switch_%d", &num); + if (!rc) { + pr_err("No number for switch device?\n"); + return -EINVAL; + } + rc = of_property_read_string(node, "qcom,default-led-trigger", &snode->cdev.default_trigger); if (rc < 0) { @@ -1499,18 +1469,16 @@ static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led, return -EINVAL; } - rc = qpnp_flash_led_regulator_parse_dt(led, snode, node); - if (rc < 0) { - dev_err(&led->pdev->dev, - "Unable to parse regulator data, rc=%d\n", rc); - return rc; - } - - if (snode->num_regulators) { - rc = qpnp_flash_led_regulator_setup(led, snode, true); - if (rc < 0) { - dev_err(&led->pdev->dev, - "Unable to setup regulator, rc=%d\n", rc); + scnprintf(reg_name, sizeof(reg_name), "switch%d-supply", num); + if (of_find_property(led->pdev->dev.of_node, reg_name, NULL)) { + scnprintf(reg_sup_name, sizeof(reg_sup_name), "switch%d", num); + snode->vreg = devm_regulator_get(&led->pdev->dev, reg_sup_name); + if (IS_ERR_OR_NULL(snode->vreg)) { + rc = PTR_ERR(snode->vreg); + if (rc != -EPROBE_DEFER) + dev_err(&led->pdev->dev, "Failed to get regulator, rc=%d\n", + rc); + snode->vreg = NULL; return rc; } } @@ -1651,6 +1619,42 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led, return rc; } + rc = of_property_read_u32(node, "qcom,led1n2-iclamp-low-ma", &val); + if (!rc) { + led->pdata->led1n2_iclamp_low_ma = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to read led1n2_iclamp_low current, rc=%d\n", + rc); + return rc; + } + + rc = of_property_read_u32(node, "qcom,led1n2-iclamp-mid-ma", &val); + if (!rc) { + led->pdata->led1n2_iclamp_mid_ma = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to read led1n2_iclamp_mid current, rc=%d\n", + rc); + return rc; + } + + rc = of_property_read_u32(node, "qcom,led3-iclamp-low-ma", &val); + if (!rc) { + led->pdata->led3_iclamp_low_ma = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to read led3_iclamp_low current, rc=%d\n", + rc); + return rc; + } + + rc = of_property_read_u32(node, "qcom,led3-iclamp-mid-ma", &val); + if (!rc) { + led->pdata->led3_iclamp_mid_ma = val; + } else if (rc != -EINVAL) { + dev_err(&led->pdev->dev, "Unable to read led3_iclamp_mid current, rc=%d\n", + rc); + return rc; + } + led->pdata->vled_max_uv = FLASH_LED_VLED_MAX_DEFAULT_UV; rc = of_property_read_u32(node, "qcom,vled-max-uv", &val); if (!rc) { @@ -1785,7 +1789,7 @@ static int qpnp_flash_led_probe(struct platform_device *pdev) struct device_node *node, *temp; const char *temp_string; unsigned int base; - int rc, i = 0; + int rc, i = 0, j = 0; node = pdev->dev.of_node; if (!node) { @@ -1863,26 +1867,35 @@ static int qpnp_flash_led_probe(struct platform_device *pdev) return -ENOMEM; temp = NULL; - for (i = 0; i < led->num_fnodes; i++) { - temp = of_get_next_available_child(node, temp); - rc = qpnp_flash_led_parse_each_led_dt(led, - &led->fnode[i], temp); + i = 0; + j = 0; + for_each_available_child_of_node(node, temp) { + rc = of_property_read_string(temp, "label", &temp_string); if (rc < 0) { dev_err(&pdev->dev, - "Unable to parse flash node %d rc=%d\n", i, rc); - goto error_led_register; + "Failed to parse label, rc=%d\n", rc); + return rc; } - } - for (i = 0; i < led->num_snodes; i++) { - temp = of_get_next_available_child(node, temp); - rc = qpnp_flash_led_parse_and_register_switch(led, - &led->snode[i], temp); - if (rc < 0) { - dev_err(&pdev->dev, - "Unable to parse and register switch node, rc=%d\n", - rc); - goto error_switch_register; + if (!strcmp("flash", temp_string) || + !strcmp("torch", temp_string)) { + rc = qpnp_flash_led_parse_each_led_dt(led, + &led->fnode[i++], temp); + if (rc < 0) { + dev_err(&pdev->dev, "Unable to parse flash node %d rc=%d\n", + i, rc); + goto error_led_register; + } + } + + if (!strcmp("switch", temp_string)) { + rc = qpnp_flash_led_parse_and_register_switch(led, + &led->snode[j++], temp); + if (rc < 0) { + dev_err(&pdev->dev, "Unable to parse and register switch node, rc=%d\n", + rc); + goto error_switch_register; + } } } @@ -1946,12 +1959,36 @@ static int qpnp_flash_led_probe(struct platform_device *pdev) goto unreg_notifier; } + for (i = 0; i < led->num_snodes; i++) { + for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) { + rc = sysfs_create_file(&led->snode[i].cdev.dev->kobj, + &qpnp_flash_led_attrs[j].attr); + if (rc < 0) { + dev_err(&pdev->dev, "sysfs creation failed, rc=%d\n", + rc); + goto sysfs_fail; + } + } + } + spin_lock_init(&led->lock); dev_set_drvdata(&pdev->dev, led); return 0; +sysfs_fail: + for (--j; j >= 0; j--) + sysfs_remove_file(&led->snode[i].cdev.dev->kobj, + &qpnp_flash_led_attrs[j].attr); + + for (--i; i >= 0; i--) { + for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) + sysfs_remove_file(&led->snode[i].cdev.dev->kobj, + &qpnp_flash_led_attrs[j].attr); + } + + i = led->num_snodes; unreg_notifier: power_supply_unreg_notifier(&led->nb); error_switch_register: @@ -1968,20 +2005,21 @@ error_led_register: static int qpnp_flash_led_remove(struct platform_device *pdev) { struct qpnp_flash_led *led = dev_get_drvdata(&pdev->dev); - int i; + int i, j; for (i = 0; i < led->num_snodes; i++) { - if (led->snode[i].num_regulators) { - if (led->snode[i].regulator_on) - qpnp_flash_led_regulator_enable(led, - &led->snode[i], false); - qpnp_flash_led_regulator_setup(led, + for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) + sysfs_remove_file(&led->snode[i].cdev.dev->kobj, + &qpnp_flash_led_attrs[j].attr); + + if (led->snode[i].regulator_on) + qpnp_flash_led_regulator_enable(led, &led->snode[i], false); - } } while (i > 0) led_classdev_unregister(&led->snode[--i].cdev); + i = led->num_fnodes; while (i > 0) led_classdev_unregister(&led->fnode[--i].cdev); |