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

Pull LED updates from Lee Jones:
 "New Support & Features:
   - Core: Extend netdev trigger speeds up to 100G
   - PWM Multicolor: Introduce default-intensity property
   - Analog Devices LTC3220: Add support for 18 channel LED driver
   - NXP PCA963x: Add multicolor LED class support

  Improvements & Fixes:
   - GPIO: Clear error pointers for skipped LEDs
   - Broadcom BCM63138: Use %pe to print pinctrl error instead of %ld
   - ISSI IS31FL319x: Modernize device registration by using fwnode APIs
   - NXP PCA9532: Fix inverted GPIO output polarity
   - NXP PCA9532: Fix phantom device registration on missing hardware
   - STMicroelectronics ST1202: Correct and extend hw_pattern
     documentation
   - STMicroelectronics ST1202: Fix channel disable logic on zero
     brightness and ensure brightness changes are applied in active mode
   - STMicroelectronics ST1202: Fix hardware pattern sequence
     programming, validate inputs, and correct pattern duration
     calculations
   - STMicroelectronics ST1202: Validate LED reg property against
     channel count
   - TI LP5860: Fix a potential double-unlock during device
     initialization and fix error handling path by using
     devm_mutex_init()

  Cleanups & Refactoring:
   - GPIO: Make legacy gpiolib interface optional

  Device Tree Binding Updates:
   - Core: Add default-intensity property
   - Core: Document "gpio" trigger
   - Analog Devices LTC3220: Add DT binding for LTC3220 18 channel LED
     driver
   - Broadcom BCM6358: Convert to DT schema
   - LaCie NS2: Convert to DT schema
   - NXP PCA963x: Add multicolor LED support
   - NXP PCA963x: Fix reg maximum for pca9635
   - TI TPS65217: Convert backlight bindings to DT schema"

* tag 'leds-next-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds: (29 commits)
  leds: is31fl319x: Modernize registration
  dt-bindings: leds: lacie,ns2-leds: Convert to DT schema
  leds: pca963x: Add multicolor LED class support
  dt-bindings: leds: nxp,pca963x: Add multicolor LED support
  dt-bindings: leds: nxp,pca963x: Fix reg maximum for pca9635
  leds: gpio: Clear error pointers for skipped LEDs
  dt-bindings: leds: backlight: Convert TPS65217 to DT schema
  leds: pca9532: Fix phantom device registration on missing hardware
  leds: gpio: Make legacy gpiolib interface optional
  leds: bcm63138: Use %pe to print pinctrl error instead of %ld
  dt-bindings: leds: Add default-intensity property
  leds: ltc3220: Add Support for LTC3220 18 channel LED Driver
  dt-bindings: leds: Add LTC3220 18 channel LED Driver
  dt-bindings: leds: bcm6358: Convert to DT schema
  dt-bindings: leds: Document "gpio" trigger
  leds: st1202: Correct and extend hw_pattern documentation
  leds: st1202: Validate LED reg property against channel count
  leds: st1202: Disable channel when brightness is set to zero
  leds: st1202: Fix brightness having no effect while pattern mode is active
  leds: st1202: Fix spurious pattern sequence start in setup
  ...
This commit is contained in:
Linus Torvalds
2026-08-27 11:05:51 -07:00
27 changed files with 1567 additions and 440 deletions

View File

@@ -0,0 +1,133 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/leds/adi,ltc3220.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices LTC3220 LED Driver
maintainers:
- Edelweise Escala <edelweise.escala@analog.com>
description: >
The LTC3220 is a multi-display LED driver, which contains a high-efficiency,
low-noise charge pump to provide power to up to 18 LED current sources.
The LEDs are individually configurable to 64-step linear brightness control,
blinking and gradation control via 2-wire I2C interface.
For more product information please see the link below
https://www.analog.com/en/products/ltc3220.html
properties:
compatible:
const: adi,ltc3220
reg:
maxItems: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
reset-gpios:
maxItems: 1
patternProperties:
'^led@([1-9a-f]|1[0-2])$':
type: object
$ref: /schemas/leds/common.yaml#
unevaluatedProperties: false
properties:
reg:
description:
Output channel for the LED (1-18 maps to LED outputs D1-D18).
Unit-address must be in hexadecimal (1-12 hex = 1-18 decimal).
For aggregated LED control, define only one LED node with reg = <0x1>
and use led-sources to list all controlled outputs. Only reg 1 should
be present when using led-sources.
items:
- minimum: 1
maximum: 18
required:
- reg
if:
required:
- led-sources
then:
properties:
reg:
items:
- const: 1
required:
- compatible
- reg
- '#address-cells'
- '#size-cells'
additionalProperties: false
examples:
- |
// Independent LEDs
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/leds/common.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
led-controller@1c {
compatible = "adi,ltc3220";
reg = <0x1c>;
#address-cells = <1>;
#size-cells = <0>;
reset-gpios = <&gpio 17 GPIO_ACTIVE_LOW>;
led@1 {
reg = <0x1>;
function = LED_FUNCTION_INDICATOR;
function-enumerator = <1>;
};
led@2 {
reg = <0x2>;
function = LED_FUNCTION_INDICATOR;
function-enumerator = <2>;
};
led@3 {
reg = <0x3>;
function = LED_FUNCTION_INDICATOR;
function-enumerator = <3>;
};
};
};
- |
// Aggregated LED
#include <dt-bindings/leds/common.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
led-controller@1c {
compatible = "adi,ltc3220";
reg = <0x1c>;
#address-cells = <1>;
#size-cells = <0>;
led@1 {
reg = <0x1>;
led-sources = <0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf 0x10 0x11 0x12>;
function = LED_FUNCTION_BACKLIGHT;
};
};
};
...

View File

@@ -0,0 +1,170 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/leds/backlight/ti,tps65217.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TPS65217 family of regulators
maintainers:
- Eduard Bostina <egbostina@gmail.com>
description:
The TPS65217 chip contains a boost converter and current sinks which can be
used to drive LEDs for use as backlights.
properties:
compatible:
const: ti,tps65217
reg:
maxItems: 1
description: I2C slave address
interrupts:
maxItems: 1
interrupt-controller: true
"#interrupt-cells":
const: 1
ti,pmic-shutdown-controller:
type: boolean
description:
Set the PMIC to shutdown on PWR_EN toggle.
backlight:
type: object
additionalProperties: false
description:
Node for specifying WLED1 and WLED2 lines in TPS65217.
properties:
isel:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 2]
description: |
Selection bit. Valid values:
1 - ISEL1 (low-level)
2 - ISEL2 (high-level)
fdim:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [100, 200, 500, 1000]
description:
PWM dimming frequency in Hz.
default-brightness:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 100
required:
- isel
- fdim
- default-brightness
regulators:
type: object
additionalProperties: false
description:
List of child nodes that specify the regulator initialization data.
Not all regulators for the given device need to be present.
patternProperties:
"^(dcdc[1-3]|ldo[1-4])$":
type: object
$ref: /schemas/regulator/regulator.yaml#
unevaluatedProperties: false
charger:
$ref: /schemas/power/supply/tps65217-charger.yaml#
unevaluatedProperties: false
pwrbutton:
$ref: /schemas/input/ti,tps65217-pwrbutton.yaml#
unevaluatedProperties: false
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic@24 {
compatible = "ti,tps65217";
reg = <0x24>;
interrupt-controller;
#interrupt-cells = <1>;
ti,pmic-shutdown-controller;
backlight {
isel = <1>; /* 1 - ISET1, 2 ISET2 */
fdim = <100>; /* TPS65217_BL_FDIM_100HZ */
default-brightness = <50>;
};
charger {
compatible = "ti,tps65217-charger";
interrupts = <0>, <1>;
interrupt-names = "USB", "AC";
};
pwrbutton {
compatible = "ti,tps65217-pwrbutton";
interrupts = <2>;
};
regulators {
dcdc1 {
regulator-name = "vdds_dpr";
regulator-always-on;
};
dcdc2 {
regulator-name = "vdd_mpu";
regulator-min-microvolt = <925000>;
regulator-max-microvolt = <1351500>;
regulator-boot-on;
regulator-always-on;
};
dcdc3 {
regulator-name = "vdd_core";
regulator-min-microvolt = <925000>;
regulator-max-microvolt = <1150000>;
regulator-boot-on;
regulator-always-on;
};
ldo1 {
regulator-name = "vio,vrtc,vdds";
regulator-always-on;
};
ldo2 {
regulator-name = "vdd_3v3aux";
regulator-always-on;
};
ldo3 {
regulator-name = "vdd_1v8";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-always-on;
};
ldo4 {
regulator-name = "vdd_3v3a";
regulator-always-on;
};
};
};
};

View File

@@ -1,27 +0,0 @@
TPS65217 family of regulators
The TPS65217 chip contains a boost converter and current sinks which can be
used to drive LEDs for use as backlights.
Required properties:
- compatible: "ti,tps65217"
- reg: I2C slave address
- backlight: node for specifying WLED1 and WLED2 lines in TPS65217
- isel: selection bit, valid values: 1 for ISEL1 (low-level) and 2 for ISEL2 (high-level)
- fdim: PWM dimming frequency, valid values: 100, 200, 500, 1000
- default-brightness: valid values: 0-100
Each regulator is defined using the standard binding for regulators.
Example:
tps: tps@24 {
reg = <0x24>;
compatible = "ti,tps65217";
backlight {
isel = <1>; /* 1 - ISET1, 2 ISET2 */
fdim = <100>; /* TPS65217_BL_FDIM_100HZ */
default-brightness = <50>;
};
};

View File

@@ -0,0 +1,96 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/leds/brcm,bcm6358-leds.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: LEDs connected to Broadcom BCM6358 controller
description: |
This controller is present on BCM6358 and BCM6368.
In these SoCs there are Serial LEDs (LEDs connected to a 74x164 controller),
which can either be controlled by software (exporting the 74x164 as spi-gpio.
See Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml), or
by hardware using this driver.
maintainers:
- Álvaro Fernández Rojas <noltari@gmail.com>
properties:
compatible:
const: brcm,bcm6358-leds
reg:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 0
brcm,clk-div:
description: SCK signal divider.
default: 1
$ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 2, 4, 8]
brcm,clk-dat-low:
description: Makes clock and data signals active low.
type: boolean
patternProperties:
"^led@1?[0-9a-f]$":
type: object
$ref: common.yaml#
unevaluatedProperties: false
description: Each LED is represented as a sub-node of
this device.
properties:
reg:
description: LED pin number (0 to 31).
maxItems: 1
required:
- reg
required:
- compatible
- reg
- "#address-cells"
- "#size-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/leds/common.h>
led-controller@fffe00d0 {
compatible = "brcm,bcm6358-leds";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xfffe00d0 0x8>;
led@0 {
reg = <0>;
active-low;
label = "white:alarm";
};
led@2 {
reg = <2>;
active-low;
label = "white:tv";
};
led@3 {
reg = <3>;
active-low;
label = "white:tel";
};
led@4 {
reg = <4>;
active-low;
label = "white:adsl";
};
};
...

View File

@@ -73,6 +73,16 @@ properties:
- keep
default: off
default-intensity:
description:
The initial intensity of the LED color component. As the intensity of
each sub-LED is multiplied with the overall brightness, without this
property on a sub-LED, it may effectively be initialized at minimum
brightness regardless of its linux,default-trigger and default-brightness
properties.
$ref: /schemas/types.yaml#/definitions/uint32
default: 0
linux,default-trigger:
description:
This parameter, if present, is a string defining the trigger assigned to
@@ -106,6 +116,8 @@ properties:
- bluetooth-power
# LED indicates camera flash state
- flash
# LED indicates level of GPIO input referenced by trigger-sources
- gpio
# LED indicated keyboard capslock
- kbd-capslock
# LED indicates MTD memory activity

View File

@@ -0,0 +1,85 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/leds/lacie,ns2-leds.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Dual-GPIO LEDs found on Network Space v2 (and parents)
maintainers:
- Simon Guinot <simon.guinot@sequanux.org>
description:
The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED
modes are available, off, on and SATA activity blinking. The LED modes are
controlled through two GPIOs (command and slow), each combination of values
for the command/slow GPIOs corresponds to a LED mode.
properties:
compatible:
const: lacie,ns2-leds
additionalProperties:
type: object
$ref: common.yaml#
unevaluatedProperties: false
description: Each child node represents a single LED
properties:
cmd-gpio:
maxItems: 1
description: GPIO connected to the command LED output
slow-gpio:
maxItems: 1
description: GPIO connected to the slow LED output
num-modes:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Number of entries in modes-map.
modes-map:
$ref: /schemas/types.yaml#/definitions/uint32-matrix
description:
A mapping between LED modes (off, on or SATA activity blinking) and
the corresponding cmd-gpio/slow-gpio values. All the GPIO values
combinations should be given in order to avoid having an unknown
mode at driver probe time.
items:
items:
- description: LED mode
enum: [0, 1, 2]
- description: Command GPIO level
enum: [0, 1]
- description: Slow GPIO level
enum: [0, 1]
required:
- cmd-gpio
- slow-gpio
- modes-map
required:
- compatible
examples:
- |
#include <dt-bindings/leds/leds-ns2.h>
led-controller {
compatible = "lacie,ns2-leds";
led-0 {
label = "ns2:blue:sata";
slow-gpio = <&gpio0 29 0>;
cmd-gpio = <&gpio0 30 0>;
modes-map = <NS_V2_LED_OFF 0 1
NS_V2_LED_ON 1 0
NS_V2_LED_ON 0 0
NS_V2_LED_SATA 1 1>;
};
};
...

View File

@@ -1,143 +0,0 @@
LEDs connected to Broadcom BCM6358 controller
This controller is present on BCM6358 and BCM6368.
In these SoCs there are Serial LEDs (LEDs connected to a 74x164 controller),
which can either be controlled by software (exporting the 74x164 as spi-gpio.
See Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml), or
by hardware using this driver.
Required properties:
- compatible : should be "brcm,bcm6358-leds".
- #address-cells : must be 1.
- #size-cells : must be 0.
- reg : BCM6358 LED controller address and size.
Optional properties:
- brcm,clk-div : SCK signal divider. Possible values are 1, 2, 4 and 8.
Default : 1
- brcm,clk-dat-low : Boolean, makes clock and data signals active low.
Default : false
Each LED is represented as a sub-node of the brcm,bcm6358-leds device.
LED sub-node required properties:
- reg : LED pin number (only LEDs 0 to 31 are valid).
LED sub-node optional properties:
- label : see Documentation/devicetree/bindings/leds/common.txt
- default-state : see
Documentation/devicetree/bindings/leds/common.txt
- linux,default-trigger : see
Documentation/devicetree/bindings/leds/common.txt
Examples:
Scenario 1 : BCM6358
leds0: led-controller@fffe00d0 {
compatible = "brcm,bcm6358-leds";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xfffe00d0 0x8>;
alarm_white {
reg = <0>;
active-low;
label = "white:alarm";
};
tv_white {
reg = <2>;
active-low;
label = "white:tv";
};
tel_white {
reg = <3>;
active-low;
label = "white:tel";
};
adsl_white {
reg = <4>;
active-low;
label = "white:adsl";
};
};
Scenario 2 : BCM6368
leds0: led-controller@100000d0 {
compatible = "brcm,bcm6358-leds";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x100000d0 0x8>;
brcm,pol-low;
brcm,clk-div = <4>;
power_red {
reg = <0>;
active-low;
label = "red:power";
};
power_green {
reg = <1>;
active-low;
label = "green:power";
default-state = "on";
};
power_blue {
reg = <2>;
label = "blue:power";
};
broadband_red {
reg = <3>;
active-low;
label = "red:broadband";
};
broadband_green {
reg = <4>;
label = "green:broadband";
};
broadband_blue {
reg = <5>;
active-low;
label = "blue:broadband";
};
wireless_red {
reg = <6>;
active-low;
label = "red:wireless";
};
wireless_green {
reg = <7>;
active-low;
label = "green:wireless";
};
wireless_blue {
reg = <8>;
label = "blue:wireless";
};
phone_red {
reg = <9>;
active-low;
label = "red:phone";
};
phone_green {
reg = <10>;
active-low;
label = "green:phone";
};
phone_blue {
reg = <11>;
label = "blue:phone";
};
upgrading_red {
reg = <12>;
active-low;
label = "red:upgrading";
};
upgrading_green {
reg = <13>;
active-low;
label = "green:upgrading";
};
upgrading_blue {
reg = <14>;
label = "blue:upgrading";
};
};

View File

@@ -1,35 +0,0 @@
Binding for dual-GPIO LED found on Network Space v2 (and parents).
Required properties:
- compatible: "lacie,ns2-leds".
Each LED is represented as a sub-node of the ns2-leds device.
Required sub-node properties:
- cmd-gpio: Command LED GPIO. See OF device-tree GPIO specification.
- slow-gpio: Slow LED GPIO. See OF device-tree GPIO specification.
- modes-map: A mapping between LED modes (off, on or SATA activity blinking) and
the corresponding cmd-gpio/slow-gpio values. All the GPIO values combinations
should be given in order to avoid having an unknown mode at driver probe time.
Optional sub-node properties:
- label: Name for this LED. If omitted, the label is taken from the node name.
- linux,default-trigger: Trigger assigned to the LED.
Example:
#include <dt-bindings/leds/leds-ns2.h>
ns2-leds {
compatible = "lacie,ns2-leds";
blue-sata {
label = "ns2:blue:sata";
slow-gpio = <&gpio0 29 0>;
cmd-gpio = <&gpio0 30 0>;
modes-map = <NS_V2_LED_OFF 0 1
NS_V2_LED_ON 1 0
NS_V2_LED_ON 0 0
NS_V2_LED_SATA 1 1>;
};
};

View File

@@ -45,6 +45,8 @@ properties:
color: true
default-intensity: true
required:
- pwms
- color
@@ -63,12 +65,14 @@ examples:
multi-led {
color = <LED_COLOR_ID_RGB>;
linux,default-trigger = "default-on";
function = LED_FUNCTION_INDICATOR;
max-brightness = <65535>;
led-red {
pwms = <&pwm1 0 1000000>;
color = <LED_COLOR_ID_RED>;
default-intensity = <65535>;
};
led-green {

View File

@@ -62,18 +62,52 @@ properties:
open-drain, newer chips to totem pole).
patternProperties:
"^led@[0-9a-f]+$":
"^led@[0-9a-f]$":
type: object
$ref: common.yaml#
unevaluatedProperties: false
properties:
reg:
minimum: 0
maximum: 15
required:
- reg
"^multi-led@[0-9a-f]$":
type: object
$ref: leds-class-multicolor.yaml#
unevaluatedProperties: false
properties:
reg:
maximum: 15
"#address-cells":
const: 1
"#size-cells":
const: 0
patternProperties:
"^led@[0-9a-f]$":
type: object
$ref: common.yaml#
unevaluatedProperties: false
properties:
reg:
maximum: 15
required:
- reg
- color
required:
- reg
- "#address-cells"
- "#size-cells"
allOf:
- if:
properties:
@@ -84,16 +118,34 @@ allOf:
- nxp,pca9633
then:
patternProperties:
"^led@[0-9a-f]+$":
"^.*led@[0-9a-f]$":
properties:
reg:
maximum: 3
else:
"^multi-led@[0-9a-f]$":
patternProperties:
"^led@[0-9a-f]$":
properties:
reg:
maximum: 3
- if:
properties:
compatible:
contains:
enum:
- nxp,pca9634
then:
patternProperties:
"^led@[0-9a-f]+$":
"^.*led@[0-9a-f]$":
properties:
reg:
maximum: 7
"^multi-led@[0-9a-f]$":
patternProperties:
"^led@[0-9a-f]$":
properties:
reg:
maximum: 7
additionalProperties: false
@@ -137,4 +189,50 @@ examples:
};
};
- |
#include <dt-bindings/leds/common.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
led-controller@62 {
compatible = "nxp,pca9633";
reg = <0x62>;
#address-cells = <1>;
#size-cells = <0>;
/* Three channels controlling one RGB LED */
multi-led@0 {
reg = <0>;
color = <LED_COLOR_ID_RGB>;
function = LED_FUNCTION_STATUS;
#address-cells = <1>;
#size-cells = <0>;
led@0 {
reg = <0>;
color = <LED_COLOR_ID_RED>;
};
led@1 {
reg = <1>;
color = <LED_COLOR_ID_GREEN>;
};
led@2 {
reg = <2>;
color = <LED_COLOR_ID_BLUE>;
};
};
/* Remaining channel used as a plain white LED */
led@3 {
reg = <3>;
color = <LED_COLOR_ID_WHITE>;
function = LED_FUNCTION_STATUS;
};
};
};
...

