Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds
Pull LED driver updates from Jacek Anaszewski: "Three new LED class drivers and some minor fixes and improvementes to the leds-gpio driver, LED Trigger core and documentation" * 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds: leds: triggers: Check return value of kobject_uevent_env() leds: triggers: Return from led_trigger_set() if there is nothing to do leds: gpio: fix and simplify error handling in gpio_leds_create leds: gpio: switch to managed version of led_classdev_register leds: gpio: fix and simplify reading property "label" leds: gpio: simplify gpio_leds_create leds: gpio: add helper cdev_to_gpio_led_data leds: gpio: fix an unhandled error case in create_gpio_led leds: gpio: introduce gpio_blink_set_t leds: add driver for Mellanox systems LEDs Documentation: move oneshot trigger attributes documentation to ABI leds: centralize definition of "default-state" property leds: add PM8058 LEDs driver leds: pm8058: add device tree bindings leds: do not overflow sysfs buffer in led_trigger_show leds: make triggers explicitly non-modular DT: leds: Add bindings for ISSI is31fl319x leds: is31fl319x: 1/3/6/9-channel light effect led driver
This commit is contained in:
@@ -24,7 +24,8 @@ Description:
|
|||||||
of led events.
|
of led events.
|
||||||
You can change triggers in a similar manner to the way an IO
|
You can change triggers in a similar manner to the way an IO
|
||||||
scheduler is chosen. Trigger specific parameters can appear in
|
scheduler is chosen. Trigger specific parameters can appear in
|
||||||
/sys/class/leds/<led> once a given trigger is selected.
|
/sys/class/leds/<led> once a given trigger is selected. For
|
||||||
|
their documentation see sysfs-class-led-trigger-*.
|
||||||
|
|
||||||
What: /sys/class/leds/<led>/inverted
|
What: /sys/class/leds/<led>/inverted
|
||||||
Date: January 2011
|
Date: January 2011
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
What: /sys/class/leds/<led>/delay_on
|
||||||
|
Date: Jun 2012
|
||||||
|
KernelVersion: 3.6
|
||||||
|
Contact: linux-leds@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Specifies for how many milliseconds the LED has to stay at
|
||||||
|
LED_FULL brightness after it has been armed.
|
||||||
|
Defaults to 100 ms.
|
||||||
|
|
||||||
|
What: /sys/class/leds/<led>/delay_off
|
||||||
|
Date: Jun 2012
|
||||||
|
KernelVersion: 3.6
|
||||||
|
Contact: linux-leds@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Specifies for how many milliseconds the LED has to stay at
|
||||||
|
LED_OFF brightness after it has been armed.
|
||||||
|
Defaults to 100 ms.
|
||||||
|
|
||||||
|
What: /sys/class/leds/<led>/invert
|
||||||
|
Date: Jun 2012
|
||||||
|
KernelVersion: 3.6
|
||||||
|
Contact: linux-leds@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Reverse the blink logic. If set to 0 (default) blink on for
|
||||||
|
delay_on ms, then blink off for delay_off ms, leaving the LED
|
||||||
|
normally off. If set to 1, blink off for delay_off ms, then
|
||||||
|
blink on for delay_on ms, leaving the LED normally on.
|
||||||
|
Setting this value also immediately changes the LED state.
|
||||||
|
|
||||||
|
What: /sys/class/leds/<led>/shot
|
||||||
|
Date: Jun 2012
|
||||||
|
KernelVersion: 3.6
|
||||||
|
Contact: linux-leds@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
Write any non-empty string to signal an events, this starts a
|
||||||
|
blink sequence if not already running.
|
||||||
@@ -19,6 +19,13 @@ Optional properties for child nodes:
|
|||||||
a device, i.e. no other LED class device can be assigned the same
|
a device, i.e. no other LED class device can be assigned the same
|
||||||
label.
|
label.
|
||||||
|
|
||||||
|
- default-state : The initial state of the LED. Valid values are "on", "off",
|
||||||
|
and "keep". If the LED is already on or off and the default-state property is
|
||||||
|
set the to same value, then no glitch should be produced where the LED
|
||||||
|
momentarily turns off (or on). The "keep" setting will keep the LED at
|
||||||
|
whatever its current state is, without producing a glitch. The default is
|
||||||
|
off if this property is not present.
|
||||||
|
|
||||||
- linux,default-trigger : This parameter, if present, is a
|
- linux,default-trigger : This parameter, if present, is a
|
||||||
string defining the trigger assigned to the LED. Current triggers are:
|
string defining the trigger assigned to the LED. Current triggers are:
|
||||||
"backlight" - LED will act as a back-light, controlled by the framebuffer
|
"backlight" - LED will act as a back-light, controlled by the framebuffer
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ LED sub-node optional properties:
|
|||||||
- active-low : Boolean, makes LED active low.
|
- active-low : Boolean, makes LED active low.
|
||||||
Default : false
|
Default : false
|
||||||
- default-state : see
|
- default-state : see
|
||||||
Documentation/devicetree/bindings/leds/leds-gpio.txt
|
Documentation/devicetree/bindings/leds/common.txt
|
||||||
- linux,default-trigger : see
|
- linux,default-trigger : see
|
||||||
Documentation/devicetree/bindings/leds/common.txt
|
Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ LED sub-node optional properties:
|
|||||||
- active-low : Boolean, makes LED active low.
|
- active-low : Boolean, makes LED active low.
|
||||||
Default : false
|
Default : false
|
||||||
- default-state : see
|
- default-state : see
|
||||||
Documentation/devicetree/bindings/leds/leds-gpio.txt
|
Documentation/devicetree/bindings/leds/common.txt
|
||||||
- linux,default-trigger : see
|
- linux,default-trigger : see
|
||||||
Documentation/devicetree/bindings/leds/common.txt
|
Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
|
||||||
|
|||||||
@@ -14,13 +14,8 @@ LED sub-node properties:
|
|||||||
see Documentation/devicetree/bindings/leds/common.txt
|
see Documentation/devicetree/bindings/leds/common.txt
|
||||||
- linux,default-trigger : (optional)
|
- linux,default-trigger : (optional)
|
||||||
see Documentation/devicetree/bindings/leds/common.txt
|
see Documentation/devicetree/bindings/leds/common.txt
|
||||||
- default-state: (optional) The initial state of the LED. Valid
|
- default-state: (optional) The initial state of the LED.
|
||||||
values are "on", "off", and "keep". If the LED is already on or off
|
see Documentation/devicetree/bindings/leds/common.txt
|
||||||
and the default-state property is set the to same value, then no
|
|
||||||
glitch should be produced where the LED momentarily turns off (or
|
|
||||||
on). The "keep" setting will keep the LED at whatever its current
|
|
||||||
state is, without producing a glitch. The default is off if this
|
|
||||||
property is not present.
|
|
||||||
- retain-state-suspended: (optional) The suspend state can be retained.Such
|
- retain-state-suspended: (optional) The suspend state can be retained.Such
|
||||||
as charge-led gpio.
|
as charge-led gpio.
|
||||||
- panic-indicator : (optional)
|
- panic-indicator : (optional)
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
LEDs connected to is31fl319x LED controller chip
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : Should be any of
|
||||||
|
"issi,is31fl3190"
|
||||||
|
"issi,is31fl3191"
|
||||||
|
"issi,is31fl3193"
|
||||||
|
"issi,is31fl3196"
|
||||||
|
"issi,is31fl3199"
|
||||||
|
"si-en,sn3199".
|
||||||
|
- #address-cells: Must be 1.
|
||||||
|
- #size-cells: Must be 0.
|
||||||
|
- reg: 0x64, 0x65, 0x66, or 0x67.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- audio-gain-db : audio gain selection for external analog modulation input.
|
||||||
|
Valid values: 0 - 21, step by 3 (rounded down)
|
||||||
|
Default: 0
|
||||||
|
|
||||||
|
Each led is represented as a sub-node of the issi,is31fl319x device.
|
||||||
|
There can be less leds subnodes than the chip can support but not more.
|
||||||
|
|
||||||
|
Required led sub-node properties:
|
||||||
|
- reg : number of LED line
|
||||||
|
Valid values: 1 - number of leds supported by the chip variant.
|
||||||
|
|
||||||
|
Optional led sub-node properties:
|
||||||
|
- label : see Documentation/devicetree/bindings/leds/common.txt.
|
||||||
|
- linux,default-trigger :
|
||||||
|
see Documentation/devicetree/bindings/leds/common.txt.
|
||||||
|
- led-max-microamp : (optional)
|
||||||
|
Valid values: 5000 - 40000, step by 5000 (rounded down)
|
||||||
|
Default: 20000 (20 mA)
|
||||||
|
Note: a driver will take the lowest of all led limits since the
|
||||||
|
chip has a single global setting. The lowest value will be chosen
|
||||||
|
due to the PWM specificity, where lower brightness is achieved
|
||||||
|
by reducing the dury-cycle of pulses and not the current, which
|
||||||
|
will always have its peak value equal to led-max-microamp.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
fancy_leds: leds@65 {
|
||||||
|
compatible = "issi,is31fl3196";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
reg = <0x65>;
|
||||||
|
|
||||||
|
red_aux: led@1 {
|
||||||
|
label = "red:aux";
|
||||||
|
reg = <1>;
|
||||||
|
led-max-microamp = <10000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
green_power: led@5 {
|
||||||
|
label = "green:power";
|
||||||
|
reg = <5>;
|
||||||
|
linux,default-trigger = "default-on";
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
Qualcomm PM8058 LED driver
|
||||||
|
|
||||||
|
The Qualcomm PM8058 is a multi-functional device which contains
|
||||||
|
an LED driver block for up to six LEDs: three normal LEDs, two
|
||||||
|
"flash" LEDs and one "keypad backlight" LED. The names are
|
||||||
|
quoted because sometimes these LED drivers are used for wildly
|
||||||
|
different things than flash or keypad backlight: their names
|
||||||
|
are more of a suggestion than a hard-wired usecase.
|
||||||
|
|
||||||
|
Hardware-wise the different LEDs support slightly different
|
||||||
|
output currents. The "flash" LEDs do not need to charge nor
|
||||||
|
do they support external triggers. They are just powerful LED
|
||||||
|
drivers.
|
||||||
|
|
||||||
|
The LEDs appear as children to the PM8058 device, with the
|
||||||
|
proper compatible string. For the PM8058 bindings see:
|
||||||
|
mfd/qcom-pm8xxx.txt.
|
||||||
|
|
||||||
|
Each LED is represented as a sub-node of the syscon device. Each
|
||||||
|
node's name represents the name of the corresponding LED.
|
||||||
|
|
||||||
|
LED sub-node properties:
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: one of
|
||||||
|
"qcom,pm8058-led" (for the normal LEDs at 0x131, 0x132 and 0x133)
|
||||||
|
"qcom,pm8058-keypad-led" (for the "keypad" LED at 0x48)
|
||||||
|
"qcom,pm8058-flash-led" (for the "flash" LEDs at 0x49 and 0xFB)
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- label: see Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
- default-state: see Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
- linux,default-trigger: see Documentation/devicetree/bindings/leds/common.txt
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
qcom,ssbi@500000 {
|
||||||
|
pmicintc: pmic@0 {
|
||||||
|
compatible = "qcom,pm8058";
|
||||||
|
led@48 {
|
||||||
|
compatible = "qcom,pm8058-keypad-led";
|
||||||
|
reg = <0x48>;
|
||||||
|
label = "pm8050:white:keypad";
|
||||||
|
default-state = "off";
|
||||||
|
};
|
||||||
|
led@131 {
|
||||||
|
compatible = "qcom,pm8058-led";
|
||||||
|
reg = <0x131>;
|
||||||
|
label = "pm8058:red";
|
||||||
|
default-state = "off";
|
||||||
|
};
|
||||||
|
led@132 {
|
||||||
|
compatible = "qcom,pm8058-led";
|
||||||
|
reg = <0x132>;
|
||||||
|
label = "pm8058:yellow";
|
||||||
|
default-state = "off";
|
||||||
|
linux,default-trigger = "mmc0";
|
||||||
|
};
|
||||||
|
led@133 {
|
||||||
|
compatible = "qcom,pm8058-led";
|
||||||
|
reg = <0x133>;
|
||||||
|
label = "pm8058:green";
|
||||||
|
default-state = "on";
|
||||||
|
linux,default-trigger = "heartbeat";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -23,13 +23,8 @@ Optional properties:
|
|||||||
see Documentation/devicetree/bindings/leds/common.txt
|
see Documentation/devicetree/bindings/leds/common.txt
|
||||||
- linux,default-trigger : (optional)
|
- linux,default-trigger : (optional)
|
||||||
see Documentation/devicetree/bindings/leds/common.txt
|
see Documentation/devicetree/bindings/leds/common.txt
|
||||||
- default-state: (optional) The initial state of the LED. Valid
|
- default-state: (optional) The initial state of the LED
|
||||||
values are "on", "off", and "keep". If the LED is already on or off
|
see Documentation/devicetree/bindings/leds/common.txt
|
||||||
and the default-state property is set the to same value, then no
|
|
||||||
glitch should be produced where the LED momentarily turns off (or
|
|
||||||
on). The "keep" setting will keep the LED at whatever its current
|
|
||||||
state is, without producing a glitch. The default is off if this
|
|
||||||
property is not present.
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
Kernel driver for Mellanox systems LEDs
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Provide system LED support for the nex Mellanox systems:
|
||||||
|
"msx6710", "msx6720", "msb7700", "msn2700", "msx1410",
|
||||||
|
"msn2410", "msb7800", "msn2740", "msn2100".
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
Driver provides the following LEDs for the systems "msx6710", "msx6720",
|
||||||
|
"msb7700", "msn2700", "msx1410", "msn2410", "msb7800", "msn2740":
|
||||||
|
mlxcpld:fan1:green
|
||||||
|
mlxcpld:fan1:red
|
||||||
|
mlxcpld:fan2:green
|
||||||
|
mlxcpld:fan2:red
|
||||||
|
mlxcpld:fan3:green
|
||||||
|
mlxcpld:fan3:red
|
||||||
|
mlxcpld:fan4:green
|
||||||
|
mlxcpld:fan4:red
|
||||||
|
mlxcpld:psu:green
|
||||||
|
mlxcpld:psu:red
|
||||||
|
mlxcpld:status:green
|
||||||
|
mlxcpld:status:red
|
||||||
|
|
||||||
|
"status"
|
||||||
|
CPLD reg offset: 0x20
|
||||||
|
Bits [3:0]
|
||||||
|
|
||||||
|
"psu"
|
||||||
|
CPLD reg offset: 0x20
|
||||||
|
Bits [7:4]
|
||||||
|
|
||||||
|
"fan1"
|
||||||
|
CPLD reg offset: 0x21
|
||||||
|
Bits [3:0]
|
||||||
|
|
||||||
|
"fan2"
|
||||||
|
CPLD reg offset: 0x21
|
||||||
|
Bits [7:4]
|
||||||
|
|
||||||
|
"fan3"
|
||||||
|
CPLD reg offset: 0x22
|
||||||
|
Bits [3:0]
|
||||||
|
|
||||||
|
"fan4"
|
||||||
|
CPLD reg offset: 0x22
|
||||||
|
Bits [7:4]
|
||||||
|
|
||||||
|
Color mask for all the above LEDs:
|
||||||
|
[bit3,bit2,bit1,bit0] or
|
||||||
|
[bit7,bit6,bit5,bit4]:
|
||||||
|
[0,0,0,0] = LED OFF
|
||||||
|
[0,1,0,1] = Red static ON
|
||||||
|
[1,1,0,1] = Green static ON
|
||||||
|
[0,1,1,0] = Red blink 3Hz
|
||||||
|
[1,1,1,0] = Green blink 3Hz
|
||||||
|
[0,1,1,1] = Red blink 6Hz
|
||||||
|
[1,1,1,1] = Green blink 6Hz
|
||||||
|
|
||||||
|
Driver provides the following LEDs for the system "msn2100":
|
||||||
|
mlxcpld:fan:green
|
||||||
|
mlxcpld:fan:red
|
||||||
|
mlxcpld:psu1:green
|
||||||
|
mlxcpld:psu1:red
|
||||||
|
mlxcpld:psu2:green
|
||||||
|
mlxcpld:psu2:red
|
||||||
|
mlxcpld:status:green
|
||||||
|
mlxcpld:status:red
|
||||||
|
mlxcpld:uid:blue
|
||||||
|
|
||||||
|
"status"
|
||||||
|
CPLD reg offset: 0x20
|
||||||
|
Bits [3:0]
|
||||||
|
|
||||||
|
"fan"
|
||||||
|
CPLD reg offset: 0x21
|
||||||
|
Bits [3:0]
|
||||||
|
|
||||||
|
"psu1"
|
||||||
|
CPLD reg offset: 0x23
|
||||||
|
Bits [3:0]
|
||||||
|
|
||||||
|
"psu2"
|
||||||
|
CPLD reg offset: 0x23
|
||||||
|
Bits [7:4]
|
||||||
|
|
||||||
|
"uid"
|
||||||
|
CPLD reg offset: 0x24
|
||||||
|
Bits [3:0]
|
||||||
|
|
||||||
|
Color mask for all the above LEDs, excepted uid:
|
||||||
|
[bit3,bit2,bit1,bit0] or
|
||||||
|
[bit7,bit6,bit5,bit4]:
|
||||||
|
[0,0,0,0] = LED OFF
|
||||||
|
[0,1,0,1] = Red static ON
|
||||||
|
[1,1,0,1] = Green static ON
|
||||||
|
[0,1,1,0] = Red blink 3Hz
|
||||||
|
[1,1,1,0] = Green blink 3Hz
|
||||||
|
[0,1,1,1] = Red blink 6Hz
|
||||||
|
[1,1,1,1] = Green blink 6Hz
|
||||||
|
|
||||||
|
Color mask for uid LED:
|
||||||
|
[bit3,bit2,bit1,bit0]:
|
||||||
|
[0,0,0,0] = LED OFF
|
||||||
|
[1,1,0,1] = Blue static ON
|
||||||
|
[1,1,1,0] = Blue blink 3Hz
|
||||||
|
[1,1,1,1] = Blue blink 6Hz
|
||||||
|
|
||||||
|
Driver supports HW blinking at 3Hz and 6Hz frequency (50% duty cycle).
|
||||||
|
For 3Hz duty cylce is about 167 msec, for 6Hz is about 83 msec.
|
||||||
@@ -21,24 +21,8 @@ below:
|
|||||||
|
|
||||||
echo oneshot > trigger
|
echo oneshot > trigger
|
||||||
|
|
||||||
This adds the following sysfs attributes to the LED:
|
This adds sysfs attributes to the LED that are documented in:
|
||||||
|
Documentation/ABI/testing/sysfs-class-led-trigger-oneshot
|
||||||
delay_on - specifies for how many milliseconds the LED has to stay at
|
|
||||||
LED_FULL brightness after it has been armed.
|
|
||||||
Default to 100 ms.
|
|
||||||
|
|
||||||
delay_off - specifies for how many milliseconds the LED has to stay at
|
|
||||||
LED_OFF brightness after it has been armed.
|
|
||||||
Default to 100 ms.
|
|
||||||
|
|
||||||
invert - reverse the blink logic. If set to 0 (default) blink on for delay_on
|
|
||||||
ms, then blink off for delay_off ms, leaving the LED normally off. If
|
|
||||||
set to 1, blink off for delay_off ms, then blink on for delay_on ms,
|
|
||||||
leaving the LED normally on.
|
|
||||||
Setting this value also immediately change the LED state.
|
|
||||||
|
|
||||||
shot - write any non-empty string to signal an events, this starts a blink
|
|
||||||
sequence if not already running.
|
|
||||||
|
|
||||||
Example use-case: network devices, initialization:
|
Example use-case: network devices, initialization:
|
||||||
|
|
||||||
|
|||||||
@@ -7678,6 +7678,13 @@ W: http://www.mellanox.com
|
|||||||
Q: http://patchwork.ozlabs.org/project/netdev/list/
|
Q: http://patchwork.ozlabs.org/project/netdev/list/
|
||||||
F: drivers/net/ethernet/mellanox/mlxsw/
|
F: drivers/net/ethernet/mellanox/mlxsw/
|
||||||
|
|
||||||
|
MELLANOX MLXCPLD LED DRIVER
|
||||||
|
M: Vadim Pasternak <vadimp@mellanox.com>
|
||||||
|
L: linux-leds@vger.kernel.org
|
||||||
|
S: Supported
|
||||||
|
F: drivers/leds/leds-mlxcpld.c
|
||||||
|
F: Documentation/leds/leds-mlxcpld.txt
|
||||||
|
|
||||||
MELLANOX PLATFORM DRIVER
|
MELLANOX PLATFORM DRIVER
|
||||||
M: Vadim Pasternak <vadimp@mellanox.com>
|
M: Vadim Pasternak <vadimp@mellanox.com>
|
||||||
L: platform-driver-x86@vger.kernel.org
|
L: platform-driver-x86@vger.kernel.org
|
||||||
|
|||||||
@@ -584,6 +584,18 @@ config LEDS_SEAD3
|
|||||||
This driver can also be built as a module. If so the module
|
This driver can also be built as a module. If so the module
|
||||||
will be called leds-sead3.
|
will be called leds-sead3.
|
||||||
|
|
||||||
|
config LEDS_IS31FL319X
|
||||||
|
tristate "LED Support for ISSI IS31FL319x I2C LED controller family"
|
||||||
|
depends on LEDS_CLASS && I2C && OF
|
||||||
|
select REGMAP_I2C
|
||||||
|
help
|
||||||
|
This option enables support for LEDs connected to ISSI IS31FL319x
|
||||||
|
fancy LED driver chips accessed via the I2C bus.
|
||||||
|
Driver supports individual PWM brightness control for each channel.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so the module will be
|
||||||
|
called leds-is31fl319x.
|
||||||
|
|
||||||
config LEDS_IS31FL32XX
|
config LEDS_IS31FL32XX
|
||||||
tristate "LED support for ISSI IS31FL32XX I2C LED controller family"
|
tristate "LED support for ISSI IS31FL32XX I2C LED controller family"
|
||||||
depends on LEDS_CLASS && I2C && OF
|
depends on LEDS_CLASS && I2C && OF
|
||||||
@@ -631,6 +643,22 @@ config LEDS_VERSATILE
|
|||||||
This option enabled support for the LEDs on the ARM Versatile
|
This option enabled support for the LEDs on the ARM Versatile
|
||||||
and RealView boards. Say Y to enabled these.
|
and RealView boards. Say Y to enabled these.
|
||||||
|
|
||||||
|
config LEDS_PM8058
|
||||||
|
tristate "LED Support for the Qualcomm PM8058 PMIC"
|
||||||
|
depends on MFD_PM8921_CORE
|
||||||
|
depends on LEDS_CLASS
|
||||||
|
help
|
||||||
|
Choose this option if you want to use the LED drivers in
|
||||||
|
the Qualcomm PM8058 PMIC.
|
||||||
|
|
||||||
|
config LEDS_MLXCPLD
|
||||||
|
tristate "LED support for the Mellanox boards"
|
||||||
|
depends on X86_64 && DMI
|
||||||
|
depends on LEDS_CLASS
|
||||||
|
help
|
||||||
|
This option enabled support for the LEDs on the Mellanox
|
||||||
|
boards. Say Y to enabled these.
|
||||||
|
|
||||||
comment "LED Triggers"
|
comment "LED Triggers"
|
||||||
source "drivers/leds/trigger/Kconfig"
|
source "drivers/leds/trigger/Kconfig"
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,10 @@ obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
|
|||||||
obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o
|
obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o
|
||||||
obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o
|
obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o
|
||||||
obj-$(CONFIG_LEDS_SEAD3) += leds-sead3.o
|
obj-$(CONFIG_LEDS_SEAD3) += leds-sead3.o
|
||||||
|
obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o
|
||||||
obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
|
obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
|
||||||
|
obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o
|
||||||
|
obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
|
||||||
|
|
||||||
# LED SPI Drivers
|
# LED SPI Drivers
|
||||||
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
|
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
|
||||||
|
|||||||
+14
-11
@@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/export.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
@@ -81,21 +81,23 @@ ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
|
|||||||
down_read(&led_cdev->trigger_lock);
|
down_read(&led_cdev->trigger_lock);
|
||||||
|
|
||||||
if (!led_cdev->trigger)
|
if (!led_cdev->trigger)
|
||||||
len += sprintf(buf+len, "[none] ");
|
len += scnprintf(buf+len, PAGE_SIZE - len, "[none] ");
|
||||||
else
|
else
|
||||||
len += sprintf(buf+len, "none ");
|
len += scnprintf(buf+len, PAGE_SIZE - len, "none ");
|
||||||
|
|
||||||
list_for_each_entry(trig, &trigger_list, next_trig) {
|
list_for_each_entry(trig, &trigger_list, next_trig) {
|
||||||
if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
|
if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
|
||||||
trig->name))
|
trig->name))
|
||||||
len += sprintf(buf+len, "[%s] ", trig->name);
|
len += scnprintf(buf+len, PAGE_SIZE - len, "[%s] ",
|
||||||
|
trig->name);
|
||||||
else
|
else
|
||||||
len += sprintf(buf+len, "%s ", trig->name);
|
len += scnprintf(buf+len, PAGE_SIZE - len, "%s ",
|
||||||
|
trig->name);
|
||||||
}
|
}
|
||||||
up_read(&led_cdev->trigger_lock);
|
up_read(&led_cdev->trigger_lock);
|
||||||
up_read(&triggers_list_lock);
|
up_read(&triggers_list_lock);
|
||||||
|
|
||||||
len += sprintf(len+buf, "\n");
|
len += scnprintf(len+buf, PAGE_SIZE - len, "\n");
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(led_trigger_show);
|
EXPORT_SYMBOL_GPL(led_trigger_show);
|
||||||
@@ -108,6 +110,9 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
|
|||||||
char *envp[2];
|
char *envp[2];
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
|
if (!led_cdev->trigger && !trig)
|
||||||
|
return;
|
||||||
|
|
||||||
name = trig ? trig->name : "none";
|
name = trig ? trig->name : "none";
|
||||||
event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name);
|
event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name);
|
||||||
|
|
||||||
@@ -136,7 +141,9 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
|
|||||||
if (event) {
|
if (event) {
|
||||||
envp[0] = event;
|
envp[0] = event;
|
||||||
envp[1] = NULL;
|
envp[1] = NULL;
|
||||||
kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp);
|
if (kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp))
|
||||||
|
dev_err(led_cdev->dev,
|
||||||
|
"%s: Error sending uevent\n", __func__);
|
||||||
kfree(event);
|
kfree(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,7 +364,3 @@ void led_trigger_unregister_simple(struct led_trigger *trig)
|
|||||||
kfree(trig);
|
kfree(trig);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
|
EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
|
||||||
|
|
||||||
MODULE_AUTHOR("Richard Purdie");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
MODULE_DESCRIPTION("LED Triggers Core");
|
|
||||||
|
|||||||
+28
-50
@@ -26,15 +26,19 @@ struct gpio_led_data {
|
|||||||
struct gpio_desc *gpiod;
|
struct gpio_desc *gpiod;
|
||||||
u8 can_sleep;
|
u8 can_sleep;
|
||||||
u8 blinking;
|
u8 blinking;
|
||||||
int (*platform_gpio_blink_set)(struct gpio_desc *desc, int state,
|
gpio_blink_set_t platform_gpio_blink_set;
|
||||||
unsigned long *delay_on, unsigned long *delay_off);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline struct gpio_led_data *
|
||||||
|
cdev_to_gpio_led_data(struct led_classdev *led_cdev)
|
||||||
|
{
|
||||||
|
return container_of(led_cdev, struct gpio_led_data, cdev);
|
||||||
|
}
|
||||||
|
|
||||||
static void gpio_led_set(struct led_classdev *led_cdev,
|
static void gpio_led_set(struct led_classdev *led_cdev,
|
||||||
enum led_brightness value)
|
enum led_brightness value)
|
||||||
{
|
{
|
||||||
struct gpio_led_data *led_dat =
|
struct gpio_led_data *led_dat = cdev_to_gpio_led_data(led_cdev);
|
||||||
container_of(led_cdev, struct gpio_led_data, cdev);
|
|
||||||
int level;
|
int level;
|
||||||
|
|
||||||
if (value == LED_OFF)
|
if (value == LED_OFF)
|
||||||
@@ -64,8 +68,7 @@ static int gpio_led_set_blocking(struct led_classdev *led_cdev,
|
|||||||
static int gpio_blink_set(struct led_classdev *led_cdev,
|
static int gpio_blink_set(struct led_classdev *led_cdev,
|
||||||
unsigned long *delay_on, unsigned long *delay_off)
|
unsigned long *delay_on, unsigned long *delay_off)
|
||||||
{
|
{
|
||||||
struct gpio_led_data *led_dat =
|
struct gpio_led_data *led_dat = cdev_to_gpio_led_data(led_cdev);
|
||||||
container_of(led_cdev, struct gpio_led_data, cdev);
|
|
||||||
|
|
||||||
led_dat->blinking = 1;
|
led_dat->blinking = 1;
|
||||||
return led_dat->platform_gpio_blink_set(led_dat->gpiod, GPIO_LED_BLINK,
|
return led_dat->platform_gpio_blink_set(led_dat->gpiod, GPIO_LED_BLINK,
|
||||||
@@ -74,8 +77,7 @@ static int gpio_blink_set(struct led_classdev *led_cdev,
|
|||||||
|
|
||||||
static int create_gpio_led(const struct gpio_led *template,
|
static int create_gpio_led(const struct gpio_led *template,
|
||||||
struct gpio_led_data *led_dat, struct device *parent,
|
struct gpio_led_data *led_dat, struct device *parent,
|
||||||
int (*blink_set)(struct gpio_desc *, int, unsigned long *,
|
gpio_blink_set_t blink_set)
|
||||||
unsigned long *))
|
|
||||||
{
|
{
|
||||||
int ret, state;
|
int ret, state;
|
||||||
|
|
||||||
@@ -120,10 +122,13 @@ static int create_gpio_led(const struct gpio_led *template,
|
|||||||
led_dat->platform_gpio_blink_set = blink_set;
|
led_dat->platform_gpio_blink_set = blink_set;
|
||||||
led_dat->cdev.blink_set = gpio_blink_set;
|
led_dat->cdev.blink_set = gpio_blink_set;
|
||||||
}
|
}
|
||||||
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
|
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) {
|
||||||
state = !!gpiod_get_value_cansleep(led_dat->gpiod);
|
state = gpiod_get_value_cansleep(led_dat->gpiod);
|
||||||
else
|
if (state < 0)
|
||||||
|
return state;
|
||||||
|
} else {
|
||||||
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
|
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
|
||||||
|
}
|
||||||
led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
|
led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
|
||||||
if (!template->retain_state_suspended)
|
if (!template->retain_state_suspended)
|
||||||
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||||
@@ -134,7 +139,7 @@ static int create_gpio_led(const struct gpio_led *template,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return led_classdev_register(parent, &led_dat->cdev);
|
return devm_led_classdev_register(parent, &led_dat->cdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct gpio_leds_priv {
|
struct gpio_leds_priv {
|
||||||
@@ -154,7 +159,6 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
|
|||||||
struct fwnode_handle *child;
|
struct fwnode_handle *child;
|
||||||
struct gpio_leds_priv *priv;
|
struct gpio_leds_priv *priv;
|
||||||
int count, ret;
|
int count, ret;
|
||||||
struct device_node *np;
|
|
||||||
|
|
||||||
count = device_get_child_node_count(dev);
|
count = device_get_child_node_count(dev);
|
||||||
if (!count)
|
if (!count)
|
||||||
@@ -168,26 +172,22 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
|
|||||||
struct gpio_led_data *led_dat = &priv->leds[priv->num_leds];
|
struct gpio_led_data *led_dat = &priv->leds[priv->num_leds];
|
||||||
struct gpio_led led = {};
|
struct gpio_led led = {};
|
||||||
const char *state = NULL;
|
const char *state = NULL;
|
||||||
|
struct device_node *np = to_of_node(child);
|
||||||
|
|
||||||
led.gpiod = devm_get_gpiod_from_child(dev, NULL, child);
|
led.gpiod = devm_get_gpiod_from_child(dev, NULL, child);
|
||||||
if (IS_ERR(led.gpiod)) {
|
if (IS_ERR(led.gpiod)) {
|
||||||
fwnode_handle_put(child);
|
fwnode_handle_put(child);
|
||||||
ret = PTR_ERR(led.gpiod);
|
return ERR_CAST(led.gpiod);
|
||||||
goto err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
np = to_of_node(child);
|
ret = fwnode_property_read_string(child, "label", &led.name);
|
||||||
|
if (ret && IS_ENABLED(CONFIG_OF) && np)
|
||||||
if (fwnode_property_present(child, "label")) {
|
led.name = np->name;
|
||||||
fwnode_property_read_string(child, "label", &led.name);
|
if (!led.name) {
|
||||||
} else {
|
fwnode_handle_put(child);
|
||||||
if (IS_ENABLED(CONFIG_OF) && !led.name && np)
|
return ERR_PTR(-EINVAL);
|
||||||
led.name = np->name;
|
|
||||||
if (!led.name) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fwnode_property_read_string(child, "linux,default-trigger",
|
fwnode_property_read_string(child, "linux,default-trigger",
|
||||||
&led.default_trigger);
|
&led.default_trigger);
|
||||||
|
|
||||||
@@ -209,18 +209,13 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
|
|||||||
ret = create_gpio_led(&led, led_dat, dev, NULL);
|
ret = create_gpio_led(&led, led_dat, dev, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fwnode_handle_put(child);
|
fwnode_handle_put(child);
|
||||||
goto err;
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
led_dat->cdev.dev->of_node = np;
|
led_dat->cdev.dev->of_node = np;
|
||||||
priv->num_leds++;
|
priv->num_leds++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return priv;
|
return priv;
|
||||||
|
|
||||||
err:
|
|
||||||
for (count = priv->num_leds - 1; count >= 0; count--)
|
|
||||||
led_classdev_unregister(&priv->leds[count].cdev);
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id of_gpio_leds_match[] = {
|
static const struct of_device_id of_gpio_leds_match[] = {
|
||||||
@@ -248,13 +243,8 @@ static int gpio_led_probe(struct platform_device *pdev)
|
|||||||
ret = create_gpio_led(&pdata->leds[i],
|
ret = create_gpio_led(&pdata->leds[i],
|
||||||
&priv->leds[i],
|
&priv->leds[i],
|
||||||
&pdev->dev, pdata->gpio_blink_set);
|
&pdev->dev, pdata->gpio_blink_set);
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
/* On failure: unwind the led creations */
|
|
||||||
for (i = i - 1; i >= 0; i--)
|
|
||||||
led_classdev_unregister(
|
|
||||||
&priv->leds[i].cdev);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
priv = gpio_leds_create(pdev);
|
priv = gpio_leds_create(pdev);
|
||||||
@@ -267,17 +257,6 @@ static int gpio_led_probe(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gpio_led_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct gpio_leds_priv *priv = platform_get_drvdata(pdev);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < priv->num_leds; i++)
|
|
||||||
led_classdev_unregister(&priv->leds[i].cdev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gpio_led_shutdown(struct platform_device *pdev)
|
static void gpio_led_shutdown(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct gpio_leds_priv *priv = platform_get_drvdata(pdev);
|
struct gpio_leds_priv *priv = platform_get_drvdata(pdev);
|
||||||
@@ -292,7 +271,6 @@ static void gpio_led_shutdown(struct platform_device *pdev)
|
|||||||
|
|
||||||
static struct platform_driver gpio_led_driver = {
|
static struct platform_driver gpio_led_driver = {
|
||||||
.probe = gpio_led_probe,
|
.probe = gpio_led_probe,
|
||||||
.remove = gpio_led_remove,
|
|
||||||
.shutdown = gpio_led_shutdown,
|
.shutdown = gpio_led_shutdown,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "leds-gpio",
|
.name = "leds-gpio",
|
||||||
|
|||||||
@@ -0,0 +1,450 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015-16 Golden Delicious Computers
|
||||||
|
*
|
||||||
|
* Author: Nikolaus Schaller <hns@goldelico.com>
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of version 2 of
|
||||||
|
* the GNU General Public License. See the file COPYING in the main
|
||||||
|
* directory of this archive for more details.
|
||||||
|
*
|
||||||
|
* LED driver for the IS31FL319{0,1,3,6,9} to drive 1, 3, 6 or 9 light
|
||||||
|
* effect LEDs.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/leds.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
/* register numbers */
|
||||||
|
#define IS31FL319X_SHUTDOWN 0x00
|
||||||
|
#define IS31FL319X_CTRL1 0x01
|
||||||
|
#define IS31FL319X_CTRL2 0x02
|
||||||
|
#define IS31FL319X_CONFIG1 0x03
|
||||||
|
#define IS31FL319X_CONFIG2 0x04
|
||||||
|
#define IS31FL319X_RAMP_MODE 0x05
|
||||||
|
#define IS31FL319X_BREATH_MASK 0x06
|
||||||
|
#define IS31FL319X_PWM(channel) (0x07 + channel)
|
||||||
|
#define IS31FL319X_DATA_UPDATE 0x10
|
||||||
|
#define IS31FL319X_T0(channel) (0x11 + channel)
|
||||||
|
#define IS31FL319X_T123_1 0x1a
|
||||||
|
#define IS31FL319X_T123_2 0x1b
|
||||||
|
#define IS31FL319X_T123_3 0x1c
|
||||||
|
#define IS31FL319X_T4(channel) (0x1d + channel)
|
||||||
|
#define IS31FL319X_TIME_UPDATE 0x26
|
||||||
|
#define IS31FL319X_RESET 0xff
|
||||||
|
|
||||||
|
#define IS31FL319X_REG_CNT (IS31FL319X_RESET + 1)
|
||||||
|
|
||||||
|
#define IS31FL319X_MAX_LEDS 9
|
||||||
|
|
||||||
|
/* CS (Current Setting) in CONFIG2 register */
|
||||||
|
#define IS31FL319X_CONFIG2_CS_SHIFT 4
|
||||||
|
#define IS31FL319X_CONFIG2_CS_MASK 0x7
|
||||||
|
#define IS31FL319X_CONFIG2_CS_STEP_REF 12
|
||||||
|
|
||||||
|
#define IS31FL319X_CURRENT_MIN ((u32)5000)
|
||||||
|
#define IS31FL319X_CURRENT_MAX ((u32)40000)
|
||||||
|
#define IS31FL319X_CURRENT_STEP ((u32)5000)
|
||||||
|
#define IS31FL319X_CURRENT_DEFAULT ((u32)20000)
|
||||||
|
|
||||||
|
/* Audio gain in CONFIG2 register */
|
||||||
|
#define IS31FL319X_AUDIO_GAIN_DB_MAX ((u32)21)
|
||||||
|
#define IS31FL319X_AUDIO_GAIN_DB_STEP ((u32)3)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* regmap is used as a cache of chip's register space,
|
||||||
|
* to avoid reading back brightness values from chip,
|
||||||
|
* which is known to hang.
|
||||||
|
*/
|
||||||
|
struct is31fl319x_chip {
|
||||||
|
const struct is31fl319x_chipdef *cdef;
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct mutex lock;
|
||||||
|
u32 audio_gain_db;
|
||||||
|
|
||||||
|
struct is31fl319x_led {
|
||||||
|
struct is31fl319x_chip *chip;
|
||||||
|
struct led_classdev cdev;
|
||||||
|
u32 max_microamp;
|
||||||
|
bool configured;
|
||||||
|
} leds[IS31FL319X_MAX_LEDS];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct is31fl319x_chipdef {
|
||||||
|
int num_leds;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct is31fl319x_chipdef is31fl3190_cdef = {
|
||||||
|
.num_leds = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct is31fl319x_chipdef is31fl3193_cdef = {
|
||||||
|
.num_leds = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct is31fl319x_chipdef is31fl3196_cdef = {
|
||||||
|
.num_leds = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct is31fl319x_chipdef is31fl3199_cdef = {
|
||||||
|
.num_leds = 9,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id of_is31fl319x_match[] = {
|
||||||
|
{ .compatible = "issi,is31fl3190", .data = &is31fl3190_cdef, },
|
||||||
|
{ .compatible = "issi,is31fl3191", .data = &is31fl3190_cdef, },
|
||||||
|
{ .compatible = "issi,is31fl3193", .data = &is31fl3193_cdef, },
|
||||||
|
{ .compatible = "issi,is31fl3196", .data = &is31fl3196_cdef, },
|
||||||
|
{ .compatible = "issi,is31fl3199", .data = &is31fl3199_cdef, },
|
||||||
|
{ .compatible = "si-en,sn3199", .data = &is31fl3199_cdef, },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, of_is31fl319x_match);
|
||||||
|
|
||||||
|
static int is31fl319x_brightness_set(struct led_classdev *cdev,
|
||||||
|
enum led_brightness brightness)
|
||||||
|
{
|
||||||
|
struct is31fl319x_led *led = container_of(cdev, struct is31fl319x_led,
|
||||||
|
cdev);
|
||||||
|
struct is31fl319x_chip *is31 = led->chip;
|
||||||
|
int chan = led - is31->leds;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
u8 ctrl1 = 0, ctrl2 = 0;
|
||||||
|
|
||||||
|
dev_dbg(&is31->client->dev, "%s %d: %d\n", __func__, chan, brightness);
|
||||||
|
|
||||||
|
mutex_lock(&is31->lock);
|
||||||
|
|
||||||
|
/* update PWM register */
|
||||||
|
ret = regmap_write(is31->regmap, IS31FL319X_PWM(chan), brightness);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* read current brightness of all PWM channels */
|
||||||
|
for (i = 0; i < is31->cdef->num_leds; i++) {
|
||||||
|
unsigned int pwm_value;
|
||||||
|
bool on;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* since neither cdev nor the chip can provide
|
||||||
|
* the current setting, we read from the regmap cache
|
||||||
|
*/
|
||||||
|
|
||||||
|
ret = regmap_read(is31->regmap, IS31FL319X_PWM(i), &pwm_value);
|
||||||
|
dev_dbg(&is31->client->dev, "%s read %d: ret=%d: %d\n",
|
||||||
|
__func__, i, ret, pwm_value);
|
||||||
|
on = ret >= 0 && pwm_value > LED_OFF;
|
||||||
|
|
||||||
|
if (i < 3)
|
||||||
|
ctrl1 |= on << i; /* 0..2 => bit 0..2 */
|
||||||
|
else if (i < 6)
|
||||||
|
ctrl1 |= on << (i + 1); /* 3..5 => bit 4..6 */
|
||||||
|
else
|
||||||
|
ctrl2 |= on << (i - 6); /* 6..8 => bit 0..2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctrl1 > 0 || ctrl2 > 0) {
|
||||||
|
dev_dbg(&is31->client->dev, "power up %02x %02x\n",
|
||||||
|
ctrl1, ctrl2);
|
||||||
|
regmap_write(is31->regmap, IS31FL319X_CTRL1, ctrl1);
|
||||||
|
regmap_write(is31->regmap, IS31FL319X_CTRL2, ctrl2);
|
||||||
|
/* update PWMs */
|
||||||
|
regmap_write(is31->regmap, IS31FL319X_DATA_UPDATE, 0x00);
|
||||||
|
/* enable chip from shut down */
|
||||||
|
ret = regmap_write(is31->regmap, IS31FL319X_SHUTDOWN, 0x01);
|
||||||
|
} else {
|
||||||
|
dev_dbg(&is31->client->dev, "power down\n");
|
||||||
|
/* shut down (no need to clear CTRL1/2) */
|
||||||
|
ret = regmap_write(is31->regmap, IS31FL319X_SHUTDOWN, 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&is31->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is31fl319x_parse_child_dt(const struct device *dev,
|
||||||
|
const struct device_node *child,
|
||||||
|
struct is31fl319x_led *led)
|
||||||
|
{
|
||||||
|
struct led_classdev *cdev = &led->cdev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (of_property_read_string(child, "label", &cdev->name))
|
||||||
|
cdev->name = child->name;
|
||||||
|
|
||||||
|
ret = of_property_read_string(child, "linux,default-trigger",
|
||||||
|
&cdev->default_trigger);
|
||||||
|
if (ret < 0 && ret != -EINVAL) /* is optional */
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
led->max_microamp = IS31FL319X_CURRENT_DEFAULT;
|
||||||
|
ret = of_property_read_u32(child, "led-max-microamp",
|
||||||
|
&led->max_microamp);
|
||||||
|
if (!ret) {
|
||||||
|
if (led->max_microamp < IS31FL319X_CURRENT_MIN)
|
||||||
|
return -EINVAL; /* not supported */
|
||||||
|
led->max_microamp = min(led->max_microamp,
|
||||||
|
IS31FL319X_CURRENT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is31fl319x_parse_dt(struct device *dev,
|
||||||
|
struct is31fl319x_chip *is31)
|
||||||
|
{
|
||||||
|
struct device_node *np = dev->of_node, *child;
|
||||||
|
const struct of_device_id *of_dev_id;
|
||||||
|
int count;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!np)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
of_dev_id = of_match_device(of_is31fl319x_match, dev);
|
||||||
|
if (!of_dev_id) {
|
||||||
|
dev_err(dev, "Failed to match device with supported chips\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
is31->cdef = of_dev_id->data;
|
||||||
|
|
||||||
|
count = of_get_child_count(np);
|
||||||
|
|
||||||
|
dev_dbg(dev, "probe %s with %d leds defined in DT\n",
|
||||||
|
of_dev_id->compatible, count);
|
||||||
|
|
||||||
|
if (!count || count > is31->cdef->num_leds) {
|
||||||
|
dev_err(dev, "Number of leds defined must be between 1 and %u\n",
|
||||||
|
is31->cdef->num_leds);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
for_each_child_of_node(np, child) {
|
||||||
|
struct is31fl319x_led *led;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(child, "reg", ®);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Failed to read led 'reg' property\n");
|
||||||
|
goto put_child_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reg < 1 || reg > is31->cdef->num_leds) {
|
||||||
|
dev_err(dev, "invalid led reg %u\n", reg);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto put_child_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
led = &is31->leds[reg - 1];
|
||||||
|
|
||||||
|
if (led->configured) {
|
||||||
|
dev_err(dev, "led %u is already configured\n", reg);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto put_child_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = is31fl319x_parse_child_dt(dev, child, led);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "led %u DT parsing failed\n", reg);
|
||||||
|
goto put_child_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
led->configured = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
is31->audio_gain_db = 0;
|
||||||
|
ret = of_property_read_u32(np, "audio-gain-db", &is31->audio_gain_db);
|
||||||
|
if (!ret)
|
||||||
|
is31->audio_gain_db = min(is31->audio_gain_db,
|
||||||
|
IS31FL319X_AUDIO_GAIN_DB_MAX);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
put_child_node:
|
||||||
|
of_node_put(child);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is31fl319x_readable_reg(struct device *dev, unsigned int reg)
|
||||||
|
{ /* we have no readable registers */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is31fl319x_volatile_reg(struct device *dev, unsigned int reg)
|
||||||
|
{ /* volatile registers are not cached */
|
||||||
|
switch (reg) {
|
||||||
|
case IS31FL319X_DATA_UPDATE:
|
||||||
|
case IS31FL319X_TIME_UPDATE:
|
||||||
|
case IS31FL319X_RESET:
|
||||||
|
return true; /* always write-through */
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct reg_default is31fl319x_reg_defaults[] = {
|
||||||
|
{ IS31FL319X_CONFIG1, 0x00},
|
||||||
|
{ IS31FL319X_CONFIG2, 0x00},
|
||||||
|
{ IS31FL319X_PWM(0), 0x00},
|
||||||
|
{ IS31FL319X_PWM(1), 0x00},
|
||||||
|
{ IS31FL319X_PWM(2), 0x00},
|
||||||
|
{ IS31FL319X_PWM(3), 0x00},
|
||||||
|
{ IS31FL319X_PWM(4), 0x00},
|
||||||
|
{ IS31FL319X_PWM(5), 0x00},
|
||||||
|
{ IS31FL319X_PWM(6), 0x00},
|
||||||
|
{ IS31FL319X_PWM(7), 0x00},
|
||||||
|
{ IS31FL319X_PWM(8), 0x00},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct regmap_config regmap_config = {
|
||||||
|
.reg_bits = 8,
|
||||||
|
.val_bits = 8,
|
||||||
|
.max_register = IS31FL319X_REG_CNT,
|
||||||
|
.cache_type = REGCACHE_FLAT,
|
||||||
|
.readable_reg = is31fl319x_readable_reg,
|
||||||
|
.volatile_reg = is31fl319x_volatile_reg,
|
||||||
|
.reg_defaults = is31fl319x_reg_defaults,
|
||||||
|
.num_reg_defaults = ARRAY_SIZE(is31fl319x_reg_defaults),
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int is31fl319x_microamp_to_cs(struct device *dev, u32 microamp)
|
||||||
|
{ /* round down to nearest supported value (range check done by caller) */
|
||||||
|
u32 step = microamp / IS31FL319X_CURRENT_STEP;
|
||||||
|
|
||||||
|
return ((IS31FL319X_CONFIG2_CS_STEP_REF - step) &
|
||||||
|
IS31FL319X_CONFIG2_CS_MASK) <<
|
||||||
|
IS31FL319X_CONFIG2_CS_SHIFT; /* CS encoding */
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int is31fl319x_db_to_gain(u32 dezibel)
|
||||||
|
{ /* round down to nearest supported value (range check done by caller) */
|
||||||
|
return dezibel / IS31FL319X_AUDIO_GAIN_DB_STEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is31fl319x_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct is31fl319x_chip *is31;
|
||||||
|
struct device *dev = &client->dev;
|
||||||
|
struct i2c_adapter *adapter = to_i2c_adapter(dev->parent);
|
||||||
|
int err;
|
||||||
|
int i = 0;
|
||||||
|
u32 aggregated_led_microamp = IS31FL319X_CURRENT_MAX;
|
||||||
|
|
||||||
|
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
is31 = devm_kzalloc(&client->dev, sizeof(*is31), GFP_KERNEL);
|
||||||
|
if (!is31)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mutex_init(&is31->lock);
|
||||||
|
|
||||||
|
err = is31fl319x_parse_dt(&client->dev, is31);
|
||||||
|
if (err)
|
||||||
|
goto free_mutex;
|
||||||
|
|
||||||
|
is31->client = client;
|
||||||
|
is31->regmap = devm_regmap_init_i2c(client, ®map_config);
|
||||||
|
if (IS_ERR(is31->regmap)) {
|
||||||
|
dev_err(&client->dev, "failed to allocate register map\n");
|
||||||
|
err = PTR_ERR(is31->regmap);
|
||||||
|
goto free_mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_set_clientdata(client, is31);
|
||||||
|
|
||||||
|
/* check for write-reply from chip (we can't read any registers) */
|
||||||
|
err = regmap_write(is31->regmap, IS31FL319X_RESET, 0x00);
|
||||||
|
if (err < 0) {
|
||||||
|
dev_err(&client->dev, "no response from chip write: err = %d\n",
|
||||||
|
err);
|
||||||
|
err = -EIO; /* does not answer */
|
||||||
|
goto free_mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kernel conventions require per-LED led-max-microamp property.
|
||||||
|
* But the chip does not allow to limit individual LEDs.
|
||||||
|
* So we take minimum from all subnodes for safety of hardware.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < is31->cdef->num_leds; i++)
|
||||||
|
if (is31->leds[i].configured &&
|
||||||
|
is31->leds[i].max_microamp < aggregated_led_microamp)
|
||||||
|
aggregated_led_microamp = is31->leds[i].max_microamp;
|
||||||
|
|
||||||
|
regmap_write(is31->regmap, IS31FL319X_CONFIG2,
|
||||||
|
is31fl319x_microamp_to_cs(dev, aggregated_led_microamp) |
|
||||||
|
is31fl319x_db_to_gain(is31->audio_gain_db));
|
||||||
|
|
||||||
|
for (i = 0; i < is31->cdef->num_leds; i++) {
|
||||||
|
struct is31fl319x_led *led = &is31->leds[i];
|
||||||
|
|
||||||
|
if (!led->configured)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
led->chip = is31;
|
||||||
|
led->cdev.brightness_set_blocking = is31fl319x_brightness_set;
|
||||||
|
|
||||||
|
err = devm_led_classdev_register(&client->dev, &led->cdev);
|
||||||
|
if (err < 0)
|
||||||
|
goto free_mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
free_mutex:
|
||||||
|
mutex_destroy(&is31->lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is31fl319x_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct is31fl319x_chip *is31 = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
mutex_destroy(&is31->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* i2c-core (and modalias) requires that id_table be properly filled,
|
||||||
|
* even though it is not used for DeviceTree based instantiation.
|
||||||
|
*/
|
||||||
|
static const struct i2c_device_id is31fl319x_id[] = {
|
||||||
|
{ "is31fl3190" },
|
||||||
|
{ "is31fl3191" },
|
||||||
|
{ "is31fl3193" },
|
||||||
|
{ "is31fl3196" },
|
||||||
|
{ "is31fl3199" },
|
||||||
|
{ "sn3199" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, is31fl319x_id);
|
||||||
|
|
||||||
|
static struct i2c_driver is31fl319x_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "leds-is31fl319x",
|
||||||
|
.of_match_table = of_match_ptr(of_is31fl319x_match),
|
||||||
|
},
|
||||||
|
.probe = is31fl319x_probe,
|
||||||
|
.remove = is31fl319x_remove,
|
||||||
|
.id_table = is31fl319x_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i2c_driver(is31fl319x_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
|
||||||
|
MODULE_AUTHOR("Andrey Utkin <andrey_utkin@fastmail.com>");
|
||||||
|
MODULE_DESCRIPTION("IS31FL319X LED driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
@@ -0,0 +1,430 @@
|
|||||||
|
/*
|
||||||
|
* drivers/leds/leds-mlxcpld.c
|
||||||
|
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
|
||||||
|
* Copyright (c) 2016 Vadim Pasternak <vadimp@mellanox.com>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the names of the copyright holders nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* Alternatively, this software may be distributed under the terms of the
|
||||||
|
* GNU General Public License ("GPL") version 2 as published by the Free
|
||||||
|
* Software Foundation.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/dmi.h>
|
||||||
|
#include <linux/hwmon.h>
|
||||||
|
#include <linux/hwmon-sysfs.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/leds.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mod_devicetable.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500 /* LPC bus access */
|
||||||
|
|
||||||
|
/* Color codes for LEDs */
|
||||||
|
#define MLXCPLD_LED_OFFSET_HALF 0x01 /* Offset from solid: 3Hz blink */
|
||||||
|
#define MLXCPLD_LED_OFFSET_FULL 0x02 /* Offset from solid: 6Hz blink */
|
||||||
|
#define MLXCPLD_LED_IS_OFF 0x00 /* Off */
|
||||||
|
#define MLXCPLD_LED_RED_STATIC_ON 0x05 /* Solid red */
|
||||||
|
#define MLXCPLD_LED_RED_BLINK_HALF (MLXCPLD_LED_RED_STATIC_ON + \
|
||||||
|
MLXCPLD_LED_OFFSET_HALF)
|
||||||
|
#define MLXCPLD_LED_RED_BLINK_FULL (MLXCPLD_LED_RED_STATIC_ON + \
|
||||||
|
MLXCPLD_LED_OFFSET_FULL)
|
||||||
|
#define MLXCPLD_LED_GREEN_STATIC_ON 0x0D /* Solid green */
|
||||||
|
#define MLXCPLD_LED_GREEN_BLINK_HALF (MLXCPLD_LED_GREEN_STATIC_ON + \
|
||||||
|
MLXCPLD_LED_OFFSET_HALF)
|
||||||
|
#define MLXCPLD_LED_GREEN_BLINK_FULL (MLXCPLD_LED_GREEN_STATIC_ON + \
|
||||||
|
MLXCPLD_LED_OFFSET_FULL)
|
||||||
|
#define MLXCPLD_LED_BLINK_3HZ 167 /* ~167 msec off/on */
|
||||||
|
#define MLXCPLD_LED_BLINK_6HZ 83 /* ~83 msec off/on */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mlxcpld_param - LED access parameters:
|
||||||
|
* @offset - offset for LED access in CPLD device
|
||||||
|
* @mask - mask for LED access in CPLD device
|
||||||
|
* @base_color - base color code for LED
|
||||||
|
**/
|
||||||
|
struct mlxcpld_param {
|
||||||
|
u8 offset;
|
||||||
|
u8 mask;
|
||||||
|
u8 base_color;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mlxcpld_led_priv - LED private data:
|
||||||
|
* @cled - LED class device instance
|
||||||
|
* @param - LED CPLD access parameters
|
||||||
|
**/
|
||||||
|
struct mlxcpld_led_priv {
|
||||||
|
struct led_classdev cdev;
|
||||||
|
struct mlxcpld_param param;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define cdev_to_priv(c) container_of(c, struct mlxcpld_led_priv, cdev)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mlxcpld_led_profile - system LED profile (defined per system class):
|
||||||
|
* @offset - offset for LED access in CPLD device
|
||||||
|
* @mask - mask for LED access in CPLD device
|
||||||
|
* @base_color - base color code
|
||||||
|
* @brightness - default brightness setting (on/off)
|
||||||
|
* @name - LED name
|
||||||
|
**/
|
||||||
|
struct mlxcpld_led_profile {
|
||||||
|
u8 offset;
|
||||||
|
u8 mask;
|
||||||
|
u8 base_color;
|
||||||
|
enum led_brightness brightness;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mlxcpld_led_pdata - system LED private data
|
||||||
|
* @pdev - platform device pointer
|
||||||
|
* @pled - LED class device instance
|
||||||
|
* @profile - system configuration profile
|
||||||
|
* @num_led_instances - number of LED instances
|
||||||
|
* @lock - device access lock
|
||||||
|
**/
|
||||||
|
struct mlxcpld_led_pdata {
|
||||||
|
struct platform_device *pdev;
|
||||||
|
struct mlxcpld_led_priv *pled;
|
||||||
|
struct mlxcpld_led_profile *profile;
|
||||||
|
int num_led_instances;
|
||||||
|
spinlock_t lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mlxcpld_led_pdata *mlxcpld_led;
|
||||||
|
|
||||||
|
/* Default profile fit the next Mellanox systems:
|
||||||
|
* "msx6710", "msx6720", "msb7700", "msn2700", "msx1410",
|
||||||
|
* "msn2410", "msb7800", "msn2740"
|
||||||
|
*/
|
||||||
|
static struct mlxcpld_led_profile mlxcpld_led_default_profile[] = {
|
||||||
|
{
|
||||||
|
0x21, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1,
|
||||||
|
"mlxcpld:fan1:green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x21, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
|
||||||
|
"mlxcpld:fan1:red",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x21, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1,
|
||||||
|
"mlxcpld:fan2:green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x21, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
|
||||||
|
"mlxcpld:fan2:red",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x22, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1,
|
||||||
|
"mlxcpld:fan3:green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x22, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
|
||||||
|
"mlxcpld:fan3:red",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x22, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1,
|
||||||
|
"mlxcpld:fan4:green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x22, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
|
||||||
|
"mlxcpld:fan4:red",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x20, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1,
|
||||||
|
"mlxcpld:psu:green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x20, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
|
||||||
|
"mlxcpld:psu:red",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x20, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1,
|
||||||
|
"mlxcpld:status:green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x20, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
|
||||||
|
"mlxcpld:status:red",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Profile fit the Mellanox systems based on "msn2100" */
|
||||||
|
static struct mlxcpld_led_profile mlxcpld_led_msn2100_profile[] = {
|
||||||
|
{
|
||||||
|
0x21, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1,
|
||||||
|
"mlxcpld:fan:green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x21, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
|
||||||
|
"mlxcpld:fan:red",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x23, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1,
|
||||||
|
"mlxcpld:psu1:green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x23, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
|
||||||
|
"mlxcpld:psu1:red",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x23, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1,
|
||||||
|
"mlxcpld:psu2:green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x23, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
|
||||||
|
"mlxcpld:psu2:red",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x20, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1,
|
||||||
|
"mlxcpld:status:green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x20, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
|
||||||
|
"mlxcpld:status:red",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0x24, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, LED_OFF,
|
||||||
|
"mlxcpld:uid:blue",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
enum mlxcpld_led_platform_types {
|
||||||
|
MLXCPLD_LED_PLATFORM_DEFAULT,
|
||||||
|
MLXCPLD_LED_PLATFORM_MSN2100,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *mlx_product_names[] = {
|
||||||
|
"DEFAULT",
|
||||||
|
"MSN2100",
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum
|
||||||
|
mlxcpld_led_platform_types mlxcpld_led_platform_check_sys_type(void)
|
||||||
|
{
|
||||||
|
const char *mlx_product_name;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mlx_product_name = dmi_get_system_info(DMI_PRODUCT_NAME);
|
||||||
|
if (!mlx_product_name)
|
||||||
|
return MLXCPLD_LED_PLATFORM_DEFAULT;
|
||||||
|
|
||||||
|
for (i = 1; i < ARRAY_SIZE(mlx_product_names); i++) {
|
||||||
|
if (strstr(mlx_product_name, mlx_product_names[i]))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MLXCPLD_LED_PLATFORM_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mlxcpld_led_bus_access_func(u16 base, u8 offset, u8 rw_flag,
|
||||||
|
u8 *data)
|
||||||
|
{
|
||||||
|
u32 addr = base + offset;
|
||||||
|
|
||||||
|
if (rw_flag == 0)
|
||||||
|
outb(*data, addr);
|
||||||
|
else
|
||||||
|
*data = inb(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mlxcpld_led_store_hw(u8 mask, u8 off, u8 vset)
|
||||||
|
{
|
||||||
|
u8 nib, val;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Each LED is controlled through low or high nibble of the relevant
|
||||||
|
* CPLD register. Register offset is specified by off parameter.
|
||||||
|
* Parameter vset provides color code: 0x0 for off, 0x5 for solid red,
|
||||||
|
* 0x6 for 3Hz blink red, 0xd for solid green, 0xe for 3Hz blink
|
||||||
|
* green.
|
||||||
|
* Parameter mask specifies which nibble is used for specific LED: mask
|
||||||
|
* 0xf0 - lower nibble is to be used (bits from 0 to 3), mask 0x0f -
|
||||||
|
* higher nibble (bits from 4 to 7).
|
||||||
|
*/
|
||||||
|
spin_lock(&mlxcpld_led->lock);
|
||||||
|
mlxcpld_led_bus_access_func(MLXPLAT_CPLD_LPC_REG_BASE_ADRR, off, 1,
|
||||||
|
&val);
|
||||||
|
nib = (mask == 0xf0) ? vset : (vset << 4);
|
||||||
|
val = (val & mask) | nib;
|
||||||
|
mlxcpld_led_bus_access_func(MLXPLAT_CPLD_LPC_REG_BASE_ADRR, off, 0,
|
||||||
|
&val);
|
||||||
|
spin_unlock(&mlxcpld_led->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mlxcpld_led_brightness_set(struct led_classdev *led,
|
||||||
|
enum led_brightness value)
|
||||||
|
{
|
||||||
|
struct mlxcpld_led_priv *pled = cdev_to_priv(led);
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
mlxcpld_led_store_hw(pled->param.mask, pled->param.offset,
|
||||||
|
pled->param.base_color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mlxcpld_led_store_hw(pled->param.mask, pled->param.offset,
|
||||||
|
MLXCPLD_LED_IS_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlxcpld_led_blink_set(struct led_classdev *led,
|
||||||
|
unsigned long *delay_on,
|
||||||
|
unsigned long *delay_off)
|
||||||
|
{
|
||||||
|
struct mlxcpld_led_priv *pled = cdev_to_priv(led);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HW supports two types of blinking: full (6Hz) and half (3Hz).
|
||||||
|
* For delay on/off zero default setting 3Hz is used.
|
||||||
|
*/
|
||||||
|
if (!(*delay_on == 0 && *delay_off == 0) &&
|
||||||
|
!(*delay_on == MLXCPLD_LED_BLINK_3HZ &&
|
||||||
|
*delay_off == MLXCPLD_LED_BLINK_3HZ) &&
|
||||||
|
!(*delay_on == MLXCPLD_LED_BLINK_6HZ &&
|
||||||
|
*delay_off == MLXCPLD_LED_BLINK_6HZ))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (*delay_on == MLXCPLD_LED_BLINK_6HZ)
|
||||||
|
mlxcpld_led_store_hw(pled->param.mask, pled->param.offset,
|
||||||
|
pled->param.base_color +
|
||||||
|
MLXCPLD_LED_OFFSET_FULL);
|
||||||
|
else
|
||||||
|
mlxcpld_led_store_hw(pled->param.mask, pled->param.offset,
|
||||||
|
pled->param.base_color +
|
||||||
|
MLXCPLD_LED_OFFSET_HALF);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlxcpld_led_config(struct device *dev,
|
||||||
|
struct mlxcpld_led_pdata *cpld)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
cpld->pled = devm_kzalloc(dev, sizeof(struct mlxcpld_led_priv) *
|
||||||
|
cpld->num_led_instances, GFP_KERNEL);
|
||||||
|
if (!cpld->pled)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < cpld->num_led_instances; i++) {
|
||||||
|
cpld->pled[i].cdev.name = cpld->profile[i].name;
|
||||||
|
cpld->pled[i].cdev.brightness = cpld->profile[i].brightness;
|
||||||
|
cpld->pled[i].cdev.max_brightness = 1;
|
||||||
|
cpld->pled[i].cdev.brightness_set = mlxcpld_led_brightness_set;
|
||||||
|
cpld->pled[i].cdev.blink_set = mlxcpld_led_blink_set;
|
||||||
|
cpld->pled[i].cdev.flags = LED_CORE_SUSPENDRESUME;
|
||||||
|
err = devm_led_classdev_register(dev, &cpld->pled[i].cdev);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
cpld->pled[i].param.offset = mlxcpld_led->profile[i].offset;
|
||||||
|
cpld->pled[i].param.mask = mlxcpld_led->profile[i].mask;
|
||||||
|
cpld->pled[i].param.base_color =
|
||||||
|
mlxcpld_led->profile[i].base_color;
|
||||||
|
|
||||||
|
if (mlxcpld_led->profile[i].brightness)
|
||||||
|
mlxcpld_led_brightness_set(&cpld->pled[i].cdev,
|
||||||
|
mlxcpld_led->profile[i].brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init mlxcpld_led_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
enum mlxcpld_led_platform_types mlxcpld_led_plat =
|
||||||
|
mlxcpld_led_platform_check_sys_type();
|
||||||
|
|
||||||
|
mlxcpld_led = devm_kzalloc(&pdev->dev, sizeof(*mlxcpld_led),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!mlxcpld_led)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mlxcpld_led->pdev = pdev;
|
||||||
|
|
||||||
|
switch (mlxcpld_led_plat) {
|
||||||
|
case MLXCPLD_LED_PLATFORM_MSN2100:
|
||||||
|
mlxcpld_led->profile = mlxcpld_led_msn2100_profile;
|
||||||
|
mlxcpld_led->num_led_instances =
|
||||||
|
ARRAY_SIZE(mlxcpld_led_msn2100_profile);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
mlxcpld_led->profile = mlxcpld_led_default_profile;
|
||||||
|
mlxcpld_led->num_led_instances =
|
||||||
|
ARRAY_SIZE(mlxcpld_led_default_profile);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_init(&mlxcpld_led->lock);
|
||||||
|
|
||||||
|
return mlxcpld_led_config(&pdev->dev, mlxcpld_led);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver mlxcpld_led_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = KBUILD_MODNAME,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init mlxcpld_led_init(void)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
|
||||||
|
if (IS_ERR(pdev)) {
|
||||||
|
pr_err("Device allocation failed\n");
|
||||||
|
return PTR_ERR(pdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = platform_driver_probe(&mlxcpld_led_driver, mlxcpld_led_probe);
|
||||||
|
if (err) {
|
||||||
|
pr_err("Probe platform driver failed\n");
|
||||||
|
platform_device_unregister(pdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit mlxcpld_led_exit(void)
|
||||||
|
{
|
||||||
|
platform_device_unregister(mlxcpld_led->pdev);
|
||||||
|
platform_driver_unregister(&mlxcpld_led_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(mlxcpld_led_init);
|
||||||
|
module_exit(mlxcpld_led_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
|
||||||
|
MODULE_DESCRIPTION("Mellanox board LED driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_ALIAS("platform:leds_mlxcpld");
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
/* Copyright (c) 2010, 2011, 2016 The Linux Foundation. All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
* only version 2 as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
#include <linux/leds.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
|
#define PM8058_LED_TYPE_COMMON 0x00
|
||||||
|
#define PM8058_LED_TYPE_KEYPAD 0x01
|
||||||
|
#define PM8058_LED_TYPE_FLASH 0x02
|
||||||
|
|
||||||
|
#define PM8058_LED_TYPE_COMMON_MASK 0xf8
|
||||||
|
#define PM8058_LED_TYPE_KEYPAD_MASK 0xf0
|
||||||
|
#define PM8058_LED_TYPE_COMMON_SHIFT 3
|
||||||
|
#define PM8058_LED_TYPE_KEYPAD_SHIFT 4
|
||||||
|
|
||||||
|
struct pm8058_led {
|
||||||
|
struct regmap *map;
|
||||||
|
u32 reg;
|
||||||
|
u32 ledtype;
|
||||||
|
struct led_classdev cdev;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pm8058_led_set(struct led_classdev *cled,
|
||||||
|
enum led_brightness value)
|
||||||
|
{
|
||||||
|
struct pm8058_led *led;
|
||||||
|
int ret = 0;
|
||||||
|
unsigned int mask = 0;
|
||||||
|
unsigned int val = 0;
|
||||||
|
|
||||||
|
led = container_of(cled, struct pm8058_led, cdev);
|
||||||
|
switch (led->ledtype) {
|
||||||
|
case PM8058_LED_TYPE_COMMON:
|
||||||
|
mask = PM8058_LED_TYPE_COMMON_MASK;
|
||||||
|
val = value << PM8058_LED_TYPE_COMMON_SHIFT;
|
||||||
|
break;
|
||||||
|
case PM8058_LED_TYPE_KEYPAD:
|
||||||
|
case PM8058_LED_TYPE_FLASH:
|
||||||
|
mask = PM8058_LED_TYPE_KEYPAD_MASK;
|
||||||
|
val = value << PM8058_LED_TYPE_KEYPAD_SHIFT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regmap_update_bits(led->map, led->reg, mask, val);
|
||||||
|
if (ret)
|
||||||
|
pr_err("Failed to set LED brightness\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum led_brightness pm8058_led_get(struct led_classdev *cled)
|
||||||
|
{
|
||||||
|
struct pm8058_led *led;
|
||||||
|
int ret;
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
|
led = container_of(cled, struct pm8058_led, cdev);
|
||||||
|
|
||||||
|
ret = regmap_read(led->map, led->reg, &val);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("Failed to get LED brightness\n");
|
||||||
|
return LED_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (led->ledtype) {
|
||||||
|
case PM8058_LED_TYPE_COMMON:
|
||||||
|
val &= PM8058_LED_TYPE_COMMON_MASK;
|
||||||
|
val >>= PM8058_LED_TYPE_COMMON_SHIFT;
|
||||||
|
break;
|
||||||
|
case PM8058_LED_TYPE_KEYPAD:
|
||||||
|
case PM8058_LED_TYPE_FLASH:
|
||||||
|
val &= PM8058_LED_TYPE_KEYPAD_MASK;
|
||||||
|
val >>= PM8058_LED_TYPE_KEYPAD_SHIFT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
val = LED_OFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pm8058_led_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct pm8058_led *led;
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
int ret;
|
||||||
|
struct regmap *map;
|
||||||
|
const char *state;
|
||||||
|
enum led_brightness maxbright;
|
||||||
|
|
||||||
|
led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
|
||||||
|
if (!led)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
led->ledtype = (u32)of_device_get_match_data(&pdev->dev);
|
||||||
|
|
||||||
|
map = dev_get_regmap(pdev->dev.parent, NULL);
|
||||||
|
if (!map) {
|
||||||
|
dev_err(&pdev->dev, "Parent regmap unavailable.\n");
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
led->map = map;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(np, "reg", &led->reg);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "no register offset specified\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use label else node name */
|
||||||
|
led->cdev.name = of_get_property(np, "label", NULL) ? : np->name;
|
||||||
|
led->cdev.default_trigger =
|
||||||
|
of_get_property(np, "linux,default-trigger", NULL);
|
||||||
|
led->cdev.brightness_set = pm8058_led_set;
|
||||||
|
led->cdev.brightness_get = pm8058_led_get;
|
||||||
|
if (led->ledtype == PM8058_LED_TYPE_COMMON)
|
||||||
|
maxbright = 31; /* 5 bits */
|
||||||
|
else
|
||||||
|
maxbright = 15; /* 4 bits */
|
||||||
|
led->cdev.max_brightness = maxbright;
|
||||||
|
|
||||||
|
state = of_get_property(np, "default-state", NULL);
|
||||||
|
if (state) {
|
||||||
|
if (!strcmp(state, "keep")) {
|
||||||
|
led->cdev.brightness = pm8058_led_get(&led->cdev);
|
||||||
|
} else if (!strcmp(state, "on")) {
|
||||||
|
led->cdev.brightness = maxbright;
|
||||||
|
pm8058_led_set(&led->cdev, maxbright);
|
||||||
|
} else {
|
||||||
|
led->cdev.brightness = LED_OFF;
|
||||||
|
pm8058_led_set(&led->cdev, LED_OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (led->ledtype == PM8058_LED_TYPE_KEYPAD ||
|
||||||
|
led->ledtype == PM8058_LED_TYPE_FLASH)
|
||||||
|
led->cdev.flags = LED_CORE_SUSPENDRESUME;
|
||||||
|
|
||||||
|
ret = devm_led_classdev_register(&pdev->dev, &led->cdev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "unable to register led \"%s\"\n",
|
||||||
|
led->cdev.name);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id pm8058_leds_id_table[] = {
|
||||||
|
{
|
||||||
|
.compatible = "qcom,pm8058-led",
|
||||||
|
.data = (void *)PM8058_LED_TYPE_COMMON
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "qcom,pm8058-keypad-led",
|
||||||
|
.data = (void *)PM8058_LED_TYPE_KEYPAD
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "qcom,pm8058-flash-led",
|
||||||
|
.data = (void *)PM8058_LED_TYPE_FLASH
|
||||||
|
},
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, pm8058_leds_id_table);
|
||||||
|
|
||||||
|
static struct platform_driver pm8058_led_driver = {
|
||||||
|
.probe = pm8058_led_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "pm8058-leds",
|
||||||
|
.of_match_table = pm8058_leds_id_table,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(pm8058_led_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("PM8058 LEDs driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_ALIAS("platform:pm8058-leds");
|
||||||
@@ -359,6 +359,11 @@ struct led_platform_data {
|
|||||||
struct led_info *leds;
|
struct led_info *leds;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct gpio_desc;
|
||||||
|
typedef int (*gpio_blink_set_t)(struct gpio_desc *desc, int state,
|
||||||
|
unsigned long *delay_on,
|
||||||
|
unsigned long *delay_off);
|
||||||
|
|
||||||
/* For the leds-gpio driver */
|
/* For the leds-gpio driver */
|
||||||
struct gpio_led {
|
struct gpio_led {
|
||||||
const char *name;
|
const char *name;
|
||||||
@@ -382,9 +387,7 @@ struct gpio_led_platform_data {
|
|||||||
#define GPIO_LED_NO_BLINK_LOW 0 /* No blink GPIO state low */
|
#define GPIO_LED_NO_BLINK_LOW 0 /* No blink GPIO state low */
|
||||||
#define GPIO_LED_NO_BLINK_HIGH 1 /* No blink GPIO state high */
|
#define GPIO_LED_NO_BLINK_HIGH 1 /* No blink GPIO state high */
|
||||||
#define GPIO_LED_BLINK 2 /* Please, blink */
|
#define GPIO_LED_BLINK 2 /* Please, blink */
|
||||||
int (*gpio_blink_set)(struct gpio_desc *desc, int state,
|
gpio_blink_set_t gpio_blink_set;
|
||||||
unsigned long *delay_on,
|
|
||||||
unsigned long *delay_off);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_NEW_LEDS
|
#ifdef CONFIG_NEW_LEDS
|
||||||
|
|||||||
Reference in New Issue
Block a user