- Limited LED current based on thermal conditions in the QCOM flash LED driver.

- Fixed device child node usage in the BD2606MVV and PCA995x drivers.
 - Used device_for_each_child_node_scoped() to access child nodes in the IS31FL319X driver.
 - Reset the LED controller during the probe in the LM3601X driver.
 - Used device_for_each_child_node() to access device child nodes in the PCA995X driver.
 - Fixed CONFIG_LEDS_CLASS_MULTICOLOR dependency in the BlinkM driver.
 - Replaced msleep() with usleep_range() in the SUN50I-A100 driver.
 - Used scoped device node handling to simplify error paths in the AAT1290, KTD2692, and MC13783 drivers.
 - Added missing of_node_get for probe duration in the MAX77693 driver.
 - Simplified using for_each_available_child_of_node_scoped() loops when iterating over device nodes.
 - Used devm_clk_get_enabled() helpers in the LP55XX driver.
 - Converted DT bindings from TXT to YAML format for various drivers, including LM3692x and SC2731-BLTC.
 - Set num_leds after allocation in the GPIO driver.
 - Removed irrelevant blink configuration error message in the PCA9532 driver.
 - Fixed module autoloading with MODULE_DEVICE_TABLE() in the Turris Omnia driver.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmbxjRMACgkQUa+KL4f8
 d2Ey+Q//WsNhqFE6YQmWIuqd9PznAb64o98JeWWymgM/W4DJkQPpVlK1+ceTWVCW
 OsE8017oiuqD1psK6dkAF+NGB85YUoCSHp1ymPrjN3BYFJfu95xLaHCt7qvKvsCu
 HR29dvscgubxwns5imxtbelwjSbQRxP+qfggyhwgpj6lQ0s5W2mSmKAU45zvnInI
 x4Hn/3cfqCkiJateqGj/tz49C/P2P1y3MXC9V3FBTQEWSsZt/BzMQvV/lxl0Ommn
 +5WAnBRmGN5/PhTpaIl/nD5XgSM3cnTA8rqz4EORQmr4X2nstAvsTj0KEXf8zs7g
 k/VcKmYnlraRJTIkC1kb8FalO7FsF+ubHxkMltEmvsNWEa2b5nhLEpH3RdA+U7B3
 0PpSnO814ii8QuTvbALgwIre5N4kOn0oVS7FMIfDcNy2K4y/RP18xO5Qxc1f5Ssl
 eM/nOBoNISvuLzAnb3AZnMzJiHqiQlH7VW8bUnjb7FcIZqNaKllF5PXhSuSopSvi
 9M9TrHjd7rZ9l7iBlj2Td7ZVNZ092TkwQcaPRyybIAONqWZ101UNlLL/Bxa0UNJ+
 agExjqIQgGDHJCVQrXQAklGVFTgOwjx8qDcuY5r7CTwBEDKBwUQqbIdVa05e1eqq
 Y8mRc0Fao1sZh6ZG1GNFeaFh3aLLcrFMRIa/qqovciKXa/VoNXQ=
 =a/t6
 -----END PGP SIGNATURE-----

Merge tag 'leds-next-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds

Pull LED updates from Lee Jones:

 - Limited LED current based on thermal conditions in the QCOM flash LED
   driver

 - Fixed device child node usage in the BD2606MVV and PCA995x drivers

 - Used device_for_each_child_node_scoped() to access child nodes in the
   IS31FL319X driver

 - Reset the LED controller during the probe in the LM3601X driver

 - Used device_for_each_child_node() to access device child nodes in the
   PCA995X driver

 - Fixed CONFIG_LEDS_CLASS_MULTICOLOR dependency in the BlinkM driver

 - Replaced msleep() with usleep_range() in the SUN50I-A100 driver

 - Used scoped device node handling to simplify error paths in the
   AAT1290, KTD2692, and MC13783 drivers

 - Added missing of_node_get for probe duration in the MAX77693 driver

 - Simplified using for_each_available_child_of_node_scoped() loops when
   iterating over device nodes

 - Used devm_clk_get_enabled() helpers in the LP55XX driver

 - Converted DT bindings from TXT to YAML format for various drivers,
   including LM3692x and SC2731-BLTC

 - Set num_leds after allocation in the GPIO driver

 - Removed irrelevant blink configuration error message in the PCA9532
   driver

 - Fixed module autoloading with MODULE_DEVICE_TABLE() in the Turris
   Omnia driver

* tag 'leds-next-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds: (38 commits)
  leds: turris-omnia: Fix module autoloading with MODULE_DEVICE_TABLE()
  leds: pca9532: Remove irrelevant blink configuration error message
  leds: gpio: Set num_leds after allocation
  dt-bindings: leds: Convert leds-lm3692x to YAML format
  leds: lp55xx: Use devm_clk_get_enabled() helpers
  leds: as3645a: Use device_* to iterate over device child nodes
  leds: qcom-lpg: Simplify with scoped for each OF child loop
  leds: turris-omnia: Simplify with scoped for each OF child loop
  leds: sc27xx: Simplify with scoped for each OF child loop
  leds: pca9532: Simplify with scoped for each OF child loop
  leds: netxbig: Simplify with scoped for each OF child loop
  leds: mt6323: Simplify with scoped for each OF child loop
  leds: mc13783: Use scoped device node handling to simplify error paths
  leds: lp55xx: Simplify with scoped for each OF child loop
  leds: is31fl32xx: Simplify with scoped for each OF child loop
  leds: bcm6358: Simplify with scoped for each OF child loop
  leds: bcm6328: Simplify with scoped for each OF child loop
  leds: aw2013: Simplify with scoped for each OF child loop
  leds: 88pm860x: Simplify with scoped for each OF child loop
  leds: max77693: Simplify with scoped for each OF child loop
  ...
This commit is contained in:
Linus Torvalds 2024-09-23 14:20:11 -07:00
commit f2debe057f
37 changed files with 778 additions and 428 deletions

View File

@ -113,6 +113,8 @@ properties:
# LED indicates NAND memory activity (deprecated), # LED indicates NAND memory activity (deprecated),
# in new implementations use "mtd" # in new implementations use "mtd"
- nand-disk - nand-disk
# LED indicates network activity
- netdev
# No trigger assigned to the LED. This is the default mode # No trigger assigned to the LED. This is the default mode
# if trigger is absent # if trigger is absent
- none - none

View File

@ -1,65 +0,0 @@
* Texas Instruments - LM3692x Highly Efficient White LED Driver
The LM3692x is an ultra-compact, highly efficient,
white-LED driver designed for LCD display backlighting.
The main difference between the LM36922 and LM36923 is the number of
LED strings it supports. The LM36922 supports two strings while the LM36923
supports three strings.
Required properties:
- compatible:
"ti,lm36922"
"ti,lm36923"
- reg : I2C slave address
- #address-cells : 1
- #size-cells : 0
Optional properties:
- enable-gpios : gpio pin to enable/disable the device.
- vled-supply : LED supply
- ti,ovp-microvolt: Overvoltage protection in
micro-volt, can be 17000000, 21000000, 25000000 or
29000000. If ti,ovp-microvolt is not specified it
defaults to 29000000.
Required child properties:
- reg : 0 - Will enable all LED sync paths
1 - Will enable the LED1 sync
2 - Will enable the LED2 sync
3 - Will enable the LED3 sync (LM36923 only)
Optional child properties:
- function : see Documentation/devicetree/bindings/leds/common.txt
- color : see Documentation/devicetree/bindings/leds/common.txt
- label : see Documentation/devicetree/bindings/leds/common.txt (deprecated)
- linux,default-trigger :
see Documentation/devicetree/bindings/leds/common.txt
- led-max-microamp :
see Documentation/devicetree/bindings/leds/common.txt
Example:
#include <dt-bindings/leds/common.h>
led-controller@36 {
compatible = "ti,lm3692x";
reg = <0x36>;
#address-cells = <1>;
#size-cells = <0>;
enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
vled-supply = <&vbatt>;
ti,ovp-microvolt = <29000000>;
led@0 {
reg = <0>;
function = LED_FUNCTION_BACKLIGHT;
color = <LED_COLOR_ID_WHITE>;
linux,default-trigger = "backlight";
led-max-microamp = <20000>;
};
}
For more product information please see the link below:
https://www.ti.com/lit/ds/snvsa29/snvsa29.pdf

View File

@ -1,43 +0,0 @@
LEDs connected to Spreadtrum SC27XX PMIC breathing light controller
The SC27xx breathing light controller supports to 3 outputs:
red LED, green LED and blue LED. Each LED can work at normal
PWM mode or breath light mode.
Required properties:
- compatible: Should be "sprd,sc2731-bltc".
- #address-cells: Must be 1.
- #size-cells: Must be 0.
- reg: Specify the controller address.
Required child properties:
- reg: Port this LED is connected to.
Optional child properties:
- function: See Documentation/devicetree/bindings/leds/common.txt.
- color: See Documentation/devicetree/bindings/leds/common.txt.
- label: See Documentation/devicetree/bindings/leds/common.txt (deprecated).
Examples:
led-controller@200 {
compatible = "sprd,sc2731-bltc";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x200>;
led@0 {
color = <LED_COLOR_ID_RED>;
reg = <0x0>;
};
led@1 {
color = <LED_COLOR_ID_GREEN>;
reg = <0x1>;
};
led@2 {
color = <LED_COLOR_ID_BLUE>;
reg = <0x2>;
};
};

View File

@ -11,19 +11,21 @@ maintainers:
- Marek Vasut <marex@denx.de> - Marek Vasut <marex@denx.de>
description: description:
The NXP PCA9952/PCA9955B are programmable LED controllers connected via I2C The NXP PCA995x family are programmable LED controllers connected via I2C
that can drive 16 separate lines. Each of them can be individually switched that can drive separate lines. Each of them can be individually switched
on and off, and brightness can be controlled via individual PWM. on and off, and brightness can be controlled via individual PWM.
Datasheets are available at Datasheets are available at
https://www.nxp.com/docs/en/data-sheet/PCA9952_PCA9955.pdf https://www.nxp.com/docs/en/data-sheet/PCA9952_PCA9955.pdf
https://www.nxp.com/docs/en/data-sheet/PCA9955B.pdf https://www.nxp.com/docs/en/data-sheet/PCA9955B.pdf
https://www.nxp.com/docs/en/data-sheet/PCA9956B.pdf
properties: properties:
compatible: compatible:
enum: enum:
- nxp,pca9952 - nxp,pca9952
- nxp,pca9955b - nxp,pca9955b
- nxp,pca9956b
reg: reg:
maxItems: 1 maxItems: 1

View File

@ -0,0 +1,84 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/leds/sprd,sc2731-bltc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Spreadtrum SC2731 PMIC breathing light controller
maintainers:
- Orson Zhai <orsonzhai@gmail.com>
- Baolin Wang <baolin.wang7@gmail.com>
- Chunyan Zhang <zhang.lyra@gmail.com>
description: |
The SC2731 breathing light controller supports up to 3 outputs:
red LED, green LED and blue LED. Each LED can work at normal PWM mode
or breath light mode.
properties:
compatible:
const: sprd,sc2731-bltc
reg:
maxItems: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^led@[0-2]$":
type: object
$ref: common.yaml#
unevaluatedProperties: false
properties:
reg:
minimum: 0
maximum: 2
required:
- reg
required:
- compatible
- reg
- '#address-cells'
- '#size-cells'
additionalProperties: false
examples:
- |
#include <dt-bindings/leds/common.h>
pmic {
#address-cells = <1>;
#size-cells = <0>;
led-controller@200 {
compatible = "sprd,sc2731-bltc";
reg = <0x200>;
#address-cells = <1>;
#size-cells = <0>;
led@0 {
reg = <0x0>;
color = <LED_COLOR_ID_RED>;
};
led@1 {
reg = <0x1>;
color = <LED_COLOR_ID_GREEN>;
};
led@2 {
reg = <0x2>;
color = <LED_COLOR_ID_BLUE>;
};
};
};
...

View File

