diff options
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/Kconfig | 9 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/leds-ss4200.c | 552 |
3 files changed, 562 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index e4f599f20e38..e481d3c52a54 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -236,6 +236,15 @@ config LEDS_BD2802 | |||
236 | This option enables support for BD2802GU RGB LED driver chips | 236 | This option enables support for BD2802GU RGB LED driver chips |
237 | accessed via the I2C bus. | 237 | accessed via the I2C bus. |
238 | 238 | ||
239 | config LEDS_INTEL_SS4200 | ||
240 | tristate "LED driver for Intel NAS SS4200 series" | ||
241 | depends on LEDS_CLASS && PCI && DMI | ||
242 | help | ||
243 | This option enables support for the Intel SS4200 series of | ||
244 | Network Attached Storage servers. You may control the hard | ||
245 | drive or power LEDs on the front panel. Using this driver | ||
246 | can stop the front LED from blinking after startup. | ||
247 | |||
239 | comment "LED Triggers" | 248 | comment "LED Triggers" |
240 | 249 | ||
241 | config LEDS_TRIGGERS | 250 | config LEDS_TRIGGERS |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 46d72704d606..12b7a20fcfe0 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -29,6 +29,7 @@ obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o | |||
29 | obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o | 29 | obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o |
30 | obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o | 30 | obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o |
31 | obj-$(CONFIG_LEDS_PWM) += leds-pwm.o | 31 | obj-$(CONFIG_LEDS_PWM) += leds-pwm.o |
32 | obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o | ||
32 | 33 | ||
33 | # LED SPI Drivers | 34 | # LED SPI Drivers |
34 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o | 35 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o |
diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c new file mode 100644 index 000000000000..0ec4ec3bc823 --- /dev/null +++ b/drivers/leds/leds-ss4200.c | |||
@@ -0,0 +1,552 @@ | |||
1 | /* | ||
2 | * SS4200-E Hardware API | ||
3 | * Copyright (c) 2009, Intel Corporation. | ||
4 | * Copyright IBM Corporation, 2009 | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along with | ||
16 | * this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | ||
19 | * Author: Dave Hansen <dave@sr71.net> | ||
20 | */ | ||
21 | |||
22 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
23 | |||
24 | #include <linux/dmi.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/ioport.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/leds.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/pci.h> | ||
31 | #include <linux/types.h> | ||
32 | #include <linux/uaccess.h> | ||
33 | |||
34 | MODULE_AUTHOR("Rodney Girod <rgirod@confocus.com>, Dave Hansen <dave@sr71.net>"); | ||
35 | MODULE_DESCRIPTION("Intel NAS/Home Server ICH7 GPIO Driver"); | ||
36 | MODULE_LICENSE("GPL"); | ||
37 | |||
38 | /* | ||
39 | * ICH7 LPC/GPIO PCI Config register offsets | ||
40 | */ | ||
41 | #define PMBASE 0x040 | ||
42 | #define GPIO_BASE 0x048 | ||
43 | #define GPIO_CTRL 0x04c | ||
44 | #define GPIO_EN 0x010 | ||
45 | |||
46 | /* | ||
47 | * The ICH7 GPIO register block is 64 bytes in size. | ||
48 | */ | ||
49 | #define ICH7_GPIO_SIZE 64 | ||
50 | |||
51 | /* | ||
52 | * Define register offsets within the ICH7 register block. | ||
53 | */ | ||
54 | #define GPIO_USE_SEL 0x000 | ||
55 | #define GP_IO_SEL 0x004 | ||
56 | #define GP_LVL 0x00c | ||
57 | #define GPO_BLINK 0x018 | ||
58 | #define GPI_INV 0x030 | ||
59 | #define GPIO_USE_SEL2 0x034 | ||
60 | #define GP_IO_SEL2 0x038 | ||
61 | #define GP_LVL2 0x03c | ||
62 | |||
63 | /* | ||
64 | * PCI ID of the Intel ICH7 LPC Device within which the GPIO block lives. | ||
65 | */ | ||
66 | static struct pci_device_id ich7_lpc_pci_id[] = | ||
67 | { | ||
68 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0) }, | ||
69 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1) }, | ||
70 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_30) }, | ||
71 | { } /* NULL entry */ | ||
72 | }; | ||
73 | |||
74 | MODULE_DEVICE_TABLE(pci, ich7_lpc_pci_id); | ||
75 | |||
76 | static int __init ss4200_led_dmi_callback(const struct dmi_system_id *id) | ||
77 | { | ||
78 | pr_info("detected '%s'\n", id->ident); | ||
79 | return 1; | ||
80 | } | ||
81 | |||
82 | static unsigned int __initdata nodetect; | ||
83 | module_param_named(nodetect, nodetect, bool, 0); | ||
84 | MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection"); | ||
85 | |||
86 | /* | ||
87 | * struct nas_led_whitelist - List of known good models | ||
88 | * | ||
89 | * Contains the known good models this driver is compatible with. | ||
90 | * When adding a new model try to be as strict as possible. This | ||
91 | * makes it possible to keep the false positives (the model is | ||
92 | * detected as working, but in reality it is not) as low as | ||
93 | * possible. | ||
94 | */ | ||
95 | static struct dmi_system_id __initdata nas_led_whitelist[] = { | ||
96 | { | ||
97 | .callback = ss4200_led_dmi_callback, | ||
98 | .ident = "Intel SS4200-E", | ||
99 | .matches = { | ||
100 | DMI_MATCH(DMI_SYS_VENDOR, "Intel"), | ||
101 | DMI_MATCH(DMI_PRODUCT_NAME, "SS4200-E"), | ||
102 | DMI_MATCH(DMI_PRODUCT_VERSION, "1.00.00") | ||
103 | } | ||
104 | }, | ||
105 | }; | ||
106 | |||
107 | /* | ||
108 | * Base I/O address assigned to the Power Management register block | ||
109 | */ | ||
110 | static u32 g_pm_io_base; | ||
111 | |||
112 | /* | ||
113 | * Base I/O address assigned to the ICH7 GPIO register block | ||
114 | */ | ||
115 | static u32 nas_gpio_io_base; | ||
116 | |||
117 | /* | ||
118 | * When we successfully register a region, we are returned a resource. | ||
119 | * We use these to identify which regions we need to release on our way | ||
120 | * back out. | ||
121 | */ | ||
122 | static struct resource *gp_gpio_resource; | ||
123 | |||
124 | struct nasgpio_led { | ||
125 | char *name; | ||
126 | u32 gpio_bit; | ||
127 | struct led_classdev led_cdev; | ||
128 | }; | ||
129 | |||
130 | /* | ||
131 | * gpio_bit(s) are the ICH7 GPIO bit assignments | ||
132 | */ | ||
133 | static struct nasgpio_led nasgpio_leds[] = { | ||
134 | { .name = "hdd1:blue:sata", .gpio_bit = 0 }, | ||
135 | { .name = "hdd1:amber:sata", .gpio_bit = 1 }, | ||
136 | { .name = "hdd2:blue:sata", .gpio_bit = 2 }, | ||
137 | { .name = "hdd2:amber:sata", .gpio_bit = 3 }, | ||
138 | { .name = "hdd3:blue:sata", .gpio_bit = 4 }, | ||
139 | { .name = "hdd3:amber:sata", .gpio_bit = 5 }, | ||
140 | { .name = "hdd4:blue:sata", .gpio_bit = 6 }, | ||
141 | { .name = "hdd4:amber:sata", .gpio_bit = 7 }, | ||
142 | { .name = "power:blue:power", .gpio_bit = 27}, | ||
143 | { .name = "power:amber:power", .gpio_bit = 28}, | ||
144 | }; | ||
145 | |||
146 | #define NAS_RECOVERY 0x00000400 /* GPIO10 */ | ||
147 | |||
148 | static struct nasgpio_led * | ||
149 | led_classdev_to_nasgpio_led(struct led_classdev *led_cdev) | ||
150 | { | ||
151 | return container_of(led_cdev, struct nasgpio_led, led_cdev); | ||
152 | } | ||
153 | |||
154 | static struct nasgpio_led *get_led_named(char *name) | ||
155 | { | ||
156 | int i; | ||
157 | for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) { | ||
158 | if (strcmp(nasgpio_leds[i].name, name)) | ||
159 | continue; | ||
160 | return &nasgpio_leds[i]; | ||
161 | } | ||
162 | return NULL; | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * This protects access to the gpio ports. | ||
167 | */ | ||
168 | static DEFINE_SPINLOCK(nasgpio_gpio_lock); | ||
169 | |||
170 | /* | ||
171 | * There are two gpio ports, one for blinking and the other | ||
172 | * for power. @port tells us if we're doing blinking or | ||
173 | * power control. | ||
174 | * | ||
175 | * Caller must hold nasgpio_gpio_lock | ||
176 | */ | ||
177 | static void __nasgpio_led_set_attr(struct led_classdev *led_cdev, | ||
178 | u32 port, u32 value) | ||
179 | { | ||
180 | struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev); | ||
181 | u32 gpio_out; | ||
182 | |||
183 | gpio_out = inl(nas_gpio_io_base + port); | ||
184 | if (value) | ||
185 | gpio_out |= (1<<led->gpio_bit); | ||
186 | else | ||
187 | gpio_out &= ~(1<<led->gpio_bit); | ||
188 | |||
189 | outl(gpio_out, nas_gpio_io_base + port); | ||
190 | } | ||
191 | |||
192 | static void nasgpio_led_set_attr(struct led_classdev *led_cdev, | ||
193 | u32 port, u32 value) | ||
194 | { | ||
195 | spin_lock(&nasgpio_gpio_lock); | ||
196 | __nasgpio_led_set_attr(led_cdev, port, value); | ||
197 | spin_unlock(&nasgpio_gpio_lock); | ||
198 | } | ||
199 | |||
200 | u32 nasgpio_led_get_attr(struct led_classdev *led_cdev, u32 port) | ||
201 | { | ||
202 | struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev); | ||
203 | u32 gpio_in; | ||
204 | |||
205 | spin_lock(&nasgpio_gpio_lock); | ||
206 | gpio_in = inl(nas_gpio_io_base + port); | ||
207 | spin_unlock(&nasgpio_gpio_lock); | ||
208 | if (gpio_in & (1<<led->gpio_bit)) | ||
209 | return 1; | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | /* | ||
214 | * There is actual brightness control in the hardware, | ||
215 | * but it is via smbus commands and not implemented | ||
216 | * in this driver. | ||
217 | */ | ||
218 | static void nasgpio_led_set_brightness(struct led_classdev *led_cdev, | ||
219 | enum led_brightness brightness) | ||
220 | { | ||
221 | u32 setting = 0; | ||
222 | if (brightness >= LED_HALF) | ||
223 | setting = 1; | ||
224 | /* | ||
225 | * Hold the lock across both operations. This ensures | ||
226 | * consistency so that both the "turn off blinking" | ||
227 | * and "turn light off" operations complete as a set. | ||
228 | */ | ||
229 | spin_lock(&nasgpio_gpio_lock); | ||
230 | /* | ||
231 | * LED class documentation asks that past blink state | ||
232 | * be disabled when brightness is turned to zero. | ||
233 | */ | ||
234 | if (brightness == 0) | ||
235 | __nasgpio_led_set_attr(led_cdev, GPO_BLINK, 0); | ||
236 | __nasgpio_led_set_attr(led_cdev, GP_LVL, setting); | ||
237 | spin_unlock(&nasgpio_gpio_lock); | ||
238 | } | ||
239 | |||
240 | static int nasgpio_led_set_blink(struct led_classdev *led_cdev, | ||
241 | unsigned long *delay_on, | ||
242 | unsigned long *delay_off) | ||
243 | { | ||
244 | u32 setting = 1; | ||
245 | if (!(*delay_on == 0 && *delay_off == 0) && | ||
246 | !(*delay_on == 500 && *delay_off == 500)) | ||
247 | return -EINVAL; | ||
248 | /* | ||
249 | * These are very approximate. | ||
250 | */ | ||
251 | *delay_on = 500; | ||
252 | *delay_off = 500; | ||
253 | |||
254 | nasgpio_led_set_attr(led_cdev, GPO_BLINK, setting); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | |||
260 | /* | ||
261 | * Initialize the ICH7 GPIO registers for NAS usage. The BIOS should have | ||
262 | * already taken care of this, but we will do so in a non destructive manner | ||
263 | * so that we have what we need whether the BIOS did it or not. | ||
264 | */ | ||
265 | static int __devinit ich7_gpio_init(struct device *dev) | ||
266 | { | ||
267 | int i; | ||
268 | u32 config_data = 0; | ||
269 | u32 all_nas_led = 0; | ||
270 | |||
271 | for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) | ||
272 | all_nas_led |= (1<<nasgpio_leds[i].gpio_bit); | ||
273 | |||
274 | spin_lock(&nasgpio_gpio_lock); | ||
275 | /* | ||
276 | * We need to enable all of the GPIO lines used by the NAS box, | ||
277 | * so we will read the current Use Selection and add our usage | ||
278 | * to it. This should be benign with regard to the original | ||
279 | * BIOS configuration. | ||
280 | */ | ||
281 | config_data = inl(nas_gpio_io_base + GPIO_USE_SEL); | ||
282 | dev_dbg(dev, ": Data read from GPIO_USE_SEL = 0x%08x\n", config_data); | ||
283 | config_data |= all_nas_led + NAS_RECOVERY; | ||
284 | outl(config_data, nas_gpio_io_base + GPIO_USE_SEL); | ||
285 | config_data = inl(nas_gpio_io_base + GPIO_USE_SEL); | ||
286 | dev_dbg(dev, ": GPIO_USE_SEL = 0x%08x\n\n", config_data); | ||
287 | |||
288 | /* | ||
289 | * The LED GPIO outputs need to be configured for output, so we | ||
290 | * will ensure that all LED lines are cleared for output and the | ||
291 | * RECOVERY line ready for input. This too should be benign with | ||
292 | * regard to BIOS configuration. | ||
293 | */ | ||
294 | config_data = inl(nas_gpio_io_base + GP_IO_SEL); | ||
295 | dev_dbg(dev, ": Data read from GP_IO_SEL = 0x%08x\n", | ||
296 | config_data); | ||
297 | config_data &= ~all_nas_led; | ||
298 | config_data |= NAS_RECOVERY; | ||
299 | outl(config_data, nas_gpio_io_base + GP_IO_SEL); | ||
300 | config_data = inl(nas_gpio_io_base + GP_IO_SEL); | ||
301 | dev_dbg(dev, ": GP_IO_SEL = 0x%08x\n", config_data); | ||
302 | |||
303 | /* | ||
304 | * In our final system, the BIOS will initialize the state of all | ||
305 | * of the LEDs. For now, we turn them all off (or Low). | ||
306 | */ | ||
307 | config_data = inl(nas_gpio_io_base + GP_LVL); | ||
308 | dev_dbg(dev, ": Data read from GP_LVL = 0x%08x\n", config_data); | ||
309 | /* | ||
310 | * In our final system, the BIOS will initialize the blink state of all | ||
311 | * of the LEDs. For now, we turn blink off for all of them. | ||
312 | */ | ||
313 | config_data = inl(nas_gpio_io_base + GPO_BLINK); | ||
314 | dev_dbg(dev, ": Data read from GPO_BLINK = 0x%08x\n", config_data); | ||
315 | |||
316 | /* | ||
317 | * At this moment, I am unsure if anything needs to happen with GPI_INV | ||
318 | */ | ||
319 | config_data = inl(nas_gpio_io_base + GPI_INV); | ||
320 | dev_dbg(dev, ": Data read from GPI_INV = 0x%08x\n", config_data); | ||
321 | |||
322 | spin_unlock(&nasgpio_gpio_lock); | ||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | static void ich7_lpc_cleanup(struct device *dev) | ||
327 | { | ||
328 | /* | ||
329 | * If we were given exclusive use of the GPIO | ||
330 | * I/O Address range, we must return it. | ||
331 | */ | ||
332 | if (gp_gpio_resource) { | ||
333 | dev_dbg(dev, ": Releasing GPIO I/O addresses\n"); | ||
334 | release_region(nas_gpio_io_base, ICH7_GPIO_SIZE); | ||
335 | gp_gpio_resource = NULL; | ||
336 | } | ||
337 | } | ||
338 | |||
339 | /* | ||
340 | * The OS has determined that the LPC of the Intel ICH7 Southbridge is present | ||
341 | * so we can retrive the required operational information and prepare the GPIO. | ||
342 | */ | ||
343 | static struct pci_dev *nas_gpio_pci_dev; | ||
344 | static int __devinit ich7_lpc_probe(struct pci_dev *dev, | ||
345 | const struct pci_device_id *id) | ||
346 | { | ||
347 | int status = 0; | ||
348 | u32 gc = 0; | ||
349 | |||
350 | pci_enable_device(dev); | ||
351 | |||
352 | nas_gpio_pci_dev = dev; | ||
353 | status = pci_read_config_dword(dev, PMBASE, &g_pm_io_base); | ||
354 | if (status) | ||
355 | goto out; | ||
356 | g_pm_io_base &= 0x00000ff80; | ||
357 | |||
358 | status = pci_read_config_dword(dev, GPIO_CTRL, &gc); | ||
359 | if (!(GPIO_EN & gc)) { | ||
360 | status = -EEXIST; | ||
361 | dev_info(&dev->dev, | ||
362 | "ERROR: The LPC GPIO Block has not been enabled.\n"); | ||
363 | goto out; | ||
364 | } | ||
365 | |||
366 | status = pci_read_config_dword(dev, GPIO_BASE, &nas_gpio_io_base); | ||
367 | if (0 > status) { | ||
368 | dev_info(&dev->dev, "Unable to read GPIOBASE.\n"); | ||
369 | goto out; | ||
370 | } | ||
371 | dev_dbg(&dev->dev, ": GPIOBASE = 0x%08x\n", nas_gpio_io_base); | ||
372 | nas_gpio_io_base &= 0x00000ffc0; | ||
373 | |||
374 | /* | ||
375 | * Insure that we have exclusive access to the GPIO I/O address range. | ||
376 | */ | ||
377 | gp_gpio_resource = request_region(nas_gpio_io_base, ICH7_GPIO_SIZE, | ||
378 | KBUILD_MODNAME); | ||
379 | if (NULL == gp_gpio_resource) { | ||
380 | dev_info(&dev->dev, | ||
381 | "ERROR Unable to register GPIO I/O addresses.\n"); | ||
382 | status = -1; | ||
383 | goto out; | ||
384 | } | ||
385 | |||
386 | /* | ||
387 | * Initialize the GPIO for NAS/Home Server Use | ||
388 | */ | ||
389 | ich7_gpio_init(&dev->dev); | ||
390 | |||
391 | out: | ||
392 | if (status) { | ||
393 | ich7_lpc_cleanup(&dev->dev); | ||
394 | pci_disable_device(dev); | ||
395 | } | ||
396 | return status; | ||
397 | } | ||
398 | |||
399 | static void ich7_lpc_remove(struct pci_dev *dev) | ||
400 | { | ||
401 | ich7_lpc_cleanup(&dev->dev); | ||
402 | pci_disable_device(dev); | ||
403 | } | ||
404 | |||
405 | /* | ||
406 | * pci_driver structure passed to the PCI modules | ||
407 | */ | ||
408 | static struct pci_driver nas_gpio_pci_driver = { | ||
409 | .name = KBUILD_MODNAME, | ||
410 | .id_table = ich7_lpc_pci_id, | ||
411 | .probe = ich7_lpc_probe, | ||
412 | .remove = ich7_lpc_remove, | ||
413 | }; | ||
414 | |||
415 | static struct led_classdev *get_classdev_for_led_nr(int nr) | ||
416 | { | ||
417 | struct nasgpio_led *nas_led = &nasgpio_leds[nr]; | ||
418 | struct led_classdev *led = &nas_led->led_cdev; | ||
419 | return led; | ||
420 | } | ||
421 | |||
422 | |||
423 | static void set_power_light_amber_noblink(void) | ||
424 | { | ||
425 | struct nasgpio_led *amber = get_led_named("power:amber:power"); | ||
426 | struct nasgpio_led *blue = get_led_named("power:blue:power"); | ||
427 | |||
428 | if (!amber || !blue) | ||
429 | return; | ||
430 | /* | ||
431 | * LED_OFF implies disabling future blinking | ||
432 | */ | ||
433 | pr_debug("setting blue off and amber on\n"); | ||
434 | |||
435 | nasgpio_led_set_brightness(&blue->led_cdev, LED_OFF); | ||
436 | nasgpio_led_set_brightness(&amber->led_cdev, LED_FULL); | ||
437 | } | ||
438 | |||
439 | static ssize_t nas_led_blink_show(struct device *dev, | ||
440 | struct device_attribute *attr, char *buf) | ||
441 | { | ||
442 | struct led_classdev *led = dev_get_drvdata(dev); | ||
443 | int blinking = 0; | ||
444 | if (nasgpio_led_get_attr(led, GPO_BLINK)) | ||
445 | blinking = 1; | ||
446 | return sprintf(buf, "%u\n", blinking); | ||
447 | } | ||
448 | |||
449 | static ssize_t nas_led_blink_store(struct device *dev, | ||
450 | struct device_attribute *attr, | ||
451 | const char *buf, size_t size) | ||
452 | { | ||
453 | int ret; | ||
454 | struct led_classdev *led = dev_get_drvdata(dev); | ||
455 | unsigned long blink_state; | ||
456 | |||
457 | ret = strict_strtoul(buf, 10, &blink_state); | ||
458 | if (ret) | ||
459 | return ret; | ||
460 | |||
461 | nasgpio_led_set_attr(led, GPO_BLINK, blink_state); | ||
462 | |||
463 | return size; | ||
464 | } | ||
465 | |||
466 | static DEVICE_ATTR(blink, 0644, nas_led_blink_show, nas_led_blink_store); | ||
467 | |||
468 | static int register_nasgpio_led(int led_nr) | ||
469 | { | ||
470 | int ret; | ||
471 | struct nasgpio_led *nas_led = &nasgpio_leds[led_nr]; | ||
472 | struct led_classdev *led = get_classdev_for_led_nr(led_nr); | ||
473 | |||
474 | led->name = nas_led->name; | ||
475 | led->brightness = LED_OFF; | ||
476 | if (nasgpio_led_get_attr(led, GP_LVL)) | ||
477 | led->brightness = LED_FULL; | ||
478 | led->brightness_set = nasgpio_led_set_brightness; | ||
479 | led->blink_set = nasgpio_led_set_blink; | ||
480 | ret = led_classdev_register(&nas_gpio_pci_dev->dev, led); | ||
481 | if (ret) | ||
482 | return ret; | ||
483 | ret = device_create_file(led->dev, &dev_attr_blink); | ||
484 | if (ret) | ||
485 | led_classdev_unregister(led); | ||
486 | return ret; | ||
487 | } | ||
488 | |||
489 | static void unregister_nasgpio_led(int led_nr) | ||
490 | { | ||
491 | struct led_classdev *led = get_classdev_for_led_nr(led_nr); | ||
492 | led_classdev_unregister(led); | ||
493 | device_remove_file(led->dev, &dev_attr_blink); | ||
494 | } | ||
495 | /* | ||
496 | * module load/initialization | ||
497 | */ | ||
498 | static int __init nas_gpio_init(void) | ||
499 | { | ||
500 | int i; | ||
501 | int ret = 0; | ||
502 | int nr_devices = 0; | ||
503 | |||
504 | nr_devices = dmi_check_system(nas_led_whitelist); | ||
505 | if (nodetect) { | ||
506 | pr_info("skipping hardware autodetection\n"); | ||
507 | pr_info("Please send 'dmidecode' output to dave@sr71.net\n"); | ||
508 | nr_devices++; | ||
509 | } | ||
510 | |||
511 | if (nr_devices <= 0) { | ||
512 | pr_info("no LED devices found\n"); | ||
513 | return -ENODEV; | ||
514 | } | ||
515 | |||
516 | pr_info("registering PCI driver\n"); | ||
517 | ret = pci_register_driver(&nas_gpio_pci_driver); | ||
518 | if (ret) | ||
519 | return ret; | ||
520 | for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) { | ||
521 | ret = register_nasgpio_led(i); | ||
522 | if (ret) | ||
523 | goto out_err; | ||
524 | } | ||
525 | /* | ||
526 | * When the system powers on, the BIOS leaves the power | ||
527 | * light blue and blinking. This will turn it solid | ||
528 | * amber once the driver is loaded. | ||
529 | */ | ||
530 | set_power_light_amber_noblink(); | ||
531 | return 0; | ||
532 | out_err: | ||
533 | for (; i >= 0; i--) | ||
534 | unregister_nasgpio_led(i); | ||
535 | pci_unregister_driver(&nas_gpio_pci_driver); | ||
536 | return ret; | ||
537 | } | ||
538 | |||
539 | /* | ||
540 | * module unload | ||
541 | */ | ||
542 | static void __exit nas_gpio_exit(void) | ||
543 | { | ||
544 | int i; | ||
545 | pr_info("Unregistering driver\n"); | ||
546 | for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) | ||
547 | unregister_nasgpio_led(i); | ||
548 | pci_unregister_driver(&nas_gpio_pci_driver); | ||
549 | } | ||
550 | |||
551 | module_init(nas_gpio_init); | ||
552 | module_exit(nas_gpio_exit); | ||