diff options
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/Kconfig | 33 | ||||
-rw-r--r-- | drivers/leds/Makefile | 4 | ||||
-rw-r--r-- | drivers/leds/leds-adp5520.c | 230 | ||||
-rw-r--r-- | drivers/leds/leds-alix2.c | 115 | ||||
-rw-r--r-- | drivers/leds/leds-cobalt-qube.c | 4 | ||||
-rw-r--r-- | drivers/leds/leds-cobalt-raq.c | 2 | ||||
-rw-r--r-- | drivers/leds/leds-lt3593.c | 217 | ||||
-rw-r--r-- | drivers/leds/leds-pwm.c | 5 | ||||
-rw-r--r-- | drivers/leds/leds-regulator.c | 242 | ||||
-rw-r--r-- | drivers/leds/leds-ss4200.c | 556 |
10 files changed, 1371 insertions, 37 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index e4f599f20e38..8a0e1ec95e4a 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -229,6 +229,12 @@ config LEDS_PWM | |||
229 | help | 229 | help |
230 | This option enables support for pwm driven LEDs | 230 | This option enables support for pwm driven LEDs |
231 | 231 | ||
232 | config LEDS_REGULATOR | ||
233 | tristate "REGULATOR driven LED support" | ||
234 | depends on LEDS_CLASS && REGULATOR | ||
235 | help | ||
236 | This option enables support for regulator driven LEDs. | ||
237 | |||
232 | config LEDS_BD2802 | 238 | config LEDS_BD2802 |
233 | tristate "LED driver for BD2802 RGB LED" | 239 | tristate "LED driver for BD2802 RGB LED" |
234 | depends on LEDS_CLASS && I2C | 240 | depends on LEDS_CLASS && I2C |
@@ -236,6 +242,33 @@ config LEDS_BD2802 | |||
236 | This option enables support for BD2802GU RGB LED driver chips | 242 | This option enables support for BD2802GU RGB LED driver chips |
237 | accessed via the I2C bus. | 243 | accessed via the I2C bus. |
238 | 244 | ||
245 | config LEDS_INTEL_SS4200 | ||
246 | tristate "LED driver for Intel NAS SS4200 series" | ||
247 | depends on LEDS_CLASS && PCI && DMI | ||
248 | help | ||
249 | This option enables support for the Intel SS4200 series of | ||
250 | Network Attached Storage servers. You may control the hard | ||
251 | drive or power LEDs on the front panel. Using this driver | ||
252 | can stop the front LED from blinking after startup. | ||
253 | |||
254 | config LEDS_LT3593 | ||
255 | tristate "LED driver for LT3593 controllers" | ||
256 | depends on LEDS_CLASS && GENERIC_GPIO | ||
257 | help | ||
258 | This option enables support for LEDs driven by a Linear Technology | ||
259 | LT3593 controller. This controller uses a special one-wire pulse | ||
260 | coding protocol to set the brightness. | ||
261 | |||
262 | config LEDS_ADP5520 | ||
263 | tristate "LED Support for ADP5520/ADP5501 PMIC" | ||
264 | depends on LEDS_CLASS && PMIC_ADP5520 | ||
265 | help | ||
266 | This option enables support for on-chip LED drivers found | ||
267 | on Analog Devices ADP5520/ADP5501 PMICs. | ||
268 | |||
269 | To compile this driver as a module, choose M here: the module will | ||
270 | be called leds-adp5520. | ||
271 | |||
239 | comment "LED Triggers" | 272 | comment "LED Triggers" |
240 | 273 | ||
241 | config LEDS_TRIGGERS | 274 | config LEDS_TRIGGERS |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 46d72704d606..9e63869d7c0d 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -29,6 +29,10 @@ 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_REGULATOR) += leds-regulator.o | ||
33 | obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o | ||
34 | obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o | ||
35 | obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o | ||
32 | 36 | ||
33 | # LED SPI Drivers | 37 | # LED SPI Drivers |
34 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o | 38 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o |
diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c new file mode 100644 index 000000000000..a8f315902131 --- /dev/null +++ b/drivers/leds/leds-adp5520.c | |||
@@ -0,0 +1,230 @@ | |||
1 | /* | ||
2 | * LEDs driver for Analog Devices ADP5520/ADP5501 MFD PMICs | ||
3 | * | ||
4 | * Copyright 2009 Analog Devices Inc. | ||
5 | * | ||
6 | * Loosely derived from leds-da903x: | ||
7 | * Copyright (C) 2008 Compulab, Ltd. | ||
8 | * Mike Rapoport <mike@compulab.co.il> | ||
9 | * | ||
10 | * Copyright (C) 2006-2008 Marvell International Ltd. | ||
11 | * Eric Miao <eric.miao@marvell.com> | ||
12 | * | ||
13 | * Licensed under the GPL-2 or later. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/leds.h> | ||
21 | #include <linux/workqueue.h> | ||
22 | #include <linux/mfd/adp5520.h> | ||
23 | |||
24 | struct adp5520_led { | ||
25 | struct led_classdev cdev; | ||
26 | struct work_struct work; | ||
27 | struct device *master; | ||
28 | enum led_brightness new_brightness; | ||
29 | int id; | ||
30 | int flags; | ||
31 | }; | ||
32 | |||
33 | static void adp5520_led_work(struct work_struct *work) | ||
34 | { | ||
35 | struct adp5520_led *led = container_of(work, struct adp5520_led, work); | ||
36 | adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1, | ||
37 | led->new_brightness >> 2); | ||
38 | } | ||
39 | |||
40 | static void adp5520_led_set(struct led_classdev *led_cdev, | ||
41 | enum led_brightness value) | ||
42 | { | ||
43 | struct adp5520_led *led; | ||
44 | |||
45 | led = container_of(led_cdev, struct adp5520_led, cdev); | ||
46 | led->new_brightness = value; | ||
47 | schedule_work(&led->work); | ||
48 | } | ||
49 | |||
50 | static int adp5520_led_setup(struct adp5520_led *led) | ||
51 | { | ||
52 | struct device *dev = led->master; | ||
53 | int flags = led->flags; | ||
54 | int ret = 0; | ||
55 | |||
56 | switch (led->id) { | ||
57 | case FLAG_ID_ADP5520_LED1_ADP5501_LED0: | ||
58 | ret |= adp5520_set_bits(dev, ADP5520_LED_TIME, | ||
59 | (flags >> ADP5520_FLAG_OFFT_SHIFT) & | ||
60 | ADP5520_FLAG_OFFT_MASK); | ||
61 | ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL, | ||
62 | ADP5520_LED1_EN); | ||
63 | break; | ||
64 | case FLAG_ID_ADP5520_LED2_ADP5501_LED1: | ||
65 | ret |= adp5520_set_bits(dev, ADP5520_LED_TIME, | ||
66 | ((flags >> ADP5520_FLAG_OFFT_SHIFT) & | ||
67 | ADP5520_FLAG_OFFT_MASK) << 2); | ||
68 | ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL, | ||
69 | ADP5520_R3_MODE); | ||
70 | ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL, | ||
71 | ADP5520_LED2_EN); | ||
72 | break; | ||
73 | case FLAG_ID_ADP5520_LED3_ADP5501_LED2: | ||
74 | ret |= adp5520_set_bits(dev, ADP5520_LED_TIME, | ||
75 | ((flags >> ADP5520_FLAG_OFFT_SHIFT) & | ||
76 | ADP5520_FLAG_OFFT_MASK) << 4); | ||
77 | ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL, | ||
78 | ADP5520_C3_MODE); | ||
79 | ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL, | ||
80 | ADP5520_LED3_EN); | ||
81 | break; | ||
82 | } | ||
83 | |||
84 | return ret; | ||
85 | } | ||
86 | |||
87 | static int __devinit adp5520_led_prepare(struct platform_device *pdev) | ||
88 | { | ||
89 | struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data; | ||
90 | struct device *dev = pdev->dev.parent; | ||
91 | int ret = 0; | ||
92 | |||
93 | ret |= adp5520_write(dev, ADP5520_LED1_CURRENT, 0); | ||
94 | ret |= adp5520_write(dev, ADP5520_LED2_CURRENT, 0); | ||
95 | ret |= adp5520_write(dev, ADP5520_LED3_CURRENT, 0); | ||
96 | ret |= adp5520_write(dev, ADP5520_LED_TIME, pdata->led_on_time << 6); | ||
97 | ret |= adp5520_write(dev, ADP5520_LED_FADE, FADE_VAL(pdata->fade_in, | ||
98 | pdata->fade_out)); | ||
99 | |||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | static int __devinit adp5520_led_probe(struct platform_device *pdev) | ||
104 | { | ||
105 | struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data; | ||
106 | struct adp5520_led *led, *led_dat; | ||
107 | struct led_info *cur_led; | ||
108 | int ret, i; | ||
109 | |||
110 | if (pdata == NULL) { | ||
111 | dev_err(&pdev->dev, "missing platform data\n"); | ||
112 | return -ENODEV; | ||
113 | } | ||
114 | |||
115 | if (pdata->num_leds > ADP5520_01_MAXLEDS) { | ||
116 | dev_err(&pdev->dev, "can't handle more than %d LEDS\n", | ||
117 | ADP5520_01_MAXLEDS); | ||
118 | return -EFAULT; | ||
119 | } | ||
120 | |||
121 | led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL); | ||
122 | if (led == NULL) { | ||
123 | dev_err(&pdev->dev, "failed to alloc memory\n"); | ||
124 | return -ENOMEM; | ||
125 | } | ||
126 | |||
127 | ret = adp5520_led_prepare(pdev); | ||
128 | |||
129 | if (ret) { | ||
130 | dev_err(&pdev->dev, "failed to write\n"); | ||
131 | goto err_free; | ||
132 | } | ||
133 | |||
134 | for (i = 0; i < pdata->num_leds; ++i) { | ||
135 | cur_led = &pdata->leds[i]; | ||
136 | led_dat = &led[i]; | ||
137 | |||
138 | led_dat->cdev.name = cur_led->name; | ||
139 | led_dat->cdev.default_trigger = cur_led->default_trigger; | ||
140 | led_dat->cdev.brightness_set = adp5520_led_set; | ||
141 | led_dat->cdev.brightness = LED_OFF; | ||
142 | |||
143 | if (cur_led->flags & ADP5520_FLAG_LED_MASK) | ||
144 | led_dat->flags = cur_led->flags; | ||
145 | else | ||
146 | led_dat->flags = i + 1; | ||
147 | |||
148 | led_dat->id = led_dat->flags & ADP5520_FLAG_LED_MASK; | ||
149 | |||
150 | led_dat->master = pdev->dev.parent; | ||
151 | led_dat->new_brightness = LED_OFF; | ||
152 | |||
153 | INIT_WORK(&led_dat->work, adp5520_led_work); | ||
154 | |||
155 | ret = led_classdev_register(led_dat->master, &led_dat->cdev); | ||
156 | if (ret) { | ||
157 | dev_err(&pdev->dev, "failed to register LED %d\n", | ||
158 | led_dat->id); | ||
159 | goto err; | ||
160 | } | ||
161 | |||
162 | ret = adp5520_led_setup(led_dat); | ||
163 | if (ret) { | ||
164 | dev_err(&pdev->dev, "failed to write\n"); | ||
165 | i++; | ||
166 | goto err; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | platform_set_drvdata(pdev, led); | ||
171 | return 0; | ||
172 | |||
173 | err: | ||
174 | if (i > 0) { | ||
175 | for (i = i - 1; i >= 0; i--) { | ||
176 | led_classdev_unregister(&led[i].cdev); | ||
177 | cancel_work_sync(&led[i].work); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | err_free: | ||
182 | kfree(led); | ||
183 | return ret; | ||
184 | } | ||
185 | |||
186 | static int __devexit adp5520_led_remove(struct platform_device *pdev) | ||
187 | { | ||
188 | struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data; | ||
189 | struct adp5520_led *led; | ||
190 | int i; | ||
191 | |||
192 | led = platform_get_drvdata(pdev); | ||
193 | |||
194 | adp5520_clr_bits(led->master, ADP5520_LED_CONTROL, | ||
195 | ADP5520_LED1_EN | ADP5520_LED2_EN | ADP5520_LED3_EN); | ||
196 | |||
197 | for (i = 0; i < pdata->num_leds; i++) { | ||
198 | led_classdev_unregister(&led[i].cdev); | ||
199 | cancel_work_sync(&led[i].work); | ||
200 | } | ||
201 | |||
202 | kfree(led); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static struct platform_driver adp5520_led_driver = { | ||
207 | .driver = { | ||
208 | .name = "adp5520-led", | ||
209 | .owner = THIS_MODULE, | ||
210 | }, | ||
211 | .probe = adp5520_led_probe, | ||
212 | .remove = __devexit_p(adp5520_led_remove), | ||
213 | }; | ||
214 | |||
215 | static int __init adp5520_led_init(void) | ||
216 | { | ||
217 | return platform_driver_register(&adp5520_led_driver); | ||
218 | } | ||
219 | module_init(adp5520_led_init); | ||
220 | |||
221 | static void __exit adp5520_led_exit(void) | ||
222 | { | ||
223 | platform_driver_unregister(&adp5520_led_driver); | ||
224 | } | ||
225 | module_exit(adp5520_led_exit); | ||
226 | |||
227 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | ||
228 | MODULE_DESCRIPTION("LEDS ADP5520(01) Driver"); | ||
229 | MODULE_LICENSE("GPL"); | ||
230 | MODULE_ALIAS("platform:adp5520-led"); | ||
diff --git a/drivers/leds/leds-alix2.c b/drivers/leds/leds-alix2.c index 731d4eef3425..f59ffadf5125 100644 --- a/drivers/leds/leds-alix2.c +++ b/drivers/leds/leds-alix2.c | |||
@@ -11,11 +11,24 @@ | |||
11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
12 | #include <linux/platform_device.h> | 12 | #include <linux/platform_device.h> |
13 | #include <linux/string.h> | 13 | #include <linux/string.h> |
14 | #include <linux/pci.h> | ||
14 | 15 | ||
15 | static int force = 0; | 16 | static int force = 0; |
16 | module_param(force, bool, 0444); | 17 | module_param(force, bool, 0444); |
17 | MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs"); | 18 | MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs"); |
18 | 19 | ||
20 | #define MSR_LBAR_GPIO 0x5140000C | ||
21 | #define CS5535_GPIO_SIZE 256 | ||
22 | |||
23 | static u32 gpio_base; | ||
24 | |||
25 | static struct pci_device_id divil_pci[] = { | ||
26 | { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, | ||
27 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, | ||
28 | { } /* NULL entry */ | ||
29 | }; | ||
30 | MODULE_DEVICE_TABLE(pci, divil_pci); | ||
31 | |||
19 | struct alix_led { | 32 | struct alix_led { |
20 | struct led_classdev cdev; | 33 | struct led_classdev cdev; |
21 | unsigned short port; | 34 | unsigned short port; |
@@ -30,9 +43,9 @@ static void alix_led_set(struct led_classdev *led_cdev, | |||
30 | container_of(led_cdev, struct alix_led, cdev); | 43 | container_of(led_cdev, struct alix_led, cdev); |
31 | 44 | ||
32 | if (brightness) | 45 | if (brightness) |
33 | outl(led_dev->on_value, led_dev->port); | 46 | outl(led_dev->on_value, gpio_base + led_dev->port); |
34 | else | 47 | else |
35 | outl(led_dev->off_value, led_dev->port); | 48 | outl(led_dev->off_value, gpio_base + led_dev->port); |
36 | } | 49 | } |
37 | 50 | ||
38 | static struct alix_led alix_leds[] = { | 51 | static struct alix_led alix_leds[] = { |
@@ -41,7 +54,7 @@ static struct alix_led alix_leds[] = { | |||
41 | .name = "alix:1", | 54 | .name = "alix:1", |
42 | .brightness_set = alix_led_set, | 55 | .brightness_set = alix_led_set, |
43 | }, | 56 | }, |
44 | .port = 0x6100, | 57 | .port = 0x00, |
45 | .on_value = 1 << 22, | 58 | .on_value = 1 << 22, |
46 | .off_value = 1 << 6, | 59 | .off_value = 1 << 6, |
47 | }, | 60 | }, |
@@ -50,7 +63,7 @@ static struct alix_led alix_leds[] = { | |||
50 | .name = "alix:2", | 63 | .name = "alix:2", |
51 | .brightness_set = alix_led_set, | 64 | .brightness_set = alix_led_set, |
52 | }, | 65 | }, |
53 | .port = 0x6180, | 66 | .port = 0x80, |
54 | .on_value = 1 << 25, | 67 | .on_value = 1 << 25, |
55 | .off_value = 1 << 9, | 68 | .off_value = 1 << 9, |
56 | }, | 69 | }, |
@@ -59,7 +72,7 @@ static struct alix_led alix_leds[] = { | |||
59 | .name = "alix:3", | 72 | .name = "alix:3", |
60 | .brightness_set = alix_led_set, | 73 | .brightness_set = alix_led_set, |
61 | }, | 74 | }, |
62 | .port = 0x6180, | 75 | .port = 0x80, |
63 | .on_value = 1 << 27, | 76 | .on_value = 1 << 27, |
64 | .off_value = 1 << 11, | 77 | .off_value = 1 << 11, |
65 | }, | 78 | }, |
@@ -101,64 +114,104 @@ static struct platform_driver alix_led_driver = { | |||
101 | }, | 114 | }, |
102 | }; | 115 | }; |
103 | 116 | ||
104 | static int __init alix_present(void) | 117 | static int __init alix_present(unsigned long bios_phys, |
118 | const char *alix_sig, | ||
119 | size_t alix_sig_len) | ||
105 | { | 120 | { |
106 | const unsigned long bios_phys = 0x000f0000; | ||
107 | const size_t bios_len = 0x00010000; | 121 | const size_t bios_len = 0x00010000; |
108 | const char alix_sig[] = "PC Engines ALIX."; | ||
109 | const size_t alix_sig_len = sizeof(alix_sig) - 1; | ||
110 | |||
111 | const char *bios_virt; | 122 | const char *bios_virt; |
112 | const char *scan_end; | 123 | const char *scan_end; |
113 | const char *p; | 124 | const char *p; |
114 | int ret = 0; | 125 | char name[64]; |
115 | 126 | ||
116 | if (force) { | 127 | if (force) { |
117 | printk(KERN_NOTICE "%s: forced to skip BIOS test, " | 128 | printk(KERN_NOTICE "%s: forced to skip BIOS test, " |
118 | "assume system has ALIX.2 style LEDs\n", | 129 | "assume system has ALIX.2 style LEDs\n", |
119 | KBUILD_MODNAME); | 130 | KBUILD_MODNAME); |
120 | ret = 1; | 131 | return 1; |
121 | goto out; | ||
122 | } | 132 | } |
123 | 133 | ||
124 | bios_virt = phys_to_virt(bios_phys); | 134 | bios_virt = phys_to_virt(bios_phys); |
125 | scan_end = bios_virt + bios_len - (alix_sig_len + 2); | 135 | scan_end = bios_virt + bios_len - (alix_sig_len + 2); |
126 | for (p = bios_virt; p < scan_end; p++) { | 136 | for (p = bios_virt; p < scan_end; p++) { |
127 | const char *tail; | 137 | const char *tail; |
138 | char *a; | ||
128 | 139 | ||
129 | if (memcmp(p, alix_sig, alix_sig_len) != 0) { | 140 | if (memcmp(p, alix_sig, alix_sig_len) != 0) |
130 | continue; | 141 | continue; |
131 | } | 142 | |
143 | memcpy(name, p, sizeof(name)); | ||
144 | |||
145 | /* remove the first \0 character from string */ | ||
146 | a = strchr(name, '\0'); | ||
147 | if (a) | ||
148 | *a = ' '; | ||
149 | |||
150 | /* cut the string at a newline */ | ||
151 | a = strchr(name, '\r'); | ||
152 | if (a) | ||
153 | *a = '\0'; | ||
132 | 154 | ||
133 | tail = p + alix_sig_len; | 155 | tail = p + alix_sig_len; |
134 | if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') { | 156 | if ((tail[0] == '2' || tail[0] == '3')) { |
135 | printk(KERN_INFO | 157 | printk(KERN_INFO |
136 | "%s: system is recognized as \"%s\"\n", | 158 | "%s: system is recognized as \"%s\"\n", |
137 | KBUILD_MODNAME, p); | 159 | KBUILD_MODNAME, name); |
138 | ret = 1; | 160 | return 1; |
139 | break; | ||
140 | } | 161 | } |
141 | } | 162 | } |
142 | 163 | ||
143 | out: | 164 | return 0; |
144 | return ret; | ||
145 | } | 165 | } |
146 | 166 | ||
147 | static struct platform_device *pdev; | 167 | static struct platform_device *pdev; |
148 | 168 | ||
149 | static int __init alix_led_init(void) | 169 | static int __init alix_pci_led_init(void) |
150 | { | 170 | { |
151 | int ret; | 171 | u32 low, hi; |
152 | 172 | ||
153 | if (!alix_present()) { | 173 | if (pci_dev_present(divil_pci) == 0) { |
154 | ret = -ENODEV; | 174 | printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n"); |
155 | goto out; | 175 | return -ENODEV; |
156 | } | 176 | } |
157 | 177 | ||
158 | /* enable output on GPIO for LED 1,2,3 */ | 178 | /* Grab the GPIO I/O range */ |
159 | outl(1 << 6, 0x6104); | 179 | rdmsr(MSR_LBAR_GPIO, low, hi); |
160 | outl(1 << 9, 0x6184); | 180 | |
161 | outl(1 << 11, 0x6184); | 181 | /* Check the mask and whether GPIO is enabled (sanity check) */ |
182 | if (hi != 0x0000f001) { | ||
183 | printk(KERN_WARNING KBUILD_MODNAME": GPIO not enabled\n"); | ||
184 | return -ENODEV; | ||
185 | } | ||
186 | |||
187 | /* Mask off the IO base address */ | ||
188 | gpio_base = low & 0x0000ff00; | ||
189 | |||
190 | if (!request_region(gpio_base, CS5535_GPIO_SIZE, KBUILD_MODNAME)) { | ||
191 | printk(KERN_ERR KBUILD_MODNAME": can't allocate I/O for GPIO\n"); | ||
192 | return -ENODEV; | ||
193 | } | ||
194 | |||
195 | /* Set GPIO function to output */ | ||
196 | outl(1 << 6, gpio_base + 0x04); | ||
197 | outl(1 << 9, gpio_base + 0x84); | ||
198 | outl(1 << 11, gpio_base + 0x84); | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static int __init alix_led_init(void) | ||
204 | { | ||
205 | int ret = -ENODEV; | ||
206 | const char tinybios_sig[] = "PC Engines ALIX."; | ||
207 | const char coreboot_sig[] = "PC Engines\0ALIX."; | ||
208 | |||
209 | if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) || | ||
210 | alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1)) | ||
211 | ret = alix_pci_led_init(); | ||
212 | |||
213 | if (ret < 0) | ||
214 | return ret; | ||
162 | 215 | ||
163 | pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); | 216 | pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); |
164 | if (!IS_ERR(pdev)) { | 217 | if (!IS_ERR(pdev)) { |
@@ -168,7 +221,6 @@ static int __init alix_led_init(void) | |||
168 | } else | 221 | } else |
169 | ret = PTR_ERR(pdev); | 222 | ret = PTR_ERR(pdev); |
170 | 223 | ||
171 | out: | ||
172 | return ret; | 224 | return ret; |
173 | } | 225 | } |
174 | 226 | ||
@@ -176,6 +228,7 @@ static void __exit alix_led_exit(void) | |||
176 | { | 228 | { |
177 | platform_device_unregister(pdev); | 229 | platform_device_unregister(pdev); |
178 | platform_driver_unregister(&alix_led_driver); | 230 | platform_driver_unregister(&alix_led_driver); |
231 | release_region(gpio_base, CS5535_GPIO_SIZE); | ||
179 | } | 232 | } |
180 | 233 | ||
181 | module_init(alix_led_init); | 234 | module_init(alix_led_init); |
diff --git a/drivers/leds/leds-cobalt-qube.c b/drivers/leds/leds-cobalt-qube.c index 8816806accd2..da5fb016b1a5 100644 --- a/drivers/leds/leds-cobalt-qube.c +++ b/drivers/leds/leds-cobalt-qube.c | |||
@@ -31,7 +31,7 @@ static struct led_classdev qube_front_led = { | |||
31 | .name = "qube::front", | 31 | .name = "qube::front", |
32 | .brightness = LED_FULL, | 32 | .brightness = LED_FULL, |
33 | .brightness_set = qube_front_led_set, | 33 | .brightness_set = qube_front_led_set, |
34 | .default_trigger = "ide-disk", | 34 | .default_trigger = "default-on", |
35 | }; | 35 | }; |
36 | 36 | ||
37 | static int __devinit cobalt_qube_led_probe(struct platform_device *pdev) | 37 | static int __devinit cobalt_qube_led_probe(struct platform_device *pdev) |
@@ -43,7 +43,7 @@ static int __devinit cobalt_qube_led_probe(struct platform_device *pdev) | |||
43 | if (!res) | 43 | if (!res) |
44 | return -EBUSY; | 44 | return -EBUSY; |
45 | 45 | ||
46 | led_port = ioremap(res->start, res->end - res->start + 1); | 46 | led_port = ioremap(res->start, resource_size(res)); |
47 | if (!led_port) | 47 | if (!led_port) |
48 | return -ENOMEM; | 48 | return -ENOMEM; |
49 | 49 | ||
diff --git a/drivers/leds/leds-cobalt-raq.c b/drivers/leds/leds-cobalt-raq.c index defc212105f3..438d48384636 100644 --- a/drivers/leds/leds-cobalt-raq.c +++ b/drivers/leds/leds-cobalt-raq.c | |||
@@ -84,7 +84,7 @@ static int __devinit cobalt_raq_led_probe(struct platform_device *pdev) | |||
84 | if (!res) | 84 | if (!res) |
85 | return -EBUSY; | 85 | return -EBUSY; |
86 | 86 | ||
87 | led_port = ioremap(res->start, res->end - res->start + 1); | 87 | led_port = ioremap(res->start, resource_size(res)); |
88 | if (!led_port) | 88 | if (!led_port) |
89 | return -ENOMEM; | 89 | return -ENOMEM; |
90 | 90 | ||
diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c new file mode 100644 index 000000000000..fee40a841959 --- /dev/null +++ b/drivers/leds/leds-lt3593.c | |||
@@ -0,0 +1,217 @@ | |||
1 | /* | ||
2 | * LEDs driver for LT3593 controllers | ||
3 | * | ||
4 | * See the datasheet at http://cds.linear.com/docs/Datasheet/3593f.pdf | ||
5 | * | ||
6 | * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> | ||
7 | * | ||
8 | * Based on leds-gpio.c, | ||
9 | * | ||
10 | * Copyright (C) 2007 8D Technologies inc. | ||
11 | * Raphael Assenat <raph@8d.com> | ||
12 | * Copyright (C) 2008 Freescale Semiconductor, Inc. | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License version 2 as | ||
16 | * published by the Free Software Foundation. | ||
17 | */ | ||
18 | |||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/leds.h> | ||
23 | #include <linux/workqueue.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/gpio.h> | ||
26 | |||
27 | struct lt3593_led_data { | ||
28 | struct led_classdev cdev; | ||
29 | unsigned gpio; | ||
30 | struct work_struct work; | ||
31 | u8 new_level; | ||
32 | }; | ||
33 | |||
34 | static void lt3593_led_work(struct work_struct *work) | ||
35 | { | ||
36 | int pulses; | ||
37 | struct lt3593_led_data *led_dat = | ||
38 | container_of(work, struct lt3593_led_data, work); | ||
39 | |||
40 | /* | ||
41 | * The LT3593 resets its internal current level register to the maximum | ||
42 | * level on the first falling edge on the control pin. Each following | ||
43 | * falling edge decreases the current level by 625uA. Up to 32 pulses | ||
44 | * can be sent, so the maximum power reduction is 20mA. | ||
45 | * After a timeout of 128us, the value is taken from the register and | ||
46 | * applied is to the output driver. | ||
47 | */ | ||
48 | |||
49 | if (led_dat->new_level == 0) { | ||
50 | gpio_set_value_cansleep(led_dat->gpio, 0); | ||
51 | return; | ||
52 | } | ||
53 | |||
54 | pulses = 32 - (led_dat->new_level * 32) / 255; | ||
55 | |||
56 | if (pulses == 0) { | ||
57 | gpio_set_value_cansleep(led_dat->gpio, 0); | ||
58 | mdelay(1); | ||
59 | gpio_set_value_cansleep(led_dat->gpio, 1); | ||
60 | return; | ||
61 | } | ||
62 | |||
63 | gpio_set_value_cansleep(led_dat->gpio, 1); | ||
64 | |||
65 | while (pulses--) { | ||
66 | gpio_set_value_cansleep(led_dat->gpio, 0); | ||
67 | udelay(1); | ||
68 | gpio_set_value_cansleep(led_dat->gpio, 1); | ||
69 | udelay(1); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | static void lt3593_led_set(struct led_classdev *led_cdev, | ||
74 | enum led_brightness value) | ||
75 | { | ||
76 | struct lt3593_led_data *led_dat = | ||
77 | container_of(led_cdev, struct lt3593_led_data, cdev); | ||
78 | |||
79 | led_dat->new_level = value; | ||
80 | schedule_work(&led_dat->work); | ||
81 | } | ||
82 | |||
83 | static int __devinit create_lt3593_led(const struct gpio_led *template, | ||
84 | struct lt3593_led_data *led_dat, struct device *parent) | ||
85 | { | ||
86 | int ret, state; | ||
87 | |||
88 | /* skip leds on GPIOs that aren't available */ | ||
89 | if (!gpio_is_valid(template->gpio)) { | ||
90 | printk(KERN_INFO "%s: skipping unavailable LT3593 LED at gpio %d (%s)\n", | ||
91 | KBUILD_MODNAME, template->gpio, template->name); | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | ret = gpio_request(template->gpio, template->name); | ||
96 | if (ret < 0) | ||
97 | return ret; | ||
98 | |||
99 | led_dat->cdev.name = template->name; | ||
100 | led_dat->cdev.default_trigger = template->default_trigger; | ||
101 | led_dat->gpio = template->gpio; | ||
102 | |||
103 | led_dat->cdev.brightness_set = lt3593_led_set; | ||
104 | |||
105 | state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); | ||
106 | led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; | ||
107 | |||
108 | if (!template->retain_state_suspended) | ||
109 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
110 | |||
111 | ret = gpio_direction_output(led_dat->gpio, state); | ||
112 | if (ret < 0) | ||
113 | goto err; | ||
114 | |||
115 | INIT_WORK(&led_dat->work, lt3593_led_work); | ||
116 | |||
117 | ret = led_classdev_register(parent, &led_dat->cdev); | ||
118 | if (ret < 0) | ||
119 | goto err; | ||
120 | |||
121 | printk(KERN_INFO "%s: registered LT3593 LED '%s' at GPIO %d\n", | ||
122 | KBUILD_MODNAME, template->name, template->gpio); | ||
123 | |||
124 | return 0; | ||
125 | |||
126 | err: | ||
127 | gpio_free(led_dat->gpio); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | static void delete_lt3593_led(struct lt3593_led_data *led) | ||
132 | { | ||
133 | if (!gpio_is_valid(led->gpio)) | ||
134 | return; | ||
135 | |||
136 | led_classdev_unregister(&led->cdev); | ||
137 | cancel_work_sync(&led->work); | ||
138 | gpio_free(led->gpio); | ||
139 | } | ||
140 | |||
141 | static int __devinit lt3593_led_probe(struct platform_device *pdev) | ||
142 | { | ||
143 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | ||
144 | struct lt3593_led_data *leds_data; | ||
145 | int i, ret = 0; | ||
146 | |||
147 | if (!pdata) | ||
148 | return -EBUSY; | ||
149 | |||
150 | leds_data = kzalloc(sizeof(struct lt3593_led_data) * pdata->num_leds, | ||
151 | GFP_KERNEL); | ||
152 | if (!leds_data) | ||
153 | return -ENOMEM; | ||
154 | |||
155 | for (i = 0; i < pdata->num_leds; i++) { | ||
156 | ret = create_lt3593_led(&pdata->leds[i], &leds_data[i], | ||
157 | &pdev->dev); | ||
158 | if (ret < 0) | ||
159 | goto err; | ||
160 | } | ||
161 | |||
162 | platform_set_drvdata(pdev, leds_data); | ||
163 | |||
164 | return 0; | ||
165 | |||
166 | err: | ||
167 | for (i = i - 1; i >= 0; i--) | ||
168 | delete_lt3593_led(&leds_data[i]); | ||
169 | |||
170 | kfree(leds_data); | ||
171 | |||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | static int __devexit lt3593_led_remove(struct platform_device *pdev) | ||
176 | { | ||
177 | int i; | ||
178 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | ||
179 | struct lt3593_led_data *leds_data; | ||
180 | |||
181 | leds_data = platform_get_drvdata(pdev); | ||
182 | |||
183 | for (i = 0; i < pdata->num_leds; i++) | ||
184 | delete_lt3593_led(&leds_data[i]); | ||
185 | |||
186 | kfree(leds_data); | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static struct platform_driver lt3593_led_driver = { | ||
192 | .probe = lt3593_led_probe, | ||
193 | .remove = __devexit_p(lt3593_led_remove), | ||
194 | .driver = { | ||
195 | .name = "leds-lt3593", | ||
196 | .owner = THIS_MODULE, | ||
197 | }, | ||
198 | }; | ||
199 | |||
200 | MODULE_ALIAS("platform:leds-lt3593"); | ||
201 | |||
202 | static int __init lt3593_led_init(void) | ||
203 | { | ||
204 | return platform_driver_register(<3593_led_driver); | ||
205 | } | ||
206 | |||
207 | static void __exit lt3593_led_exit(void) | ||
208 | { | ||
209 | platform_driver_unregister(<3593_led_driver); | ||
210 | } | ||
211 | |||
212 | module_init(lt3593_led_init); | ||
213 | module_exit(lt3593_led_exit); | ||
214 | |||
215 | MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); | ||
216 | MODULE_DESCRIPTION("LED driver for LT3593 controllers"); | ||
217 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index cdfdc8714e10..88b1dd091cfb 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c | |||
@@ -27,7 +27,6 @@ struct led_pwm_data { | |||
27 | struct pwm_device *pwm; | 27 | struct pwm_device *pwm; |
28 | unsigned int active_low; | 28 | unsigned int active_low; |
29 | unsigned int period; | 29 | unsigned int period; |
30 | unsigned int max_brightness; | ||
31 | }; | 30 | }; |
32 | 31 | ||
33 | static void led_pwm_set(struct led_classdev *led_cdev, | 32 | static void led_pwm_set(struct led_classdev *led_cdev, |
@@ -35,7 +34,7 @@ static void led_pwm_set(struct led_classdev *led_cdev, | |||
35 | { | 34 | { |
36 | struct led_pwm_data *led_dat = | 35 | struct led_pwm_data *led_dat = |
37 | container_of(led_cdev, struct led_pwm_data, cdev); | 36 | container_of(led_cdev, struct led_pwm_data, cdev); |
38 | unsigned int max = led_dat->max_brightness; | 37 | unsigned int max = led_dat->cdev.max_brightness; |
39 | unsigned int period = led_dat->period; | 38 | unsigned int period = led_dat->period; |
40 | 39 | ||
41 | if (brightness == 0) { | 40 | if (brightness == 0) { |
@@ -77,10 +76,10 @@ static int led_pwm_probe(struct platform_device *pdev) | |||
77 | led_dat->cdev.name = cur_led->name; | 76 | led_dat->cdev.name = cur_led->name; |
78 | led_dat->cdev.default_trigger = cur_led->default_trigger; | 77 | led_dat->cdev.default_trigger = cur_led->default_trigger; |
79 | led_dat->active_low = cur_led->active_low; | 78 | led_dat->active_low = cur_led->active_low; |
80 | led_dat->max_brightness = cur_led->max_brightness; | ||
81 | led_dat->period = cur_led->pwm_period_ns; | 79 | led_dat->period = cur_led->pwm_period_ns; |
82 | led_dat->cdev.brightness_set = led_pwm_set; | 80 | led_dat->cdev.brightness_set = led_pwm_set; |
83 | led_dat->cdev.brightness = LED_OFF; | 81 | led_dat->cdev.brightness = LED_OFF; |
82 | led_dat->cdev.max_brightness = cur_led->max_brightness; | ||
84 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | 83 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
85 | 84 | ||
86 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); | 85 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); |
diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c new file mode 100644 index 000000000000..7f00de3ef922 --- /dev/null +++ b/drivers/leds/leds-regulator.c | |||
@@ -0,0 +1,242 @@ | |||
1 | /* | ||
2 | * leds-regulator.c - LED class driver for regulator driven LEDs. | ||
3 | * | ||
4 | * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it> | ||
5 | * | ||
6 | * Inspired by leds-wm8350 driver. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/workqueue.h> | ||
17 | #include <linux/leds.h> | ||
18 | #include <linux/leds-regulator.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/regulator/consumer.h> | ||
21 | |||
22 | #define to_regulator_led(led_cdev) \ | ||
23 | container_of(led_cdev, struct regulator_led, cdev) | ||
24 | |||
25 | struct regulator_led { | ||
26 | struct led_classdev cdev; | ||
27 | enum led_brightness value; | ||
28 | int enabled; | ||
29 | struct mutex mutex; | ||
30 | struct work_struct work; | ||
31 | |||
32 | struct regulator *vcc; | ||
33 | }; | ||
34 | |||
35 | static inline int led_regulator_get_max_brightness(struct regulator *supply) | ||
36 | { | ||
37 | int ret; | ||
38 | int voltage = regulator_list_voltage(supply, 0); | ||
39 | |||
40 | if (voltage <= 0) | ||
41 | return 1; | ||
42 | |||
43 | /* even if regulator can't change voltages, | ||
44 | * we still assume it can change status | ||
45 | * and the LED can be turned on and off. | ||
46 | */ | ||
47 | ret = regulator_set_voltage(supply, voltage, voltage); | ||
48 | if (ret < 0) | ||
49 | return 1; | ||
50 | |||
51 | return regulator_count_voltages(supply); | ||
52 | } | ||
53 | |||
54 | static int led_regulator_get_voltage(struct regulator *supply, | ||
55 | enum led_brightness brightness) | ||
56 | { | ||
57 | if (brightness == 0) | ||
58 | return -EINVAL; | ||
59 | |||
60 | return regulator_list_voltage(supply, brightness - 1); | ||
61 | } | ||
62 | |||
63 | |||
64 | static void regulator_led_enable(struct regulator_led *led) | ||
65 | { | ||
66 | int ret; | ||
67 | |||
68 | if (led->enabled) | ||
69 | return; | ||
70 | |||
71 | ret = regulator_enable(led->vcc); | ||
72 | if (ret != 0) { | ||
73 | dev_err(led->cdev.dev, "Failed to enable vcc: %d\n", ret); | ||
74 | return; | ||
75 | } | ||
76 | |||
77 | led->enabled = 1; | ||
78 | } | ||
79 | |||
80 | static void regulator_led_disable(struct regulator_led *led) | ||
81 | { | ||
82 | int ret; | ||
83 | |||
84 | if (!led->enabled) | ||
85 | return; | ||
86 | |||
87 | ret = regulator_disable(led->vcc); | ||
88 | if (ret != 0) { | ||
89 | dev_err(led->cdev.dev, "Failed to disable vcc: %d\n", ret); | ||
90 | return; | ||
91 | } | ||
92 | |||
93 | led->enabled = 0; | ||
94 | } | ||
95 | |||
96 | static void regulator_led_set_value(struct regulator_led *led) | ||
97 | { | ||
98 | int voltage; | ||
99 | int ret; | ||
100 | |||
101 | mutex_lock(&led->mutex); | ||
102 | |||
103 | if (led->value == LED_OFF) { | ||
104 | regulator_led_disable(led); | ||
105 | goto out; | ||
106 | } | ||
107 | |||
108 | if (led->cdev.max_brightness > 1) { | ||
109 | voltage = led_regulator_get_voltage(led->vcc, led->value); | ||
110 | dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n", | ||
111 | led->value, voltage); | ||
112 | |||
113 | ret = regulator_set_voltage(led->vcc, voltage, voltage); | ||
114 | if (ret != 0) | ||
115 | dev_err(led->cdev.dev, "Failed to set voltage %d: %d\n", | ||
116 | voltage, ret); | ||
117 | } | ||
118 | |||
119 | regulator_led_enable(led); | ||
120 | |||
121 | out: | ||
122 | mutex_unlock(&led->mutex); | ||
123 | } | ||
124 | |||
125 | static void led_work(struct work_struct *work) | ||
126 | { | ||
127 | struct regulator_led *led; | ||
128 | |||
129 | led = container_of(work, struct regulator_led, work); | ||
130 | regulator_led_set_value(led); | ||
131 | } | ||
132 | |||
133 | static void regulator_led_brightness_set(struct led_classdev *led_cdev, | ||
134 | enum led_brightness value) | ||
135 | { | ||
136 | struct regulator_led *led = to_regulator_led(led_cdev); | ||
137 | |||
138 | led->value = value; | ||
139 | schedule_work(&led->work); | ||
140 | } | ||
141 | |||
142 | static int __devinit regulator_led_probe(struct platform_device *pdev) | ||
143 | { | ||
144 | struct led_regulator_platform_data *pdata = pdev->dev.platform_data; | ||
145 | struct regulator_led *led; | ||
146 | struct regulator *vcc; | ||
147 | int ret = 0; | ||
148 | |||
149 | if (pdata == NULL) { | ||
150 | dev_err(&pdev->dev, "no platform data\n"); | ||
151 | return -ENODEV; | ||
152 | } | ||
153 | |||
154 | vcc = regulator_get_exclusive(&pdev->dev, "vled"); | ||
155 | if (IS_ERR(vcc)) { | ||
156 | dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name); | ||
157 | return PTR_ERR(vcc); | ||
158 | } | ||
159 | |||
160 | led = kzalloc(sizeof(*led), GFP_KERNEL); | ||
161 | if (led == NULL) { | ||
162 | ret = -ENOMEM; | ||
163 | goto err_vcc; | ||
164 | } | ||
165 | |||
166 | led->cdev.max_brightness = led_regulator_get_max_brightness(vcc); | ||
167 | if (pdata->brightness > led->cdev.max_brightness) { | ||
168 | dev_err(&pdev->dev, "Invalid default brightness %d\n", | ||
169 | pdata->brightness); | ||
170 | ret = -EINVAL; | ||
171 | goto err_led; | ||
172 | } | ||
173 | led->value = pdata->brightness; | ||
174 | |||
175 | led->cdev.brightness_set = regulator_led_brightness_set; | ||
176 | led->cdev.name = pdata->name; | ||
177 | led->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
178 | led->vcc = vcc; | ||
179 | |||
180 | mutex_init(&led->mutex); | ||
181 | INIT_WORK(&led->work, led_work); | ||
182 | |||
183 | platform_set_drvdata(pdev, led); | ||
184 | |||
185 | ret = led_classdev_register(&pdev->dev, &led->cdev); | ||
186 | if (ret < 0) { | ||
187 | cancel_work_sync(&led->work); | ||
188 | goto err_led; | ||
189 | } | ||
190 | |||
191 | /* to expose the default value to userspace */ | ||
192 | led->cdev.brightness = led->value; | ||
193 | |||
194 | /* Set the default led status */ | ||
195 | regulator_led_set_value(led); | ||
196 | |||
197 | return 0; | ||
198 | |||
199 | err_led: | ||
200 | kfree(led); | ||
201 | err_vcc: | ||
202 | regulator_put(vcc); | ||
203 | return ret; | ||
204 | } | ||
205 | |||
206 | static int __devexit regulator_led_remove(struct platform_device *pdev) | ||
207 | { | ||
208 | struct regulator_led *led = platform_get_drvdata(pdev); | ||
209 | |||
210 | led_classdev_unregister(&led->cdev); | ||
211 | cancel_work_sync(&led->work); | ||
212 | regulator_led_disable(led); | ||
213 | regulator_put(led->vcc); | ||
214 | kfree(led); | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static struct platform_driver regulator_led_driver = { | ||
219 | .driver = { | ||
220 | .name = "leds-regulator", | ||
221 | .owner = THIS_MODULE, | ||
222 | }, | ||
223 | .probe = regulator_led_probe, | ||
224 | .remove = __devexit_p(regulator_led_remove), | ||
225 | }; | ||
226 | |||
227 | static int __init regulator_led_init(void) | ||
228 | { | ||
229 | return platform_driver_register(®ulator_led_driver); | ||
230 | } | ||
231 | module_init(regulator_led_init); | ||
232 | |||
233 | static void __exit regulator_led_exit(void) | ||
234 | { | ||
235 | platform_driver_unregister(®ulator_led_driver); | ||
236 | } | ||
237 | module_exit(regulator_led_exit); | ||
238 | |||
239 | MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); | ||
240 | MODULE_DESCRIPTION("Regulator driven LED driver"); | ||
241 | MODULE_LICENSE("GPL"); | ||
242 | MODULE_ALIAS("platform:leds-regulator"); | ||
diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c new file mode 100644 index 000000000000..97f04984c1ca --- /dev/null +++ b/drivers/leds/leds-ss4200.c | |||
@@ -0,0 +1,556 @@ | |||
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; | ||
348 | u32 gc = 0; | ||
349 | |||
350 | status = pci_enable_device(dev); | ||
351 | if (status) { | ||
352 | dev_err(&dev->dev, "pci_enable_device failed\n"); | ||
353 | return -EIO; | ||
354 | } | ||
355 | |||
356 | nas_gpio_pci_dev = dev; | ||
357 | status = pci_read_config_dword(dev, PMBASE, &g_pm_io_base); | ||
358 | if (status) | ||
359 | goto out; | ||
360 | g_pm_io_base &= 0x00000ff80; | ||
361 | |||
362 | status = pci_read_config_dword(dev, GPIO_CTRL, &gc); | ||
363 | if (!(GPIO_EN & gc)) { | ||
364 | status = -EEXIST; | ||
365 | dev_info(&dev->dev, | ||
366 | "ERROR: The LPC GPIO Block has not been enabled.\n"); | ||
367 | goto out; | ||
368 | } | ||
369 | |||
370 | status = pci_read_config_dword(dev, GPIO_BASE, &nas_gpio_io_base); | ||
371 | if (0 > status) { | ||
372 | dev_info(&dev->dev, "Unable to read GPIOBASE.\n"); | ||
373 | goto out; | ||
374 | } | ||
375 | dev_dbg(&dev->dev, ": GPIOBASE = 0x%08x\n", nas_gpio_io_base); | ||
376 | nas_gpio_io_base &= 0x00000ffc0; | ||
377 | |||
378 | /* | ||
379 | * Insure that we have exclusive access to the GPIO I/O address range. | ||
380 | */ | ||
381 | gp_gpio_resource = request_region(nas_gpio_io_base, ICH7_GPIO_SIZE, | ||
382 | KBUILD_MODNAME); | ||
383 | if (NULL == gp_gpio_resource) { | ||
384 | dev_info(&dev->dev, | ||
385 | "ERROR Unable to register GPIO I/O addresses.\n"); | ||
386 | status = -1; | ||
387 | goto out; | ||
388 | } | ||
389 | |||
390 | /* | ||
391 | * Initialize the GPIO for NAS/Home Server Use | ||
392 | */ | ||
393 | ich7_gpio_init(&dev->dev); | ||
394 | |||
395 | out: | ||
396 | if (status) { | ||
397 | ich7_lpc_cleanup(&dev->dev); | ||
398 | pci_disable_device(dev); | ||
399 | } | ||
400 | return status; | ||
401 | } | ||
402 | |||
403 | static void ich7_lpc_remove(struct pci_dev *dev) | ||
404 | { | ||
405 | ich7_lpc_cleanup(&dev->dev); | ||
406 | pci_disable_device(dev); | ||
407 | } | ||
408 | |||
409 | /* | ||
410 | * pci_driver structure passed to the PCI modules | ||
411 | */ | ||
412 | static struct pci_driver nas_gpio_pci_driver = { | ||
413 | .name = KBUILD_MODNAME, | ||
414 | .id_table = ich7_lpc_pci_id, | ||
415 | .probe = ich7_lpc_probe, | ||
416 | .remove = ich7_lpc_remove, | ||
417 | }; | ||
418 | |||
419 | static struct led_classdev *get_classdev_for_led_nr(int nr) | ||
420 | { | ||
421 | struct nasgpio_led *nas_led = &nasgpio_leds[nr]; | ||
422 | struct led_classdev *led = &nas_led->led_cdev; | ||
423 | return led; | ||
424 | } | ||
425 | |||
426 | |||
427 | static void set_power_light_amber_noblink(void) | ||
428 | { | ||
429 | struct nasgpio_led *amber = get_led_named("power:amber:power"); | ||
430 | struct nasgpio_led *blue = get_led_named("power:blue:power"); | ||
431 | |||
432 | if (!amber || !blue) | ||
433 | return; | ||
434 | /* | ||
435 | * LED_OFF implies disabling future blinking | ||
436 | */ | ||
437 | pr_debug("setting blue off and amber on\n"); | ||
438 | |||
439 | nasgpio_led_set_brightness(&blue->led_cdev, LED_OFF); | ||
440 | nasgpio_led_set_brightness(&amber->led_cdev, LED_FULL); | ||
441 | } | ||
442 | |||
443 | static ssize_t nas_led_blink_show(struct device *dev, | ||
444 | struct device_attribute *attr, char *buf) | ||
445 | { | ||
446 | struct led_classdev *led = dev_get_drvdata(dev); | ||
447 | int blinking = 0; | ||
448 | if (nasgpio_led_get_attr(led, GPO_BLINK)) | ||
449 | blinking = 1; | ||
450 | return sprintf(buf, "%u\n", blinking); | ||
451 | } | ||
452 | |||
453 | static ssize_t nas_led_blink_store(struct device *dev, | ||
454 | struct device_attribute *attr, | ||
455 | const char *buf, size_t size) | ||
456 | { | ||
457 | int ret; | ||
458 | struct led_classdev *led = dev_get_drvdata(dev); | ||
459 | unsigned long blink_state; | ||
460 | |||
461 | ret = strict_strtoul(buf, 10, &blink_state); | ||
462 | if (ret) | ||
463 | return ret; | ||
464 | |||
465 | nasgpio_led_set_attr(led, GPO_BLINK, blink_state); | ||
466 | |||
467 | return size; | ||
468 | } | ||
469 | |||
470 | static DEVICE_ATTR(blink, 0644, nas_led_blink_show, nas_led_blink_store); | ||
471 | |||
472 | static int register_nasgpio_led(int led_nr) | ||
473 | { | ||
474 | int ret; | ||
475 | struct nasgpio_led *nas_led = &nasgpio_leds[led_nr]; | ||
476 | struct led_classdev *led = get_classdev_for_led_nr(led_nr); | ||
477 | |||
478 | led->name = nas_led->name; | ||
479 | led->brightness = LED_OFF; | ||
480 | if (nasgpio_led_get_attr(led, GP_LVL)) | ||
481 | led->brightness = LED_FULL; | ||
482 | led->brightness_set = nasgpio_led_set_brightness; | ||
483 | led->blink_set = nasgpio_led_set_blink; | ||
484 | ret = led_classdev_register(&nas_gpio_pci_dev->dev, led); | ||
485 | if (ret) | ||
486 | return ret; | ||
487 | ret = device_create_file(led->dev, &dev_attr_blink); | ||
488 | if (ret) | ||
489 | led_classdev_unregister(led); | ||
490 | return ret; | ||
491 | } | ||
492 | |||
493 | static void unregister_nasgpio_led(int led_nr) | ||
494 | { | ||
495 | struct led_classdev *led = get_classdev_for_led_nr(led_nr); | ||
496 | led_classdev_unregister(led); | ||
497 | device_remove_file(led->dev, &dev_attr_blink); | ||
498 | } | ||
499 | /* | ||
500 | * module load/initialization | ||
501 | */ | ||
502 | static int __init nas_gpio_init(void) | ||
503 | { | ||
504 | int i; | ||
505 | int ret = 0; | ||
506 | int nr_devices = 0; | ||
507 | |||
508 | nr_devices = dmi_check_system(nas_led_whitelist); | ||
509 | if (nodetect) { | ||
510 | pr_info("skipping hardware autodetection\n"); | ||
511 | pr_info("Please send 'dmidecode' output to dave@sr71.net\n"); | ||
512 | nr_devices++; | ||
513 | } | ||
514 | |||
515 | if (nr_devices <= 0) { | ||
516 | pr_info("no LED devices found\n"); | ||
517 | return -ENODEV; | ||
518 | } | ||
519 | |||
520 | pr_info("registering PCI driver\n"); | ||
521 | ret = pci_register_driver(&nas_gpio_pci_driver); | ||
522 | if (ret) | ||
523 | return ret; | ||
524 | for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) { | ||
525 | ret = register_nasgpio_led(i); | ||
526 | if (ret) | ||
527 | goto out_err; | ||
528 | } | ||
529 | /* | ||
530 | * When the system powers on, the BIOS leaves the power | ||
531 | * light blue and blinking. This will turn it solid | ||
532 | * amber once the driver is loaded. | ||
533 | */ | ||
534 | set_power_light_amber_noblink(); | ||
535 | return 0; | ||
536 | out_err: | ||
537 | for (; i >= 0; i--) | ||
538 | unregister_nasgpio_led(i); | ||
539 | pci_unregister_driver(&nas_gpio_pci_driver); | ||
540 | return ret; | ||
541 | } | ||
542 | |||
543 | /* | ||
544 | * module unload | ||
545 | */ | ||
546 | static void __exit nas_gpio_exit(void) | ||
547 | { | ||
548 | int i; | ||
549 | pr_info("Unregistering driver\n"); | ||
550 | for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) | ||
551 | unregister_nasgpio_led(i); | ||
552 | pci_unregister_driver(&nas_gpio_pci_driver); | ||
553 | } | ||
554 | |||
555 | module_init(nas_gpio_init); | ||
556 | module_exit(nas_gpio_exit); | ||