@ -0,0 +1,110 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/leds/ti.lm36922.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments - LM3692x Highly Efficient White LED Driver
maintainers:
- Dan Murphy <dmurphy@ti.com>
description: |
The LM3692x is an ultra-compact, highly efficient,
white-LED driver designed for LCD display backlighting.
The main difference between the LM36922 and LM36923 is the number of
LED strings it supports. The LM36922 supports two strings while the LM36923
supports three strings.
For more product information please see the link below:
https://www.ti.com/lit/ds/snvsa29/snvsa29.pdf
properties:
compatible:
enum:
- ti,lm36922
- ti,lm36923
reg:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 0
enable-gpios:
description: gpio pin to enable/disable the device.
vled-supply:
description: LED supply
ti,ovp-microvolt:
description: Overvoltage protection.
default: 29000000
enum: [17000000, 21000000, 25000000, 29000000]
patternProperties:
'^led@[0-3]$':
type: object
$ref: common.yaml
properties:
reg:
enum: [0, 1, 2, 3]
description: |
0 - Will enable all LED sync paths
1 - Will enable the LED1 sync
2 - Will enable the LED2 sync
3 - Will enable the LED3 sync (LM36923 only)
unevaluatedProperties: false
required:
- compatible
- reg
- "#address-cells"
- "#size-cells"
allOf:
- if:
properties:
compatible:
contains:
const: ti,lm36922
then:
properties:
led@3: false
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/leds/common.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
led-controller@36 {
compatible = "ti,lm36922";
reg = <0x36>;
#address-cells = <1>;
#size-cells = <0>;
enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
vled-supply = <&vbatt>;
ti,ovp-microvolt = <29000000>;
led@0 {
reg = <0>;
function = LED_FUNCTION_BACKLIGHT;
color = <LED_COLOR_ID_WHITE>;
linux,default-trigger = "backlight";
led-max-microamp = <20000>;
};
};
};

View File

@ -13,9 +13,31 @@ The device accepts RGB and HSB color values through separate commands.
Also you can store blinking sequences as "scripts" in Also you can store blinking sequences as "scripts" in
the controller and run them. Also fading is an option. the controller and run them. Also fading is an option.
The interface this driver provides is 2-fold: The interface this driver provides is 3-fold:
a) LED class interface for use with triggers a) LED multicolor class interface for use with triggers
#######################################################
The registration follows the scheme::
blinkm-<i2c-bus-nr>-<i2c-device-nr>:rgb:indicator
$ ls -h /sys/class/leds/blinkm-1-9:rgb:indicator
brightness device max_brightness multi_index multi_intensity power subsystem trigger uevent
Hue is controlled by the multi_intensity file and lightness is controlled by
the brightness file.
The order in which to write the intensity values can be found in multi_index.
Exactly three values between 0 and 255 must be written to multi_intensity to
change the color::
$ echo 255 100 50 > multi_intensity
The overall lightness be changed by writing a value between 0 and 255 to the
brightness file.
b) LED class interface for use with triggers
############################################ ############################################
The registration follows the scheme:: The registration follows the scheme::
@ -79,6 +101,7 @@ E.g.::
as of 6/2012 as of 07/2024
dl9pf <at> gmx <dot> de dl9pf <at> gmx <dot> de
jstrauss <at> mailbox <dot> org

View File

@ -72,6 +72,14 @@ Good: "platform:*:charging" (allwinner sun50i, leds-cht-wcove)
Good: ":backlight" (Motorola Droid 4) Good: ":backlight" (Motorola Droid 4)
* Indicators
Good: ":indicator" (Blinkm)
* RGB
Good: ":rgb" (Blinkm)
* Ethernet LEDs * Ethernet LEDs
Currently two types of Network LEDs are support, those controlled by Currently two types of Network LEDs are support, those controlled by

View File

@ -825,6 +825,14 @@ config LEDS_BLINKM
This option enables support for the BlinkM RGB LED connected This option enables support for the BlinkM RGB LED connected
through I2C. Say Y to enable support for the BlinkM LED. through I2C. Say Y to enable support for the BlinkM LED.
config LEDS_BLINKM_MULTICOLOR
bool "Enable multicolor support for BlinkM I2C RGB LED"
depends on LEDS_BLINKM
depends on LEDS_CLASS_MULTICOLOR=y || LEDS_CLASS_MULTICOLOR=LEDS_BLINKM
help
This option enables multicolor sysfs class support for BlinkM LED and
disables the older, separated sysfs interface
config LEDS_POWERNV config LEDS_POWERNV
tristate "LED support for PowerNV Platform" tristate "LED support for PowerNV Platform"
depends on LEDS_CLASS depends on LEDS_CLASS

View File