View File

@@ -1,78 +0,0 @@
TPS65217 family of regulators
Required properties:
- compatible: "ti,tps65217"
- reg: I2C slave address
- regulators: list of regulators provided by this controller, must be named
after their hardware counterparts: dcdc[1-3] and ldo[1-4]
- regulators: This is the list of child nodes that specify the regulator
initialization data for defined regulators. Not all regulators for the given
device need to be present. The definition for each of these nodes is defined
using the standard binding for regulators found at
Documentation/devicetree/bindings/regulator/regulator.txt.
Optional properties:
- ti,pmic-shutdown-controller: Telling the PMIC to shutdown on PWR_EN toggle.
The valid names for regulators are:
tps65217: dcdc1, dcdc2, dcdc3, ldo1, ldo2, ldo3 and ldo4
Each regulator is defined using the standard binding for regulators.
Example:
tps: tps@24 {
compatible = "ti,tps65217";
ti,pmic-shutdown-controller;
regulators {
dcdc1_reg: dcdc1 {
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <1800000>;
regulator-boot-on;
regulator-always-on;
};
dcdc2_reg: dcdc2 {
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
dcdc3_reg: dcc3 {
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
ldo1_reg: ldo1 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
ldo2_reg: ldo2 {
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
ldo3_reg: ldo3 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
ldo4_reg: ldo4 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
};
};

View File

@@ -16,8 +16,9 @@ in terms of PWM duty-cycle and duration (ms).
To be compatible with the hardware pattern format, maximum 8 tuples of
brightness (PWM) and duration must be written to hw_pattern.
- Brightness range: 0-255
- Min pattern duration: 22 ms
- Max pattern duration: 5660 ms
- Max pattern duration: 5610 ms
The format of the hardware pattern values should be:
"brightness duration brightness duration ..."
@@ -26,9 +27,7 @@ The format of the hardware pattern values should be:
----------------------------
Specify a pattern repeat number, which is common for all channels.
Default is 1; negative numbers and 0 are invalid.
Default is 1. Writing 0 is invalid. Writing -1 or 255 repeats the
pattern indefinitely.
This file will always return the originally written repeat number.
When the 255 value is written to it, all patterns will repeat
indefinitely.

View File

@@ -15527,6 +15527,14 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
F: drivers/iio/temperature/ltc2983.c
LTC3220 LED DRIVER
M: Edelweise Escala <edelweise.escala@analog.com>
L: linux-leds@vger.kernel.org
S: Maintained
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/leds/adi,ltc3220.yaml
F: drivers/leds/leds-ltc3220.c
LTC4282 HARDWARE MONITOR DRIVER
M: Nuno Sa <nuno.sa@analog.com>
L: linux-hwmon@vger.kernel.org

View File

@@ -596,6 +596,7 @@ config LEDS_PCA963X
tristate "LED support for PCA963x I2C chip"
depends on LEDS_CLASS
depends on I2C
select LEDS_CLASS_MULTICOLOR
help
This option enables support for LEDs connected to the PCA963x
LED driver chip accessed via the I2C bus. Supported
@@ -1000,6 +1001,19 @@ config LEDS_ST1202
Say Y to enable support for LEDs connected to LED1202
LED driver chips accessed via the I2C bus.
config LEDS_LTC3220
tristate "LED Driver for Analog Devices Inc. LTC3220"
depends on I2C && LEDS_CLASS
select REGMAP_I2C
help
Say Y to enable support for the Analog Devices LTC3220
18-channel LED controller with I2C interface.
The driver supports individual LED brightness control (64 steps),
hardware-assisted blinking and gradation effects.
To compile this driver as a module, choose M here: the module will
be called leds-ltc3220.
config LEDS_TPS6105X
tristate "LED support for TI TPS6105X"
depends on LEDS_CLASS

View File

@@ -61,6 +61,7 @@ obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o
obj-$(CONFIG_LEDS_LP8864) += leds-lp8864.o
obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o
obj-$(CONFIG_LEDS_LTC3220) += leds-ltc3220.o
obj-$(CONFIG_LEDS_MAX5970) += leds-max5970.o
obj-$(CONFIG_LEDS_MAX77650) += leds-max77650.o
obj-$(CONFIG_LEDS_MAX77705) += leds-max77705.o

View File

@@ -236,8 +236,8 @@ static void bcm63138_leds_create_led(struct bcm63138_leds *leds,
pinctrl = devm_pinctrl_get_select_default(led->cdev.dev);
if (IS_ERR(pinctrl) && PTR_ERR(pinctrl) != -ENODEV) {
dev_warn(led->cdev.dev, "Failed to select %pOF pinctrl: %ld\n",
np, PTR_ERR(pinctrl));
dev_warn(led->cdev.dev, "Failed to select %pOF pinctrl: %pe\n",
np, pinctrl);
}
bit = BIT(led->pin);

View File

@@ -9,8 +9,8 @@
#include <linux/container_of.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/legacy.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/overflow.h>
@@ -211,7 +211,6 @@ static struct gpio_desc *gpio_led_get_gpiod(struct device *dev, int idx,
const struct gpio_led *template)
{
struct gpio_desc *gpiod;
int ret;
/*
* This means the LED does not come from the device tree
@@ -222,16 +221,29 @@ static struct gpio_desc *gpio_led_get_gpiod(struct device *dev, int idx,
gpiod = devm_gpiod_get_index_optional(dev, NULL, idx, GPIOD_OUT_LOW);
if (IS_ERR(gpiod))
return gpiod;
if (gpiod) {
gpiod_set_consumer_name(gpiod, template->name);
return gpiod;
}
/*
* This is the legacy code path for platform code that
* still uses GPIO numbers. Ultimately we would like to get
* rid of this block completely.
*/
gpiod_set_consumer_name(gpiod, template->name);
return gpiod;
}
#ifdef CONFIG_GPIOLIB_LEGACY
/*
* This is the legacy code path for platform code that still uses
* GPIO numbers, mainly MIPS and SuperH board files.
* Ultimately we would like to get rid of this block completely.
*
* ppc44x-warp sets the template->gpiod directly instead of
* adding a lookup table or device properties. This is not
* much better.
*/
static struct gpio_desc *gpio_led_get_legacy_gpiod(struct device *dev, int idx,
const struct gpio_led *template)
{
struct gpio_desc *gpiod;
int ret;
if (template->gpiod)
return template->gpiod;
/* skip leds that aren't available */
if (!gpio_is_valid(template->gpio))
@@ -251,6 +263,13 @@ static struct gpio_desc *gpio_led_get_gpiod(struct device *dev, int idx,
return gpiod;
}
#else
static struct gpio_desc *gpio_led_get_legacy_gpiod(struct device *dev, int idx,
const struct gpio_led *template)
{
return template->gpiod ?: ERR_PTR(-ENOENT);
}
#endif
static int gpio_led_probe(struct platform_device *pdev)
{
@@ -269,14 +288,14 @@ static int gpio_led_probe(struct platform_device *pdev)
const struct gpio_led *template = &pdata->leds[i];
struct gpio_led_data *led_dat = &priv->leds[i];
if (template->gpiod)
led_dat->gpiod = template->gpiod;
else
led_dat->gpiod = gpio_led_get_gpiod(dev, i, template);
if (!led_dat->gpiod)
led_dat->gpiod =
gpio_led_get_gpiod(dev, i, template);
gpio_led_get_legacy_gpiod(dev, i, template);
if (IS_ERR(led_dat->gpiod)) {
dev_info(dev, "Skipping unavailable LED gpio %d (%s)\n",
template->gpio, template->name);
dev_info(dev, "Skipping unavailable LED gpio %s\n",
template->name);
led_dat->gpiod = NULL;
continue;
}

View File

@@ -97,7 +97,7 @@ struct is31fl319x_chip {
struct is31fl319x_chip *chip;
struct led_classdev cdev;
u32 max_microamp;
bool configured;
struct fwnode_handle *fwnode;
} leds[IS31FL319X_MAX_LEDS];
};
@@ -362,31 +362,17 @@ static const struct of_device_id of_is31fl319x_match[] = {
};
MODULE_DEVICE_TABLE(of, of_is31fl319x_match);
static int is31fl319x_parse_child_fw(const struct device *dev,
const struct fwnode_handle *child,
struct is31fl319x_led *led,
struct is31fl319x_chip *is31)
static void is31_free_fwnode(void *data)
{
struct led_classdev *cdev = &led->cdev;
int ret;
struct is31fl319x_chip *is31 = data;
int i;
if (fwnode_property_read_string(child, "label", &cdev->name))
cdev->name = fwnode_get_name(child);
for (i = 0; i < is31->cdef->num_leds; i++) {
if (is31->leds[i].fwnode)
fwnode_handle_put(is31->leds[i].fwnode);
ret = fwnode_property_read_string(child, "linux,default-trigger", &cdev->default_trigger);
if (ret < 0 && ret != -EINVAL) /* is optional */
return ret;
led->max_microamp = is31->cdef->current_default;
ret = fwnode_property_read_u32(child, "led-max-microamp", &led->max_microamp);
if (!ret) {
if (led->max_microamp < is31->cdef->current_min)
return -EINVAL; /* not supported */
led->max_microamp = min(led->max_microamp,
is31->cdef->current_max);
is31->leds[i].fwnode = NULL;
}
return 0;
}
static int is31fl319x_parse_fw(struct device *dev, struct is31fl319x_chip *is31)
@@ -401,6 +387,12 @@ static int is31fl319x_parse_fw(struct device *dev, struct is31fl319x_chip *is31)
"Failed to get shutdown gpio\n");
is31->cdef = device_get_match_data(dev);
if (!is31->cdef)
return -ENODEV;
ret = devm_add_action_or_reset(dev, is31_free_fwnode, is31);
if (ret)
return ret;
count = 0;
device_for_each_child_node_scoped(dev, child)
@@ -426,14 +418,20 @@ static int is31fl319x_parse_fw(struct device *dev, struct is31fl319x_chip *is31)
led = &is31->leds[reg - 1];
if (led->configured)
if (led->fwnode)
return dev_err_probe(dev, -EINVAL, "led %u is already configured\n", reg);
ret = is31fl319x_parse_child_fw(dev, child, led, is31);
if (ret)
return dev_err_probe(dev, ret, "led %u DT parsing failed\n", reg);
led->max_microamp = is31->cdef->current_default;
ret = fwnode_property_read_u32(child, "led-max-microamp", &led->max_microamp);
if (!ret) {
if (led->max_microamp < is31->cdef->current_min)
return dev_err_probe(dev, -EINVAL, "invalid maximum current\n");
led->configured = true;
led->max_microamp = min(led->max_microamp,
is31->cdef->current_max);
}
led->fwnode = fwnode_handle_get(child);
}
is31->audio_gain_db = 0;
@@ -530,7 +528,7 @@ static int is31fl319x_probe(struct i2c_client *client)
*/
aggregated_led_microamp = is31->cdef->current_max;
for (i = 0; i < is31->cdef->num_leds; i++)
if (is31->leds[i].configured &&
if (is31->leds[i].fwnode &&
is31->leds[i].max_microamp < aggregated_led_microamp)
aggregated_led_microamp = is31->leds[i].max_microamp;
@@ -544,14 +542,17 @@ static int is31fl319x_probe(struct i2c_client *client)
for (i = 0; i < is31->cdef->num_leds; i++) {
struct is31fl319x_led *led = &is31->leds[i];
struct led_init_data init_data = {};
if (!led->configured)
if (!led->fwnode)
continue;
init_data.fwnode = led->fwnode;
led->chip = is31;
led->cdev.brightness_set_blocking = is31->cdef->brightness_set;
err = devm_led_classdev_register(&client->dev, &led->cdev);
err = devm_led_classdev_register_ext(&client->dev, &led->cdev, &init_data);
if (err < 0)
return err;
}

586
drivers/leds/leds-ltc3220.c Normal file
View File

@@ -0,0 +1,586 @@
// SPDX-License-Identifier: GPL-2.0
/*
* LTC3220 18-Channel LED Driver
*
* Copyright 2026 Analog Devices Inc.
*
* Author: Edelweise Escala <edelweise.escala@analog.com>
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/types.h>
/* LTC3220 Registers */
#define LTC3220_COMMAND_REG 0x00
#define LTC3220_QUICK_WRITE_MASK BIT(0)
#define LTC3220_SHUTDOWN_MASK BIT(3)
#define LTC3220_ULED_REG(x) (0x01 + (x))
#define LTC3220_LED_CURRENT_MASK GENMASK(5, 0)
#define LTC3220_LED_MODE_MASK GENMASK(7, 6)
#define LTC3220_GRAD_BLINK_REG 0x13
#define LTC3220_GRADATION_MASK GENMASK(2, 0)
#define LTC3220_GRADATION_DIRECTION_MASK BIT(0)
#define LTC3220_GRADATION_PERIOD_MASK GENMASK(2, 1)
#define LTC3220_BLINK_MASK GENMASK(4, 3)
#define LTC3220_NUM_LEDS 18
#define LTC3220_MAX_BRIGHTNESS 63
#define LTC3220_GRADATION_RAMP_TIME_240MS 240
#define LTC3220_GRADATION_RAMP_TIME_480MS 480
#define LTC3220_BLINK_ON_156MS 156
#define LTC3220_BLINK_ON_625MS 625
#define LTC3220_BLINK_PERIOD_1250MS 1250
#define LTC3220_BLINK_PERIOD_2500MS 2500
#define LTC3220_BLINK_SHORT_ON_TIME BIT(0)
#define LTC3220_BLINK_LONG_PERIOD BIT(1)
enum ltc3220_led_mode {
LTC3220_NORMAL_MODE,
LTC3220_BLINK_MODE,
LTC3220_GRADATION_MODE,
};
enum ltc3220_blink_mode {
LTC3220_BLINK_MODE_625MS_1250MS,
LTC3220_BLINK_MODE_156MS_1250MS,
LTC3220_BLINK_MODE_625MS_2500MS,
LTC3220_BLINK_MODE_156MS_2500MS
};
enum ltc3220_gradation_mode {
LTC3220_GRADATION_MODE_DISABLED,
LTC3220_GRADATION_MODE_240MS_RAMP_TIME,
LTC3220_GRADATION_MODE_480MS_RAMP_TIME,
LTC3220_GRADATION_MODE_960MS_RAMP_TIME
};
static const struct regmap_config ltc3220_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = LTC3220_GRAD_BLINK_REG,
.cache_type = REGCACHE_FLAT_S,
};
struct ltc3220_uled_cfg {
struct led_classdev led_cdev;
u8 reg_value;
u8 led_index;
bool registered;
};
struct ltc3220 {
struct ltc3220_uled_cfg uled_cfg[LTC3220_NUM_LEDS];
struct regmap *regmap;
struct mutex lock;
};
/*
* Set LED brightness. Hardware supports 0-63 brightness levels.
* Mode switching (blink/gradation) is handled through dedicated callbacks.
*
* In aggregated mode only a single LED (reg = 1) is registered and the
* hardware quick-write feature propagates the write to all 18 channels, so
* there is no need to update the other registers explicitly.
*/
static int __ltc3220_set_led_data(struct ltc3220 *ltc3220,
struct ltc3220_uled_cfg *uled_cfg,
enum led_brightness brightness)
{
int ret;
brightness &= LTC3220_LED_CURRENT_MASK;
ret = regmap_write(ltc3220->regmap, LTC3220_ULED_REG(uled_cfg->led_index),
brightness);
if (ret)
return ret;
uled_cfg->reg_value = brightness;
return 0;
}
static int ltc3220_set_led_data(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct ltc3220_uled_cfg *uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg,
led_cdev);
struct ltc3220 *ltc3220 = container_of(uled_cfg - uled_cfg->led_index, struct ltc3220,
uled_cfg[0]);
int ret;
mutex_lock(&ltc3220->lock);
ret = __ltc3220_set_led_data(ltc3220, uled_cfg, brightness);
mutex_unlock(&ltc3220->lock);
return ret;
}
static enum led_brightness ltc3220_get_led_data(struct led_classdev *led_cdev)
{
struct ltc3220_uled_cfg *uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg,
led_cdev);
return uled_cfg->reg_value;
}
/*
* LTC3220 pattern support for hardware-assisted breathing/gradation.
* The hardware supports 3 gradation ramp times (240ms, 480ms, 960ms)
* and can ramp up or down. The gradation period and direction are chip-global
* registers (LTC3220_GRAD_BLINK_REG), affecting all 18 channels simultaneously.
* This is a hardware limitation, not a driver bug.
*
* Pattern array interpretation:
* pattern[0].brightness = start brightness (0-63)
* pattern[0].delta_t = ramp time in milliseconds
* pattern[1].brightness = end brightness (0-63)
* pattern[1].delta_t = (optional, can be 0 or same as pattern[0].delta_t)
*/
static int ltc3220_pattern_set(struct led_classdev *led_cdev,
struct led_pattern *pattern,
u32 len, int repeat)
{
struct ltc3220_uled_cfg *uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg,
led_cdev);
struct ltc3220 *ltc3220 = container_of(uled_cfg - uled_cfg->led_index, struct ltc3220,
uled_cfg[0]);
u8 gradation_period;
u8 start_brightness;
u8 end_brightness;
u8 gradation_val;
u8 led_mode;
bool is_increasing;
int ret;
if (len != 2)
return -EINVAL;
start_brightness = clamp_val(pattern[0].brightness, 0, LTC3220_LED_CURRENT_MASK);
end_brightness = clamp_val(pattern[1].brightness, 0, LTC3220_LED_CURRENT_MASK);
is_increasing = end_brightness > start_brightness;
if (pattern[0].delta_t == 0)
gradation_period = LTC3220_GRADATION_MODE_DISABLED;
else if (pattern[0].delta_t <= LTC3220_GRADATION_RAMP_TIME_240MS)
gradation_period = LTC3220_GRADATION_MODE_240MS_RAMP_TIME;
else if (pattern[0].delta_t <= LTC3220_GRADATION_RAMP_TIME_480MS)
gradation_period = LTC3220_GRADATION_MODE_480MS_RAMP_TIME;
else
gradation_period = LTC3220_GRADATION_MODE_960MS_RAMP_TIME;
gradation_val = FIELD_PREP(LTC3220_GRADATION_PERIOD_MASK, gradation_period);
gradation_val |= FIELD_PREP(LTC3220_GRADATION_DIRECTION_MASK, is_increasing);
/*
* With the ramp disabled (delta_t == 0) there is no gradation to run,
* so apply the end brightness directly in NORMAL mode instead of
* leaving the channel in gradation mode with a disabled ramp.
*/
led_mode = gradation_period == LTC3220_GRADATION_MODE_DISABLED ?
LTC3220_NORMAL_MODE : LTC3220_GRADATION_MODE;
mutex_lock(&ltc3220->lock);
ret = regmap_update_bits(ltc3220->regmap, LTC3220_GRAD_BLINK_REG,
LTC3220_GRADATION_MASK, gradation_val);
if (ret)
goto unlock;
if (led_mode == LTC3220_GRADATION_MODE) {
ret = regmap_write(ltc3220->regmap, LTC3220_ULED_REG(uled_cfg->led_index),
start_brightness & LTC3220_LED_CURRENT_MASK);
if (ret)
goto unlock;
ret = regmap_write(ltc3220->regmap, LTC3220_ULED_REG(uled_cfg->led_index),
FIELD_PREP(LTC3220_LED_MODE_MASK, led_mode) |
(end_brightness & LTC3220_LED_CURRENT_MASK));
if (ret)
goto unlock;
uled_cfg->reg_value = end_brightness;
} else {
ret = __ltc3220_set_led_data(ltc3220, uled_cfg, end_brightness);
if (ret)
goto unlock;
}
unlock:
mutex_unlock(&ltc3220->lock);
return ret;
}
static int ltc3220_pattern_clear(struct led_classdev *led_cdev)
{
struct ltc3220_uled_cfg *uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg,
led_cdev);
struct ltc3220 *ltc3220 = container_of(uled_cfg - uled_cfg->led_index, struct ltc3220,
uled_cfg[0]);
int ret;
mutex_lock(&ltc3220->lock);
ret = regmap_update_bits(ltc3220->regmap, LTC3220_ULED_REG(uled_cfg->led_index),
LTC3220_LED_MODE_MASK, LTC3220_NORMAL_MODE);
if (ret)
goto unlock;
ret = __ltc3220_set_led_data(ltc3220, uled_cfg, LED_OFF);
unlock:
mutex_unlock(&ltc3220->lock);
return ret;
}
/*
* LTC3220 has a global blink configuration that affects all LEDs.
* This implementation allows per-LED blink requests via sysfs, but setting
* blink on any LED reprograms the timing for all 18 channels simultaneously.
* The delay values are mapped to the hardware's discrete blink rates.
*
* HARDWARE LIMITATION: This is not a driver bug. Per-LED blink timing control
* is not possible with this hardware due to the global blink register.
*/
static int ltc3220_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
struct ltc3220_uled_cfg *uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg,
led_cdev);
struct ltc3220 *ltc3220 = container_of(uled_cfg - uled_cfg->led_index, struct ltc3220,
uled_cfg[0]);
u8 blink_brightness;
u8 blink_mode = 0;
int ret;
if (*delay_on <= LTC3220_BLINK_ON_156MS)
blink_mode = LTC3220_BLINK_SHORT_ON_TIME;
if (*delay_on + *delay_off > LTC3220_BLINK_PERIOD_1250MS)
blink_mode |= LTC3220_BLINK_LONG_PERIOD;
switch (blink_mode) {
case LTC3220_BLINK_MODE_625MS_1250MS:
*delay_on = LTC3220_BLINK_ON_625MS;
*delay_off = LTC3220_BLINK_PERIOD_1250MS - LTC3220_BLINK_ON_625MS;
break;
case LTC3220_BLINK_MODE_156MS_1250MS:
*delay_on = LTC3220_BLINK_ON_156MS;
*delay_off = LTC3220_BLINK_PERIOD_1250MS - LTC3220_BLINK_ON_156MS;
break;
case LTC3220_BLINK_MODE_625MS_2500MS:
*delay_on = LTC3220_BLINK_ON_625MS;
*delay_off = LTC3220_BLINK_PERIOD_2500MS - LTC3220_BLINK_ON_625MS;
break;
case LTC3220_BLINK_MODE_156MS_2500MS:
*delay_on = LTC3220_BLINK_ON_156MS;
*delay_off = LTC3220_BLINK_PERIOD_2500MS - LTC3220_BLINK_ON_156MS;
break;
}
mutex_lock(&ltc3220->lock);
ret = regmap_update_bits(ltc3220->regmap, LTC3220_GRAD_BLINK_REG,
LTC3220_BLINK_MASK, FIELD_PREP(LTC3220_BLINK_MASK, blink_mode));
if (ret)
goto unlock;
blink_brightness = uled_cfg->reg_value ? : led_cdev->max_brightness;
ret = regmap_write(ltc3220->regmap, LTC3220_ULED_REG(uled_cfg->led_index),
FIELD_PREP(LTC3220_LED_MODE_MASK, LTC3220_BLINK_MODE) |
(blink_brightness & LTC3220_LED_CURRENT_MASK));
if (ret)
goto unlock;
uled_cfg->reg_value = blink_brightness;
unlock:
mutex_unlock(&ltc3220->lock);
return ret;
}
static void ltc3220_reset_gpio_action(void *data)
{
struct gpio_desc *reset_gpio = data;
gpiod_set_value_cansleep(reset_gpio, 1);
}
static int ltc3220_reset(struct ltc3220 *ltc3220, struct i2c_client *client)
{
struct gpio_desc *reset_gpio;
int ret;
reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(reset_gpio))
return dev_err_probe(&client->dev, PTR_ERR(reset_gpio), "Failed on reset GPIO\n");
if (reset_gpio) {
usleep_range(10000, 12000);
gpiod_set_value_cansleep(reset_gpio, 0);
usleep_range(10000, 12000);
ret = devm_add_action_or_reset(&client->dev, ltc3220_reset_gpio_action,
reset_gpio);
if (ret)
return ret;
}
ret = regmap_write(ltc3220->regmap, LTC3220_COMMAND_REG, 0);
if (ret)
return ret;
for (int i = 0; i < LTC3220_NUM_LEDS; i++) {
ret = regmap_write(ltc3220->regmap, LTC3220_ULED_REG(i), 0);
if (ret)
return ret;
}
return regmap_write(ltc3220->regmap, LTC3220_GRAD_BLINK_REG, 0);
}
static int ltc3220_suspend(struct device *dev)
{
struct ltc3220 *ltc3220 = i2c_get_clientdata(to_i2c_client(dev));
int ret;
ret = regmap_update_bits(ltc3220->regmap, LTC3220_COMMAND_REG,
LTC3220_SHUTDOWN_MASK, LTC3220_SHUTDOWN_MASK);
if (ret)
return ret;
regcache_mark_dirty(ltc3220->regmap);
return 0;
}
static int ltc3220_resume(struct device *dev)
{
struct ltc3220 *ltc3220 = i2c_get_clientdata(to_i2c_client(dev));
bool quick_write_enabled;
unsigned int command_reg;
int ret;
ret = regmap_read(ltc3220->regmap, LTC3220_COMMAND_REG, &command_reg);
if (ret)
return ret;
quick_write_enabled = command_reg & LTC3220_QUICK_WRITE_MASK;
if (quick_write_enabled) {
ret = regmap_update_bits(ltc3220->regmap, LTC3220_COMMAND_REG,
LTC3220_QUICK_WRITE_MASK, 0);
if (ret)
return ret;
}
ret = regmap_update_bits(ltc3220->regmap, LTC3220_COMMAND_REG,
LTC3220_SHUTDOWN_MASK, 0);
if (ret)
return ret;
usleep_range(10000, 12000);
ret = regcache_sync(ltc3220->regmap);
if (ret)
return ret;
if (quick_write_enabled) {
ret = regmap_update_bits(ltc3220->regmap, LTC3220_COMMAND_REG,
LTC3220_QUICK_WRITE_MASK,
LTC3220_QUICK_WRITE_MASK);
if (ret)
return ret;
}
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(ltc3220_pm_ops, ltc3220_suspend, ltc3220_resume);
static int ltc3220_probe(struct i2c_client *client)
{
struct ltc3220 *ltc3220;
bool aggregated_led_found = false;
int num_leds = 0;
u8 led_index = 0;
int ret;
ltc3220 = devm_kzalloc(&client->dev, sizeof(*ltc3220), GFP_KERNEL);
if (!ltc3220)
return -ENOMEM;
ltc3220->regmap = devm_regmap_init_i2c(client, &ltc3220_regmap_config);
if (IS_ERR(ltc3220->regmap))
return dev_err_probe(&client->dev, PTR_ERR(ltc3220->regmap),
"Failed to initialize regmap\n");
ret = devm_mutex_init(&client->dev, &ltc3220->lock);
if (ret)
return ret;
i2c_set_clientdata(client, ltc3220);
ret = ltc3220_reset(ltc3220, client);
if (ret)
return dev_err_probe(&client->dev, ret, "Failed to reset device\n");
/* First pass: validate configuration and set up LED structures */
device_for_each_child_node_scoped(&client->dev, child) {
struct ltc3220_uled_cfg *led;
u32 source;
ret = fwnode_property_read_u32(child, "reg", &source);
if (ret)
return dev_err_probe(&client->dev, ret, "Couldn't read LED address\n");
if (!source || source > LTC3220_NUM_LEDS)
return dev_err_probe(&client->dev, -EINVAL, "LED address out of range\n");
if (fwnode_property_present(child, "led-sources")) {
u32 led_sources[LTC3220_NUM_LEDS];
int count;
if (source != 1)
return dev_err_probe(&client->dev, -EINVAL,
"Aggregated LED out of range\n");
if (aggregated_led_found)
return dev_err_probe(&client->dev, -EINVAL,
"One Aggregated LED only\n");
count = fwnode_property_count_u32(child, "led-sources");
if (count != LTC3220_NUM_LEDS)
return dev_err_probe(&client->dev, -EINVAL,
"Aggregated mode requires all %d outputs in led-sources, got %d\n",
LTC3220_NUM_LEDS, count);
ret = fwnode_property_read_u32_array(child, "led-sources",
led_sources, LTC3220_NUM_LEDS);
if (ret)
return dev_err_probe(&client->dev, ret,
"Failed to read led-sources array\n");
/*
* Validate array contents for DT correctness. The hardware
* quick-write broadcasts to all 18 channels regardless of
* array contents, but checking helps catch DT mistakes.
*/
for (int i = 0; i < LTC3220_NUM_LEDS; i++) {
if (led_sources[i] < 1 || led_sources[i] > LTC3220_NUM_LEDS)
return dev_err_probe(&client->dev, -EINVAL,
"Invalid output %u in led-sources\n",
led_sources[i]);
}
aggregated_led_found = true;
}
num_leds++;
/* LED node reg/index/address goes from 1 to 18 */
led_index = source - 1;
led = &ltc3220->uled_cfg[led_index];
if (led->registered)
return dev_err_probe(&client->dev, -EINVAL,
"Duplicate LED reg %u found\n", source);
led->registered = true;
led->led_index = led_index;
led->reg_value = 0;
led->led_cdev.brightness_set_blocking = ltc3220_set_led_data;
led->led_cdev.brightness_get = ltc3220_get_led_data;
led->led_cdev.max_brightness = LTC3220_MAX_BRIGHTNESS;
led->led_cdev.blink_set = ltc3220_blink_set;
led->led_cdev.pattern_set = ltc3220_pattern_set;
led->led_cdev.pattern_clear = ltc3220_pattern_clear;
}
/*
* Aggregated LED mode uses hardware quick-write to control all 18 LEDs
* simultaneously. This is mutually exclusive with individual LED control.
* See Documentation/devicetree/bindings/leds/adi,ltc3220.yaml for details
* on how to configure aggregated LED mode.
*/
if (aggregated_led_found && num_leds > 1)
return dev_err_probe(&client->dev, -EINVAL,
"Aggregated LED must be the only LED node\n");
if (num_leds == 0)
return dev_err_probe(&client->dev, -EINVAL,
"No LED nodes found in device tree\n");
if (aggregated_led_found) {
ret = regmap_update_bits(ltc3220->regmap,
LTC3220_COMMAND_REG,
LTC3220_QUICK_WRITE_MASK,
LTC3220_QUICK_WRITE_MASK);
if (ret)
return dev_err_probe(&client->dev, ret,
"Failed to set quick write mode\n");
}
/* Second pass: register LEDs after validation */
device_for_each_child_node_scoped(&client->dev, child) {
struct led_init_data init_data = {};
struct ltc3220_uled_cfg *led;
u32 source;
ret = fwnode_property_read_u32(child, "reg", &source);
if (ret)
return ret;
if (!source || source > LTC3220_NUM_LEDS)
return dev_err_probe(&client->dev, -EINVAL,
"LED address out of range in second pass\n");
init_data.fwnode = child;
init_data.devicename = "ltc3220";
led_index = source - 1;
led = &ltc3220->uled_cfg[led_index];
ret = devm_led_classdev_register_ext(&client->dev, &led->led_cdev, &init_data);
if (ret)
return dev_err_probe(&client->dev, ret, "Failed to register LED class\n");
}
return 0;
}
static const struct of_device_id ltc3220_of_match[] = {
{ .compatible = "adi,ltc3220" },
{ }
};
MODULE_DEVICE_TABLE(of, ltc3220_of_match);
static struct i2c_driver ltc3220_led_driver = {
.driver = {
.name = "ltc3220",
.of_match_table = ltc3220_of_match,
.pm = pm_sleep_ptr(&ltc3220_pm_ops),
},
.probe = ltc3220_probe,
};
module_i2c_driver(ltc3220_led_driver);
MODULE_AUTHOR("Edelweise Escala <edelweise.escala@analog.com>");
MODULE_DESCRIPTION("LED driver for LTC3220 controllers");
MODULE_LICENSE("GPL");

View File

@@ -327,9 +327,9 @@ static int pca9532_gpio_set_value(struct gpio_chip *gc, unsigned int offset,
struct pca9532_led *led = &data->leds[offset];
if (val)
led->state = PCA9532_ON;
else
led->state = PCA9532_OFF;
else
led->state = PCA9532_ON;
pca9532_setled(led);
@@ -349,7 +349,7 @@ static int pca9532_gpio_get_value(struct gpio_chip *gc, unsigned offset)
static int pca9532_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
{
/* To use as input ensure pin is not driven */
pca9532_gpio_set_value(gc, offset, 0);
pca9532_gpio_set_value(gc, offset, 1);
return 0;
}
@@ -397,10 +397,14 @@ static int pca9532_configure(struct i2c_client *client,
for (i = 0; i < 2; i++) {
data->pwm[i] = pdata->pwm[i];
data->psc[i] = pdata->psc[i];
i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, i),
data->pwm[i]);
i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, i),
data->psc[i]);
err = i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, i),
data->pwm[i]);
if (err < 0)
return err;
err = i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, i),
data->psc[i]);
if (err < 0)
return err;
}
data->hw_blink = true;

