diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-10-14 15:22:07 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-10-14 15:22:07 -0700 |
commit | 7fafb54c7d390e9b273a1d7d377e38d9c408046e (patch) | |
tree | b154474b905af8059a427fef74392de45e8bfbe2 | |
parent | 55e0500eb5c0440a3d43074edbd8db3e95851b66 (diff) | |
parent | 19d2e0cef0b14f8c7210162f58327485f5fa7c51 (diff) |
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
...
61 files changed, 1791 insertions, 1104 deletions
diff --git a/Documentation/devicetree/bindings/leds/leds-is31fl319x.txt b/Documentation/devicetree/bindings/leds/leds-is31fl319x.txt index fc2603484544..676d43ec8169 100644 --- a/Documentation/devicetree/bindings/leds/leds-is31fl319x.txt +++ b/Documentation/devicetree/bindings/leds/leds-is31fl319x.txt @@ -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"; diff --git a/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml b/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml new file mode 100644 index 000000000000..947542a253ec --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml @@ -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>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml b/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml index b1bb3feb0f4d..89f69d62493e 100644 --- a/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml +++ b/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml @@ -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"; diff --git a/Documentation/devicetree/bindings/leds/leds-pca955x.txt b/Documentation/devicetree/bindings/leds/leds-pca955x.txt index 7a5830f8d5ab..817f460f3a72 100644 --- a/Documentation/devicetree/bindings/leds/leds-pca955x.txt +++ b/Documentation/devicetree/bindings/leds/leds-pca955x.txt @@ -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 diff --git a/Documentation/devicetree/bindings/leds/tca6507.txt b/Documentation/devicetree/bindings/leds/tca6507.txt deleted file mode 100644 index bad9102796f3..000000000000 --- a/Documentation/devicetree/bindings/leds/tca6507.txt +++ /dev/null @@ -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"; - }; -}; - diff --git a/Documentation/devicetree/bindings/leds/ti,tca6507.yaml b/Documentation/devicetree/bindings/leds/ti,tca6507.yaml new file mode 100644 index 000000000000..94c307c98762 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/ti,tca6507.yaml @@ -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>; + }; + }; + }; + +... diff --git a/Documentation/leds/ledtrig-transient.rst b/Documentation/leds/ledtrig-transient.rst index eedfa1626e8a..63072f310660 100644 --- a/Documentation/leds/ledtrig-transient.rst +++ b/Documentation/leds/ledtrig-transient.rst @@ -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 diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 1c181df24eae..849d3c5f908e 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -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" diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index c2c7d7ade0d0..73e603e1727e 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -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 diff --git a/drivers/leds/TODO b/drivers/leds/TODO new file mode 100644 index 000000000000..bfa60fa1d812 --- /dev/null +++ b/drivers/leds/TODO @@ -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. + diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index cc3929f858b6..131ca83f5fb3 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -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; } diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 465c3755cf2e..508d0d859f2e 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -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); diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c index 5a0fe7b7b8bc..589484b22c79 100644 --- a/drivers/leds/leds-aat1290.c +++ b/drivers/leds/leds-aat1290.c @@ -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; diff --git a/drivers/leds/leds-acer-a500.c b/drivers/leds/leds-acer-a500.c new file mode 100644 index 000000000000..8cf0b11f4390 --- /dev/null +++ b/drivers/leds/leds-acer-a500.c @@ -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"); diff --git a/drivers/leds/leds-an30259a.c b/drivers/leds/leds-an30259a.c index 82350a28a564..a0df1fb28774 100644 --- a/drivers/leds/leds-an30259a.c +++ b/drivers/leds/leds-an30259a.c @@ -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++; } diff --git a/drivers/leds/leds-aw2013.c b/drivers/leds/leds-aw2013.c index d709cc1f949e..80d937454aee 100644 --- a/drivers/leds/leds-aw2013.c +++ b/drivers/leds/leds-aw2013.c @@ -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++; } diff --git a/drivers/leds/leds-bcm6328.c b/drivers/leds/leds-bcm6328.c index bad7efb75112..226d17d253ed 100644 --- a/drivers/leds/leds-bcm6328.c +++ b/drivers/leds/leds-bcm6328.c @@ -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 */ diff --git a/drivers/leds/leds-bcm6358.c b/drivers/leds/leds-bcm6358.c index 94fefd456ba0..9d2e487fa08a 100644 --- a/drivers/leds/leds-bcm6358.c +++ b/drivers/leds/leds-bcm6358.c @@ -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 */ diff --git a/drivers/leds/leds-cpcap.c b/drivers/leds/leds-cpcap.c index 9f3fa4737213..7d41ce8c9bb1 100644 --- a/drivers/leds/leds-cpcap.c +++ b/drivers/leds/leds-cpcap.c @@ -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) { diff --git a/drivers/leds/leds-cr0014114.c b/drivers/leds/leds-cr0014114.c index 2da448ae718e..d03cfd3c0bfb 100644 --- a/drivers/leds/leds-cr0014114.c +++ b/drivers/leds/leds-cr0014114.c @@ -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; diff --git a/drivers/leds/leds-el15203000.c b/drivers/leds/leds-el15203000.c index 298b13e4807a..6ca47f2a2004 100644 --- a/drivers/leds/leds-el15203000.c +++ b/drivers/leds/leds-el15203000.c @@ -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; diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index cf84096d88ce..93f5b1b60fde 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -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")) diff --git a/drivers/leds/leds-ip30.c b/drivers/leds/leds-ip30.c index d4ec7361c616..1f952bad0fe8 100644 --- a/drivers/leds/leds-ip30.c +++ b/drivers/leds/leds-ip30.c @@ -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> diff --git a/drivers/leds/leds-is31fl319x.c b/drivers/leds/leds-is31fl319x.c index ca6634b8683c..4161b9dd7e48 100644 --- a/drivers/leds/leds-is31fl319x.c +++ b/drivers/leds/leds-is31fl319x.c @@ -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)) { diff --git a/drivers/leds/leds-is31fl32xx.c b/drivers/leds/leds-is31fl32xx.c index cd768f991da1..2180255ad339 100644 --- a/drivers/leds/leds-is31fl32xx.c +++ b/drivers/leds/leds-is31fl32xx.c @@ -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 = of_dev_id->data; + cdef = device_get_match_data(dev); - count = of_get_child_count(dev->of_node); + count = of_get_available_child_count(dev_of_node(dev)); if (!count) return -EINVAL; diff --git a/drivers/leds/leds-ktd2692.c b/drivers/leds/leds-ktd2692.c index 670efee9b131..632f10db4b3f 100644 --- a/drivers/leds/leds-ktd2692.c +++ b/drivers/leds/leds-ktd2692.c @@ -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); diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c index 946ad67eaecb..0bf25bdde02f 100644 --- a/drivers/leds/leds-lm3532.c +++ b/drivers/leds/leds-lm3532.c @@ -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); diff --git a/drivers/leds/leds-lm36274.c b/drivers/leds/leds-lm36274.c index bfeee03a0053..aadb03468a40 100644 --- a/drivers/leds/leds-lm36274.c +++ b/drivers/leds/leds-lm36274.c @@ -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); - - 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); + child = device_get_next_child_node(dev, NULL); - } + init_data->fwnode = child; + init_data->devicename = chip->pdev->name; + /* for backwards compatibility when `label` property is not present */ + init_data->default_label = ":"; - 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; + chip->num_leds = fwnode_property_count_u32(child, "led-sources"); + if (chip->num_leds <= 0) { + ret = -ENODEV; + goto err; + } - 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) diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index e1e2d2b64a56..e945de45388c 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -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"); diff --git a/drivers/leds/leds-lm3697.c b/drivers/leds/leds-lm3697.c index 024983088d59..7d216cdb91a8 100644 --- a/drivers/leds/leds-lm3697.c +++ b/drivers/leds/leds-lm3697.c @@ -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); diff --git a/drivers/leds/leds-lp50xx.c b/drivers/leds/leds-lp50xx.c new file mode 100644 index 000000000000..5fb4f24aeb2e --- /dev/null +++ b/drivers/leds/leds-lp50xx.c @@ -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"); diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index ef8c3bfa8f3c..a9e7507c998c 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -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) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index f55d97258d5e..fc433e63b1dc 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -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) diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c index 7ecdd199d7ef..31c14016d289 100644 --- a/drivers/leds/leds-lp5562.c +++ b/drivers/leds/leds-lp5562.c @@ -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) diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 56210f4ad919..81de1346bf5d 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -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++; } diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c index ac2c31db4a65..2d2fda2ab104 100644 --- a/drivers/leds/leds-lp8501.c +++ b/drivers/leds/leds-lp8501.c @@ -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) diff --git a/drivers/leds/leds-lp8860.c b/drivers/leds/leds-lp8860.c index ac2f5d6272dc..f0533a337bc1 100644 --- a/drivers/leds/leds-lp8860.c +++ b/drivers/leds/leds-lp8860.c @@ -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)) { diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c index 9079850e6ea4..68e06434ac08 100644 --- a/drivers/leds/leds-lt3593.c +++ b/drivers/leds/leds-lt3593.c @@ -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; diff --git a/drivers/leds/leds-max77650.c b/drivers/leds/leds-max77650.c index a0d4b725c917..1eeac56b0014 100644 --- a/drivers/leds/leds-max77650.c +++ b/drivers/leds/leds-max77650.c @@ -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; - } - } - - fwnode_property_read_string(child, "linux,default-trigger", - &led->cdev.default_trigger); + init_data.fwnode = child; + init_data.devicename = "max77650"; + /* for backwards compatibility if `label` is not present */ + init_data.default_label = ":"; - 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; diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c index fec56090c2ba..5c1faeb55a31 100644 --- a/drivers/leds/leds-max77693.c +++ b/drivers/leds/leds-max77693.c @@ -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; diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index 5cd810c545f3..675502c15c2b 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -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); diff --git a/drivers/leds/leds-mt6323.c b/drivers/leds/leds-mt6323.c index 2a13e3161bf4..f59e0e8bda8b 100644 --- a/drivers/leds/leds-mt6323.c +++ b/drivers/leds/leds-mt6323.c @@ -249,15 +249,6 @@ static int mt6323_led_set_blink(struct led_classdev *cdev, 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 * 1Hz duty cycle 50% here if without specific @@ -269,6 +260,15 @@ static int mt6323_led_set_blink(struct led_classdev *cdev, } /* + * 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; diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c index ceceeb6a0e96..e6fd47365b58 100644 --- a/drivers/leds/leds-netxbig.c +++ b/drivers/leds/leds-netxbig.c @@ -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; diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index bd806e7c8017..1677d66d8b0e 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -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; + + 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->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); - rwlock_init(&led_dat->rw_lock); - - 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->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); -static void delete_ns2_led(struct ns2_led_data *led_dat) -{ - led_classdev_unregister(&led_dat->cdev); + return 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) +static int ns2_led_probe(struct platform_device *pdev) { - struct device_node *np = dev->of_node; - struct device_node *child; - struct ns2_led *led, *leds; - int ret, num_leds = 0; + struct device *dev = &pdev->dev; + struct fwnode_handle *child; + struct ns2_led *leds; + int count; + int ret; - 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; - } - 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); + device_for_each_child_node(dev, child) { + ret = ns2_led_register(dev, child, leds++); + if (ret) { + fwnode_handle_put(child); + return ret; } - - 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, }, }; diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index 7d515d5e57bd..27d027165472 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -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); diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 131f8e922ade..7087ca4592fc 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -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 }, {}, }; diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index d288acbc99c7..00aecd67e348 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -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; + + 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->grppwm, pca963x->gdc); + i2c_smbus_write_byte_data(client, chipdef->grppwm, led->gdc); - i2c_smbus_write_byte_data(pca963x->chip->client, - pca963x->chip->chipdef->grpfreq, pca963x->gfrq); + 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); - - 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); + i2c_smbus_write_byte_data(client, PCA963X_MODE2, + mode2 | PCA963X_MODE2_DMBLNK); + + 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; - - count = device_get_child_node_count(&client->dev); - if (!count || count > chip->n_leds) - return ERR_PTR(-ENODEV); - - pca963x_leds = devm_kcalloc(&client->dev, - chip->n_leds, sizeof(struct led_info), GFP_KERNEL); - if (!pca963x_leds) - return ERR_PTR(-ENOMEM); - - 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; + bool hw_blink; + s32 mode2; + u32 reg; + int ret; - res = fwnode_property_read_string(child, "label", &led.name); - if ((res != 0) && is_of_node(child)) - led.name = to_of_node(child)->name; + if (device_property_read_u32(dev, "nxp,period-scale", + &chipdef->scaling)) + chipdef->scaling = 1000; - fwnode_property_read_string(child, "linux,default-trigger", - &led.default_trigger); + hw_blink = device_property_read_bool(dev, "nxp,hw-blink"); - 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; + mode2 &= ~PCA963X_MODE2_OUTDRV; - /* 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; + /* default to non-inverted output, unless inverted is specified */ + if (device_property_read_bool(dev, "nxp,inverted-out")) + mode2 |= PCA963X_MODE2_INVRT; else - pdata->blink_type = PCA963X_SW_BLINK; + mode2 &= ~PCA963X_MODE2_INVRT; + + 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; + } - if (device_property_read_u32(&client->dev, "nxp,period-scale", - &chip->scaling)) - chip->scaling = 1000; + 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; + } - /* default to non-inverted output, unless inverted is specified */ - if (device_property_read_bool(&client->dev, "nxp,inverted-out")) - pdata->dir = PCA963X_INVERTED; - else - pdata->dir = PCA963X_NORMAL; + ++led; + } - return pdata; + 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; - - chip = &pca963x_chipdefs[id->driver_data]; - pdata = dev_get_platdata(&client->dev); - - if (!pdata) { - pdata = pca963x_get_pdata(client, chip); - if (IS_ERR(pdata)) { - dev_warn(&client->dev, "could not parse configuration\n"); - pdata = NULL; - } - } + struct device *dev = &client->dev; + struct pca963x_chipdef *chipdef; + struct pca963x *chip; + int i, count; - 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); + chipdef = &pca963x_chipdefs[id->driver_data]; + + 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, }; diff --git a/drivers/leds/leds-pm8058.c b/drivers/leds/leds-pm8058.c index 7869ccdf70ce..fb2ab72c0c40 100644 --- a/drivers/leds/leds-pm8058.c +++ b/drivers/leds/leds-pm8058.c @@ -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); + + 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 0; + return ret; } static const struct of_device_id pm8058_leds_id_table[] = { diff --git a/drivers/leds/leds-powernv.c b/drivers/leds/leds-powernv.c index cd43d5dff7f4..743e2cdd0891 100644 --- a/drivers/leds/leds-powernv.c +++ b/drivers/leds/leds-powernv.c @@ -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) { diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index ef7b91bd2064..f53f9309ca6c 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -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; diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c index 9b5e67664ba3..3c0c7aa63b8c 100644 --- a/drivers/leds/leds-s3c24xx.c +++ b/drivers/leds/leds-s3c24xx.c @@ -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 { diff --git a/drivers/leds/leds-sc27xx-bltc.c b/drivers/leds/leds-sc27xx-bltc.c index 0ede87420bfc..e199ea15e406 100644 --- a/drivers/leds/leds-sc27xx-bltc.c +++ b/drivers/leds/leds-sc27xx-bltc.c @@ -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); diff --git a/drivers/leds/leds-sgm3140.c b/drivers/leds/leds-sgm3140.c index c494b934ae09..f4f831570f11 100644 --- a/drivers/leds/leds-sgm3140.c +++ b/drivers/leds/leds-sgm3140.c @@ -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"); diff --git a/drivers/leds/leds-spi-byte.c b/drivers/leds/leds-spi-byte.c index b231b563b7bb..f1964c96fb15 100644 --- a/drivers/leds/leds-spi-byte.c +++ b/drivers/leds/leds-spi-byte.c @@ -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; diff --git a/drivers/leds/leds-syscon.c b/drivers/leds/leds-syscon.c index b58f3cafe16f..7eddb8ecb44e 100644 --- a/drivers/leds/leds-syscon.c +++ b/drivers/leds/leds-syscon.c @@ -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; diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index 1128ac75443c..225b765830bd 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -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 */ diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c index 0929f1275814..5b9dfdf743ec 100644 --- a/drivers/leds/leds-tlc591xx.c +++ b/drivers/leds/leds-tlc591xx.c @@ -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; diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c index bb23d8e16614..8c5bdc3847ee 100644 --- a/drivers/leds/leds-turris-omnia.c +++ b/drivers/leds/leds-turris-omnia.c @@ -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; } diff --git a/drivers/leds/trigger/ledtrig-cpu.c b/drivers/leds/trigger/ledtrig-cpu.c index 869976d1b734..fca62d503590 100644 --- a/drivers/leds/trigger/ledtrig-cpu.c +++ b/drivers/leds/trigger/ledtrig-cpu.c @@ -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); diff --git a/include/linux/leds-tca6507.h b/include/linux/leds-tca6507.h deleted file mode 100644 index 50d330ed1100..000000000000 --- a/include/linux/leds-tca6507.h +++ /dev/null @@ -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*/ diff --git a/include/linux/platform_data/leds-pca963x.h b/include/linux/platform_data/leds-pca963x.h deleted file mode 100644 index 6091337ce4bf..000000000000 --- a/include/linux/platform_data/leds-pca963x.h +++ /dev/null @@ -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*/ |