Pull request for 5.10-rc1.
Quite a lot of stuff is going on here. Great cleanups/fixes from Marek and others are biggest part. I limited CPU LED trigger to 8 LEDs, because it was willing to register 1024 "triggers" on machine with 1024 CPUs. I don't believe it will cause any problems, but we can raise the limit if it does. -----BEGIN PGP SIGNATURE----- iF0EABECAB0WIQRPfPO7r0eAhk010v0w5/Bqldv68gUCX4bazAAKCRAw5/Bqldv6 8tVrAJ4szXoDAwqphVAV1UBVDLfW8TmgogCgjLkj6bqMxBukSp2ynEVEoqyuPBQ= =a+Fi -----END PGP SIGNATURE----- Merge tag 'leds-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds Pull LED updates from Pavel Machek: "Quite a lot of stuff is going on here. Great cleanups/fixes from Marek and others are biggest part. I limited CPU LED trigger to 8 LEDs, because it was willing to register 1024 'triggers' on machine with 1024 CPUs. I don't believe it will cause any problems, but we can raise the limit if it does" * tag 'leds-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds: (84 commits) leds: pwm: Remove platform_data support leds: lm3697: Fix out-of-bound access leds: ns2: do not guard OF match pointer with of_match_ptr leds: ns2: convert to fwnode API leds: tlc591xx: fix leak of device node iterator leds: pca963x: use struct led_init_data when registering leds: pca963x: register LEDs immediately after parsing, get rid of platdata leds: tca6507: remove binding comment leds: tca6507: cosmetic change: use helper variable leds: tca6507: do not set GPIO names dt-bindings: leds: tca6507: convert to YAML ledtrig-cpu: Limit to 8 CPUs leds: TODO: Add documentation about possible subsystem improvements leds: pca9532: read pwm settings from device tree leds: pca9532: correct shift computation in pca9532_getled leds: lm36274: Fix warning for undefined parameters leds: lm3532: Fix warnings for undefined parameters leds: pca963x: use flexible array leds: pca963x: cosmetic: rename variables leds: pca963x: cosmetic: rename variables ...
This commit is contained in:
commit
7fafb54c7d
|
@ -16,6 +16,7 @@ Optional properties:
|
|||
- audio-gain-db : audio gain selection for external analog modulation input.
|
||||
Valid values: 0 - 21, step by 3 (rounded down)
|
||||
Default: 0
|
||||
- shutdown-gpios : Specifier of the GPIO connected to SDB pin of the chip.
|
||||
|
||||
Each led is represented as a sub-node of the issi,is31fl319x device.
|
||||
There can be less leds subnodes than the chip can support but not more.
|
||||
|
@ -44,6 +45,7 @@ fancy_leds: leds@65 {
|
|||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x65>;
|
||||
shutdown-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
red_aux: led@1 {
|
||||
label = "red:aux";
|
||||
|
|
130
Documentation/devicetree/bindings/leds/leds-lp50xx.yaml
Normal file
130
Documentation/devicetree/bindings/leds/leds-lp50xx.yaml
Normal file
|
@ -0,0 +1,130 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/leds-lp50xx.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: LED driver for LP50XX RGB LED from Texas Instruments.
|
||||
|
||||
maintainers:
|
||||
- Dan Murphy <dmurphy@ti.com>
|
||||
|
||||
description: |
|
||||
The LP50XX is multi-channel, I2C RGB LED Drivers that can group RGB LEDs into
|
||||
a LED group or control them individually.
|
||||
|
||||
The difference in these RGB LED drivers is the number of supported RGB
|
||||
modules.
|
||||
|
||||
For more product information please see the link below:
|
||||
https://www.ti.com/lit/ds/symlink/lp5012.pdf
|
||||
https://www.ti.com/lit/ds/symlink/lp5024.pdf
|
||||
https://www.ti.com/lit/ds/symlink/lp5036.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,lp5009
|
||||
- ti,lp5012
|
||||
- ti,lp5018
|
||||
- ti,lp5024
|
||||
- ti,lp5030
|
||||
- ti,lp5036
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description:
|
||||
I2C slave address
|
||||
lp5009/12 - 0x14, 0x15, 0x16, 0x17
|
||||
lp5018/24 - 0x28, 0x29, 0x2a, 0x2b
|
||||
lp5030/36 - 0x30, 0x31, 0x32, 0x33
|
||||
|
||||
enable-gpios:
|
||||
maxItems: 1
|
||||
description: GPIO pin to enable/disable the device.
|
||||
|
||||
vled-supply:
|
||||
description: LED supply.
|
||||
|
||||
patternProperties:
|
||||
'^multi-led@[0-9a-f]$':
|
||||
type: object
|
||||
allOf:
|
||||
- $ref: leds-class-multicolor.yaml#
|
||||
properties:
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 12
|
||||
description:
|
||||
This property denotes the LED module number(s) that is used on the
|
||||
for the child node. The LED modules can either be used stand alone
|
||||
or grouped into a module bank.
|
||||
|
||||
patternProperties:
|
||||
"(^led-[0-9a-f]$|led)":
|
||||
type: object
|
||||
$ref: common.yaml#
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led-controller@14 {
|
||||
compatible = "ti,lp5009";
|
||||
reg = <0x14>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
enable-gpios = <&gpio1 16>;
|
||||
|
||||
multi-led@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x1>;
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_CHARGING;
|
||||
|
||||
led-0 {
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
led-1 {
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
};
|
||||
|
||||
led-2 {
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
};
|
||||
};
|
||||
|
||||
multi-led@2 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <2>;
|
||||
reg = <0x2 0x3 0x5>;
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_STANDBY;
|
||||
|
||||
led-6 {
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
};
|
||||
|
||||
led-7 {
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
};
|
||||
|
||||
led-8 {
|
||||
color = <LED_COLOR_ID_BLUE>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -189,7 +189,7 @@ examples:
|
|||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x2>;
|
||||
color = <LED_COLOR_ID_MULTI>;
|
||||
color = <LED_COLOR_ID_RGB>;
|
||||
function = LED_FUNCTION_STANDBY;
|
||||
linux,default-trigger = "heartbeat";
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ Required properties:
|
|||
"nxp,pca9550"
|
||||
"nxp,pca9551"
|
||||
"nxp,pca9552"
|
||||
"ibm,pca9552"
|
||||
"nxp,pca9553"
|
||||
- #address-cells: must be 1
|
||||
- #size-cells: must be 0
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
LEDs connected to tca6507
|
||||
|
||||
Required properties:
|
||||
- compatible : should be : "ti,tca6507".
|
||||
- #address-cells: must be 1
|
||||
- #size-cells: must be 0
|
||||
- reg: typically 0x45.
|
||||
|
||||
Optional properties:
|
||||
- gpio-controller: allows lines to be used as output-only GPIOs.
|
||||
- #gpio-cells: if present, must not be 0.
|
||||
|
||||
Each led is represented as a sub-node of the ti,tca6507 device.
|
||||
|
||||
LED sub-node properties:
|
||||
- label : (optional) see Documentation/devicetree/bindings/leds/common.txt
|
||||
- reg : number of LED line (could be from 0 to 6)
|
||||
- linux,default-trigger : (optional)
|
||||
see Documentation/devicetree/bindings/leds/common.txt
|
||||
- compatible: either "led" (the default) or "gpio".
|
||||
|
||||
Examples:
|
||||
|
||||
tca6507@45 {
|
||||
compatible = "ti,tca6507";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x45>;
|
||||
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
||||
led0: red-aux@0 {
|
||||
label = "red:aux";
|
||||
reg = <0x0>;
|
||||
};
|
||||
|
||||
led1: green-aux@1 {
|
||||
label = "green:aux";
|
||||
reg = <0x5>;
|
||||
linux,default-trigger = "default-on";
|
||||
};
|
||||
|
||||
wifi-reset@6 {
|
||||
reg = <0x6>;
|
||||
compatible = "gpio";
|
||||
};
|
||||
};
|
||||
|
134
Documentation/devicetree/bindings/leds/ti,tca6507.yaml
Normal file
134
Documentation/devicetree/bindings/leds/ti,tca6507.yaml
Normal file
|
@ -0,0 +1,134 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/leds/ti,tca6507.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TCA6507 LED and GPIO controller
|
||||
|
||||
maintainers:
|
||||
- NeilBrown <neilb@suse.de>
|
||||
|
||||
description:
|
||||
The TCA6507 is a programmable LED controller connected via I2C that can drive
|
||||
7 separate lines either by holding them low, or by pulsing them with modulated
|
||||
width.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ti,tca6507
|
||||
|
||||
reg:
|
||||
description: I2C slave address of the controller.
|
||||
maxItems: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
gpio-line-names: true
|
||||
|
||||
patternProperties:
|
||||
"^led@[0-6]$":
|
||||
type: object
|
||||
|
||||
$ref: common.yaml#
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 6
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
"^gpio@[0-6]$":
|
||||
type: object
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: gpio
|
||||
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 6
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- reg
|
||||
- compatible
|
||||
|
||||
if:
|
||||
patternProperties:
|
||||
"^gpio@[0-6]$":
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: gpio
|
||||
then:
|
||||
required:
|
||||
- gpio-controller
|
||||
- "#gpio-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
i2c0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
led-controller@45 {
|
||||
compatible = "ti,tca6507";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x45>;
|
||||
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
||||
gpio-line-names = "wifi_reset@6";
|
||||
|
||||
led@0 {
|
||||
label = "gta04:red:aux";
|
||||
reg = <0x0>;
|
||||
};
|
||||
|
||||
led@1 {
|
||||
label = "gta04:green:aux";
|
||||
reg = <0x1>;
|
||||
};
|
||||
|
||||
led@3 {
|
||||
reg = <0x3>;
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
function = LED_FUNCTION_POWER;
|
||||
linux,default-trigger = "default-on";
|
||||
};
|
||||
|
||||
led@4 {
|
||||
color = <LED_COLOR_ID_GREEN>;
|
||||
function = LED_FUNCTION_POWER;
|
||||
reg = <0x4>;
|
||||
};
|
||||
|
||||
gpio@6 {
|
||||
compatible = "gpio";
|
||||
reg = <0x6>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -17,12 +17,6 @@ set a timer to hold a state, however when user space application crashes or
|
|||
goes away without deactivating the timer, the hardware will be left in that
|
||||
state permanently.
|
||||
|
||||
As a specific example of this use-case, let's look at vibrate feature on
|
||||
phones. Vibrate function on phones is implemented using PWM pins on SoC or
|
||||
PMIC. There is a need to activate one shot timer to control the vibrate
|
||||
feature, to prevent user space crashes leaving the phone in vibrate mode
|
||||
permanently causing the battery to drain.
|
||||
|
||||
Transient trigger addresses the need for one shot timer activation. The
|
||||
transient trigger can be enabled and disabled just like the other leds
|
||||
triggers.
|
||||
|
@ -159,7 +153,6 @@ repeat the following step as needed::
|
|||
|
||||
This trigger is intended to be used for the following example use cases:
|
||||
|
||||
- Control of vibrate (phones, tablets etc.) hardware by user space app.
|
||||
- Use of LED by user space app as activity indicator.
|
||||
- Use of LED by user space app as a kind of watchdog indicator -- as
|
||||
long as the app is alive, it can keep the LED illuminated, if it dies
|
||||
|
|
|
@ -274,7 +274,7 @@ config LEDS_MT6323
|
|||
config LEDS_S3C24XX
|
||||
tristate "LED Support for Samsung S3C24XX GPIO LEDs"
|
||||
depends on LEDS_CLASS
|
||||
depends on ARCH_S3C24XX
|
||||
depends on ARCH_S3C24XX || COMPILE_TEST
|
||||
help
|
||||
This option enables support for LEDs connected to GPIO lines
|
||||
on Samsung S3C24XX series CPUs, such as the S3C2410 and S3C2440.
|
||||
|
@ -304,13 +304,13 @@ config LEDS_WRAP
|
|||
config LEDS_COBALT_QUBE
|
||||
tristate "LED Support for the Cobalt Qube series front LED"
|
||||
depends on LEDS_CLASS
|
||||
depends on MIPS_COBALT
|
||||
depends on MIPS_COBALT || COMPILE_TEST
|
||||
help
|
||||
This option enables support for the front LED on Cobalt Qube series
|
||||
|
||||
config LEDS_COBALT_RAQ
|
||||
bool "LED Support for the Cobalt Raq series"
|
||||
depends on LEDS_CLASS=y && MIPS_COBALT
|
||||
depends on LEDS_CLASS=y && (MIPS_COBALT || COMPILE_TEST)
|
||||
select LEDS_TRIGGERS
|
||||
help
|
||||
This option enables support for the Cobalt Raq series LEDs.
|
||||
|
@ -395,8 +395,20 @@ config LEDS_LP3952
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called leds-lp3952.
|
||||
|
||||
config LEDS_LP50XX
|
||||
tristate "LED Support for TI LP5036/30/24/18/12/9 LED driver chip"
|
||||
depends on LEDS_CLASS && REGMAP_I2C
|
||||
depends on LEDS_CLASS_MULTICOLOR || !LEDS_CLASS_MULTICOLOR
|
||||
help
|
||||
If you say yes here you get support for the Texas Instruments
|
||||
LP5036, LP5030, LP5024, LP5018, LP5012 and LP5009 LED driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called leds-lp50xx.
|
||||
|
||||
config LEDS_LP55XX_COMMON
|
||||
tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501"
|
||||
depends on LEDS_CLASS
|
||||
depends on LEDS_CLASS_MULTICOLOR || !LEDS_CLASS_MULTICOLOR
|
||||
depends on OF
|
||||
depends on I2C
|
||||
|
@ -632,7 +644,7 @@ config LEDS_MC13783
|
|||
config LEDS_NS2
|
||||
tristate "LED support for Network Space v2 GPIO LEDs"
|
||||
depends on LEDS_CLASS
|
||||
depends on MACH_KIRKWOOD || MACH_ARMADA_370
|
||||
depends on MACH_KIRKWOOD || MACH_ARMADA_370 || COMPILE_TEST
|
||||
default y
|
||||
help
|
||||
This option enables support for the dual-GPIO LEDs found on the
|
||||
|
@ -646,7 +658,7 @@ config LEDS_NS2
|
|||
config LEDS_NETXBIG
|
||||
tristate "LED support for Big Network series LEDs"
|
||||
depends on LEDS_CLASS
|
||||
depends on MACH_KIRKWOOD
|
||||
depends on MACH_KIRKWOOD || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
default y
|
||||
help
|
||||
|
@ -893,7 +905,7 @@ config LEDS_TPS6105X
|
|||
config LEDS_IP30
|
||||
tristate "LED support for SGI Octane machines"
|
||||
depends on LEDS_CLASS
|
||||
depends on SGI_MFD_IOC3
|
||||
depends on SGI_MFD_IOC3 || COMPILE_TEST
|
||||
help
|
||||
This option enables support for the Red and White LEDs of
|
||||
SGI Octane machines.
|
||||
|
@ -909,6 +921,13 @@ config LEDS_SGM3140
|
|||
This option enables support for the SGM3140 500mA Buck/Boost Charge
|
||||
Pump LED Driver.
|
||||
|
||||
config LEDS_ACER_A500
|
||||
tristate "Power button LED support for Acer Iconia Tab A500"
|
||||
depends on LEDS_CLASS && MFD_ACER_A500_EC
|
||||
help
|
||||
This option enables support for the Power Button LED of
|
||||
Acer Iconia Tab A500.
|
||||
|
||||
comment "LED Triggers"
|
||||
source "drivers/leds/trigger/Kconfig"
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
|
|||
# LED Platform Drivers (keep this sorted, M-| sort)
|
||||
obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o
|
||||
obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o
|
||||
obj-$(CONFIG_LEDS_ACER_A500) += leds-acer-a500.o
|
||||
obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o
|
||||
obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o
|
||||
obj-$(CONFIG_LEDS_APU) += leds-apu.o
|
||||
|
@ -49,6 +50,7 @@ obj-$(CONFIG_LEDS_LM3697) += leds-lm3697.o
|
|||
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
|
||||
obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
|
||||
obj-$(CONFIG_LEDS_LP3952) += leds-lp3952.o
|
||||
obj-$(CONFIG_LEDS_LP50XX) += leds-lp50xx.o
|
||||
obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
|
||||
obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o
|
||||
obj-$(CONFIG_LEDS_LP5562) += leds-lp5562.o
|
||||
|
|
75
drivers/leds/TODO
Normal file
75
drivers/leds/TODO
Normal file
|
@ -0,0 +1,75 @@
|
|||
-*- org -*-
|
||||
|
||||
* On/off LEDs should have max_brightness of 1
|
||||
* Get rid of enum led_brightness
|
||||
|
||||
It is really an integer, as maximum is configurable. Get rid of it, or
|
||||
make it into typedef or something.
|
||||
|
||||
* Review atomicity requirements in LED subsystem
|
||||
|
||||
Calls that may and that may not block are mixed in same structure, and
|
||||
semantics is sometimes non-intuitive. (For example blink callback may
|
||||
not sleep.) Review the requirements for any bugs and document them
|
||||
clearly.
|
||||
|
||||
* LED names are still a mess
|
||||
|
||||
No two LEDs have same name, so the names are probably unusable for the
|
||||
userland. Nudge authors into creating common LED names for common
|
||||
functionality.
|
||||
|
||||
? Perhaps check for known LED names during boot, and warn if there are
|
||||
LEDs not on the list?
|
||||
|
||||
* Split drivers into subdirectories
|
||||
|
||||
The number of drivers is getting big, and driver for on/off LED on a
|
||||
i/o port is really quite different from camera flash LED, which is
|
||||
really different from driver for RGB color LED that can run its own
|
||||
microcode. Split the drivers somehow.
|
||||
|
||||
* Figure out what to do with RGB leds
|
||||
|
||||
Multicolor is a bit too abstract. Yes, we can have
|
||||
Green-Magenta-Ultraviolet LED, but so far all the LEDs we support are
|
||||
RGB, and not even RGB-White or RGB-Yellow variants emerged.
|
||||
|
||||
Multicolor is not a good fit for RGB LED. It does not really know
|
||||
about LED color. In particular, there's no way to make LED "white".
|
||||
|
||||
Userspace is interested in knowing "this LED can produce arbitrary
|
||||
color", which not all multicolor LEDs can.
|
||||
|
||||
Proposal: let's add "rgb" to led_colors in drivers/leds/led-core.c,
|
||||
add corresponding device tree defines, and use that, instead of
|
||||
multicolor for RGB LEDs.
|
||||
|
||||
We really need to do that now; "white" stuff can wait.
|
||||
|
||||
RGB LEDs are quite common, and it would be good to be able to turn LED
|
||||
white and to turn it into any arbitrary color. It is essential that
|
||||
userspace is able to set arbitrary colors, and it might be good to
|
||||
have that ability from kernel, too... to allow full-color triggers.
|
||||
|
||||
* Command line utility to manipulate the LEDs?
|
||||
|
||||
/sys interface is not really suitable to use by hand, should we have
|
||||
an utility to perform LED control?
|
||||
|
||||
In particular, LED names are still a mess (see above) and utility
|
||||
could help there by presenting both old and new names while we clean
|
||||
them up.
|
||||
|
||||
In future, I'd like utility to accept both old and new names while we
|
||||
clean them up.
|
||||
|
||||
It would be also nice to have useful listing mode -- name, type,
|
||||
current brightness/trigger...
|
||||
|
||||
In future, it would be good to be able to set rgb led to particular
|
||||
color.
|
||||
|
||||
And probably user-friendly interface to access LEDs for particular
|
||||
ethernet interface would be nice.
|
||||
|
|
@ -354,6 +354,11 @@ int led_classdev_register_ext(struct device *parent,
|
|||
ret = led_compose_name(parent, init_data, composed_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (init_data->fwnode)
|
||||
fwnode_property_read_string(init_data->fwnode,
|
||||
"linux,default-trigger",
|
||||
&led_cdev->default_trigger);
|
||||
} else {
|
||||
proposed_name = led_cdev->name;
|
||||
}
|
||||
|
|
|
@ -118,14 +118,14 @@ static int pm860x_led_dt_init(struct platform_device *pdev,
|
|||
struct device_node *nproot, *np;
|
||||
int iset = 0;
|
||||
|
||||
if (!pdev->dev.parent->of_node)
|
||||
if (!dev_of_node(pdev->dev.parent))
|
||||
return -ENODEV;
|
||||
nproot = of_get_child_by_name(pdev->dev.parent->of_node, "leds");
|
||||
nproot = of_get_child_by_name(dev_of_node(pdev->dev.parent), "leds");
|
||||
if (!nproot) {
|
||||
dev_err(&pdev->dev, "failed to find leds node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
for_each_child_of_node(nproot, np) {
|
||||
for_each_available_child_of_node(nproot, np) {
|
||||
if (of_node_name_eq(np, data->name)) {
|
||||
of_property_read_u32(np, "marvell,88pm860x-iset",
|
||||
&iset);
|
||||
|
|
|
@ -248,7 +248,7 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
|
|||
}
|
||||
#endif
|
||||
|
||||
child_node = of_get_next_available_child(dev->of_node, NULL);
|
||||
child_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;
|
||||
|
|
129
drivers/leds/leds-acer-a500.c
Normal file
129
drivers/leds/leds-acer-a500.c
Normal file
|
@ -0,0 +1,129 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define A500_EC_LED_DELAY_USEC (100 * 1000)
|
||||
|
||||
enum {
|
||||
REG_RESET_LEDS = 0x40,
|
||||
REG_POWER_LED_ON = 0x42,
|
||||
REG_CHARGE_LED_ON = 0x43,
|
||||
REG_ANDROID_LEDS_OFF = 0x5a,
|
||||
};
|
||||
|
||||
struct a500_led {
|
||||
struct led_classdev cdev;
|
||||
const struct reg_sequence *enable_seq;
|
||||
struct a500_led *other;
|
||||
struct regmap *rmap;
|
||||
};
|
||||
|
||||
static const struct reg_sequence a500_ec_leds_reset_seq[] = {
|
||||
REG_SEQ(REG_RESET_LEDS, 0x0, A500_EC_LED_DELAY_USEC),
|
||||
REG_SEQ(REG_ANDROID_LEDS_OFF, 0x0, A500_EC_LED_DELAY_USEC),
|
||||
};
|
||||
|
||||
static const struct reg_sequence a500_ec_white_led_enable_seq[] = {
|
||||
REG_SEQ(REG_POWER_LED_ON, 0x0, A500_EC_LED_DELAY_USEC),
|
||||
};
|
||||
|
||||
static const struct reg_sequence a500_ec_orange_led_enable_seq[] = {
|
||||
REG_SEQ(REG_CHARGE_LED_ON, 0x0, A500_EC_LED_DELAY_USEC),
|
||||
};
|
||||
|
||||
static int a500_ec_led_brightness_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct a500_led *led = container_of(led_cdev, struct a500_led, cdev);
|
||||
struct reg_sequence control_seq[2];
|
||||
unsigned int num_regs = 1;
|
||||
|
||||
if (value) {
|
||||
control_seq[0] = led->enable_seq[0];
|
||||
} else {
|
||||
/*
|
||||
* There is no separate controls which can disable LEDs
|
||||
* individually, there is only RESET_LEDS command that turns
|
||||
* off both LEDs.
|
||||
*
|
||||
* RESET_LEDS turns off both LEDs, thus restore other LED if
|
||||
* it's turned ON.
|
||||
*/
|
||||
if (led->other->cdev.brightness)
|
||||
num_regs = 2;
|
||||
|
||||
control_seq[0] = a500_ec_leds_reset_seq[0];
|
||||
control_seq[1] = led->other->enable_seq[0];
|
||||
}
|
||||
|
||||
return regmap_multi_reg_write(led->rmap, control_seq, num_regs);
|
||||
}
|
||||
|
||||
static int a500_ec_leds_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct a500_led *white_led, *orange_led;
|
||||
struct regmap *rmap;
|
||||
int err;
|
||||
|
||||
rmap = dev_get_regmap(pdev->dev.parent, "KB930");
|
||||
if (!rmap)
|
||||
return -EINVAL;
|
||||
|
||||
/* reset and turn off LEDs */
|
||||
regmap_multi_reg_write(rmap, a500_ec_leds_reset_seq, 2);
|
||||
|
||||
white_led = devm_kzalloc(&pdev->dev, sizeof(*white_led), GFP_KERNEL);
|
||||
if (!white_led)
|
||||
return -ENOMEM;
|
||||
|
||||
white_led->cdev.name = "power:white";
|
||||
white_led->cdev.brightness_set_blocking = a500_ec_led_brightness_set;
|
||||
white_led->cdev.flags = LED_CORE_SUSPENDRESUME;
|
||||
white_led->cdev.max_brightness = 1;
|
||||
white_led->enable_seq = a500_ec_white_led_enable_seq;
|
||||
white_led->rmap = rmap;
|
||||
|
||||
orange_led = devm_kzalloc(&pdev->dev, sizeof(*orange_led), GFP_KERNEL);
|
||||
if (!orange_led)
|
||||
return -ENOMEM;
|
||||
|
||||
orange_led->cdev.name = "power:orange";
|
||||
orange_led->cdev.brightness_set_blocking = a500_ec_led_brightness_set;
|
||||
orange_led->cdev.flags = LED_CORE_SUSPENDRESUME;
|
||||
orange_led->cdev.max_brightness = 1;
|
||||
orange_led->enable_seq = a500_ec_orange_led_enable_seq;
|
||||
orange_led->rmap = rmap;
|
||||
|
||||
white_led->other = orange_led;
|
||||
orange_led->other = white_led;
|
||||
|
||||
err = devm_led_classdev_register(&pdev->dev, &white_led->cdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to register white LED\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = devm_led_classdev_register(&pdev->dev, &orange_led->cdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to register orange LED\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver a500_ec_leds_driver = {
|
||||
.driver = {
|
||||
.name = "acer-a500-iconia-leds",
|
||||
},
|
||||
.probe = a500_ec_leds_probe,
|
||||
};
|
||||
module_platform_driver(a500_ec_leds_driver);
|
||||
|
||||
MODULE_DESCRIPTION("LED driver for Acer Iconia Tab A500 Power Button");
|
||||
MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
|
||||
MODULE_ALIAS("platform:acer-a500-iconia-leds");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -202,13 +202,13 @@ error:
|
|||
static int an30259a_dt_init(struct i2c_client *client,
|
||||
struct an30259a *chip)
|
||||
{
|
||||
struct device_node *np = client->dev.of_node, *child;
|
||||
struct device_node *np = dev_of_node(&client->dev), *child;
|
||||
int count, ret;
|
||||
int i = 0;
|
||||
const char *str;
|
||||
struct an30259a_led *led;
|
||||
|
||||
count = of_get_child_count(np);
|
||||
count = of_get_available_child_count(np);
|
||||
if (!count || count > AN30259A_MAX_LEDS)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -238,9 +238,6 @@ static int an30259a_dt_init(struct i2c_client *client,
|
|||
led->default_state = STATE_OFF;
|
||||
}
|
||||
|
||||
of_property_read_string(child, "linux,default-trigger",
|
||||
&led->cdev.default_trigger);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
|
|
|
@ -261,11 +261,11 @@ out:
|
|||
|
||||
static int aw2013_probe_dt(struct aw2013 *chip)
|
||||
{
|
||||
struct device_node *np = chip->client->dev.of_node, *child;
|
||||
struct device_node *np = dev_of_node(&chip->client->dev), *child;
|
||||
int count, ret = 0, i = 0;
|
||||
struct aw2013_led *led;
|
||||
|
||||
count = of_get_child_count(np);
|
||||
count = of_get_available_child_count(np);
|
||||
if (!count || count > AW2013_MAX_LEDS)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -297,16 +297,15 @@ static int aw2013_probe_dt(struct aw2013 *chip)
|
|||
"DT property led-max-microamp is missing\n");
|
||||
}
|
||||
|
||||
of_property_read_string(child, "linux,default-trigger",
|
||||
&led->cdev.default_trigger);
|
||||
|
||||
led->cdev.brightness_set_blocking = aw2013_brightness_set;
|
||||
led->cdev.blink_set = aw2013_blink_set;
|
||||
|
||||
ret = devm_led_classdev_register_ext(&chip->client->dev,
|
||||
&led->cdev, &init_data);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
|
|
@ -328,6 +328,7 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
|
|||
void __iomem *mem, spinlock_t *lock,
|
||||
unsigned long *blink_leds, unsigned long *blink_delay)
|
||||
{
|
||||
struct led_init_data init_data = {};
|
||||
struct bcm6328_led *led;
|
||||
const char *state;
|
||||
int rc;
|
||||
|
@ -345,11 +346,6 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
|
|||
if (of_property_read_bool(nc, "active-low"))
|
||||
led->active_low = true;
|
||||
|
||||
led->cdev.name = of_get_property(nc, "label", NULL) ? : nc->name;
|
||||
led->cdev.default_trigger = of_get_property(nc,
|
||||
"linux,default-trigger",
|
||||
NULL);
|
||||
|
||||
if (!of_property_read_string(nc, "default-state", &state)) {
|
||||
if (!strcmp(state, "on")) {
|
||||
led->cdev.brightness = LED_FULL;
|
||||
|
@ -382,8 +378,9 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
|
|||
|
||||
led->cdev.brightness_set = bcm6328_led_set;
|
||||
led->cdev.blink_set = bcm6328_blink_set;
|
||||
init_data.fwnode = of_fwnode_handle(nc);
|
||||
|
||||
rc = led_classdev_register(dev, &led->cdev);
|
||||
rc = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
|
@ -395,7 +392,7 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
|
|||
static int bcm6328_leds_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *np = dev_of_node(&pdev->dev);
|
||||
struct device_node *child;
|
||||
void __iomem *mem;
|
||||
spinlock_t *lock; /* memory lock */
|
||||
|
|
|
@ -94,6 +94,7 @@ static void bcm6358_led_set(struct led_classdev *led_cdev,
|
|||
static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
|
||||
void __iomem *mem, spinlock_t *lock)
|
||||
{
|
||||
struct led_init_data init_data = {};
|
||||
struct bcm6358_led *led;
|
||||
const char *state;
|
||||
int rc;
|
||||
|
@ -109,11 +110,6 @@ static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
|
|||
if (of_property_read_bool(nc, "active-low"))
|
||||
led->active_low = true;
|
||||
|
||||
led->cdev.name = of_get_property(nc, "label", NULL) ? : nc->name;
|
||||
led->cdev.default_trigger = of_get_property(nc,
|
||||
"linux,default-trigger",
|
||||
NULL);
|
||||
|
||||
if (!of_property_read_string(nc, "default-state", &state)) {
|
||||
if (!strcmp(state, "on")) {
|
||||
led->cdev.brightness = LED_FULL;
|
||||
|
@ -136,8 +132,9 @@ static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
|
|||
bcm6358_led_set(&led->cdev, led->cdev.brightness);
|
||||
|
||||
led->cdev.brightness_set = bcm6358_led_set;
|
||||
init_data.fwnode = of_fwnode_handle(nc);
|
||||
|
||||
rc = led_classdev_register(dev, &led->cdev);
|
||||
rc = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
|
@ -149,7 +146,7 @@ static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
|
|||
static int bcm6358_leds_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *np = dev_of_node(&pdev->dev);
|
||||
struct device_node *child;
|
||||
void __iomem *mem;
|
||||
spinlock_t *lock; /* memory lock */
|
||||
|
|
|
@ -158,19 +158,14 @@ MODULE_DEVICE_TABLE(of, cpcap_led_of_match);
|
|||
|
||||
static int cpcap_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct cpcap_led *led;
|
||||
int err;
|
||||
|
||||
match = of_match_device(of_match_ptr(cpcap_led_of_match), &pdev->dev);
|
||||
if (!match || !match->data)
|
||||
return -EINVAL;
|
||||
|
||||
led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
|
||||
if (!led)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, led);
|
||||
led->info = match->data;
|
||||
led->info = device_get_match_data(&pdev->dev);
|
||||
led->dev = &pdev->dev;
|
||||
|
||||
if (led->info->reg == 0x0000) {
|
||||
|
|
|
@ -188,9 +188,6 @@ static int cr0014114_probe_dt(struct cr0014114 *priv)
|
|||
device_for_each_child_node(priv->dev, child) {
|
||||
led = &priv->leds[i];
|
||||
|
||||
fwnode_property_read_string(child, "linux,default-trigger",
|
||||
&led->ldev.default_trigger);
|
||||
|
||||
led->priv = priv;
|
||||
led->ldev.max_brightness = CR_MAX_BRIGHTNESS;
|
||||
led->ldev.brightness_set_blocking = cr0014114_set_sync;
|
||||
|
|
|
@ -263,9 +263,6 @@ static int el15203000_probe_dt(struct el15203000 *priv)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
fwnode_property_read_string(child, "linux,default-trigger",
|
||||
&led->ldev.default_trigger);
|
||||
|
||||
led->priv = priv;
|
||||
led->ldev.max_brightness = LED_ON;
|
||||
led->ldev.brightness_set_blocking = el15203000_set_blocking;
|
||||
|
|
|
@ -160,9 +160,6 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
|
|||
|
||||
led_dat->gpiod = led.gpiod;
|
||||
|
||||
fwnode_property_read_string(child, "linux,default-trigger",
|
||||
&led.default_trigger);
|
||||
|
||||
if (!fwnode_property_read_string(child, "default-state",
|
||||
&state)) {
|
||||
if (!strcmp(state, "keep"))
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* LED Driver for SGI Octane machines
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
/* register numbers */
|
||||
#define IS31FL319X_SHUTDOWN 0x00
|
||||
|
@ -61,6 +63,7 @@
|
|||
struct is31fl319x_chip {
|
||||
const struct is31fl319x_chipdef *cdef;
|
||||
struct i2c_client *client;
|
||||
struct gpio_desc *shutdown_gpio;
|
||||
struct regmap *regmap;
|
||||
struct mutex lock;
|
||||
u32 audio_gain_db;
|
||||
|
@ -199,26 +202,27 @@ static int is31fl319x_parse_child_dt(const struct device *dev,
|
|||
static int is31fl319x_parse_dt(struct device *dev,
|
||||
struct is31fl319x_chip *is31)
|
||||
{
|
||||
struct device_node *np = dev->of_node, *child;
|
||||
const struct of_device_id *of_dev_id;
|
||||
struct device_node *np = dev_of_node(dev), *child;
|
||||
int count;
|
||||
int ret;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
of_dev_id = of_match_device(of_is31fl319x_match, dev);
|
||||
if (!of_dev_id) {
|
||||
dev_err(dev, "Failed to match device with supported chips\n");
|
||||
return -EINVAL;
|
||||
is31->shutdown_gpio = devm_gpiod_get_optional(dev,
|
||||
"shutdown",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(is31->shutdown_gpio)) {
|
||||
ret = PTR_ERR(is31->shutdown_gpio);
|
||||
dev_err(dev, "Failed to get shutdown gpio: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
is31->cdef = of_dev_id->data;
|
||||
is31->cdef = device_get_match_data(dev);
|
||||
|
||||
count = of_get_child_count(np);
|
||||
count = of_get_available_child_count(np);
|
||||
|
||||
dev_dbg(dev, "probe %s with %d leds defined in DT\n",
|
||||
of_dev_id->compatible, count);
|
||||
dev_dbg(dev, "probing with %d leds defined in DT\n", count);
|
||||
|
||||
if (!count || count > is31->cdef->num_leds) {
|
||||
dev_err(dev, "Number of leds defined must be between 1 and %u\n",
|
||||
|
@ -226,7 +230,7 @@ static int is31fl319x_parse_dt(struct device *dev,
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
for_each_available_child_of_node(np, child) {
|
||||
struct is31fl319x_led *led;
|
||||
u32 reg;
|
||||
|
||||
|
@ -350,6 +354,12 @@ static int is31fl319x_probe(struct i2c_client *client,
|
|||
if (err)
|
||||
goto free_mutex;
|
||||
|
||||
if (is31->shutdown_gpio) {
|
||||
gpiod_direction_output(is31->shutdown_gpio, 0);
|
||||
mdelay(5);
|
||||
gpiod_direction_output(is31->shutdown_gpio, 1);
|
||||
}
|
||||
|
||||
is31->client = client;
|
||||
is31->regmap = devm_regmap_init_i2c(client, ®map_config);
|
||||
if (IS_ERR(is31->regmap)) {
|
||||
|
|
|
@ -332,9 +332,6 @@ static int is31fl32xx_parse_child_dt(const struct device *dev,
|
|||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
if (of_property_read_string(child, "label", &cdev->name))
|
||||
cdev->name = child->name;
|
||||
|
||||
ret = of_property_read_u32(child, "reg", ®);
|
||||
if (ret || reg < 1 || reg > led_data->priv->cdef->channels) {
|
||||
dev_err(dev,
|
||||
|
@ -344,9 +341,6 @@ static int is31fl32xx_parse_child_dt(const struct device *dev,
|
|||
}
|
||||
led_data->channel = reg;
|
||||
|
||||
of_property_read_string(child, "linux,default-trigger",
|
||||
&cdev->default_trigger);
|
||||
|
||||
cdev->brightness_set_blocking = is31fl32xx_brightness_set;
|
||||
|
||||
return 0;
|
||||
|
@ -372,7 +366,8 @@ static int is31fl32xx_parse_dt(struct device *dev,
|
|||
struct device_node *child;
|
||||
int ret = 0;
|
||||
|
||||
for_each_child_of_node(dev->of_node, child) {
|
||||
for_each_available_child_of_node(dev_of_node(dev), child) {
|
||||
struct led_init_data init_data = {};
|
||||
struct is31fl32xx_led_data *led_data =
|
||||
&priv->leds[priv->num_leds];
|
||||
const struct is31fl32xx_led_data *other_led_data;
|
||||
|
@ -388,17 +383,18 @@ static int is31fl32xx_parse_dt(struct device *dev,
|
|||
led_data->channel);
|
||||
if (other_led_data) {
|
||||
dev_err(dev,
|
||||
"%s and %s both attempting to use channel %d\n",
|
||||
led_data->cdev.name,
|
||||
other_led_data->cdev.name,
|
||||
led_data->channel);
|
||||
"Node %pOF 'reg' conflicts with another LED\n",
|
||||
child);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = devm_led_classdev_register(dev, &led_data->cdev);
|
||||
init_data.fwnode = of_fwnode_handle(child);
|
||||
|
||||
ret = devm_led_classdev_register_ext(dev, &led_data->cdev,
|
||||
&init_data);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register PWM led for %s: %d\n",
|
||||
led_data->cdev.name, ret);
|
||||
dev_err(dev, "Failed to register LED for %pOF: %d\n",
|
||||
child, ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -428,19 +424,14 @@ static int is31fl32xx_probe(struct i2c_client *client,
|
|||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct is31fl32xx_chipdef *cdef;
|
||||
const struct of_device_id *of_dev_id;
|
||||
struct device *dev = &client->dev;
|
||||
struct is31fl32xx_priv *priv;
|
||||
int count;
|
||||
int ret = 0;
|
||||
|
||||
of_dev_id = of_match_device(of_is31fl32xx_match, dev);
|
||||
if (!of_dev_id)
|
||||
return -EINVAL;
|
||||
cdef = device_get_match_data(dev);
|
||||
|
||||
cdef = of_dev_id->data;
|
||||
|
||||
count = of_get_child_count(dev->of_node);
|
||||
count = of_get_available_child_count(dev_of_node(dev));
|
||||
if (!count)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
@ -259,11 +259,11 @@ static void ktd2692_setup(struct ktd2692_context *led)
|
|||
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;
|
||||
struct device_node *np = dev_of_node(dev);
|
||||
struct device_node *child_node;
|
||||
int ret;
|
||||
|
||||
if (!dev->of_node)
|
||||
if (!dev_of_node(dev))
|
||||
return -ENXIO;
|
||||
|
||||
led->ctrl_gpio = devm_gpiod_get(dev, "ctrl", GPIOD_ASIS);
|
||||
|
|
|
@ -96,15 +96,15 @@
|
|||
|
||||
/*
|
||||
* struct lm3532_als_data
|
||||
* @config - value of ALS configuration register
|
||||
* @als1_imp_sel - value of ALS1 resistor select register
|
||||
* @als2_imp_sel - value of ALS2 resistor select register
|
||||
* @als_avrg_time - ALS averaging time
|
||||
* @als_input_mode - ALS input mode for brightness control
|
||||
* @als_vmin - Minimum ALS voltage
|
||||
* @als_vmax - Maximum ALS voltage
|
||||
* @zone_lo - values of ALS lo ZB(Zone Boundary) registers
|
||||
* @zone_hi - values of ALS hi ZB(Zone Boundary) registers
|
||||
* @config: value of ALS configuration register
|
||||
* @als1_imp_sel: value of ALS1 resistor select register
|
||||
* @als2_imp_sel: value of ALS2 resistor select register
|
||||
* @als_avrg_time: ALS averaging time
|
||||
* @als_input_mode: ALS input mode for brightness control
|
||||
* @als_vmin: Minimum ALS voltage
|
||||
* @als_vmax: Maximum ALS voltage
|
||||
* @zone_lo: values of ALS lo ZB(Zone Boundary) registers
|
||||
* @zone_hi: values of ALS hi ZB(Zone Boundary) registers
|
||||
*/
|
||||
struct lm3532_als_data {
|
||||
u8 config;
|
||||
|
@ -121,15 +121,14 @@ struct lm3532_als_data {
|
|||
/**
|
||||
* struct lm3532_led
|
||||
* @led_dev: led class device
|
||||
* @priv - Pointer the device data structure
|
||||
* @control_bank - Control bank the LED is associated to
|
||||
* @mode - Mode of the LED string
|
||||
* @ctrl_brt_pointer - Zone target register that controls the sink
|
||||
* @num_leds - Number of LED strings are supported in this array
|
||||
* @full_scale_current - The full-scale current setting for the current sink.
|
||||
* @led_strings - The LED strings supported in this array
|
||||
* @enabled - Enabled status
|
||||
* @label - LED label
|
||||
* @priv: Pointer the device data structure
|
||||
* @control_bank: Control bank the LED is associated to
|
||||
* @mode: Mode of the LED string
|
||||
* @ctrl_brt_pointer: Zone target register that controls the sink
|
||||
* @num_leds: Number of LED strings are supported in this array
|
||||
* @full_scale_current: The full-scale current setting for the current sink.
|
||||
* @led_strings: The LED strings supported in this array
|
||||
* @enabled: Enabled status
|
||||
*/
|
||||
struct lm3532_led {
|
||||
struct led_classdev led_dev;
|
||||
|
@ -142,21 +141,20 @@ struct lm3532_led {
|
|||
int full_scale_current;
|
||||
unsigned int enabled:1;
|
||||
u32 led_strings[LM3532_MAX_CONTROL_BANKS];
|
||||
char label[LED_MAX_NAME_SIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct lm3532_data
|
||||
* @enable_gpio - Hardware enable gpio
|
||||
* @enable_gpio: Hardware enable gpio
|
||||
* @regulator: regulator
|
||||
* @client: i2c client
|
||||
* @regmap - Devices register map
|
||||
* @dev - Pointer to the devices device struct
|
||||
* @lock - Lock for reading/writing the device
|
||||
* @als_data - Pointer to the als data struct
|
||||
* @runtime_ramp_up - Runtime ramp up setting
|
||||
* @runtime_ramp_down - Runtime ramp down setting
|
||||
* @leds - Array of LED strings
|
||||
* @regmap: Devices register map
|
||||
* @dev: Pointer to the devices device struct
|
||||
* @lock: Lock for reading/writing the device
|
||||
* @als_data: Pointer to the als data struct
|
||||
* @runtime_ramp_up: Runtime ramp up setting
|
||||
* @runtime_ramp_down: Runtime ramp down setting
|
||||
* @leds: Array of LED strings
|
||||
*/
|
||||
struct lm3532_data {
|
||||
struct gpio_desc *enable_gpio;
|
||||
|
@ -548,7 +546,6 @@ static int lm3532_parse_node(struct lm3532_data *priv)
|
|||
{
|
||||
struct fwnode_handle *child = NULL;
|
||||
struct lm3532_led *led;
|
||||
const char *name;
|
||||
int control_bank;
|
||||
u32 ramp_time;
|
||||
size_t i = 0;
|
||||
|
@ -643,19 +640,7 @@ static int lm3532_parse_node(struct lm3532_data *priv)
|
|||
goto child_out;
|
||||
}
|
||||
|
||||
fwnode_property_read_string(child, "linux,default-trigger",
|
||||
&led->led_dev.default_trigger);
|
||||
|
||||
ret = fwnode_property_read_string(child, "label", &name);
|
||||
if (ret)
|
||||
snprintf(led->label, sizeof(led->label),
|
||||
"%s::", priv->client->name);
|
||||
else
|
||||
snprintf(led->label, sizeof(led->label),
|
||||
"%s:%s", priv->client->name, name);
|
||||
|
||||
led->priv = priv;
|
||||
led->led_dev.name = led->label;
|
||||
led->led_dev.brightness_set_blocking = lm3532_brightness_set;
|
||||
|
||||
ret = devm_led_classdev_register_ext(priv->dev, &led->led_dev, &idata);
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
* @lmu_data: Register and setting values for common code
|
||||
* @regmap: Devices register map
|
||||
* @dev: Pointer to the devices device struct
|
||||
* @led_sources - The LED strings supported in this array
|
||||
* @num_leds - Number of LED strings are supported in this array
|
||||
* @led_sources: The LED strings supported in this array
|
||||
* @num_leds: Number of LED strings are supported in this array
|
||||
*/
|
||||
struct lm36274 {
|
||||
struct platform_device *pdev;
|
||||
|
@ -41,122 +41,113 @@ struct lm36274 {
|
|||
};
|
||||
|
||||
static int lm36274_brightness_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brt_val)
|
||||
enum led_brightness brt_val)
|
||||
{
|
||||
struct lm36274 *led = container_of(led_cdev, struct lm36274, led_dev);
|
||||
struct lm36274 *chip = container_of(led_cdev, struct lm36274, led_dev);
|
||||
|
||||
return ti_lmu_common_set_brightness(&led->lmu_data, brt_val);
|
||||
return ti_lmu_common_set_brightness(&chip->lmu_data, brt_val);
|
||||
}
|
||||
|
||||
static int lm36274_init(struct lm36274 *lm36274_data)
|
||||
static int lm36274_init(struct lm36274 *chip)
|
||||
{
|
||||
int enable_val = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < lm36274_data->num_leds; i++)
|
||||
enable_val |= (1 << lm36274_data->led_sources[i]);
|
||||
for (i = 0; i < chip->num_leds; i++)
|
||||
enable_val |= (1 << chip->led_sources[i]);
|
||||
|
||||
if (!enable_val) {
|
||||
dev_err(lm36274_data->dev, "No LEDs were enabled\n");
|
||||
dev_err(chip->dev, "No LEDs were enabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
enable_val |= LM36274_BL_EN;
|
||||
|
||||
return regmap_write(lm36274_data->regmap, LM36274_REG_BL_EN,
|
||||
enable_val);
|
||||
return regmap_write(chip->regmap, LM36274_REG_BL_EN, enable_val);
|
||||
}
|
||||
|
||||
static int lm36274_parse_dt(struct lm36274 *lm36274_data)
|
||||
static int lm36274_parse_dt(struct lm36274 *chip,
|
||||
struct led_init_data *init_data)
|
||||
{
|
||||
struct fwnode_handle *child = NULL;
|
||||
char label[LED_MAX_NAME_SIZE];
|
||||
struct device *dev = &lm36274_data->pdev->dev;
|
||||
const char *name;
|
||||
int child_cnt;
|
||||
int ret = -EINVAL;
|
||||
struct device *dev = chip->dev;
|
||||
struct fwnode_handle *child;
|
||||
int ret;
|
||||
|
||||
/* There should only be 1 node */
|
||||
child_cnt = device_get_child_node_count(dev);
|
||||
if (child_cnt != 1)
|
||||
if (device_get_child_node_count(dev) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
ret = fwnode_property_read_string(child, "label", &name);
|
||||
if (ret)
|
||||
snprintf(label, sizeof(label),
|
||||
"%s::", lm36274_data->pdev->name);
|
||||
else
|
||||
snprintf(label, sizeof(label),
|
||||
"%s:%s", lm36274_data->pdev->name, name);
|
||||
child = device_get_next_child_node(dev, NULL);
|
||||
|
||||
lm36274_data->num_leds = fwnode_property_count_u32(child, "led-sources");
|
||||
if (lm36274_data->num_leds <= 0)
|
||||
return -ENODEV;
|
||||
|
||||
ret = fwnode_property_read_u32_array(child, "led-sources",
|
||||
lm36274_data->led_sources,
|
||||
lm36274_data->num_leds);
|
||||
if (ret) {
|
||||
dev_err(dev, "led-sources property missing\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
fwnode_property_read_string(child, "linux,default-trigger",
|
||||
&lm36274_data->led_dev.default_trigger);
|
||||
init_data->fwnode = child;
|
||||
init_data->devicename = chip->pdev->name;
|
||||
/* for backwards compatibility when `label` property is not present */
|
||||
init_data->default_label = ":";
|
||||
|
||||
chip->num_leds = fwnode_property_count_u32(child, "led-sources");
|
||||
if (chip->num_leds <= 0) {
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
lm36274_data->lmu_data.regmap = lm36274_data->regmap;
|
||||
lm36274_data->lmu_data.max_brightness = MAX_BRIGHTNESS_11BIT;
|
||||
lm36274_data->lmu_data.msb_brightness_reg = LM36274_REG_BRT_MSB;
|
||||
lm36274_data->lmu_data.lsb_brightness_reg = LM36274_REG_BRT_LSB;
|
||||
|
||||
lm36274_data->led_dev.name = label;
|
||||
lm36274_data->led_dev.max_brightness = MAX_BRIGHTNESS_11BIT;
|
||||
lm36274_data->led_dev.brightness_set_blocking = lm36274_brightness_set;
|
||||
ret = fwnode_property_read_u32_array(child, "led-sources",
|
||||
chip->led_sources, chip->num_leds);
|
||||
if (ret) {
|
||||
dev_err(dev, "led-sources property missing\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
fwnode_handle_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lm36274_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ti_lmu *lmu = dev_get_drvdata(pdev->dev.parent);
|
||||
struct lm36274 *lm36274_data;
|
||||
struct led_init_data init_data = {};
|
||||
struct lm36274 *chip;
|
||||
int ret;
|
||||
|
||||
lm36274_data = devm_kzalloc(&pdev->dev, sizeof(*lm36274_data),
|
||||
GFP_KERNEL);
|
||||
if (!lm36274_data)
|
||||
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
lm36274_data->pdev = pdev;
|
||||
lm36274_data->dev = lmu->dev;
|
||||
lm36274_data->regmap = lmu->regmap;
|
||||
platform_set_drvdata(pdev, lm36274_data);
|
||||
chip->pdev = pdev;
|
||||
chip->dev = &pdev->dev;
|
||||
chip->regmap = lmu->regmap;
|
||||
platform_set_drvdata(pdev, chip);
|
||||
|
||||
ret = lm36274_parse_dt(lm36274_data);
|
||||
ret = lm36274_parse_dt(chip, &init_data);
|
||||
if (ret) {
|
||||
dev_err(lm36274_data->dev, "Failed to parse DT node\n");
|
||||
dev_err(chip->dev, "Failed to parse DT node\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = lm36274_init(lm36274_data);
|
||||
ret = lm36274_init(chip);
|
||||
if (ret) {
|
||||
dev_err(lm36274_data->dev, "Failed to init the device\n");
|
||||
dev_err(chip->dev, "Failed to init the device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return led_classdev_register(lm36274_data->dev, &lm36274_data->led_dev);
|
||||
}
|
||||
chip->lmu_data.regmap = chip->regmap;
|
||||
chip->lmu_data.max_brightness = MAX_BRIGHTNESS_11BIT;
|
||||
chip->lmu_data.msb_brightness_reg = LM36274_REG_BRT_MSB;
|
||||
chip->lmu_data.lsb_brightness_reg = LM36274_REG_BRT_LSB;
|
||||
|
||||
static int lm36274_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct lm36274 *lm36274_data = platform_get_drvdata(pdev);
|
||||
chip->led_dev.max_brightness = MAX_BRIGHTNESS_11BIT;
|
||||
chip->led_dev.brightness_set_blocking = lm36274_brightness_set;
|
||||
|
||||
led_classdev_unregister(&lm36274_data->led_dev);
|
||||
ret = devm_led_classdev_register_ext(chip->dev, &chip->led_dev,
|
||||
&init_data);
|
||||
if (ret)
|
||||
dev_err(chip->dev, "Failed to register LED for node %pfw\n",
|
||||
init_data.fwnode);
|
||||
|
||||
return 0;
|
||||
fwnode_handle_put(init_data.fwnode);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_lm36274_leds_match[] = {
|
||||
|
@ -167,9 +158,9 @@ MODULE_DEVICE_TABLE(of, of_lm36274_leds_match);
|
|||
|
||||
static struct platform_driver lm36274_driver = {
|
||||
.probe = lm36274_probe,
|
||||
.remove = lm36274_remove,
|
||||
.driver = {
|
||||
.name = "lm36274-leds",
|
||||
.of_match_table = of_lm36274_leds_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(lm36274_driver)
|
||||
|
|
|
@ -394,13 +394,10 @@ static int lm3692x_probe_dt(struct lm3692x_led *led)
|
|||
led->regulator = devm_regulator_get_optional(&led->client->dev, "vled");
|
||||
if (IS_ERR(led->regulator)) {
|
||||
ret = PTR_ERR(led->regulator);
|
||||
if (ret != -ENODEV) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&led->client->dev,
|
||||
"Failed to get vled regulator: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
if (ret != -ENODEV)
|
||||
return dev_err_probe(&led->client->dev, ret,
|
||||
"Failed to get vled regulator\n");
|
||||
|
||||
led->regulator = NULL;
|
||||
}
|
||||
|
||||
|
@ -436,9 +433,6 @@ static int lm3692x_probe_dt(struct lm3692x_led *led)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
fwnode_property_read_string(child, "linux,default-trigger",
|
||||
&led->led_dev.default_trigger);
|
||||
|
||||
ret = fwnode_property_read_u32(child, "reg", &led->led_enable);
|
||||
if (ret) {
|
||||
dev_err(&led->client->dev, "reg DT property missing\n");
|
||||
|
|
|
@ -78,6 +78,7 @@ struct lm3697 {
|
|||
struct mutex lock;
|
||||
|
||||
int bank_cfg;
|
||||
int num_banks;
|
||||
|
||||
struct lm3697_led leds[];
|
||||
};
|
||||
|
@ -115,6 +116,7 @@ static int lm3697_brightness_set(struct led_classdev *led_cdev,
|
|||
struct lm3697_led *led = container_of(led_cdev, struct lm3697_led,
|
||||
led_dev);
|
||||
int ctrl_en_val = (1 << led->control_bank);
|
||||
struct device *dev = led->priv->dev;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&led->priv->lock);
|
||||
|
@ -123,7 +125,7 @@ static int lm3697_brightness_set(struct led_classdev *led_cdev,
|
|||
ret = regmap_update_bits(led->priv->regmap, LM3697_CTRL_ENABLE,
|
||||
ctrl_en_val, ~ctrl_en_val);
|
||||
if (ret) {
|
||||
dev_err(&led->priv->client->dev, "Cannot write ctrl register\n");
|
||||
dev_err(dev, "Cannot write ctrl register\n");
|
||||
goto brightness_out;
|
||||
}
|
||||
|
||||
|
@ -131,8 +133,7 @@ static int lm3697_brightness_set(struct led_classdev *led_cdev,
|
|||
} else {
|
||||
ret = ti_lmu_common_set_brightness(&led->lmu_data, brt_val);
|
||||
if (ret) {
|
||||
dev_err(&led->priv->client->dev,
|
||||
"Cannot write brightness\n");
|
||||
dev_err(dev, "Cannot write brightness\n");
|
||||
goto brightness_out;
|
||||
}
|
||||
|
||||
|
@ -141,8 +142,7 @@ static int lm3697_brightness_set(struct led_classdev *led_cdev,
|
|||
LM3697_CTRL_ENABLE,
|
||||
ctrl_en_val, ctrl_en_val);
|
||||
if (ret) {
|
||||
dev_err(&led->priv->client->dev,
|
||||
"Cannot enable the device\n");
|
||||
dev_err(dev, "Cannot enable the device\n");
|
||||
goto brightness_out;
|
||||
}
|
||||
|
||||
|
@ -157,6 +157,7 @@ brightness_out:
|
|||
|
||||
static int lm3697_init(struct lm3697 *priv)
|
||||
{
|
||||
struct device *dev = priv->dev;
|
||||
struct lm3697_led *led;
|
||||
int i, ret;
|
||||
|
||||
|
@ -165,26 +166,26 @@ static int lm3697_init(struct lm3697 *priv)
|
|||
} else {
|
||||
ret = regmap_write(priv->regmap, LM3697_RESET, LM3697_SW_RESET);
|
||||
if (ret) {
|
||||
dev_err(&priv->client->dev, "Cannot reset the device\n");
|
||||
dev_err(dev, "Cannot reset the device\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_write(priv->regmap, LM3697_CTRL_ENABLE, 0x0);
|
||||
if (ret) {
|
||||
dev_err(&priv->client->dev, "Cannot write ctrl enable\n");
|
||||
dev_err(dev, "Cannot write ctrl enable\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = regmap_write(priv->regmap, LM3697_OUTPUT_CONFIG, priv->bank_cfg);
|
||||
if (ret)
|
||||
dev_err(&priv->client->dev, "Cannot write OUTPUT config\n");
|
||||
dev_err(dev, "Cannot write OUTPUT config\n");
|
||||
|
||||
for (i = 0; i < LM3697_MAX_CONTROL_BANKS; i++) {
|
||||
for (i = 0; i < priv->num_banks; i++) {
|
||||
led = &priv->leds[i];
|
||||
ret = ti_lmu_common_set_ramp(&led->lmu_data);
|
||||
if (ret)
|
||||
dev_err(&priv->client->dev, "Setting the ramp rate failed\n");
|
||||
dev_err(dev, "Setting the ramp rate failed\n");
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
|
@ -193,36 +194,37 @@ out:
|
|||
static int lm3697_probe_dt(struct lm3697 *priv)
|
||||
{
|
||||
struct fwnode_handle *child = NULL;
|
||||
struct device *dev = priv->dev;
|
||||
struct lm3697_led *led;
|
||||
const char *name;
|
||||
int ret = -EINVAL;
|
||||
int control_bank;
|
||||
size_t i = 0;
|
||||
int ret = -EINVAL;
|
||||
int j;
|
||||
|
||||
priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
|
||||
"enable", GPIOD_OUT_LOW);
|
||||
priv->enable_gpio = devm_gpiod_get_optional(dev, "enable",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(priv->enable_gpio)) {
|
||||
ret = PTR_ERR(priv->enable_gpio);
|
||||
dev_err(&priv->client->dev, "Failed to get enable gpio: %d\n",
|
||||
ret);
|
||||
dev_err(dev, "Failed to get enable gpio: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->regulator = devm_regulator_get(&priv->client->dev, "vled");
|
||||
priv->regulator = devm_regulator_get(dev, "vled");
|
||||
if (IS_ERR(priv->regulator))
|
||||
priv->regulator = NULL;
|
||||
|
||||
device_for_each_child_node(priv->dev, child) {
|
||||
device_for_each_child_node(dev, child) {
|
||||
struct led_init_data init_data = {};
|
||||
|
||||
ret = fwnode_property_read_u32(child, "reg", &control_bank);
|
||||
if (ret) {
|
||||
dev_err(&priv->client->dev, "reg property missing\n");
|
||||
dev_err(dev, "reg property missing\n");
|
||||
fwnode_handle_put(child);
|
||||
goto child_out;
|
||||
}
|
||||
|
||||
if (control_bank > LM3697_CONTROL_B) {
|
||||
dev_err(&priv->client->dev, "reg property is invalid\n");
|
||||
dev_err(dev, "reg property is invalid\n");
|
||||
ret = -EINVAL;
|
||||
fwnode_handle_put(child);
|
||||
goto child_out;
|
||||
|
@ -230,10 +232,10 @@ static int lm3697_probe_dt(struct lm3697 *priv)
|
|||
|
||||
led = &priv->leds[i];
|
||||
|
||||
ret = ti_lmu_common_get_brt_res(&priv->client->dev,
|
||||
child, &led->lmu_data);
|
||||
ret = ti_lmu_common_get_brt_res(dev, child, &led->lmu_data);
|
||||
if (ret)
|
||||
dev_warn(&priv->client->dev, "brightness resolution property missing\n");
|
||||
dev_warn(dev,
|
||||
"brightness resolution property missing\n");
|
||||
|
||||
led->control_bank = control_bank;
|
||||
led->lmu_data.regmap = priv->regmap;
|
||||
|
@ -246,7 +248,7 @@ static int lm3697_probe_dt(struct lm3697 *priv)
|
|||
|
||||
led->num_leds = fwnode_property_count_u32(child, "led-sources");
|
||||
if (led->num_leds > LM3697_MAX_LED_STRINGS) {
|
||||
dev_err(&priv->client->dev, "Too many LED strings defined\n");
|
||||
dev_err(dev, "Too many LED strings defined\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -254,7 +256,7 @@ static int lm3697_probe_dt(struct lm3697 *priv)
|
|||
led->hvled_strings,
|
||||
led->num_leds);
|
||||
if (ret) {
|
||||
dev_err(&priv->client->dev, "led-sources property missing\n");
|
||||
dev_err(dev, "led-sources property missing\n");
|
||||
fwnode_handle_put(child);
|
||||
goto child_out;
|
||||
}
|
||||
|
@ -263,31 +265,23 @@ static int lm3697_probe_dt(struct lm3697 *priv)
|
|||
priv->bank_cfg |=
|
||||
(led->control_bank << led->hvled_strings[j]);
|
||||
|
||||
ret = ti_lmu_common_get_ramp_params(&priv->client->dev,
|
||||
child, &led->lmu_data);
|
||||
ret = ti_lmu_common_get_ramp_params(dev, child, &led->lmu_data);
|
||||
if (ret)
|
||||
dev_warn(&priv->client->dev, "runtime-ramp properties missing\n");
|
||||
dev_warn(dev, "runtime-ramp properties missing\n");
|
||||
|
||||
fwnode_property_read_string(child, "linux,default-trigger",
|
||||
&led->led_dev.default_trigger);
|
||||
|
||||
ret = fwnode_property_read_string(child, "label", &name);
|
||||
if (ret)
|
||||
snprintf(led->label, sizeof(led->label),
|
||||
"%s::", priv->client->name);
|
||||
else
|
||||
snprintf(led->label, sizeof(led->label),
|
||||
"%s:%s", priv->client->name, name);
|
||||
init_data.fwnode = child;
|
||||
init_data.devicename = priv->client->name;
|
||||
/* for backwards compatibility if `label` is not present */
|
||||
init_data.default_label = ":";
|
||||
|
||||
led->priv = priv;
|
||||
led->led_dev.name = led->label;
|
||||
led->led_dev.max_brightness = led->lmu_data.max_brightness;
|
||||
led->led_dev.brightness_set_blocking = lm3697_brightness_set;
|
||||
|
||||
ret = devm_led_classdev_register(priv->dev, &led->led_dev);
|
||||
ret = devm_led_classdev_register_ext(dev, &led->led_dev,
|
||||
&init_data);
|
||||
if (ret) {
|
||||
dev_err(&priv->client->dev, "led register err: %d\n",
|
||||
ret);
|
||||
dev_err(dev, "led register err: %d\n", ret);
|
||||
fwnode_handle_put(child);
|
||||
goto child_out;
|
||||
}
|
||||
|
@ -302,18 +296,18 @@ child_out:
|
|||
static int lm3697_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct lm3697 *led;
|
||||
int count;
|
||||
int ret;
|
||||
|
||||
count = device_get_child_node_count(&client->dev);
|
||||
if (!count) {
|
||||
dev_err(&client->dev, "LEDs are not defined in device tree!");
|
||||
count = device_get_child_node_count(dev);
|
||||
if (!count || count > LM3697_MAX_CONTROL_BANKS) {
|
||||
dev_err(dev, "Strange device tree!");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
led = devm_kzalloc(&client->dev, struct_size(led, leds, count),
|
||||
GFP_KERNEL);
|
||||
led = devm_kzalloc(dev, struct_size(led, leds, count), GFP_KERNEL);
|
||||
if (!led)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -321,12 +315,12 @@ static int lm3697_probe(struct i2c_client *client,
|
|||
i2c_set_clientdata(client, led);
|
||||
|
||||
led->client = client;
|
||||
led->dev = &client->dev;
|
||||
led->dev = dev;
|
||||
led->num_banks = count;
|
||||
led->regmap = devm_regmap_init_i2c(client, &lm3697_regmap_config);
|
||||
if (IS_ERR(led->regmap)) {
|
||||
ret = PTR_ERR(led->regmap);
|
||||
dev_err(&client->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
dev_err(dev, "Failed to allocate register map: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -340,12 +334,13 @@ static int lm3697_probe(struct i2c_client *client,
|
|||
static int lm3697_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lm3697 *led = i2c_get_clientdata(client);
|
||||
struct device *dev = &led->client->dev;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(led->regmap, LM3697_CTRL_ENABLE,
|
||||
LM3697_CTRL_A_B_EN, 0);
|
||||
if (ret) {
|
||||
dev_err(&led->client->dev, "Failed to disable the device\n");
|
||||
dev_err(dev, "Failed to disable the device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -355,8 +350,7 @@ static int lm3697_remove(struct i2c_client *client)
|
|||
if (led->regulator) {
|
||||
ret = regulator_disable(led->regulator);
|
||||
if (ret)
|
||||
dev_err(&led->client->dev,
|
||||
"Failed to disable regulator\n");
|
||||
dev_err(dev, "Failed to disable regulator\n");
|
||||
}
|
||||
|
||||
mutex_destroy(&led->lock);
|
||||
|
|
631
drivers/leds/leds-lp50xx.c
Normal file
631
drivers/leds/leds-lp50xx.c
Normal file
|
@ -0,0 +1,631 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// TI LP50XX LED chip family driver
|
||||
// Copyright (C) 2018-20 Texas Instruments Incorporated - https://www.ti.com/
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <uapi/linux/uleds.h>
|
||||
|
||||
#include <linux/led-class-multicolor.h>
|
||||
|
||||
#include "leds.h"
|
||||
|
||||
#define LP50XX_DEV_CFG0 0x00
|
||||
#define LP50XX_DEV_CFG1 0x01
|
||||
#define LP50XX_LED_CFG0 0x02
|
||||
|
||||
/* LP5009 and LP5012 registers */
|
||||
#define LP5012_BNK_BRT 0x03
|
||||
#define LP5012_BNKA_CLR 0x04
|
||||
#define LP5012_BNKB_CLR 0x05
|
||||
#define LP5012_BNKC_CLR 0x06
|
||||
#define LP5012_LED0_BRT 0x07
|
||||
#define LP5012_OUT0_CLR 0x0b
|
||||
#define LP5012_RESET 0x17
|
||||
|
||||
/* LP5018 and LP5024 registers */
|
||||
#define LP5024_BNK_BRT 0x03
|
||||
#define LP5024_BNKA_CLR 0x04
|
||||
#define LP5024_BNKB_CLR 0x05
|
||||
#define LP5024_BNKC_CLR 0x06
|
||||
#define LP5024_LED0_BRT 0x07
|
||||
#define LP5024_OUT0_CLR 0x0f
|
||||
#define LP5024_RESET 0x27
|
||||
|
||||
/* LP5030 and LP5036 registers */
|
||||
#define LP5036_LED_CFG1 0x03
|
||||
#define LP5036_BNK_BRT 0x04
|
||||
#define LP5036_BNKA_CLR 0x05
|
||||
#define LP5036_BNKB_CLR 0x06
|
||||
#define LP5036_BNKC_CLR 0x07
|
||||
#define LP5036_LED0_BRT 0x08
|
||||
#define LP5036_OUT0_CLR 0x14
|
||||
#define LP5036_RESET 0x38
|
||||
|
||||
#define LP50XX_SW_RESET 0xff
|
||||
#define LP50XX_CHIP_EN BIT(6)
|
||||
|
||||
/* There are 3 LED outputs per bank */
|
||||
#define LP50XX_LEDS_PER_MODULE 3
|
||||
|
||||
#define LP5009_MAX_LED_MODULES 2
|
||||
#define LP5012_MAX_LED_MODULES 4
|
||||
#define LP5018_MAX_LED_MODULES 6
|
||||
#define LP5024_MAX_LED_MODULES 8
|
||||
#define LP5030_MAX_LED_MODULES 10
|
||||
#define LP5036_MAX_LED_MODULES 12
|
||||
|
||||
static const struct reg_default lp5012_reg_defs[] = {
|
||||
{LP50XX_DEV_CFG0, 0x0},
|
||||
{LP50XX_DEV_CFG1, 0x3c},
|
||||
{LP50XX_LED_CFG0, 0x0},
|
||||
{LP5012_BNK_BRT, 0xff},
|
||||
{LP5012_BNKA_CLR, 0x0f},
|
||||
{LP5012_BNKB_CLR, 0x0f},
|
||||
{LP5012_BNKC_CLR, 0x0f},
|
||||
{LP5012_LED0_BRT, 0x0f},
|
||||
/* LEDX_BRT registers are all 0xff for defaults */
|
||||
{0x08, 0xff}, {0x09, 0xff}, {0x0a, 0xff},
|
||||
{LP5012_OUT0_CLR, 0x0f},
|
||||
/* OUTX_CLR registers are all 0x0 for defaults */
|
||||
{0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00}, {0x10, 0x00},
|
||||
{0x11, 0x00}, {0x12, 0x00}, {0x13, 0x00}, {0x14, 0x00}, {0x15, 0x00},
|
||||
{0x16, 0x00},
|
||||
{LP5012_RESET, 0x00}
|
||||
};
|
||||
|
||||
static const struct reg_default lp5024_reg_defs[] = {
|
||||
{LP50XX_DEV_CFG0, 0x0},
|
||||
{LP50XX_DEV_CFG1, 0x3c},
|
||||
{LP50XX_LED_CFG0, 0x0},
|
||||
{LP5024_BNK_BRT, 0xff},
|
||||
{LP5024_BNKA_CLR, 0x0f},
|
||||
{LP5024_BNKB_CLR, 0x0f},
|
||||
{LP5024_BNKC_CLR, 0x0f},
|
||||
{LP5024_LED0_BRT, 0x0f},
|
||||
/* LEDX_BRT registers are all 0xff for defaults */
|
||||
{0x08, 0xff}, {0x09, 0xff}, {0x0a, 0xff}, {0x0b, 0xff}, {0x0c, 0xff},
|
||||
{0x0d, 0xff}, {0x0e, 0xff},
|
||||
{LP5024_OUT0_CLR, 0x0f},
|
||||
/* OUTX_CLR registers are all 0x0 for defaults */
|
||||
{0x10, 0x00}, {0x11, 0x00}, {0x12, 0x00}, {0x13, 0x00}, {0x14, 0x00},
|
||||
{0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00}, {0x18, 0x00}, {0x19, 0x00},
|
||||
{0x1a, 0x00}, {0x1b, 0x00}, {0x1c, 0x00}, {0x1d, 0x00}, {0x1e, 0x00},
|
||||
{0x1f, 0x00}, {0x20, 0x00}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00},
|
||||
{0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00},
|
||||
{LP5024_RESET, 0x00}
|
||||
};
|
||||
|
||||
static const struct reg_default lp5036_reg_defs[] = {
|
||||
{LP50XX_DEV_CFG0, 0x0},
|
||||
{LP50XX_DEV_CFG1, 0x3c},
|
||||
{LP50XX_LED_CFG0, 0x0},
|
||||
{LP5036_LED_CFG1, 0x0},
|
||||
{LP5036_BNK_BRT, 0xff},
|
||||
{LP5036_BNKA_CLR, 0x0f},
|
||||
{LP5036_BNKB_CLR, 0x0f},
|
||||
{LP5036_BNKC_CLR, 0x0f},
|
||||
{LP5036_LED0_BRT, 0x0f},
|
||||
/* LEDX_BRT registers are all 0xff for defaults */
|
||||
{0x08, 0xff}, {0x09, 0xff}, {0x0a, 0xff}, {0x0b, 0xff}, {0x0c, 0xff},
|
||||
{0x0d, 0xff}, {0x0e, 0xff}, {0x0f, 0xff}, {0x10, 0xff}, {0x11, 0xff},
|
||||
{0x12, 0xff}, {0x13, 0xff},
|
||||
{LP5036_OUT0_CLR, 0x0f},
|
||||
/* OUTX_CLR registers are all 0x0 for defaults */
|
||||
{0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00}, {0x18, 0x00}, {0x19, 0x00},
|
||||
{0x1a, 0x00}, {0x1b, 0x00}, {0x1c, 0x00}, {0x1d, 0x00}, {0x1e, 0x00},
|
||||
{0x1f, 0x00}, {0x20, 0x00}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00},
|
||||
{0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00}, {0x28, 0x00},
|
||||
{0x29, 0x00}, {0x2a, 0x00}, {0x2b, 0x00}, {0x2c, 0x00}, {0x2d, 0x00},
|
||||
{0x2e, 0x00}, {0x2f, 0x00}, {0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00},
|
||||
{0x33, 0x00}, {0x34, 0x00}, {0x35, 0x00}, {0x36, 0x00}, {0x37, 0x00},
|
||||
{LP5036_RESET, 0x00}
|
||||
};
|
||||
|
||||
static const struct regmap_config lp5012_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = LP5012_RESET,
|
||||
.reg_defaults = lp5012_reg_defs,
|
||||
.num_reg_defaults = ARRAY_SIZE(lp5012_reg_defs),
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static const struct regmap_config lp5024_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = LP5024_RESET,
|
||||
.reg_defaults = lp5024_reg_defs,
|
||||
.num_reg_defaults = ARRAY_SIZE(lp5024_reg_defs),
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static const struct regmap_config lp5036_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = LP5036_RESET,
|
||||
.reg_defaults = lp5036_reg_defs,
|
||||
.num_reg_defaults = ARRAY_SIZE(lp5036_reg_defs),
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
enum lp50xx_model {
|
||||
LP5009,
|
||||
LP5012,
|
||||
LP5018,
|
||||
LP5024,
|
||||
LP5030,
|
||||
LP5036,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct lp50xx_chip_info -
|
||||
* @lp50xx_regmap_config: regmap register configuration
|
||||
* @model_id: LED device model
|
||||
* @max_modules: total number of supported LED modules
|
||||
* @num_leds: number of LED outputs available on the device
|
||||
* @led_brightness0_reg: first brightness register of the device
|
||||
* @mix_out0_reg: first color mix register of the device
|
||||
* @bank_brt_reg: bank brightness register
|
||||
* @bank_mix_reg: color mix register
|
||||
* @reset_reg: device reset register
|
||||
*/
|
||||
struct lp50xx_chip_info {
|
||||
const struct regmap_config *lp50xx_regmap_config;
|
||||
int model_id;
|
||||
u8 max_modules;
|
||||
u8 num_leds;
|
||||
u8 led_brightness0_reg;
|
||||
u8 mix_out0_reg;
|
||||
u8 bank_brt_reg;
|
||||
u8 bank_mix_reg;
|
||||
u8 reset_reg;
|
||||
};
|
||||
|
||||
static const struct lp50xx_chip_info lp50xx_chip_info_tbl[] = {
|
||||
[LP5009] = {
|
||||
.model_id = LP5009,
|
||||
.max_modules = LP5009_MAX_LED_MODULES,
|
||||
.num_leds = LP5009_MAX_LED_MODULES * LP50XX_LEDS_PER_MODULE,
|
||||
.led_brightness0_reg = LP5012_LED0_BRT,
|
||||
.mix_out0_reg = LP5012_OUT0_CLR,
|
||||
.bank_brt_reg = LP5012_BNK_BRT,
|
||||
.bank_mix_reg = LP5012_BNKA_CLR,
|
||||
.reset_reg = LP5012_RESET,
|
||||
.lp50xx_regmap_config = &lp5012_regmap_config,
|
||||
},
|
||||
[LP5012] = {
|
||||
.model_id = LP5012,
|
||||
.max_modules = LP5012_MAX_LED_MODULES,
|
||||
.num_leds = LP5012_MAX_LED_MODULES * LP50XX_LEDS_PER_MODULE,
|
||||
.led_brightness0_reg = LP5012_LED0_BRT,
|
||||
.mix_out0_reg = LP5012_OUT0_CLR,
|
||||
.bank_brt_reg = LP5012_BNK_BRT,
|
||||
.bank_mix_reg = LP5012_BNKA_CLR,
|
||||
.reset_reg = LP5012_RESET,
|
||||
.lp50xx_regmap_config = &lp5012_regmap_config,
|
||||
},
|
||||
[LP5018] = {
|
||||
.model_id = LP5018,
|
||||
.max_modules = LP5018_MAX_LED_MODULES,
|
||||
.num_leds = LP5018_MAX_LED_MODULES * LP50XX_LEDS_PER_MODULE,
|
||||
.led_brightness0_reg = LP5024_LED0_BRT,
|
||||
.mix_out0_reg = LP5024_OUT0_CLR,
|
||||
.bank_brt_reg = LP5024_BNK_BRT,
|
||||
.bank_mix_reg = LP5024_BNKA_CLR,
|
||||
.reset_reg = LP5024_RESET,
|
||||
.lp50xx_regmap_config = &lp5024_regmap_config,
|
||||
},
|
||||
[LP5024] = {
|
||||
.model_id = LP5024,
|
||||
.max_modules = LP5024_MAX_LED_MODULES,
|
||||
.num_leds = LP5024_MAX_LED_MODULES * LP50XX_LEDS_PER_MODULE,
|
||||
.led_brightness0_reg = LP5024_LED0_BRT,
|
||||
.mix_out0_reg = LP5024_OUT0_CLR,
|
||||
.bank_brt_reg = LP5024_BNK_BRT,
|
||||
.bank_mix_reg = LP5024_BNKA_CLR,
|
||||
.reset_reg = LP5024_RESET,
|
||||
.lp50xx_regmap_config = &lp5024_regmap_config,
|
||||
},
|
||||
[LP5030] = {
|
||||
.model_id = LP5030,
|
||||
.max_modules = LP5030_MAX_LED_MODULES,
|
||||
.num_leds = LP5030_MAX_LED_MODULES * LP50XX_LEDS_PER_MODULE,
|
||||
.led_brightness0_reg = LP5036_LED0_BRT,
|
||||
.mix_out0_reg = LP5036_OUT0_CLR,
|
||||
.bank_brt_reg = LP5036_BNK_BRT,
|
||||
.bank_mix_reg = LP5036_BNKA_CLR,
|
||||
.reset_reg = LP5036_RESET,
|
||||
.lp50xx_regmap_config = &lp5036_regmap_config,
|
||||
},
|
||||
[LP5036] = {
|
||||
.model_id = LP5036,
|
||||
.max_modules = LP5036_MAX_LED_MODULES,
|
||||
.num_leds = LP5036_MAX_LED_MODULES * LP50XX_LEDS_PER_MODULE,
|
||||
.led_brightness0_reg = LP5036_LED0_BRT,
|
||||
.mix_out0_reg = LP5036_OUT0_CLR,
|
||||
.bank_brt_reg = LP5036_BNK_BRT,
|
||||
.bank_mix_reg = LP5036_BNKA_CLR,
|
||||
.reset_reg = LP5036_RESET,
|
||||
.lp50xx_regmap_config = &lp5036_regmap_config,
|
||||
},
|
||||
};
|
||||
|
||||
struct lp50xx_led {
|
||||
struct led_classdev_mc mc_cdev;
|
||||
struct lp50xx *priv;
|
||||
unsigned long bank_modules;
|
||||
int led_intensity[LP50XX_LEDS_PER_MODULE];
|
||||
u8 ctrl_bank_enabled;
|
||||
int led_number;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct lp50xx -
|
||||
* @enable_gpio: hardware enable gpio
|
||||
* @regulator: LED supply regulator pointer
|
||||
* @client: pointer to the I2C client
|
||||
* @regmap: device register map
|
||||
* @dev: pointer to the devices device struct
|
||||
* @lock: lock for reading/writing the device
|
||||
* @chip_info: chip specific information (ie num_leds)
|
||||
* @num_of_banked_leds: holds the number of banked LEDs
|
||||
* @leds: array of LED strings
|
||||
*/
|
||||
struct lp50xx {
|
||||
struct gpio_desc *enable_gpio;
|
||||
struct regulator *regulator;
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
struct mutex lock;
|
||||
const struct lp50xx_chip_info *chip_info;
|
||||
int num_of_banked_leds;
|
||||
|
||||
/* This needs to be at the end of the struct */
|
||||
struct lp50xx_led leds[];
|
||||
};
|
||||
|
||||
static struct lp50xx_led *mcled_cdev_to_led(struct led_classdev_mc *mc_cdev)
|
||||
{
|
||||
return container_of(mc_cdev, struct lp50xx_led, mc_cdev);
|
||||
}
|
||||
|
||||
static int lp50xx_brightness_set(struct led_classdev *cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct led_classdev_mc *mc_dev = lcdev_to_mccdev(cdev);
|
||||
struct lp50xx_led *led = mcled_cdev_to_led(mc_dev);
|
||||
const struct lp50xx_chip_info *led_chip = led->priv->chip_info;
|
||||
u8 led_offset, reg_val;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
mutex_lock(&led->priv->lock);
|
||||
if (led->ctrl_bank_enabled)
|
||||
reg_val = led_chip->bank_brt_reg;
|
||||
else
|
||||
reg_val = led_chip->led_brightness0_reg +
|
||||
led->led_number;
|
||||
|
||||
ret = regmap_write(led->priv->regmap, reg_val, brightness);
|
||||
if (ret) {
|
||||
dev_err(&led->priv->client->dev,
|
||||
"Cannot write brightness value %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < led->mc_cdev.num_colors; i++) {
|
||||
if (led->ctrl_bank_enabled) {
|
||||
reg_val = led_chip->bank_mix_reg + i;
|
||||
} else {
|
||||
led_offset = (led->led_number * 3) + i;
|
||||
reg_val = led_chip->mix_out0_reg + led_offset;
|
||||
}
|
||||
|
||||
ret = regmap_write(led->priv->regmap, reg_val,
|
||||
mc_dev->subled_info[i].intensity);
|
||||
if (ret) {
|
||||
dev_err(&led->priv->client->dev,
|
||||
"Cannot write intensity value %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&led->priv->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp50xx_set_banks(struct lp50xx *priv, u32 led_banks[])
|
||||
{
|
||||
u8 led_config_lo, led_config_hi;
|
||||
u32 bank_enable_mask = 0;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->chip_info->max_modules; i++) {
|
||||
if (led_banks[i])
|
||||
bank_enable_mask |= (1 << led_banks[i]);
|
||||
}
|
||||
|
||||
led_config_lo = (u8)(bank_enable_mask & 0xff);
|
||||
led_config_hi = (u8)(bank_enable_mask >> 8) & 0xff;
|
||||
|
||||
ret = regmap_write(priv->regmap, LP50XX_LED_CFG0, led_config_lo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->chip_info->model_id >= LP5030)
|
||||
ret = regmap_write(priv->regmap, LP5036_LED_CFG1, led_config_hi);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp50xx_reset(struct lp50xx *priv)
|
||||
{
|
||||
return regmap_write(priv->regmap, priv->chip_info->reset_reg, LP50XX_SW_RESET);
|
||||
}
|
||||
|
||||
static int lp50xx_enable_disable(struct lp50xx *priv, int enable_disable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (priv->enable_gpio) {
|
||||
ret = gpiod_direction_output(priv->enable_gpio, enable_disable);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (enable_disable)
|
||||
return regmap_write(priv->regmap, LP50XX_DEV_CFG0, LP50XX_CHIP_EN);
|
||||
else
|
||||
return regmap_write(priv->regmap, LP50XX_DEV_CFG0, 0);
|
||||
|
||||
}
|
||||
|
||||
static int lp50xx_probe_leds(struct fwnode_handle *child, struct lp50xx *priv,
|
||||
struct lp50xx_led *led, int num_leds)
|
||||
{
|
||||
u32 led_banks[LP5036_MAX_LED_MODULES] = {0};
|
||||
int led_number;
|
||||
int ret;
|
||||
|
||||
if (num_leds > 1) {
|
||||
if (num_leds > priv->chip_info->max_modules) {
|
||||
dev_err(&priv->client->dev, "reg property is invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->num_of_banked_leds = num_leds;
|
||||
|
||||
ret = fwnode_property_read_u32_array(child, "reg", led_banks, num_leds);
|
||||
if (ret) {
|
||||
dev_err(&priv->client->dev, "reg property is missing\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = lp50xx_set_banks(priv, led_banks);
|
||||
if (ret) {
|
||||
dev_err(&priv->client->dev, "Cannot setup banked LEDs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
led->ctrl_bank_enabled = 1;
|
||||
} else {
|
||||
ret = fwnode_property_read_u32(child, "reg", &led_number);
|
||||
if (ret) {
|
||||
dev_err(&priv->client->dev, "led reg property missing\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (led_number > priv->chip_info->num_leds) {
|
||||
dev_err(&priv->client->dev, "led-sources property is invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
led->led_number = led_number;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lp50xx_probe_dt(struct lp50xx *priv)
|
||||
{
|
||||
struct fwnode_handle *child = NULL;
|
||||
struct fwnode_handle *led_node = NULL;
|
||||
struct led_init_data init_data = {};
|
||||
struct led_classdev *led_cdev;
|
||||
struct mc_subled *mc_led_info;
|
||||
struct lp50xx_led *led;
|
||||
int ret = -EINVAL;
|
||||
int num_colors;
|
||||
u32 color_id;
|
||||
int i = 0;
|
||||
|
||||
priv->enable_gpio = devm_gpiod_get_optional(priv->dev, "enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(priv->enable_gpio)) {
|
||||
ret = PTR_ERR(priv->enable_gpio);
|
||||
dev_err(&priv->client->dev, "Failed to get enable gpio: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->regulator = devm_regulator_get(priv->dev, "vled");
|
||||
if (IS_ERR(priv->regulator))
|
||||
priv->regulator = NULL;
|
||||
|
||||
device_for_each_child_node(priv->dev, child) {
|
||||
led = &priv->leds[i];
|
||||
ret = fwnode_property_count_u32(child, "reg");
|
||||
if (ret < 0) {
|
||||
dev_err(&priv->client->dev, "reg property is invalid\n");
|
||||
goto child_out;
|
||||
}
|
||||
|
||||
ret = lp50xx_probe_leds(child, priv, led, ret);
|
||||
if (ret)
|
||||
goto child_out;
|
||||
|
||||
init_data.fwnode = child;
|
||||
num_colors = 0;
|
||||
|
||||
/*
|
||||
* There are only 3 LEDs per module otherwise they should be
|
||||
* banked which also is presented as 3 LEDs.
|
||||
*/
|
||||
mc_led_info = devm_kcalloc(priv->dev, LP50XX_LEDS_PER_MODULE,
|
||||
sizeof(*mc_led_info), GFP_KERNEL);
|
||||
if (!mc_led_info)
|
||||
return -ENOMEM;
|
||||
|
||||
fwnode_for_each_child_node(child, led_node) {
|
||||
ret = fwnode_property_read_u32(led_node, "color",
|
||||
&color_id);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Cannot read color\n");
|
||||
goto child_out;
|
||||
}
|
||||
|
||||
mc_led_info[num_colors].color_index = color_id;
|
||||
num_colors++;
|
||||
}
|
||||
|
||||
led->priv = priv;
|
||||
led->mc_cdev.num_colors = num_colors;
|
||||
led->mc_cdev.subled_info = mc_led_info;
|
||||
led_cdev = &led->mc_cdev.led_cdev;
|
||||
led_cdev->brightness_set_blocking = lp50xx_brightness_set;
|
||||
|
||||
ret = devm_led_classdev_multicolor_register_ext(&priv->client->dev,
|
||||
&led->mc_cdev,
|
||||
&init_data);
|
||||
if (ret) {
|
||||
dev_err(&priv->client->dev, "led register err: %d\n",
|
||||
ret);
|
||||
goto child_out;
|
||||
}
|
||||
i++;
|
||||
fwnode_handle_put(child);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
child_out:
|
||||
fwnode_handle_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp50xx_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct lp50xx *led;
|
||||
int count;
|
||||
int ret;
|
||||
|
||||
count = device_get_child_node_count(&client->dev);
|
||||
if (!count) {
|
||||
dev_err(&client->dev, "LEDs are not defined in device tree!");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
led = devm_kzalloc(&client->dev, struct_size(led, leds, count),
|
||||
GFP_KERNEL);
|
||||
if (!led)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&led->lock);
|
||||
led->client = client;
|
||||
led->dev = &client->dev;
|
||||
led->chip_info = &lp50xx_chip_info_tbl[id->driver_data];
|
||||
i2c_set_clientdata(client, led);
|
||||
led->regmap = devm_regmap_init_i2c(client,
|
||||
led->chip_info->lp50xx_regmap_config);
|
||||
if (IS_ERR(led->regmap)) {
|
||||
ret = PTR_ERR(led->regmap);
|
||||
dev_err(&client->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = lp50xx_reset(led);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lp50xx_enable_disable(led, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return lp50xx_probe_dt(led);
|
||||
}
|
||||
|
||||
static int lp50xx_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lp50xx *led = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
ret = lp50xx_enable_disable(led, 0);
|
||||
if (ret) {
|
||||
dev_err(&led->client->dev, "Failed to disable chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (led->regulator) {
|
||||
ret = regulator_disable(led->regulator);
|
||||
if (ret)
|
||||
dev_err(&led->client->dev,
|
||||
"Failed to disable regulator\n");
|
||||
}
|
||||
|
||||
mutex_destroy(&led->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lp50xx_id[] = {
|
||||
{ "lp5009", LP5009 },
|
||||
{ "lp5012", LP5012 },
|
||||
{ "lp5018", LP5018 },
|
||||
{ "lp5024", LP5024 },
|
||||
{ "lp5030", LP5030 },
|
||||
{ "lp5036", LP5036 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lp50xx_id);
|
||||
|
||||
static const struct of_device_id of_lp50xx_leds_match[] = {
|
||||
{ .compatible = "ti,lp5009", .data = (void *)LP5009 },
|
||||
{ .compatible = "ti,lp5012", .data = (void *)LP5012 },
|
||||
{ .compatible = "ti,lp5018", .data = (void *)LP5018 },
|
||||
{ .compatible = "ti,lp5024", .data = (void *)LP5024 },
|
||||
{ .compatible = "ti,lp5030", .data = (void *)LP5030 },
|
||||
{ .compatible = "ti,lp5036", .data = (void *)LP5036 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_lp50xx_leds_match);
|
||||
|
||||
static struct i2c_driver lp50xx_driver = {
|
||||
.driver = {
|
||||
.name = "lp50xx",
|
||||
.of_match_table = of_lp50xx_leds_match,
|
||||
},
|
||||
.probe = lp50xx_probe,
|
||||
.remove = lp50xx_remove,
|
||||
.id_table = lp50xx_id,
|
||||
};
|
||||
module_i2c_driver(lp50xx_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Texas Instruments LP50XX LED driver");
|
||||
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -523,7 +523,7 @@ static int lp5521_probe(struct i2c_client *client,
|
|||
struct lp55xx_chip *chip;
|
||||
struct lp55xx_led *led;
|
||||
struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct device_node *np = dev_of_node(&client->dev);
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
|
|
|
@ -891,7 +891,7 @@ static int lp5523_probe(struct i2c_client *client,
|
|||
struct lp55xx_chip *chip;
|
||||
struct lp55xx_led *led;
|
||||
struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct device_node *np = dev_of_node(&client->dev);
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
|
|
|
@ -518,7 +518,7 @@ static int lp5562_probe(struct i2c_client *client,
|
|||
struct lp55xx_chip *chip;
|
||||
struct lp55xx_led *led;
|
||||
struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct device_node *np = dev_of_node(&client->dev);
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
|
|
|
@ -611,11 +611,13 @@ static int lp55xx_parse_multi_led(struct device_node *np,
|
|||
struct device_node *child;
|
||||
int num_colors = 0, ret;
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
for_each_available_child_of_node(np, child) {
|
||||
ret = lp55xx_parse_multi_led_child(child, cfg, child_number,
|
||||
num_colors);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
num_colors++;
|
||||
}
|
||||
|
||||
|
@ -665,7 +667,7 @@ struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev,
|
|||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
num_channels = of_get_child_count(np);
|
||||
num_channels = of_get_available_child_count(np);
|
||||
if (num_channels == 0) {
|
||||
dev_err(dev, "no LED channels\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -679,10 +681,12 @@ struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev,
|
|||
pdata->num_channels = num_channels;
|
||||
cfg->max_channel = chip->cfg->max_channel;
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
for_each_available_child_of_node(np, child) {
|
||||
ret = lp55xx_parse_logical_led(child, cfg, i);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
|
|
|
@ -306,7 +306,7 @@ static int lp8501_probe(struct i2c_client *client,
|
|||
struct lp55xx_chip *chip;
|
||||
struct lp55xx_led *led;
|
||||
struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct device_node *np = dev_of_node(&client->dev);
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
|
|
|
@ -380,7 +380,7 @@ static int lp8860_probe(struct i2c_client *client,
|
|||
{
|
||||
int ret;
|
||||
struct lp8860_led *led;
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct device_node *np = dev_of_node(&client->dev);
|
||||
struct device_node *child_node;
|
||||
struct led_init_data init_data = {};
|
||||
|
||||
|
@ -392,10 +392,6 @@ static int lp8860_probe(struct i2c_client *client,
|
|||
if (!child_node)
|
||||
return -EINVAL;
|
||||
|
||||
led->led_dev.default_trigger = of_get_property(child_node,
|
||||
"linux,default-trigger",
|
||||
NULL);
|
||||
|
||||
led->enable_gpio = devm_gpiod_get_optional(&client->dev,
|
||||
"enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(led->enable_gpio)) {
|
||||
|
|
|
@ -68,7 +68,7 @@ static int lt3593_led_probe(struct platform_device *pdev)
|
|||
struct led_init_data init_data = {};
|
||||
const char *tmp;
|
||||
|
||||
if (!dev->of_node)
|
||||
if (!dev_of_node(dev))
|
||||
return -ENODEV;
|
||||
|
||||
led_data = devm_kzalloc(dev, sizeof(*led_data), GFP_KERNEL);
|
||||
|
@ -86,9 +86,6 @@ static int lt3593_led_probe(struct platform_device *pdev)
|
|||
|
||||
child = device_get_next_child_node(dev, NULL);
|
||||
|
||||
fwnode_property_read_string(child, "linux,default-trigger",
|
||||
&led_data->cdev.default_trigger);
|
||||
|
||||
if (!fwnode_property_read_string(child, "default-state", &tmp)) {
|
||||
if (!strcmp(tmp, "on"))
|
||||
state = LEDS_GPIO_DEFSTATE_ON;
|
||||
|
@ -107,7 +104,6 @@ static int lt3593_led_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
led_data->cdev.dev->of_node = dev->of_node;
|
||||
platform_set_drvdata(pdev, led_data);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -66,7 +66,6 @@ static int max77650_led_probe(struct platform_device *pdev)
|
|||
struct max77650_led *leds, *led;
|
||||
struct device *dev;
|
||||
struct regmap *map;
|
||||
const char *label;
|
||||
int rv, num_leds;
|
||||
u32 reg;
|
||||
|
||||
|
@ -86,6 +85,8 @@ static int max77650_led_probe(struct platform_device *pdev)
|
|||
return -ENODEV;
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
struct led_init_data init_data = {};
|
||||
|
||||
rv = fwnode_property_read_u32(child, "reg", ®);
|
||||
if (rv || reg >= MAX77650_LED_NUM_LEDS) {
|
||||
rv = -EINVAL;
|
||||
|
@ -99,22 +100,13 @@ static int max77650_led_probe(struct platform_device *pdev)
|
|||
led->cdev.brightness_set_blocking = max77650_led_brightness_set;
|
||||
led->cdev.max_brightness = MAX77650_LED_MAX_BRIGHTNESS;
|
||||
|
||||
rv = fwnode_property_read_string(child, "label", &label);
|
||||
if (rv) {
|
||||
led->cdev.name = "max77650::";
|
||||
} else {
|
||||
led->cdev.name = devm_kasprintf(dev, GFP_KERNEL,
|
||||
"max77650:%s", label);
|
||||
if (!led->cdev.name) {
|
||||
rv = -ENOMEM;
|
||||
goto err_node_put;
|
||||
}
|
||||
}
|
||||
init_data.fwnode = child;
|
||||
init_data.devicename = "max77650";
|
||||
/* for backwards compatibility if `label` is not present */
|
||||
init_data.default_label = ":";
|
||||
|
||||
fwnode_property_read_string(child, "linux,default-trigger",
|
||||
&led->cdev.default_trigger);
|
||||
|
||||
rv = devm_led_classdev_register(dev, &led->cdev);
|
||||
rv = devm_led_classdev_register_ext(dev, &led->cdev,
|
||||
&init_data);
|
||||
if (rv)
|
||||
goto err_node_put;
|
||||
|
||||
|
|
|
@ -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, *child_node;
|
||||
struct device_node *node = dev_of_node(dev), *child_node;
|
||||
struct property *prop;
|
||||
u32 led_sources[2];
|
||||
int i, ret, fled_id;
|
||||
|
|
|
@ -121,7 +121,7 @@ static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(
|
|||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
parent = of_get_child_by_name(dev->parent->of_node, "leds");
|
||||
parent = of_get_child_by_name(dev_of_node(dev->parent), "leds");
|
||||
if (!parent)
|
||||
goto out_node_put;
|
||||
|
||||
|
@ -131,7 +131,7 @@ static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(
|
|||
if (ret)
|
||||
goto out_node_put;
|
||||
|
||||
pdata->num_leds = of_get_child_count(parent);
|
||||
pdata->num_leds = of_get_available_child_count(parent);
|
||||
|
||||
pdata->led = devm_kcalloc(dev, pdata->num_leds, sizeof(*pdata->led),
|
||||
GFP_KERNEL);
|
||||
|
@ -140,7 +140,7 @@ static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt(
|
|||
goto out_node_put;
|
||||
}
|
||||
|
||||
for_each_child_of_node(parent, child) {
|
||||
for_each_available_child_of_node(parent, child) {
|
||||
const char *str;
|
||||
u32 tmp;
|
||||
|
||||
|
@ -192,7 +192,7 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev)
|
|||
leds->master = mcdev;
|
||||
platform_set_drvdata(pdev, leds);
|
||||
|
||||
if (dev->parent->of_node) {
|
||||
if (dev_of_node(dev->parent)) {
|
||||
pdata = mc13xxx_led_probe_dt(pdev);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
|
|
|
@ -248,15 +248,6 @@ static int mt6323_led_set_blink(struct led_classdev *cdev,
|
|||
u8 duty_hw;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Units are in ms, if over the hardware able
|
||||
* to support, fallback into software blink
|
||||
*/
|
||||
period = *delay_on + *delay_off;
|
||||
|
||||
if (period > MT6323_MAX_PERIOD)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* LED subsystem requires a default user
|
||||
* friendly blink pattern for the LED so using
|
||||
|
@ -268,6 +259,15 @@ static int mt6323_led_set_blink(struct led_classdev *cdev,
|
|||
*delay_off = 500;
|
||||
}
|
||||
|
||||
/*
|
||||
* Units are in ms, if over the hardware able
|
||||
* to support, fallback into software blink
|
||||
*/
|
||||
period = *delay_on + *delay_off;
|
||||
|
||||
if (period > MT6323_MAX_PERIOD)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Calculate duty_hw based on the percentage of period during
|
||||
* which the led is ON.
|
||||
|
@ -342,11 +342,6 @@ static int mt6323_led_set_dt_default(struct led_classdev *cdev,
|
|||
const char *state;
|
||||
int ret = 0;
|
||||
|
||||
led->cdev.name = of_get_property(np, "label", NULL) ? : np->name;
|
||||
led->cdev.default_trigger = of_get_property(np,
|
||||
"linux,default-trigger",
|
||||
NULL);
|
||||
|
||||
state = of_get_property(np, "default-state", NULL);
|
||||
if (state) {
|
||||
if (!strcmp(state, "keep")) {
|
||||
|
@ -369,9 +364,9 @@ static int mt6323_led_set_dt_default(struct led_classdev *cdev,
|
|||
static int mt6323_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *np = dev_of_node(dev);
|
||||
struct device_node *child;
|
||||
struct mt6397_chip *hw = dev_get_drvdata(pdev->dev.parent);
|
||||
struct mt6397_chip *hw = dev_get_drvdata(dev->parent);
|
||||
struct mt6323_leds *leds;
|
||||
struct mt6323_led *led;
|
||||
int ret;
|
||||
|
@ -402,6 +397,8 @@ static int mt6323_led_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
struct led_init_data init_data = {};
|
||||
|
||||
ret = of_property_read_u32(child, "reg", ®);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to read led 'reg' property\n");
|
||||
|
@ -437,13 +434,14 @@ static int mt6323_led_probe(struct platform_device *pdev)
|
|||
goto put_child_node;
|
||||
}
|
||||
|
||||
ret = devm_led_classdev_register(dev, &leds->led[reg]->cdev);
|
||||
init_data.fwnode = of_fwnode_handle(child);
|
||||
|
||||
ret = devm_led_classdev_register_ext(dev, &leds->led[reg]->cdev,
|
||||
&init_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to register LED: %d\n",
|
||||
ret);
|
||||
dev_err(dev, "Failed to register LED: %d\n", ret);
|
||||
goto put_child_node;
|
||||
}
|
||||
leds->led[reg]->cdev.dev->of_node = child;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -419,7 +419,7 @@ static int netxbig_gpio_ext_get(struct device *dev,
|
|||
static int netxbig_leds_get_of_pdata(struct device *dev,
|
||||
struct netxbig_led_platform_data *pdata)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *np = dev_of_node(dev);
|
||||
struct device_node *gpio_ext_np;
|
||||
struct platform_device *gpio_ext_pdev;
|
||||
struct device *gpio_ext_dev;
|
||||
|
@ -485,7 +485,7 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
|
|||
}
|
||||
|
||||
/* LEDs */
|
||||
num_leds = of_get_child_count(np);
|
||||
num_leds = of_get_available_child_count(np);
|
||||
if (!num_leds) {
|
||||
dev_err(dev, "No LED subnodes found in DT\n");
|
||||
return -ENODEV;
|
||||
|
@ -496,7 +496,7 @@ static int netxbig_leds_get_of_pdata(struct device *dev,
|
|||
return -ENOMEM;
|
||||
|
||||
led = leds;
|
||||
for_each_child_of_node(np, child) {
|
||||
for_each_available_child_of_node(np, child) {
|
||||
const char *string;
|
||||
int *mode_val;
|
||||
int num_modes;
|
||||
|
|
|
@ -24,25 +24,16 @@ enum ns2_led_modes {
|
|||
NS_V2_LED_SATA,
|
||||
};
|
||||
|
||||
/*
|
||||
* If the size of this structure or types of its members is changed,
|
||||
* the filling of array modval in function ns2_led_register must be changed
|
||||
* accordingly.
|
||||
*/
|
||||
struct ns2_led_modval {
|
||||
enum ns2_led_modes mode;
|
||||
int cmd_level;
|
||||
int slow_level;
|
||||
};
|
||||
|
||||
struct ns2_led {
|
||||
const char *name;
|
||||
const char *default_trigger;
|
||||
struct gpio_desc *cmd;
|
||||
struct gpio_desc *slow;
|
||||
int num_modes;
|
||||
struct ns2_led_modval *modval;
|
||||
};
|
||||
|
||||
struct ns2_led_platform_data {
|
||||
int num_leds;
|
||||
struct ns2_led *leds;
|
||||
};
|
||||
u32 mode;
|
||||
u32 cmd_level;
|
||||
u32 slow_level;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED
|
||||
|
@ -51,7 +42,7 @@ struct ns2_led_platform_data {
|
|||
* for the command/slow GPIOs corresponds to a LED mode.
|
||||
*/
|
||||
|
||||
struct ns2_led_data {
|
||||
struct ns2_led {
|
||||
struct led_classdev cdev;
|
||||
struct gpio_desc *cmd;
|
||||
struct gpio_desc *slow;
|
||||
|
@ -62,77 +53,67 @@ struct ns2_led_data {
|
|||
struct ns2_led_modval *modval;
|
||||
};
|
||||
|
||||
static int ns2_led_get_mode(struct ns2_led_data *led_dat,
|
||||
enum ns2_led_modes *mode)
|
||||
static int ns2_led_get_mode(struct ns2_led *led, enum ns2_led_modes *mode)
|
||||
{
|
||||
int i;
|
||||
int ret = -EINVAL;
|
||||
int cmd_level;
|
||||
int slow_level;
|
||||
|
||||
cmd_level = gpiod_get_value_cansleep(led_dat->cmd);
|
||||
slow_level = gpiod_get_value_cansleep(led_dat->slow);
|
||||
cmd_level = gpiod_get_value_cansleep(led->cmd);
|
||||
slow_level = gpiod_get_value_cansleep(led->slow);
|
||||
|
||||
for (i = 0; i < led_dat->num_modes; i++) {
|
||||
if (cmd_level == led_dat->modval[i].cmd_level &&
|
||||
slow_level == led_dat->modval[i].slow_level) {
|
||||
*mode = led_dat->modval[i].mode;
|
||||
ret = 0;
|
||||
break;
|
||||
for (i = 0; i < led->num_modes; i++) {
|
||||
if (cmd_level == led->modval[i].cmd_level &&
|
||||
slow_level == led->modval[i].slow_level) {
|
||||
*mode = led->modval[i].mode;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void ns2_led_set_mode(struct ns2_led_data *led_dat,
|
||||
enum ns2_led_modes mode)
|
||||
static void ns2_led_set_mode(struct ns2_led *led, enum ns2_led_modes mode)
|
||||
{
|
||||
int i;
|
||||
bool found = false;
|
||||
unsigned long flags;
|
||||
|
||||
for (i = 0; i < led_dat->num_modes; i++)
|
||||
if (mode == led_dat->modval[i].mode) {
|
||||
found = true;
|
||||
for (i = 0; i < led->num_modes; i++)
|
||||
if (mode == led->modval[i].mode)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
if (i == led->num_modes)
|
||||
return;
|
||||
|
||||
write_lock_irqsave(&led_dat->rw_lock, flags);
|
||||
write_lock_irqsave(&led->rw_lock, flags);
|
||||
|
||||
if (!led_dat->can_sleep) {
|
||||
gpiod_set_value(led_dat->cmd,
|
||||
led_dat->modval[i].cmd_level);
|
||||
gpiod_set_value(led_dat->slow,
|
||||
led_dat->modval[i].slow_level);
|
||||
if (!led->can_sleep) {
|
||||
gpiod_set_value(led->cmd, led->modval[i].cmd_level);
|
||||
gpiod_set_value(led->slow, led->modval[i].slow_level);
|
||||
goto exit_unlock;
|
||||
}
|
||||
|
||||
gpiod_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level);
|
||||
gpiod_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level);
|
||||
gpiod_set_value_cansleep(led->cmd, led->modval[i].cmd_level);
|
||||
gpiod_set_value_cansleep(led->slow, led->modval[i].slow_level);
|
||||
|
||||
exit_unlock:
|
||||
write_unlock_irqrestore(&led_dat->rw_lock, flags);
|
||||
write_unlock_irqrestore(&led->rw_lock, flags);
|
||||
}
|
||||
|
||||
static void ns2_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct ns2_led_data *led_dat =
|
||||
container_of(led_cdev, struct ns2_led_data, cdev);
|
||||
struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
|
||||
enum ns2_led_modes mode;
|
||||
|
||||
if (value == LED_OFF)
|
||||
mode = NS_V2_LED_OFF;
|
||||
else if (led_dat->sata)
|
||||
else if (led->sata)
|
||||
mode = NS_V2_LED_SATA;
|
||||
else
|
||||
mode = NS_V2_LED_ON;
|
||||
|
||||
ns2_led_set_mode(led_dat, mode);
|
||||
ns2_led_set_mode(led, mode);
|
||||
}
|
||||
|
||||
static int ns2_led_set_blocking(struct led_classdev *led_cdev,
|
||||
|
@ -147,8 +128,7 @@ static ssize_t ns2_led_sata_store(struct device *dev,
|
|||
const char *buff, size_t count)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct ns2_led_data *led_dat =
|
||||
container_of(led_cdev, struct ns2_led_data, cdev);
|
||||
struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
|
||||
int ret;
|
||||
unsigned long enable;
|
||||
|
||||
|
@ -158,18 +138,18 @@ static ssize_t ns2_led_sata_store(struct device *dev,
|
|||
|
||||
enable = !!enable;
|
||||
|
||||
if (led_dat->sata == enable)
|
||||
if (led->sata == enable)
|
||||
goto exit;
|
||||
|
||||
led_dat->sata = enable;
|
||||
led->sata = enable;
|
||||
|
||||
if (!led_get_brightness(led_cdev))
|
||||
goto exit;
|
||||
|
||||
if (enable)
|
||||
ns2_led_set_mode(led_dat, NS_V2_LED_SATA);
|
||||
ns2_led_set_mode(led, NS_V2_LED_SATA);
|
||||
else
|
||||
ns2_led_set_mode(led_dat, NS_V2_LED_ON);
|
||||
ns2_led_set_mode(led, NS_V2_LED_ON);
|
||||
|
||||
exit:
|
||||
return count;
|
||||
|
@ -179,10 +159,9 @@ static ssize_t ns2_led_sata_show(struct device *dev,
|
|||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct ns2_led_data *led_dat =
|
||||
container_of(led_cdev, struct ns2_led_data, cdev);
|
||||
struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
|
||||
|
||||
return sprintf(buf, "%d\n", led_dat->sata);
|
||||
return sprintf(buf, "%d\n", led->sata);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store);
|
||||
|
@ -193,147 +172,94 @@ static struct attribute *ns2_led_attrs[] = {
|
|||
};
|
||||
ATTRIBUTE_GROUPS(ns2_led);
|
||||
|
||||
static int
|
||||
create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
|
||||
const struct ns2_led *template)
|
||||
static int ns2_led_register(struct device *dev, struct fwnode_handle *node,
|
||||
struct ns2_led *led)
|
||||
{
|
||||
int ret;
|
||||
struct led_init_data init_data = {};
|
||||
struct ns2_led_modval *modval;
|
||||
enum ns2_led_modes mode;
|
||||
int nmodes, ret;
|
||||
|
||||
rwlock_init(&led_dat->rw_lock);
|
||||
led->cmd = devm_fwnode_gpiod_get_index(dev, node, "cmd", 0, GPIOD_ASIS,
|
||||
fwnode_get_name(node));
|
||||
if (IS_ERR(led->cmd))
|
||||
return PTR_ERR(led->cmd);
|
||||
|
||||
led_dat->cdev.name = template->name;
|
||||
led_dat->cdev.default_trigger = template->default_trigger;
|
||||
led_dat->cdev.blink_set = NULL;
|
||||
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||
led_dat->cdev.groups = ns2_led_groups;
|
||||
led_dat->cmd = template->cmd;
|
||||
led_dat->slow = template->slow;
|
||||
led_dat->can_sleep = gpiod_cansleep(led_dat->cmd) |
|
||||
gpiod_cansleep(led_dat->slow);
|
||||
if (led_dat->can_sleep)
|
||||
led_dat->cdev.brightness_set_blocking = ns2_led_set_blocking;
|
||||
led->slow = devm_fwnode_gpiod_get_index(dev, node, "slow", 0,
|
||||
GPIOD_ASIS,
|
||||
fwnode_get_name(node));
|
||||
if (IS_ERR(led->slow))
|
||||
return PTR_ERR(led->slow);
|
||||
|
||||
ret = fwnode_property_count_u32(node, "modes-map");
|
||||
if (ret < 0 || ret % 3) {
|
||||
dev_err(dev, "Missing or malformed modes-map for %pfw\n", node);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nmodes = ret / 3;
|
||||
modval = devm_kcalloc(dev, nmodes, sizeof(*modval), GFP_KERNEL);
|
||||
if (!modval)
|
||||
return -ENOMEM;
|
||||
|
||||
fwnode_property_read_u32_array(node, "modes-map", (void *)modval,
|
||||
nmodes * 3);
|
||||
|
||||
rwlock_init(&led->rw_lock);
|
||||
|
||||
led->cdev.blink_set = NULL;
|
||||
led->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||
led->cdev.groups = ns2_led_groups;
|
||||
led->can_sleep = gpiod_cansleep(led->cmd) || gpiod_cansleep(led->slow);
|
||||
if (led->can_sleep)
|
||||
led->cdev.brightness_set_blocking = ns2_led_set_blocking;
|
||||
else
|
||||
led_dat->cdev.brightness_set = ns2_led_set;
|
||||
led_dat->modval = template->modval;
|
||||
led_dat->num_modes = template->num_modes;
|
||||
led->cdev.brightness_set = ns2_led_set;
|
||||
led->num_modes = nmodes;
|
||||
led->modval = modval;
|
||||
|
||||
ret = ns2_led_get_mode(led_dat, &mode);
|
||||
ret = ns2_led_get_mode(led, &mode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Set LED initial state. */
|
||||
led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0;
|
||||
led_dat->cdev.brightness =
|
||||
(mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
|
||||
led->sata = (mode == NS_V2_LED_SATA) ? 1 : 0;
|
||||
led->cdev.brightness = (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
|
||||
|
||||
ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
init_data.fwnode = node;
|
||||
|
||||
return 0;
|
||||
ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to register LED for node %pfw\n", node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void delete_ns2_led(struct ns2_led_data *led_dat)
|
||||
static int ns2_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
led_classdev_unregister(&led_dat->cdev);
|
||||
}
|
||||
struct device *dev = &pdev->dev;
|
||||
struct fwnode_handle *child;
|
||||
struct ns2_led *leds;
|
||||
int count;
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
/*
|
||||
* Translate OpenFirmware node properties into platform_data.
|
||||
*/
|
||||
static int
|
||||
ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *child;
|
||||
struct ns2_led *led, *leds;
|
||||
int ret, num_leds = 0;
|
||||
|
||||
num_leds = of_get_child_count(np);
|
||||
if (!num_leds)
|
||||
count = device_get_child_node_count(dev);
|
||||
if (!count)
|
||||
return -ENODEV;
|
||||
|
||||
leds = devm_kcalloc(dev, num_leds, sizeof(struct ns2_led),
|
||||
GFP_KERNEL);
|
||||
leds = devm_kzalloc(dev, array_size(sizeof(*leds), count), GFP_KERNEL);
|
||||
if (!leds)
|
||||
return -ENOMEM;
|
||||
|
||||
led = leds;
|
||||
for_each_child_of_node(np, child) {
|
||||
const char *string;
|
||||
int i, num_modes;
|
||||
struct ns2_led_modval *modval;
|
||||
struct gpio_desc *gd;
|
||||
|
||||
ret = of_property_read_string(child, "label", &string);
|
||||
led->name = (ret == 0) ? string : child->name;
|
||||
|
||||
gd = gpiod_get_from_of_node(child, "cmd-gpio", 0,
|
||||
GPIOD_ASIS, led->name);
|
||||
if (IS_ERR(gd)) {
|
||||
ret = PTR_ERR(gd);
|
||||
goto err_node_put;
|
||||
device_for_each_child_node(dev, child) {
|
||||
ret = ns2_led_register(dev, child, leds++);
|
||||
if (ret) {
|
||||
fwnode_handle_put(child);
|
||||
return ret;
|
||||
}
|
||||
led->cmd = gd;
|
||||
gd = gpiod_get_from_of_node(child, "slow-gpio", 0,
|
||||
GPIOD_ASIS, led->name);
|
||||
if (IS_ERR(gd)) {
|
||||
ret = PTR_ERR(gd);
|
||||
goto err_node_put;
|
||||
}
|
||||
led->slow = gd;
|
||||
|
||||
ret = of_property_read_string(child, "linux,default-trigger",
|
||||
&string);
|
||||
if (ret == 0)
|
||||
led->default_trigger = string;
|
||||
|
||||
ret = of_property_count_u32_elems(child, "modes-map");
|
||||
if (ret < 0 || ret % 3) {
|
||||
dev_err(dev,
|
||||
"Missing or malformed modes-map property\n");
|
||||
ret = -EINVAL;
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
num_modes = ret / 3;
|
||||
modval = devm_kcalloc(dev,
|
||||
num_modes,
|
||||
sizeof(struct ns2_led_modval),
|
||||
GFP_KERNEL);
|
||||
if (!modval) {
|
||||
ret = -ENOMEM;
|
||||
goto err_node_put;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_modes; i++) {
|
||||
of_property_read_u32_index(child,
|
||||
"modes-map", 3 * i,
|
||||
(u32 *) &modval[i].mode);
|
||||
of_property_read_u32_index(child,
|
||||
"modes-map", 3 * i + 1,
|
||||
(u32 *) &modval[i].cmd_level);
|
||||
of_property_read_u32_index(child,
|
||||
"modes-map", 3 * i + 2,
|
||||
(u32 *) &modval[i].slow_level);
|
||||
}
|
||||
|
||||
led->num_modes = num_modes;
|
||||
led->modval = modval;
|
||||
|
||||
led++;
|
||||
}
|
||||
|
||||
pdata->leds = leds;
|
||||
pdata->num_leds = num_leds;
|
||||
|
||||
return 0;
|
||||
|
||||
err_node_put:
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_ns2_leds_match[] = {
|
||||
|
@ -341,76 +267,12 @@ static const struct of_device_id of_ns2_leds_match[] = {
|
|||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_ns2_leds_match);
|
||||
#endif /* CONFIG_OF_GPIO */
|
||||
|
||||
struct ns2_led_priv {
|
||||
int num_leds;
|
||||
struct ns2_led_data leds_data[];
|
||||
};
|
||||
|
||||
static int ns2_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct ns2_led_priv *priv;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
if (!pdata) {
|
||||
pdata = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ns2_led_platform_data),
|
||||
GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = ns2_leds_get_of_pdata(&pdev->dev, pdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
#endif /* CONFIG_OF_GPIO */
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds_data, pdata->num_leds), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
priv->num_leds = pdata->num_leds;
|
||||
|
||||
for (i = 0; i < priv->num_leds; i++) {
|
||||
ret = create_ns2_led(pdev, &priv->leds_data[i],
|
||||
&pdata->leds[i]);
|
||||
if (ret < 0) {
|
||||
for (i = i - 1; i >= 0; i--)
|
||||
delete_ns2_led(&priv->leds_data[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ns2_led_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
struct ns2_led_priv *priv;
|
||||
|
||||
priv = platform_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < priv->num_leds; i++)
|
||||
delete_ns2_led(&priv->leds_data[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ns2_led_driver = {
|
||||
.probe = ns2_led_probe,
|
||||
.remove = ns2_led_remove,
|
||||
.driver = {
|
||||
.name = "leds-ns2",
|
||||
.of_match_table = of_match_ptr(of_ns2_leds_match),
|
||||
.of_match_table = of_ns2_leds_match,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#define PCA9532_REG_PWM(m, i) (PCA9532_REG_OFFSET(m) + 0x2 + (i) * 2)
|
||||
#define LED_REG(m, led) (PCA9532_REG_OFFSET(m) + 0x5 + (led >> 2))
|
||||
#define LED_NUM(led) (led & 0x3)
|
||||
#define LED_SHIFT(led) (LED_NUM(led) * 2)
|
||||
#define LED_MASK(led) (0x3 << LED_SHIFT(led))
|
||||
|
||||
#define ldev_to_led(c) container_of(c, struct pca9532_led, ldev)
|
||||
|
||||
|
@ -162,9 +164,9 @@ static void pca9532_setled(struct pca9532_led *led)
|
|||
mutex_lock(&data->update_lock);
|
||||
reg = i2c_smbus_read_byte_data(client, LED_REG(maxleds, led->id));
|
||||
/* zero led bits */
|
||||
reg = reg & ~(0x3<<LED_NUM(led->id)*2);
|
||||
reg = reg & ~LED_MASK(led->id);
|
||||
/* set the new value */
|
||||
reg = reg | (led->state << LED_NUM(led->id)*2);
|
||||
reg = reg | (led->state << LED_SHIFT(led->id));
|
||||
i2c_smbus_write_byte_data(client, LED_REG(maxleds, led->id), reg);
|
||||
mutex_unlock(&data->update_lock);
|
||||
}
|
||||
|
@ -260,7 +262,7 @@ static enum pca9532_state pca9532_getled(struct pca9532_led *led)
|
|||
|
||||
mutex_lock(&data->update_lock);
|
||||
reg = i2c_smbus_read_byte_data(client, LED_REG(maxleds, led->id));
|
||||
ret = reg >> LED_NUM(led->id)/2;
|
||||
ret = (reg & LED_MASK(led->id)) >> LED_SHIFT(led->id);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -478,7 +480,12 @@ pca9532_of_populate_pdata(struct device *dev, struct device_node *np)
|
|||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
of_property_read_u8_array(np, "nxp,pwm", &pdata->pwm[0],
|
||||
ARRAY_SIZE(pdata->pwm));
|
||||
of_property_read_u8_array(np, "nxp,psc", &pdata->psc[0],
|
||||
ARRAY_SIZE(pdata->psc));
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
if (of_property_read_string(child, "label",
|
||||
&pdata->leds[i].name))
|
||||
pdata->leds[i].name = child->name;
|
||||
|
@ -507,7 +514,7 @@ static int pca9532_probe(struct i2c_client *client,
|
|||
struct pca9532_data *data = i2c_get_clientdata(client);
|
||||
struct pca9532_platform_data *pca9532_pdata =
|
||||
dev_get_platdata(&client->dev);
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct device_node *np = dev_of_node(&client->dev);
|
||||
|
||||
if (!pca9532_pdata) {
|
||||
if (np) {
|
||||
|
@ -545,13 +552,8 @@ static int pca9532_probe(struct i2c_client *client,
|
|||
static int pca9532_remove(struct i2c_client *client)
|
||||
{
|
||||
struct pca9532_data *data = i2c_get_clientdata(client);
|
||||
int err;
|
||||
|
||||
err = pca9532_destroy_devices(data, data->chip_info->num_leds);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
return pca9532_destroy_devices(data, data->chip_info->num_leds);
|
||||
}
|
||||
|
||||
module_i2c_driver(pca9532_driver);
|
||||
|
|
|
@ -65,6 +65,7 @@ enum pca955x_type {
|
|||
pca9550,
|
||||
pca9551,
|
||||
pca9552,
|
||||
ibm_pca9552,
|
||||
pca9553,
|
||||
};
|
||||
|
||||
|
@ -90,6 +91,11 @@ static struct pca955x_chipdef pca955x_chipdefs[] = {
|
|||
.slv_addr = /* 1100xxx */ 0x60,
|
||||
.slv_addr_shift = 3,
|
||||
},
|
||||
[ibm_pca9552] = {
|
||||
.bits = 16,
|
||||
.slv_addr = /* 0110xxx */ 0x30,
|
||||
.slv_addr_shift = 3,
|
||||
},
|
||||
[pca9553] = {
|
||||
.bits = 4,
|
||||
.slv_addr = /* 110001x */ 0x62,
|
||||
|
@ -101,6 +107,7 @@ static const struct i2c_device_id pca955x_id[] = {
|
|||
{ "pca9550", pca9550 },
|
||||
{ "pca9551", pca9551 },
|
||||
{ "pca9552", pca9552 },
|
||||
{ "ibm-pca9552", ibm_pca9552 },
|
||||
{ "pca9553", pca9553 },
|
||||
{ }
|
||||
};
|
||||
|
@ -412,6 +419,7 @@ static const struct of_device_id of_pca955x_match[] = {
|
|||
{ .compatible = "nxp,pca9550", .data = (void *)pca9550 },
|
||||
{ .compatible = "nxp,pca9551", .data = (void *)pca9551 },
|
||||
{ .compatible = "nxp,pca9552", .data = (void *)pca9552 },
|
||||
{ .compatible = "ibm,pca9552", .data = (void *)ibm_pca9552 },
|
||||
{ .compatible = "nxp,pca9553", .data = (void *)pca9553 },
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_data/leds-pca963x.h>
|
||||
|
||||
/* LED select registers determine the source that drives LED outputs */
|
||||
#define PCA963X_LED_OFF 0x0 /* LED driver off */
|
||||
|
@ -96,142 +95,148 @@ static const struct i2c_device_id pca963x_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pca963x_id);
|
||||
|
||||
struct pca963x_led;
|
||||
|
||||
struct pca963x {
|
||||
struct pca963x_chipdef *chipdef;
|
||||
struct mutex mutex;
|
||||
struct i2c_client *client;
|
||||
struct pca963x_led *leds;
|
||||
unsigned long leds_on;
|
||||
};
|
||||
struct pca963x;
|
||||
|
||||
struct pca963x_led {
|
||||
struct pca963x *chip;
|
||||
struct led_classdev led_cdev;
|
||||
int led_num; /* 0 .. 15 potentially */
|
||||
char name[32];
|
||||
u8 gdc;
|
||||
u8 gfrq;
|
||||
};
|
||||
|
||||
static int pca963x_brightness(struct pca963x_led *pca963x,
|
||||
enum led_brightness brightness)
|
||||
struct pca963x {
|
||||
struct pca963x_chipdef *chipdef;
|
||||
struct mutex mutex;
|
||||
struct i2c_client *client;
|
||||
unsigned long leds_on;
|
||||
struct pca963x_led leds[];
|
||||
};
|
||||
|
||||
static int pca963x_brightness(struct pca963x_led *led,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
u8 ledout_addr = pca963x->chip->chipdef->ledout_base
|
||||
+ (pca963x->led_num / 4);
|
||||
u8 ledout;
|
||||
int shift = 2 * (pca963x->led_num % 4);
|
||||
u8 mask = 0x3 << shift;
|
||||
struct i2c_client *client = led->chip->client;
|
||||
struct pca963x_chipdef *chipdef = led->chip->chipdef;
|
||||
u8 ledout_addr, ledout, mask, val;
|
||||
int shift;
|
||||
int ret;
|
||||
|
||||
ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr);
|
||||
ledout_addr = chipdef->ledout_base + (led->led_num / 4);
|
||||
shift = 2 * (led->led_num % 4);
|
||||
mask = 0x3 << shift;
|
||||
ledout = i2c_smbus_read_byte_data(client, ledout_addr);
|
||||
|
||||
switch (brightness) {
|
||||
case LED_FULL:
|
||||
ret = i2c_smbus_write_byte_data(pca963x->chip->client,
|
||||
ledout_addr,
|
||||
(ledout & ~mask) | (PCA963X_LED_ON << shift));
|
||||
val = (ledout & ~mask) | (PCA963X_LED_ON << shift);
|
||||
ret = i2c_smbus_write_byte_data(client, ledout_addr, val);
|
||||
break;
|
||||
case LED_OFF:
|
||||
ret = i2c_smbus_write_byte_data(pca963x->chip->client,
|
||||
ledout_addr, ledout & ~mask);
|
||||
val = ledout & ~mask;
|
||||
ret = i2c_smbus_write_byte_data(client, ledout_addr, val);
|
||||
break;
|
||||
default:
|
||||
ret = i2c_smbus_write_byte_data(pca963x->chip->client,
|
||||
PCA963X_PWM_BASE + pca963x->led_num,
|
||||
brightness);
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
PCA963X_PWM_BASE +
|
||||
led->led_num,
|
||||
brightness);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = i2c_smbus_write_byte_data(pca963x->chip->client,
|
||||
ledout_addr,
|
||||
(ledout & ~mask) | (PCA963X_LED_PWM << shift));
|
||||
|
||||
val = (ledout & ~mask) | (PCA963X_LED_PWM << shift);
|
||||
ret = i2c_smbus_write_byte_data(client, ledout_addr, val);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pca963x_blink(struct pca963x_led *pca963x)
|
||||
static void pca963x_blink(struct pca963x_led *led)
|
||||
{
|
||||
u8 ledout_addr = pca963x->chip->chipdef->ledout_base +
|
||||
(pca963x->led_num / 4);
|
||||
u8 ledout;
|
||||
u8 mode2 = i2c_smbus_read_byte_data(pca963x->chip->client,
|
||||
PCA963X_MODE2);
|
||||
int shift = 2 * (pca963x->led_num % 4);
|
||||
u8 mask = 0x3 << shift;
|
||||
struct i2c_client *client = led->chip->client;
|
||||
struct pca963x_chipdef *chipdef = led->chip->chipdef;
|
||||
u8 ledout_addr, ledout, mask, val, mode2;
|
||||
int shift;
|
||||
|
||||
i2c_smbus_write_byte_data(pca963x->chip->client,
|
||||
pca963x->chip->chipdef->grppwm, pca963x->gdc);
|
||||
ledout_addr = chipdef->ledout_base + (led->led_num / 4);
|
||||
shift = 2 * (led->led_num % 4);
|
||||
mask = 0x3 << shift;
|
||||
mode2 = i2c_smbus_read_byte_data(client, PCA963X_MODE2);
|
||||
|
||||
i2c_smbus_write_byte_data(pca963x->chip->client,
|
||||
pca963x->chip->chipdef->grpfreq, pca963x->gfrq);
|
||||
i2c_smbus_write_byte_data(client, chipdef->grppwm, led->gdc);
|
||||
|
||||
i2c_smbus_write_byte_data(client, chipdef->grpfreq, led->gfrq);
|
||||
|
||||
if (!(mode2 & PCA963X_MODE2_DMBLNK))
|
||||
i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2,
|
||||
mode2 | PCA963X_MODE2_DMBLNK);
|
||||
i2c_smbus_write_byte_data(client, PCA963X_MODE2,
|
||||
mode2 | PCA963X_MODE2_DMBLNK);
|
||||
|
||||
mutex_lock(&pca963x->chip->mutex);
|
||||
ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr);
|
||||
if ((ledout & mask) != (PCA963X_LED_GRP_PWM << shift))
|
||||
i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
|
||||
(ledout & ~mask) | (PCA963X_LED_GRP_PWM << shift));
|
||||
mutex_unlock(&pca963x->chip->mutex);
|
||||
mutex_lock(&led->chip->mutex);
|
||||
|
||||
ledout = i2c_smbus_read_byte_data(client, ledout_addr);
|
||||
if ((ledout & mask) != (PCA963X_LED_GRP_PWM << shift)) {
|
||||
val = (ledout & ~mask) | (PCA963X_LED_GRP_PWM << shift);
|
||||
i2c_smbus_write_byte_data(client, ledout_addr, val);
|
||||
}
|
||||
|
||||
mutex_unlock(&led->chip->mutex);
|
||||
}
|
||||
|
||||
static int pca963x_power_state(struct pca963x_led *pca963x)
|
||||
static int pca963x_power_state(struct pca963x_led *led)
|
||||
{
|
||||
unsigned long *leds_on = &pca963x->chip->leds_on;
|
||||
unsigned long cached_leds = pca963x->chip->leds_on;
|
||||
struct i2c_client *client = led->chip->client;
|
||||
unsigned long *leds_on = &led->chip->leds_on;
|
||||
unsigned long cached_leds = *leds_on;
|
||||
|
||||
if (pca963x->led_cdev.brightness)
|
||||
set_bit(pca963x->led_num, leds_on);
|
||||
if (led->led_cdev.brightness)
|
||||
set_bit(led->led_num, leds_on);
|
||||
else
|
||||
clear_bit(pca963x->led_num, leds_on);
|
||||
clear_bit(led->led_num, leds_on);
|
||||
|
||||
if (!(*leds_on) != !cached_leds)
|
||||
return i2c_smbus_write_byte_data(pca963x->chip->client,
|
||||
PCA963X_MODE1, *leds_on ? 0 : BIT(4));
|
||||
return i2c_smbus_write_byte_data(client, PCA963X_MODE1,
|
||||
*leds_on ? 0 : BIT(4));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pca963x_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct pca963x_led *pca963x;
|
||||
struct pca963x_led *led;
|
||||
int ret;
|
||||
|
||||
pca963x = container_of(led_cdev, struct pca963x_led, led_cdev);
|
||||
led = container_of(led_cdev, struct pca963x_led, led_cdev);
|
||||
|
||||
mutex_lock(&pca963x->chip->mutex);
|
||||
mutex_lock(&led->chip->mutex);
|
||||
|
||||
ret = pca963x_brightness(pca963x, value);
|
||||
ret = pca963x_brightness(led, value);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
ret = pca963x_power_state(pca963x);
|
||||
ret = pca963x_power_state(led);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&pca963x->chip->mutex);
|
||||
mutex_unlock(&led->chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int pca963x_period_scale(struct pca963x_led *pca963x,
|
||||
unsigned int val)
|
||||
static unsigned int pca963x_period_scale(struct pca963x_led *led,
|
||||
unsigned int val)
|
||||
{
|
||||
unsigned int scaling = pca963x->chip->chipdef->scaling;
|
||||
unsigned int scaling = led->chip->chipdef->scaling;
|
||||
|
||||
return scaling ? DIV_ROUND_CLOSEST(val * scaling, 1000) : val;
|
||||
}
|
||||
|
||||
static int pca963x_blink_set(struct led_classdev *led_cdev,
|
||||
unsigned long *delay_on, unsigned long *delay_off)
|
||||
unsigned long *delay_on, unsigned long *delay_off)
|
||||
{
|
||||
struct pca963x_led *pca963x;
|
||||
unsigned long time_on, time_off, period;
|
||||
struct pca963x_led *led;
|
||||
u8 gdc, gfrq;
|
||||
|
||||
pca963x = container_of(led_cdev, struct pca963x_led, led_cdev);
|
||||
led = container_of(led_cdev, struct pca963x_led, led_cdev);
|
||||
|
||||
time_on = *delay_on;
|
||||
time_off = *delay_off;
|
||||
|
@ -242,14 +247,14 @@ static int pca963x_blink_set(struct led_classdev *led_cdev,
|
|||
time_off = 500;
|
||||
}
|
||||
|
||||
period = pca963x_period_scale(pca963x, time_on + time_off);
|
||||
period = pca963x_period_scale(led, time_on + time_off);
|
||||
|
||||
/* If period not supported by hardware, default to someting sane. */
|
||||
if ((period < PCA963X_BLINK_PERIOD_MIN) ||
|
||||
(period > PCA963X_BLINK_PERIOD_MAX)) {
|
||||
time_on = 500;
|
||||
time_off = 500;
|
||||
period = pca963x_period_scale(pca963x, 1000);
|
||||
period = pca963x_period_scale(led, 1000);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -257,7 +262,7 @@ static int pca963x_blink_set(struct led_classdev *led_cdev,
|
|||
* (time_on / period) = (GDC / 256) ->
|
||||
* GDC = ((time_on * 256) / period)
|
||||
*/
|
||||
gdc = (pca963x_period_scale(pca963x, time_on) * 256) / period;
|
||||
gdc = (pca963x_period_scale(led, time_on) * 256) / period;
|
||||
|
||||
/*
|
||||
* From manual: period = ((GFRQ + 1) / 24) in seconds.
|
||||
|
@ -266,10 +271,10 @@ static int pca963x_blink_set(struct led_classdev *led_cdev,
|
|||
*/
|
||||
gfrq = (period * 24 / 1000) - 1;
|
||||
|
||||
pca963x->gdc = gdc;
|
||||
pca963x->gfrq = gfrq;
|
||||
led->gdc = gdc;
|
||||
led->gfrq = gfrq;
|
||||
|
||||
pca963x_blink(pca963x);
|
||||
pca963x_blink(led);
|
||||
|
||||
*delay_on = time_on;
|
||||
*delay_off = time_off;
|
||||
|
@ -277,72 +282,84 @@ static int pca963x_blink_set(struct led_classdev *led_cdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct pca963x_platform_data *
|
||||
pca963x_get_pdata(struct i2c_client *client, struct pca963x_chipdef *chip)
|
||||
static int pca963x_register_leds(struct i2c_client *client,
|
||||
struct pca963x *chip)
|
||||
{
|
||||
struct pca963x_platform_data *pdata;
|
||||
struct led_info *pca963x_leds;
|
||||
struct pca963x_chipdef *chipdef = chip->chipdef;
|
||||
struct pca963x_led *led = chip->leds;
|
||||
struct device *dev = &client->dev;
|
||||
struct fwnode_handle *child;
|
||||
int count;
|
||||
bool hw_blink;
|
||||
s32 mode2;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
count = device_get_child_node_count(&client->dev);
|
||||
if (!count || count > chip->n_leds)
|
||||
return ERR_PTR(-ENODEV);
|
||||
if (device_property_read_u32(dev, "nxp,period-scale",
|
||||
&chipdef->scaling))
|
||||
chipdef->scaling = 1000;
|
||||
|
||||
pca963x_leds = devm_kcalloc(&client->dev,
|
||||
chip->n_leds, sizeof(struct led_info), GFP_KERNEL);
|
||||
if (!pca963x_leds)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
hw_blink = device_property_read_bool(dev, "nxp,hw-blink");
|
||||
|
||||
device_for_each_child_node(&client->dev, child) {
|
||||
struct led_info led = {};
|
||||
u32 reg;
|
||||
int res;
|
||||
|
||||
res = fwnode_property_read_u32(child, "reg", ®);
|
||||
if ((res != 0) || (reg >= chip->n_leds))
|
||||
continue;
|
||||
|
||||
res = fwnode_property_read_string(child, "label", &led.name);
|
||||
if ((res != 0) && is_of_node(child))
|
||||
led.name = to_of_node(child)->name;
|
||||
|
||||
fwnode_property_read_string(child, "linux,default-trigger",
|
||||
&led.default_trigger);
|
||||
|
||||
pca963x_leds[reg] = led;
|
||||
}
|
||||
pdata = devm_kzalloc(&client->dev,
|
||||
sizeof(struct pca963x_platform_data), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pdata->leds.leds = pca963x_leds;
|
||||
pdata->leds.num_leds = chip->n_leds;
|
||||
mode2 = i2c_smbus_read_byte_data(client, PCA963X_MODE2);
|
||||
if (mode2 < 0)
|
||||
return mode2;
|
||||
|
||||
/* default to open-drain unless totem pole (push-pull) is specified */
|
||||
if (device_property_read_bool(&client->dev, "nxp,totem-pole"))
|
||||
pdata->outdrv = PCA963X_TOTEM_POLE;
|
||||
if (device_property_read_bool(dev, "nxp,totem-pole"))
|
||||
mode2 |= PCA963X_MODE2_OUTDRV;
|
||||
else
|
||||
pdata->outdrv = PCA963X_OPEN_DRAIN;
|
||||
|
||||
/* default to software blinking unless hardware blinking is specified */
|
||||
if (device_property_read_bool(&client->dev, "nxp,hw-blink"))
|
||||
pdata->blink_type = PCA963X_HW_BLINK;
|
||||
else
|
||||
pdata->blink_type = PCA963X_SW_BLINK;
|
||||
|
||||
if (device_property_read_u32(&client->dev, "nxp,period-scale",
|
||||
&chip->scaling))
|
||||
chip->scaling = 1000;
|
||||
mode2 &= ~PCA963X_MODE2_OUTDRV;
|
||||
|
||||
/* default to non-inverted output, unless inverted is specified */
|
||||
if (device_property_read_bool(&client->dev, "nxp,inverted-out"))
|
||||
pdata->dir = PCA963X_INVERTED;
|
||||
if (device_property_read_bool(dev, "nxp,inverted-out"))
|
||||
mode2 |= PCA963X_MODE2_INVRT;
|
||||
else
|
||||
pdata->dir = PCA963X_NORMAL;
|
||||
mode2 &= ~PCA963X_MODE2_INVRT;
|
||||
|
||||
return pdata;
|
||||
ret = i2c_smbus_write_byte_data(client, PCA963X_MODE2, mode2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
struct led_init_data init_data = {};
|
||||
char default_label[32];
|
||||
|
||||
ret = fwnode_property_read_u32(child, "reg", ®);
|
||||
if (ret || reg >= chipdef->n_leds) {
|
||||
dev_err(dev, "Invalid 'reg' property for node %pfw\n",
|
||||
child);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
goto err;
|
||||
}
|
||||
|
||||
++led;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
fwnode_handle_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_pca963x_match[] = {
|
||||
|
@ -355,119 +372,40 @@ static const struct of_device_id of_pca963x_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, of_pca963x_match);
|
||||
|
||||
static int pca963x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct pca963x *pca963x_chip;
|
||||
struct pca963x_led *pca963x;
|
||||
struct pca963x_platform_data *pdata;
|
||||
struct pca963x_chipdef *chip;
|
||||
int i, err;
|
||||
struct device *dev = &client->dev;
|
||||
struct pca963x_chipdef *chipdef;
|
||||
struct pca963x *chip;
|
||||
int i, count;
|
||||
|
||||
chip = &pca963x_chipdefs[id->driver_data];
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
chipdef = &pca963x_chipdefs[id->driver_data];
|
||||
|
||||
if (!pdata) {
|
||||
pdata = pca963x_get_pdata(client, chip);
|
||||
if (IS_ERR(pdata)) {
|
||||
dev_warn(&client->dev, "could not parse configuration\n");
|
||||
pdata = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata && (pdata->leds.num_leds < 1 ||
|
||||
pdata->leds.num_leds > chip->n_leds)) {
|
||||
dev_err(&client->dev, "board info must claim 1-%d LEDs",
|
||||
chip->n_leds);
|
||||
count = device_get_child_node_count(dev);
|
||||
if (!count || count > chipdef->n_leds) {
|
||||
dev_err(dev, "Node %pfw must define between 1 and %d LEDs\n",
|
||||
dev_fwnode(dev), chipdef->n_leds);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pca963x_chip = devm_kzalloc(&client->dev, sizeof(*pca963x_chip),
|
||||
GFP_KERNEL);
|
||||
if (!pca963x_chip)
|
||||
return -ENOMEM;
|
||||
pca963x = devm_kcalloc(&client->dev, chip->n_leds, sizeof(*pca963x),
|
||||
GFP_KERNEL);
|
||||
if (!pca963x)
|
||||
chip = devm_kzalloc(dev, struct_size(chip, leds, count), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, pca963x_chip);
|
||||
i2c_set_clientdata(client, chip);
|
||||
|
||||
mutex_init(&pca963x_chip->mutex);
|
||||
pca963x_chip->chipdef = chip;
|
||||
pca963x_chip->client = client;
|
||||
pca963x_chip->leds = pca963x;
|
||||
mutex_init(&chip->mutex);
|
||||
chip->chipdef = chipdef;
|
||||
chip->client = client;
|
||||
|
||||
/* Turn off LEDs by default*/
|
||||
for (i = 0; i < chip->n_leds / 4; i++)
|
||||
i2c_smbus_write_byte_data(client, chip->ledout_base + i, 0x00);
|
||||
|
||||
for (i = 0; i < chip->n_leds; i++) {
|
||||
pca963x[i].led_num = i;
|
||||
pca963x[i].chip = pca963x_chip;
|
||||
|
||||
/* Platform data can specify LED names and default triggers */
|
||||
if (pdata && i < pdata->leds.num_leds) {
|
||||
if (pdata->leds.leds[i].name)
|
||||
snprintf(pca963x[i].name,
|
||||
sizeof(pca963x[i].name), "pca963x:%s",
|
||||
pdata->leds.leds[i].name);
|
||||
if (pdata->leds.leds[i].default_trigger)
|
||||
pca963x[i].led_cdev.default_trigger =
|
||||
pdata->leds.leds[i].default_trigger;
|
||||
}
|
||||
if (!pdata || i >= pdata->leds.num_leds ||
|
||||
!pdata->leds.leds[i].name)
|
||||
snprintf(pca963x[i].name, sizeof(pca963x[i].name),
|
||||
"pca963x:%d:%.2x:%d", client->adapter->nr,
|
||||
client->addr, i);
|
||||
|
||||
pca963x[i].led_cdev.name = pca963x[i].name;
|
||||
pca963x[i].led_cdev.brightness_set_blocking = pca963x_led_set;
|
||||
|
||||
if (pdata && pdata->blink_type == PCA963X_HW_BLINK)
|
||||
pca963x[i].led_cdev.blink_set = pca963x_blink_set;
|
||||
|
||||
err = led_classdev_register(&client->dev, &pca963x[i].led_cdev);
|
||||
if (err < 0)
|
||||
goto exit;
|
||||
}
|
||||
for (i = 0; i < chipdef->n_leds / 4; i++)
|
||||
i2c_smbus_write_byte_data(client, chipdef->ledout_base + i, 0x00);
|
||||
|
||||
/* Disable LED all-call address, and power down initially */
|
||||
i2c_smbus_write_byte_data(client, PCA963X_MODE1, BIT(4));
|
||||
|
||||
if (pdata) {
|
||||
u8 mode2 = i2c_smbus_read_byte_data(pca963x->chip->client,
|
||||
PCA963X_MODE2);
|
||||
/* Configure output: open-drain or totem pole (push-pull) */
|
||||
if (pdata->outdrv == PCA963X_OPEN_DRAIN)
|
||||
mode2 &= ~PCA963X_MODE2_OUTDRV;
|
||||
else
|
||||
mode2 |= PCA963X_MODE2_OUTDRV;
|
||||
/* Configure direction: normal or inverted */
|
||||
if (pdata->dir == PCA963X_INVERTED)
|
||||
mode2 |= PCA963X_MODE2_INVRT;
|
||||
i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2,
|
||||
mode2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
while (i--)
|
||||
led_classdev_unregister(&pca963x[i].led_cdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pca963x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct pca963x *pca963x = i2c_get_clientdata(client);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pca963x->chipdef->n_leds; i++)
|
||||
led_classdev_unregister(&pca963x->leds[i].led_cdev);
|
||||
|
||||
return 0;
|
||||
return pca963x_register_leds(client, chip);
|
||||
}
|
||||
|
||||
static struct i2c_driver pca963x_driver = {
|
||||
|
@ -476,7 +414,6 @@ static struct i2c_driver pca963x_driver = {
|
|||
.of_match_table = of_pca963x_match,
|
||||
},
|
||||
.probe = pca963x_probe,
|
||||
.remove = pca963x_remove,
|
||||
.id_table = pca963x_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -87,36 +87,36 @@ static enum led_brightness pm8058_led_get(struct led_classdev *cled)
|
|||
|
||||
static int pm8058_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct led_init_data init_data = {};
|
||||
struct device *dev = &pdev->dev;
|
||||
struct pm8058_led *led;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
struct regmap *map;
|
||||
const char *state;
|
||||
enum led_brightness maxbright;
|
||||
|
||||
led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
|
||||
led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
|
||||
if (!led)
|
||||
return -ENOMEM;
|
||||
|
||||
led->ledtype = (u32)(unsigned long)of_device_get_match_data(&pdev->dev);
|
||||
led->ledtype = (u32)(unsigned long)of_device_get_match_data(dev);
|
||||
|
||||
map = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
map = dev_get_regmap(dev->parent, NULL);
|
||||
if (!map) {
|
||||
dev_err(&pdev->dev, "Parent regmap unavailable.\n");
|
||||
dev_err(dev, "Parent regmap unavailable.\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
led->map = map;
|
||||
|
||||
np = dev_of_node(dev);
|
||||
|
||||
ret = of_property_read_u32(np, "reg", &led->reg);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "no register offset specified\n");
|
||||
dev_err(dev, "no register offset specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Use label else node name */
|
||||
led->cdev.name = of_get_property(np, "label", NULL) ? : np->name;
|
||||
led->cdev.default_trigger =
|
||||
of_get_property(np, "linux,default-trigger", NULL);
|
||||
led->cdev.brightness_set = pm8058_led_set;
|
||||
led->cdev.brightness_get = pm8058_led_get;
|
||||
if (led->ledtype == PM8058_LED_TYPE_COMMON)
|
||||
|
@ -142,14 +142,13 @@ static int pm8058_led_probe(struct platform_device *pdev)
|
|||
led->ledtype == PM8058_LED_TYPE_FLASH)
|
||||
led->cdev.flags = LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = devm_led_classdev_register(&pdev->dev, &led->cdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to register led \"%s\"\n",
|
||||
led->cdev.name);
|
||||
return ret;
|
||||
}
|
||||
init_data.fwnode = of_fwnode_handle(np);
|
||||
|
||||
return 0;
|
||||
ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to register LED for %pOF\n", np);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id pm8058_leds_id_table[] = {
|
||||
|
|
|
@ -250,7 +250,7 @@ static int powernv_led_classdev(struct platform_device *pdev,
|
|||
struct powernv_led_data *powernv_led;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
for_each_child_of_node(led_node, np) {
|
||||
for_each_available_child_of_node(led_node, np) {
|
||||
p = of_find_property(np, "led-types", NULL);
|
||||
|
||||
while ((cur = of_prop_next_string(p, cur)) != NULL) {
|
||||
|
|
|
@ -20,16 +20,10 @@
|
|||
|
||||
struct led_pwm {
|
||||
const char *name;
|
||||
const char *default_trigger;
|
||||
u8 active_low;
|
||||
unsigned int max_brightness;
|
||||
};
|
||||
|
||||
struct led_pwm_platform_data {
|
||||
int num_leds;
|
||||
struct led_pwm *leds;
|
||||
};
|
||||
|
||||
struct led_pwm_data {
|
||||
struct led_classdev cdev;
|
||||
struct pwm_device *pwm;
|
||||
|
@ -61,36 +55,31 @@ static int led_pwm_set(struct led_classdev *led_cdev,
|
|||
return pwm_apply_state(led_dat->pwm, &led_dat->pwmstate);
|
||||
}
|
||||
|
||||
__attribute__((nonnull))
|
||||
static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
|
||||
struct led_pwm *led, struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct led_pwm_data *led_data = &priv->leds[priv->num_leds];
|
||||
struct led_init_data init_data = { .fwnode = fwnode };
|
||||
int ret;
|
||||
|
||||
led_data->active_low = led->active_low;
|
||||
led_data->cdev.name = led->name;
|
||||
led_data->cdev.default_trigger = led->default_trigger;
|
||||
led_data->cdev.brightness = LED_OFF;
|
||||
led_data->cdev.max_brightness = led->max_brightness;
|
||||
led_data->cdev.flags = LED_CORE_SUSPENDRESUME;
|
||||
|
||||
if (fwnode)
|
||||
led_data->pwm = devm_fwnode_pwm_get(dev, fwnode, NULL);
|
||||
else
|
||||
led_data->pwm = devm_pwm_get(dev, led->name);
|
||||
if (IS_ERR(led_data->pwm)) {
|
||||
ret = PTR_ERR(led_data->pwm);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "unable to request PWM for %s: %d\n",
|
||||
led->name, ret);
|
||||
return ret;
|
||||
}
|
||||
led_data->pwm = devm_fwnode_pwm_get(dev, fwnode, NULL);
|
||||
if (IS_ERR(led_data->pwm))
|
||||
return dev_err_probe(dev, PTR_ERR(led_data->pwm),
|
||||
"unable to request PWM for %s\n",
|
||||
led->name);
|
||||
|
||||
led_data->cdev.brightness_set_blocking = led_pwm_set;
|
||||
|
||||
pwm_init_state(led_data->pwm, &led_data->pwmstate);
|
||||
|
||||
ret = devm_led_classdev_register(dev, &led_data->cdev);
|
||||
ret = devm_led_classdev_register_ext(dev, &led_data->cdev, &init_data);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register PWM led for %s: %d\n",
|
||||
led->name, ret);
|
||||
|
@ -126,9 +115,6 @@ static int led_pwm_create_fwnode(struct device *dev, struct led_pwm_priv *priv)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
fwnode_property_read_string(fwnode, "linux,default-trigger",
|
||||
&led.default_trigger);
|
||||
|
||||
led.active_low = fwnode_property_read_bool(fwnode,
|
||||
"active-low");
|
||||
fwnode_property_read_u32(fwnode, "max-brightness",
|
||||
|
@ -146,15 +132,11 @@ static int led_pwm_create_fwnode(struct device *dev, struct led_pwm_priv *priv)
|
|||
|
||||
static int led_pwm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct led_pwm_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct led_pwm_priv *priv;
|
||||
int count, i;
|
||||
int ret = 0;
|
||||
int count;
|
||||
|
||||
if (pdata)
|
||||
count = pdata->num_leds;
|
||||
else
|
||||
count = device_get_child_node_count(&pdev->dev);
|
||||
count = device_get_child_node_count(&pdev->dev);
|
||||
|
||||
if (!count)
|
||||
return -EINVAL;
|
||||
|
@ -164,16 +146,7 @@ static int led_pwm_probe(struct platform_device *pdev)
|
|||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
if (pdata) {
|
||||
for (i = 0; i < count; i++) {
|
||||
ret = led_pwm_add(&pdev->dev, priv, &pdata->leds[i],
|
||||
NULL);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ret = led_pwm_create_fwnode(&pdev->dev, priv);
|
||||
}
|
||||
ret = led_pwm_create_fwnode(&pdev->dev, priv);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/platform_data/leds-s3c24xx.h>
|
||||
|
||||
#include <mach/regs-gpio.h>
|
||||
|
||||
/* our context */
|
||||
|
||||
struct s3c24xx_gpio_led {
|
||||
|
|
|
@ -276,12 +276,12 @@ 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, *child;
|
||||
struct device_node *np = dev_of_node(dev), *child;
|
||||
struct sc27xx_led_priv *priv;
|
||||
u32 base, count, reg;
|
||||
int err;
|
||||
|
||||
count = of_get_child_count(np);
|
||||
count = of_get_available_child_count(np);
|
||||
if (!count || count > SC27XX_LEDS_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -305,7 +305,7 @@ static int sc27xx_led_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
for_each_available_child_of_node(np, child) {
|
||||
err = of_property_read_u32(child, "reg", ®);
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
|
|
|
@ -195,30 +195,21 @@ static int sgm3140_probe(struct platform_device *pdev)
|
|||
|
||||
priv->flash_gpio = devm_gpiod_get(&pdev->dev, "flash", GPIOD_OUT_LOW);
|
||||
ret = PTR_ERR_OR_ZERO(priv->flash_gpio);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to request flash gpio: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"Failed to request flash gpio\n");
|
||||
|
||||
priv->enable_gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW);
|
||||
ret = PTR_ERR_OR_ZERO(priv->enable_gpio);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to request enable gpio: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"Failed to request enable gpio\n");
|
||||
|
||||
priv->vin_regulator = devm_regulator_get(&pdev->dev, "vin");
|
||||
ret = PTR_ERR_OR_ZERO(priv->vin_regulator);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to request regulator: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"Failed to request regulator\n");
|
||||
|
||||
child_node = fwnode_get_next_available_child_node(pdev->dev.fwnode,
|
||||
NULL);
|
||||
|
@ -316,5 +307,5 @@ static struct platform_driver sgm3140_driver = {
|
|||
module_platform_driver(sgm3140_driver);
|
||||
|
||||
MODULE_AUTHOR("Luca Weiss <luca@z3ntu.xyz>");
|
||||
MODULE_DESCRIPTION("SG Micro SGM3140 charge pump led driver");
|
||||
MODULE_DESCRIPTION("SG Micro SGM3140 charge pump LED driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -80,7 +80,6 @@ static int spi_byte_brightness_set_blocking(struct led_classdev *dev,
|
|||
|
||||
static int spi_byte_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct of_device_id *of_dev_id;
|
||||
struct device_node *child;
|
||||
struct device *dev = &spi->dev;
|
||||
struct spi_byte_led *led;
|
||||
|
@ -88,15 +87,11 @@ static int spi_byte_probe(struct spi_device *spi)
|
|||
const char *state;
|
||||
int ret;
|
||||
|
||||
of_dev_id = of_match_device(spi_byte_dt_ids, dev);
|
||||
if (!of_dev_id)
|
||||
return -EINVAL;
|
||||
|
||||
if (of_get_child_count(dev->of_node) != 1) {
|
||||
if (of_get_available_child_count(dev_of_node(dev)) != 1) {
|
||||
dev_err(dev, "Device must have exactly one LED sub-node.");
|
||||
return -EINVAL;
|
||||
}
|
||||
child = of_get_next_child(dev->of_node, NULL);
|
||||
child = of_get_next_available_child(dev_of_node(dev), NULL);
|
||||
|
||||
led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
|
||||
if (!led)
|
||||
|
@ -106,7 +101,7 @@ static int spi_byte_probe(struct spi_device *spi)
|
|||
strlcpy(led->name, name, sizeof(led->name));
|
||||
led->spi = spi;
|
||||
mutex_init(&led->mutex);
|
||||
led->cdef = of_dev_id->data;
|
||||
led->cdef = device_get_match_data(dev);
|
||||
led->ldev.name = led->name;
|
||||
led->ldev.brightness = LED_OFF;
|
||||
led->ldev.max_brightness = led->cdef->max_value - led->cdef->off_value;
|
||||
|
|
|
@ -55,8 +55,9 @@ static void syscon_led_set(struct led_classdev *led_cdev,
|
|||
|
||||
static int syscon_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct led_init_data init_data = {};
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *np = dev_of_node(dev);
|
||||
struct device *parent;
|
||||
struct regmap *map;
|
||||
struct syscon_led *sled;
|
||||
|
@ -68,7 +69,7 @@ static int syscon_led_probe(struct platform_device *pdev)
|
|||
dev_err(dev, "no parent for syscon LED\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
map = syscon_node_to_regmap(parent->of_node);
|
||||
map = syscon_node_to_regmap(dev_of_node(parent));
|
||||
if (IS_ERR(map)) {
|
||||
dev_err(dev, "no regmap for syscon LED parent\n");
|
||||
return PTR_ERR(map);
|
||||
|
@ -84,10 +85,6 @@ static int syscon_led_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
if (of_property_read_u32(np, "mask", &sled->mask))
|
||||
return -EINVAL;
|
||||
sled->cdev.name =
|
||||
of_get_property(np, "label", NULL) ? : np->name;
|
||||
sled->cdev.default_trigger =
|
||||
of_get_property(np, "linux,default-trigger", NULL);
|
||||
|
||||
state = of_get_property(np, "default-state", NULL);
|
||||
if (state) {
|
||||
|
@ -115,7 +112,9 @@ static int syscon_led_probe(struct platform_device *pdev)
|
|||
}
|
||||
sled->cdev.brightness_set = syscon_led_set;
|
||||
|
||||
ret = devm_led_classdev_register(dev, &sled->cdev);
|
||||
init_data.fwnode = of_fwnode_handle(np);
|
||||
|
||||
ret = devm_led_classdev_register_ext(dev, &sled->cdev, &init_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -69,23 +69,6 @@
|
|||
* defaulted. Similarly the banks know if each time was explicit or a
|
||||
* default. Defaults are permitted to be changed freely - they are
|
||||
* not recognised when matching.
|
||||
*
|
||||
*
|
||||
* An led-tca6507 device must be provided with platform data or
|
||||
* configured via devicetree.
|
||||
*
|
||||
* The platform-data lists for each output: the name, default trigger,
|
||||
* and whether the signal is being used as a GPIO rather than an LED.
|
||||
* 'struct led_plaform_data' is used for this. If 'name' is NULL, the
|
||||
* output isn't used. If 'flags' is TCA6507_MAKE_GPIO, the output is
|
||||
* a GPO. The "struct led_platform_data" can be embedded in a "struct
|
||||
* tca6507_platform_data" which adds a 'gpio_base' for the GPIOs, and
|
||||
* a 'setup' callback which is called once the GPIOs are available.
|
||||
*
|
||||
* When configured via devicetree there is one child for each output.
|
||||
* The "reg" determines the output number and "compatible" determines
|
||||
* whether it is an LED or a GPIO. "linux,default-trigger" can set a
|
||||
* default trigger.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -94,9 +77,8 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/leds-tca6507.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/* LED select registers determine the source that drives LED outputs */
|
||||
#define TCA6507_LS_LED_OFF 0x0 /* Output HI-Z (off) */
|
||||
|
@ -108,6 +90,15 @@
|
|||
#define TCA6507_LS_BLINK0 0x6 /* Blink at Bank0 rate */
|
||||
#define TCA6507_LS_BLINK1 0x7 /* Blink at Bank1 rate */
|
||||
|
||||
struct tca6507_platform_data {
|
||||
struct led_platform_data leds;
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
int gpio_base;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define TCA6507_MAKE_GPIO 1
|
||||
|
||||
enum {
|
||||
BANK0,
|
||||
BANK1,
|
||||
|
@ -189,7 +180,6 @@ struct tca6507_chip {
|
|||
} leds[NUM_LEDS];
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
struct gpio_chip gpio;
|
||||
const char *gpio_name[NUM_LEDS];
|
||||
int gpio_map[NUM_LEDS];
|
||||
#endif
|
||||
};
|
||||
|
@ -628,7 +618,7 @@ static int tca6507_gpio_direction_output(struct gpio_chip *gc,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tca6507_probe_gpios(struct i2c_client *client,
|
||||
static int tca6507_probe_gpios(struct device *dev,
|
||||
struct tca6507_chip *tca,
|
||||
struct tca6507_platform_data *pdata)
|
||||
{
|
||||
|
@ -639,7 +629,6 @@ static int tca6507_probe_gpios(struct i2c_client *client,
|
|||
for (i = 0; i < NUM_LEDS; i++)
|
||||
if (pdata->leds.leds[i].name && pdata->leds.leds[i].flags) {
|
||||
/* Configure as a gpio */
|
||||
tca->gpio_name[gpios] = pdata->leds.leds[i].name;
|
||||
tca->gpio_map[gpios] = i;
|
||||
gpios++;
|
||||
}
|
||||
|
@ -648,23 +637,20 @@ static int tca6507_probe_gpios(struct i2c_client *client,
|
|||
return 0;
|
||||
|
||||
tca->gpio.label = "gpio-tca6507";
|
||||
tca->gpio.names = tca->gpio_name;
|
||||
tca->gpio.ngpio = gpios;
|
||||
tca->gpio.base = pdata->gpio_base;
|
||||
tca->gpio.owner = THIS_MODULE;
|
||||
tca->gpio.direction_output = tca6507_gpio_direction_output;
|
||||
tca->gpio.set = tca6507_gpio_set_value;
|
||||
tca->gpio.parent = &client->dev;
|
||||
tca->gpio.parent = dev;
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
tca->gpio.of_node = of_node_get(client->dev.of_node);
|
||||
tca->gpio.of_node = of_node_get(dev_of_node(dev));
|
||||
#endif
|
||||
err = gpiochip_add_data(&tca->gpio, tca);
|
||||
if (err) {
|
||||
tca->gpio.ngpio = 0;
|
||||
return err;
|
||||
}
|
||||
if (pdata->setup)
|
||||
pdata->setup(tca->gpio.base, tca->gpio.ngpio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -674,7 +660,7 @@ static void tca6507_remove_gpio(struct tca6507_chip *tca)
|
|||
gpiochip_remove(&tca->gpio);
|
||||
}
|
||||
#else /* CONFIG_GPIOLIB */
|
||||
static int tca6507_probe_gpios(struct i2c_client *client,
|
||||
static int tca6507_probe_gpios(struct device *dev,
|
||||
struct tca6507_chip *tca,
|
||||
struct tca6507_platform_data *pdata)
|
||||
{
|
||||
|
@ -685,44 +671,50 @@ static void tca6507_remove_gpio(struct tca6507_chip *tca)
|
|||
}
|
||||
#endif /* CONFIG_GPIOLIB */
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct tca6507_platform_data *
|
||||
tca6507_led_dt_init(struct i2c_client *client)
|
||||
tca6507_led_dt_init(struct device *dev)
|
||||
{
|
||||
struct device_node *np = client->dev.of_node, *child;
|
||||
struct tca6507_platform_data *pdata;
|
||||
struct fwnode_handle *child;
|
||||
struct led_info *tca_leds;
|
||||
int count;
|
||||
|
||||
count = of_get_child_count(np);
|
||||
count = device_get_child_node_count(dev);
|
||||
if (!count || count > NUM_LEDS)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
tca_leds = devm_kcalloc(&client->dev,
|
||||
NUM_LEDS, sizeof(struct led_info), GFP_KERNEL);
|
||||
tca_leds = devm_kcalloc(dev, NUM_LEDS, sizeof(struct led_info),
|
||||
GFP_KERNEL);
|
||||
if (!tca_leds)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
device_for_each_child_node(dev, child) {
|
||||
struct led_info led;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
led.name =
|
||||
of_get_property(child, "label", NULL) ? : child->name;
|
||||
led.default_trigger =
|
||||
of_get_property(child, "linux,default-trigger", NULL);
|
||||
if (fwnode_property_read_string(child, "label", &led.name))
|
||||
led.name = fwnode_get_name(child);
|
||||
|
||||
fwnode_property_read_string(child, "linux,default-trigger",
|
||||
&led.default_trigger);
|
||||
|
||||
led.flags = 0;
|
||||
if (of_property_match_string(child, "compatible", "gpio") >= 0)
|
||||
if (fwnode_property_match_string(child, "compatible",
|
||||
"gpio") >= 0)
|
||||
led.flags |= TCA6507_MAKE_GPIO;
|
||||
ret = of_property_read_u32(child, "reg", ®);
|
||||
if (ret != 0 || reg >= NUM_LEDS)
|
||||
continue;
|
||||
|
||||
ret = fwnode_property_read_u32(child, "reg", ®);
|
||||
if (ret || reg >= NUM_LEDS) {
|
||||
fwnode_handle_put(child);
|
||||
return ERR_PTR(ret ? : -EINVAL);
|
||||
}
|
||||
|
||||
tca_leds[reg] = led;
|
||||
}
|
||||
pdata = devm_kzalloc(&client->dev,
|
||||
sizeof(struct tca6507_platform_data), GFP_KERNEL);
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(struct tca6507_platform_data),
|
||||
GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
@ -731,48 +723,37 @@ tca6507_led_dt_init(struct i2c_client *client)
|
|||
#ifdef CONFIG_GPIOLIB
|
||||
pdata->gpio_base = -1;
|
||||
#endif
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_tca6507_leds_match[] = {
|
||||
static const struct of_device_id __maybe_unused of_tca6507_leds_match[] = {
|
||||
{ .compatible = "ti,tca6507", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_tca6507_leds_match);
|
||||
|
||||
#else
|
||||
static struct tca6507_platform_data *
|
||||
tca6507_led_dt_init(struct i2c_client *client)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int tca6507_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tca6507_chip *tca;
|
||||
struct device *dev = &client->dev;
|
||||
struct i2c_adapter *adapter;
|
||||
struct tca6507_chip *tca;
|
||||
struct tca6507_platform_data *pdata;
|
||||
int err;
|
||||
int i = 0;
|
||||
|
||||
adapter = client->adapter;
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
|
||||
return -EIO;
|
||||
|
||||
if (!pdata || pdata->leds.num_leds != NUM_LEDS) {
|
||||
pdata = tca6507_led_dt_init(client);
|
||||
if (IS_ERR(pdata)) {
|
||||
dev_err(&client->dev, "Need %d entries in platform-data list\n",
|
||||
NUM_LEDS);
|
||||
return PTR_ERR(pdata);
|
||||
}
|
||||
pdata = tca6507_led_dt_init(dev);
|
||||
if (IS_ERR(pdata)) {
|
||||
dev_err(dev, "Need %d entries in platform-data list\n", NUM_LEDS);
|
||||
return PTR_ERR(pdata);
|
||||
}
|
||||
tca = devm_kzalloc(&client->dev, sizeof(*tca), GFP_KERNEL);
|
||||
tca = devm_kzalloc(dev, sizeof(*tca), GFP_KERNEL);
|
||||
if (!tca)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -793,13 +774,12 @@ static int tca6507_probe(struct i2c_client *client,
|
|||
l->led_cdev.brightness_set = tca6507_brightness_set;
|
||||
l->led_cdev.blink_set = tca6507_blink_set;
|
||||
l->bank = -1;
|
||||
err = led_classdev_register(&client->dev,
|
||||
&l->led_cdev);
|
||||
err = led_classdev_register(dev, &l->led_cdev);
|
||||
if (err < 0)
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
err = tca6507_probe_gpios(client, tca, pdata);
|
||||
err = tca6507_probe_gpios(dev, tca, pdata);
|
||||
if (err)
|
||||
goto exit;
|
||||
/* set all registers to known state - zero */
|
||||
|
|
|
@ -148,22 +148,17 @@ static int
|
|||
tlc591xx_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device_node *np = client->dev.of_node, *child;
|
||||
struct device_node *np = dev_of_node(&client->dev), *child;
|
||||
struct device *dev = &client->dev;
|
||||
const struct of_device_id *match;
|
||||
const struct tlc591xx *tlc591xx;
|
||||
struct tlc591xx_priv *priv;
|
||||
int err, count, reg;
|
||||
|
||||
match = of_match_device(of_tlc591xx_leds_match, dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
tlc591xx = match->data;
|
||||
tlc591xx = device_get_match_data(dev);
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
count = of_get_child_count(np);
|
||||
count = of_get_available_child_count(np);
|
||||
if (!count || count > tlc591xx->max_leds)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -185,7 +180,7 @@ tlc591xx_probe(struct i2c_client *client,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
for_each_available_child_of_node(np, child) {
|
||||
struct tlc591xx_led *led;
|
||||
struct led_init_data init_data = {};
|
||||
|
||||
|
@ -204,9 +199,6 @@ tlc591xx_probe(struct i2c_client *client,
|
|||
led = &priv->leds[reg];
|
||||
|
||||
led->active = true;
|
||||
led->ldev.default_trigger =
|
||||
of_get_property(child, "linux,default-trigger", NULL);
|
||||
|
||||
led->priv = priv;
|
||||
led->led_no = reg;
|
||||
led->ldev.brightness_set_blocking = tlc591xx_brightness_set;
|
||||
|
@ -214,10 +206,10 @@ tlc591xx_probe(struct i2c_client *client,
|
|||
err = devm_led_classdev_register_ext(dev, &led->ldev,
|
||||
&init_data);
|
||||
if (err < 0) {
|
||||
if (err != -EPROBE_DEFER)
|
||||
dev_err(dev, "couldn't register LED %s\n",
|
||||
led->ldev.name);
|
||||
return err;
|
||||
of_node_put(child);
|
||||
return dev_err_probe(dev, err,
|
||||
"couldn't register LED %s\n",
|
||||
led->ldev.name);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -121,8 +121,6 @@ static int omnia_led_register(struct i2c_client *client, struct omnia_led *led,
|
|||
cdev->max_brightness = 255;
|
||||
cdev->brightness_set_blocking = omnia_led_brightness_set_blocking;
|
||||
|
||||
of_property_read_string(np, "linux,default-trigger", &cdev->default_trigger);
|
||||
|
||||
/* put the LED into software mode */
|
||||
ret = i2c_smbus_write_byte_data(client, CMD_LED_MODE,
|
||||
CMD_LED_MODE_LED(led->reg) |
|
||||
|
@ -210,7 +208,7 @@ static int omnia_leds_probe(struct i2c_client *client,
|
|||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device_node *np = dev->of_node, *child;
|
||||
struct device_node *np = dev_of_node(dev), *child;
|
||||
struct omnia_leds *leds;
|
||||
struct omnia_led *led;
|
||||
int ret, count;
|
||||
|
@ -236,8 +234,10 @@ static int omnia_leds_probe(struct i2c_client *client,
|
|||
led = &leds->leds[0];
|
||||
for_each_available_child_of_node(np, child) {
|
||||
ret = omnia_led_register(client, led, child);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
led += ret;
|
||||
}
|
||||
|
|
|
@ -2,14 +2,18 @@
|
|||
/*
|
||||
* ledtrig-cpu.c - LED trigger based on CPU activity
|
||||
*
|
||||
* This LED trigger will be registered for each possible CPU and named as
|
||||
* cpu0, cpu1, cpu2, cpu3, etc.
|
||||
* This LED trigger will be registered for first 8 CPUs and named
|
||||
* as cpu0..cpu7. There's additional trigger called cpu that
|
||||
* is on when any CPU is active.
|
||||
*
|
||||
* If you want support for arbitrary number of CPUs, make it one trigger,
|
||||
* with additional sysfs file selecting which CPU to watch.
|
||||
*
|
||||
* It can be bound to any LED just like other triggers using either a
|
||||
* board file or via sysfs interface.
|
||||
*
|
||||
* An API named ledtrig_cpu is exported for any user, who want to add CPU
|
||||
* activity indication in their code
|
||||
* activity indication in their code.
|
||||
*
|
||||
* Copyright 2011 Linus Walleij <linus.walleij@linaro.org>
|
||||
* Copyright 2011 - 2012 Bryan Wu <bryan.wu@canonical.com>
|
||||
|
@ -145,6 +149,9 @@ static int __init ledtrig_cpu_init(void)
|
|||
for_each_possible_cpu(cpu) {
|
||||
struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu);
|
||||
|
||||
if (cpu >= 8)
|
||||
continue;
|
||||
|
||||
snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu);
|
||||
|
||||
led_trigger_register_simple(trig->name, &trig->_trig);
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* TCA6507 LED chip driver.
|
||||
*
|
||||
* Copyright (C) 2011 Neil Brown <neil@brown.name>
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_TCA6507_H
|
||||
#define __LINUX_TCA6507_H
|
||||
#include <linux/leds.h>
|
||||
|
||||
struct tca6507_platform_data {
|
||||
struct led_platform_data leds;
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
int gpio_base;
|
||||
void (*setup)(unsigned gpio_base, unsigned ngpio);
|
||||
#endif
|
||||
};
|
||||
|
||||
#define TCA6507_MAKE_GPIO 1
|
||||
#endif /* __LINUX_TCA6507_H*/
|
|
@ -1,35 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* PCA963X LED chip driver.
|
||||
*
|
||||
* Copyright 2012 bct electronic GmbH
|
||||
* Copyright 2013 Qtechnology A/S
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_PCA963X_H
|
||||
#define __LINUX_PCA963X_H
|
||||
#include <linux/leds.h>
|
||||
|
||||
enum pca963x_outdrv {
|
||||
PCA963X_OPEN_DRAIN,
|
||||
PCA963X_TOTEM_POLE, /* aka push-pull */
|
||||
};
|
||||
|
||||
enum pca963x_blink_type {
|
||||
PCA963X_SW_BLINK,
|
||||
PCA963X_HW_BLINK,
|
||||
};
|
||||
|
||||
enum pca963x_direction {
|
||||
PCA963X_NORMAL,
|
||||
PCA963X_INVERTED,
|
||||
};
|
||||
|
||||
struct pca963x_platform_data {
|
||||
struct led_platform_data leds;
|
||||
enum pca963x_outdrv outdrv;
|
||||
enum pca963x_blink_type blink_type;
|
||||
enum pca963x_direction dir;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_PCA963X_H*/
|
Loading…
Reference in a new issue