- 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 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmSinb0ACgkQUa+KL4f8 d2FYfg//WWLVfXRuRpY9ueOxvWj65WVPQSQ+wzF/vRTweogR+lN0qxNPH6yT943z ap2EBxpWf84zCifYG4yhTEYDHQT+nH1fIz6xaK29DK8sCQi4WdRpHuE2pE30R/tf Q7SyZi9DlWyoqNiqgNNCl7vkTaHpO3trxoxfEfN2YIB0npLf8yyWRz4feVXXsYtg 41S4Mo7oTxphd7OLvw9PKogdTbT29vBMXen8jzv5g8FObj1Gheg0frq2t2W+bfAl 27cJJJS7he4/WLCDzXVQfB46Nva5NpqHiANbgOAApDGx3hFCzZFTCg6K7+VucpjY bNz3pqmslT5uJxMjqNz8fCSzwWTjyKLHBeGsIT/4HBXD4DnfFbWz9HYkorfNgsu2 lKEp0SYhSmmuS8IVzJvqDqXg6k21hGpuR9P+dI7teoClh0qLTMCz2L2c9p2zNfth 0W+WeLYQ67QTRH9EcHo3dlZH/mP/J1jGmUDbF+DFI6bHsg2iahZUA6ixD18E7sWE RwtCnb3xyn7eoDe3LwJdKtJMyrX59MbFWqozij2NNhvduXc+m1kH/DX5CSaBUVwf RtfDZwWHf4qK4CipuuqOLd5fiUArJ3TSHBxXkoo0Wz7NYXK9k86eIZgWrgdEbvuA oHmSousS19Eiscjtzxl7OjvDJMRc0rTJfD7LzYoHQBL4Vpnd8VI= =9kd5 -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
c156d4af43
|
@ -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.
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -34,7 +34,7 @@ required:
|
|||
- color
|
||||
|
||||
allOf:
|
||||
- $ref: "common.yaml#"
|
||||
- $ref: common.yaml#
|
||||
|
||||
additionalProperties: true
|
||||
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -20,6 +20,7 @@ properties:
|
|||
compatible:
|
||||
enum:
|
||||
- ocs,ocp8110
|
||||
- richtek,rt5033-led
|
||||
- sgmicro,sgm3140
|
||||
|
||||
enable-gpios:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -71,6 +71,7 @@ properties:
|
|||
- qcom,pm8998
|
||||
- qcom,pma8084
|
||||
- qcom,pmd9635
|
||||
- qcom,pmi632
|
||||
- qcom,pmi8950
|
||||
- qcom,pmi8962
|
||||
- qcom,pmi8994
|
||||
|
|
|
@ -17,6 +17,7 @@ LEDs
|
|||
uleds
|
||||
|
||||
leds-blinkm
|
||||
leds-cht-wcove
|
||||
leds-el15203000
|
||||
leds-lm3556
|
||||
leds-lp3944
|
||||
|
|
|
@ -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.
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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 */ }
|
||||
};
|
||||
|
|
|
@ -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))) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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");
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
#define ISINK_CON(r, i) (r + 0x8 * (i))
|
||||
|
||||
/* 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)
|
||||
/* 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)
|
||||
|
||||
/* 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)
|
||||
/* 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;
|
||||
|
||||
|
@ -86,12 +79,63 @@ struct mt6323_led {
|
|||
enum led_brightness current_brightness;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
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, MT6323_ISINK_CON2(led->id),
|
||||
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", ®);
|
||||
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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>");
|
|
@ -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>");
|
|
@ -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>");
|
|
@ -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>");
|
|
@ -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 */
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue