- 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:
commit
f2debe057f
Documentation
devicetree/bindings/leds
leds
drivers/leds
Kconfig
flash
leds-88pm860x.cleds-aw2013.cleds-bcm6328.cleds-bcm6358.cleds-bd2606mvv.cleds-blinkm.cleds-gpio.cleds-is31fl319x.cleds-is31fl32xx.cleds-lp55xx-common.cleds-lp55xx-common.hleds-mc13783.cleds-mt6323.cleds-netxbig.cleds-pca9532.cleds-pca995x.cleds-sc27xx-bltc.cleds-sun50i-a100.cleds-turris-omnia.crgb
trigger
include/linux
@ -113,6 +113,8 @@ properties:
|
||||
# LED indicates NAND memory activity (deprecated),
|
||||
# in new implementations use "mtd"
|
||||
- nand-disk
|
||||
# LED indicates network activity
|
||||
- netdev
|
||||
# No trigger assigned to the LED. This is the default mode
|
||||
# if trigger is absent
|
||||
- none
|
||||
|
@ -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
|
@ -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>;
|
||||
};
|
||||
};
|
@ -11,19 +11,21 @@ maintainers:
|
||||
- Marek Vasut <marex@denx.de>
|
||||
|
||||
description:
|
||||
The NXP PCA9952/PCA9955B are programmable LED controllers connected via I2C
|
||||
that can drive 16 separate lines. Each of them can be individually switched
|
||||
The NXP PCA995x family are programmable LED controllers connected via I2C
|
||||
that can drive separate lines. Each of them can be individually switched
|
||||
on and off, and brightness can be controlled via individual PWM.
|
||||
|
||||
Datasheets are available at
|
||||
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/PCA9956B.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- nxp,pca9952
|
||||
- nxp,pca9955b
|
||||
- nxp,pca9956b
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
84
Documentation/devicetree/bindings/leds/sprd,sc2731-bltc.yaml
Normal file
84
Documentation/devicetree/bindings/leds/sprd,sc2731-bltc.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
110
Documentation/devicetree/bindings/leds/ti.lm36922.yaml
Normal file
110
Documentation/devicetree/bindings/leds/ti.lm36922.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -13,9 +13,31 @@ The device accepts RGB and HSB color values through separate commands.
|
||||
Also you can store blinking sequences as "scripts" in
|
||||
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::
|
||||
@ -79,6 +101,7 @@ E.g.::
|
||||
|
||||
|
||||
|
||||
as of 6/2012
|
||||
as of 07/2024
|
||||
|
||||
dl9pf <at> gmx <dot> de
|
||||
jstrauss <at> mailbox <dot> org
|
||||
|
@ -72,6 +72,14 @@ Good: "platform:*:charging" (allwinner sun50i, leds-cht-wcove)
|
||||
|
||||
Good: ":backlight" (Motorola Droid 4)
|
||||
|
||||
* Indicators
|
||||
|
||||
Good: ":indicator" (Blinkm)
|
||||
|
||||
* RGB
|
||||
|
||||
Good: ":rgb" (Blinkm)
|
||||
|
||||
* Ethernet LEDs
|
||||
|
||||
Currently two types of Network LEDs are support, those controlled by
|
||||
|
@ -825,6 +825,14 @@ config LEDS_BLINKM
|
||||
This option enables support for the BlinkM RGB LED connected
|
||||
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
|
||||
tristate "LED support for PowerNV Platform"
|
||||
depends on LEDS_CLASS
|
||||
|
@ -7,6 +7,7 @@
|
||||
* Author: Jacek Anaszewski <j.anaszewski@samsung.com>
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.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 *dev = &led->pdev->dev;
|
||||
struct device_node *child_node;
|
||||
#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
|
||||
struct pinctrl *pinctrl;
|
||||
#endif
|
||||
@ -246,7 +246,8 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
|
||||
}
|
||||
#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) {
|
||||
dev_err(dev, "No DT child node found for connected LED.\n");
|
||||
return -EINVAL;
|
||||
@ -267,7 +268,7 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"flash-max-microamp DT property missing\n");
|
||||
goto err_parse_dt;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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) {
|
||||
dev_err(dev,
|
||||
"flash-max-timeout-us DT property missing\n");
|
||||
goto err_parse_dt;
|
||||
return ret;
|
||||
}
|
||||
|
||||
*sub_node = child_node;
|
||||
|
||||
err_parse_dt:
|
||||
of_node_put(child_node);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aat1290_led_validate_mm_current(struct aat1290_led *led,
|
||||
|
@ -478,14 +478,12 @@ static int as3645a_detect(struct as3645a *flash)
|
||||
return as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE);
|
||||
}
|
||||
|
||||
static int as3645a_parse_node(struct as3645a *flash,
|
||||
struct fwnode_handle *fwnode)
|
||||
static int as3645a_parse_node(struct device *dev, struct as3645a *flash)
|
||||
{
|
||||
struct as3645a_config *cfg = &flash->cfg;
|
||||
struct fwnode_handle *child;
|
||||
int rval;
|
||||
|
||||
fwnode_for_each_child_node(fwnode, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
u32 id = 0;
|
||||
|
||||
fwnode_property_read_u32(child, "reg", &id);
|
||||
@ -686,7 +684,7 @@ static int as3645a_probe(struct i2c_client *client)
|
||||
|
||||
flash->client = client;
|
||||
|
||||
rval = as3645a_parse_node(flash, dev_fwnode(&client->dev));
|
||||
rval = as3645a_parse_node(&client->dev, flash);
|
||||
if (rval < 0)
|
||||
return rval;
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
* Ingi Kim <ingi2.kim@samsung.com>
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.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 device_node *np = dev_of_node(dev);
|
||||
struct device_node *child_node;
|
||||
int ret;
|
||||
|
||||
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) {
|
||||
dev_err(dev, "No DT child node found for connected LED.\n");
|
||||
return -EINVAL;
|
||||
@ -252,26 +253,24 @@ static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
|
||||
&cfg->movie_max_microamp);
|
||||
if (ret) {
|
||||
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",
|
||||
&cfg->flash_max_microamp);
|
||||
if (ret) {
|
||||
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",
|
||||
&cfg->flash_max_timeout);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to parse flash-max-timeout-us\n");
|
||||
goto err_parse_dt;
|
||||
return ret;
|
||||
}
|
||||
|
||||
err_parse_dt:
|
||||
of_node_put(child_node);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct led_flash_ops flash_ops = {
|
||||
|
@ -190,7 +190,7 @@ static int lm3601x_brightness_set(struct led_classdev *cdev,
|
||||
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)
|
||||
goto out;
|
||||
|
||||
@ -341,8 +341,9 @@ static int lm3601x_register_leds(struct lm3601x_led *led,
|
||||
|
||||
led_cdev = &led->fled_cdev.led_cdev;
|
||||
led_cdev->brightness_set_blocking = lm3601x_brightness_set;
|
||||
led_cdev->max_brightness = DIV_ROUND_UP(led->torch_current_max,
|
||||
LM3601X_TORCH_REG_DIV);
|
||||
led_cdev->max_brightness =
|
||||
DIV_ROUND_UP(led->torch_current_max - LM3601X_MIN_TORCH_I_UA + 1,
|
||||
LM3601X_TORCH_REG_DIV);
|
||||
led_cdev->flags |= LED_DEV_CAP_FLASH;
|
||||
|
||||
init_data.fwnode = fwnode;
|
||||
@ -386,6 +387,14 @@ static int lm3601x_parse_node(struct lm3601x_led *led,
|
||||
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",
|
||||
&led->flash_current_max);
|
||||
if (ret) {
|
||||
@ -434,6 +443,10 @@ static int lm3601x_probe(struct i2c_client *client)
|
||||
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);
|
||||
|
||||
return lm3601x_register_leds(led, fwnode);
|
||||
|
@ -599,7 +599,7 @@ static int max77693_led_parse_dt(struct max77693_led_device *led,
|
||||
{
|
||||
struct device *dev = &led->pdev->dev;
|
||||
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;
|
||||
u32 led_sources[2];
|
||||
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,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);
|
||||
if (prop) {
|
||||
const __be32 *srcs = NULL;
|
||||
@ -622,7 +622,6 @@ static int max77693_led_parse_dt(struct max77693_led_device *led,
|
||||
} else {
|
||||
dev_err(dev,
|
||||
"led-sources DT property missing\n");
|
||||
of_node_put(child_node);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -638,18 +637,16 @@ static int max77693_led_parse_dt(struct max77693_led_device *led,
|
||||
} else {
|
||||
dev_err(dev,
|
||||
"Wrong led-sources DT property value.\n");
|
||||
of_node_put(child_node);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sub_nodes[fled_id]) {
|
||||
dev_err(dev,
|
||||
"Conflicting \"led-sources\" DT properties\n");
|
||||
of_node_put(child_node);
|
||||
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;
|
||||
|
||||
cfg->label[fled_id] =
|
||||
@ -681,10 +678,8 @@ static int max77693_led_parse_dt(struct max77693_led_device *led,
|
||||
|
||||
if (++cfg->num_leds == 2 ||
|
||||
(max77693_fled_used(led, FLED1) &&
|
||||
max77693_fled_used(led, FLED2))) {
|
||||
of_node_put(child_node);
|
||||
max77693_fled_used(led, FLED2)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg->num_leds == 0) {
|
||||
@ -968,7 +963,7 @@ static int max77693_led_probe(struct platform_device *pdev)
|
||||
|
||||
ret = max77693_setup(led, &led_cfg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto err_setup;
|
||||
|
||||
mutex_init(&led->lock);
|
||||
|
||||
@ -1000,6 +995,8 @@ static int max77693_led_probe(struct platform_device *pdev)
|
||||
else
|
||||
goto err_register_led1;
|
||||
}
|
||||
of_node_put(sub_nodes[i]);
|
||||
sub_nodes[i] = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1013,6 +1010,9 @@ err_register_led2:
|
||||
err_register_led1:
|
||||
mutex_destroy(&led->lock);
|
||||
|
||||
err_setup:
|
||||
for (i = FLED1; i <= FLED2; i++)
|
||||
of_node_put(sub_nodes[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// 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>
|
||||
@ -14,6 +14,9 @@
|
||||
#include <media/v4l2-flash-led-class.h>
|
||||
|
||||
/* registers definitions */
|
||||
#define FLASH_REVISION_REG 0x00
|
||||
#define FLASH_4CH_REVISION_V0P1 0x01
|
||||
|
||||
#define FLASH_TYPE_REG 0x04
|
||||
#define FLASH_TYPE_VAL 0x18
|
||||
|
||||
@ -73,6 +76,16 @@
|
||||
|
||||
#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 {
|
||||
QCOM_MVFLASH_3CH,
|
||||
QCOM_MVFLASH_4CH,
|
||||
@ -98,6 +111,9 @@ enum {
|
||||
REG_IRESOLUTION,
|
||||
REG_CHAN_STROBE,
|
||||
REG_CHAN_EN,
|
||||
REG_THERM_THRSH1,
|
||||
REG_THERM_THRSH2,
|
||||
REG_THERM_THRSH3,
|
||||
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_ID(0x49, 0, 2, 3, 1), /* chan_strobe */
|
||||
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] = {
|
||||
@ -123,6 +142,8 @@ static struct reg_field mvflash_4ch_regs[REG_MAX_COUNT] = {
|
||||
REG_FIELD(0x49, 0, 3), /* iresolution */
|
||||
REG_FIELD_ID(0x4a, 0, 6, 4, 1), /* chan_strobe */
|
||||
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 {
|
||||
@ -130,9 +151,11 @@ struct qcom_flash_data {
|
||||
struct regmap_field *r_fields[REG_MAX_COUNT];
|
||||
struct mutex lock;
|
||||
enum hw_type hw_type;
|
||||
u32 total_ma;
|
||||
u8 leds_count;
|
||||
u8 max_channels;
|
||||
u8 chan_en_bits;
|
||||
u8 revision;
|
||||
};
|
||||
|
||||
struct qcom_flash_led {
|
||||
@ -143,6 +166,7 @@ struct qcom_flash_led {
|
||||
u32 max_timeout_ms;
|
||||
u32 flash_current_ma;
|
||||
u32 flash_timeout_ms;
|
||||
u32 current_in_use_ma;
|
||||
u8 *chan_id;
|
||||
u8 chan_count;
|
||||
bool enabled;
|
||||
@ -172,6 +196,127 @@ static int set_flash_module_en(struct qcom_flash_led *led, bool en)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
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);
|
||||
if (rc)
|
||||
return rc;
|
||||
@ -429,6 +578,10 @@ static int qcom_flash_led_brightness_set(struct led_classdev *led_cdev,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = update_allowed_flash_current(led, ¤t_ma, enable);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = set_flash_current(led, current_ma, TORCH_MODE);
|
||||
if (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->max_channels = 4;
|
||||
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 {
|
||||
dev_err(dev, "flash LED subtype %#x is not yet supported\n", val);
|
||||
return -ENODEV;
|
||||
|
@ -115,7 +115,7 @@ static int pm860x_led_set(struct led_classdev *cdev,
|
||||
static int pm860x_led_dt_init(struct platform_device *pdev,
|
||||
struct pm860x_led *data)
|
||||
{
|
||||
struct device_node *nproot, *np;
|
||||
struct device_node *nproot;
|
||||
int iset = 0;
|
||||
|
||||
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");
|
||||
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)) {
|
||||
of_property_read_u32(np, "marvell,88pm860x-iset",
|
||||
&iset);
|
||||
data->iset = PM8606_LED_CURRENT(iset);
|
||||
of_node_put(np);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -263,7 +263,7 @@ out:
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
struct led_init_data init_data = {};
|
||||
u32 source;
|
||||
u32 imax;
|
||||
@ -304,10 +304,8 @@ static int aw2013_probe_dt(struct aw2013 *chip)
|
||||
|
||||
ret = devm_led_classdev_register_ext(&chip->client->dev,
|
||||
&led->cdev, &init_data);
|
||||
if (ret < 0) {
|
||||
of_node_put(child);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
@ -392,7 +392,6 @@ static int bcm6328_leds_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev_of_node(&pdev->dev);
|
||||
struct device_node *child;
|
||||
void __iomem *mem;
|
||||
spinlock_t *lock; /* memory lock */
|
||||
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;
|
||||
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;
|
||||
u32 reg;
|
||||
|
||||
@ -454,10 +453,8 @@ static int bcm6328_leds_probe(struct platform_device *pdev)
|
||||
rc = bcm6328_led(dev, child, reg, mem, lock,
|
||||
blink_leds, blink_delay);
|
||||
|
||||
if (rc < 0) {
|
||||
of_node_put(child);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -147,7 +147,6 @@ static int bcm6358_leds_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev_of_node(&pdev->dev);
|
||||
struct device_node *child;
|
||||
void __iomem *mem;
|
||||
spinlock_t *lock; /* memory lock */
|
||||
unsigned long val;
|
||||
@ -184,7 +183,7 @@ static int bcm6358_leds_probe(struct platform_device *pdev)
|
||||
}
|
||||
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;
|
||||
u32 reg;
|
||||
|
||||
@ -198,10 +197,8 @@ static int bcm6358_leds_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
rc = bcm6358_led(dev, child, reg, mem, lock);
|
||||
if (rc < 0) {
|
||||
of_node_put(child);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -69,16 +69,14 @@ static const struct regmap_config bd2606mvv_regmap = {
|
||||
|
||||
static int bd2606mvv_probe(struct i2c_client *client)
|
||||
{
|
||||
struct fwnode_handle *np, *child;
|
||||
struct device *dev = &client->dev;
|
||||
struct bd2606mvv_priv *priv;
|
||||
struct fwnode_handle *led_fwnodes[BD2606_MAX_LEDS] = { 0 };
|
||||
int active_pairs[BD2606_MAX_LEDS / 2] = { 0 };
|
||||
int err, reg;
|
||||
int i;
|
||||
int i, j;
|
||||
|
||||
np = dev_fwnode(dev);
|
||||
if (!np)
|
||||
if (!dev_fwnode(dev))
|
||||
return -ENODEV;
|
||||
|
||||
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);
|
||||
|
||||
fwnode_for_each_available_child_node(np, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
struct bd2606mvv_led *led;
|
||||
|
||||
err = fwnode_property_read_u32(child, "reg", ®);
|
||||
if (err) {
|
||||
fwnode_handle_put(child);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
if (reg < 0 || reg >= BD2606_MAX_LEDS || led_fwnodes[reg]) {
|
||||
fwnode_handle_put(child);
|
||||
|
||||
if (reg < 0 || reg >= BD2606_MAX_LEDS || led_fwnodes[reg])
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
led = &priv->leds[reg];
|
||||
led_fwnodes[reg] = child;
|
||||
led_fwnodes[reg] = fwnode_handle_get(child);
|
||||
active_pairs[reg / 2]++;
|
||||
led->priv = priv;
|
||||
led->led_no = reg;
|
||||
@ -130,7 +126,8 @@ static int bd2606mvv_probe(struct i2c_client *client)
|
||||
&priv->leds[i].ldev,
|
||||
&init_data);
|
||||
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,
|
||||
"couldn't register LED %s\n",
|
||||
priv->leds[i].ldev.name);
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* leds-blinkm.c
|
||||
* (c) Jan-Simon Möller (dl9pf@gmx.de)
|
||||
* (c) Joseph Strauss (jstrauss@mailbox.org)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -15,6 +16,10 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/leds.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*/
|
||||
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_test_run(struct i2c_client *client);
|
||||
|
||||
/* Contains structs for both the color-separated sysfs classes, and the new multicolor class */
|
||||
struct blinkm_led {
|
||||
struct i2c_client *i2c_client;
|
||||
struct led_classdev led_cdev;
|
||||
union {
|
||||
/* used when multicolor support is disabled */
|
||||
struct led_classdev led_cdev;
|
||||
struct led_classdev_mc mcled_cdev;
|
||||
} cdev;
|
||||
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 i2c_client *i2c_client;
|
||||
struct mutex update_lock;
|
||||
/* used for led class interface */
|
||||
struct blinkm_led blinkm_leds[3];
|
||||
struct blinkm_led blinkm_leds[NUM_LEDS];
|
||||
/* used for "blinkm" sysfs interface */
|
||||
u8 red; /* color red */
|
||||
u8 green; /* color green */
|
||||
@ -419,11 +430,29 @@ static int blinkm_transfer_hw(struct i2c_client *client, int cmd)
|
||||
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,
|
||||
enum led_brightness value, int color)
|
||||
{
|
||||
/* 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);
|
||||
|
||||
switch (color) {
|
||||
@ -565,25 +594,147 @@ static int blinkm_detect(struct i2c_client *client, struct i2c_board_info *info)
|
||||
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)
|
||||
{
|
||||
struct blinkm_data *data;
|
||||
struct blinkm_led *led[3];
|
||||
int err, i;
|
||||
char blinkm_led_name[28];
|
||||
int err;
|
||||
|
||||
data = devm_kzalloc(&client->dev,
|
||||
sizeof(struct blinkm_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->i2c_addr = 0x08;
|
||||
/* i2c addr - use fake addr of 0x08 initially (real is 0x09) */
|
||||
data->fw_ver = 0xfe;
|
||||
/* firmware version - use fake until we read real value
|
||||
* (currently broken - BlinkM confused!) */
|
||||
* (currently broken - BlinkM confused!)
|
||||
*/
|
||||
data->script_id = 0x01;
|
||||
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);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "couldn't register sysfs group\n");
|
||||
goto exit;
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
/* RED = 0, GREEN = 1, BLUE = 2 */
|
||||
led[i] = &data->blinkm_leds[i];
|
||||
led[i]->i2c_client = client;
|
||||
led[i]->id = i;
|
||||
led[i]->led_cdev.max_brightness = 255;
|
||||
led[i]->led_cdev.flags = LED_CORE_SUSPENDRESUME;
|
||||
switch (i) {
|
||||
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 */
|
||||
if (!IS_ENABLED(CONFIG_LEDS_BLINKM_MULTICOLOR)) {
|
||||
err = register_separate_colors(client, data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else {
|
||||
err = register_multicolor(client, data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Initialize the blinkm */
|
||||
blinkm_init_hw(client);
|
||||
|
||||
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)
|
||||
@ -683,8 +770,8 @@ static void blinkm_remove(struct i2c_client *client)
|
||||
int i;
|
||||
|
||||
/* make sure no workqueue entries are pending */
|
||||
for (i = 0; i < 3; i++)
|
||||
led_classdev_unregister(&data->blinkm_leds[i].led_cdev);
|
||||
for (i = 0; i < NUM_LEDS; i++)
|
||||
led_classdev_unregister(&data->blinkm_leds[i].cdev.led_cdev);
|
||||
|
||||
/* reset rgb */
|
||||
data->next_red = 0x00;
|
||||
@ -740,6 +827,7 @@ static struct i2c_driver blinkm_driver = {
|
||||
module_i2c_driver(blinkm_driver);
|
||||
|
||||
MODULE_AUTHOR("Jan-Simon Moeller <dl9pf@gmx.de>");
|
||||
MODULE_AUTHOR("Joseph Strauss <jstrauss@mailbox.org>");
|
||||
MODULE_DESCRIPTION("BlinkM RGB LED driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
@ -150,7 +150,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct device *dev)
|
||||
{
|
||||
struct fwnode_handle *child;
|
||||
struct gpio_leds_priv *priv;
|
||||
int count, ret;
|
||||
int count, used, ret;
|
||||
|
||||
count = device_get_child_node_count(dev);
|
||||
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);
|
||||
if (!priv)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
priv->num_leds = count;
|
||||
used = 0;
|
||||
|
||||
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 = {};
|
||||
|
||||
/*
|
||||
@ -197,8 +199,9 @@ static struct gpio_leds_priv *gpio_leds_create(struct device *dev)
|
||||
/* Set gpiod label to match the corresponding LED name. */
|
||||
gpiod_set_consumer_name(led_dat->gpiod,
|
||||
led_dat->cdev.dev->kobj.name);
|
||||
priv->num_leds++;
|
||||
used++;
|
||||
}
|
||||
priv->num_leds = used;
|
||||
|
||||
return priv;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
struct fwnode_handle *fwnode = dev_fwnode(dev), *child;
|
||||
struct fwnode_handle *fwnode = dev_fwnode(dev);
|
||||
int count;
|
||||
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);
|
||||
|
||||
count = 0;
|
||||
fwnode_for_each_available_child_node(fwnode, child)
|
||||
device_for_each_child_node_scoped(dev, child)
|
||||
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",
|
||||
is31->cdef->num_leds);
|
||||
|
||||
fwnode_for_each_available_child_node(fwnode, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
struct is31fl319x_led *led;
|
||||
u32 reg;
|
||||
|
||||
ret = fwnode_property_read_u32(child, "reg", ®);
|
||||
if (ret) {
|
||||
ret = dev_err_probe(dev, ret, "Failed to read led 'reg' property\n");
|
||||
goto put_child_node;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to read led 'reg' property\n");
|
||||
|
||||
if (reg < 1 || reg > is31->cdef->num_leds) {
|
||||
ret = dev_err_probe(dev, -EINVAL, "invalid led reg %u\n", reg);
|
||||
goto put_child_node;
|
||||
}
|
||||
if (reg < 1 || reg > is31->cdef->num_leds)
|
||||
return dev_err_probe(dev, -EINVAL, "invalid led reg %u\n", reg);
|
||||
|
||||
led = &is31->leds[reg - 1];
|
||||
|
||||
if (led->configured) {
|
||||
ret = dev_err_probe(dev, -EINVAL, "led %u is already configured\n", reg);
|
||||
goto put_child_node;
|
||||
}
|
||||
if (led->configured)
|
||||
return dev_err_probe(dev, -EINVAL, "led %u is already configured\n", reg);
|
||||
|
||||
ret = is31fl319x_parse_child_fw(dev, child, led, is31);
|
||||
if (ret) {
|
||||
ret = dev_err_probe(dev, ret, "led %u DT parsing failed\n", reg);
|
||||
goto put_child_node;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "led %u DT parsing failed\n", reg);
|
||||
|
||||
led->configured = true;
|
||||
}
|
||||
@ -454,10 +446,6 @@ static int is31fl319x_parse_fw(struct device *dev, struct is31fl319x_chip *is31)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
put_child_node:
|
||||
fwnode_handle_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int is31fl3190_microamp_to_cs(struct device *dev, u32 microamp)
|
||||
|
@ -363,10 +363,9 @@ static struct is31fl32xx_led_data *is31fl32xx_find_led_data(
|
||||
static int is31fl32xx_parse_dt(struct device *dev,
|
||||
struct is31fl32xx_priv *priv)
|
||||
{
|
||||
struct device_node *child;
|
||||
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 is31fl32xx_led_data *led_data =
|
||||
&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);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
/* Detect if channel is already in use by another child */
|
||||
other_led_data = is31fl32xx_find_led_data(priv,
|
||||
@ -385,8 +384,7 @@ static int is31fl32xx_parse_dt(struct device *dev,
|
||||
dev_err(dev,
|
||||
"Node %pOF 'reg' conflicts with another LED\n",
|
||||
child);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
init_data.fwnode = of_fwnode_handle(child);
|
||||
@ -396,17 +394,13 @@ static int is31fl32xx_parse_dt(struct device *dev,
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register LED for %pOF: %d\n",
|
||||
child, ret);
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->num_leds++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_is31fl32xx_match[] = {
|
||||
|
@ -965,24 +965,16 @@ EXPORT_SYMBOL_GPL(lp55xx_update_bits);
|
||||
bool lp55xx_is_extclk_used(struct lp55xx_chip *chip)
|
||||
{
|
||||
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))
|
||||
goto use_internal_clk;
|
||||
|
||||
err = clk_prepare_enable(clk);
|
||||
if (err)
|
||||
if (clk_get_rate(clk) != LP55XX_CLK_32K)
|
||||
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);
|
||||
|
||||
chip->clk = clk;
|
||||
return true;
|
||||
|
||||
use_internal_clk:
|
||||
@ -995,9 +987,6 @@ static void lp55xx_deinit_device(struct lp55xx_chip *chip)
|
||||
{
|
||||
struct lp55xx_platform_data *pdata = chip->pdata;
|
||||
|
||||
if (chip->clk)
|
||||
clk_disable_unprepare(chip->clk);
|
||||
|
||||
if (pdata->enable_gpiod)
|
||||
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,
|
||||
int child_number)
|
||||
{
|
||||
struct device_node *child;
|
||||
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,
|
||||
num_colors);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
num_colors++;
|
||||
}
|
||||
|
||||
|
@ -193,7 +193,6 @@ struct lp55xx_engine {
|
||||
*/
|
||||
struct lp55xx_chip {
|
||||
struct i2c_client *cl;
|
||||
struct clk *clk;
|
||||
struct lp55xx_platform_data *pdata;
|
||||
struct mutex lock; /* lock for user-space interface */
|
||||
int num_leds;
|
||||
|
@ -12,6 +12,7 @@
|
||||
* Eric Miao <eric.miao@marvell.com>
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.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_platform_data *pdata;
|
||||
struct device_node *parent, *child;
|
||||
struct device_node *child;
|
||||
struct device *dev = &pdev->dev;
|
||||
int i = 0, ret = -ENODATA;
|
||||
|
||||
@ -121,24 +122,23 @@ static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(
|
||||
if (!pdata)
|
||||
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)
|
||||
goto out_node_put;
|
||||
return ERR_PTR(-ENODATA);
|
||||
|
||||
ret = of_property_read_u32_array(parent, "led-control",
|
||||
pdata->led_control,
|
||||
leds->devtype->num_regs);
|
||||
if (ret)
|
||||
goto out_node_put;
|
||||
return ERR_PTR(ret);
|
||||
|
||||
pdata->num_leds = of_get_available_child_count(parent);
|
||||
|
||||
pdata->led = devm_kcalloc(dev, pdata->num_leds, sizeof(*pdata->led),
|
||||
GFP_KERNEL);
|
||||
if (!pdata->led) {
|
||||
ret = -ENOMEM;
|
||||
goto out_node_put;
|
||||
}
|
||||
if (!pdata->led)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for_each_available_child_of_node(parent, child) {
|
||||
const char *str;
|
||||
@ -158,12 +158,10 @@ static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(
|
||||
}
|
||||
|
||||
pdata->num_leds = i;
|
||||
ret = i > 0 ? 0 : -ENODATA;
|
||||
if (i <= 0)
|
||||
return ERR_PTR(-ENODATA);
|
||||
|
||||
out_node_put:
|
||||
of_node_put(parent);
|
||||
|
||||
return ret ? ERR_PTR(ret) : pdata;
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
static inline struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(
|
||||
|
@ -527,7 +527,6 @@ static int mt6323_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev_of_node(dev);
|
||||
struct device_node *child;
|
||||
struct mt6397_chip *hw = dev_get_drvdata(dev->parent);
|
||||
struct mt6323_leds *leds;
|
||||
struct mt6323_led *led;
|
||||
@ -565,28 +564,25 @@ static int mt6323_led_probe(struct platform_device *pdev)
|
||||
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 = {};
|
||||
bool is_wled;
|
||||
|
||||
ret = of_property_read_u32(child, "reg", ®);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to read led 'reg' property\n");
|
||||
goto put_child_node;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (reg >= max_leds || reg >= MAX_SUPPORTED_LEDS ||
|
||||
leds->led[reg]) {
|
||||
dev_err(dev, "Invalid led reg %u\n", reg);
|
||||
ret = -EINVAL;
|
||||
goto put_child_node;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
|
||||
if (!led) {
|
||||
ret = -ENOMEM;
|
||||
goto put_child_node;
|
||||
}
|
||||
if (!led)
|
||||
return -ENOMEM;
|
||||
|
||||
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) {
|
||||
dev_err(leds->dev,
|
||||
"Failed to LED set default from devicetree\n");
|
||||
goto put_child_node;
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_data.fwnode = of_fwnode_handle(child);
|
||||
@ -621,15 +617,11 @@ static int mt6323_led_probe(struct platform_device *pdev)
|
||||
&init_data);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register LED: %d\n", ret);
|
||||
goto put_child_node;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
put_child_node:
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mt6323_led_remove(struct platform_device *pdev)
|
||||
|
@ -423,7 +423,6 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
|
||||
struct device_node *gpio_ext_np;
|
||||
struct platform_device *gpio_ext_pdev;
|
||||
struct device *gpio_ext_dev;
|
||||
struct device_node *child;
|
||||
struct netxbig_gpio_ext *gpio_ext;
|
||||
struct netxbig_led_timer *timers;
|
||||
struct netxbig_led *leds, *led;
|
||||
@ -507,7 +506,7 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
|
||||
}
|
||||
|
||||
led = leds;
|
||||
for_each_available_child_of_node(np, child) {
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
const char *string;
|
||||
int *mode_val;
|
||||
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",
|
||||
&led->mode_addr);
|
||||
if (ret)
|
||||
goto err_node_put;
|
||||
goto put_device;
|
||||
|
||||
ret = of_property_read_u32(child, "bright-addr",
|
||||
&led->bright_addr);
|
||||
if (ret)
|
||||
goto err_node_put;
|
||||
goto put_device;
|
||||
|
||||
ret = of_property_read_u32(child, "max-brightness",
|
||||
&led->bright_max);
|
||||
if (ret)
|
||||
goto err_node_put;
|
||||
goto put_device;
|
||||
|
||||
mode_val =
|
||||
devm_kcalloc(dev,
|
||||
@ -533,7 +532,7 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
|
||||
GFP_KERNEL);
|
||||
if (!mode_val) {
|
||||
ret = -ENOMEM;
|
||||
goto err_node_put;
|
||||
goto put_device;
|
||||
}
|
||||
|
||||
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");
|
||||
if (ret < 0 || ret % 2) {
|
||||
ret = -EINVAL;
|
||||
goto err_node_put;
|
||||
goto put_device;
|
||||
}
|
||||
num_modes = ret / 2;
|
||||
if (num_modes > NETXBIG_LED_MODE_NUM) {
|
||||
ret = -EINVAL;
|
||||
goto err_node_put;
|
||||
goto put_device;
|
||||
}
|
||||
|
||||
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);
|
||||
if (mode >= NETXBIG_LED_MODE_NUM) {
|
||||
ret = -EINVAL;
|
||||
goto err_node_put;
|
||||
goto put_device;
|
||||
}
|
||||
mode_val[mode] = val;
|
||||
}
|
||||
@ -583,8 +582,6 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
|
||||
|
||||
return 0;
|
||||
|
||||
err_node_put:
|
||||
of_node_put(child);
|
||||
put_device:
|
||||
put_device(gpio_ext_dev);
|
||||
return ret;
|
||||
|
@ -215,8 +215,7 @@ static int pca9532_update_hw_blink(struct pca9532_led *led,
|
||||
if (other->state == PCA9532_PWM1) {
|
||||
if (other->ldev.blink_delay_on != delay_on ||
|
||||
other->ldev.blink_delay_off != delay_off) {
|
||||
dev_err(&led->client->dev,
|
||||
"HW can handle only one blink configuration at a time\n");
|
||||
/* HW can handle only one blink configuration at a time */
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -506,7 +505,6 @@ static struct pca9532_platform_data *
|
||||
pca9532_of_populate_pdata(struct device *dev, struct device_node *np)
|
||||
{
|
||||
struct pca9532_platform_data *pdata;
|
||||
struct device_node *child;
|
||||
int devid, maxleds;
|
||||
int i = 0;
|
||||
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],
|
||||
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",
|
||||
&pdata->leds[i].name))
|
||||
pdata->leds[i].name = child->name;
|
||||
@ -538,10 +536,8 @@ pca9532_of_populate_pdata(struct device *dev, struct device_node *np)
|
||||
else if (!strcmp(state, "keep"))
|
||||
pdata->leds[i].state = PCA9532_KEEP;
|
||||
}
|
||||
if (++i >= maxleds) {
|
||||
of_node_put(child);
|
||||
if (++i >= maxleds)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pdata;
|
||||
|
@ -19,10 +19,6 @@
|
||||
#define PCA995X_MODE1 0x00
|
||||
#define PCA995X_MODE2 0x01
|
||||
#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 */
|
||||
#define PCA995X_MODE1_CFG 0x00
|
||||
@ -34,17 +30,38 @@
|
||||
#define PCA995X_LDRX_MASK 0x3
|
||||
#define PCA995X_LDRX_BITS 2
|
||||
|
||||
#define PCA995X_MAX_OUTPUTS 16
|
||||
#define PCA995X_MAX_OUTPUTS 24
|
||||
#define PCA995X_OUTPUTS_PER_REG 4
|
||||
|
||||
#define PCA995X_IREFALL_FULL_CFG 0xFF
|
||||
#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)
|
||||
|
||||
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 {
|
||||
unsigned int led_no;
|
||||
struct led_classdev ldev;
|
||||
@ -54,7 +71,7 @@ struct pca995x_led {
|
||||
struct pca995x_chip {
|
||||
struct regmap *regmap;
|
||||
struct pca995x_led leds[PCA995X_MAX_OUTPUTS];
|
||||
int btype;
|
||||
const struct pca995x_chipdef *chipdef;
|
||||
};
|
||||
|
||||
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_chip *chip = led->chip;
|
||||
const struct pca995x_chipdef *chipdef = chip->chipdef;
|
||||
u8 ledout_addr, pwmout_addr;
|
||||
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);
|
||||
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)
|
||||
{
|
||||
struct fwnode_handle *led_fwnodes[PCA995X_MAX_OUTPUTS] = { 0 };
|
||||
struct fwnode_handle *np, *child;
|
||||
struct device *dev = &client->dev;
|
||||
const struct pca995x_chipdef *chipdef;
|
||||
struct pca995x_chip *chip;
|
||||
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 (!np)
|
||||
if (!dev_fwnode(dev))
|
||||
return -ENODEV;
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->btype = btype;
|
||||
chip->chipdef = chipdef;
|
||||
chip->regmap = devm_regmap_init_i2c(client, &pca995x_regmap);
|
||||
if (IS_ERR(chip->regmap))
|
||||
return PTR_ERR(chip->regmap);
|
||||
|
||||
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", ®);
|
||||
if (ret) {
|
||||
fwnode_handle_put(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (reg < 0 || reg >= PCA995X_MAX_OUTPUTS || led_fwnodes[reg]) {
|
||||
fwnode_handle_put(child);
|
||||
if (reg < 0 || reg >= PCA995X_MAX_OUTPUTS || led_fwnodes[reg])
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
led = &chip->leds[reg];
|
||||
led_fwnodes[reg] = child;
|
||||
led_fwnodes[reg] = fwnode_handle_get(child);
|
||||
led->chip = chip;
|
||||
led->led_no = reg;
|
||||
led->ldev.brightness_set_blocking = pca995x_brightness_set;
|
||||
@ -157,7 +170,8 @@ static int pca995x_probe(struct i2c_client *client)
|
||||
&chip->leds[i].ldev,
|
||||
&init_data);
|
||||
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,
|
||||
"Could not register LED %s\n",
|
||||
chip->leds[i].ldev.name);
|
||||
@ -170,21 +184,21 @@ static int pca995x_probe(struct i2c_client *client)
|
||||
return ret;
|
||||
|
||||
/* IREF Output current value for all LEDn outputs */
|
||||
return regmap_write(chip->regmap,
|
||||
btype ? PCA9955B_IREFALL : PCA9952_IREFALL,
|
||||
PCA995X_IREFALL_HALF_CFG);
|
||||
return regmap_write(chip->regmap, chipdef->irefall, PCA995X_IREFALL_HALF_CFG);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id pca995x_id[] = {
|
||||
{ "pca9952", .driver_data = (kernel_ulong_t)PCA995X_TYPE_NON_B },
|
||||
{ "pca9955b", .driver_data = (kernel_ulong_t)PCA995X_TYPE_B },
|
||||
{ "pca9952", .driver_data = (kernel_ulong_t)&pca9952_chipdef },
|
||||
{ "pca9955b", .driver_data = (kernel_ulong_t)&pca9955b_chipdef },
|
||||
{ "pca9956b", .driver_data = (kernel_ulong_t)&pca9956b_chipdef },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pca995x_id);
|
||||
|
||||
static const struct of_device_id pca995x_of_match[] = {
|
||||
{ .compatible = "nxp,pca9952", .data = (void *)PCA995X_TYPE_NON_B },
|
||||
{ .compatible = "nxp,pca9955b", .data = (void *)PCA995X_TYPE_B },
|
||||
{ .compatible = "nxp,pca9952", .data = &pca9952_chipdef },
|
||||
{ .compatible = "nxp,pca9955b", . data = &pca9955b_chipdef },
|
||||
{ .compatible = "nxp,pca9956b", .data = &pca9956b_chipdef },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pca995x_of_match);
|
||||
|
@ -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)
|
||||
{
|
||||
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;
|
||||
u32 base, count, reg;
|
||||
int err;
|
||||
@ -304,17 +304,13 @@ static int sc27xx_led_probe(struct platform_device *pdev)
|
||||
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", ®);
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) {
|
||||
of_node_put(child);
|
||||
if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->leds[reg].fwnode = of_fwnode_handle(child);
|
||||
priv->leds[reg].active = true;
|
||||
|
@ -368,7 +368,7 @@ static int sun50i_a100_ledc_suspend(struct device *dev)
|
||||
if (!xfer_active)
|
||||
break;
|
||||
|
||||
msleep(1);
|
||||
usleep_range(1000, 1100);
|
||||
}
|
||||
|
||||
clk_disable_unprepare(priv->mod_clk);
|
||||
|
@ -452,7 +452,7 @@ static int omnia_mcu_get_features(const struct i2c_client *client)
|
||||
static int omnia_leds_probe(struct i2c_client *client)
|
||||
{
|
||||
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_led *led;
|
||||
int ret, count;
|
||||
@ -497,12 +497,10 @@ static int omnia_leds_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
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);
|
||||
if (ret < 0) {
|
||||
of_node_put(child);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
led += ret;
|
||||
}
|
||||
@ -532,6 +530,7 @@ static const struct of_device_id of_omnia_leds_match[] = {
|
||||
{ .compatible = "cznic,turris-omnia-leds", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_omnia_leds_match);
|
||||
|
||||
static const struct i2c_device_id omnia_id[] = {
|
||||
{ "omnia" },
|
||||
|
@ -1368,7 +1368,6 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
|
||||
{
|
||||
struct led_init_data init_data = {};
|
||||
struct led_classdev *cdev;
|
||||
struct device_node *child;
|
||||
struct mc_subled *info;
|
||||
struct lpg_led *led;
|
||||
const char *state;
|
||||
@ -1399,12 +1398,10 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
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]);
|
||||
if (ret < 0) {
|
||||
of_node_put(child);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
info[i].color_index = led->channels[i]->color;
|
||||
info[i].intensity = 0;
|
||||
@ -1600,7 +1597,6 @@ static int lpg_init_sdam(struct lpg *lpg)
|
||||
|
||||
static int lpg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct lpg *lpg;
|
||||
int ret;
|
||||
int i;
|
||||
@ -1640,12 +1636,10 @@ static int lpg_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
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);
|
||||
if (ret) {
|
||||
of_node_put(np);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < lpg->num_channels; i++)
|
||||
|
@ -39,6 +39,8 @@
|
||||
* (has carrier) or not
|
||||
* tx - LED blinks on transmitted 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
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -337,6 +341,8 @@ static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
|
||||
case TRIGGER_NETDEV_FULL_DUPLEX:
|
||||
case TRIGGER_NETDEV_TX:
|
||||
case TRIGGER_NETDEV_RX:
|
||||
case TRIGGER_NETDEV_TX_ERR:
|
||||
case TRIGGER_NETDEV_RX_ERR:
|
||||
bit = attr;
|
||||
break;
|
||||
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_TX:
|
||||
case TRIGGER_NETDEV_RX:
|
||||
case TRIGGER_NETDEV_TX_ERR:
|
||||
case TRIGGER_NETDEV_RX_ERR:
|
||||
bit = attr;
|
||||
break;
|
||||
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(tx, TRIGGER_NETDEV_TX);
|
||||
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,
|
||||
struct device_attribute *attr, char *buf)
|
||||
@ -538,6 +548,8 @@ static struct attribute *netdev_trig_attrs[] = {
|
||||
&dev_attr_half_duplex.attr,
|
||||
&dev_attr_rx.attr,
|
||||
&dev_attr_tx.attr,
|
||||
&dev_attr_rx_err.attr,
|
||||
&dev_attr_tx_err.attr,
|
||||
&dev_attr_interval.attr,
|
||||
&dev_attr_offloaded.attr,
|
||||
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 (!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;
|
||||
|
||||
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) ?
|
||||
dev_stats->tx_packets : 0) +
|
||||
(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) {
|
||||
led_stop_software_blink(trigger_data->led_cdev);
|
||||
|
@ -611,6 +611,8 @@ enum led_trigger_netdev_modes {
|
||||
TRIGGER_NETDEV_FULL_DUPLEX,
|
||||
TRIGGER_NETDEV_TX,
|
||||
TRIGGER_NETDEV_RX,
|
||||
TRIGGER_NETDEV_TX_ERR,
|
||||
TRIGGER_NETDEV_RX_ERR,
|
||||
|
||||
/* Keep last */
|
||||
__TRIGGER_NETDEV_MAX,
|
||||
|
Loading…
Reference in New Issue
Block a user