@ -7,6 +7,7 @@
* Author: Jacek Anaszewski <j.anaszewski@samsung.com> * Author: Jacek Anaszewski <j.anaszewski@samsung.com>
*/ */
#include <linux/cleanup.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/led-class-flash.h> #include <linux/led-class-flash.h>
@ -215,7 +216,6 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
struct device_node **sub_node) struct device_node **sub_node)
{ {
struct device *dev = &led->pdev->dev; struct device *dev = &led->pdev->dev;
struct device_node *child_node;
#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
struct pinctrl *pinctrl; struct pinctrl *pinctrl;
#endif #endif
@ -246,7 +246,8 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
} }
#endif #endif
child_node = of_get_next_available_child(dev_of_node(dev), NULL); struct device_node *child_node __free(device_node) =
of_get_next_available_child(dev_of_node(dev), NULL);
if (!child_node) { if (!child_node) {
dev_err(dev, "No DT child node found for connected LED.\n"); dev_err(dev, "No DT child node found for connected LED.\n");
return -EINVAL; return -EINVAL;
@ -267,7 +268,7 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
if (ret < 0) { if (ret < 0) {
dev_err(dev, dev_err(dev,
"flash-max-microamp DT property missing\n"); "flash-max-microamp DT property missing\n");
goto err_parse_dt; return ret;
} }
ret = of_property_read_u32(child_node, "flash-max-timeout-us", ret = of_property_read_u32(child_node, "flash-max-timeout-us",
@ -275,15 +276,12 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
if (ret < 0) { if (ret < 0) {
dev_err(dev, dev_err(dev,
"flash-max-timeout-us DT property missing\n"); "flash-max-timeout-us DT property missing\n");
goto err_parse_dt; return ret;
} }
*sub_node = child_node; *sub_node = child_node;
err_parse_dt: return 0;
of_node_put(child_node);
return ret;
} }
static void aat1290_led_validate_mm_current(struct aat1290_led *led, static void aat1290_led_validate_mm_current(struct aat1290_led *led,

View File

@ -478,14 +478,12 @@ static int as3645a_detect(struct as3645a *flash)
return as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE); return as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE);
} }
static int as3645a_parse_node(struct as3645a *flash, static int as3645a_parse_node(struct device *dev, struct as3645a *flash)
struct fwnode_handle *fwnode)
{ {
struct as3645a_config *cfg = &flash->cfg; struct as3645a_config *cfg = &flash->cfg;
struct fwnode_handle *child;
int rval; int rval;
fwnode_for_each_child_node(fwnode, child) { device_for_each_child_node_scoped(dev, child) {
u32 id = 0; u32 id = 0;
fwnode_property_read_u32(child, "reg", &id); fwnode_property_read_u32(child, "reg", &id);
@ -686,7 +684,7 @@ static int as3645a_probe(struct i2c_client *client)
flash->client = client; flash->client = client;
rval = as3645a_parse_node(flash, dev_fwnode(&client->dev)); rval = as3645a_parse_node(&client->dev, flash);
if (rval < 0) if (rval < 0)
return rval; return rval;

View File

@ -6,6 +6,7 @@
* Ingi Kim <ingi2.kim@samsung.com> * Ingi Kim <ingi2.kim@samsung.com>
*/ */
#include <linux/cleanup.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/leds-expresswire.h> #include <linux/leds-expresswire.h>
@ -208,7 +209,6 @@ static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
struct ktd2692_led_config_data *cfg) struct ktd2692_led_config_data *cfg)
{ {
struct device_node *np = dev_of_node(dev); struct device_node *np = dev_of_node(dev);
struct device_node *child_node;
int ret; int ret;
if (!np) if (!np)
@ -239,7 +239,8 @@ static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
} }
} }
child_node = of_get_next_available_child(np, NULL); struct device_node *child_node __free(device_node) =
of_get_next_available_child(np, NULL);
if (!child_node) { if (!child_node) {
dev_err(dev, "No DT child node found for connected LED.\n"); dev_err(dev, "No DT child node found for connected LED.\n");
return -EINVAL; return -EINVAL;
@ -252,26 +253,24 @@ static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
&cfg->movie_max_microamp); &cfg->movie_max_microamp);
if (ret) { if (ret) {
dev_err(dev, "failed to parse led-max-microamp\n"); dev_err(dev, "failed to parse led-max-microamp\n");
goto err_parse_dt; return ret;
} }
ret = of_property_read_u32(child_node, "flash-max-microamp", ret = of_property_read_u32(child_node, "flash-max-microamp",
&cfg->flash_max_microamp); &cfg->flash_max_microamp);
if (ret) { if (ret) {
dev_err(dev, "failed to parse flash-max-microamp\n"); dev_err(dev, "failed to parse flash-max-microamp\n");
goto err_parse_dt; return ret;
} }
ret = of_property_read_u32(child_node, "flash-max-timeout-us", ret = of_property_read_u32(child_node, "flash-max-timeout-us",
&cfg->flash_max_timeout); &cfg->flash_max_timeout);
if (ret) { if (ret) {
dev_err(dev, "failed to parse flash-max-timeout-us\n"); dev_err(dev, "failed to parse flash-max-timeout-us\n");
goto err_parse_dt; return ret;
} }
err_parse_dt: return 0;
of_node_put(child_node);
return ret;
} }
static const struct led_flash_ops flash_ops = { static const struct led_flash_ops flash_ops = {

View File

@ -190,7 +190,7 @@ static int lm3601x_brightness_set(struct led_classdev *cdev,
goto out; goto out;
} }
ret = regmap_write(led->regmap, LM3601X_LED_TORCH_REG, brightness); ret = regmap_write(led->regmap, LM3601X_LED_TORCH_REG, brightness - 1);
if (ret < 0) if (ret < 0)
goto out; goto out;
@ -341,7 +341,8 @@ static int lm3601x_register_leds(struct lm3601x_led *led,
led_cdev = &led->fled_cdev.led_cdev; led_cdev = &led->fled_cdev.led_cdev;
led_cdev->brightness_set_blocking = lm3601x_brightness_set; led_cdev->brightness_set_blocking = lm3601x_brightness_set;
led_cdev->max_brightness = DIV_ROUND_UP(led->torch_current_max, led_cdev->max_brightness =
DIV_ROUND_UP(led->torch_current_max - LM3601X_MIN_TORCH_I_UA + 1,
LM3601X_TORCH_REG_DIV); LM3601X_TORCH_REG_DIV);
led_cdev->flags |= LED_DEV_CAP_FLASH; led_cdev->flags |= LED_DEV_CAP_FLASH;
@ -386,6 +387,14 @@ static int lm3601x_parse_node(struct lm3601x_led *led,
goto out_err; goto out_err;
} }
if (led->torch_current_max > LM3601X_MAX_TORCH_I_UA) {
dev_warn(&led->client->dev,
"Max torch current set too high (%d vs %d)\n",
led->torch_current_max,
LM3601X_MAX_TORCH_I_UA);
led->torch_current_max = LM3601X_MAX_TORCH_I_UA;
}
ret = fwnode_property_read_u32(child, "flash-max-microamp", ret = fwnode_property_read_u32(child, "flash-max-microamp",
&led->flash_current_max); &led->flash_current_max);
if (ret) { if (ret) {
@ -434,6 +443,10 @@ static int lm3601x_probe(struct i2c_client *client)
return ret; return ret;
} }
ret = regmap_write(led->regmap, LM3601X_DEV_ID_REG, LM3601X_SW_RESET);
if (ret)
dev_warn(&client->dev, "Failed to reset the LED controller\n");
mutex_init(&led->lock); mutex_init(&led->lock);
return lm3601x_register_leds(led, fwnode); return lm3601x_register_leds(led, fwnode);

View File

@ -599,7 +599,7 @@ static int max77693_led_parse_dt(struct max77693_led_device *led,
{ {
struct device *dev = &led->pdev->dev; struct device *dev = &led->pdev->dev;
struct max77693_sub_led *sub_leds = led->sub_leds; struct max77693_sub_led *sub_leds = led->sub_leds;
struct device_node *node = dev_of_node(dev), *child_node; struct device_node *node = dev_of_node(dev);
struct property *prop; struct property *prop;
u32 led_sources[2]; u32 led_sources[2];
int i, ret, fled_id; int i, ret, fled_id;
@ -608,7 +608,7 @@ static int max77693_led_parse_dt(struct max77693_led_device *led,
of_property_read_u32(node, "maxim,boost-mvout", &cfg->boost_vout); of_property_read_u32(node, "maxim,boost-mvout", &cfg->boost_vout);
of_property_read_u32(node, "maxim,mvsys-min", &cfg->low_vsys); of_property_read_u32(node, "maxim,mvsys-min", &cfg->low_vsys);
for_each_available_child_of_node(node, child_node) { for_each_available_child_of_node_scoped(node, child_node) {
prop = of_find_property(child_node, "led-sources", NULL); prop = of_find_property(child_node, "led-sources", NULL);
if (prop) { if (prop) {
const __be32 *srcs = NULL; const __be32 *srcs = NULL;
@ -622,7 +622,6 @@ static int max77693_led_parse_dt(struct max77693_led_device *led,
} else { } else {
dev_err(dev, dev_err(dev,
"led-sources DT property missing\n"); "led-sources DT property missing\n");
of_node_put(child_node);
return -EINVAL; return -EINVAL;
} }
@ -638,18 +637,16 @@ static int max77693_led_parse_dt(struct max77693_led_device *led,
} else { } else {
dev_err(dev, dev_err(dev,
"Wrong led-sources DT property value.\n"); "Wrong led-sources DT property value.\n");
of_node_put(child_node);
return -EINVAL; return -EINVAL;
} }
if (sub_nodes[fled_id]) { if (sub_nodes[fled_id]) {
dev_err(dev, dev_err(dev,
"Conflicting \"led-sources\" DT properties\n"); "Conflicting \"led-sources\" DT properties\n");
of_node_put(child_node);
return -EINVAL; return -EINVAL;
} }
sub_nodes[fled_id] = child_node; sub_nodes[fled_id] = of_node_get(child_node);
sub_leds[fled_id].fled_id = fled_id; sub_leds[fled_id].fled_id = fled_id;
cfg->label[fled_id] = cfg->label[fled_id] =
@ -681,11 +678,9 @@ static int max77693_led_parse_dt(struct max77693_led_device *led,
if (++cfg->num_leds == 2 || if (++cfg->num_leds == 2 ||
(max77693_fled_used(led, FLED1) && (max77693_fled_used(led, FLED1) &&
max77693_fled_used(led, FLED2))) { max77693_fled_used(led, FLED2)))
of_node_put(child_node);
break; break;
} }
}
if (cfg->num_leds == 0) { if (cfg->num_leds == 0) {
dev_err(dev, "No DT child node found for connected LED(s).\n"); dev_err(dev, "No DT child node found for connected LED(s).\n");
@ -968,7 +963,7 @@ static int max77693_led_probe(struct platform_device *pdev)
ret = max77693_setup(led, &led_cfg); ret = max77693_setup(led, &led_cfg);
if (ret < 0) if (ret < 0)
return ret; goto err_setup;
mutex_init(&led->lock); mutex_init(&led->lock);
@ -1000,6 +995,8 @@ static int max77693_led_probe(struct platform_device *pdev)
else else
goto err_register_led1; goto err_register_led1;
} }
of_node_put(sub_nodes[i]);
sub_nodes[i] = NULL;
} }
return 0; return 0;
@ -1013,6 +1010,9 @@ err_register_led2:
err_register_led1: err_register_led1:
mutex_destroy(&led->lock); mutex_destroy(&led->lock);
err_setup:
for (i = FLED1; i <= FLED2; i++)
of_node_put(sub_nodes[i]);
return ret; return ret;
} }

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/ */
#include <linux/bitfield.h> #include <linux/bitfield.h>
@ -14,6 +14,9 @@
#include <media/v4l2-flash-led-class.h> #include <media/v4l2-flash-led-class.h>
/* registers definitions */ /* registers definitions */
#define FLASH_REVISION_REG 0x00
#define FLASH_4CH_REVISION_V0P1 0x01
#define FLASH_TYPE_REG 0x04 #define FLASH_TYPE_REG 0x04
#define FLASH_TYPE_VAL 0x18 #define FLASH_TYPE_VAL 0x18
@ -73,6 +76,16 @@
#define UA_PER_MA 1000 #define UA_PER_MA 1000
/* thermal threshold constants */
#define OTST_3CH_MIN_VAL 3
#define OTST1_4CH_MIN_VAL 0
#define OTST1_4CH_V0P1_MIN_VAL 3
#define OTST2_4CH_MIN_VAL 0
#define OTST1_MAX_CURRENT_MA 1000
#define OTST2_MAX_CURRENT_MA 500
#define OTST3_MAX_CURRENT_MA 200
enum hw_type { enum hw_type {
QCOM_MVFLASH_3CH, QCOM_MVFLASH_3CH,
QCOM_MVFLASH_4CH, QCOM_MVFLASH_4CH,
@ -98,6 +111,9 @@ enum {
REG_IRESOLUTION, REG_IRESOLUTION,
REG_CHAN_STROBE, REG_CHAN_STROBE,
REG_CHAN_EN, REG_CHAN_EN,
REG_THERM_THRSH1,
REG_THERM_THRSH2,
REG_THERM_THRSH3,
REG_MAX_COUNT, REG_MAX_COUNT,
}; };
@ -111,6 +127,9 @@ static struct reg_field mvflash_3ch_regs[REG_MAX_COUNT] = {
REG_FIELD(0x47, 0, 5), /* iresolution */ REG_FIELD(0x47, 0, 5), /* iresolution */
REG_FIELD_ID(0x49, 0, 2, 3, 1), /* chan_strobe */ REG_FIELD_ID(0x49, 0, 2, 3, 1), /* chan_strobe */
REG_FIELD(0x4c, 0, 2), /* chan_en */ REG_FIELD(0x4c, 0, 2), /* chan_en */
REG_FIELD(0x56, 0, 2), /* therm_thrsh1 */
REG_FIELD(0x57, 0, 2), /* therm_thrsh2 */
REG_FIELD(0x58, 0, 2), /* therm_thrsh3 */
}; };
static struct reg_field mvflash_4ch_regs[REG_MAX_COUNT] = { static struct reg_field mvflash_4ch_regs[REG_MAX_COUNT] = {
@ -123,6 +142,8 @@ static struct reg_field mvflash_4ch_regs[REG_MAX_COUNT] = {
REG_FIELD(0x49, 0, 3), /* iresolution */ REG_FIELD(0x49, 0, 3), /* iresolution */
REG_FIELD_ID(0x4a, 0, 6, 4, 1), /* chan_strobe */ REG_FIELD_ID(0x4a, 0, 6, 4, 1), /* chan_strobe */
REG_FIELD(0x4e, 0, 3), /* chan_en */ REG_FIELD(0x4e, 0, 3), /* chan_en */
REG_FIELD(0x7a, 0, 2), /* therm_thrsh1 */
REG_FIELD(0x78, 0, 2), /* therm_thrsh2 */
}; };
struct qcom_flash_data { struct qcom_flash_data {
@ -130,9 +151,11 @@ struct qcom_flash_data {
struct regmap_field *r_fields[REG_MAX_COUNT]; struct regmap_field *r_fields[REG_MAX_COUNT];
struct mutex lock; struct mutex lock;
enum hw_type hw_type; enum hw_type hw_type;
u32 total_ma;
u8 leds_count; u8 leds_count;
u8 max_channels; u8 max_channels;
u8 chan_en_bits; u8 chan_en_bits;
u8 revision;
}; };
struct qcom_flash_led { struct qcom_flash_led {
@ -143,6 +166,7 @@ struct qcom_flash_led {
u32 max_timeout_ms; u32 max_timeout_ms;
u32 flash_current_ma; u32 flash_current_ma;
u32 flash_timeout_ms; u32 flash_timeout_ms;
u32 current_in_use_ma;
u8 *chan_id; u8 *chan_id;
u8 chan_count; u8 chan_count;
bool enabled; bool enabled;
@ -172,6 +196,127 @@ static int set_flash_module_en(struct qcom_flash_led *led, bool en)
return rc; return rc;
} }
static int update_allowed_flash_current(struct qcom_flash_led *led, u32 *current_ma, bool strobe)
{
struct qcom_flash_data *flash_data = led->flash_data;
u32 therm_ma, avail_ma, thrsh[3], min_thrsh, sts;
int rc = 0;
mutex_lock(&flash_data->lock);
/*
* Put previously allocated current into allowed budget in either of these two cases:
* 1) LED is disabled;
* 2) LED is enabled repeatedly
*/
if (!strobe || led->current_in_use_ma != 0) {
if (flash_data->total_ma >= led->current_in_use_ma)
flash_data->total_ma -= led->current_in_use_ma;
else
flash_data->total_ma = 0;
led->current_in_use_ma = 0;
if (!strobe)
goto unlock;
}
/*
* Cache the default thermal threshold settings, and set them to the lowest levels before
* reading over-temp real time status. If over-temp has been triggered at the lowest
* threshold, it's very likely that it would be triggered at a higher (default) threshold
* when more flash current is requested. Prevent device from triggering over-temp condition
* by limiting the flash current for the new request.
*/
rc = regmap_field_read(flash_data->r_fields[REG_THERM_THRSH1], &thrsh[0]);
if (rc < 0)
goto unlock;
rc = regmap_field_read(flash_data->r_fields[REG_THERM_THRSH2], &thrsh[1]);
if (rc < 0)
goto unlock;
if (flash_data->hw_type == QCOM_MVFLASH_3CH) {
rc = regmap_field_read(flash_data->r_fields[REG_THERM_THRSH3], &thrsh[2]);
if (rc < 0)
goto unlock;
}
min_thrsh = OTST_3CH_MIN_VAL;
if (flash_data->hw_type == QCOM_MVFLASH_4CH)
min_thrsh = (flash_data->revision == FLASH_4CH_REVISION_V0P1) ?
OTST1_4CH_V0P1_MIN_VAL : OTST1_4CH_MIN_VAL;
rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH1], min_thrsh);
if (rc < 0)
goto unlock;
if (flash_data->hw_type == QCOM_MVFLASH_4CH)
min_thrsh = OTST2_4CH_MIN_VAL;
/*
* The default thermal threshold settings have been updated hence
* restore them if any fault happens starting from here.
*/
rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH2], min_thrsh);
if (rc < 0)
goto restore;
if (flash_data->hw_type == QCOM_MVFLASH_3CH) {
rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH3], min_thrsh);
if (rc < 0)
goto restore;
}
/* Read thermal level status to get corresponding derating flash current */
rc = regmap_field_read(flash_data->r_fields[REG_STATUS2], &sts);
if (rc)
goto restore;
therm_ma = FLASH_TOTAL_CURRENT_MAX_UA / 1000;
if (flash_data->hw_type == QCOM_MVFLASH_3CH) {
if (sts & FLASH_STS_3CH_OTST3)
therm_ma = OTST3_MAX_CURRENT_MA;
else if (sts & FLASH_STS_3CH_OTST2)
therm_ma = OTST2_MAX_CURRENT_MA;
else if (sts & FLASH_STS_3CH_OTST1)
therm_ma = OTST1_MAX_CURRENT_MA;
} else {
if (sts & FLASH_STS_4CH_OTST2)
therm_ma = OTST2_MAX_CURRENT_MA;
else if (sts & FLASH_STS_4CH_OTST1)
therm_ma = OTST1_MAX_CURRENT_MA;
}
/* Calculate the allowed flash current for the request */
if (therm_ma <= flash_data->total_ma)
avail_ma = 0;
else
avail_ma = therm_ma - flash_data->total_ma;
*current_ma = min_t(u32, *current_ma, avail_ma);
led->current_in_use_ma = *current_ma;
flash_data->total_ma += led->current_in_use_ma;
dev_dbg(led->flash.led_cdev.dev, "allowed flash current: %dmA, total current: %dmA\n",
led->current_in_use_ma, flash_data->total_ma);
restore:
/* Restore to default thermal threshold settings */
rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH1], thrsh[0]);
if (rc < 0)
goto unlock;
rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH2], thrsh[1]);
if (rc < 0)
goto unlock;
if (flash_data->hw_type == QCOM_MVFLASH_3CH)
rc = regmap_field_write(flash_data->r_fields[REG_THERM_THRSH3], thrsh[2]);
unlock:
mutex_unlock(&flash_data->lock);
return rc;
}
static int set_flash_current(struct qcom_flash_led *led, u32 current_ma, enum led_mode mode) static int set_flash_current(struct qcom_flash_led *led, u32 current_ma, enum led_mode mode)
{ {
struct qcom_flash_data *flash_data = led->flash_data; struct qcom_flash_data *flash_data = led->flash_data;
@ -313,6 +458,10 @@ static int qcom_flash_strobe_set(struct led_classdev_flash *fled_cdev, bool stat
if (rc) if (rc)
return rc; return rc;
rc = update_allowed_flash_current(led, &led->flash_current_ma, state);
if (rc < 0)
return rc;
rc = set_flash_current(led, led->flash_current_ma, FLASH_MODE); rc = set_flash_current(led, led->flash_current_ma, FLASH_MODE);
if (rc) if (rc)
return rc; return rc;
@ -429,6 +578,10 @@ static int qcom_flash_led_brightness_set(struct led_classdev *led_cdev,
if (rc) if (rc)
return rc; return rc;
rc = update_allowed_flash_current(led, &current_ma, enable);
if (rc < 0)
return rc;
rc = set_flash_current(led, current_ma, TORCH_MODE); rc = set_flash_current(led, current_ma, TORCH_MODE);
if (rc) if (rc)
return rc; return rc;
@ -707,6 +860,14 @@ static int qcom_flash_led_probe(struct platform_device *pdev)
flash_data->hw_type = QCOM_MVFLASH_4CH; flash_data->hw_type = QCOM_MVFLASH_4CH;
flash_data->max_channels = 4; flash_data->max_channels = 4;
regs = mvflash_4ch_regs; regs = mvflash_4ch_regs;
rc = regmap_read(regmap, reg_base + FLASH_REVISION_REG, &val);
if (rc < 0) {
dev_err(dev, "Failed to read flash LED module revision, rc=%d\n", rc);
return rc;
}
flash_data->revision = val;
} else { } else {
dev_err(dev, "flash LED subtype %#x is not yet supported\n", val); dev_err(dev, "flash LED subtype %#x is not yet supported\n", val);
return -ENODEV; return -ENODEV;

View File

@ -115,7 +115,7 @@ static int pm860x_led_set(struct led_classdev *cdev,
static int pm860x_led_dt_init(struct platform_device *pdev, static int pm860x_led_dt_init(struct platform_device *pdev,
struct pm860x_led *data) struct pm860x_led *data)
{ {
struct device_node *nproot, *np; struct device_node *nproot;
int iset = 0; int iset = 0;
if (!dev_of_node(pdev->dev.parent)) if (!dev_of_node(pdev->dev.parent))
@ -125,12 +125,11 @@ static int pm860x_led_dt_init(struct platform_device *pdev,
dev_err(&pdev->dev, "failed to find leds node\n"); dev_err(&pdev->dev, "failed to find leds node\n");
return -ENODEV; return -ENODEV;
} }
for_each_available_child_of_node(nproot, np) { for_each_available_child_of_node_scoped(nproot, np) {
if (of_node_name_eq(np, data->name)) { if (of_node_name_eq(np, data->name)) {
of_property_read_u32(np, "marvell,88pm860x-iset", of_property_read_u32(np, "marvell,88pm860x-iset",
&iset); &iset);
data->iset = PM8606_LED_CURRENT(iset); data->iset = PM8606_LED_CURRENT(iset);
of_node_put(np);
break; break;
} }
} }

View File

@ -263,7 +263,7 @@ out:
static int aw2013_probe_dt(struct aw2013 *chip) static int aw2013_probe_dt(struct aw2013 *chip)
{ {
struct device_node *np = dev_of_node(&chip->client->dev), *child; struct device_node *np = dev_of_node(&chip->client->dev);
int count, ret = 0, i = 0; int count, ret = 0, i = 0;
struct aw2013_led *led; struct aw2013_led *led;
@ -273,7 +273,7 @@ static int aw2013_probe_dt(struct aw2013 *chip)
regmap_write(chip->regmap, AW2013_RSTR, AW2013_RSTR_RESET); regmap_write(chip->regmap, AW2013_RSTR, AW2013_RSTR_RESET);
for_each_available_child_of_node(np, child) { for_each_available_child_of_node_scoped(np, child) {
struct led_init_data init_data = {}; struct led_init_data init_data = {};
u32 source; u32 source;
u32 imax; u32 imax;
@ -304,10 +304,8 @@ static int aw2013_probe_dt(struct aw2013 *chip)
ret = devm_led_classdev_register_ext(&chip->client->dev, ret = devm_led_classdev_register_ext(&chip->client->dev,
&led->cdev, &init_data); &led->cdev, &init_data);
if (ret < 0) { if (ret < 0)
of_node_put(child);
return ret; return ret;
}
i++; i++;
} }

View File

@ -392,7 +392,6 @@ static int bcm6328_leds_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *np = dev_of_node(&pdev->dev); struct device_node *np = dev_of_node(&pdev->dev);
struct device_node *child;
void __iomem *mem; void __iomem *mem;
spinlock_t *lock; /* memory lock */ spinlock_t *lock; /* memory lock */
unsigned long val, *blink_leds, *blink_delay; unsigned long val, *blink_leds, *blink_delay;
@ -435,7 +434,7 @@ static int bcm6328_leds_probe(struct platform_device *pdev)
val |= BCM6328_SERIAL_LED_SHIFT_DIR; val |= BCM6328_SERIAL_LED_SHIFT_DIR;
bcm6328_led_write(mem + BCM6328_REG_INIT, val); bcm6328_led_write(mem + BCM6328_REG_INIT, val);
for_each_available_child_of_node(np, child) { for_each_available_child_of_node_scoped(np, child) {
int rc; int rc;
u32 reg; u32 reg;
@ -454,11 +453,9 @@ static int bcm6328_leds_probe(struct platform_device *pdev)
rc = bcm6328_led(dev, child, reg, mem, lock, rc = bcm6328_led(dev, child, reg, mem, lock,
blink_leds, blink_delay); blink_leds, blink_delay);
if (rc < 0) { if (rc < 0)
of_node_put(child);
return rc; return rc;
} }
}
return 0; return 0;
} }

View File

@ -147,7 +147,6 @@ static int bcm6358_leds_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *np = dev_of_node(&pdev->dev); struct device_node *np = dev_of_node(&pdev->dev);
struct device_node *child;
void __iomem *mem; void __iomem *mem;
spinlock_t *lock; /* memory lock */ spinlock_t *lock; /* memory lock */
unsigned long val; unsigned long val;
@ -184,7 +183,7 @@ static int bcm6358_leds_probe(struct platform_device *pdev)
} }
bcm6358_led_write(mem + BCM6358_REG_CTRL, val); bcm6358_led_write(mem + BCM6358_REG_CTRL, val);
for_each_available_child_of_node(np, child) { for_each_available_child_of_node_scoped(np, child) {
int rc; int rc;
u32 reg; u32 reg;
@ -198,11 +197,9 @@ static int bcm6358_leds_probe(struct platform_device *pdev)
} }
rc = bcm6358_led(dev, child, reg, mem, lock); rc = bcm6358_led(dev, child, reg, mem, lock);
if (rc < 0) { if (rc < 0)
of_node_put(child);
return rc; return rc;
} }
}
return 0; return 0;
} }

