diff options
author | Vadim Pasternak <vadimp@mellanox.com> | 2016-09-08 03:25:53 -0400 |
---|---|---|
committer | Jacek Anaszewski <j.anaszewski@samsung.com> | 2016-09-15 10:49:39 -0400 |
commit | be4fdf99fa4dc95aa01144b207caf2cc9fa074d8 (patch) | |
tree | 3f976a3e9ad156a779a4fdd839a270d85348f0f2 | |
parent | 1f70cb4045cba47287fed3ba98dadc7187f13ef8 (diff) |
leds: add driver for Mellanox systems LEDs
This makes it possible to create a set of LEDs for Mellanox systems:
"msx6710", "msx6720", "msb7700", "msn2700", "msx1410", "msn2410",
"msb7800", "msn2740", "msn2100".
Driver obtains LED devices according to system configuration, provided
through system DMI data, like mlxcpld:fan1:green, mlxcpld:fan1:red and
creates devices in form: "devicename:colour:function".
LED setting is controlled through on board CPLD Lattice device.
For setting particular LED off, solid, blink:
echo 0 > /sys/class/leds/mlxcpld\:status\:green/brightness
echo 1 > /sys/class/leds/mlxcpld\:status\:green/brightness
echo timer > /sys/class/leds/mlxcpld\:status\:green/trigger
On module probing all LEDs are set green, on removing - off.
Last setting overwrites previous, f.e. sequence for
changing LED from green - red - green:
echo 1 > /sys/class/leds/mlxcpld\:psu\:green/brightness
echo 1 > /sys/class/leds/mlxcpld\:psu\:red/brightness
echo 1 > /sys/class/leds/mlxcpld\:psu\:green/brightness
Note: LEDs cannot be turned on/off simultaneously.
The Kconfig currently controlling compilation of this code is:
drivers/leds/Kconfig:config LEDS_MLXCPLD
Signed-off-by: Vadim Pasternak <vadimp@mellanox.com>
Reviewed-by: Jiri Pirko <jiri@mellanox.com>
Reviewed-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
-rw-r--r-- | Documentation/leds/leds-mlxcpld.txt | 110 | ||||
-rw-r--r-- | MAINTAINERS | 7 | ||||
-rw-r--r-- | drivers/leds/Kconfig | 8 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/leds-mlxcpld.c | 430 |
5 files changed, 556 insertions, 0 deletions
diff --git a/Documentation/leds/leds-mlxcpld.txt b/Documentation/leds/leds-mlxcpld.txt new file mode 100644 index 000000000000..a0e8fd457117 --- /dev/null +++ b/Documentation/leds/leds-mlxcpld.txt | |||
@@ -0,0 +1,110 @@ | |||
1 | Kernel driver for Mellanox systems LEDs | ||
2 | ======================================= | ||
3 | |||
4 | Provide system LED support for the nex Mellanox systems: | ||
5 | "msx6710", "msx6720", "msb7700", "msn2700", "msx1410", | ||
6 | "msn2410", "msb7800", "msn2740", "msn2100". | ||
7 | |||
8 | Description | ||
9 | ----------- | ||
10 | Driver provides the following LEDs for the systems "msx6710", "msx6720", | ||
11 | "msb7700", "msn2700", "msx1410", "msn2410", "msb7800", "msn2740": | ||
12 | mlxcpld:fan1:green | ||
13 | mlxcpld:fan1:red | ||
14 | mlxcpld:fan2:green | ||
15 | mlxcpld:fan2:red | ||
16 | mlxcpld:fan3:green | ||
17 | mlxcpld:fan3:red | ||
18 | mlxcpld:fan4:green | ||
19 | mlxcpld:fan4:red | ||
20 | mlxcpld:psu:green | ||
21 | mlxcpld:psu:red | ||
22 | mlxcpld:status:green | ||
23 | mlxcpld:status:red | ||
24 | |||
25 | "status" | ||
26 | CPLD reg offset: 0x20 | ||
27 | Bits [3:0] | ||
28 | |||
29 | "psu" | ||
30 | CPLD reg offset: 0x20 | ||
31 | Bits [7:4] | ||
32 | |||
33 | "fan1" | ||
34 | CPLD reg offset: 0x21 | ||
35 | Bits [3:0] | ||
36 | |||
37 | "fan2" | ||
38 | CPLD reg offset: 0x21 | ||
39 | Bits [7:4] | ||
40 | |||
41 | "fan3" | ||
42 | CPLD reg offset: 0x22 | ||
43 | Bits [3:0] | ||
44 | |||
45 | "fan4" | ||
46 | CPLD reg offset: 0x22 | ||
47 | Bits [7:4] | ||
48 | |||
49 | Color mask for all the above LEDs: | ||
50 | [bit3,bit2,bit1,bit0] or | ||
51 | [bit7,bit6,bit5,bit4]: | ||
52 | [0,0,0,0] = LED OFF | ||
53 | [0,1,0,1] = Red static ON | ||
54 | [1,1,0,1] = Green static ON | ||
55 | [0,1,1,0] = Red blink 3Hz | ||
56 | [1,1,1,0] = Green blink 3Hz | ||
57 | [0,1,1,1] = Red blink 6Hz | ||
58 | [1,1,1,1] = Green blink 6Hz | ||
59 | |||
60 | Driver provides the following LEDs for the system "msn2100": | ||
61 | mlxcpld:fan:green | ||
62 | mlxcpld:fan:red | ||
63 | mlxcpld:psu1:green | ||
64 | mlxcpld:psu1:red | ||
65 | mlxcpld:psu2:green | ||
66 | mlxcpld:psu2:red | ||
67 | mlxcpld:status:green | ||
68 | mlxcpld:status:red | ||
69 | mlxcpld:uid:blue | ||
70 | |||
71 | "status" | ||
72 | CPLD reg offset: 0x20 | ||
73 | Bits [3:0] | ||
74 | |||
75 | "fan" | ||
76 | CPLD reg offset: 0x21 | ||
77 | Bits [3:0] | ||
78 | |||
79 | "psu1" | ||
80 | CPLD reg offset: 0x23 | ||
81 | Bits [3:0] | ||
82 | |||
83 | "psu2" | ||
84 | CPLD reg offset: 0x23 | ||
85 | Bits [7:4] | ||
86 | |||
87 | "uid" | ||
88 | CPLD reg offset: 0x24 | ||
89 | Bits [3:0] | ||
90 | |||
91 | Color mask for all the above LEDs, excepted uid: | ||
92 | [bit3,bit2,bit1,bit0] or | ||
93 | [bit7,bit6,bit5,bit4]: | ||
94 | [0,0,0,0] = LED OFF | ||
95 | [0,1,0,1] = Red static ON | ||
96 | [1,1,0,1] = Green static ON | ||
97 | [0,1,1,0] = Red blink 3Hz | ||
98 | [1,1,1,0] = Green blink 3Hz | ||
99 | [0,1,1,1] = Red blink 6Hz | ||
100 | [1,1,1,1] = Green blink 6Hz | ||
101 | |||
102 | Color mask for uid LED: | ||
103 | [bit3,bit2,bit1,bit0]: | ||
104 | [0,0,0,0] = LED OFF | ||
105 | [1,1,0,1] = Blue static ON | ||
106 | [1,1,1,0] = Blue blink 3Hz | ||
107 | [1,1,1,1] = Blue blink 6Hz | ||
108 | |||
109 | Driver supports HW blinking at 3Hz and 6Hz frequency (50% duty cycle). | ||
110 | For 3Hz duty cylce is about 167 msec, for 6Hz is about 83 msec. | ||
diff --git a/MAINTAINERS b/MAINTAINERS index a306795a7450..f742e88ccf21 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -7649,6 +7649,13 @@ W: http://www.mellanox.com | |||
7649 | Q: http://patchwork.ozlabs.org/project/netdev/list/ | 7649 | Q: http://patchwork.ozlabs.org/project/netdev/list/ |
7650 | F: drivers/net/ethernet/mellanox/mlxsw/ | 7650 | F: drivers/net/ethernet/mellanox/mlxsw/ |
7651 | 7651 | ||
7652 | MELLANOX MLXCPLD LED DRIVER | ||
7653 | M: Vadim Pasternak <vadimp@mellanox.com> | ||
7654 | L: linux-leds@vger.kernel.org | ||
7655 | S: Supported | ||
7656 | F: drivers/leds/leds-mlxcpld.c | ||
7657 | F: Documentation/leds/leds-mlxcpld.txt | ||
7658 | |||
7652 | SOFT-ROCE DRIVER (rxe) | 7659 | SOFT-ROCE DRIVER (rxe) |
7653 | M: Moni Shoua <monis@mellanox.com> | 7660 | M: Moni Shoua <monis@mellanox.com> |
7654 | L: linux-rdma@vger.kernel.org | 7661 | L: linux-rdma@vger.kernel.org |
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 969c720dfd10..7a628c6516f6 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -651,6 +651,14 @@ config LEDS_PM8058 | |||
651 | Choose this option if you want to use the LED drivers in | 651 | Choose this option if you want to use the LED drivers in |
652 | the Qualcomm PM8058 PMIC. | 652 | the Qualcomm PM8058 PMIC. |
653 | 653 | ||
654 | config LEDS_MLXCPLD | ||
655 | tristate "LED support for the Mellanox boards" | ||
656 | depends on X86_64 && DMI | ||
657 | depends on LEDS_CLASS | ||
658 | help | ||
659 | This option enabled support for the LEDs on the Mellanox | ||
660 | boards. Say Y to enabled these. | ||
661 | |||
654 | comment "LED Triggers" | 662 | comment "LED Triggers" |
655 | source "drivers/leds/trigger/Kconfig" | 663 | source "drivers/leds/trigger/Kconfig" |
656 | 664 | ||
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 06a27ef17554..3965070190f5 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -70,6 +70,7 @@ obj-$(CONFIG_LEDS_SEAD3) += leds-sead3.o | |||
70 | obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o | 70 | obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o |
71 | obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o | 71 | obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o |
72 | obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o | 72 | obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o |
73 | obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o | ||
73 | 74 | ||
74 | # LED SPI Drivers | 75 | # LED SPI Drivers |
75 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o | 76 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o |
diff --git a/drivers/leds/leds-mlxcpld.c b/drivers/leds/leds-mlxcpld.c new file mode 100644 index 000000000000..197ab9b29a9c --- /dev/null +++ b/drivers/leds/leds-mlxcpld.c | |||
@@ -0,0 +1,430 @@ | |||
1 | /* | ||
2 | * drivers/leds/leds-mlxcpld.c | ||
3 | * Copyright (c) 2016 Mellanox Technologies. All rights reserved. | ||
4 | * Copyright (c) 2016 Vadim Pasternak <vadimp@mellanox.com> | ||
5 | * | ||
6 | * Redistribution and use in source and binary forms, with or without | ||
7 | * modification, are permitted provided that the following conditions are met: | ||
8 | * | ||
9 | * 1. Redistributions of source code must retain the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer. | ||
11 | * 2. Redistributions in binary form must reproduce the above copyright | ||
12 | * notice, this list of conditions and the following disclaimer in the | ||
13 | * documentation and/or other materials provided with the distribution. | ||
14 | * 3. Neither the names of the copyright holders nor the names of its | ||
15 | * contributors may be used to endorse or promote products derived from | ||
16 | * this software without specific prior written permission. | ||
17 | * | ||
18 | * Alternatively, this software may be distributed under the terms of the | ||
19 | * GNU General Public License ("GPL") version 2 as published by the Free | ||
20 | * Software Foundation. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||
26 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
32 | * POSSIBILITY OF SUCH DAMAGE. | ||
33 | */ | ||
34 | |||
35 | #include <linux/acpi.h> | ||
36 | #include <linux/device.h> | ||
37 | #include <linux/dmi.h> | ||
38 | #include <linux/hwmon.h> | ||
39 | #include <linux/hwmon-sysfs.h> | ||
40 | #include <linux/io.h> | ||
41 | #include <linux/leds.h> | ||
42 | #include <linux/module.h> | ||
43 | #include <linux/mod_devicetable.h> | ||
44 | #include <linux/platform_device.h> | ||
45 | #include <linux/slab.h> | ||
46 | |||
47 | #define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500 /* LPC bus access */ | ||
48 | |||
49 | /* Color codes for LEDs */ | ||
50 | #define MLXCPLD_LED_OFFSET_HALF 0x01 /* Offset from solid: 3Hz blink */ | ||
51 | #define MLXCPLD_LED_OFFSET_FULL 0x02 /* Offset from solid: 6Hz blink */ | ||
52 | #define MLXCPLD_LED_IS_OFF 0x00 /* Off */ | ||
53 | #define MLXCPLD_LED_RED_STATIC_ON 0x05 /* Solid red */ | ||
54 | #define MLXCPLD_LED_RED_BLINK_HALF (MLXCPLD_LED_RED_STATIC_ON + \ | ||
55 | MLXCPLD_LED_OFFSET_HALF) | ||
56 | #define MLXCPLD_LED_RED_BLINK_FULL (MLXCPLD_LED_RED_STATIC_ON + \ | ||
57 | MLXCPLD_LED_OFFSET_FULL) | ||
58 | #define MLXCPLD_LED_GREEN_STATIC_ON 0x0D /* Solid green */ | ||
59 | #define MLXCPLD_LED_GREEN_BLINK_HALF (MLXCPLD_LED_GREEN_STATIC_ON + \ | ||
60 | MLXCPLD_LED_OFFSET_HALF) | ||
61 | #define MLXCPLD_LED_GREEN_BLINK_FULL (MLXCPLD_LED_GREEN_STATIC_ON + \ | ||
62 | MLXCPLD_LED_OFFSET_FULL) | ||
63 | #define MLXCPLD_LED_BLINK_3HZ 167 /* ~167 msec off/on */ | ||
64 | #define MLXCPLD_LED_BLINK_6HZ 83 /* ~83 msec off/on */ | ||
65 | |||
66 | /** | ||
67 | * mlxcpld_param - LED access parameters: | ||
68 | * @offset - offset for LED access in CPLD device | ||
69 | * @mask - mask for LED access in CPLD device | ||
70 | * @base_color - base color code for LED | ||
71 | **/ | ||
72 | struct mlxcpld_param { | ||
73 | u8 offset; | ||
74 | u8 mask; | ||
75 | u8 base_color; | ||
76 | }; | ||
77 | |||
78 | /** | ||
79 | * mlxcpld_led_priv - LED private data: | ||
80 | * @cled - LED class device instance | ||
81 | * @param - LED CPLD access parameters | ||
82 | **/ | ||
83 | struct mlxcpld_led_priv { | ||
84 | struct led_classdev cdev; | ||
85 | struct mlxcpld_param param; | ||
86 | }; | ||
87 | |||
88 | #define cdev_to_priv(c) container_of(c, struct mlxcpld_led_priv, cdev) | ||
89 | |||
90 | /** | ||
91 | * mlxcpld_led_profile - system LED profile (defined per system class): | ||
92 | * @offset - offset for LED access in CPLD device | ||
93 | * @mask - mask for LED access in CPLD device | ||
94 | * @base_color - base color code | ||
95 | * @brightness - default brightness setting (on/off) | ||
96 | * @name - LED name | ||
97 | **/ | ||
98 | struct mlxcpld_led_profile { | ||
99 | u8 offset; | ||
100 | u8 mask; | ||
101 | u8 base_color; | ||
102 | enum led_brightness brightness; | ||
103 | const char *name; | ||
104 | }; | ||
105 | |||
106 | /** | ||
107 | * mlxcpld_led_pdata - system LED private data | ||
108 | * @pdev - platform device pointer | ||
109 | * @pled - LED class device instance | ||
110 | * @profile - system configuration profile | ||
111 | * @num_led_instances - number of LED instances | ||
112 | * @lock - device access lock | ||
113 | **/ | ||
114 | struct mlxcpld_led_pdata { | ||
115 | struct platform_device *pdev; | ||
116 | struct mlxcpld_led_priv *pled; | ||
117 | struct mlxcpld_led_profile *profile; | ||
118 | int num_led_instances; | ||
119 | spinlock_t lock; | ||
120 | }; | ||
121 | |||
122 | static struct mlxcpld_led_pdata *mlxcpld_led; | ||
123 | |||
124 | /* Default profile fit the next Mellanox systems: | ||
125 | * "msx6710", "msx6720", "msb7700", "msn2700", "msx1410", | ||
126 | * "msn2410", "msb7800", "msn2740" | ||
127 | */ | ||
128 | static struct mlxcpld_led_profile mlxcpld_led_default_profile[] = { | ||
129 | { | ||
130 | 0x21, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1, | ||
131 | "mlxcpld:fan1:green", | ||
132 | }, | ||
133 | { | ||
134 | 0x21, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, | ||
135 | "mlxcpld:fan1:red", | ||
136 | }, | ||
137 | { | ||
138 | 0x21, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1, | ||
139 | "mlxcpld:fan2:green", | ||
140 | }, | ||
141 | { | ||
142 | 0x21, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, | ||
143 | "mlxcpld:fan2:red", | ||
144 | }, | ||
145 | { | ||
146 | 0x22, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1, | ||
147 | "mlxcpld:fan3:green", | ||
148 | }, | ||
149 | { | ||
150 | 0x22, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, | ||
151 | "mlxcpld:fan3:red", | ||
152 | }, | ||
153 | { | ||
154 | 0x22, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1, | ||
155 | "mlxcpld:fan4:green", | ||
156 | }, | ||
157 | { | ||
158 | 0x22, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, | ||
159 | "mlxcpld:fan4:red", | ||
160 | }, | ||
161 | { | ||
162 | 0x20, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1, | ||
163 | "mlxcpld:psu:green", | ||
164 | }, | ||
165 | { | ||
166 | 0x20, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, | ||
167 | "mlxcpld:psu:red", | ||
168 | }, | ||
169 | { | ||
170 | 0x20, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1, | ||
171 | "mlxcpld:status:green", | ||
172 | }, | ||
173 | { | ||
174 | 0x20, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, | ||
175 | "mlxcpld:status:red", | ||
176 | }, | ||
177 | }; | ||
178 | |||
179 | /* Profile fit the Mellanox systems based on "msn2100" */ | ||
180 | static struct mlxcpld_led_profile mlxcpld_led_msn2100_profile[] = { | ||
181 | { | ||
182 | 0x21, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1, | ||
183 | "mlxcpld:fan:green", | ||
184 | }, | ||
185 | { | ||
186 | 0x21, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, | ||
187 | "mlxcpld:fan:red", | ||
188 | }, | ||
189 | { | ||
190 | 0x23, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1, | ||
191 | "mlxcpld:psu1:green", | ||
192 | }, | ||
193 | { | ||
194 | 0x23, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, | ||
195 | "mlxcpld:psu1:red", | ||
196 | }, | ||
197 | { | ||
198 | 0x23, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1, | ||
199 | "mlxcpld:psu2:green", | ||
200 | }, | ||
201 | { | ||
202 | 0x23, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, | ||
203 | "mlxcpld:psu2:red", | ||
204 | }, | ||
205 | { | ||
206 | 0x20, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1, | ||
207 | "mlxcpld:status:green", | ||
208 | }, | ||
209 | { | ||
210 | 0x20, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, | ||
211 | "mlxcpld:status:red", | ||
212 | }, | ||
213 | { | ||
214 | 0x24, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, LED_OFF, | ||
215 | "mlxcpld:uid:blue", | ||
216 | }, | ||
217 | }; | ||
218 | |||
219 | enum mlxcpld_led_platform_types { | ||
220 | MLXCPLD_LED_PLATFORM_DEFAULT, | ||
221 | MLXCPLD_LED_PLATFORM_MSN2100, | ||
222 | }; | ||
223 | |||
224 | static const char *mlx_product_names[] = { | ||
225 | "DEFAULT", | ||
226 | "MSN2100", | ||
227 | }; | ||
228 | |||
229 | static enum | ||
230 | mlxcpld_led_platform_types mlxcpld_led_platform_check_sys_type(void) | ||
231 | { | ||
232 | const char *mlx_product_name; | ||
233 | int i; | ||
234 | |||
235 | mlx_product_name = dmi_get_system_info(DMI_PRODUCT_NAME); | ||
236 | if (!mlx_product_name) | ||
237 | return MLXCPLD_LED_PLATFORM_DEFAULT; | ||
238 | |||
239 | for (i = 1; i < ARRAY_SIZE(mlx_product_names); i++) { | ||
240 | if (strstr(mlx_product_name, mlx_product_names[i])) | ||
241 | return i; | ||
242 | } | ||
243 | |||
244 | return MLXCPLD_LED_PLATFORM_DEFAULT; | ||
245 | } | ||
246 | |||
247 | static void mlxcpld_led_bus_access_func(u16 base, u8 offset, u8 rw_flag, | ||
248 | u8 *data) | ||
249 | { | ||
250 | u32 addr = base + offset; | ||
251 | |||
252 | if (rw_flag == 0) | ||
253 | outb(*data, addr); | ||
254 | else | ||
255 | *data = inb(addr); | ||
256 | } | ||
257 | |||
258 | static void mlxcpld_led_store_hw(u8 mask, u8 off, u8 vset) | ||
259 | { | ||
260 | u8 nib, val; | ||
261 | |||
262 | /* | ||
263 | * Each LED is controlled through low or high nibble of the relevant | ||
264 | * CPLD register. Register offset is specified by off parameter. | ||
265 | * Parameter vset provides color code: 0x0 for off, 0x5 for solid red, | ||
266 | * 0x6 for 3Hz blink red, 0xd for solid green, 0xe for 3Hz blink | ||
267 | * green. | ||
268 | * Parameter mask specifies which nibble is used for specific LED: mask | ||
269 | * 0xf0 - lower nibble is to be used (bits from 0 to 3), mask 0x0f - | ||
270 | * higher nibble (bits from 4 to 7). | ||
271 | */ | ||
272 | spin_lock(&mlxcpld_led->lock); | ||
273 | mlxcpld_led_bus_access_func(MLXPLAT_CPLD_LPC_REG_BASE_ADRR, off, 1, | ||
274 | &val); | ||
275 | nib = (mask == 0xf0) ? vset : (vset << 4); | ||
276 | val = (val & mask) | nib; | ||
277 | mlxcpld_led_bus_access_func(MLXPLAT_CPLD_LPC_REG_BASE_ADRR, off, 0, | ||
278 | &val); | ||
279 | spin_unlock(&mlxcpld_led->lock); | ||
280 | } | ||
281 | |||
282 | static void mlxcpld_led_brightness_set(struct led_classdev *led, | ||
283 | enum led_brightness value) | ||
284 | { | ||
285 | struct mlxcpld_led_priv *pled = cdev_to_priv(led); | ||
286 | |||
287 | if (value) { | ||
288 | mlxcpld_led_store_hw(pled->param.mask, pled->param.offset, | ||
289 | pled->param.base_color); | ||
290 | return; | ||
291 | } | ||
292 | |||
293 | mlxcpld_led_store_hw(pled->param.mask, pled->param.offset, | ||
294 | MLXCPLD_LED_IS_OFF); | ||
295 | } | ||
296 | |||
297 | static int mlxcpld_led_blink_set(struct led_classdev *led, | ||
298 | unsigned long *delay_on, | ||
299 | unsigned long *delay_off) | ||
300 | { | ||
301 | struct mlxcpld_led_priv *pled = cdev_to_priv(led); | ||
302 | |||
303 | /* | ||
304 | * HW supports two types of blinking: full (6Hz) and half (3Hz). | ||
305 | * For delay on/off zero default setting 3Hz is used. | ||
306 | */ | ||
307 | if (!(*delay_on == 0 && *delay_off == 0) && | ||
308 | !(*delay_on == MLXCPLD_LED_BLINK_3HZ && | ||
309 | *delay_off == MLXCPLD_LED_BLINK_3HZ) && | ||
310 | !(*delay_on == MLXCPLD_LED_BLINK_6HZ && | ||
311 | *delay_off == MLXCPLD_LED_BLINK_6HZ)) | ||
312 | return -EINVAL; | ||
313 | |||
314 | if (*delay_on == MLXCPLD_LED_BLINK_6HZ) | ||
315 | mlxcpld_led_store_hw(pled->param.mask, pled->param.offset, | ||
316 | pled->param.base_color + | ||
317 | MLXCPLD_LED_OFFSET_FULL); | ||
318 | else | ||
319 | mlxcpld_led_store_hw(pled->param.mask, pled->param.offset, | ||
320 | pled->param.base_color + | ||
321 | MLXCPLD_LED_OFFSET_HALF); | ||
322 | |||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | static int mlxcpld_led_config(struct device *dev, | ||
327 | struct mlxcpld_led_pdata *cpld) | ||
328 | { | ||
329 | int i; | ||
330 | int err; | ||
331 | |||
332 | cpld->pled = devm_kzalloc(dev, sizeof(struct mlxcpld_led_priv) * | ||
333 | cpld->num_led_instances, GFP_KERNEL); | ||
334 | if (!cpld->pled) | ||
335 | return -ENOMEM; | ||
336 | |||
337 | for (i = 0; i < cpld->num_led_instances; i++) { | ||
338 | cpld->pled[i].cdev.name = cpld->profile[i].name; | ||
339 | cpld->pled[i].cdev.brightness = cpld->profile[i].brightness; | ||
340 | cpld->pled[i].cdev.max_brightness = 1; | ||
341 | cpld->pled[i].cdev.brightness_set = mlxcpld_led_brightness_set; | ||
342 | cpld->pled[i].cdev.blink_set = mlxcpld_led_blink_set; | ||
343 | cpld->pled[i].cdev.flags = LED_CORE_SUSPENDRESUME; | ||
344 | err = devm_led_classdev_register(dev, &cpld->pled[i].cdev); | ||
345 | if (err) | ||
346 | return err; | ||
347 | |||
348 | cpld->pled[i].param.offset = mlxcpld_led->profile[i].offset; | ||
349 | cpld->pled[i].param.mask = mlxcpld_led->profile[i].mask; | ||
350 | cpld->pled[i].param.base_color = | ||
351 | mlxcpld_led->profile[i].base_color; | ||
352 | |||
353 | if (mlxcpld_led->profile[i].brightness) | ||
354 | mlxcpld_led_brightness_set(&cpld->pled[i].cdev, | ||
355 | mlxcpld_led->profile[i].brightness); | ||
356 | } | ||
357 | |||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | static int __init mlxcpld_led_probe(struct platform_device *pdev) | ||
362 | { | ||
363 | enum mlxcpld_led_platform_types mlxcpld_led_plat = | ||
364 | mlxcpld_led_platform_check_sys_type(); | ||
365 | |||
366 | mlxcpld_led = devm_kzalloc(&pdev->dev, sizeof(*mlxcpld_led), | ||
367 | GFP_KERNEL); | ||
368 | if (!mlxcpld_led) | ||
369 | return -ENOMEM; | ||
370 | |||
371 | mlxcpld_led->pdev = pdev; | ||
372 | |||
373 | switch (mlxcpld_led_plat) { | ||
374 | case MLXCPLD_LED_PLATFORM_MSN2100: | ||
375 | mlxcpld_led->profile = mlxcpld_led_msn2100_profile; | ||
376 | mlxcpld_led->num_led_instances = | ||
377 | ARRAY_SIZE(mlxcpld_led_msn2100_profile); | ||
378 | break; | ||
379 | |||
380 | default: | ||
381 | mlxcpld_led->profile = mlxcpld_led_default_profile; | ||
382 | mlxcpld_led->num_led_instances = | ||
383 | ARRAY_SIZE(mlxcpld_led_default_profile); | ||
384 | break; | ||
385 | } | ||
386 | |||
387 | spin_lock_init(&mlxcpld_led->lock); | ||
388 | |||
389 | return mlxcpld_led_config(&pdev->dev, mlxcpld_led); | ||
390 | } | ||
391 | |||
392 | static struct platform_driver mlxcpld_led_driver = { | ||
393 | .driver = { | ||
394 | .name = KBUILD_MODNAME, | ||
395 | }, | ||
396 | }; | ||
397 | |||
398 | static int __init mlxcpld_led_init(void) | ||
399 | { | ||
400 | struct platform_device *pdev; | ||
401 | int err; | ||
402 | |||
403 | pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); | ||
404 | if (IS_ERR(pdev)) { | ||
405 | pr_err("Device allocation failed\n"); | ||
406 | return PTR_ERR(pdev); | ||
407 | } | ||
408 | |||
409 | err = platform_driver_probe(&mlxcpld_led_driver, mlxcpld_led_probe); | ||
410 | if (err) { | ||
411 | pr_err("Probe platform driver failed\n"); | ||
412 | platform_device_unregister(pdev); | ||
413 | } | ||
414 | |||
415 | return err; | ||
416 | } | ||
417 | |||
418 | static void __exit mlxcpld_led_exit(void) | ||
419 | { | ||
420 | platform_device_unregister(mlxcpld_led->pdev); | ||
421 | platform_driver_unregister(&mlxcpld_led_driver); | ||
422 | } | ||
423 | |||
424 | module_init(mlxcpld_led_init); | ||
425 | module_exit(mlxcpld_led_exit); | ||
426 | |||
427 | MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); | ||
428 | MODULE_DESCRIPTION("Mellanox board LED driver"); | ||
429 | MODULE_LICENSE("GPL v2"); | ||
430 | MODULE_ALIAS("platform:leds_mlxcpld"); | ||