summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-07-03 11:26:05 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2023-07-03 11:26:05 -0700
commitc156d4af4354091c38a1cbef62c0b1574e8c4394 (patch)
treefe2d517469814ab302884f4a4e8dd9b2f9822e33
parentb8ec70ab66b09ee9e081a38b8625b5accb388176 (diff)
parent877e91191ccf0782ae18c5dfa7522fb1e5bfba8c (diff)
Merge tag 'leds-next-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds
Pull LED updates from Lee Jones: "New Drivers: - Add support for Intel Cherry Trail Whiskey Cove PMIC LEDs - Add support for Awinic AW20036/AW20054/AW20072 LEDs New Device Support: - Add support for PMI632 LPG to QCom LPG - Add support for PMI8998 to QCom Flash - Add support for MT6331, WLEDs and MT6332 to Mediatek MT6323 PMIC New Functionality: - Implement the LP55xx Charge Pump - Add support for suspend / resume to Intel Cherry Trail Whiskey Cove PMIC - Add support for breathing mode to Intel Cherry Trail Whiskey Cove PMIC - Enable per-pin resolution Pinctrl in LEDs GPIO Fix-ups: - Allow thread to sleep by switching from spinlock to mutex - Add lots of Device Tree bindings / support - Adapt relationships / dependencies driven by Kconfig - Switch I2C drivers from .probe_new() to .probe() - Remove superfluous / duplicate code - Replace strlcpy() with strscpy() for efficiency and overflow prevention - Staticify various functions - Trivial: Fixing coding style - Simplify / reduce code Bug Fixes: - Prevent NETDEV_LED_MODE_LINKUP from being cleared on rename - Repair race between led_set_brightness(LED_{OFF,FULL}) - Fix Oops relating to sleeping in critical sections - Clear LED_INIT_DEFAULT_TRIGGER flag when clearing the current trigger - Do not leak resources in error handling paths - Fix unsigned comparison which can never be negative - Provide missing NULL terminating entries in tables - Fix misnaming issues" * tag 'leds-next-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds: (53 commits) leds: leds-mt6323: Adjust return/parameter types in wled get/set callbacks leds: sgm3140: Add richtek,rt5033-led compatible dt-bindings: leds: sgm3140: Document richtek,rt5033 compatible dt-bindings: backlight: kinetic,ktz8866: Add missing type for "current-num-sinks" dt-bindings: leds: Drop unneeded quotes leds: Fix config reference for AW200xx driver leds: leds-mt6323: Add support for WLEDs and MT6332 leds: leds-mt6323: Add support for MT6331 leds leds: leds-mt6323: Open code and drop MT6323_CAL_HW_DUTY macro leds: leds-mt6323: Drop MT6323_ prefix from macros and defines leds: leds-mt6323: Specify registers and specs in platform data dt-bindings: leds: leds-mt6323: Document mt6332 compatible dt-bindings: leds: leds-mt6323: Document mt6331 compatible leds: simatic-ipc-leds-gpio: Introduce more Kconfig switches leds: simatic-ipc-leds-gpio: Split up into multiple drivers leds: simatic-ipc-leds-gpio: Move two extra gpio pins into another table leds: simatic-ipc-leds-gpio: Add terminating entries to gpio tables leds: flash: leds-qcom-flash: Fix an unsigned comparison which can never be negative leds: cht-wcove: Remove unneeded semicolon leds: cht-wcove: Fix an unsigned comparison which can never be negative ...
-rw-r--r--Documentation/ABI/testing/sysfs-class-led-driver-aw200xx5
-rw-r--r--Documentation/devicetree/bindings/leds/awinic,aw200xx.yaml126
-rw-r--r--Documentation/devicetree/bindings/leds/backlight/kinetic,ktz8866.yaml1
-rw-r--r--Documentation/devicetree/bindings/leds/common.yaml2
-rw-r--r--Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml2
-rw-r--r--Documentation/devicetree/bindings/leds/leds-lp55xx.yaml10
-rw-r--r--Documentation/devicetree/bindings/leds/leds-mt6323.txt5
-rw-r--r--Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml30
-rw-r--r--Documentation/devicetree/bindings/leds/leds-sgm3140.yaml1
-rw-r--r--Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml2
-rw-r--r--Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml2
-rw-r--r--Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml1
-rw-r--r--Documentation/leds/index.rst1
-rw-r--r--Documentation/leds/leds-cht-wcove.rst38
-rw-r--r--Documentation/leds/well-known-leds.txt3
-rw-r--r--drivers/leds/Kconfig37
-rw-r--r--drivers/leds/Makefile2
-rw-r--r--drivers/leds/flash/leds-aat1290.c2
-rw-r--r--drivers/leds/flash/leds-as3645a.c6
-rw-r--r--drivers/leds/flash/leds-lm3601x.c2
-rw-r--r--drivers/leds/flash/leds-qcom-flash.c17
-rw-r--r--drivers/leds/flash/leds-rt4505.c2
-rw-r--r--drivers/leds/flash/leds-sgm3140.c1
-rw-r--r--drivers/leds/led-class.c2
-rw-r--r--drivers/leds/led-core.c81
-rw-r--r--drivers/leds/led-triggers.c17
-rw-r--r--drivers/leds/leds-an30259a.c2
-rw-r--r--drivers/leds/leds-aw200xx.c594
-rw-r--r--drivers/leds/leds-aw2013.c2
-rw-r--r--drivers/leds/leds-bd2606mvv.c2
-rw-r--r--drivers/leds/leds-bd2802.c2
-rw-r--r--drivers/leds/leds-blinkm.c4
-rw-r--r--drivers/leds/leds-cht-wcove.c476
-rw-r--r--drivers/leds/leds-gpio.c18
-rw-r--r--drivers/leds/leds-is31fl319x.c2
-rw-r--r--drivers/leds/leds-is31fl32xx.c2
-rw-r--r--drivers/leds/leds-lm3530.c2
-rw-r--r--drivers/leds/leds-lm3532.c2
-rw-r--r--drivers/leds/leds-lm355x.c2
-rw-r--r--drivers/leds/leds-lm3642.c2
-rw-r--r--drivers/leds/leds-lm3692x.c2
-rw-r--r--drivers/leds/leds-lm3697.c2
-rw-r--r--drivers/leds/leds-lp3944.c2
-rw-r--r--drivers/leds/leds-lp3952.c2
-rw-r--r--drivers/leds/leds-lp50xx.c2
-rw-r--r--drivers/leds/leds-lp5521.c13
-rw-r--r--drivers/leds/leds-lp5523.c16
-rw-r--r--drivers/leds/leds-lp5562.c2
-rw-r--r--drivers/leds/leds-lp55xx-common.c9
-rw-r--r--drivers/leds/leds-lp8501.c10
-rw-r--r--drivers/leds/leds-lp8860.c2
-rw-r--r--drivers/leds/leds-mt6323.c446
-rw-r--r--drivers/leds/leds-pca9532.c2
-rw-r--r--drivers/leds/leds-pca955x.c2
-rw-r--r--drivers/leds/leds-pca963x.c2
-rw-r--r--drivers/leds/leds-spi-byte.c2
-rw-r--r--drivers/leds/leds-tca6507.c2
-rw-r--r--drivers/leds/leds-tlc591xx.c2
-rw-r--r--drivers/leds/leds-turris-omnia.c2
-rw-r--r--drivers/leds/rgb/leds-qcom-lpg.c23
-rw-r--r--drivers/leds/simple/Kconfig31
-rw-r--r--drivers/leds/simple/Makefile5
-rw-r--r--drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c66
-rw-r--r--drivers/leds/simple/simatic-ipc-leds-gpio-core.c104
-rw-r--r--drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c66
-rw-r--r--drivers/leds/simple/simatic-ipc-leds-gpio.c139
-rw-r--r--drivers/leds/simple/simatic-ipc-leds-gpio.h22
-rw-r--r--drivers/leds/simple/simatic-ipc-leds.c1
-rw-r--r--drivers/leds/trigger/ledtrig-disk.c9
-rw-r--r--drivers/leds/trigger/ledtrig-mtd.c8
-rw-r--r--drivers/leds/trigger/ledtrig-netdev.c6
-rw-r--r--drivers/net/arcnet/arcnet.c8
-rw-r--r--drivers/platform/x86/simatic-ipc.c7
-rw-r--r--drivers/power/supply/power_supply_leds.c5
-rw-r--r--drivers/usb/common/led.c4
-rw-r--r--include/dt-bindings/leds/leds-lp55xx.h10
-rw-r--r--include/linux/leds.h43
-rw-r--r--include/linux/platform_data/leds-lp55xx.h3
-rw-r--r--net/mac80211/led.c2
-rw-r--r--net/mac80211/led.h8
-rw-r--r--net/netfilter/xt_LED.c3
81 files changed, 2209 insertions, 396 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-led-driver-aw200xx b/Documentation/ABI/testing/sysfs-class-led-driver-aw200xx
new file mode 100644
index 000000000000..6d4449cf9d71
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-led-driver-aw200xx
@@ -0,0 +1,5 @@
+What: /sys/class/leds/<led>/dim
+Date: May 2023
+Description: 64-level DIM current. If you write a negative value or
+ "auto", the dim will be calculated according to the
+ brightness.
diff --git a/Documentation/devicetree/bindings/leds/awinic,aw200xx.yaml b/Documentation/devicetree/bindings/leds/awinic,aw200xx.yaml
new file mode 100644
index 000000000000..feb5febaf361
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/awinic,aw200xx.yaml
@@ -0,0 +1,126 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/awinic,aw200xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: AWINIC AW200XX LED
+
+maintainers:
+ - Martin Kurbanov <mmkurbanov@sberdevices.ru>
+
+description: |
+ This controller is present on AW20036/AW20054/AW20072.
+ It is a 3x12/6x9/6x12 matrix LED programmed via
+ an I2C interface, up to 36/54/72 LEDs or 12/18/24 RGBs,
+ 3 pattern controllers for auto breathing or group dimming control.
+
+ For more product information please see the link below:
+ aw20036 - https://www.awinic.com/en/productDetail/AW20036QNR#tech-docs
+ aw20054 - https://www.awinic.com/en/productDetail/AW20054QNR#tech-docs
+ aw20072 - https://www.awinic.com/en/productDetail/AW20072QNR#tech-docs
+
+properties:
+ compatible:
+ enum:
+ - awinic,aw20036
+ - awinic,aw20054
+ - awinic,aw20072
+
+ reg:
+ maxItems: 1
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ awinic,display-rows:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Leds matrix size
+
+patternProperties:
+ "^led@[0-9a-f]$":
+ type: object
+ $ref: common.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ reg:
+ description:
+ LED number
+ maxItems: 1
+
+ led-max-microamp:
+ default: 9780
+ description: |
+ Note that a driver will take the minimum of all LED limits
+ since the chip has a single global setting.
+ The maximum output current of each LED is calculated by the
+ following formula:
+ IMAXled = 160000 * (592 / 600.5) * (1 / display-rows)
+ And the minimum output current formula:
+ IMINled = 3300 * (592 / 600.5) * (1 / display-rows)
+
+required:
+ - compatible
+ - reg
+ - "#address-cells"
+ - "#size-cells"
+ - awinic,display-rows
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: awinic,aw20036
+ then:
+ properties:
+ awinic,display-rows:
+ enum: [1, 2, 3]
+ else:
+ properties:
+ awinic,display-rows:
+ enum: [1, 2, 3, 4, 5, 6, 7]
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/leds/common.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led-controller@3a {
+ compatible = "awinic,aw20036";
+ reg = <0x3a>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ awinic,display-rows = <3>;
+
+ led@0 {
+ reg = <0x0>;
+ color = <LED_COLOR_ID_RED>;
+ led-max-microamp = <9780>;
+ };
+
+ led@1 {
+ reg = <0x1>;
+ color = <LED_COLOR_ID_GREEN>;
+ led-max-microamp = <9780>;
+ };
+
+ led@2 {
+ reg = <0x2>;
+ color = <LED_COLOR_ID_BLUE>;
+ led-max-microamp = <9780>;
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/leds/backlight/kinetic,ktz8866.yaml b/Documentation/devicetree/bindings/leds/backlight/kinetic,ktz8866.yaml
index e1191453c2f0..11b6fc36183d 100644
--- a/Documentation/devicetree/bindings/leds/backlight/kinetic,ktz8866.yaml
+++ b/Documentation/devicetree/bindings/leds/backlight/kinetic,ktz8866.yaml
@@ -33,6 +33,7 @@ properties:
current-num-sinks:
description: number of the LED current sinks' channels.
+ $ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 2, 3, 4, 5, 6]
kinetic,current-ramp-delay-ms:
diff --git a/Documentation/devicetree/bindings/leds/common.yaml b/Documentation/devicetree/bindings/leds/common.yaml
index 11aedf1650a1..58b492d00246 100644
--- a/Documentation/devicetree/bindings/leds/common.yaml
+++ b/Documentation/devicetree/bindings/leds/common.yaml
@@ -105,8 +105,6 @@ properties:
- audio-mute
# LED indicates bluetooth power state
- bluetooth-power
- # LED indicates activity of all CPUs
- - cpu
# LED indicates camera flash state
- flash
# LED indicated keyboard capslock
diff --git a/Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml b/Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml
index 31840e33dcf5..e850a8894758 100644
--- a/Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml
+++ b/Documentation/devicetree/bindings/leds/leds-class-multicolor.yaml
@@ -34,7 +34,7 @@ required:
- color
allOf:
- - $ref: "common.yaml#"
+ - $ref: common.yaml#
additionalProperties: true
diff --git a/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml b/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml
index ae607911f1db..058be1fedbc8 100644
--- a/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml
+++ b/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml
@@ -66,6 +66,14 @@ properties:
'#size-cells':
const: 0
+ ti,charge-pump-mode:
+ description:
+ Set the operating mode of the internal charge pump as defined in
+ <dt-bindings/leds/leds-lp55xx.h>.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ default: 3 # auto
+ maximum: 3
+
patternProperties:
'^multi-led@[0-8]$':
type: object
@@ -152,6 +160,7 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/leds/common.h>
+ #include <dt-bindings/leds/leds-lp55xx.h>
i2c {
#address-cells = <1>;
@@ -164,6 +173,7 @@ examples:
reg = <0x32>;
clock-mode = /bits/ 8 <2>;
pwr-sel = /bits/ 8 <3>; /* D1~9 connected to VOUT */
+ ti,charge-pump-mode = <LP55XX_CP_BYPASS>;
led@0 {
reg = <0>;
diff --git a/Documentation/devicetree/bindings/leds/leds-mt6323.txt b/Documentation/devicetree/bindings/leds/leds-mt6323.txt
index 73353692efa1..052dccb8f2ce 100644
--- a/Documentation/devicetree/bindings/leds/leds-mt6323.txt
+++ b/Documentation/devicetree/bindings/leds/leds-mt6323.txt
@@ -12,7 +12,10 @@ For MediaTek PMIC wrapper bindings see:
Documentation/devicetree/bindings/soc/mediatek/mediatek,pwrap.yaml
Required properties:
-- compatible : Must be "mediatek,mt6323-led"
+- compatible : Must be one of
+ - "mediatek,mt6323-led"
+ - "mediatek,mt6331-led"
+ - "mediatek,mt6332-led"
- address-cells : Must be 1
- size-cells : Must be 0
diff --git a/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml b/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml
index 6295c91f43e8..e6f1999cb22f 100644
--- a/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml
+++ b/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml
@@ -16,18 +16,24 @@ description: >
properties:
compatible:
- enum:
- - qcom,pm660l-lpg
- - qcom,pm8150b-lpg
- - qcom,pm8150l-lpg
- - qcom,pm8350c-pwm
- - qcom,pm8916-pwm
- - qcom,pm8941-lpg
- - qcom,pm8994-lpg
- - qcom,pmc8180c-lpg
- - qcom,pmi8994-lpg
- - qcom,pmi8998-lpg
- - qcom,pmk8550-pwm
+ oneOf:
+ - enum:
+ - qcom,pm660l-lpg
+ - qcom,pm8150b-lpg
+ - qcom,pm8150l-lpg
+ - qcom,pm8350c-pwm
+ - qcom,pm8916-pwm
+ - qcom,pm8941-lpg
+ - qcom,pm8994-lpg
+ - qcom,pmc8180c-lpg
+ - qcom,pmi632-lpg
+ - qcom,pmi8994-lpg
+ - qcom,pmi8998-lpg
+ - qcom,pmk8550-pwm
+ - items:
+ - enum:
+ - qcom,pm8550-pwm
+ - const: qcom,pm8350c-pwm
"#pwm-cells":
const: 2
diff --git a/Documentation/devicetree/bindings/leds/leds-sgm3140.yaml b/Documentation/devicetree/bindings/leds/leds-sgm3140.yaml
index 4d2ffe5fcfc7..37d2a93780ab 100644
--- a/Documentation/devicetree/bindings/leds/leds-sgm3140.yaml
+++ b/Documentation/devicetree/bindings/leds/leds-sgm3140.yaml
@@ -20,6 +20,7 @@ properties:
compatible:
enum:
- ocs,ocp8110
+ - richtek,rt5033-led
- sgmicro,sgm3140
enable-gpios:
diff --git a/Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml b/Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml
index ffacf703d9f9..a8736fd5a539 100644
--- a/Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml
+++ b/Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml
@@ -26,6 +26,8 @@ properties:
- qcom,pm8150c-flash-led
- qcom,pm8150l-flash-led
- qcom,pm8350c-flash-led
+ - qcom,pm8550-flash-led
+ - qcom,pmi8998-flash-led
- const: qcom,spmi-flash-led
reg:
diff --git a/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml b/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml
index 64b0be9cf70b..58f0d94c6d71 100644
--- a/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml
+++ b/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml
@@ -32,7 +32,7 @@ patternProperties:
properties:
rohm,led-compatible:
description: LED identification string
- $ref: "/schemas/types.yaml#/definitions/string"
+ $ref: /schemas/types.yaml#/definitions/string
enum:
- bd71828-ambled
- bd71828-grnled
diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml
index abcdc0dd4aba..e43e6d74e308 100644
--- a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml
+++ b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml
@@ -71,6 +71,7 @@ properties:
- qcom,pm8998
- qcom,pma8084
- qcom,pmd9635
+ - qcom,pmi632
- qcom,pmi8950
- qcom,pmi8962
- qcom,pmi8994
diff --git a/Documentation/leds/index.rst b/Documentation/leds/index.rst
index ce57254cb871..3ade16c18328 100644
--- a/Documentation/leds/index.rst
+++ b/Documentation/leds/index.rst
@@ -17,6 +17,7 @@ LEDs
uleds
leds-blinkm
+ leds-cht-wcove
leds-el15203000
leds-lm3556
leds-lp3944
diff --git a/Documentation/leds/leds-cht-wcove.rst b/Documentation/leds/leds-cht-wcove.rst
new file mode 100644
index 000000000000..5ec7cb60c4aa
--- /dev/null
+++ b/Documentation/leds/leds-cht-wcove.rst
@@ -0,0 +1,38 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================================================
+Kernel driver for Intel Cherry Trail Whiskey Cove PMIC LEDs
+===========================================================
+
+/sys/class/leds/<led>/hw_pattern
+--------------------------------
+
+Specify a hardware pattern for the Whiskey Cove PMIC LEDs.
+
+The only supported pattern is hardware breathing mode::
+
+ "0 2000 1 2000"
+
+ ^
+ |
+ Max-| ---
+ | / \
+ | / \
+ | / \ /
+ | / \ /
+ Min-|- ---
+ |
+ 0------2------4--> time (sec)
+
+The rise and fall times must be the same value.
+Supported values are 2000, 1000, 500 and 250 for
+breathing frequencies of 1/4, 1/2, 1 and 2 Hz.
+
+The set pattern only controls the timing. For max brightness the last
+set brightness is used and the max brightness can be changed
+while breathing by writing the brightness attribute.
+
+This is just like how blinking works in the LED subsystem,
+for both sw and hw blinking the brightness can also be changed
+while blinking. Breathing on this hw really is just a variant
+mode of blinking.
diff --git a/Documentation/leds/well-known-leds.txt b/Documentation/leds/well-known-leds.txt
index e9c30dc75884..67b44704801f 100644
--- a/Documentation/leds/well-known-leds.txt
+++ b/Documentation/leds/well-known-leds.txt
@@ -58,6 +58,7 @@ LEDs on notebook body, indicating that sound input / output is muted.
* System notification
+Good: "rgb:status"
Legacy: "status-led:{red,green,blue}" (Motorola Droid 4)
Legacy: "lp5523:{r,g,b}" (Nokia N900)
@@ -65,7 +66,7 @@ Phones usually have multi-color status LED.
* Power management
-Good: "platform:*:charging" (allwinner sun50i)
+Good: "platform:*:charging" (allwinner sun50i, leds-cht-wcove)
* Screen
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 2c5fdf848210..6046dfeca16f 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -94,6 +94,19 @@ config LEDS_ARIEL
Say Y to if your machine is a Dell Wyse 3020 thin client.
+config LEDS_AW200XX
+ tristate "LED support for Awinic AW20036/AW20054/AW20072"
+ depends on LEDS_CLASS
+ depends on I2C
+ help
+ This option enables support for the AW20036/AW20054/AW20072 LED driver.
+ It is a 3x12/6x9/6x12 matrix LED driver programmed via
+ an I2C interface, up to 36/54/72 LEDs or 12/18/24 RGBs,
+ 3 pattern controllers for auto breathing or group dimming control.
+
+ To compile this driver as a module, choose M here: the module
+ will be called leds-aw200xx.
+
config LEDS_AW2013
tristate "LED support for Awinic AW2013"
depends on LEDS_CLASS && I2C && OF
@@ -122,6 +135,17 @@ config LEDS_BCM6358
This option enables support for LEDs connected to the BCM6358
LED HW controller accessed via MMIO registers.
+config LEDS_CHT_WCOVE
+ tristate "LED support for Intel Cherry Trail Whiskey Cove PMIC"
+ depends on LEDS_CLASS
+ depends on INTEL_SOC_PMIC_CHTWC
+ help
+ This option enables support for charger and general purpose LEDs
+ connected to the Intel Cherrytrail Whiskey Cove PMIC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called leds-cht-wcove.
+
config LEDS_CPCAP
tristate "LED Support for Motorola CPCAP"
depends on LEDS_CLASS
@@ -676,7 +700,7 @@ config LEDS_LM355x
config LEDS_OT200
tristate "LED support for the Bachmann OT200"
- depends on LEDS_CLASS && HAS_IOMEM && (X86_32 || COMPILE_TEST)
+ depends on LEDS_CLASS && HAS_IOPORT && (X86_32 || COMPILE_TEST)
help
This option enables support for the LEDs on the Bachmann OT200.
Say Y to enable LEDs on the Bachmann OT200.
@@ -807,8 +831,7 @@ config LEDS_SPI_BYTE
supported: Ubiquiti airCube ISP microcontroller based LED controller.
config LEDS_TI_LMU_COMMON
- tristate "LED driver for TI LMU"
- depends on LEDS_CLASS
+ tristate "LED driver for TI LMU" if COMPILE_TEST
select REGMAP
help
Say Y to enable the LED driver for TI LMU devices.
@@ -817,16 +840,16 @@ config LEDS_TI_LMU_COMMON
config LEDS_LM3697
tristate "LED driver for LM3697"
- depends on LEDS_TI_LMU_COMMON
- depends on I2C && OF
+ depends on LEDS_CLASS && I2C && OF
+ select LEDS_TI_LMU_COMMON
help
Say Y to enable the LM3697 LED driver for TI LMU devices.
This supports the LED device LM3697.
config LEDS_LM36274
tristate "LED driver for LM36274"
- depends on LEDS_TI_LMU_COMMON
- depends on MFD_TI_LMU
+ depends on LEDS_CLASS && MFD_TI_LMU
+ select LEDS_TI_LMU_COMMON
help
Say Y to enable the LM36274 LED driver for TI LMU devices.
This supports the LED device LM36274.
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index c07d1512c745..d71f1226540c 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -14,12 +14,14 @@ obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o
obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o
obj-$(CONFIG_LEDS_APU) += leds-apu.o
obj-$(CONFIG_LEDS_ARIEL) += leds-ariel.o
+obj-$(CONFIG_LEDS_AW200XX) += leds-aw200xx.o
obj-$(CONFIG_LEDS_AW2013) += leds-aw2013.o
obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o
obj-$(CONFIG_LEDS_BCM6358) += leds-bcm6358.o
obj-$(CONFIG_LEDS_BD2606MVV) += leds-bd2606mvv.o
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
+obj-$(CONFIG_LEDS_CHT_WCOVE) += leds-cht-wcove.o
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
diff --git a/drivers/leds/flash/leds-aat1290.c b/drivers/leds/flash/leds-aat1290.c
index 589484b22c79..f12ecb2c6580 100644
--- a/drivers/leds/flash/leds-aat1290.c
+++ b/drivers/leds/flash/leds-aat1290.c
@@ -425,7 +425,7 @@ static void aat1290_init_v4l2_flash_config(struct aat1290_led *led,
struct led_classdev *led_cdev = &led->fled_cdev.led_cdev;
struct led_flash_setting *s;
- strlcpy(v4l2_sd_cfg->dev_name, led_cdev->dev->kobj.name,
+ strscpy(v4l2_sd_cfg->dev_name, led_cdev->dev->kobj.name,
sizeof(v4l2_sd_cfg->dev_name));
s = &v4l2_sd_cfg->intensity;
diff --git a/drivers/leds/flash/leds-as3645a.c b/drivers/leds/flash/leds-as3645a.c
index bb2249771acb..12c2609c1137 100644
--- a/drivers/leds/flash/leds-as3645a.c
+++ b/drivers/leds/flash/leds-as3645a.c
@@ -651,8 +651,8 @@ static int as3645a_v4l2_setup(struct as3645a *flash)
},
};
- strlcpy(cfg.dev_name, led->dev->kobj.name, sizeof(cfg.dev_name));
- strlcpy(cfgind.dev_name, flash->iled_cdev.dev->kobj.name,
+ strscpy(cfg.dev_name, led->dev->kobj.name, sizeof(cfg.dev_name));
+ strscpy(cfgind.dev_name, flash->iled_cdev.dev->kobj.name,
sizeof(cfgind.dev_name));
flash->vf = v4l2_flash_init(
@@ -759,7 +759,7 @@ static struct i2c_driver as3645a_i2c_driver = {
.of_match_table = as3645a_of_table,
.name = AS_NAME,
},
- .probe_new = as3645a_probe,
+ .probe = as3645a_probe,
.remove = as3645a_remove,
.id_table = as3645a_id_table,
};
diff --git a/drivers/leds/flash/leds-lm3601x.c b/drivers/leds/flash/leds-lm3601x.c
index 78730e066a73..b6c524facf49 100644
--- a/drivers/leds/flash/leds-lm3601x.c
+++ b/drivers/leds/flash/leds-lm3601x.c
@@ -471,7 +471,7 @@ static struct i2c_driver lm3601x_i2c_driver = {
.name = "lm3601x",
.of_match_table = of_lm3601x_leds_match,
},
- .probe_new = lm3601x_probe,
+ .probe = lm3601x_probe,
.remove = lm3601x_remove,
.id_table = lm3601x_id,
};
diff --git a/drivers/leds/flash/leds-qcom-flash.c b/drivers/leds/flash/leds-qcom-flash.c
index 90a24fa25a49..b089ca1a1901 100644
--- a/drivers/leds/flash/leds-qcom-flash.c
+++ b/drivers/leds/flash/leds-qcom-flash.c
@@ -18,7 +18,8 @@
#define FLASH_TYPE_VAL 0x18
#define FLASH_SUBTYPE_REG 0x05
-#define FLASH_SUBTYPE_3CH_VAL 0x04
+#define FLASH_SUBTYPE_3CH_PM8150_VAL 0x04
+#define FLASH_SUBTYPE_3CH_PMI8998_VAL 0x03
#define FLASH_SUBTYPE_4CH_VAL 0x07
#define FLASH_STS_3CH_OTST1 BIT(0)
@@ -416,6 +417,14 @@ static int qcom_flash_led_brightness_set(struct led_classdev *led_cdev,
bool enable = !!brightness;
int rc;
+ rc = set_flash_strobe(led, SW_STROBE, false);
+ if (rc)
+ return rc;
+
+ rc = set_flash_module_en(led, false);
+ if (rc)
+ return rc;
+
rc = set_flash_current(led, current_ma, TORCH_MODE);
if (rc)
return rc;
@@ -529,9 +538,9 @@ static int qcom_flash_register_led_device(struct device *dev,
struct led_init_data init_data;
struct led_classdev_flash *flash = &led->flash;
struct led_flash_setting *brightness, *timeout;
- u32 count, current_ua, timeout_us;
+ u32 current_ua, timeout_us;
u32 channels[4];
- int i, rc;
+ int i, rc, count;
count = fwnode_property_count_u32(node, "led-sources");
if (count <= 0) {
@@ -682,7 +691,7 @@ static int qcom_flash_led_probe(struct platform_device *pdev)
return rc;
}
- if (val == FLASH_SUBTYPE_3CH_VAL) {
+ if (val == FLASH_SUBTYPE_3CH_PM8150_VAL || val == FLASH_SUBTYPE_3CH_PMI8998_VAL) {
flash_data->hw_type = QCOM_MVFLASH_3CH;
flash_data->max_channels = 3;
regs = mvflash_3ch_regs;
diff --git a/drivers/leds/flash/leds-rt4505.c b/drivers/leds/flash/leds-rt4505.c
index e404fe8b0314..1ae5b387f4a5 100644
--- a/drivers/leds/flash/leds-rt4505.c
+++ b/drivers/leds/flash/leds-rt4505.c
@@ -419,7 +419,7 @@ static struct i2c_driver rt4505_driver = {
.name = "rt4505",
.of_match_table = of_match_ptr(rt4505_leds_match),
},
- .probe_new = rt4505_probe,
+ .probe = rt4505_probe,
.remove = rt4505_remove,
.shutdown = rt4505_shutdown,
};
diff --git a/drivers/leds/flash/leds-sgm3140.c b/drivers/leds/flash/leds-sgm3140.c
index d3a30ad94ac4..d3f50dca5136 100644
--- a/drivers/leds/flash/leds-sgm3140.c
+++ b/drivers/leds/flash/leds-sgm3140.c
@@ -291,6 +291,7 @@ static int sgm3140_remove(struct platform_device *pdev)
static const struct of_device_id sgm3140_dt_match[] = {
{ .compatible = "ocs,ocp8110" },
+ { .compatible = "richtek,rt5033-led" },
{ .compatible = "sgmicro,sgm3140" },
{ /* sentinel */ }
};
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 9255bc11f99d..6dae56b914fe 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -409,7 +409,7 @@ static int led_classdev_next_name(const char *init_name, char *name,
int ret = 0;
struct device *dev;
- strlcpy(name, init_name, len);
+ strscpy(name, init_name, len);
while ((ret < len) &&
(dev = class_find_device_by_name(leds_class, name))) {
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 4a97cb745788..b9b1295833c9 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -114,21 +114,14 @@ static void led_timer_function(struct timer_list *t)
mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
}
-static void set_brightness_delayed(struct work_struct *ws)
+static void set_brightness_delayed_set_brightness(struct led_classdev *led_cdev,
+ unsigned int value)
{
- struct led_classdev *led_cdev =
- container_of(ws, struct led_classdev, set_brightness_work);
int ret = 0;
- if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
- led_cdev->delayed_set_value = LED_OFF;
- led_stop_software_blink(led_cdev);
- }
-
- ret = __led_set_brightness(led_cdev, led_cdev->delayed_set_value);
+ ret = __led_set_brightness(led_cdev, value);
if (ret == -ENOTSUPP)
- ret = __led_set_brightness_blocking(led_cdev,
- led_cdev->delayed_set_value);
+ ret = __led_set_brightness_blocking(led_cdev, value);
if (ret < 0 &&
/* LED HW might have been unplugged, therefore don't warn */
!(ret == -ENODEV && (led_cdev->flags & LED_UNREGISTERING) &&
@@ -137,6 +130,37 @@ static void set_brightness_delayed(struct work_struct *ws)
"Setting an LED's brightness failed (%d)\n", ret);
}
+static void set_brightness_delayed(struct work_struct *ws)
+{
+ struct led_classdev *led_cdev =
+ container_of(ws, struct led_classdev, set_brightness_work);
+
+ if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
+ led_stop_software_blink(led_cdev);
+ set_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags);
+ }
+
+ /*
+ * Triggers may call led_set_brightness(LED_OFF),
+ * led_set_brightness(LED_FULL) in quick succession to disable blinking
+ * and turn the LED on. Both actions may have been scheduled to run
+ * before this work item runs once. To make sure this works properly
+ * handle LED_SET_BRIGHTNESS_OFF first.
+ */
+ if (test_and_clear_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags))
+ set_brightness_delayed_set_brightness(led_cdev, LED_OFF);
+
+ if (test_and_clear_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags))
+ set_brightness_delayed_set_brightness(led_cdev, led_cdev->delayed_set_value);
+
+ if (test_and_clear_bit(LED_SET_BLINK, &led_cdev->work_flags)) {
+ unsigned long delay_on = led_cdev->delayed_delay_on;
+ unsigned long delay_off = led_cdev->delayed_delay_off;
+
+ led_blink_set(led_cdev, &delay_on, &delay_off);
+ }
+}
+
static void led_set_software_blink(struct led_classdev *led_cdev,
unsigned long delay_on,
unsigned long delay_off)
@@ -229,6 +253,22 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev,
}
EXPORT_SYMBOL_GPL(led_blink_set_oneshot);
+void led_blink_set_nosleep(struct led_classdev *led_cdev, unsigned long delay_on,
+ unsigned long delay_off)
+{
+ /* If necessary delegate to a work queue task. */
+ if (led_cdev->blink_set && led_cdev->brightness_set_blocking) {
+ led_cdev->delayed_delay_on = delay_on;
+ led_cdev->delayed_delay_off = delay_off;
+ set_bit(LED_SET_BLINK, &led_cdev->work_flags);
+ schedule_work(&led_cdev->set_brightness_work);
+ return;
+ }
+
+ led_blink_set(led_cdev, &delay_on, &delay_off);
+}
+EXPORT_SYMBOL_GPL(led_blink_set_nosleep);
+
void led_stop_software_blink(struct led_classdev *led_cdev)
{
del_timer_sync(&led_cdev->blink_timer);
@@ -271,8 +311,23 @@ void led_set_brightness_nopm(struct led_classdev *led_cdev, unsigned int value)
if (!__led_set_brightness(led_cdev, value))
return;
- /* If brightness setting can sleep, delegate it to a work queue task */
- led_cdev->delayed_set_value = value;
+ /*
+ * Brightness setting can sleep, delegate it to a work queue task.
+ * value 0 / LED_OFF is special, since it also disables hw-blinking
+ * (sw-blink disable is handled in led_set_brightness()).
+ * To avoid a hw-blink-disable getting lost when a second brightness
+ * change is done immediately afterwards (before the work runs),
+ * it uses a separate work_flag.
+ */
+ if (value) {
+ led_cdev->delayed_set_value = value;
+ set_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
+ } else {
+ clear_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
+ clear_bit(LED_SET_BLINK, &led_cdev->work_flags);
+ set_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags);
+ }
+
schedule_work(&led_cdev->set_brightness_work);
}
EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 072491d3e17b..6a5e1f41f9a4 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -185,6 +185,7 @@ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
led_cdev->trigger = NULL;
led_cdev->trigger_data = NULL;
led_cdev->activated = false;
+ led_cdev->flags &= ~LED_INIT_DEFAULT_TRIGGER;
led_set_brightness(led_cdev, LED_OFF);
}
if (trig) {
@@ -393,8 +394,8 @@ void led_trigger_event(struct led_trigger *trig,
EXPORT_SYMBOL_GPL(led_trigger_event);
static void led_trigger_blink_setup(struct led_trigger *trig,
- unsigned long *delay_on,
- unsigned long *delay_off,
+ unsigned long delay_on,
+ unsigned long delay_off,
int oneshot,
int invert)
{
@@ -406,25 +407,25 @@ static void led_trigger_blink_setup(struct led_trigger *trig,
rcu_read_lock();
list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list) {
if (oneshot)
- led_blink_set_oneshot(led_cdev, delay_on, delay_off,
+ led_blink_set_oneshot(led_cdev, &delay_on, &delay_off,
invert);
else
- led_blink_set(led_cdev, delay_on, delay_off);
+ led_blink_set_nosleep(led_cdev, delay_on, delay_off);
}
rcu_read_unlock();
}
void led_trigger_blink(struct led_trigger *trig,
- unsigned long *delay_on,
- unsigned long *delay_off)
+ unsigned long delay_on,
+ unsigned long delay_off)
{
led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0);
}
EXPORT_SYMBOL_GPL(led_trigger_blink);
void led_trigger_blink_oneshot(struct led_trigger *trig,
- unsigned long *delay_on,
- unsigned long *delay_off,
+ unsigned long delay_on,
+ unsigned long delay_off,
int invert)
{
led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert);
diff --git a/drivers/leds/leds-an30259a.c b/drivers/leds/leds-an30259a.c
index 89df267853a9..24b1041213c2 100644
--- a/drivers/leds/leds-an30259a.c
+++ b/drivers/leds/leds-an30259a.c
@@ -346,7 +346,7 @@ static struct i2c_driver an30259a_driver = {
.name = "leds-an30259a",
.of_match_table = of_match_ptr(an30259a_match_table),
},
- .probe_new = an30259a_probe,
+ .probe = an30259a_probe,
.remove = an30259a_remove,
.id_table = an30259a_id,
};
diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c
new file mode 100644
index 000000000000..96979b8e09b7
--- /dev/null
+++ b/drivers/leds/leds-aw200xx.c
@@ -0,0 +1,594 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Awinic AW20036/AW20054/AW20072 LED driver
+ *
+ * Copyright (c) 2023, SberDevices. All Rights Reserved.
+ *
+ * Author: Martin Kurbanov <mmkurbanov@sberdevices.ru>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/container_of.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/time.h>
+#include <linux/units.h>
+
+#define AW200XX_DIM_MAX (BIT(6) - 1)
+#define AW200XX_FADE_MAX (BIT(8) - 1)
+#define AW200XX_IMAX_DEFAULT_uA 60000
+#define AW200XX_IMAX_MAX_uA 160000
+#define AW200XX_IMAX_MIN_uA 3300
+
+/* Page 0 */
+#define AW200XX_REG_PAGE0_BASE 0xc000
+
+/* Select page register */
+#define AW200XX_REG_PAGE 0xF0
+#define AW200XX_PAGE_MASK (GENMASK(7, 6) | GENMASK(2, 0))
+#define AW200XX_PAGE_SHIFT 0
+#define AW200XX_NUM_PAGES 6
+#define AW200XX_PAGE_SIZE 256
+#define AW200XX_REG(page, reg) \
+ (AW200XX_REG_PAGE0_BASE + (page) * AW200XX_PAGE_SIZE + (reg))
+#define AW200XX_REG_MAX \
+ AW200XX_REG(AW200XX_NUM_PAGES - 1, AW200XX_PAGE_SIZE - 1)
+#define AW200XX_PAGE0 0
+#define AW200XX_PAGE1 1
+#define AW200XX_PAGE2 2
+#define AW200XX_PAGE3 3
+#define AW200XX_PAGE4 4
+#define AW200XX_PAGE5 5
+
+/* Chip ID register */
+#define AW200XX_REG_IDR AW200XX_REG(AW200XX_PAGE0, 0x00)
+#define AW200XX_IDR_CHIPID 0x18
+
+/* Sleep mode register */
+#define AW200XX_REG_SLPCR AW200XX_REG(AW200XX_PAGE0, 0x01)
+#define AW200XX_SLPCR_ACTIVE 0x00
+
+/* Reset register */
+#define AW200XX_REG_RSTR AW200XX_REG(AW200XX_PAGE0, 0x02)
+#define AW200XX_RSTR_RESET 0x01
+
+/* Global current configuration register */
+#define AW200XX_REG_GCCR AW200XX_REG(AW200XX_PAGE0, 0x03)
+#define AW200XX_GCCR_IMAX_MASK GENMASK(7, 4)
+#define AW200XX_GCCR_IMAX(x) ((x) << 4)
+#define AW200XX_GCCR_ALLON BIT(3)
+
+/* Fast clear display control register */
+#define AW200XX_REG_FCD AW200XX_REG(AW200XX_PAGE0, 0x04)
+#define AW200XX_FCD_CLEAR 0x01
+
+/* Display size configuration */
+#define AW200XX_REG_DSIZE AW200XX_REG(AW200XX_PAGE0, 0x80)
+#define AW200XX_DSIZE_COLUMNS_MAX 12
+
+#define AW200XX_LED2REG(x, columns) \
+ ((x) + (((x) / (columns)) * (AW200XX_DSIZE_COLUMNS_MAX - (columns))))
+
+/*
+ * DIM current configuration register (page 4).
+ * The even address for current DIM configuration.
+ * The odd address for current FADE configuration
+ */
+#define AW200XX_REG_DIM(x, columns) \
+ AW200XX_REG(AW200XX_PAGE4, AW200XX_LED2REG(x, columns) * 2)
+#define AW200XX_REG_DIM2FADE(x) ((x) + 1)
+
+/*
+ * Duty ratio of display scan (see p.15 of datasheet for formula):
+ * duty = (592us / 600.5us) * (1 / (display_rows + 1))
+ *
+ * Multiply to 1000 (MILLI) to improve the accuracy of calculations.
+ */
+#define AW200XX_DUTY_RATIO(rows) \
+ (((592UL * USEC_PER_SEC) / 600500UL) * (MILLI / (rows)) / MILLI)
+
+struct aw200xx_chipdef {
+ u32 channels;
+ u32 display_size_rows_max;
+ u32 display_size_columns;
+};
+
+struct aw200xx_led {
+ struct led_classdev cdev;
+ struct aw200xx *chip;
+ int dim;
+ u32 num;
+};
+
+struct aw200xx {
+ const struct aw200xx_chipdef *cdef;
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct mutex mutex;
+ u32 num_leds;
+ u32 display_rows;
+ struct aw200xx_led leds[];
+};
+
+static ssize_t dim_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev);
+ int dim = led->dim;
+
+ if (dim < 0)
+ return sysfs_emit(buf, "auto\n");
+
+ return sysfs_emit(buf, "%d\n", dim);
+}
+
+static ssize_t dim_store(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct led_classdev *cdev = dev_get_drvdata(dev);
+ struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev);
+ struct aw200xx *chip = led->chip;
+ u32 columns = chip->cdef->display_size_columns;
+ int dim;
+ ssize_t ret;
+
+ if (sysfs_streq(buf, "auto")) {
+ dim = -1;
+ } else {
+ ret = kstrtoint(buf, 0, &dim);
+ if (ret)
+ return ret;
+
+ if (dim > AW200XX_DIM_MAX)
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->mutex);
+
+ if (dim >= 0) {
+ ret = regmap_write(chip->regmap,
+ AW200XX_REG_DIM(led->num, columns), dim);
+ if (ret)
+ goto out_unlock;
+ }
+
+ led->dim = dim;
+ ret = count;
+
+out_unlock:
+ mutex_unlock(&chip->mutex);
+ return ret;
+}
+static DEVICE_ATTR_RW(dim);
+
+static struct attribute *dim_attrs[] = {
+ &dev_attr_dim.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(dim);
+
+static int aw200xx_brightness_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct aw200xx_led *led = container_of(cdev, struct aw200xx_led, cdev);
+ struct aw200xx *chip = led->chip;
+ int dim;
+ u32 reg;
+ int ret;
+
+ mutex_lock(&chip->mutex);
+
+ reg = AW200XX_REG_DIM(led->num, chip->cdef->display_size_columns);
+
+ dim = led->dim;
+ if (dim < 0)
+ dim = max_t(int,
+ brightness / (AW200XX_FADE_MAX / AW200XX_DIM_MAX),
+ 1);
+
+ ret = regmap_write(chip->regmap, reg, dim);
+ if (ret)
+ goto out_unlock;
+
+ ret = regmap_write(chip->regmap,
+ AW200XX_REG_DIM2FADE(reg), brightness);
+
+out_unlock:
+ mutex_unlock(&chip->mutex);
+
+ return ret;
+}
+
+static u32 aw200xx_imax_from_global(const struct aw200xx *const chip,
+ u32 global_imax_uA)
+{
+ u64 led_imax_uA;
+
+ /*
+ * The output current of each LED (see p.14 of datasheet for formula):
+ * Iled = Imax * (dim / 63) * ((fade + 1) / 256) * duty
+ *
+ * The value of duty is determined by the following formula:
+ * duty = (592us / 600.5us) * (1 / (display_rows + 1))
+ *
+ * Calculated for the maximum values of fade and dim.
+ * We divide by 1000 because we earlier multiplied by 1000 to improve
+ * accuracy when calculating the duty.
+ */
+ led_imax_uA = global_imax_uA * AW200XX_DUTY_RATIO(chip->display_rows);
+ do_div(led_imax_uA, MILLI);
+
+ return led_imax_uA;
+}
+
+static u32 aw200xx_imax_to_global(const struct aw200xx *const chip,
+ u32 led_imax_uA)
+{
+ u32 duty = AW200XX_DUTY_RATIO(chip->display_rows);
+
+ /* The output current of each LED (see p.14 of datasheet for formula) */
+ return (led_imax_uA * 1000U) / duty;
+}
+
+#define AW200XX_IMAX_MULTIPLIER1 10000
+#define AW200XX_IMAX_MULTIPLIER2 3333
+#define AW200XX_IMAX_BASE_VAL1 0
+#define AW200XX_IMAX_BASE_VAL2 8
+
+/*
+ * The AW200XX has a 4-bit register (GCCR) to configure the global current,
+ * which ranges from 3.3mA to 160mA. The following table indicates the values
+ * of the global current, divided into two parts:
+ *
+ * +-----------+-----------------+-----------+-----------------+
+ * | reg value | global max (mA) | reg value | global max (mA) |
+ * +-----------+-----------------+-----------+-----------------+
+ * | 0 | 10 | 8 | 3.3 |
+ * | 1 | 20 | 9 | 6.7 |
+ * | 2 | 30 | 10 | 10 |
+ * | 3 | 40 | 11 | 13.3 |
+ * | 4 | 60 | 12 | 20 |
+ * | 5 | 80 | 13 | 26.7 |
+ * | 6 | 120 | 14 | 40 |
+ * | 7 | 160 | 15 | 53.3 |
+ * +-----------+-----------------+-----------+-----------------+
+ *
+ * The left part with a multiplier of 10, and the right part with a multiplier
+ * of 3.3.
+ * So we have two formulas to calculate the global current:
+ * for the left part of the table:
+ * imax = coefficient * 10
+ *
+ * for the right part of the table:
+ * imax = coefficient * 3.3
+ *
+ * The coefficient table consists of the following values:
+ * 1, 2, 3, 4, 6, 8, 12, 16.
+ */
+static int aw200xx_set_imax(const struct aw200xx *const chip,
+ u32 led_imax_uA)
+{
+ u32 g_imax_uA = aw200xx_imax_to_global(chip, led_imax_uA);
+ u32 coeff_table[] = {1, 2, 3, 4, 6, 8, 12, 16};
+ u32 gccr_imax = UINT_MAX;
+ u32 cur_imax = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_table); i++) {
+ u32 imax;
+
+ /* select closest ones */
+ imax = coeff_table[i] * AW200XX_IMAX_MULTIPLIER1;
+ if (g_imax_uA >= imax && imax > cur_imax) {
+ cur_imax = imax;
+ gccr_imax = i + AW200XX_IMAX_BASE_VAL1;
+ }
+
+ imax = coeff_table[i] * AW200XX_IMAX_MULTIPLIER2;
+ imax = DIV_ROUND_CLOSEST(imax, 100) * 100;
+ if (g_imax_uA >= imax && imax > cur_imax) {
+ cur_imax = imax;
+ gccr_imax = i + AW200XX_IMAX_BASE_VAL2;
+ }
+ }
+
+ if (gccr_imax == UINT_MAX)
+ return -EINVAL;
+
+ return regmap_update_bits(chip->regmap, AW200XX_REG_GCCR,
+ AW200XX_GCCR_IMAX_MASK,
+ AW200XX_GCCR_IMAX(gccr_imax));
+}
+
+static int aw200xx_chip_reset(const struct aw200xx *const chip)
+{
+ int ret;
+
+ ret = regmap_write(chip->regmap, AW200XX_REG_RSTR, AW200XX_RSTR_RESET);
+ if (ret)
+ return ret;
+
+ regcache_mark_dirty(chip->regmap);
+ return regmap_write(chip->regmap, AW200XX_REG_FCD, AW200XX_FCD_CLEAR);
+}
+
+static int aw200xx_chip_init(const struct aw200xx *const chip)
+{
+ int ret;
+
+ ret = regmap_write(chip->regmap, AW200XX_REG_DSIZE,
+ chip->display_rows - 1);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(chip->regmap, AW200XX_REG_SLPCR,
+ AW200XX_SLPCR_ACTIVE);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(chip->regmap, AW200XX_REG_GCCR,
+ AW200XX_GCCR_ALLON, AW200XX_GCCR_ALLON);
+}
+
+static int aw200xx_chip_check(const struct aw200xx *const chip)
+{
+ struct device *dev = &chip->client->dev;
+ u32 chipid;
+ int ret;
+
+ ret = regmap_read(chip->regmap, AW200XX_REG_IDR, &chipid);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read chip ID\n");
+
+ if (chipid != AW200XX_IDR_CHIPID)
+ return dev_err_probe(dev, -ENODEV,
+ "Chip reported wrong ID: %x\n", chipid);
+
+ return 0;
+}
+
+static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip)
+{
+ struct fwnode_handle *child;
+ u32 current_min, current_max, min_uA;
+ int ret;
+ int i;
+
+ ret = device_property_read_u32(dev, "awinic,display-rows",
+ &chip->display_rows);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to read 'display-rows' property\n");
+
+ if (!chip->display_rows ||
+ chip->display_rows > chip->cdef->display_size_rows_max) {
+ return dev_err_probe(dev, ret,
+ "Invalid leds display size %u\n",
+ chip->display_rows);
+ }
+
+ current_max = aw200xx_imax_from_global(chip, AW200XX_IMAX_MAX_uA);
+ current_min = aw200xx_imax_from_global(chip, AW200XX_IMAX_MIN_uA);
+ min_uA = UINT_MAX;
+ i = 0;
+
+ device_for_each_child_node(dev, child) {
+ struct led_init_data init_data = {};
+ struct aw200xx_led *led;
+ u32 source, imax;
+
+ ret = fwnode_property_read_u32(child, "reg", &source);
+ if (ret) {
+ dev_err(dev, "Missing reg property\n");
+ chip->num_leds--;
+ continue;
+ }
+
+ if (source >= chip->cdef->channels) {
+ dev_err(dev, "LED reg %u out of range (max %u)\n",
+ source, chip->cdef->channels);
+ chip->num_leds--;
+ continue;
+ }
+
+ ret = fwnode_property_read_u32(child, "led-max-microamp",
+ &imax);
+ if (ret) {
+ dev_info(&chip->client->dev,
+ "DT property led-max-microamp is missing\n");
+ } else if (imax < current_min || imax > current_max) {
+ dev_err(dev, "Invalid value %u for led-max-microamp\n",
+ imax);
+ chip->num_leds--;
+ continue;
+ } else {
+ min_uA = min(min_uA, imax);
+ }
+
+ led = &chip->leds[i];
+ led->dim = -1;
+ led->num = source;
+ led->chip = chip;
+ led->cdev.brightness_set_blocking = aw200xx_brightness_set;
+ led->cdev.groups = dim_groups;
+ init_data.fwnode = child;
+
+ ret = devm_led_classdev_register_ext(dev, &led->cdev,
+ &init_data);
+ if (ret) {
+ fwnode_handle_put(child);
+ break;
+ }
+
+ i++;
+ }
+
+ if (!chip->num_leds)
+ return -EINVAL;
+
+ if (min_uA == UINT_MAX) {
+ min_uA = aw200xx_imax_from_global(chip,
+ AW200XX_IMAX_DEFAULT_uA);
+ }
+
+ return aw200xx_set_imax(chip, min_uA);
+}
+
+static const struct regmap_range_cfg aw200xx_ranges[] = {
+ {
+ .name = "aw200xx",
+ .range_min = 0,
+ .range_max = AW200XX_REG_MAX,
+ .selector_reg = AW200XX_REG_PAGE,
+ .selector_mask = AW200XX_PAGE_MASK,
+ .selector_shift = AW200XX_PAGE_SHIFT,
+ .window_start = 0,
+ .window_len = AW200XX_PAGE_SIZE,
+ },
+};
+
+static const struct regmap_range aw200xx_writeonly_ranges[] = {
+ regmap_reg_range(AW200XX_REG(AW200XX_PAGE1, 0x00), AW200XX_REG_MAX),
+};
+
+static const struct regmap_access_table aw200xx_readable_table = {
+ .no_ranges = aw200xx_writeonly_ranges,
+ .n_no_ranges = ARRAY_SIZE(aw200xx_writeonly_ranges),
+};
+
+static const struct regmap_range aw200xx_readonly_ranges[] = {
+ regmap_reg_range(AW200XX_REG_IDR, AW200XX_REG_IDR),
+};
+
+static const struct regmap_access_table aw200xx_writeable_table = {
+ .no_ranges = aw200xx_readonly_ranges,
+ .n_no_ranges = ARRAY_SIZE(aw200xx_readonly_ranges),
+};
+
+static const struct regmap_config aw200xx_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AW200XX_REG_MAX,
+ .ranges = aw200xx_ranges,
+ .num_ranges = ARRAY_SIZE(aw200xx_ranges),
+ .rd_table = &aw200xx_readable_table,
+ .wr_table = &aw200xx_writeable_table,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int aw200xx_probe(struct i2c_client *client)
+{
+ const struct aw200xx_chipdef *cdef;
+ struct aw200xx *chip;
+ int count;
+ int ret;
+
+ cdef = device_get_match_data(&client->dev);
+ if (!cdef)
+ return -ENODEV;
+
+ count = device_get_child_node_count(&client->dev);
+ if (!count || count > cdef->channels)
+ return dev_err_probe(&client->dev, -EINVAL,
+ "Incorrect number of leds (%d)", count);
+
+ chip = devm_kzalloc(&client->dev, struct_size(chip, leds, count),
+ GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->cdef = cdef;
+ chip->num_leds = count;
+ chip->client = client;
+ i2c_set_clientdata(client, chip);
+
+ chip->regmap = devm_regmap_init_i2c(client, &aw200xx_regmap_config);
+ if (IS_ERR(chip->regmap))
+ return PTR_ERR(chip->regmap);
+
+ ret = aw200xx_chip_check(chip);
+ if (ret)
+ return ret;
+
+ mutex_init(&chip->mutex);
+
+ /* Need a lock now since after call aw200xx_probe_fw, sysfs nodes created */
+ mutex_lock(&chip->mutex);
+
+ ret = aw200xx_chip_reset(chip);
+ if (ret)
+ goto out_unlock;
+
+ ret = aw200xx_probe_fw(&client->dev, chip);
+ if (ret)
+ goto out_unlock;
+
+ ret = aw200xx_chip_init(chip);
+
+out_unlock:
+ mutex_unlock(&chip->mutex);
+ return ret;
+}
+
+static void aw200xx_remove(struct i2c_client *client)
+{
+ struct aw200xx *chip = i2c_get_clientdata(client);
+
+ aw200xx_chip_reset(chip);
+ mutex_destroy(&chip->mutex);
+}
+
+static const struct aw200xx_chipdef aw20036_cdef = {
+ .channels = 36,
+ .display_size_rows_max = 3,
+ .display_size_columns = 12,
+};
+
+static const struct aw200xx_chipdef aw20054_cdef = {
+ .channels = 54,
+ .display_size_rows_max = 6,
+ .display_size_columns = 9,
+};
+
+static const struct aw200xx_chipdef aw20072_cdef = {
+ .channels = 72,
+ .display_size_rows_max = 6,
+ .display_size_columns = 12,
+};
+
+static const struct i2c_device_id aw200xx_id[] = {
+ { "aw20036" },
+ { "aw20054" },
+ { "aw20072" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, aw200xx_id);
+
+static const struct of_device_id aw200xx_match_table[] = {
+ { .compatible = "awinic,aw20036", .data = &aw20036_cdef, },
+ { .compatible = "awinic,aw20054", .data = &aw20054_cdef, },
+ { .compatible = "awinic,aw20072", .data = &aw20072_cdef, },
+ {}
+};
+MODULE_DEVICE_TABLE(of, aw200xx_match_table);
+
+static struct i2c_driver aw200xx_driver = {
+ .driver = {
+ .name = "aw200xx",
+ .of_match_table = aw200xx_match_table,
+ },
+ .probe_new = aw200xx_probe,
+ .remove = aw200xx_remove,
+ .id_table = aw200xx_id,
+};
+module_i2c_driver(aw200xx_driver);
+
+MODULE_AUTHOR("Martin Kurbanov <mmkurbanov@sberdevices.ru>");
+MODULE_DESCRIPTION("AW200XX LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-aw2013.c b/drivers/leds/leds-aw2013.c
index 0b52fc9097c6..59765640b70f 100644
--- a/drivers/leds/leds-aw2013.c
+++ b/drivers/leds/leds-aw2013.c
@@ -422,7 +422,7 @@ static struct i2c_driver aw2013_driver = {
.name = "leds-aw2013",
.of_match_table = of_match_ptr(aw2013_match_table),
},
- .probe_new = aw2013_probe,
+ .probe = aw2013_probe,
.remove = aw2013_remove,
};
diff --git a/drivers/leds/leds-bd2606mvv.c b/drivers/leds/leds-bd2606mvv.c
index 76f9d4d70f9a..3fda712d2f80 100644
--- a/drivers/leds/leds-bd2606mvv.c
+++ b/drivers/leds/leds-bd2606mvv.c
@@ -150,7 +150,7 @@ static struct i2c_driver bd2606mvv_driver = {
.name = "leds-bd2606mvv",
.of_match_table = of_match_ptr(of_bd2606mvv_leds_match),
},
- .probe_new = bd2606mvv_probe,
+ .probe = bd2606mvv_probe,
};
module_i2c_driver(bd2606mvv_driver);
diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c
index 601185ddabcc..0792ea126cea 100644
--- a/drivers/leds/leds-bd2802.c
+++ b/drivers/leds/leds-bd2802.c
@@ -786,7 +786,7 @@ static struct i2c_driver bd2802_i2c_driver = {
.name = "BD2802",
.pm = &bd2802_pm,
},
- .probe_new = bd2802_probe,
+ .probe = bd2802_probe,
.remove = bd2802_remove,
.id_table = bd2802_id,
};
diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c
index 37f2f32ae42d..2782da1a1930 100644
--- a/drivers/leds/leds-blinkm.c
+++ b/drivers/leds/leds-blinkm.c
@@ -561,7 +561,7 @@ static int blinkm_detect(struct i2c_client *client, struct i2c_board_info *info)
return -ENODEV;
}
- strlcpy(info->type, "blinkm", I2C_NAME_SIZE);
+ strscpy(info->type, "blinkm", I2C_NAME_SIZE);
return 0;
}
@@ -730,7 +730,7 @@ static struct i2c_driver blinkm_driver = {
.driver = {
.name = "blinkm",
},
- .probe_new = blinkm_probe,
+ .probe = blinkm_probe,
.remove = blinkm_remove,
.id_table = blinkm_id,
.detect = blinkm_detect,
diff --git a/drivers/leds/leds-cht-wcove.c b/drivers/leds/leds-cht-wcove.c
new file mode 100644
index 000000000000..b4998402b8c6
--- /dev/null
+++ b/drivers/leds/leds-cht-wcove.c
@@ -0,0 +1,476 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for LEDs connected to the Intel Cherry Trail Whiskey Cove PMIC
+ *
+ * Copyright 2019 Yauhen Kharuzhy <jekhor@gmail.com>
+ * Copyright 2023 Hans de Goede <hansg@kernel.org>
+ *
+ * Register info comes from the Lenovo Yoga Book Android opensource code
+ * available from Lenovo. File lenovo_yb1_x90f_l_osc_201803.7z path in the 7z:
+ * YB1_source_code/kernel/cht/drivers/misc/charger_gp_led.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/suspend.h>
+
+#define CHT_WC_LED1_CTRL 0x5e1f
+#define CHT_WC_LED1_FSM 0x5e20
+#define CHT_WC_LED1_PWM 0x5e21
+
+#define CHT_WC_LED2_CTRL 0x4fdf
+#define CHT_WC_LED2_FSM 0x4fe0
+#define CHT_WC_LED2_PWM 0x4fe1
+
+#define CHT_WC_LED1_SWCTL BIT(0) /* HW or SW control of charging led */
+#define CHT_WC_LED1_ON BIT(1)
+
+#define CHT_WC_LED2_ON BIT(0)
+#define CHT_WC_LED_I_MA2_5 (2 << 2) /* LED current limit */
+#define CHT_WC_LED_I_MASK GENMASK(3, 2) /* LED current limit mask */
+
+#define CHT_WC_LED_F_1_4_HZ (0 << 4)
+#define CHT_WC_LED_F_1_2_HZ (1 << 4)
+#define CHT_WC_LED_F_1_HZ (2 << 4)
+#define CHT_WC_LED_F_2_HZ (3 << 4)
+#define CHT_WC_LED_F_MASK GENMASK(5, 4)
+
+#define CHT_WC_LED_EFF_OFF (0 << 1)
+#define CHT_WC_LED_EFF_ON (1 << 1)
+#define CHT_WC_LED_EFF_BLINKING (2 << 1)
+#define CHT_WC_LED_EFF_BREATHING (3 << 1)
+#define CHT_WC_LED_EFF_MASK GENMASK(2, 1)
+
+#define CHT_WC_LED_COUNT 2
+
+struct cht_wc_led_regs {
+ /* Register addresses */
+ u16 ctrl;
+ u16 fsm;
+ u16 pwm;
+ /* Mask + values for turning the LED on/off */
+ u8 on_off_mask;
+ u8 on_val;
+ u8 off_val;
+};
+
+struct cht_wc_led_saved_regs {
+ unsigned int ctrl;
+ unsigned int fsm;
+ unsigned int pwm;
+};
+
+struct cht_wc_led {
+ struct led_classdev cdev;
+ const struct cht_wc_led_regs *regs;
+ struct regmap *regmap;
+ struct mutex mutex;
+ struct cht_wc_led_saved_regs saved_regs;
+};
+
+struct cht_wc_leds {
+ struct cht_wc_led leds[CHT_WC_LED_COUNT];
+ /* Saved LED1 initial register values */
+ struct cht_wc_led_saved_regs led1_initial_regs;
+};
+
+static const struct cht_wc_led_regs cht_wc_led_regs[CHT_WC_LED_COUNT] = {
+ {
+ .ctrl = CHT_WC_LED1_CTRL,
+ .fsm = CHT_WC_LED1_FSM,
+ .pwm = CHT_WC_LED1_PWM,
+ .on_off_mask = CHT_WC_LED1_SWCTL | CHT_WC_LED1_ON,
+ .on_val = CHT_WC_LED1_SWCTL | CHT_WC_LED1_ON,
+ .off_val = CHT_WC_LED1_SWCTL,
+ },
+ {
+ .ctrl = CHT_WC_LED2_CTRL,
+ .fsm = CHT_WC_LED2_FSM,
+ .pwm = CHT_WC_LED2_PWM,
+ .on_off_mask = CHT_WC_LED2_ON,
+ .on_val = CHT_WC_LED2_ON,
+ .off_val = 0,
+ },
+};
+
+static const char * const cht_wc_leds_names[CHT_WC_LED_COUNT] = {
+ "platform::" LED_FUNCTION_CHARGING,
+ "platform::" LED_FUNCTION_INDICATOR,
+};
+
+static int cht_wc_leds_brightness_set(struct led_classdev *cdev,
+ enum led_brightness value)
+{
+ struct cht_wc_led *led = container_of(cdev, struct cht_wc_led, cdev);
+ int ret;
+
+ mutex_lock(&led->mutex);
+
+ if (!value) {
+ ret = regmap_update_bits(led->regmap, led->regs->ctrl,
+ led->regs->on_off_mask, led->regs->off_val);
+ if (ret < 0) {
+ dev_err(cdev->dev, "Failed to turn off: %d\n", ret);
+ goto out;
+ }
+
+ /* Disable HW blinking */
+ ret = regmap_update_bits(led->regmap, led->regs->fsm,
+ CHT_WC_LED_EFF_MASK, CHT_WC_LED_EFF_ON);
+ if (ret < 0)
+ dev_err(cdev->dev, "Failed to update LED FSM reg: %d\n", ret);
+ } else {
+ ret = regmap_write(led->regmap, led->regs->pwm, value);
+ if (ret < 0) {
+ dev_err(cdev->dev, "Failed to set brightness: %d\n", ret);
+ goto out;
+ }
+
+ ret = regmap_update_bits(led->regmap, led->regs->ctrl,
+ led->regs->on_off_mask, led->regs->on_val);
+ if (ret < 0)
+ dev_err(cdev->dev, "Failed to turn on: %d\n", ret);
+ }
+out:
+ mutex_unlock(&led->mutex);
+ return ret;
+}
+
+static enum led_brightness cht_wc_leds_brightness_get(struct led_classdev *cdev)
+{
+ struct cht_wc_led *led = container_of(cdev, struct cht_wc_led, cdev);
+ unsigned int val;
+ int ret;
+
+ mutex_lock(&led->mutex);
+
+ ret = regmap_read(led->regmap, led->regs->ctrl, &val);
+ if (ret < 0) {
+ dev_err(cdev->dev, "Failed to read LED CTRL reg: %d\n", ret);
+ ret = 0;
+ goto done;
+ }
+
+ val &= led->regs->on_off_mask;
+ if (val != led->regs->on_val) {
+ ret = 0;
+ goto done;
+ }
+
+ ret = regmap_read(led->regmap, led->regs->pwm, &val);
+ if (ret < 0) {
+ dev_err(cdev->dev, "Failed to read LED PWM reg: %d\n", ret);
+ ret = 0;
+ goto done;
+ }
+
+ ret = val;
+done:
+ mutex_unlock(&led->mutex);
+
+ return ret;
+}
+
+/* Return blinking period for given CTRL reg value */
+static unsigned long cht_wc_leds_get_period(int ctrl)
+{
+ ctrl &= CHT_WC_LED_F_MASK;
+
+ switch (ctrl) {
+ case CHT_WC_LED_F_1_4_HZ:
+ return 1000 * 4;
+ case CHT_WC_LED_F_1_2_HZ:
+ return 1000 * 2;
+ case CHT_WC_LED_F_1_HZ:
+ return 1000;
+ case CHT_WC_LED_F_2_HZ:
+ return 1000 / 2;
+ }
+
+ return 0;
+}
+
+/*
+ * Find suitable hardware blink mode for given period.
+ * period < 750 ms - select 2 HZ
+ * 750 ms <= period < 1500 ms - select 1 HZ
+ * 1500 ms <= period < 3000 ms - select 1/2 HZ
+ * 3000 ms <= period < 5000 ms - select 1/4 HZ
+ * 5000 ms <= period - return -1
+ */
+static int cht_wc_leds_find_freq(unsigned long period)
+{
+ if (period < 750)
+ return CHT_WC_LED_F_2_HZ;
+ else if (period < 1500)
+ return CHT_WC_LED_F_1_HZ;
+ else if (period < 3000)
+ return CHT_WC_LED_F_1_2_HZ;
+ else if (period < 5000)
+ return CHT_WC_LED_F_1_4_HZ;
+ else
+ return -1;
+}
+
+static int cht_wc_leds_set_effect(struct led_classdev *cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off,
+ u8 effect)
+{
+ struct cht_wc_led *led = container_of(cdev, struct cht_wc_led, cdev);
+ int ctrl, ret;
+
+ mutex_lock(&led->mutex);
+
+ /* Blink with 1 Hz as default if nothing specified */
+ if (!*delay_on && !*delay_off)
+ *delay_on = *delay_off = 500;
+
+ ctrl = cht_wc_leds_find_freq(*delay_on + *delay_off);
+ if (ctrl < 0) {
+ /* Disable HW blinking */
+ ret = regmap_update_bits(led->regmap, led->regs->fsm,
+ CHT_WC_LED_EFF_MASK, CHT_WC_LED_EFF_ON);
+ if (ret < 0)
+ dev_err(cdev->dev, "Failed to update LED FSM reg: %d\n", ret);
+
+ /* Fallback to software timer */
+ *delay_on = *delay_off = 0;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = regmap_update_bits(led->regmap, led->regs->fsm,
+ CHT_WC_LED_EFF_MASK, effect);
+ if (ret < 0)
+ dev_err(cdev->dev, "Failed to update LED FSM reg: %d\n", ret);
+
+ /* Set the frequency and make sure the LED is on */
+ ret = regmap_update_bits(led->regmap, led->regs->ctrl,
+ CHT_WC_LED_F_MASK | led->regs->on_off_mask,
+ ctrl | led->regs->on_val);
+ if (ret < 0)
+ dev_err(cdev->dev, "Failed to update LED CTRL reg: %d\n", ret);
+
+ *delay_off = *delay_on = cht_wc_leds_get_period(ctrl) / 2;
+
+done:
+ mutex_unlock(&led->mutex);
+
+ return ret;
+}
+
+static int cht_wc_leds_blink_set(struct led_classdev *cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ u8 effect = CHT_WC_LED_EFF_BLINKING;
+
+ /*
+ * The desired default behavior of LED1 / the charge LED is breathing
+ * while charging and on/solid when full. Since triggers cannot select
+ * breathing, blink_set() gets called when charging. Use slow breathing
+ * when the default "charging-blink-full-solid" trigger is used to
+ * achieve the desired default behavior.
+ */
+ if (cdev->flags & LED_INIT_DEFAULT_TRIGGER) {
+ *delay_on = *delay_off = 1000;
+ effect = CHT_WC_LED_EFF_BREATHING;
+ }
+
+ return cht_wc_leds_set_effect(cdev, delay_on, delay_off, effect);
+}
+
+static int cht_wc_leds_pattern_set(struct led_classdev *cdev,
+ struct led_pattern *pattern,
+ u32 len, int repeat)
+{
+ unsigned long delay_off, delay_on;
+
+ if (repeat > 0 || len != 2 ||
+ pattern[0].brightness != 0 || pattern[1].brightness != 1 ||
+ pattern[0].delta_t != pattern[1].delta_t ||
+ (pattern[0].delta_t != 250 && pattern[0].delta_t != 500 &&
+ pattern[0].delta_t != 1000 && pattern[0].delta_t != 2000))
+ return -EINVAL;
+
+ delay_off = pattern[0].delta_t;
+ delay_on = pattern[1].delta_t;
+
+ return cht_wc_leds_set_effect(cdev, &delay_on, &delay_off, CHT_WC_LED_EFF_BREATHING);
+}
+
+static int cht_wc_leds_pattern_clear(struct led_classdev *cdev)
+{
+ return cht_wc_leds_brightness_set(cdev, 0);
+}
+
+static int cht_wc_led_save_regs(struct cht_wc_led *led,
+ struct cht_wc_led_saved_regs *saved_regs)
+{
+ int ret;
+
+ ret = regmap_read(led->regmap, led->regs->ctrl, &saved_regs->ctrl);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(led->regmap, led->regs->fsm, &saved_regs->fsm);
+ if (ret < 0)
+ return ret;
+
+ return regmap_read(led->regmap, led->regs->pwm, &saved_regs->pwm);
+}
+
+static void cht_wc_led_restore_regs(struct cht_wc_led *led,
+ const struct cht_wc_led_saved_regs *saved_regs)
+{
+ regmap_write(led->regmap, led->regs->ctrl, saved_regs->ctrl);
+ regmap_write(led->regmap, led->regs->fsm, saved_regs->fsm);
+ regmap_write(led->regmap, led->regs->pwm, saved_regs->pwm);
+}
+
+static int cht_wc_leds_probe(struct platform_device *pdev)
+{
+ struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
+ struct cht_wc_leds *leds;
+ int ret;
+ int i;
+
+ /*
+ * On the Lenovo Yoga Tab 3 the LED1 driver output is actually
+ * connected to a haptic feedback motor rather then a LED.
+ * So do not register a LED classdev there (LED2 is unused).
+ */
+ if (pmic->cht_wc_model == INTEL_CHT_WC_LENOVO_YT3_X90)
+ return -ENODEV;
+
+ leds = devm_kzalloc(&pdev->dev, sizeof(*leds), GFP_KERNEL);
+ if (!leds)
+ return -ENOMEM;
+
+ /*
+ * LED1 might be in hw-controlled mode when this driver gets loaded; and
+ * since the PMIC is always powered by the battery any changes made are
+ * permanent. Save LED1 regs to restore them on remove() or shutdown().
+ */
+ leds->leds[0].regs = &cht_wc_led_regs[0];
+ leds->leds[0].regmap = pmic->regmap;
+ ret = cht_wc_led_save_regs(&leds->leds[0], &leds->led1_initial_regs);
+ if (ret < 0)
+ return ret;
+
+ /* Set LED1 default trigger based on machine model */
+ switch (pmic->cht_wc_model) {
+ case INTEL_CHT_WC_GPD_WIN_POCKET:
+ leds->leds[0].cdev.default_trigger = "max170xx_battery-charging-blink-full-solid";
+ break;
+ case INTEL_CHT_WC_XIAOMI_MIPAD2:
+ leds->leds[0].cdev.default_trigger = "bq27520-0-charging-blink-full-solid";
+ break;
+ case INTEL_CHT_WC_LENOVO_YOGABOOK1:
+ leds->leds[0].cdev.default_trigger = "bq27542-0-charging-blink-full-solid";
+ break;
+ default:
+ dev_warn(&pdev->dev, "Unknown model, no default charging trigger\n");
+ break;
+ }
+
+ for (i = 0; i < CHT_WC_LED_COUNT; i++) {
+ struct cht_wc_led *led = &leds->leds[i];
+
+ led->regs = &cht_wc_led_regs[i];
+ led->regmap = pmic->regmap;
+ mutex_init(&led->mutex);
+ led->cdev.name = cht_wc_leds_names[i];
+ led->cdev.brightness_set_blocking = cht_wc_leds_brightness_set;
+ led->cdev.brightness_get = cht_wc_leds_brightness_get;
+ led->cdev.blink_set = cht_wc_leds_blink_set;
+ led->cdev.pattern_set = cht_wc_leds_pattern_set;
+ led->cdev.pattern_clear = cht_wc_leds_pattern_clear;
+ led->cdev.max_brightness = 255;
+
+ ret = led_classdev_register(&pdev->dev, &led->cdev);
+ if (ret < 0)
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, leds);
+ return 0;
+}
+
+static void cht_wc_leds_remove(struct platform_device *pdev)
+{
+ struct cht_wc_leds *leds = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < CHT_WC_LED_COUNT; i++)
+ led_classdev_unregister(&leds->leds[i].cdev);
+
+ /* Restore LED1 regs if hw-control was active else leave LED1 off */
+ if (!(leds->led1_initial_regs.ctrl & CHT_WC_LED1_SWCTL))
+ cht_wc_led_restore_regs(&leds->leds[0], &leds->led1_initial_regs);
+}
+
+static void cht_wc_leds_disable(struct platform_device *pdev)
+{
+ struct cht_wc_leds *leds = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < CHT_WC_LED_COUNT; i++)
+ cht_wc_leds_brightness_set(&leds->leds[i].cdev, 0);
+
+ /* Restore LED1 regs if hw-control was active else leave LED1 off */
+ if (!(leds->led1_initial_regs.ctrl & CHT_WC_LED1_SWCTL))
+ cht_wc_led_restore_regs(&leds->leds[0], &leds->led1_initial_regs);
+}
+
+/* On suspend save current settings and turn LEDs off */
+static int cht_wc_leds_suspend(struct device *dev)
+{
+ struct cht_wc_leds *leds = dev_get_drvdata(dev);
+ int i, ret;
+
+ for (i = 0; i < CHT_WC_LED_COUNT; i++) {
+ ret = cht_wc_led_save_regs(&leds->leds[i], &leds->leds[i].saved_regs);
+ if (ret < 0)
+ return ret;
+ }
+
+ cht_wc_leds_disable(to_platform_device(dev));
+ return 0;
+}
+
+/* On resume restore the saved settings */
+static int cht_wc_leds_resume(struct device *dev)
+{
+ struct cht_wc_leds *leds = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < CHT_WC_LED_COUNT; i++)
+ cht_wc_led_restore_regs(&leds->leds[i], &leds->leds[i].saved_regs);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(cht_wc_leds_pm, cht_wc_leds_suspend, cht_wc_leds_resume);
+
+static struct platform_driver cht_wc_leds_driver = {
+ .probe = cht_wc_leds_probe,
+ .remove_new = cht_wc_leds_remove,
+ .shutdown = cht_wc_leds_disable,
+ .driver = {
+ .name = "cht_wcove_leds",
+ .pm = pm_sleep_ptr(&cht_wc_leds_pm),
+ },
+};
+module_platform_driver(cht_wc_leds_driver);
+
+MODULE_ALIAS("platform:cht_wcove_leds");
+MODULE_DESCRIPTION("Intel Cherry Trail Whiskey Cove PMIC LEDs driver");
+MODULE_AUTHOR("Yauhen Kharuzhy <jekhor@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index ce4e79939731..7bfe40a6bfdd 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -13,6 +13,7 @@
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>
@@ -77,6 +78,7 @@ static int create_gpio_led(const struct gpio_led *template,
struct fwnode_handle *fwnode, gpio_blink_set_t blink_set)
{
struct led_init_data init_data = {};
+ struct pinctrl *pinctrl;
int ret, state;
led_dat->cdev.default_trigger = template->default_trigger;
@@ -119,6 +121,22 @@ static int create_gpio_led(const struct gpio_led *template,
&init_data);
}
+ if (ret)
+ return ret;
+
+ pinctrl = devm_pinctrl_get_select_default(led_dat->cdev.dev);
+ if (IS_ERR(pinctrl)) {
+ ret = PTR_ERR(pinctrl);
+ if (ret != -ENODEV) {
+ dev_warn(led_dat->cdev.dev,
+ "Failed to select %pOF pinctrl: %d\n",
+ to_of_node(fwnode), ret);
+ } else {
+ /* pinctrl-%d not present, not an error */
+ ret = 0;
+ }
+ }
+
return ret;
}
diff --git a/drivers/leds/leds-is31fl319x.c b/drivers/leds/leds-is31fl319x.c
index 7c908414ac7e..66c65741202e 100644
--- a/drivers/leds/leds-is31fl319x.c
+++ b/drivers/leds/leds-is31fl319x.c
@@ -602,7 +602,7 @@ static struct i2c_driver is31fl319x_driver = {
.name = "leds-is31fl319x",
.of_match_table = of_is31fl319x_match,
},
- .probe_new = is31fl319x_probe,
+ .probe = is31fl319x_probe,
.id_table = is31fl319x_id,
};
diff --git a/drivers/leds/leds-is31fl32xx.c b/drivers/leds/leds-is31fl32xx.c
index 799191859ce0..72cb56d305c4 100644
--- a/drivers/leds/leds-is31fl32xx.c
+++ b/drivers/leds/leds-is31fl32xx.c
@@ -488,7 +488,7 @@ static struct i2c_driver is31fl32xx_driver = {
.name = "is31fl32xx",
.of_match_table = of_is31fl32xx_match,
},
- .probe_new = is31fl32xx_probe,
+ .probe = is31fl32xx_probe,
.remove = is31fl32xx_remove,
.id_table = is31fl32xx_id,
};
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c
index a9a2018592ff..a2feef8e4ac5 100644
--- a/drivers/leds/leds-lm3530.c
+++ b/drivers/leds/leds-lm3530.c
@@ -484,7 +484,7 @@ static const struct i2c_device_id lm3530_id[] = {
MODULE_DEVICE_TABLE(i2c, lm3530_id);
static struct i2c_driver lm3530_i2c_driver = {
- .probe_new = lm3530_probe,
+ .probe = lm3530_probe,
.remove = lm3530_remove,
.id_table = lm3530_id,
.driver = {
diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c
index a08c09129a68..13662a4aa1f2 100644
--- a/drivers/leds/leds-lm3532.c
+++ b/drivers/leds/leds-lm3532.c
@@ -726,7 +726,7 @@ static const struct i2c_device_id lm3532_id[] = {
MODULE_DEVICE_TABLE(i2c, lm3532_id);
static struct i2c_driver lm3532_i2c_driver = {
- .probe_new = lm3532_probe,
+ .probe = lm3532_probe,
.remove = lm3532_remove,
.id_table = lm3532_id,
.driver = {
diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c
index 612873070ca4..f68771b9eac6 100644
--- a/drivers/leds/leds-lm355x.c
+++ b/drivers/leds/leds-lm355x.c
@@ -516,7 +516,7 @@ static struct i2c_driver lm355x_i2c_driver = {
.name = LM355x_NAME,
.pm = NULL,
},
- .probe_new = lm355x_probe,
+ .probe = lm355x_probe,
.remove = lm355x_remove,
.id_table = lm355x_id,
};
diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c
index b75ee3546c2e..6eee52e211be 100644
--- a/drivers/leds/leds-lm3642.c
+++ b/drivers/leds/leds-lm3642.c
@@ -401,7 +401,7 @@ static struct i2c_driver lm3642_i2c_driver = {
.name = LM3642_NAME,
.pm = NULL,
},
- .probe_new = lm3642_probe,
+ .probe = lm3642_probe,
.remove = lm3642_remove,
.id_table = lm3642_id,
};
diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c
index 66126d0666f5..f8ad61e47a19 100644
--- a/drivers/leds/leds-lm3692x.c
+++ b/drivers/leds/leds-lm3692x.c
@@ -518,7 +518,7 @@ static struct i2c_driver lm3692x_driver = {
.name = "lm3692x",
.of_match_table = of_lm3692x_leds_match,
},
- .probe_new = lm3692x_probe,
+ .probe = lm3692x_probe,
.remove = lm3692x_remove,
.id_table = lm3692x_id,
};
diff --git a/drivers/leds/leds-lm3697.c b/drivers/leds/leds-lm3697.c
index 10e904bf40a0..cfb8ac220db6 100644
--- a/drivers/leds/leds-lm3697.c
+++ b/drivers/leds/leds-lm3697.c
@@ -376,7 +376,7 @@ static struct i2c_driver lm3697_driver = {
.name = "lm3697",
.of_match_table = of_lm3697_leds_match,
},
- .probe_new = lm3697_probe,
+ .probe = lm3697_probe,
.remove = lm3697_remove,
.id_table = lm3697_id,
};
diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c
index be47c66b2e00..8ea746c499d1 100644
--- a/drivers/leds/leds-lp3944.c
+++ b/drivers/leds/leds-lp3944.c
@@ -427,7 +427,7 @@ static struct i2c_driver lp3944_driver = {
.driver = {
.name = "lp3944",
},
- .probe_new = lp3944_probe,
+ .probe = lp3944_probe,
.remove = lp3944_remove,
.id_table = lp3944_id,
};
diff --git a/drivers/leds/leds-lp3952.c b/drivers/leds/leds-lp3952.c
index 24b2e0f9080d..3bd55652a706 100644
--- a/drivers/leds/leds-lp3952.c
+++ b/drivers/leds/leds-lp3952.c
@@ -273,7 +273,7 @@ static struct i2c_driver lp3952_i2c_driver = {
.driver = {
.name = LP3952_NAME,
},
- .probe_new = lp3952_probe,
+ .probe = lp3952_probe,
.remove = lp3952_remove,
.id_table = lp3952_id,
};
diff --git a/drivers/leds/leds-lp50xx.c b/drivers/leds/leds-lp50xx.c
index 28d6b39fa72d..68c4d9967d68 100644
--- a/drivers/leds/leds-lp50xx.c
+++ b/drivers/leds/leds-lp50xx.c
@@ -608,7 +608,7 @@ static struct i2c_driver lp50xx_driver = {
.name = "lp50xx",
.of_match_table = of_lp50xx_leds_match,
},
- .probe_new = lp50xx_probe,
+ .probe = lp50xx_probe,
.remove = lp50xx_remove,
.id_table = lp50xx_id,
};
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index a004af8e22c7..030c040fdf6d 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -58,14 +58,11 @@
/* CONFIG register */
#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */
#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */
-#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */
-#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */
-#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */
-#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */
+#define LP5521_CP_MODE_MASK 0x18 /* Charge pump mode */
+#define LP5521_CP_MODE_SHIFT 3
#define LP5521_R_TO_BATT 0x04 /* R out: 0 = CP, 1 = Vbat */
#define LP5521_CLK_INT 0x01 /* Internal clock */
-#define LP5521_DEFAULT_CFG \
- (LP5521_PWM_HF | LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO)
+#define LP5521_DEFAULT_CFG (LP5521_PWM_HF | LP5521_PWRSAVE_EN)
/* Status */
#define LP5521_EXT_CLK_USED 0x08
@@ -310,6 +307,8 @@ static int lp5521_post_init_device(struct lp55xx_chip *chip)
if (!lp55xx_is_extclk_used(chip))
val |= LP5521_CLK_INT;
+ val |= (chip->pdata->charge_pump_mode << LP5521_CP_MODE_SHIFT) & LP5521_CP_MODE_MASK;
+
ret = lp55xx_write(chip, LP5521_REG_CONFIG, val);
if (ret)
return ret;
@@ -608,7 +607,7 @@ static struct i2c_driver lp5521_driver = {
.name = "lp5521",
.of_match_table = of_match_ptr(of_lp5521_leds_match),
},
- .probe_new = lp5521_probe,
+ .probe = lp5521_probe,
.remove = lp5521_remove,
.id_table = lp5521_id,
};
diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c
index 55da914b8e5c..daa6a165fba6 100644
--- a/drivers/leds/leds-lp5523.c
+++ b/drivers/leds/leds-lp5523.c
@@ -57,8 +57,11 @@
#define LP5523_AUTO_INC 0x40
#define LP5523_PWR_SAVE 0x20
#define LP5523_PWM_PWR_SAVE 0x04
-#define LP5523_CP_AUTO 0x18
+#define LP5523_CP_MODE_MASK 0x18
+#define LP5523_CP_MODE_SHIFT 3
#define LP5523_AUTO_CLK 0x02
+#define LP5523_DEFAULT_CONFIG \
+ (LP5523_AUTO_INC | LP5523_PWR_SAVE | LP5523_AUTO_CLK | LP5523_PWM_PWR_SAVE)
#define LP5523_EN_LEDTEST 0x80
#define LP5523_LEDTEST_DONE 0x80
@@ -125,6 +128,7 @@ static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current)
static int lp5523_post_init_device(struct lp55xx_chip *chip)
{
int ret;
+ int val;
ret = lp55xx_write(chip, LP5523_REG_ENABLE, LP5523_ENABLE);
if (ret)
@@ -133,10 +137,10 @@ static int lp5523_post_init_device(struct lp55xx_chip *chip)
/* Chip startup time is 500 us, 1 - 2 ms gives some margin */
usleep_range(1000, 2000);
- ret = lp55xx_write(chip, LP5523_REG_CONFIG,
- LP5523_AUTO_INC | LP5523_PWR_SAVE |
- LP5523_CP_AUTO | LP5523_AUTO_CLK |
- LP5523_PWM_PWR_SAVE);
+ val = LP5523_DEFAULT_CONFIG;
+ val |= (chip->pdata->charge_pump_mode << LP5523_CP_MODE_SHIFT) & LP5523_CP_MODE_MASK;
+
+ ret = lp55xx_write(chip, LP5523_REG_CONFIG, val);
if (ret)
return ret;
@@ -983,7 +987,7 @@ static struct i2c_driver lp5523_driver = {
.name = "lp5523x",
.of_match_table = of_match_ptr(of_lp5523_leds_match),
},
- .probe_new = lp5523_probe,
+ .probe = lp5523_probe,
.remove = lp5523_remove,
.id_table = lp5523_id,
};
diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c
index b5d877faf6d7..4565cc12cea8 100644
--- a/drivers/leds/leds-lp5562.c
+++ b/drivers/leds/leds-lp5562.c
@@ -603,7 +603,7 @@ static struct i2c_driver lp5562_driver = {
.name = "lp5562",
.of_match_table = of_match_ptr(of_lp5562_leds_match),
},
- .probe_new = lp5562_probe,
+ .probe = lp5562_probe,
.remove = lp5562_remove,
.id_table = lp5562_id,
};
diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c
index c1940964067a..77bb26906ea6 100644
--- a/drivers/leds/leds-lp55xx-common.c
+++ b/drivers/leds/leds-lp55xx-common.c
@@ -18,6 +18,7 @@
#include <linux/platform_data/leds-lp55xx.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
+#include <dt-bindings/leds/leds-lp55xx.h>
#include "leds-lp55xx-common.h"
@@ -691,6 +692,14 @@ struct lp55xx_platform_data *lp55xx_of_populate_pdata(struct device *dev,
i++;
}
+ if (of_property_read_u32(np, "ti,charge-pump-mode", &pdata->charge_pump_mode))
+ pdata->charge_pump_mode = LP55XX_CP_AUTO;
+
+ if (pdata->charge_pump_mode > LP55XX_CP_AUTO) {
+ dev_err(dev, "invalid charge pump mode %d\n", pdata->charge_pump_mode);
+ return ERR_PTR(-EINVAL);
+ }
+
of_property_read_string(np, "label", &pdata->label);
of_property_read_u8(np, "clock-mode", &pdata->clock_mode);
diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c
index 165d6423a928..f11886aa8965 100644
--- a/drivers/leds/leds-lp8501.c
+++ b/drivers/leds/leds-lp8501.c
@@ -53,10 +53,10 @@
#define LP8501_PWM_PSAVE BIT(7)
#define LP8501_AUTO_INC BIT(6)
#define LP8501_PWR_SAVE BIT(5)
-#define LP8501_CP_AUTO 0x18
+#define LP8501_CP_MODE_MASK 0x18
+#define LP8501_CP_MODE_SHIFT 3
#define LP8501_INT_CLK BIT(0)
-#define LP8501_DEFAULT_CFG \
- (LP8501_PWM_PSAVE | LP8501_AUTO_INC | LP8501_PWR_SAVE | LP8501_CP_AUTO)
+#define LP8501_DEFAULT_CFG (LP8501_PWM_PSAVE | LP8501_AUTO_INC | LP8501_PWR_SAVE)
#define LP8501_REG_RESET 0x3D
#define LP8501_RESET 0xFF
@@ -102,6 +102,8 @@ static int lp8501_post_init_device(struct lp55xx_chip *chip)
if (chip->pdata->clock_mode != LP55XX_CLOCK_EXT)
val |= LP8501_INT_CLK;
+ val |= (chip->pdata->charge_pump_mode << LP8501_CP_MODE_SHIFT) & LP8501_CP_MODE_MASK;
+
ret = lp55xx_write(chip, LP8501_REG_CONFIG, val);
if (ret)
return ret;
@@ -392,7 +394,7 @@ static struct i2c_driver lp8501_driver = {
.name = "lp8501",
.of_match_table = of_match_ptr(of_lp8501_leds_match),
},
- .probe_new = lp8501_probe,
+ .probe = lp8501_probe,
.remove = lp8501_remove,
.id_table = lp8501_id,
};
diff --git a/drivers/leds/leds-lp8860.c b/drivers/leds/leds-lp8860.c
index 221b386443bc..19b621012e58 100644
--- a/drivers/leds/leds-lp8860.c
+++ b/drivers/leds/leds-lp8860.c
@@ -475,7 +475,7 @@ static struct i2c_driver lp8860_driver = {
.name = "lp8860",
.of_match_table = of_lp8860_leds_match,
},
- .probe_new = lp8860_probe,
+ .probe = lp8860_probe,
.remove = lp8860_remove,
.id_table = lp8860_id,
};
diff --git a/drivers/leds/leds-mt6323.c b/drivers/leds/leds-mt6323.c
index 17ee88043f52..24f35bdb55fb 100644
--- a/drivers/leds/leds-mt6323.c
+++ b/drivers/leds/leds-mt6323.c
@@ -14,61 +14,54 @@
#include <linux/regmap.h>
/*
- * Register field for MT6323_TOP_CKPDN0 to enable
+ * Register field for TOP_CKPDN0 to enable
* 32K clock common for LED device.
*/
-#define MT6323_RG_DRV_32K_CK_PDN BIT(11)
-#define MT6323_RG_DRV_32K_CK_PDN_MASK BIT(11)
+#define RG_DRV_32K_CK_PDN BIT(11)
+#define RG_DRV_32K_CK_PDN_MASK BIT(11)
+
+/* 32K/1M/6M clock common for WLED device */
+#define RG_VWLED_1M_CK_PDN BIT(0)
+#define RG_VWLED_32K_CK_PDN BIT(12)
+#define RG_VWLED_6M_CK_PDN BIT(13)
/*
- * Register field for MT6323_TOP_CKPDN2 to enable
+ * Register field for TOP_CKPDN2 to enable
* individual clock for LED device.
*/
-#define MT6323_RG_ISINK_CK_PDN(i) BIT(i)
-#define MT6323_RG_ISINK_CK_PDN_MASK(i) BIT(i)
+#define RG_ISINK_CK_PDN(i) BIT(i)
+#define RG_ISINK_CK_PDN_MASK(i) BIT(i)
/*
- * Register field for MT6323_TOP_CKCON1 to select
+ * Register field for TOP_CKCON1 to select
* clock source.
*/
-#define MT6323_RG_ISINK_CK_SEL_MASK(i) (BIT(10) << (i))
+#define RG_ISINK_CK_SEL_MASK(i) (BIT(10) << (i))
-/*
- * Register for MT6323_ISINK_CON0 to setup the
- * duty cycle of the blink.
- */
-#define MT6323_ISINK_CON0(i) (MT6323_ISINK0_CON0 + 0x8 * (i))
-#define MT6323_ISINK_DIM_DUTY_MASK (0x1f << 8)
-#define MT6323_ISINK_DIM_DUTY(i) (((i) << 8) & \
- MT6323_ISINK_DIM_DUTY_MASK)
-
-/* Register to setup the period of the blink. */
-#define MT6323_ISINK_CON1(i) (MT6323_ISINK0_CON1 + 0x8 * (i))
-#define MT6323_ISINK_DIM_FSEL_MASK (0xffff)
-#define MT6323_ISINK_DIM_FSEL(i) ((i) & MT6323_ISINK_DIM_FSEL_MASK)
-
-/* Register to control the brightness. */
-#define MT6323_ISINK_CON2(i) (MT6323_ISINK0_CON2 + 0x8 * (i))
-#define MT6323_ISINK_CH_STEP_SHIFT 12
-#define MT6323_ISINK_CH_STEP_MASK (0x7 << 12)
-#define MT6323_ISINK_CH_STEP(i) (((i) << 12) & \
- MT6323_ISINK_CH_STEP_MASK)
-#define MT6323_ISINK_SFSTR0_TC_MASK (0x3 << 1)
-#define MT6323_ISINK_SFSTR0_TC(i) (((i) << 1) & \
- MT6323_ISINK_SFSTR0_TC_MASK)
-#define MT6323_ISINK_SFSTR0_EN_MASK BIT(0)
-#define MT6323_ISINK_SFSTR0_EN BIT(0)
+#define ISINK_CON(r, i) (r + 0x8 * (i))
+
+/* ISINK_CON0: Register to setup the duty cycle of the blink. */
+#define ISINK_DIM_DUTY_MASK (0x1f << 8)
+#define ISINK_DIM_DUTY(i) (((i) << 8) & ISINK_DIM_DUTY_MASK)
+
+/* ISINK_CON1: Register to setup the period of the blink. */
+#define ISINK_DIM_FSEL_MASK (0xffff)
+#define ISINK_DIM_FSEL(i) ((i) & ISINK_DIM_FSEL_MASK)
+
+/* ISINK_CON2: Register to control the brightness. */
+#define ISINK_CH_STEP_SHIFT 12
+#define ISINK_CH_STEP_MASK (0x7 << 12)
+#define ISINK_CH_STEP(i) (((i) << 12) & ISINK_CH_STEP_MASK)
+#define ISINK_SFSTR0_TC_MASK (0x3 << 1)
+#define ISINK_SFSTR0_TC(i) (((i) << 1) & ISINK_SFSTR0_TC_MASK)
+#define ISINK_SFSTR0_EN_MASK BIT(0)
+#define ISINK_SFSTR0_EN BIT(0)
/* Register to LED channel enablement. */
-#define MT6323_ISINK_CH_EN_MASK(i) BIT(i)
-#define MT6323_ISINK_CH_EN(i) BIT(i)
+#define ISINK_CH_EN_MASK(i) BIT(i)
+#define ISINK_CH_EN(i) BIT(i)
-#define MT6323_MAX_PERIOD 10000
-#define MT6323_MAX_LEDS 4
-#define MT6323_MAX_BRIGHTNESS 6
-#define MT6323_UNIT_DUTY 3125
-#define MT6323_CAL_HW_DUTY(o, p) DIV_ROUND_CLOSEST((o) * 100000ul,\
- (p) * MT6323_UNIT_DUTY)
+#define MAX_SUPPORTED_LEDS 8
struct mt6323_leds;
@@ -87,11 +80,62 @@ struct mt6323_led {
};
/**
+ * struct mt6323_regs - register spec for the LED device
+ * @top_ckpdn: Offset to ISINK_CKPDN[0..x] registers
+ * @num_top_ckpdn: Number of ISINK_CKPDN registers
+ * @top_ckcon: Offset to ISINK_CKCON[0..x] registers
+ * @num_top_ckcon: Number of ISINK_CKCON registers
+ * @isink_con: Offset to ISINKx_CON[0..x] registers
+ * @num_isink_con: Number of ISINKx_CON registers
+ * @isink_max_regs: Number of ISINK[0..x] registers
+ * @isink_en_ctrl: Offset to ISINK_EN_CTRL register
+ * @iwled_en_ctrl: Offset to IWLED_EN_CTRL register
+ */
+struct mt6323_regs {
+ const u16 *top_ckpdn;
+ u8 num_top_ckpdn;
+ const u16 *top_ckcon;
+ u8 num_top_ckcon;
+ const u16 *isink_con;
+ u8 num_isink_con;
+ u8 isink_max_regs;
+ u16 isink_en_ctrl;
+ u16 iwled_en_ctrl;
+};
+
+/**
+ * struct mt6323_hwspec - hardware specific parameters
+ * @max_period: Maximum period for all LEDs
+ * @max_leds: Maximum number of supported LEDs
+ * @max_wleds: Maximum number of WLEDs
+ * @max_brightness: Maximum brightness for all LEDs
+ * @unit_duty: Steps of duty per period
+ */
+struct mt6323_hwspec {
+ u16 max_period;
+ u8 max_leds;
+ u8 max_wleds;
+ u16 max_brightness;
+ u16 unit_duty;
+};
+
+/**
+ * struct mt6323_data - device specific data
+ * @regs: Register spec for this device
+ * @spec: Hardware specific parameters
+ */
+struct mt6323_data {
+ const struct mt6323_regs *regs;
+ const struct mt6323_hwspec *spec;
+};
+
+/**
* struct mt6323_leds - state container for holding LED controller
* of the driver
* @dev: the device pointer
* @hw: the underlying hardware providing shared
* bus for the register operations
+ * @pdata: device specific data
* @lock: the lock among process context
* @led: the array that contains the state of individual
* LED device
@@ -99,9 +143,10 @@ struct mt6323_led {
struct mt6323_leds {
struct device *dev;
struct mt6397_chip *hw;
+ const struct mt6323_data *pdata;
/* protect among process context */
struct mutex lock;
- struct mt6323_led *led[MT6323_MAX_LEDS];
+ struct mt6323_led *led[MAX_SUPPORTED_LEDS];
};
static int mt6323_led_hw_brightness(struct led_classdev *cdev,
@@ -109,6 +154,7 @@ static int mt6323_led_hw_brightness(struct led_classdev *cdev,
{
struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
struct mt6323_leds *leds = led->parent;
+ const struct mt6323_regs *regs = leds->pdata->regs;
struct regmap *regmap = leds->hw->regmap;
u32 con2_mask = 0, con2_val = 0;
int ret;
@@ -117,14 +163,14 @@ static int mt6323_led_hw_brightness(struct led_classdev *cdev,
* Setup current output for the corresponding
* brightness level.
*/
- con2_mask |= MT6323_ISINK_CH_STEP_MASK |
- MT6323_ISINK_SFSTR0_TC_MASK |
- MT6323_ISINK_SFSTR0_EN_MASK;
- con2_val |= MT6323_ISINK_CH_STEP(brightness - 1) |
- MT6323_ISINK_SFSTR0_TC(2) |
- MT6323_ISINK_SFSTR0_EN;
-
- ret = regmap_update_bits(regmap, MT6323_ISINK_CON2(led->id),
+ con2_mask |= ISINK_CH_STEP_MASK |
+ ISINK_SFSTR0_TC_MASK |
+ ISINK_SFSTR0_EN_MASK;
+ con2_val |= ISINK_CH_STEP(brightness - 1) |
+ ISINK_SFSTR0_TC(2) |
+ ISINK_SFSTR0_EN;
+
+ ret = regmap_update_bits(regmap, ISINK_CON(regs->isink_con[2], led->id),
con2_mask, con2_val);
return ret;
}
@@ -133,20 +179,21 @@ static int mt6323_led_hw_off(struct led_classdev *cdev)
{
struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
struct mt6323_leds *leds = led->parent;
+ const struct mt6323_regs *regs = leds->pdata->regs;
struct regmap *regmap = leds->hw->regmap;
unsigned int status;
int ret;
- status = MT6323_ISINK_CH_EN(led->id);
- ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL,
- MT6323_ISINK_CH_EN_MASK(led->id), ~status);
+ status = ISINK_CH_EN(led->id);
+ ret = regmap_update_bits(regmap, regs->isink_en_ctrl,
+ ISINK_CH_EN_MASK(led->id), ~status);
if (ret < 0)
return ret;
usleep_range(100, 300);
- ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2,
- MT6323_RG_ISINK_CK_PDN_MASK(led->id),
- MT6323_RG_ISINK_CK_PDN(led->id));
+ ret = regmap_update_bits(regmap, regs->top_ckpdn[2],
+ RG_ISINK_CK_PDN_MASK(led->id),
+ RG_ISINK_CK_PDN(led->id));
if (ret < 0)
return ret;
@@ -158,30 +205,31 @@ mt6323_get_led_hw_brightness(struct led_classdev *cdev)
{
struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
struct mt6323_leds *leds = led->parent;
+ const struct mt6323_regs *regs = leds->pdata->regs;
struct regmap *regmap = leds->hw->regmap;
unsigned int status;
int ret;
- ret = regmap_read(regmap, MT6323_TOP_CKPDN2, &status);
+ ret = regmap_read(regmap, regs->top_ckpdn[2], &status);
if (ret < 0)
return ret;
- if (status & MT6323_RG_ISINK_CK_PDN_MASK(led->id))
+ if (status & RG_ISINK_CK_PDN_MASK(led->id))
return 0;
- ret = regmap_read(regmap, MT6323_ISINK_EN_CTRL, &status);
+ ret = regmap_read(regmap, regs->isink_en_ctrl, &status);
if (ret < 0)
return ret;
- if (!(status & MT6323_ISINK_CH_EN(led->id)))
+ if (!(status & ISINK_CH_EN(led->id)))
return 0;
- ret = regmap_read(regmap, MT6323_ISINK_CON2(led->id), &status);
+ ret = regmap_read(regmap, ISINK_CON(regs->isink_con[2], led->id), &status);
if (ret < 0)
return ret;
- return ((status & MT6323_ISINK_CH_STEP_MASK)
- >> MT6323_ISINK_CH_STEP_SHIFT) + 1;
+ return ((status & ISINK_CH_STEP_MASK)
+ >> ISINK_CH_STEP_SHIFT) + 1;
}
static int mt6323_led_hw_on(struct led_classdev *cdev,
@@ -189,6 +237,7 @@ static int mt6323_led_hw_on(struct led_classdev *cdev,
{
struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
struct mt6323_leds *leds = led->parent;
+ const struct mt6323_regs *regs = leds->pdata->regs;
struct regmap *regmap = leds->hw->regmap;
unsigned int status;
int ret;
@@ -198,23 +247,23 @@ static int mt6323_led_hw_on(struct led_classdev *cdev,
* clock and channel and let work with continuous blink as
* the default.
*/
- ret = regmap_update_bits(regmap, MT6323_TOP_CKCON1,
- MT6323_RG_ISINK_CK_SEL_MASK(led->id), 0);
+ ret = regmap_update_bits(regmap, regs->top_ckcon[1],
+ RG_ISINK_CK_SEL_MASK(led->id), 0);
if (ret < 0)
return ret;
- status = MT6323_RG_ISINK_CK_PDN(led->id);
- ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2,
- MT6323_RG_ISINK_CK_PDN_MASK(led->id),
+ status = RG_ISINK_CK_PDN(led->id);
+ ret = regmap_update_bits(regmap, regs->top_ckpdn[2],
+ RG_ISINK_CK_PDN_MASK(led->id),
~status);
if (ret < 0)
return ret;
usleep_range(100, 300);
- ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL,
- MT6323_ISINK_CH_EN_MASK(led->id),
- MT6323_ISINK_CH_EN(led->id));
+ ret = regmap_update_bits(regmap, regs->isink_en_ctrl,
+ ISINK_CH_EN_MASK(led->id),
+ ISINK_CH_EN(led->id));
if (ret < 0)
return ret;
@@ -222,15 +271,15 @@ static int mt6323_led_hw_on(struct led_classdev *cdev,
if (ret < 0)
return ret;
- ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id),
- MT6323_ISINK_DIM_DUTY_MASK,
- MT6323_ISINK_DIM_DUTY(31));
+ ret = regmap_update_bits(regmap, ISINK_CON(regs->isink_con[0], led->id),
+ ISINK_DIM_DUTY_MASK,
+ ISINK_DIM_DUTY(31));
if (ret < 0)
return ret;
- ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id),
- MT6323_ISINK_DIM_FSEL_MASK,
- MT6323_ISINK_DIM_FSEL(1000));
+ ret = regmap_update_bits(regmap, ISINK_CON(regs->isink_con[1], led->id),
+ ISINK_DIM_FSEL_MASK,
+ ISINK_DIM_FSEL(1000));
if (ret < 0)
return ret;
@@ -243,6 +292,8 @@ static int mt6323_led_set_blink(struct led_classdev *cdev,
{
struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
struct mt6323_leds *leds = led->parent;
+ const struct mt6323_regs *regs = leds->pdata->regs;
+ const struct mt6323_hwspec *spec = leds->pdata->spec;
struct regmap *regmap = leds->hw->regmap;
unsigned long period;
u8 duty_hw;
@@ -265,14 +316,14 @@ static int mt6323_led_set_blink(struct led_classdev *cdev,
*/
period = *delay_on + *delay_off;
- if (period > MT6323_MAX_PERIOD)
+ if (period > spec->max_period)
return -EINVAL;
/*
* Calculate duty_hw based on the percentage of period during
* which the led is ON.
*/
- duty_hw = MT6323_CAL_HW_DUTY(*delay_on, period);
+ duty_hw = DIV_ROUND_CLOSEST(*delay_on * 100000ul, period * spec->unit_duty);
/* hardware doesn't support zero duty cycle. */
if (!duty_hw)
@@ -290,15 +341,15 @@ static int mt6323_led_set_blink(struct led_classdev *cdev,
led->current_brightness = cdev->max_brightness;
}
- ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id),
- MT6323_ISINK_DIM_DUTY_MASK,
- MT6323_ISINK_DIM_DUTY(duty_hw - 1));
+ ret = regmap_update_bits(regmap, ISINK_CON(regs->isink_con[0], led->id),
+ ISINK_DIM_DUTY_MASK,
+ ISINK_DIM_DUTY(duty_hw - 1));
if (ret < 0)
goto out;
- ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id),
- MT6323_ISINK_DIM_FSEL_MASK,
- MT6323_ISINK_DIM_FSEL(period - 1));
+ ret = regmap_update_bits(regmap, ISINK_CON(regs->isink_con[1], led->id),
+ ISINK_DIM_FSEL_MASK,
+ ISINK_DIM_FSEL(period - 1));
out:
mutex_unlock(&leds->lock);
@@ -335,6 +386,117 @@ out:
return ret;
}
+static int mtk_wled_hw_on(struct led_classdev *cdev)
+{
+ struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
+ struct mt6323_leds *leds = led->parent;
+ const struct mt6323_regs *regs = leds->pdata->regs;
+ struct regmap *regmap = leds->hw->regmap;
+ int ret;
+
+ ret = regmap_clear_bits(regmap, regs->top_ckpdn[0], RG_VWLED_32K_CK_PDN);
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(regmap, regs->top_ckpdn[0], RG_VWLED_6M_CK_PDN);
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(regmap, regs->top_ckpdn[0], RG_VWLED_1M_CK_PDN);
+ if (ret)
+ return ret;
+
+ usleep_range(5000, 6000);
+
+ /* Enable WLED channel pair */
+ ret = regmap_set_bits(regmap, regs->iwled_en_ctrl, BIT(led->id));
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(regmap, regs->iwled_en_ctrl, BIT(led->id + 1));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mtk_wled_hw_off(struct led_classdev *cdev)
+{
+ struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
+ struct mt6323_leds *leds = led->parent;
+ const struct mt6323_regs *regs = leds->pdata->regs;
+ struct regmap *regmap = leds->hw->regmap;
+ int ret;
+
+ ret = regmap_clear_bits(regmap, regs->iwled_en_ctrl, BIT(led->id + 1));
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(regmap, regs->iwled_en_ctrl, BIT(led->id));
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(regmap, regs->top_ckpdn[0], RG_VWLED_32K_CK_PDN);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(regmap, regs->top_ckpdn[0], RG_VWLED_6M_CK_PDN);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(regmap, regs->top_ckpdn[0], RG_VWLED_1M_CK_PDN);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static enum led_brightness mt6323_get_wled_brightness(struct led_classdev *cdev)
+{
+ struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
+ struct mt6323_leds *leds = led->parent;
+ const struct mt6323_regs *regs = leds->pdata->regs;
+ struct regmap *regmap = leds->hw->regmap;
+ unsigned int status;
+ int ret;
+
+ ret = regmap_read(regmap, regs->iwled_en_ctrl, &status);
+ if (ret)
+ return 0;
+
+ /* Always two channels per WLED */
+ status &= BIT(led->id) | BIT(led->id + 1);
+
+ return status ? led->current_brightness : 0;
+}
+
+static int mt6323_wled_set_brightness(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
+ struct mt6323_leds *leds = led->parent;
+ int ret = 0;
+
+ mutex_lock(&leds->lock);
+
+ if (brightness) {
+ if (!led->current_brightness)
+ ret = mtk_wled_hw_on(cdev);
+ if (ret)
+ goto out;
+ } else {
+ ret = mtk_wled_hw_off(cdev);
+ if (ret)
+ goto out;
+ }
+
+ led->current_brightness = brightness;
+out:
+ mutex_unlock(&leds->lock);
+
+ return ret;
+}
+
static int mt6323_led_set_dt_default(struct led_classdev *cdev,
struct device_node *np)
{
@@ -369,9 +531,12 @@ static int mt6323_led_probe(struct platform_device *pdev)
struct mt6397_chip *hw = dev_get_drvdata(dev->parent);
struct mt6323_leds *leds;
struct mt6323_led *led;
+ const struct mt6323_regs *regs;
+ const struct mt6323_hwspec *spec;
int ret;
unsigned int status;
u32 reg;
+ u8 max_leds;
leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL);
if (!leds)
@@ -379,6 +544,10 @@ static int mt6323_led_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, leds);
leds->dev = dev;
+ leds->pdata = device_get_match_data(dev);
+ regs = leds->pdata->regs;
+ spec = leds->pdata->spec;
+ max_leds = spec->max_leds + spec->max_wleds;
/*
* leds->hw points to the underlying bus for the register
@@ -387,17 +556,18 @@ static int mt6323_led_probe(struct platform_device *pdev)
leds->hw = hw;
mutex_init(&leds->lock);
- status = MT6323_RG_DRV_32K_CK_PDN;
- ret = regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0,
- MT6323_RG_DRV_32K_CK_PDN_MASK, ~status);
+ status = RG_DRV_32K_CK_PDN;
+ ret = regmap_update_bits(leds->hw->regmap, regs->top_ckpdn[0],
+ RG_DRV_32K_CK_PDN_MASK, ~status);
if (ret < 0) {
dev_err(leds->dev,
- "Failed to update MT6323_TOP_CKPDN0 Register\n");
+ "Failed to update TOP_CKPDN0 Register\n");
return ret;
}
for_each_available_child_of_node(np, child) {
struct led_init_data init_data = {};
+ bool is_wled;
ret = of_property_read_u32(child, "reg", &reg);
if (ret) {
@@ -405,7 +575,8 @@ static int mt6323_led_probe(struct platform_device *pdev)
goto put_child_node;
}
- if (reg >= MT6323_MAX_LEDS || leds->led[reg]) {
+ if (reg >= max_leds || reg >= MAX_SUPPORTED_LEDS ||
+ leds->led[reg]) {
dev_err(dev, "Invalid led reg %u\n", reg);
ret = -EINVAL;
goto put_child_node;
@@ -417,14 +588,24 @@ static int mt6323_led_probe(struct platform_device *pdev)
goto put_child_node;
}
+ is_wled = of_property_read_bool(child, "mediatek,is-wled");
+
leds->led[reg] = led;
leds->led[reg]->id = reg;
- leds->led[reg]->cdev.max_brightness = MT6323_MAX_BRIGHTNESS;
- leds->led[reg]->cdev.brightness_set_blocking =
- mt6323_led_set_brightness;
- leds->led[reg]->cdev.blink_set = mt6323_led_set_blink;
- leds->led[reg]->cdev.brightness_get =
- mt6323_get_led_hw_brightness;
+ leds->led[reg]->cdev.max_brightness = spec->max_brightness;
+
+ if (is_wled) {
+ leds->led[reg]->cdev.brightness_set_blocking =
+ mt6323_wled_set_brightness;
+ leds->led[reg]->cdev.brightness_get =
+ mt6323_get_wled_brightness;
+ } else {
+ leds->led[reg]->cdev.brightness_set_blocking =
+ mt6323_led_set_brightness;
+ leds->led[reg]->cdev.blink_set = mt6323_led_set_blink;
+ leds->led[reg]->cdev.brightness_get =
+ mt6323_get_led_hw_brightness;
+ }
leds->led[reg]->parent = leds;
ret = mt6323_led_set_dt_default(&leds->led[reg]->cdev, child);
@@ -454,23 +635,88 @@ put_child_node:
static int mt6323_led_remove(struct platform_device *pdev)
{
struct mt6323_leds *leds = platform_get_drvdata(pdev);
+ const struct mt6323_regs *regs = leds->pdata->regs;
int i;
/* Turn the LEDs off on driver removal. */
for (i = 0 ; leds->led[i] ; i++)
mt6323_led_hw_off(&leds->led[i]->cdev);
- regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0,
- MT6323_RG_DRV_32K_CK_PDN_MASK,
- MT6323_RG_DRV_32K_CK_PDN);
+ regmap_update_bits(leds->hw->regmap, regs->top_ckpdn[0],
+ RG_DRV_32K_CK_PDN_MASK,
+ RG_DRV_32K_CK_PDN);
mutex_destroy(&leds->lock);
return 0;
}
+static const struct mt6323_regs mt6323_registers = {
+ .top_ckpdn = (const u16[]){ 0x102, 0x106, 0x10e },
+ .num_top_ckpdn = 3,
+ .top_ckcon = (const u16[]){ 0x120, 0x126 },
+ .num_top_ckcon = 2,
+ .isink_con = (const u16[]){ 0x330, 0x332, 0x334 },
+ .num_isink_con = 3,
+ .isink_max_regs = 4, /* ISINK[0..3] */
+ .isink_en_ctrl = 0x356,
+};
+
+static const struct mt6323_regs mt6331_registers = {
+ .top_ckpdn = (const u16[]){ 0x138, 0x13e, 0x144 },
+ .num_top_ckpdn = 3,
+ .top_ckcon = (const u16[]){ 0x14c, 0x14a },
+ .num_top_ckcon = 2,
+ .isink_con = (const u16[]){ 0x40c, 0x40e, 0x410, 0x412, 0x414 },
+ .num_isink_con = 5,
+ .isink_max_regs = 4, /* ISINK[0..3] */
+ .isink_en_ctrl = 0x43a,
+};
+
+static const struct mt6323_regs mt6332_registers = {
+ .top_ckpdn = (const u16[]){ 0x8094, 0x809a, 0x80a0 },
+ .num_top_ckpdn = 3,
+ .top_ckcon = (const u16[]){ 0x80a6, 0x80ac },
+ .num_top_ckcon = 2,
+ .isink_con = (const u16[]){ 0x8cd4 },
+ .num_isink_con = 1,
+ .isink_max_regs = 12, /* IWLED[0..2, 3..9] */
+ .iwled_en_ctrl = 0x8cda,
+};
+
+static const struct mt6323_hwspec mt6323_spec = {
+ .max_period = 10000,
+ .max_leds = 4,
+ .max_brightness = 6,
+ .unit_duty = 3125,
+};
+
+static const struct mt6323_hwspec mt6332_spec = {
+ /* There are no LEDs in MT6332. Only WLEDs are present. */
+ .max_leds = 0,
+ .max_wleds = 1,
+ .max_brightness = 1024,
+};
+
+static const struct mt6323_data mt6323_pdata = {
+ .regs = &mt6323_registers,
+ .spec = &mt6323_spec,
+};
+
+static const struct mt6323_data mt6331_pdata = {
+ .regs = &mt6331_registers,
+ .spec = &mt6323_spec,
+};
+
+static const struct mt6323_data mt6332_pdata = {
+ .regs = &mt6332_registers,
+ .spec = &mt6332_spec,
+};
+
static const struct of_device_id mt6323_led_dt_match[] = {
- { .compatible = "mediatek,mt6323-led" },
+ { .compatible = "mediatek,mt6323-led", .data = &mt6323_pdata},
+ { .compatible = "mediatek,mt6331-led", .data = &mt6331_pdata },
+ { .compatible = "mediatek,mt6332-led", .data = &mt6332_pdata },
{},
};
MODULE_DEVICE_TABLE(of, mt6323_led_dt_match);
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index 15b1acfa442e..8b5c62083e50 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -102,7 +102,7 @@ static struct i2c_driver pca9532_driver = {
.name = "leds-pca953x",
.of_match_table = of_match_ptr(of_pca9532_leds_match),
},
- .probe_new = pca9532_probe,
+ .probe = pca9532_probe,
.remove = pca9532_remove,
.id_table = pca9532_id,
};
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index 1edd092e7894..b10e1ef38db0 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -668,7 +668,7 @@ static struct i2c_driver pca955x_driver = {
.name = "leds-pca955x",
.of_match_table = of_pca955x_match,
},
- .probe_new = pca955x_probe,
+ .probe = pca955x_probe,
.id_table = pca955x_id,
};
diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c
index 9cd476db601f..47223c850e4b 100644
--- a/drivers/leds/leds-pca963x.c
+++ b/drivers/leds/leds-pca963x.c
@@ -431,7 +431,7 @@ static struct i2c_driver pca963x_driver = {
.name = "leds-pca963x",
.of_match_table = of_pca963x_match,
},
- .probe_new = pca963x_probe,
+ .probe = pca963x_probe,
.id_table = pca963x_id,
};
diff --git a/drivers/leds/leds-spi-byte.c b/drivers/leds/leds-spi-byte.c
index 2bc5c99daf51..2c7ffc3c78e6 100644
--- a/drivers/leds/leds-spi-byte.c
+++ b/drivers/leds/leds-spi-byte.c
@@ -98,7 +98,7 @@ static int spi_byte_probe(struct spi_device *spi)
return -ENOMEM;
of_property_read_string(child, "label", &name);
- strlcpy(led->name, name, sizeof(led->name));
+ strscpy(led->name, name, sizeof(led->name));
led->spi = spi;
mutex_init(&led->mutex);
led->cdef = device_get_match_data(dev);
diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c
index 634cabd5bb79..aab861771210 100644
--- a/drivers/leds/leds-tca6507.c
+++ b/drivers/leds/leds-tca6507.c
@@ -808,7 +808,7 @@ static struct i2c_driver tca6507_driver = {
.name = "leds-tca6507",
.of_match_table = of_match_ptr(of_tca6507_leds_match),
},
- .probe_new = tca6507_probe,
+ .probe = tca6507_probe,
.remove = tca6507_remove,
.id_table = tca6507_id,
};
diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c
index 7e31db50036f..dfc6fb2b3e52 100644
--- a/drivers/leds/leds-tlc591xx.c
+++ b/drivers/leds/leds-tlc591xx.c
@@ -230,7 +230,7 @@ static struct i2c_driver tlc591xx_driver = {
.name = "tlc591xx",
.of_match_table = of_match_ptr(of_tlc591xx_leds_match),
},
- .probe_new = tlc591xx_probe,
+ .probe = tlc591xx_probe,
.id_table = tlc591xx_id,
};
diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c
index 013f551b32b2..64b2d7b6d3f3 100644
--- a/drivers/leds/leds-turris-omnia.c
+++ b/drivers/leds/leds-turris-omnia.c
@@ -271,7 +271,7 @@ static const struct i2c_device_id omnia_id[] = {
MODULE_DEVICE_TABLE(i2c, omnia_id);
static struct i2c_driver omnia_leds_driver = {
- .probe_new = omnia_leds_probe,
+ .probe = omnia_leds_probe,
.remove = omnia_leds_remove,
.id_table = omnia_id,
.driver = {
diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c
index 1c849814a491..59581b3e25ca 100644
--- a/drivers/leds/rgb/leds-qcom-lpg.c
+++ b/drivers/leds/rgb/leds-qcom-lpg.c
@@ -1173,8 +1173,10 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
i = 0;
for_each_available_child_of_node(np, child) {
ret = lpg_parse_channel(lpg, child, &led->channels[i]);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(child);
return ret;
+ }
info[i].color_index = led->channels[i]->color;
info[i].intensity = 0;
@@ -1352,8 +1354,10 @@ static int lpg_probe(struct platform_device *pdev)
for_each_available_child_of_node(pdev->dev.of_node, np) {
ret = lpg_add_led(lpg, np);
- if (ret)
+ if (ret) {
+ of_node_put(np);
return ret;
+ }
}
for (i = 0; i < lpg->num_channels; i++)
@@ -1414,6 +1418,20 @@ static const struct lpg_data pm8994_lpg_data = {
},
};
+/* PMI632 uses SDAM instead of LUT for pattern */
+static const struct lpg_data pmi632_lpg_data = {
+ .triled_base = 0xd000,
+
+ .num_channels = 5,
+ .channels = (const struct lpg_channel_data[]) {
+ { .base = 0xb300, .triled_mask = BIT(7) },
+ { .base = 0xb400, .triled_mask = BIT(6) },
+ { .base = 0xb500, .triled_mask = BIT(5) },
+ { .base = 0xb600 },
+ { .base = 0xb700 },
+ },
+};
+
static const struct lpg_data pmi8994_lpg_data = {
.lut_base = 0xb000,
.lut_size = 24,
@@ -1505,6 +1523,7 @@ static const struct of_device_id lpg_of_table[] = {
{ .compatible = "qcom,pm8916-pwm", .data = &pm8916_pwm_data },
{ .compatible = "qcom,pm8941-lpg", .data = &pm8941_lpg_data },
{ .compatible = "qcom,pm8994-lpg", .data = &pm8994_lpg_data },
+ { .compatible = "qcom,pmi632-lpg", .data = &pmi632_lpg_data },
{ .compatible = "qcom,pmi8994-lpg", .data = &pmi8994_lpg_data },
{ .compatible = "qcom,pmi8998-lpg", .data = &pmi8998_lpg_data },
{ .compatible = "qcom,pmc8180c-lpg", .data = &pm8150l_lpg_data },
diff --git a/drivers/leds/simple/Kconfig b/drivers/leds/simple/Kconfig
index fd2b8225d926..44fa0f93cb3b 100644
--- a/drivers/leds/simple/Kconfig
+++ b/drivers/leds/simple/Kconfig
@@ -1,11 +1,36 @@
# SPDX-License-Identifier: GPL-2.0-only
config LEDS_SIEMENS_SIMATIC_IPC
tristate "LED driver for Siemens Simatic IPCs"
- depends on LEDS_GPIO
depends on SIEMENS_SIMATIC_IPC
help
This option enables support for the LEDs of several Industrial PCs
from Siemens.
- To compile this driver as a module, choose M here: the modules
- will be called simatic-ipc-leds and simatic-ipc-leds-gpio.
+ To compile this driver as a module, choose M here: the module
+ will be called simatic-ipc-leds.
+
+config LEDS_SIEMENS_SIMATIC_IPC_APOLLOLAKE
+ tristate "LED driver for Siemens Simatic IPCs based on Intel Apollo Lake GPIO"
+ depends on LEDS_GPIO
+ depends on PINCTRL_BROXTON
+ depends on SIEMENS_SIMATIC_IPC
+ default LEDS_SIEMENS_SIMATIC_IPC
+ help
+ This option enables support for the LEDs of several Industrial PCs
+ from Siemens based on Apollo Lake GPIO i.e. IPC127E.
+
+ To compile this driver as a module, choose M here: the module
+ will be called simatic-ipc-leds-gpio-apollolake.
+
+config LEDS_SIEMENS_SIMATIC_IPC_F7188X
+ tristate "LED driver for Siemens Simatic IPCs based on Nuvoton GPIO"
+ depends on LEDS_GPIO
+ depends on GPIO_F7188X
+ depends on SIEMENS_SIMATIC_IPC
+ default LEDS_SIEMENS_SIMATIC_IPC
+ help
+ This option enables support for the LEDs of several Industrial PCs
+ from Siemens based on Nuvoton GPIO i.e. IPC227G.
+
+ To compile this driver as a module, choose M here: the module
+ will be called simatic-ipc-leds-gpio-f7188x.
diff --git a/drivers/leds/simple/Makefile b/drivers/leds/simple/Makefile
index 1c7ef5e1324b..e3e840cea275 100644
--- a/drivers/leds/simple/Makefile
+++ b/drivers/leds/simple/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds.o
-obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds-gpio.o
+obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds.o
+obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC_APOLLOLAKE) += simatic-ipc-leds-gpio-core.o simatic-ipc-leds-gpio-apollolake.o
+obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC_F7188X) += simatic-ipc-leds-gpio-core.o simatic-ipc-leds-gpio-f7188x.o
diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c b/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c
new file mode 100644
index 000000000000..e1c712729dcf
--- /dev/null
+++ b/drivers/leds/simple/simatic-ipc-leds-gpio-apollolake.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Siemens SIMATIC IPC driver for GPIO based LEDs
+ *
+ * Copyright (c) Siemens AG, 2023
+ *
+ * Author:
+ * Henning Schild <henning.schild@siemens.com>
+ */
+
+#include <linux/gpio/machine.h>
+#include <linux/gpio/consumer.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/x86/simatic-ipc-base.h>
+
+#include "simatic-ipc-leds-gpio.h"
+
+static struct gpiod_lookup_table simatic_ipc_led_gpio_table = {
+ .dev_id = "leds-gpio",
+ .table = {
+ GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 52, NULL, 0, GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 53, NULL, 1, GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 57, NULL, 2, GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 58, NULL, 3, GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 60, NULL, 4, GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 51, NULL, 5, GPIO_ACTIVE_LOW),
+ {} /* Terminating entry */
+ },
+};
+
+static struct gpiod_lookup_table simatic_ipc_led_gpio_table_extra = {
+ .dev_id = NULL, /* Filled during initialization */
+ .table = {
+ GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 56, NULL, 6, GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 59, NULL, 7, GPIO_ACTIVE_HIGH),
+ {} /* Terminating entry */
+ },
+};
+
+static int simatic_ipc_leds_gpio_apollolake_probe(struct platform_device *pdev)
+{
+ return simatic_ipc_leds_gpio_probe(pdev, &simatic_ipc_led_gpio_table,
+ &simatic_ipc_led_gpio_table_extra);
+}
+
+static int simatic_ipc_leds_gpio_apollolake_remove(struct platform_device *pdev)
+{
+ return simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table,
+ &simatic_ipc_led_gpio_table_extra);
+}
+
+static struct platform_driver simatic_ipc_led_gpio_apollolake_driver = {
+ .probe = simatic_ipc_leds_gpio_apollolake_probe,
+ .remove = simatic_ipc_leds_gpio_apollolake_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+};
+module_platform_driver(simatic_ipc_led_gpio_apollolake_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_SOFTDEP("pre: simatic-ipc-leds-gpio-core platform:apollolake-pinctrl");
+MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-core.c b/drivers/leds/simple/simatic-ipc-leds-gpio-core.c
new file mode 100644
index 000000000000..2a21b663df87
--- /dev/null
+++ b/drivers/leds/simple/simatic-ipc-leds-gpio-core.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Siemens SIMATIC IPC driver for GPIO based LEDs
+ *
+ * Copyright (c) Siemens AG, 2023
+ *
+ * Author:
+ * Henning Schild <henning.schild@siemens.com>
+ */
+
+#include <linux/gpio/machine.h>
+#include <linux/gpio/consumer.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/x86/simatic-ipc-base.h>
+
+#include "simatic-ipc-leds-gpio.h"
+
+static struct platform_device *simatic_leds_pdev;
+
+static const struct gpio_led simatic_ipc_gpio_leds[] = {
+ { .name = "red:" LED_FUNCTION_STATUS "-1" },
+ { .name = "green:" LED_FUNCTION_STATUS "-1" },
+ { .name = "red:" LED_FUNCTION_STATUS "-2" },
+ { .name = "green:" LED_FUNCTION_STATUS "-2" },
+ { .name = "red:" LED_FUNCTION_STATUS "-3" },
+ { .name = "green:" LED_FUNCTION_STATUS "-3" },
+};
+
+static const struct gpio_led_platform_data simatic_ipc_gpio_leds_pdata = {
+ .num_leds = ARRAY_SIZE(simatic_ipc_gpio_leds),
+ .leds = simatic_ipc_gpio_leds,
+};
+
+int simatic_ipc_leds_gpio_remove(struct platform_device *pdev,
+ struct gpiod_lookup_table *table,
+ struct gpiod_lookup_table *table_extra)
+{
+ gpiod_remove_lookup_table(table);
+ gpiod_remove_lookup_table(table_extra);
+ platform_device_unregister(simatic_leds_pdev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(simatic_ipc_leds_gpio_remove);
+
+int simatic_ipc_leds_gpio_probe(struct platform_device *pdev,
+ struct gpiod_lookup_table *table,
+ struct gpiod_lookup_table *table_extra)
+{
+ const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct gpio_desc *gpiod;
+ int err;
+
+ switch (plat->devmode) {
+ case SIMATIC_IPC_DEVICE_127E:
+ case SIMATIC_IPC_DEVICE_227G:
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ gpiod_add_lookup_table(table);
+ simatic_leds_pdev = platform_device_register_resndata(NULL,
+ "leds-gpio", PLATFORM_DEVID_NONE, NULL, 0,
+ &simatic_ipc_gpio_leds_pdata,
+ sizeof(simatic_ipc_gpio_leds_pdata));
+ if (IS_ERR(simatic_leds_pdev)) {
+ err = PTR_ERR(simatic_leds_pdev);
+ goto out;
+ }
+
+ table_extra->dev_id = dev_name(dev);
+ gpiod_add_lookup_table(table_extra);
+
+ /* PM_BIOS_BOOT_N */
+ gpiod = gpiod_get_index(dev, NULL, 6, GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod)) {
+ err = PTR_ERR(gpiod);
+ goto out;
+ }
+ gpiod_put(gpiod);
+
+ /* PM_WDT_OUT */
+ gpiod = gpiod_get_index(dev, NULL, 7, GPIOD_OUT_LOW);
+ if (IS_ERR(gpiod)) {
+ err = PTR_ERR(gpiod);
+ goto out;
+ }
+ gpiod_put(gpiod);
+
+ return 0;
+out:
+ simatic_ipc_leds_gpio_remove(pdev, table, table_extra);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(simatic_ipc_leds_gpio_probe);
+
+MODULE_LICENSE("GPL v2");
+MODULE_SOFTDEP("pre: platform:leds-gpio");
+MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c b/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c
new file mode 100644
index 000000000000..583a6b6c7c22
--- /dev/null
+++ b/drivers/leds/simple/simatic-ipc-leds-gpio-f7188x.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Siemens SIMATIC IPC driver for GPIO based LEDs
+ *
+ * Copyright (c) Siemens AG, 2023
+ *
+ * Author:
+ * Henning Schild <henning.schild@siemens.com>
+ */
+
+#include <linux/gpio/machine.h>
+#include <linux/gpio/consumer.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/x86/simatic-ipc-base.h>
+
+#include "simatic-ipc-leds-gpio.h"
+
+static struct gpiod_lookup_table simatic_ipc_led_gpio_table = {
+ .dev_id = "leds-gpio",
+ .table = {
+ GPIO_LOOKUP_IDX("gpio-f7188x-2", 0, NULL, 0, GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP_IDX("gpio-f7188x-2", 1, NULL, 1, GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP_IDX("gpio-f7188x-2", 2, NULL, 2, GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP_IDX("gpio-f7188x-2", 3, NULL, 3, GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP_IDX("gpio-f7188x-2", 4, NULL, 4, GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP_IDX("gpio-f7188x-2", 5, NULL, 5, GPIO_ACTIVE_LOW),
+ {} /* Terminating entry */
+ },
+};
+
+static struct gpiod_lookup_table simatic_ipc_led_gpio_table_extra = {
+ .dev_id = NULL, /* Filled during initialization */
+ .table = {
+ GPIO_LOOKUP_IDX("gpio-f7188x-3", 6, NULL, 6, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("gpio-f7188x-3", 7, NULL, 7, GPIO_ACTIVE_HIGH),
+ {} /* Terminating entry */
+ },
+};
+
+static int simatic_ipc_leds_gpio_f7188x_probe(struct platform_device *pdev)
+{
+ return simatic_ipc_leds_gpio_probe(pdev, &simatic_ipc_led_gpio_table,
+ &simatic_ipc_led_gpio_table_extra);
+}
+
+static int simatic_ipc_leds_gpio_f7188x_remove(struct platform_device *pdev)
+{
+ return simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table,
+ &simatic_ipc_led_gpio_table_extra);
+}
+
+static struct platform_driver simatic_ipc_led_gpio_driver = {
+ .probe = simatic_ipc_leds_gpio_f7188x_probe,
+ .remove = simatic_ipc_leds_gpio_f7188x_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+};
+module_platform_driver(simatic_ipc_led_gpio_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" KBUILD_MODNAME);
+MODULE_SOFTDEP("pre: simatic-ipc-leds-gpio-core gpio_f7188x");
+MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio.c b/drivers/leds/simple/simatic-ipc-leds-gpio.c
deleted file mode 100644
index e8d329b5a68c..000000000000
--- a/drivers/leds/simple/simatic-ipc-leds-gpio.c
+++ /dev/null
@@ -1,139 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Siemens SIMATIC IPC driver for GPIO based LEDs
- *
- * Copyright (c) Siemens AG, 2022
- *
- * Authors:
- * Henning Schild <henning.schild@siemens.com>
- */
-
-#include <linux/gpio/machine.h>
-#include <linux/gpio/consumer.h>
-#include <linux/leds.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/platform_data/x86/simatic-ipc-base.h>
-
-static struct gpiod_lookup_table *simatic_ipc_led_gpio_table;
-
-static struct gpiod_lookup_table simatic_ipc_led_gpio_table_127e = {
- .dev_id = "leds-gpio",
- .table = {
- GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 52, NULL, 0, GPIO_ACTIVE_LOW),
- GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 53, NULL, 1, GPIO_ACTIVE_LOW),
- GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 57, NULL, 2, GPIO_ACTIVE_LOW),
- GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 58, NULL, 3, GPIO_ACTIVE_LOW),
- GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 60, NULL, 4, GPIO_ACTIVE_LOW),
- GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 51, NULL, 5, GPIO_ACTIVE_LOW),
- GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 56, NULL, 6, GPIO_ACTIVE_LOW),
- GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 59, NULL, 7, GPIO_ACTIVE_HIGH),
- },
-};
-
-static struct gpiod_lookup_table simatic_ipc_led_gpio_table_227g = {
- .dev_id = "leds-gpio",
- .table = {
- GPIO_LOOKUP_IDX("gpio-f7188x-2", 0, NULL, 0, GPIO_ACTIVE_LOW),
- GPIO_LOOKUP_IDX("gpio-f7188x-2", 1, NULL, 1, GPIO_ACTIVE_LOW),
- GPIO_LOOKUP_IDX("gpio-f7188x-2", 2, NULL, 2, GPIO_ACTIVE_LOW),
- GPIO_LOOKUP_IDX("gpio-f7188x-2", 3, NULL, 3, GPIO_ACTIVE_LOW),
- GPIO_LOOKUP_IDX("gpio-f7188x-2", 4, NULL, 4, GPIO_ACTIVE_LOW),
- GPIO_LOOKUP_IDX("gpio-f7188x-2", 5, NULL, 5, GPIO_ACTIVE_LOW),
- GPIO_LOOKUP_IDX("gpio-f7188x-3", 6, NULL, 6, GPIO_ACTIVE_HIGH),
- GPIO_LOOKUP_IDX("gpio-f7188x-3", 7, NULL, 7, GPIO_ACTIVE_HIGH),
- }
-};
-
-static const struct gpio_led simatic_ipc_gpio_leds[] = {
- { .name = "red:" LED_FUNCTION_STATUS "-1" },
- { .name = "green:" LED_FUNCTION_STATUS "-1" },
- { .name = "red:" LED_FUNCTION_STATUS "-2" },
- { .name = "green:" LED_FUNCTION_STATUS "-2" },
- { .name = "red:" LED_FUNCTION_STATUS "-3" },
- { .name = "green:" LED_FUNCTION_STATUS "-3" },
-};
-
-static const struct gpio_led_platform_data simatic_ipc_gpio_leds_pdata = {
- .num_leds = ARRAY_SIZE(simatic_ipc_gpio_leds),
- .leds = simatic_ipc_gpio_leds,
-};
-
-static struct platform_device *simatic_leds_pdev;
-
-static int simatic_ipc_leds_gpio_remove(struct platform_device *pdev)
-{
- gpiod_remove_lookup_table(simatic_ipc_led_gpio_table);
- platform_device_unregister(simatic_leds_pdev);
-
- return 0;
-}
-
-static int simatic_ipc_leds_gpio_probe(struct platform_device *pdev)
-{
- const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
- struct gpio_desc *gpiod;
- int err;
-
- switch (plat->devmode) {
- case SIMATIC_IPC_DEVICE_127E:
- if (!IS_ENABLED(CONFIG_PINCTRL_BROXTON))
- return -ENODEV;
- simatic_ipc_led_gpio_table = &simatic_ipc_led_gpio_table_127e;
- break;
- case SIMATIC_IPC_DEVICE_227G:
- if (!IS_ENABLED(CONFIG_GPIO_F7188X))
- return -ENODEV;
- request_module("gpio-f7188x");
- simatic_ipc_led_gpio_table = &simatic_ipc_led_gpio_table_227g;
- break;
- default:
- return -ENODEV;
- }
-
- gpiod_add_lookup_table(simatic_ipc_led_gpio_table);
- simatic_leds_pdev = platform_device_register_resndata(NULL,
- "leds-gpio", PLATFORM_DEVID_NONE, NULL, 0,
- &simatic_ipc_gpio_leds_pdata,
- sizeof(simatic_ipc_gpio_leds_pdata));
- if (IS_ERR(simatic_leds_pdev)) {
- err = PTR_ERR(simatic_leds_pdev);
- goto out;
- }
-
- /* PM_BIOS_BOOT_N */
- gpiod = gpiod_get_index(&simatic_leds_pdev->dev, NULL, 6, GPIOD_OUT_LOW);
- if (IS_ERR(gpiod)) {
- err = PTR_ERR(gpiod);
- goto out;
- }
- gpiod_put(gpiod);
-
- /* PM_WDT_OUT */
- gpiod = gpiod_get_index(&simatic_leds_pdev->dev, NULL, 7, GPIOD_OUT_LOW);
- if (IS_ERR(gpiod)) {
- err = PTR_ERR(gpiod);
- goto out;
- }
- gpiod_put(gpiod);
-
- return 0;
-out:
- simatic_ipc_leds_gpio_remove(pdev);
-
- return err;
-}
-
-static struct platform_driver simatic_ipc_led_gpio_driver = {
- .probe = simatic_ipc_leds_gpio_probe,
- .remove = simatic_ipc_leds_gpio_remove,
- .driver = {
- .name = KBUILD_MODNAME,
- }
-};
-module_platform_driver(simatic_ipc_led_gpio_driver);
-
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" KBUILD_MODNAME);
-MODULE_SOFTDEP("pre: platform:leds-gpio");
-MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio.h b/drivers/leds/simple/simatic-ipc-leds-gpio.h
new file mode 100644
index 000000000000..bf258c32f83d
--- /dev/null
+++ b/drivers/leds/simple/simatic-ipc-leds-gpio.h
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Siemens SIMATIC IPC driver for GPIO based LEDs
+ *
+ * Copyright (c) Siemens AG, 2023
+ *
+ * Author:
+ * Henning Schild <henning.schild@siemens.com>
+ */
+
+#ifndef _SIMATIC_IPC_LEDS_GPIO_H
+#define _SIMATIC_IPC_LEDS_GPIO_H
+
+int simatic_ipc_leds_gpio_probe(struct platform_device *pdev,
+ struct gpiod_lookup_table *table,
+ struct gpiod_lookup_table *table_extra);
+
+int simatic_ipc_leds_gpio_remove(struct platform_device *pdev,
+ struct gpiod_lookup_table *table,
+ struct gpiod_lookup_table *table_extra);
+
+#endif /* _SIMATIC_IPC_LEDS_GPIO_H */
diff --git a/drivers/leds/simple/simatic-ipc-leds.c b/drivers/leds/simple/simatic-ipc-leds.c
index 4894c228c165..2124f6d09930 100644
--- a/drivers/leds/simple/simatic-ipc-leds.c
+++ b/drivers/leds/simple/simatic-ipc-leds.c
@@ -126,7 +126,6 @@ static struct platform_driver simatic_ipc_led_driver = {
.name = KBUILD_MODNAME,
}
};
-
module_platform_driver(simatic_ipc_led_driver);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/trigger/ledtrig-disk.c b/drivers/leds/trigger/ledtrig-disk.c
index 0b7dfbd04273..e9b87ee944f2 100644
--- a/drivers/leds/trigger/ledtrig-disk.c
+++ b/drivers/leds/trigger/ledtrig-disk.c
@@ -19,16 +19,13 @@ DEFINE_LED_TRIGGER(ledtrig_disk_write);
void ledtrig_disk_activity(bool write)
{
- unsigned long blink_delay = BLINK_DELAY;
-
- led_trigger_blink_oneshot(ledtrig_disk,
- &blink_delay, &blink_delay, 0);
+ led_trigger_blink_oneshot(ledtrig_disk, BLINK_DELAY, BLINK_DELAY, 0);
if (write)
led_trigger_blink_oneshot(ledtrig_disk_write,
- &blink_delay, &blink_delay, 0);
+ BLINK_DELAY, BLINK_DELAY, 0);
else
led_trigger_blink_oneshot(ledtrig_disk_read,
- &blink_delay, &blink_delay, 0);
+ BLINK_DELAY, BLINK_DELAY, 0);
}
EXPORT_SYMBOL(ledtrig_disk_activity);
diff --git a/drivers/leds/trigger/ledtrig-mtd.c b/drivers/leds/trigger/ledtrig-mtd.c
index 8fa763c2269b..bbe6876a249d 100644
--- a/drivers/leds/trigger/ledtrig-mtd.c
+++ b/drivers/leds/trigger/ledtrig-mtd.c
@@ -22,12 +22,8 @@ DEFINE_LED_TRIGGER(ledtrig_nand);
void ledtrig_mtd_activity(void)
{
- unsigned long blink_delay = BLINK_DELAY;
-
- led_trigger_blink_oneshot(ledtrig_mtd,
- &blink_delay, &blink_delay, 0);
- led_trigger_blink_oneshot(ledtrig_nand,
- &blink_delay, &blink_delay, 0);
+ led_trigger_blink_oneshot(ledtrig_mtd, BLINK_DELAY, BLINK_DELAY, 0);
+ led_trigger_blink_oneshot(ledtrig_nand, BLINK_DELAY, BLINK_DELAY, 0);
}
EXPORT_SYMBOL(ledtrig_mtd_activity);
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index 32b66703068a..c9bc5a91ec83 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -462,8 +462,7 @@ static int netdev_trig_notify(struct notifier_block *nb,
get_device_state(trigger_data);
fallthrough;
case NETDEV_REGISTER:
- if (trigger_data->net_dev)
- dev_put(trigger_data->net_dev);
+ dev_put(trigger_data->net_dev);
dev_hold(dev);
trigger_data->net_dev = dev;
break;
@@ -594,8 +593,7 @@ static void netdev_trig_deactivate(struct led_classdev *led_cdev)
cancel_delayed_work_sync(&trigger_data->work);
- if (trigger_data->net_dev)
- dev_put(trigger_data->net_dev);
+ dev_put(trigger_data->net_dev);
kfree(trigger_data);
}
diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c
index 1bad1866ae46..99265667538c 100644
--- a/drivers/net/arcnet/arcnet.c
+++ b/drivers/net/arcnet/arcnet.c
@@ -196,13 +196,10 @@ static void arcnet_dump_packet(struct net_device *dev, int bufnum,
void arcnet_led_event(struct net_device *dev, enum arcnet_led_event event)
{
struct arcnet_local *lp = netdev_priv(dev);
- unsigned long led_delay = 350;
- unsigned long tx_delay = 50;
switch (event) {
case ARCNET_LED_EVENT_RECON:
- led_trigger_blink_oneshot(lp->recon_led_trig,
- &led_delay, &led_delay, 0);
+ led_trigger_blink_oneshot(lp->recon_led_trig, 350, 350, 0);
break;
case ARCNET_LED_EVENT_OPEN:
led_trigger_event(lp->tx_led_trig, LED_OFF);
@@ -213,8 +210,7 @@ void arcnet_led_event(struct net_device *dev, enum arcnet_led_event event)
led_trigger_event(lp->recon_led_trig, LED_OFF);
break;
case ARCNET_LED_EVENT_TX:
- led_trigger_blink_oneshot(lp->tx_led_trig,
- &tx_delay, &tx_delay, 0);
+ led_trigger_blink_oneshot(lp->tx_led_trig, 50, 50, 0);
break;
}
}
diff --git a/drivers/platform/x86/simatic-ipc.c b/drivers/platform/x86/simatic-ipc.c
index b3622419cd1a..c773995b230d 100644
--- a/drivers/platform/x86/simatic-ipc.c
+++ b/drivers/platform/x86/simatic-ipc.c
@@ -68,9 +68,10 @@ static int register_platform_devices(u32 station_id)
}
if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
- if (ledmode == SIMATIC_IPC_DEVICE_127E ||
- ledmode == SIMATIC_IPC_DEVICE_227G)
- pdevname = KBUILD_MODNAME "_leds_gpio";
+ if (ledmode == SIMATIC_IPC_DEVICE_127E)
+ pdevname = KBUILD_MODNAME "_leds_gpio_apollolake";
+ if (ledmode == SIMATIC_IPC_DEVICE_227G)
+ pdevname = KBUILD_MODNAME "_leds_gpio_f7188x";
platform_data.devmode = ledmode;
ipc_led_platform_device =
platform_device_register_data(NULL,
diff --git a/drivers/power/supply/power_supply_leds.c b/drivers/power/supply/power_supply_leds.c
index 0674483279d7..c7db29d5fcb8 100644
--- a/drivers/power/supply/power_supply_leds.c
+++ b/drivers/power/supply/power_supply_leds.c
@@ -22,8 +22,6 @@
static void power_supply_update_bat_leds(struct power_supply *psy)
{
union power_supply_propval status;
- unsigned long delay_on = 0;
- unsigned long delay_off = 0;
if (power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, &status))
return;
@@ -43,8 +41,7 @@ static void power_supply_update_bat_leds(struct power_supply *psy)
led_trigger_event(psy->charging_full_trig, LED_FULL);
led_trigger_event(psy->charging_trig, LED_FULL);
led_trigger_event(psy->full_trig, LED_OFF);
- led_trigger_blink(psy->charging_blink_full_solid_trig,
- &delay_on, &delay_off);
+ led_trigger_blink(psy->charging_blink_full_solid_trig, 0, 0);
break;
default:
led_trigger_event(psy->charging_full_trig, LED_OFF);
diff --git a/drivers/usb/common/led.c b/drivers/usb/common/led.c
index 0865dd44a80a..1de18d90b134 100644
--- a/drivers/usb/common/led.c
+++ b/drivers/usb/common/led.c
@@ -14,8 +14,6 @@
#define BLINK_DELAY 30
-static unsigned long usb_blink_delay = BLINK_DELAY;
-
DEFINE_LED_TRIGGER(ledtrig_usb_gadget);
DEFINE_LED_TRIGGER(ledtrig_usb_host);
@@ -32,7 +30,7 @@ void usb_led_activity(enum usb_led_event ev)
break;
}
/* led_trigger_blink_oneshot() handles trig == NULL gracefully */
- led_trigger_blink_oneshot(trig, &usb_blink_delay, &usb_blink_delay, 0);
+ led_trigger_blink_oneshot(trig, BLINK_DELAY, BLINK_DELAY, 0);
}
EXPORT_SYMBOL_GPL(usb_led_activity);
diff --git a/include/dt-bindings/leds/leds-lp55xx.h b/include/dt-bindings/leds/leds-lp55xx.h
new file mode 100644
index 000000000000..a4fb4567715d
--- /dev/null
+++ b/include/dt-bindings/leds/leds-lp55xx.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+#ifndef _DT_BINDINGS_LEDS_LP55XX_H
+#define _DT_BINDINGS_LEDS_LP55XX_H
+
+#define LP55XX_CP_OFF 0
+#define LP55XX_CP_BYPASS 1
+#define LP55XX_CP_BOOST 2
+#define LP55XX_CP_AUTO 3
+
+#endif /* _DT_BINDINGS_LEDS_LP55XX_H */
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 3a65ff72bb04..7d428100b42b 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -124,6 +124,10 @@ struct led_classdev {
#define LED_BLINK_INVERT 3
#define LED_BLINK_BRIGHTNESS_CHANGE 4
#define LED_BLINK_DISABLE 5
+ /* Brightness off also disables hw-blinking so it is a separate action */
+#define LED_SET_BRIGHTNESS_OFF 6
+#define LED_SET_BRIGHTNESS 7
+#define LED_SET_BLINK 8
/* Set LED brightness level
* Must not sleep. Use brightness_set_blocking for drivers
@@ -147,6 +151,10 @@ struct led_classdev {
* match the values specified exactly.
* Deactivate blinking again when the brightness is set to LED_OFF
* via the brightness_set() callback.
+ * For led_blink_set_nosleep() the LED core assumes that blink_set
+ * implementations, of drivers which do not use brightness_set_blocking,
+ * will not sleep. Therefor if brightness_set_blocking is not set
+ * this function must not sleep!
*/
int (*blink_set)(struct led_classdev *led_cdev,
unsigned long *delay_on,
@@ -170,6 +178,8 @@ struct led_classdev {
struct work_struct set_brightness_work;
int delayed_set_value;
+ unsigned long delayed_delay_on;
+ unsigned long delayed_delay_off;
#ifdef CONFIG_LEDS_TRIGGERS
/* Protects the trigger data below */
@@ -315,12 +325,27 @@ struct led_classdev *__must_check devm_of_led_get(struct device *dev,
* software blinking if there is no hardware blinking or if
* the LED refuses the passed values.
*
+ * This function may sleep!
+ *
* Note that if software blinking is active, simply calling
* led_cdev->brightness_set() will not stop the blinking,
* use led_set_brightness() instead.
*/
void led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
unsigned long *delay_off);
+
+/**
+ * led_blink_set_nosleep - set blinking, guaranteed to not sleep
+ * @led_cdev: the LED to start blinking
+ * @delay_on: the time it should be on (in ms)
+ * @delay_off: the time it should ble off (in ms)
+ *
+ * This function makes the LED blink and is guaranteed to not sleep. Otherwise
+ * this is the same as led_blink_set(), see led_blink_set() for details.
+ */
+void led_blink_set_nosleep(struct led_classdev *led_cdev, unsigned long delay_on,
+ unsigned long delay_off);
+
/**
* led_blink_set_oneshot - do a oneshot software blink
* @led_cdev: the LED to start blinking
@@ -334,6 +359,8 @@ void led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
*
* If invert is set, led blinks for delay_off first, then for
* delay_on and leave the led on after the on-off cycle.
+ *
+ * This function is guaranteed not to sleep.
*/
void led_blink_set_oneshot(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off,
@@ -476,11 +503,11 @@ void led_trigger_register_simple(const char *name,
struct led_trigger **trigger);
void led_trigger_unregister_simple(struct led_trigger *trigger);
void led_trigger_event(struct led_trigger *trigger, enum led_brightness event);
-void led_trigger_blink(struct led_trigger *trigger, unsigned long *delay_on,
- unsigned long *delay_off);
+void led_trigger_blink(struct led_trigger *trigger, unsigned long delay_on,
+ unsigned long delay_off);
void led_trigger_blink_oneshot(struct led_trigger *trigger,
- unsigned long *delay_on,
- unsigned long *delay_off,
+ unsigned long delay_on,
+ unsigned long delay_off,
int invert);
void led_trigger_set_default(struct led_classdev *led_cdev);
int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger);
@@ -530,11 +557,11 @@ static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {}
static inline void led_trigger_event(struct led_trigger *trigger,
enum led_brightness event) {}
static inline void led_trigger_blink(struct led_trigger *trigger,
- unsigned long *delay_on,
- unsigned long *delay_off) {}
+ unsigned long delay_on,
+ unsigned long delay_off) {}
static inline void led_trigger_blink_oneshot(struct led_trigger *trigger,
- unsigned long *delay_on,
- unsigned long *delay_off,
+ unsigned long delay_on,
+ unsigned long delay_off,
int invert) {}
static inline void led_trigger_set_default(struct led_classdev *led_cdev) {}
static inline int led_trigger_set(struct led_classdev *led_cdev,
diff --git a/include/linux/platform_data/leds-lp55xx.h b/include/linux/platform_data/leds-lp55xx.h
index 3441064713a3..3cc8db0b12b5 100644
--- a/include/linux/platform_data/leds-lp55xx.h
+++ b/include/linux/platform_data/leds-lp55xx.h
@@ -73,6 +73,9 @@ struct lp55xx_platform_data {
/* Clock configuration */
u8 clock_mode;
+ /* Charge pump mode */
+ u32 charge_pump_mode;
+
/* optional enable GPIO */
struct gpio_desc *enable_gpiod;
diff --git a/net/mac80211/led.c b/net/mac80211/led.c
index 6de8d0ad5497..2dc732147e85 100644
--- a/net/mac80211/led.c
+++ b/net/mac80211/led.c
@@ -282,7 +282,7 @@ static void tpt_trig_timer(struct timer_list *t)
}
}
- led_trigger_blink(&local->tpt_led, &on, &off);
+ led_trigger_blink(&local->tpt_led, on, off);
}
const char *
diff --git a/net/mac80211/led.h b/net/mac80211/led.h
index b71a1428d883..d25f13346b82 100644
--- a/net/mac80211/led.h
+++ b/net/mac80211/led.h
@@ -13,22 +13,18 @@
static inline void ieee80211_led_rx(struct ieee80211_local *local)
{
#ifdef CONFIG_MAC80211_LEDS
- unsigned long led_delay = MAC80211_BLINK_DELAY;
-
if (!atomic_read(&local->rx_led_active))
return;
- led_trigger_blink_oneshot(&local->rx_led, &led_delay, &led_delay, 0);
+ led_trigger_blink_oneshot(&local->rx_led, MAC80211_BLINK_DELAY, MAC80211_BLINK_DELAY, 0);
#endif
}
static inline void ieee80211_led_tx(struct ieee80211_local *local)
{
#ifdef CONFIG_MAC80211_LEDS
- unsigned long led_delay = MAC80211_BLINK_DELAY;
-
if (!atomic_read(&local->tx_led_active))
return;
- led_trigger_blink_oneshot(&local->tx_led, &led_delay, &led_delay, 0);
+ led_trigger_blink_oneshot(&local->tx_led, MAC80211_BLINK_DELAY, MAC80211_BLINK_DELAY, 0);
#endif
}
diff --git a/net/netfilter/xt_LED.c b/net/netfilter/xt_LED.c
index 66b0f941d8fb..36c9720ad8d6 100644
--- a/net/netfilter/xt_LED.c
+++ b/net/netfilter/xt_LED.c
@@ -43,7 +43,6 @@ led_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct xt_led_info *ledinfo = par->targinfo;
struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
- unsigned long led_delay = XT_LED_BLINK_DELAY;
/*
* If "always blink" is enabled, and there's still some time until the
@@ -52,7 +51,7 @@ led_tg(struct sk_buff *skb, const struct xt_action_param *par)
if ((ledinfo->delay > 0) && ledinfo->always_blink &&
timer_pending(&ledinternal->timer))
led_trigger_blink_oneshot(&ledinternal->netfilter_led_trigger,
- &led_delay, &led_delay, 1);
+ XT_LED_BLINK_DELAY, XT_LED_BLINK_DELAY, 1);
else
led_trigger_event(&ledinternal->netfilter_led_trigger, LED_FULL);