View File

@ -69,16 +69,14 @@ static const struct regmap_config bd2606mvv_regmap = {
static int bd2606mvv_probe(struct i2c_client *client) static int bd2606mvv_probe(struct i2c_client *client)
{ {
struct fwnode_handle *np, *child;
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct bd2606mvv_priv *priv; struct bd2606mvv_priv *priv;
struct fwnode_handle *led_fwnodes[BD2606_MAX_LEDS] = { 0 }; struct fwnode_handle *led_fwnodes[BD2606_MAX_LEDS] = { 0 };
int active_pairs[BD2606_MAX_LEDS / 2] = { 0 }; int active_pairs[BD2606_MAX_LEDS / 2] = { 0 };
int err, reg; int err, reg;
int i; int i, j;
np = dev_fwnode(dev); if (!dev_fwnode(dev))
if (!np)
return -ENODEV; return -ENODEV;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@ -94,20 +92,18 @@ static int bd2606mvv_probe(struct i2c_client *client)
i2c_set_clientdata(client, priv); i2c_set_clientdata(client, priv);
fwnode_for_each_available_child_node(np, child) { device_for_each_child_node_scoped(dev, child) {
struct bd2606mvv_led *led; struct bd2606mvv_led *led;
err = fwnode_property_read_u32(child, "reg", &reg); err = fwnode_property_read_u32(child, "reg", &reg);
if (err) { if (err)
fwnode_handle_put(child);
return err; return err;
}
if (reg < 0 || reg >= BD2606_MAX_LEDS || led_fwnodes[reg]) { if (reg < 0 || reg >= BD2606_MAX_LEDS || led_fwnodes[reg])
fwnode_handle_put(child);
return -EINVAL; return -EINVAL;
}
led = &priv->leds[reg]; led = &priv->leds[reg];
led_fwnodes[reg] = child; led_fwnodes[reg] = fwnode_handle_get(child);
active_pairs[reg / 2]++; active_pairs[reg / 2]++;
led->priv = priv; led->priv = priv;
led->led_no = reg; led->led_no = reg;
@ -130,7 +126,8 @@ static int bd2606mvv_probe(struct i2c_client *client)
&priv->leds[i].ldev, &priv->leds[i].ldev,
&init_data); &init_data);
if (err < 0) { if (err < 0) {
fwnode_handle_put(child); for (j = i; j < BD2606_MAX_LEDS; j++)
fwnode_handle_put(led_fwnodes[j]);
return dev_err_probe(dev, err, return dev_err_probe(dev, err,
"couldn't register LED %s\n", "couldn't register LED %s\n",
priv->leds[i].ldev.name); priv->leds[i].ldev.name);

View File

@ -2,6 +2,7 @@
/* /*
* leds-blinkm.c * leds-blinkm.c
* (c) Jan-Simon Möller (dl9pf@gmx.de) * (c) Jan-Simon Möller (dl9pf@gmx.de)
* (c) Joseph Strauss (jstrauss@mailbox.org)
*/ */
#include <linux/module.h> #include <linux/module.h>
@ -15,6 +16,10 @@
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/led-class-multicolor.h>
#include <linux/kconfig.h>
#define NUM_LEDS 3
/* Addresses to scan - BlinkM is on 0x09 by default*/ /* Addresses to scan - BlinkM is on 0x09 by default*/
static const unsigned short normal_i2c[] = { 0x09, I2C_CLIENT_END }; static const unsigned short normal_i2c[] = { 0x09, I2C_CLIENT_END };
@ -22,19 +27,25 @@ static const unsigned short normal_i2c[] = { 0x09, I2C_CLIENT_END };
static int blinkm_transfer_hw(struct i2c_client *client, int cmd); static int blinkm_transfer_hw(struct i2c_client *client, int cmd);
static int blinkm_test_run(struct i2c_client *client); static int blinkm_test_run(struct i2c_client *client);
/* Contains structs for both the color-separated sysfs classes, and the new multicolor class */
struct blinkm_led { struct blinkm_led {
struct i2c_client *i2c_client; struct i2c_client *i2c_client;
union {
/* used when multicolor support is disabled */
struct led_classdev led_cdev; struct led_classdev led_cdev;
struct led_classdev_mc mcled_cdev;
} cdev;
int id; int id;
}; };
#define cdev_to_blmled(c) container_of(c, struct blinkm_led, led_cdev) #define led_cdev_to_blmled(c) container_of(c, struct blinkm_led, cdev.led_cdev)
#define mcled_cdev_to_led(c) container_of(c, struct blinkm_led, cdev.mcled_cdev)
struct blinkm_data { struct blinkm_data {
struct i2c_client *i2c_client; struct i2c_client *i2c_client;
struct mutex update_lock; struct mutex update_lock;
/* used for led class interface */ /* used for led class interface */
struct blinkm_led blinkm_leds[3]; struct blinkm_led blinkm_leds[NUM_LEDS];
/* used for "blinkm" sysfs interface */ /* used for "blinkm" sysfs interface */
u8 red; /* color red */ u8 red; /* color red */
u8 green; /* color green */ u8 green; /* color green */
@ -419,11 +430,29 @@ static int blinkm_transfer_hw(struct i2c_client *client, int cmd)
return 0; return 0;
} }
static int blinkm_set_mc_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
struct blinkm_led *led = mcled_cdev_to_led(mcled_cdev);
struct blinkm_data *data = i2c_get_clientdata(led->i2c_client);
led_mc_calc_color_components(mcled_cdev, value);
data->next_red = (u8) mcled_cdev->subled_info[RED].brightness;
data->next_green = (u8) mcled_cdev->subled_info[GREEN].brightness;
data->next_blue = (u8) mcled_cdev->subled_info[BLUE].brightness;
blinkm_transfer_hw(led->i2c_client, BLM_GO_RGB);
return 0;
}
static int blinkm_led_common_set(struct led_classdev *led_cdev, static int blinkm_led_common_set(struct led_classdev *led_cdev,
enum led_brightness value, int color) enum led_brightness value, int color)
{ {
/* led_brightness is 0, 127 or 255 - we just use it here as-is */ /* led_brightness is 0, 127 or 255 - we just use it here as-is */
struct blinkm_led *led = cdev_to_blmled(led_cdev); struct blinkm_led *led = led_cdev_to_blmled(led_cdev);
struct blinkm_data *data = i2c_get_clientdata(led->i2c_client); struct blinkm_data *data = i2c_get_clientdata(led->i2c_client);
switch (color) { switch (color) {
@ -565,25 +594,147 @@ static int blinkm_detect(struct i2c_client *client, struct i2c_board_info *info)
return 0; return 0;
} }
static int register_separate_colors(struct i2c_client *client, struct blinkm_data *data)
{
/* 3 separate classes for red, green, and blue respectively */
struct blinkm_led *leds[NUM_LEDS];
int err;
char blinkm_led_name[28];
/* Register red, green, and blue sysfs classes */
for (int i = 0; i < NUM_LEDS; i++) {
/* RED = 0, GREEN = 1, BLUE = 2 */
leds[i] = &data->blinkm_leds[i];
leds[i]->i2c_client = client;
leds[i]->id = i;
leds[i]->cdev.led_cdev.max_brightness = 255;
leds[i]->cdev.led_cdev.flags = LED_CORE_SUSPENDRESUME;
switch (i) {
case RED:
scnprintf(blinkm_led_name, sizeof(blinkm_led_name),
"blinkm-%d-%d-red",
client->adapter->nr,
client->addr);
leds[i]->cdev.led_cdev.name = blinkm_led_name;
leds[i]->cdev.led_cdev.brightness_set_blocking =
blinkm_led_red_set;
err = led_classdev_register(&client->dev,
&leds[i]->cdev.led_cdev);
if (err < 0) {
dev_err(&client->dev,
"couldn't register LED %s\n",
leds[i]->cdev.led_cdev.name);
goto failred;
}
break;
case GREEN:
scnprintf(blinkm_led_name, sizeof(blinkm_led_name),
"blinkm-%d-%d-green",
client->adapter->nr,
client->addr);
leds[i]->cdev.led_cdev.name = blinkm_led_name;
leds[i]->cdev.led_cdev.brightness_set_blocking =
blinkm_led_green_set;
err = led_classdev_register(&client->dev,
&leds[i]->cdev.led_cdev);
if (err < 0) {
dev_err(&client->dev,
"couldn't register LED %s\n",
leds[i]->cdev.led_cdev.name);
goto failgreen;
}
break;
case BLUE:
scnprintf(blinkm_led_name, sizeof(blinkm_led_name),
"blinkm-%d-%d-blue",
client->adapter->nr,
client->addr);
leds[i]->cdev.led_cdev.name = blinkm_led_name;
leds[i]->cdev.led_cdev.brightness_set_blocking =
blinkm_led_blue_set;
err = led_classdev_register(&client->dev,
&leds[i]->cdev.led_cdev);
if (err < 0) {
dev_err(&client->dev,
"couldn't register LED %s\n",
leds[i]->cdev.led_cdev.name);
goto failblue;
}
break;
default:
break;
} /* end switch */
} /* end for */
return 0;
failblue:
led_classdev_unregister(&leds[GREEN]->cdev.led_cdev);
failgreen:
led_classdev_unregister(&leds[RED]->cdev.led_cdev);
failred:
sysfs_remove_group(&client->dev.kobj, &blinkm_group);
return err;
}
static int register_multicolor(struct i2c_client *client, struct blinkm_data *data)
{
struct blinkm_led *mc_led;
struct mc_subled *mc_led_info;
char blinkm_led_name[28];
int err;
/* Register multicolor sysfs class */
/* The first element of leds is used for multicolor facilities */
mc_led = &data->blinkm_leds[RED];
mc_led->i2c_client = client;
mc_led_info = devm_kcalloc(&client->dev, NUM_LEDS, sizeof(*mc_led_info),
GFP_KERNEL);
if (!mc_led_info)
return -ENOMEM;
mc_led_info[RED].color_index = LED_COLOR_ID_RED;
mc_led_info[GREEN].color_index = LED_COLOR_ID_GREEN;
mc_led_info[BLUE].color_index = LED_COLOR_ID_BLUE;
mc_led->cdev.mcled_cdev.subled_info = mc_led_info;
mc_led->cdev.mcled_cdev.num_colors = NUM_LEDS;
mc_led->cdev.mcled_cdev.led_cdev.brightness = 255;
mc_led->cdev.mcled_cdev.led_cdev.max_brightness = 255;
mc_led->cdev.mcled_cdev.led_cdev.flags = LED_CORE_SUSPENDRESUME;
scnprintf(blinkm_led_name, sizeof(blinkm_led_name),
"blinkm-%d-%d:rgb:indicator",
client->adapter->nr,
client->addr);
mc_led->cdev.mcled_cdev.led_cdev.name = blinkm_led_name;
mc_led->cdev.mcled_cdev.led_cdev.brightness_set_blocking = blinkm_set_mc_brightness;
err = led_classdev_multicolor_register(&client->dev, &mc_led->cdev.mcled_cdev);
if (err < 0) {
dev_err(&client->dev, "couldn't register LED %s\n",
mc_led->cdev.led_cdev.name);
sysfs_remove_group(&client->dev.kobj, &blinkm_group);
}
return 0;
}
static int blinkm_probe(struct i2c_client *client) static int blinkm_probe(struct i2c_client *client)
{ {
struct blinkm_data *data; struct blinkm_data *data;
struct blinkm_led *led[3]; int err;
int err, i;
char blinkm_led_name[28];
data = devm_kzalloc(&client->dev, data = devm_kzalloc(&client->dev,
sizeof(struct blinkm_data), GFP_KERNEL); sizeof(struct blinkm_data), GFP_KERNEL);
if (!data) { if (!data)
err = -ENOMEM; return -ENOMEM;
goto exit;
}
data->i2c_addr = 0x08; data->i2c_addr = 0x08;
/* i2c addr - use fake addr of 0x08 initially (real is 0x09) */ /* i2c addr - use fake addr of 0x08 initially (real is 0x09) */
data->fw_ver = 0xfe; data->fw_ver = 0xfe;
/* firmware version - use fake until we read real value /* firmware version - use fake until we read real value
* (currently broken - BlinkM confused!) */ * (currently broken - BlinkM confused!)
*/
data->script_id = 0x01; data->script_id = 0x01;
data->i2c_client = client; data->i2c_client = client;
@ -594,86 +745,22 @@ static int blinkm_probe(struct i2c_client *client)
err = sysfs_create_group(&client->dev.kobj, &blinkm_group); err = sysfs_create_group(&client->dev.kobj, &blinkm_group);
if (err < 0) { if (err < 0) {
dev_err(&client->dev, "couldn't register sysfs group\n"); dev_err(&client->dev, "couldn't register sysfs group\n");
goto exit; return err;
} }
for (i = 0; i < 3; i++) { if (!IS_ENABLED(CONFIG_LEDS_BLINKM_MULTICOLOR)) {
/* RED = 0, GREEN = 1, BLUE = 2 */ err = register_separate_colors(client, data);
led[i] = &data->blinkm_leds[i]; if (err < 0)
led[i]->i2c_client = client; return err;
led[i]->id = i; } else {
led[i]->led_cdev.max_brightness = 255; err = register_multicolor(client, data);
led[i]->led_cdev.flags = LED_CORE_SUSPENDRESUME; if (err < 0)
switch (i) { return err;
case RED:
snprintf(blinkm_led_name, sizeof(blinkm_led_name),
"blinkm-%d-%d-red",
client->adapter->nr,
client->addr);
led[i]->led_cdev.name = blinkm_led_name;
led[i]->led_cdev.brightness_set_blocking =
blinkm_led_red_set;
err = led_classdev_register(&client->dev,
&led[i]->led_cdev);
if (err < 0) {
dev_err(&client->dev,
"couldn't register LED %s\n",
led[i]->led_cdev.name);
goto failred;
} }
break;
case GREEN:
snprintf(blinkm_led_name, sizeof(blinkm_led_name),
"blinkm-%d-%d-green",
client->adapter->nr,
client->addr);
led[i]->led_cdev.name = blinkm_led_name;
led[i]->led_cdev.brightness_set_blocking =
blinkm_led_green_set;
err = led_classdev_register(&client->dev,
&led[i]->led_cdev);
if (err < 0) {
dev_err(&client->dev,
"couldn't register LED %s\n",
led[i]->led_cdev.name);
goto failgreen;
}
break;
case BLUE:
snprintf(blinkm_led_name, sizeof(blinkm_led_name),
"blinkm-%d-%d-blue",
client->adapter->nr,
client->addr);
led[i]->led_cdev.name = blinkm_led_name;
led[i]->led_cdev.brightness_set_blocking =
blinkm_led_blue_set;
err = led_classdev_register(&client->dev,
&led[i]->led_cdev);
if (err < 0) {
dev_err(&client->dev,
"couldn't register LED %s\n",
led[i]->led_cdev.name);
goto failblue;
}
break;
} /* end switch */
} /* end for */
/* Initialize the blinkm */
blinkm_init_hw(client); blinkm_init_hw(client);
return 0; return 0;
failblue:
led_classdev_unregister(&led[GREEN]->led_cdev);
failgreen:
led_classdev_unregister(&led[RED]->led_cdev);
failred:
sysfs_remove_group(&client->dev.kobj, &blinkm_group);
exit:
return err;
} }
static void blinkm_remove(struct i2c_client *client) static void blinkm_remove(struct i2c_client *client)
@ -683,8 +770,8 @@ static void blinkm_remove(struct i2c_client *client)
int i; int i;
/* make sure no workqueue entries are pending */ /* make sure no workqueue entries are pending */
for (i = 0; i < 3; i++) for (i = 0; i < NUM_LEDS; i++)
led_classdev_unregister(&data->blinkm_leds[i].led_cdev); led_classdev_unregister(&data->blinkm_leds[i].cdev.led_cdev);
/* reset rgb */ /* reset rgb */
data->next_red = 0x00; data->next_red = 0x00;
@ -740,6 +827,7 @@ static struct i2c_driver blinkm_driver = {
module_i2c_driver(blinkm_driver); module_i2c_driver(blinkm_driver);
MODULE_AUTHOR("Jan-Simon Moeller <dl9pf@gmx.de>"); MODULE_AUTHOR("Jan-Simon Moeller <dl9pf@gmx.de>");
MODULE_AUTHOR("Joseph Strauss <jstrauss@mailbox.org>");
MODULE_DESCRIPTION("BlinkM RGB LED driver"); MODULE_DESCRIPTION("BlinkM RGB LED driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -150,7 +150,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct device *dev)
{ {
struct fwnode_handle *child; struct fwnode_handle *child;
struct gpio_leds_priv *priv; struct gpio_leds_priv *priv;
int count, ret; int count, used, ret;
count = device_get_child_node_count(dev); count = device_get_child_node_count(dev);
if (!count) if (!count)
@ -159,9 +159,11 @@ static struct gpio_leds_priv *gpio_leds_create(struct device *dev)
priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL); priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL);
if (!priv) if (!priv)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
priv->num_leds = count;
used = 0;
device_for_each_child_node(dev, child) { device_for_each_child_node(dev, child) {
struct gpio_led_data *led_dat = &priv->leds[priv->num_leds]; struct gpio_led_data *led_dat = &priv->leds[used];
struct gpio_led led = {}; struct gpio_led led = {};
/* /*
@ -197,8 +199,9 @@ static struct gpio_leds_priv *gpio_leds_create(struct device *dev)
/* Set gpiod label to match the corresponding LED name. */ /* Set gpiod label to match the corresponding LED name. */
gpiod_set_consumer_name(led_dat->gpiod, gpiod_set_consumer_name(led_dat->gpiod,
led_dat->cdev.dev->kobj.name); led_dat->cdev.dev->kobj.name);
priv->num_leds++; used++;
} }
priv->num_leds = used;
return priv; return priv;
} }

View File

@ -392,7 +392,7 @@ static int is31fl319x_parse_child_fw(const struct device *dev,
static int is31fl319x_parse_fw(struct device *dev, struct is31fl319x_chip *is31) static int is31fl319x_parse_fw(struct device *dev, struct is31fl319x_chip *is31)
{ {
struct fwnode_handle *fwnode = dev_fwnode(dev), *child; struct fwnode_handle *fwnode = dev_fwnode(dev);
int count; int count;
int ret; int ret;
@ -404,7 +404,7 @@ static int is31fl319x_parse_fw(struct device *dev, struct is31fl319x_chip *is31)
is31->cdef = device_get_match_data(dev); is31->cdef = device_get_match_data(dev);
count = 0; count = 0;
fwnode_for_each_available_child_node(fwnode, child) device_for_each_child_node_scoped(dev, child)
count++; count++;
dev_dbg(dev, "probing with %d leds defined in DT\n", count); dev_dbg(dev, "probing with %d leds defined in DT\n", count);
@ -414,33 +414,25 @@ static int is31fl319x_parse_fw(struct device *dev, struct is31fl319x_chip *is31)
"Number of leds defined must be between 1 and %u\n", "Number of leds defined must be between 1 and %u\n",
is31->cdef->num_leds); is31->cdef->num_leds);
fwnode_for_each_available_child_node(fwnode, child) { device_for_each_child_node_scoped(dev, child) {
struct is31fl319x_led *led; struct is31fl319x_led *led;
u32 reg; u32 reg;
ret = fwnode_property_read_u32(child, "reg", &reg); ret = fwnode_property_read_u32(child, "reg", &reg);
if (ret) { if (ret)
ret = dev_err_probe(dev, ret, "Failed to read led 'reg' property\n"); return dev_err_probe(dev, ret, "Failed to read led 'reg' property\n");
goto put_child_node;
}
if (reg < 1 || reg > is31->cdef->num_leds) { if (reg < 1 || reg > is31->cdef->num_leds)
ret = dev_err_probe(dev, -EINVAL, "invalid led reg %u\n", reg); return dev_err_probe(dev, -EINVAL, "invalid led reg %u\n", reg);
goto put_child_node;
}
led = &is31->leds[reg - 1]; led = &is31->leds[reg - 1];
if (led->configured) { if (led->configured)
ret = dev_err_probe(dev, -EINVAL, "led %u is already configured\n", reg); return dev_err_probe(dev, -EINVAL, "led %u is already configured\n", reg);
goto put_child_node;
}
ret = is31fl319x_parse_child_fw(dev, child, led, is31); ret = is31fl319x_parse_child_fw(dev, child, led, is31);
if (ret) { if (ret)
ret = dev_err_probe(dev, ret, "led %u DT parsing failed\n", reg); return dev_err_probe(dev, ret, "led %u DT parsing failed\n", reg);
goto put_child_node;
}
led->configured = true; led->configured = true;
} }
@ -454,10 +446,6 @@ static int is31fl319x_parse_fw(struct device *dev, struct is31fl319x_chip *is31)
} }
return 0; return 0;
put_child_node:
fwnode_handle_put(child);
return ret;
} }
static inline int is31fl3190_microamp_to_cs(struct device *dev, u32 microamp) static inline int is31fl3190_microamp_to_cs(struct device *dev, u32 microamp)

View File

@ -363,10 +363,9 @@ static struct is31fl32xx_led_data *is31fl32xx_find_led_data(
static int is31fl32xx_parse_dt(struct device *dev, static int is31fl32xx_parse_dt(struct device *dev,
struct is31fl32xx_priv *priv) struct is31fl32xx_priv *priv)
{ {
struct device_node *child;
int ret = 0; int ret = 0;
for_each_available_child_of_node(dev_of_node(dev), child) { for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
struct led_init_data init_data = {}; struct led_init_data init_data = {};
struct is31fl32xx_led_data *led_data = struct is31fl32xx_led_data *led_data =
&priv->leds[priv->num_leds]; &priv->leds[priv->num_leds];
@ -376,7 +375,7 @@ static int is31fl32xx_parse_dt(struct device *dev,
ret = is31fl32xx_parse_child_dt(dev, child, led_data); ret = is31fl32xx_parse_child_dt(dev, child, led_data);
if (ret) if (ret)
goto err; return ret;
/* Detect if channel is already in use by another child */ /* Detect if channel is already in use by another child */
other_led_data = is31fl32xx_find_led_data(priv, other_led_data = is31fl32xx_find_led_data(priv,
@ -385,8 +384,7 @@ static int is31fl32xx_parse_dt(struct device *dev,
dev_err(dev, dev_err(dev,
"Node %pOF 'reg' conflicts with another LED\n", "Node %pOF 'reg' conflicts with another LED\n",
child); child);
ret = -EINVAL; return -EINVAL;
goto err;
} }
init_data.fwnode = of_fwnode_handle(child); init_data.fwnode = of_fwnode_handle(child);
@ -396,17 +394,13 @@ static int is31fl32xx_parse_dt(struct device *dev,
if (ret) { if (ret) {
dev_err(dev, "Failed to register LED for %pOF: %d\n", dev_err(dev, "Failed to register LED for %pOF: %d\n",
child, ret); child, ret);
goto err; return ret;
} }
priv->num_leds++; priv->num_leds++;
} }
return 0; return 0;
err:
of_node_put(child);
return ret;
} }
static const struct of_device_id of_is31fl32xx_match[] = { static const struct of_device_id of_is31fl32xx_match[] = {

View File

@ -965,24 +965,16 @@ EXPORT_SYMBOL_GPL(lp55xx_update_bits);
bool lp55xx_is_extclk_used(struct lp55xx_chip *chip) bool lp55xx_is_extclk_used(struct lp55xx_chip *chip)
{ {
struct clk *clk; struct clk *clk;
int err;
clk = devm_clk_get(&chip->cl->dev, "32k_clk"); clk = devm_clk_get_enabled(&chip->cl->dev, "32k_clk");
if (IS_ERR(clk)) if (IS_ERR(clk))
goto use_internal_clk; goto use_internal_clk;
err = clk_prepare_enable(clk); if (clk_get_rate(clk) != LP55XX_CLK_32K)
if (err)
goto use_internal_clk; goto use_internal_clk;
if (clk_get_rate(clk) != LP55XX_CLK_32K) {
clk_disable_unprepare(clk);
goto use_internal_clk;
}
dev_info(&chip->cl->dev, "%dHz external clock used\n", LP55XX_CLK_32K); dev_info(&chip->cl->dev, "%dHz external clock used\n", LP55XX_CLK_32K);
chip->clk = clk;
return true; return true;
use_internal_clk: use_internal_clk:
@ -995,9 +987,6 @@ static void lp55xx_deinit_device(struct lp55xx_chip *chip)
{ {
struct lp55xx_platform_data *pdata = chip->pdata; struct lp55xx_platform_data *pdata = chip->pdata;
if (chip->clk)
clk_disable_unprepare(chip->clk);
if (pdata->enable_gpiod) if (pdata->enable_gpiod)
gpiod_set_value(pdata->enable_gpiod, 0); gpiod_set_value(pdata->enable_gpiod, 0);
} }
@ -1173,16 +1162,13 @@ static int lp55xx_parse_multi_led(struct device_node *np,
struct lp55xx_led_config *cfg, struct lp55xx_led_config *cfg,
int child_number) int child_number)
{ {
struct device_node *child;
int num_colors = 0, ret; int num_colors = 0, ret;
for_each_available_child_of_node(np, child) { for_each_available_child_of_node_scoped(np, child) {
ret = lp55xx_parse_multi_led_child(child, cfg, child_number, ret = lp55xx_parse_multi_led_child(child, cfg, child_number,
num_colors); num_colors);
if (ret) { if (ret)
of_node_put(child);
return ret; return ret;
}
num_colors++; num_colors++;
} }

View File

@ -193,7 +193,6 @@ struct lp55xx_engine {
*/ */
struct lp55xx_chip { struct lp55xx_chip {
struct i2c_client *cl; struct i2c_client *cl;
struct clk *clk;
struct lp55xx_platform_data *pdata; struct lp55xx_platform_data *pdata;
struct mutex lock; /* lock for user-space interface */ struct mutex lock; /* lock for user-space interface */
int num_leds; int num_leds;

View File

@ -12,6 +12,7 @@
* Eric Miao <eric.miao@marvell.com> * Eric Miao <eric.miao@marvell.com>
*/ */
#include <linux/cleanup.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
@ -113,7 +114,7 @@ static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(
{ {
struct mc13xxx_leds *leds = platform_get_drvdata(pdev); struct mc13xxx_leds *leds = platform_get_drvdata(pdev);
struct mc13xxx_leds_platform_data *pdata; struct mc13xxx_leds_platform_data *pdata;
struct device_node *parent, *child; struct device_node *child;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
int i = 0, ret = -ENODATA; int i = 0, ret = -ENODATA;
@ -121,24 +122,23 @@ static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(
if (!pdata) if (!pdata)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
parent = of_get_child_by_name(dev_of_node(dev->parent), "leds"); struct device_node *parent __free(device_node) =
of_get_child_by_name(dev_of_node(dev->parent), "leds");
if (!parent) if (!parent)
goto out_node_put; return ERR_PTR(-ENODATA);
ret = of_property_read_u32_array(parent, "led-control", ret = of_property_read_u32_array(parent, "led-control",
pdata->led_control, pdata->led_control,
leds->devtype->num_regs); leds->devtype->num_regs);
if (ret) if (ret)
goto out_node_put; return ERR_PTR(ret);
pdata->num_leds = of_get_available_child_count(parent); pdata->num_leds = of_get_available_child_count(parent);
pdata->led = devm_kcalloc(dev, pdata->num_leds, sizeof(*pdata->led), pdata->led = devm_kcalloc(dev, pdata->num_leds, sizeof(*pdata->led),
GFP_KERNEL); GFP_KERNEL);
if (!pdata->led) { if (!pdata->led)
ret = -ENOMEM; return ERR_PTR(-ENOMEM);
goto out_node_put;
}
for_each_available_child_of_node(parent, child) { for_each_available_child_of_node(parent, child) {
const char *str; const char *str;
@ -158,12 +158,10 @@ static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(
} }
pdata->num_leds = i; pdata->num_leds = i;
ret = i > 0 ? 0 : -ENODATA; if (i <= 0)
return ERR_PTR(-ENODATA);
out_node_put: return pdata;
of_node_put(parent);
return ret ? ERR_PTR(ret) : pdata;
} }
#else #else
static inline struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt( static inline struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(

View File

@ -527,7 +527,6 @@ static int mt6323_led_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *np = dev_of_node(dev); struct device_node *np = dev_of_node(dev);
struct device_node *child;
struct mt6397_chip *hw = dev_get_drvdata(dev->parent); struct mt6397_chip *hw = dev_get_drvdata(dev->parent);
struct mt6323_leds *leds; struct mt6323_leds *leds;
struct mt6323_led *led; struct mt6323_led *led;
@ -565,28 +564,25 @@ static int mt6323_led_probe(struct platform_device *pdev)
return ret; return ret;
} }
for_each_available_child_of_node(np, child) { for_each_available_child_of_node_scoped(np, child) {
struct led_init_data init_data = {}; struct led_init_data init_data = {};
bool is_wled; bool is_wled;
ret = of_property_read_u32(child, "reg", &reg); ret = of_property_read_u32(child, "reg", &reg);
if (ret) { if (ret) {
dev_err(dev, "Failed to read led 'reg' property\n"); dev_err(dev, "Failed to read led 'reg' property\n");
goto put_child_node; return ret;
} }
if (reg >= max_leds || reg >= MAX_SUPPORTED_LEDS || if (reg >= max_leds || reg >= MAX_SUPPORTED_LEDS ||
leds->led[reg]) { leds->led[reg]) {
dev_err(dev, "Invalid led reg %u\n", reg); dev_err(dev, "Invalid led reg %u\n", reg);
ret = -EINVAL; return -EINVAL;
goto put_child_node;
} }
led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
if (!led) { if (!led)
ret = -ENOMEM; return -ENOMEM;
goto put_child_node;
}
is_wled = of_property_read_bool(child, "mediatek,is-wled"); is_wled = of_property_read_bool(child, "mediatek,is-wled");
@ -612,7 +608,7 @@ static int mt6323_led_probe(struct platform_device *pdev)
if (ret < 0) { if (ret < 0) {
dev_err(leds->dev, dev_err(leds->dev,
"Failed to LED set default from devicetree\n"); "Failed to LED set default from devicetree\n");
goto put_child_node; return ret;
} }
init_data.fwnode = of_fwnode_handle(child); init_data.fwnode = of_fwnode_handle(child);
@ -621,15 +617,11 @@ static int mt6323_led_probe(struct platform_device *pdev)
&init_data); &init_data);
if (ret) { if (ret) {
dev_err(dev, "Failed to register LED: %d\n", ret); dev_err(dev, "Failed to register LED: %d\n", ret);
goto put_child_node; return ret;
} }
} }
return 0; return 0;
put_child_node:
of_node_put(child);
return ret;
} }
static void mt6323_led_remove(struct platform_device *pdev) static void mt6323_led_remove(struct platform_device *pdev)

View File

@ -423,7 +423,6 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
struct device_node *gpio_ext_np; struct device_node *gpio_ext_np;
struct platform_device *gpio_ext_pdev; struct platform_device *gpio_ext_pdev;
struct device *gpio_ext_dev; struct device *gpio_ext_dev;
struct device_node *child;
struct netxbig_gpio_ext *gpio_ext; struct netxbig_gpio_ext *gpio_ext;
struct netxbig_led_timer *timers; struct netxbig_led_timer *timers;
struct netxbig_led *leds, *led; struct netxbig_led *leds, *led;
@ -507,7 +506,7 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
} }
led = leds; led = leds;
for_each_available_child_of_node(np, child) { for_each_available_child_of_node_scoped(np, child) {
const char *string; const char *string;
int *mode_val; int *mode_val;
int num_modes; int num_modes;
@ -515,17 +514,17 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
ret = of_property_read_u32(child, "mode-addr", ret = of_property_read_u32(child, "mode-addr",
&led->mode_addr); &led->mode_addr);
if (ret) if (ret)
goto err_node_put; goto put_device;
ret = of_property_read_u32(child, "bright-addr", ret = of_property_read_u32(child, "bright-addr",
&led->bright_addr); &led->bright_addr);
if (ret) if (ret)
goto err_node_put; goto put_device;
ret = of_property_read_u32(child, "max-brightness", ret = of_property_read_u32(child, "max-brightness",
&led->bright_max); &led->bright_max);
if (ret) if (ret)
goto err_node_put; goto put_device;
mode_val = mode_val =
devm_kcalloc(dev, devm_kcalloc(dev,
@ -533,7 +532,7 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
GFP_KERNEL); GFP_KERNEL);
if (!mode_val) { if (!mode_val) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_node_put; goto put_device;
} }
for (i = 0; i < NETXBIG_LED_MODE_NUM; i++) for (i = 0; i < NETXBIG_LED_MODE_NUM; i++)
@ -542,12 +541,12 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
ret = of_property_count_u32_elems(child, "mode-val"); ret = of_property_count_u32_elems(child, "mode-val");
if (ret < 0 || ret % 2) { if (ret < 0 || ret % 2) {
ret = -EINVAL; ret = -EINVAL;
goto err_node_put; goto put_device;
} }
num_modes = ret / 2; num_modes = ret / 2;
if (num_modes > NETXBIG_LED_MODE_NUM) { if (num_modes > NETXBIG_LED_MODE_NUM) {
ret = -EINVAL; ret = -EINVAL;
goto err_node_put; goto put_device;
} }
for (i = 0; i < num_modes; i++) { for (i = 0; i < num_modes; i++) {
@ -560,7 +559,7 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
"mode-val", 2 * i + 1, &val); "mode-val", 2 * i + 1, &val);
if (mode >= NETXBIG_LED_MODE_NUM) { if (mode >= NETXBIG_LED_MODE_NUM) {
ret = -EINVAL; ret = -EINVAL;
goto err_node_put; goto put_device;
} }
mode_val[mode] = val; mode_val[mode] = val;
} }
@ -583,8 +582,6 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
return 0; return 0;
err_node_put:
of_node_put(child);
put_device: put_device:
put_device(gpio_ext_dev); put_device(gpio_ext_dev);
return ret; return ret;

View File

@ -215,8 +215,7 @@ static int pca9532_update_hw_blink(struct pca9532_led *led,
if (other->state == PCA9532_PWM1) { if (other->state == PCA9532_PWM1) {
if (other->ldev.blink_delay_on != delay_on || if (other->ldev.blink_delay_on != delay_on ||
other->ldev.blink_delay_off != delay_off) { other->ldev.blink_delay_off != delay_off) {
dev_err(&led->client->dev, /* HW can handle only one blink configuration at a time */
"HW can handle only one blink configuration at a time\n");
return -EINVAL; return -EINVAL;
} }
} }
@ -224,7 +223,7 @@ static int pca9532_update_hw_blink(struct pca9532_led *led,
psc = ((delay_on + delay_off) * PCA9532_PWM_PERIOD_DIV - 1) / 1000; psc = ((delay_on + delay_off) * PCA9532_PWM_PERIOD_DIV - 1) / 1000;
if (psc > U8_MAX) { if (psc > U8_MAX) {
dev_err(&led->client->dev, "Blink period too long to be handled by hardware\n"); /* Blink period too long to be handled by hardware */
return -EINVAL; return -EINVAL;
} }
@ -506,7 +505,6 @@ static struct pca9532_platform_data *
pca9532_of_populate_pdata(struct device *dev, struct device_node *np) pca9532_of_populate_pdata(struct device *dev, struct device_node *np)
{ {
struct pca9532_platform_data *pdata; struct pca9532_platform_data *pdata;
struct device_node *child;
int devid, maxleds; int devid, maxleds;
int i = 0; int i = 0;
const char *state; const char *state;
@ -525,7 +523,7 @@ pca9532_of_populate_pdata(struct device *dev, struct device_node *np)
of_property_read_u8_array(np, "nxp,psc", &pdata->psc[PCA9532_PWM_ID_0], of_property_read_u8_array(np, "nxp,psc", &pdata->psc[PCA9532_PWM_ID_0],
ARRAY_SIZE(pdata->psc)); ARRAY_SIZE(pdata->psc));
for_each_available_child_of_node(np, child) { for_each_available_child_of_node_scoped(np, child) {
if (of_property_read_string(child, "label", if (of_property_read_string(child, "label",
&pdata->leds[i].name)) &pdata->leds[i].name))
pdata->leds[i].name = child->name; pdata->leds[i].name = child->name;
@ -538,11 +536,9 @@ pca9532_of_populate_pdata(struct device *dev, struct device_node *np)
else if (!strcmp(state, "keep")) else if (!strcmp(state, "keep"))
pdata->leds[i].state = PCA9532_KEEP; pdata->leds[i].state = PCA9532_KEEP;
} }
if (++i >= maxleds) { if (++i >= maxleds)
of_node_put(child);
break; break;
} }
}
return pdata; return pdata;
} }

View File

@ -19,10 +19,6 @@
#define PCA995X_MODE1 0x00 #define PCA995X_MODE1 0x00
#define PCA995X_MODE2 0x01 #define PCA995X_MODE2 0x01
#define PCA995X_LEDOUT0 0x02 #define PCA995X_LEDOUT0 0x02
#define PCA9955B_PWM0 0x08
#define PCA9952_PWM0 0x0A
#define PCA9952_IREFALL 0x43
#define PCA9955B_IREFALL 0x45
/* Auto-increment disabled. Normal mode */ /* Auto-increment disabled. Normal mode */
#define PCA995X_MODE1_CFG 0x00 #define PCA995X_MODE1_CFG 0x00
@ -34,17 +30,38 @@
#define PCA995X_LDRX_MASK 0x3 #define PCA995X_LDRX_MASK 0x3
#define PCA995X_LDRX_BITS 2 #define PCA995X_LDRX_BITS 2
#define PCA995X_MAX_OUTPUTS 16 #define PCA995X_MAX_OUTPUTS 24
#define PCA995X_OUTPUTS_PER_REG 4 #define PCA995X_OUTPUTS_PER_REG 4
#define PCA995X_IREFALL_FULL_CFG 0xFF #define PCA995X_IREFALL_FULL_CFG 0xFF
#define PCA995X_IREFALL_HALF_CFG (PCA995X_IREFALL_FULL_CFG / 2) #define PCA995X_IREFALL_HALF_CFG (PCA995X_IREFALL_FULL_CFG / 2)
#define PCA995X_TYPE_NON_B 0
#define PCA995X_TYPE_B 1
#define ldev_to_led(c) container_of(c, struct pca995x_led, ldev) #define ldev_to_led(c) container_of(c, struct pca995x_led, ldev)
struct pca995x_chipdef {
unsigned int num_leds;
u8 pwm_base;
u8 irefall;
};
static const struct pca995x_chipdef pca9952_chipdef = {
.num_leds = 16,
.pwm_base = 0x0a,
.irefall = 0x43,
};
static const struct pca995x_chipdef pca9955b_chipdef = {
.num_leds = 16,
.pwm_base = 0x08,
.irefall = 0x45,
};
static const struct pca995x_chipdef pca9956b_chipdef = {
.num_leds = 24,
.pwm_base = 0x0a,
.irefall = 0x40,
};
struct pca995x_led { struct pca995x_led {
unsigned int led_no; unsigned int led_no;
struct led_classdev ldev; struct led_classdev ldev;
@ -54,7 +71,7 @@ struct pca995x_led {
struct pca995x_chip { struct pca995x_chip {
struct regmap *regmap; struct regmap *regmap;
struct pca995x_led leds[PCA995X_MAX_OUTPUTS]; struct pca995x_led leds[PCA995X_MAX_OUTPUTS];
int btype; const struct pca995x_chipdef *chipdef;
}; };
static int pca995x_brightness_set(struct led_classdev *led_cdev, static int pca995x_brightness_set(struct led_classdev *led_cdev,
@ -62,10 +79,11 @@ static int pca995x_brightness_set(struct led_classdev *led_cdev,
{ {
struct pca995x_led *led = ldev_to_led(led_cdev); struct pca995x_led *led = ldev_to_led(led_cdev);
struct pca995x_chip *chip = led->chip; struct pca995x_chip *chip = led->chip;
const struct pca995x_chipdef *chipdef = chip->chipdef;
u8 ledout_addr, pwmout_addr; u8 ledout_addr, pwmout_addr;
int shift, ret; int shift, ret;
pwmout_addr = (chip->btype ? PCA9955B_PWM0 : PCA9952_PWM0) + led->led_no; pwmout_addr = chipdef->pwm_base + led->led_no;
ledout_addr = PCA995X_LEDOUT0 + (led->led_no / PCA995X_OUTPUTS_PER_REG); ledout_addr = PCA995X_LEDOUT0 + (led->led_no / PCA995X_OUTPUTS_PER_REG);
shift = PCA995X_LDRX_BITS * (led->led_no % PCA995X_OUTPUTS_PER_REG); shift = PCA995X_LDRX_BITS * (led->led_no % PCA995X_OUTPUTS_PER_REG);
@ -102,43 +120,38 @@ static const struct regmap_config pca995x_regmap = {
static int pca995x_probe(struct i2c_client *client) static int pca995x_probe(struct i2c_client *client)
{ {
struct fwnode_handle *led_fwnodes[PCA995X_MAX_OUTPUTS] = { 0 }; struct fwnode_handle *led_fwnodes[PCA995X_MAX_OUTPUTS] = { 0 };
struct fwnode_handle *np, *child;
struct device *dev = &client->dev; struct device *dev = &client->dev;
const struct pca995x_chipdef *chipdef;
struct pca995x_chip *chip; struct pca995x_chip *chip;
struct pca995x_led *led; struct pca995x_led *led;
int i, btype, reg, ret; int i, j, reg, ret;
btype = (unsigned long)device_get_match_data(&client->dev); chipdef = device_get_match_data(&client->dev);
np = dev_fwnode(dev); if (!dev_fwnode(dev))
if (!np)
return -ENODEV; return -ENODEV;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip) if (!chip)
return -ENOMEM; return -ENOMEM;
chip->btype = btype; chip->chipdef = chipdef;
chip->regmap = devm_regmap_init_i2c(client, &pca995x_regmap); chip->regmap = devm_regmap_init_i2c(client, &pca995x_regmap);
if (IS_ERR(chip->regmap)) if (IS_ERR(chip->regmap))
return PTR_ERR(chip->regmap); return PTR_ERR(chip->regmap);
i2c_set_clientdata(client, chip); i2c_set_clientdata(client, chip);
fwnode_for_each_available_child_node(np, child) { device_for_each_child_node_scoped(dev, child) {
ret = fwnode_property_read_u32(child, "reg", &reg); ret = fwnode_property_read_u32(child, "reg", &reg);
if (ret) { if (ret)
fwnode_handle_put(child);
return ret; return ret;
}
if (reg < 0 || reg >= PCA995X_MAX_OUTPUTS || led_fwnodes[reg]) { if (reg < 0 || reg >= PCA995X_MAX_OUTPUTS || led_fwnodes[reg])
fwnode_handle_put(child);
return -EINVAL; return -EINVAL;
}
led = &chip->leds[reg]; led = &chip->leds[reg];
led_fwnodes[reg] = child; led_fwnodes[reg] = fwnode_handle_get(child);
led->chip = chip; led->chip = chip;
led->led_no = reg; led->led_no = reg;
led->ldev.brightness_set_blocking = pca995x_brightness_set; led->ldev.brightness_set_blocking = pca995x_brightness_set;
@ -157,7 +170,8 @@ static int pca995x_probe(struct i2c_client *client)
&chip->leds[i].ldev, &chip->leds[i].ldev,
&init_data); &init_data);
if (ret < 0) { if (ret < 0) {
fwnode_handle_put(child); for (j = i; j < PCA995X_MAX_OUTPUTS; j++)
fwnode_handle_put(led_fwnodes[j]);
return dev_err_probe(dev, ret, return dev_err_probe(dev, ret,
"Could not register LED %s\n", "Could not register LED %s\n",
chip->leds[i].ldev.name); chip->leds[i].ldev.name);
@ -170,21 +184,21 @@ static int pca995x_probe(struct i2c_client *client)
return ret; return ret;
/* IREF Output current value for all LEDn outputs */ /* IREF Output current value for all LEDn outputs */
return regmap_write(chip->regmap, return regmap_write(chip->regmap, chipdef->irefall, PCA995X_IREFALL_HALF_CFG);
btype ? PCA9955B_IREFALL : PCA9952_IREFALL,
PCA995X_IREFALL_HALF_CFG);
} }
static const struct i2c_device_id pca995x_id[] = { static const struct i2c_device_id pca995x_id[] = {
{ "pca9952", .driver_data = (kernel_ulong_t)PCA995X_TYPE_NON_B }, { "pca9952", .driver_data = (kernel_ulong_t)&pca9952_chipdef },
{ "pca9955b", .driver_data = (kernel_ulong_t)PCA995X_TYPE_B }, { "pca9955b", .driver_data = (kernel_ulong_t)&pca9955b_chipdef },
{ "pca9956b", .driver_data = (kernel_ulong_t)&pca9956b_chipdef },
{} {}
}; };
MODULE_DEVICE_TABLE(i2c, pca995x_id); MODULE_DEVICE_TABLE(i2c, pca995x_id);
static const struct of_device_id pca995x_of_match[] = { static const struct of_device_id pca995x_of_match[] = {
{ .compatible = "nxp,pca9952", .data = (void *)PCA995X_TYPE_NON_B }, { .compatible = "nxp,pca9952", .data = &pca9952_chipdef },
{ .compatible = "nxp,pca9955b", .data = (void *)PCA995X_TYPE_B }, { .compatible = "nxp,pca9955b", . data = &pca9955b_chipdef },
{ .compatible = "nxp,pca9956b", .data = &pca9956b_chipdef },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, pca995x_of_match); MODULE_DEVICE_TABLE(of, pca995x_of_match);

View File

@ -276,7 +276,7 @@ static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv)
static int sc27xx_led_probe(struct platform_device *pdev) static int sc27xx_led_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *np = dev_of_node(dev), *child; struct device_node *np = dev_of_node(dev);
struct sc27xx_led_priv *priv; struct sc27xx_led_priv *priv;
u32 base, count, reg; u32 base, count, reg;
int err; int err;
@ -304,17 +304,13 @@ static int sc27xx_led_probe(struct platform_device *pdev)
return err; return err;
} }
for_each_available_child_of_node(np, child) { for_each_available_child_of_node_scoped(np, child) {
err = of_property_read_u32(child, "reg", &reg); err = of_property_read_u32(child, "reg", &reg);
if (err) { if (err)
of_node_put(child);
return err; return err;
}
if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) { if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active)
of_node_put(child);
return -EINVAL; return -EINVAL;
}
priv->leds[reg].fwnode = of_fwnode_handle(child); priv->leds[reg].fwnode = of_fwnode_handle(child);
priv->leds[reg].active = true; priv->leds[reg].active = true;

View File

@ -368,7 +368,7 @@ static int sun50i_a100_ledc_suspend(struct device *dev)
if (!xfer_active) if (!xfer_active)
break; break;
msleep(1); usleep_range(1000, 1100);
} }
clk_disable_unprepare(priv->mod_clk); clk_disable_unprepare(priv->mod_clk);

View File

@ -452,7 +452,7 @@ static int omnia_mcu_get_features(const struct i2c_client *client)
static int omnia_leds_probe(struct i2c_client *client) static int omnia_leds_probe(struct i2c_client *client)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct device_node *np = dev_of_node(dev), *child; struct device_node *np = dev_of_node(dev);
struct omnia_leds *leds; struct omnia_leds *leds;
struct omnia_led *led; struct omnia_led *led;
int ret, count; int ret, count;
@ -497,12 +497,10 @@ static int omnia_leds_probe(struct i2c_client *client)
} }
led = &leds->leds[0]; led = &leds->leds[0];
for_each_available_child_of_node(np, child) { for_each_available_child_of_node_scoped(np, child) {
ret = omnia_led_register(client, led, child); ret = omnia_led_register(client, led, child);
if (ret < 0) { if (ret < 0)
of_node_put(child);
return ret; return ret;
}
led += ret; led += ret;
} }
@ -532,6 +530,7 @@ static const struct of_device_id of_omnia_leds_match[] = {
{ .compatible = "cznic,turris-omnia-leds", }, { .compatible = "cznic,turris-omnia-leds", },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, of_omnia_leds_match);
static const struct i2c_device_id omnia_id[] = { static const struct i2c_device_id omnia_id[] = {
{ "omnia" }, { "omnia" },

View File

@ -1368,7 +1368,6 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
{ {
struct led_init_data init_data = {}; struct led_init_data init_data = {};
struct led_classdev *cdev; struct led_classdev *cdev;
struct device_node *child;
struct mc_subled *info; struct mc_subled *info;
struct lpg_led *led; struct lpg_led *led;
const char *state; const char *state;
@ -1399,12 +1398,10 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
i = 0; i = 0;
for_each_available_child_of_node(np, child) { for_each_available_child_of_node_scoped(np, child) {
ret = lpg_parse_channel(lpg, child, &led->channels[i]); ret = lpg_parse_channel(lpg, child, &led->channels[i]);
if (ret < 0) { if (ret < 0)
of_node_put(child);
return ret; return ret;
}
info[i].color_index = led->channels[i]->color; info[i].color_index = led->channels[i]->color;
info[i].intensity = 0; info[i].intensity = 0;
@ -1600,7 +1597,6 @@ static int lpg_init_sdam(struct lpg *lpg)
static int lpg_probe(struct platform_device *pdev) static int lpg_probe(struct platform_device *pdev)
{ {
struct device_node *np;
struct lpg *lpg; struct lpg *lpg;
int ret; int ret;
int i; int i;
@ -1640,13 +1636,11 @@ static int lpg_probe(struct platform_device *pdev)
if (ret < 0) if (ret < 0)
return ret; return ret;
for_each_available_child_of_node(pdev->dev.of_node, np) { for_each_available_child_of_node_scoped(pdev->dev.of_node, np) {
ret = lpg_add_led(lpg, np); ret = lpg_add_led(lpg, np);
if (ret) { if (ret)
of_node_put(np);
return ret; return ret;
} }
}
for (i = 0; i < lpg->num_channels; i++) for (i = 0; i < lpg->num_channels; i++)
lpg_apply_dtest(&lpg->channels[i]); lpg_apply_dtest(&lpg->channels[i]);

View File

@ -39,6 +39,8 @@
* (has carrier) or not * (has carrier) or not
* tx - LED blinks on transmitted data * tx - LED blinks on transmitted data
* rx - LED blinks on receive data * rx - LED blinks on receive data
* tx_err - LED blinks on transmit error
* rx_err - LED blinks on receive error
* *
* Note: If the user selects a mode that is not supported by hw, default * Note: If the user selects a mode that is not supported by hw, default
* behavior is to fall back to software control of the LED. However not every * behavior is to fall back to software control of the LED. However not every
@ -144,7 +146,9 @@ static void set_baseline_state(struct led_netdev_data *trigger_data)
* checking stats * checking stats
*/ */
if (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) || if (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ||
test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode)) test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ||
test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) ||
test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode))
schedule_delayed_work(&trigger_data->work, 0); schedule_delayed_work(&trigger_data->work, 0);
} }
} }
@ -337,6 +341,8 @@ static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
case TRIGGER_NETDEV_FULL_DUPLEX: case TRIGGER_NETDEV_FULL_DUPLEX:
case TRIGGER_NETDEV_TX: case TRIGGER_NETDEV_TX:
case TRIGGER_NETDEV_RX: case TRIGGER_NETDEV_RX:
case TRIGGER_NETDEV_TX_ERR:
case TRIGGER_NETDEV_RX_ERR:
bit = attr; bit = attr;
break; break;
default: default:
@ -371,6 +377,8 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
case TRIGGER_NETDEV_FULL_DUPLEX: case TRIGGER_NETDEV_FULL_DUPLEX:
case TRIGGER_NETDEV_TX: case TRIGGER_NETDEV_TX:
case TRIGGER_NETDEV_RX: case TRIGGER_NETDEV_RX:
case TRIGGER_NETDEV_TX_ERR:
case TRIGGER_NETDEV_RX_ERR:
bit = attr; bit = attr;
break; break;
default: default:
@ -429,6 +437,8 @@ DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX);
DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX); DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX);
DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX); DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);
DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX); DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX);
DEFINE_NETDEV_TRIGGER(tx_err, TRIGGER_NETDEV_TX_ERR);
DEFINE_NETDEV_TRIGGER(rx_err, TRIGGER_NETDEV_RX_ERR);
static ssize_t interval_show(struct device *dev, static ssize_t interval_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
@ -538,6 +548,8 @@ static struct attribute *netdev_trig_attrs[] = {
&dev_attr_half_duplex.attr, &dev_attr_half_duplex.attr,
&dev_attr_rx.attr, &dev_attr_rx.attr,
&dev_attr_tx.attr, &dev_attr_tx.attr,
&dev_attr_rx_err.attr,
&dev_attr_tx_err.attr,
&dev_attr_interval.attr, &dev_attr_interval.attr,
&dev_attr_offloaded.attr, &dev_attr_offloaded.attr,
NULL NULL
@ -628,7 +640,9 @@ static void netdev_trig_work(struct work_struct *work)
/* If we are not looking for RX/TX then return */ /* If we are not looking for RX/TX then return */
if (!test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) && if (!test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) &&
!test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode)) !test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) &&
!test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) &&
!test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode))
return; return;
dev_stats = dev_get_stats(trigger_data->net_dev, &temp); dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
@ -636,7 +650,11 @@ static void netdev_trig_work(struct work_struct *work)
(test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ? (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ?
dev_stats->tx_packets : 0) + dev_stats->tx_packets : 0) +
(test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ? (test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ?
dev_stats->rx_packets : 0); dev_stats->rx_packets : 0) +
(test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) ?
dev_stats->tx_errors : 0) +
(test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode) ?
dev_stats->rx_errors : 0);
if (trigger_data->last_activity != new_activity) { if (trigger_data->last_activity != new_activity) {
led_stop_software_blink(trigger_data->led_cdev); led_stop_software_blink(trigger_data->led_cdev);

View File

@ -611,6 +611,8 @@ enum led_trigger_netdev_modes {
TRIGGER_NETDEV_FULL_DUPLEX, TRIGGER_NETDEV_FULL_DUPLEX,
TRIGGER_NETDEV_TX, TRIGGER_NETDEV_TX,
TRIGGER_NETDEV_RX, TRIGGER_NETDEV_RX,
TRIGGER_NETDEV_TX_ERR,
TRIGGER_NETDEV_RX_ERR,
/* Keep last */ /* Keep last */
__TRIGGER_NETDEV_MAX, __TRIGGER_NETDEV_MAX,