View File

@@ -27,6 +27,7 @@
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/leds.h>
#include <linux/led-class-multicolor.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/property.h>
@@ -101,8 +102,11 @@ struct pca963x;
struct pca963x_led {
struct pca963x *chip;
struct led_classdev led_cdev;
struct led_classdev_mc mc_cdev;
struct mc_subled subleds[4];
int led_num; /* 0 .. 15 potentially */
bool blinking;
bool is_mc;
u8 gdc;
u8 gfrq;
};
@@ -115,7 +119,7 @@ struct pca963x {
struct pca963x_led leds[];
};
static int pca963x_brightness(struct pca963x_led *led,
static int pca963x_brightness(struct pca963x_led *led, unsigned int led_num,
enum led_brightness brightness)
{
struct i2c_client *client = led->chip->client;
@@ -124,8 +128,8 @@ static int pca963x_brightness(struct pca963x_led *led,
int shift;
int ret;
ledout_addr = chipdef->ledout_base + (led->led_num / 4);
shift = 2 * (led->led_num % 4);
ledout_addr = chipdef->ledout_base + (led_num / 4);
shift = 2 * (led_num % 4);
mask = 0x3 << shift;
ledout = i2c_smbus_read_byte_data(client, ledout_addr);
@@ -135,7 +139,7 @@ static int pca963x_brightness(struct pca963x_led *led,
val = (ledout & ~mask) | (PCA963X_LED_GRP_PWM << shift);
ret = i2c_smbus_write_byte_data(client,
PCA963X_PWM_BASE +
led->led_num,
led_num,
LED_FULL);
} else {
val = (ledout & ~mask) | (PCA963X_LED_ON << shift);
@@ -150,7 +154,7 @@ static int pca963x_brightness(struct pca963x_led *led,
default:
ret = i2c_smbus_write_byte_data(client,
PCA963X_PWM_BASE +
led->led_num,
led_num,
brightness);
if (ret < 0)
return ret;
@@ -199,20 +203,24 @@ static void pca963x_blink(struct pca963x_led *led)
led->blinking = true;
}
static int pca963x_power_state(struct pca963x_led *led)
static void pca963x_track_power_state(struct pca963x_led *led, unsigned int led_num,
enum led_brightness brightness)
{
unsigned long *leds_on = &led->chip->leds_on;
if (brightness)
set_bit(led_num, leds_on);
else
clear_bit(led_num, leds_on);
}
static int pca963x_sync_power_state(struct pca963x_led *led, unsigned long cached_leds)
{
struct i2c_client *client = led->chip->client;
unsigned long *leds_on = &led->chip->leds_on;
unsigned long cached_leds = *leds_on;
if (led->led_cdev.brightness)
set_bit(led->led_num, leds_on);
else
clear_bit(led->led_num, leds_on);
if (!(*leds_on) != !cached_leds)
if (!led->chip->leds_on != !cached_leds)
return i2c_smbus_write_byte_data(client, PCA963X_MODE1,
*leds_on ? 0 : BIT(4));
led->chip->leds_on ? 0 : BIT(4));
return 0;
}
@@ -221,22 +229,60 @@ static int pca963x_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct pca963x_led *led;
unsigned long cached_leds;
int ret;
led = container_of(led_cdev, struct pca963x_led, led_cdev);
mutex_lock(&led->chip->mutex);
ret = pca963x_brightness(led, value);
if (ret < 0)
cached_leds = led->chip->leds_on;
ret = pca963x_brightness(led, led->led_num, value);
if (ret)
goto unlock;
ret = pca963x_power_state(led);
pca963x_track_power_state(led, led->led_num, value);
ret = pca963x_sync_power_state(led, cached_leds);
unlock:
mutex_unlock(&led->chip->mutex);
return ret;
}
static int pca963x_led_mc_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
struct pca963x_led *led = container_of(mc_cdev, struct pca963x_led, mc_cdev);
unsigned long cached_leds;
int ret = 0, sync_ret;
led_mc_calc_color_components(mc_cdev, value);
guard(mutex)(&led->chip->mutex);
cached_leds = led->chip->leds_on;
for (unsigned int i = 0; i < mc_cdev->num_colors; i++) {
unsigned int channel = mc_cdev->subled_info[i].channel;
ret = pca963x_brightness(led, channel,
mc_cdev->subled_info[i].brightness);
if (ret)
break;
pca963x_track_power_state(led, channel,
mc_cdev->subled_info[i].brightness);
}
/*
* Some channels may already have been updated before the error, so
* still sync the global on/off state to reflect what actually changed.
*/
sync_ret = pca963x_sync_power_state(led, cached_leds);
return ret ? : sync_ret;
}
static unsigned int pca963x_period_scale(struct pca963x_led *led,
unsigned int val)
{
@@ -300,6 +346,81 @@ static int pca963x_blink_set(struct led_classdev *led_cdev,
return 0;
}
static int pca963x_parse_mc_subleds(struct device *dev, struct pca963x_led *led,
struct fwnode_handle *fwnode,
const struct pca963x_chipdef *chipdef)
{
unsigned int num_colors = 0;
int ret;
fwnode_for_each_child_node_scoped(fwnode, sub) {
u32 color, subreg;
if (num_colors >= ARRAY_SIZE(led->subleds))
return dev_err_probe(dev, -EINVAL, "Too many LEDs for node %pfw\n", fwnode);
ret = fwnode_property_read_u32(sub, "reg", &subreg);
if (ret)
return dev_err_probe(dev, ret, "Missing 'reg' for sub-LED %pfw\n", sub);
if (subreg >= chipdef->n_leds)
return dev_err_probe(dev, -EINVAL, "Invalid 'reg' for sub-LED %pfw\n", sub);
ret = fwnode_property_read_u32(sub, "color", &color);
if (ret)
return dev_err_probe(dev, ret, "Missing 'color' for sub-LED %pfw\n", sub);
led->subleds[num_colors].channel = subreg;
led->subleds[num_colors].color_index = color;
led->subleds[num_colors].intensity = LED_FULL;
num_colors++;
}
led->mc_cdev.subled_info = led->subleds;
led->mc_cdev.num_colors = num_colors;
led->mc_cdev.led_cdev.max_brightness = LED_FULL;
led->mc_cdev.led_cdev.brightness_set_blocking = pca963x_led_mc_set;
return 0;
}
static int pca963x_register_led(struct device *dev, struct pca963x_led *led,
u32 reg, struct fwnode_handle *fwnode,
const struct pca963x_chipdef *chipdef,
bool hw_blink)
{
struct i2c_client *client = led->chip->client;
struct led_init_data init_data = {};
char label[32];
int ret;
led->led_num = reg;
/* A node with sub-children groups several channels into a multicolor LED. */
led->is_mc = fwnode_get_child_node_count(fwnode) > 0;
if (led->is_mc) {
ret = pca963x_parse_mc_subleds(dev, led, fwnode, chipdef);
if (ret)
return ret;
} else {
led->led_cdev.brightness_set_blocking = pca963x_led_set;
if (hw_blink)
led->led_cdev.blink_set = pca963x_blink_set;
}
init_data.fwnode = fwnode;
/* Keep the legacy device name to preserve existing sysfs LED names. */
init_data.devicename = "pca963x";
snprintf(label, sizeof(label), "%d:%.2x:%u", client->adapter->nr, client->addr, reg);
init_data.default_label = label;
if (led->is_mc)
return devm_led_classdev_multicolor_register_ext(dev, &led->mc_cdev,
&init_data);
return devm_led_classdev_register_ext(dev, &led->led_cdev, &init_data);
}
static int pca963x_register_leds(struct i2c_client *client,
struct pca963x *chip)
{
@@ -338,37 +459,21 @@ static int pca963x_register_leds(struct i2c_client *client,
return ret;
device_for_each_child_node_scoped(dev, child) {
struct led_init_data init_data = {};
char default_label[32];
ret = fwnode_property_read_u32(child, "reg", &reg);
if (ret || reg >= chipdef->n_leds) {
dev_err(dev, "Invalid 'reg' property for node %pfw\n",
child);
return -EINVAL;
}
if (ret)
return dev_err_probe(dev, ret,
"Missing 'reg' property for node %pfw\n", child);
if (reg >= chipdef->n_leds)
return dev_err_probe(dev, -EINVAL,
"Invalid 'reg' property for node %pfw\n", child);
led->led_num = reg;
led->chip = chip;
led->led_cdev.brightness_set_blocking = pca963x_led_set;
if (hw_blink)
led->led_cdev.blink_set = pca963x_blink_set;
led->blinking = false;
init_data.fwnode = child;
/* for backwards compatibility */
init_data.devicename = "pca963x";
snprintf(default_label, sizeof(default_label), "%d:%.2x:%u",
client->adapter->nr, client->addr, reg);
init_data.default_label = default_label;
ret = devm_led_classdev_register_ext(dev, &led->led_cdev,
&init_data);
if (ret) {
dev_err(dev, "Failed to register LED for node %pfw\n",
child);
return ret;
}
ret = pca963x_register_led(dev, led, reg, child, chipdef, hw_blink);
if (ret)
return dev_err_probe(dev, ret, "Failed to register LED for node %pfw\n",
child);
++led;
}

View File

@@ -31,10 +31,11 @@
#define ST1202_ILED_REG0 0x09
#define ST1202_MAX_LEDS 12
#define ST1202_MAX_PATTERNS 8
#define ST1202_MILLIS_PATTERN_DUR_MAX 5660
#define ST1202_MILLIS_PATTERN_DUR_MAX (ST1202_MILLIS_PATTERN_DUR_MIN * U8_MAX)
#define ST1202_MILLIS_PATTERN_DUR_MIN 22
#define ST1202_PATTERN_DUR 0x16
#define ST1202_PATTERN_PWM 0x1E
#define ST1202_PATTERN_PWM_FULL 0x0FFF
#define ST1202_PATTERN_REP 0x15
struct st1202_led {
@@ -85,7 +86,7 @@ static int st1202_write_reg(struct st1202_chip *chip, int reg, uint8_t val)
static uint8_t st1202_prescalar_to_miliseconds(unsigned int value)
{
return value / ST1202_MILLIS_PATTERN_DUR_MIN - 1;
return value / ST1202_MILLIS_PATTERN_DUR_MIN;
}
static int st1202_pwm_pattern_write(struct st1202_chip *chip, int led_num,
@@ -127,37 +128,11 @@ static int st1202_duration_pattern_write(struct st1202_chip *chip, int pattern,
st1202_prescalar_to_miliseconds(value));
}
static void st1202_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct st1202_led *led = cdev_to_st1202_led(led_cdev);
struct st1202_chip *chip = led->chip;
guard(mutex)(&chip->lock);
st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, value);
}
static enum led_brightness st1202_brightness_get(struct led_classdev *led_cdev)
{
struct st1202_led *led = cdev_to_st1202_led(led_cdev);
struct st1202_chip *chip = led->chip;
u8 value = 0;
guard(mutex)(&chip->lock);
st1202_read_reg(chip, ST1202_ILED_REG0 + led->led_num, &value);
return value;
}
static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active)
static int __st1202_channel_set(struct st1202_chip *chip, int led_num, bool active)
{
u8 chan_low, chan_high;
int ret;
guard(mutex)(&chip->lock);
if (led_num <= 7) {
ret = st1202_read_reg(chip, ST1202_CHAN_ENABLE_LOW, &chan_low);
if (ret < 0)
@@ -185,6 +160,40 @@ static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active
return 0;
}
static int st1202_channel_set(struct st1202_chip *chip, int led_num, bool active)
{
guard(mutex)(&chip->lock);
return __st1202_channel_set(chip, led_num, active);
}
static void st1202_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct st1202_led *led = cdev_to_st1202_led(led_cdev);
struct st1202_chip *chip = led->chip;
guard(mutex)(&chip->lock);
for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++)
st1202_pwm_pattern_write(chip, led->led_num, patt, ST1202_PATTERN_PWM_FULL);
st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, value);
__st1202_channel_set(chip, led->led_num, !!value);
}
static enum led_brightness st1202_brightness_get(struct led_classdev *led_cdev)
{
struct st1202_led *led = cdev_to_st1202_led(led_cdev);
struct st1202_chip *chip = led->chip;
u8 value = 0;
guard(mutex)(&chip->lock);
st1202_read_reg(chip, ST1202_ILED_REG0 + led->led_num, &value);
return value;
}
static int st1202_led_set(struct led_classdev *ldev, enum led_brightness value)
{
struct st1202_led *led = cdev_to_st1202_led(ldev);
@@ -200,12 +209,16 @@ static int st1202_led_pattern_clear(struct led_classdev *ldev)
guard(mutex)(&chip->lock);
ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
if (ret != 0)
return ret;
for (int patt = 0; patt < ST1202_MAX_PATTERNS; patt++) {
ret = st1202_pwm_pattern_write(chip, led->led_num, patt, LED_OFF);
ret = st1202_pwm_pattern_write(chip, led->led_num, patt, ST1202_PATTERN_PWM_FULL);
if (ret != 0)
return ret;
ret = st1202_duration_pattern_write(chip, patt, ST1202_MILLIS_PATTERN_DUR_MIN);
ret = st1202_write_reg(chip, ST1202_PATTERN_DUR + patt, 0);
if (ret != 0)
return ret;
}
@@ -224,13 +237,19 @@ static int st1202_led_pattern_set(struct led_classdev *ldev,
if (len > ST1202_MAX_PATTERNS)
return -EINVAL;
guard(mutex)(&chip->lock);
for (int patt = 0; patt < len; patt++) {
if (pattern[patt].delta_t < ST1202_MILLIS_PATTERN_DUR_MIN ||
pattern[patt].delta_t > ST1202_MILLIS_PATTERN_DUR_MAX)
return -EINVAL;
}
guard(mutex)(&chip->lock);
ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT);
if (ret != 0)
return ret;
for (int patt = 0; patt < len; patt++) {
ret = st1202_pwm_pattern_write(chip, led->led_num, patt, pattern[patt].brightness);
if (ret != 0)
return ret;
@@ -244,6 +263,10 @@ static int st1202_led_pattern_set(struct led_classdev *ldev,
if (ret != 0)
return ret;
ret = __st1202_channel_set(chip, led->led_num, true);
if (ret != 0)
return ret;
ret = st1202_write_reg(chip, ST1202_CONFIG_REG, (ST1202_CONFIG_REG_PATSR |
ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_SHFT));
if (ret != 0)
@@ -256,13 +279,19 @@ static int st1202_dt_init(struct st1202_chip *chip)
{
struct device *dev = &chip->client->dev;
struct st1202_led *led;
int err, reg;
int err;
u32 reg;
for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
err = of_property_read_u32(child, "reg", &reg);
if (err)
return dev_err_probe(dev, err, "Invalid register\n");
if (reg >= ST1202_MAX_LEDS)
return dev_err_probe(dev, -EINVAL,
"LED reg %u out of range [0, %d]\n",
reg, ST1202_MAX_LEDS - 1);
led = &chip->leds[reg];
led->is_active = true;
led->fwnode = of_fwnode_handle(child);
@@ -322,11 +351,6 @@ static int st1202_setup(struct st1202_chip *chip)
if (ret < 0)
return ret;
ret = st1202_write_reg(chip, ST1202_CONFIG_REG,
ST1202_CONFIG_REG_PATS | ST1202_CONFIG_REG_PATSR);
if (ret < 0)
return ret;
return 0;
}

View File

@@ -204,9 +204,9 @@ int lp5860_device_init(struct device *dev)
mutex_lock(&lp->lock);
ret = regmap_update_bits(lp->regmap, LP5860_REG_DEV_INITIAL, LP5860_MODE_MASK,
LP5860_MODE_1 << LP5860_MODE_SHIFT);
mutex_unlock(&lp->lock);
if (ret)
goto err_disable;
mutex_unlock(&lp->lock);
ret = lp5860_init_dt(lp);
if (ret)
@@ -215,7 +215,6 @@ int lp5860_device_init(struct device *dev)
return 0;
err_disable:
mutex_unlock(&lp->lock);
lp5860_chip_enable(lp, LP5860_CHIP_DISABLE);
return ret;
}

View File

@@ -38,6 +38,7 @@ static int lp5860_probe(struct spi_device *spi)
struct device *dev = &spi->dev;
struct lp5860 *lp5860;
unsigned int multi_leds;
int ret;
multi_leds = device_get_child_node_count(dev);
if (!multi_leds) {
@@ -61,7 +62,10 @@ static int lp5860_probe(struct spi_device *spi)
"Failed to initialise Regmap.\n");
lp5860->dev = dev;
mutex_init(&lp5860->lock);
ret = devm_mutex_init(dev, &lp5860->lock);
if (ret)
return ret;
spi_set_drvdata(spi, lp5860);
@@ -70,10 +74,6 @@ static int lp5860_probe(struct spi_device *spi)
static void lp5860_remove(struct spi_device *spi)
{
struct lp5860 *lp5860 = spi_get_drvdata(spi);
mutex_destroy(&lp5860->lock);
lp5860_device_remove(&spi->dev);
}

View File

@@ -94,6 +94,8 @@ static int iterate_subleds(struct device *dev, struct pwm_mc_led *priv,
}
subled[priv->mc_cdev.num_colors].color_index = color;
fwnode_property_read_u32(fwnode, "default-intensity",
&subled[priv->mc_cdev.num_colors].intensity);
priv->mc_cdev.num_colors++;
}

View File

@@ -129,6 +129,22 @@ static void set_baseline_state(struct led_netdev_data *trigger_data)
trigger_data->link_speed == SPEED_10000)
blink_on = true;
if (test_bit(TRIGGER_NETDEV_LINK_25000, &trigger_data->mode) &&
trigger_data->link_speed == SPEED_25000)
blink_on = true;
if (test_bit(TRIGGER_NETDEV_LINK_40000, &trigger_data->mode) &&
trigger_data->link_speed == SPEED_40000)
blink_on = true;
if (test_bit(TRIGGER_NETDEV_LINK_50000, &trigger_data->mode) &&
trigger_data->link_speed == SPEED_50000)
blink_on = true;
if (test_bit(TRIGGER_NETDEV_LINK_100000, &trigger_data->mode) &&
trigger_data->link_speed == SPEED_100000)
blink_on = true;
if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) &&
trigger_data->duplex == DUPLEX_HALF)
blink_on = true;
@@ -342,6 +358,10 @@ static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
case TRIGGER_NETDEV_LINK_2500:
case TRIGGER_NETDEV_LINK_5000:
case TRIGGER_NETDEV_LINK_10000:
case TRIGGER_NETDEV_LINK_25000:
case TRIGGER_NETDEV_LINK_40000:
case TRIGGER_NETDEV_LINK_50000:
case TRIGGER_NETDEV_LINK_100000:
case TRIGGER_NETDEV_HALF_DUPLEX:
case TRIGGER_NETDEV_FULL_DUPLEX:
case TRIGGER_NETDEV_TX:
@@ -378,6 +398,10 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
case TRIGGER_NETDEV_LINK_2500:
case TRIGGER_NETDEV_LINK_5000:
case TRIGGER_NETDEV_LINK_10000:
case TRIGGER_NETDEV_LINK_25000:
case TRIGGER_NETDEV_LINK_40000:
case TRIGGER_NETDEV_LINK_50000:
case TRIGGER_NETDEV_LINK_100000:
case TRIGGER_NETDEV_HALF_DUPLEX:
case TRIGGER_NETDEV_FULL_DUPLEX:
case TRIGGER_NETDEV_TX:
@@ -401,7 +425,11 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
test_bit(TRIGGER_NETDEV_LINK_1000, &mode) ||
test_bit(TRIGGER_NETDEV_LINK_2500, &mode) ||
test_bit(TRIGGER_NETDEV_LINK_5000, &mode) ||
test_bit(TRIGGER_NETDEV_LINK_10000, &mode)))
test_bit(TRIGGER_NETDEV_LINK_10000, &mode) ||
test_bit(TRIGGER_NETDEV_LINK_25000, &mode) ||
test_bit(TRIGGER_NETDEV_LINK_40000, &mode) ||
test_bit(TRIGGER_NETDEV_LINK_50000, &mode) ||
test_bit(TRIGGER_NETDEV_LINK_100000, &mode)))
return -EINVAL;
cancel_delayed_work_sync(&trigger_data->work);
@@ -438,6 +466,10 @@ DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000);
DEFINE_NETDEV_TRIGGER(link_2500, TRIGGER_NETDEV_LINK_2500);
DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000);
DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000);
DEFINE_NETDEV_TRIGGER(link_25000, TRIGGER_NETDEV_LINK_25000);
DEFINE_NETDEV_TRIGGER(link_40000, TRIGGER_NETDEV_LINK_40000);
DEFINE_NETDEV_TRIGGER(link_50000, TRIGGER_NETDEV_LINK_50000);
DEFINE_NETDEV_TRIGGER(link_100000, TRIGGER_NETDEV_LINK_100000);
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);
@@ -526,6 +558,10 @@ static umode_t netdev_trig_link_speed_visible(struct kobject *kobj,
CHECK_LINK_MODE_ATTR(2500);
CHECK_LINK_MODE_ATTR(5000);
CHECK_LINK_MODE_ATTR(10000);
CHECK_LINK_MODE_ATTR(25000);
CHECK_LINK_MODE_ATTR(40000);
CHECK_LINK_MODE_ATTR(50000);
CHECK_LINK_MODE_ATTR(100000);
}
return 0;
@@ -538,6 +574,10 @@ static struct attribute *netdev_trig_link_speed_attrs[] = {
&dev_attr_link_2500.attr,
&dev_attr_link_5000.attr,
&dev_attr_link_10000.attr,
&dev_attr_link_25000.attr,
&dev_attr_link_40000.attr,
&dev_attr_link_50000.attr,
&dev_attr_link_100000.attr,
NULL
};
@@ -673,6 +713,10 @@ static void netdev_trig_work(struct work_struct *work)
test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) ||
test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) ||
test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) ||
test_bit(TRIGGER_NETDEV_LINK_25000, &trigger_data->mode) ||
test_bit(TRIGGER_NETDEV_LINK_40000, &trigger_data->mode) ||
test_bit(TRIGGER_NETDEV_LINK_50000, &trigger_data->mode) ||
test_bit(TRIGGER_NETDEV_LINK_100000, &trigger_data->mode) ||
test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) ||
test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode);
interval = jiffies_to_msecs(

View File

@@ -607,6 +607,10 @@ enum led_trigger_netdev_modes {
TRIGGER_NETDEV_LINK_2500,
TRIGGER_NETDEV_LINK_5000,
TRIGGER_NETDEV_LINK_10000,
TRIGGER_NETDEV_LINK_25000,
TRIGGER_NETDEV_LINK_40000,
TRIGGER_NETDEV_LINK_50000,
TRIGGER_NETDEV_LINK_100000,
TRIGGER_NETDEV_HALF_DUPLEX,
TRIGGER_NETDEV_FULL_DUPLEX,
TRIGGER_NETDEV_TX,
@@ -676,8 +680,10 @@ typedef int (*gpio_blink_set_t)(struct gpio_desc *desc, int state,
struct gpio_led {
const char *name;
const char *default_trigger;
#ifdef CONFIG_GPIOLIB_LEGACY
unsigned gpio;
unsigned active_low : 1;
#endif
unsigned retain_state_suspended : 1;
unsigned panic_indicator : 1;
unsigned default_state : 2;