2 * leds-ns2.c - Driver for the Network Space v2 (and parents) dual-GPIO LED
4 * Copyright (C) 2010 LaCie
6 * Author: Simon Guinot <sguinot@lacie.com>
8 * Based on leds-gpio.c by Raphael Assenat <raph@8d.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include <linux/kernel.h>
26 #include <linux/init.h>
27 #include <linux/platform_device.h>
28 #include <linux/slab.h>
29 #include <linux/gpio.h>
30 #include <linux/leds.h>
31 #include <mach/leds-ns2.h>
34 * The Network Space v2 dual-GPIO LED is wired to a CPLD and can blink in
35 * relation with the SATA activity. This capability is exposed through the
36 * "sata" sysfs attribute.
38 * The following array detail the different LED registers and the combination
39 * of their possible values:
41 * cmd_led | slow_led | /SATA active | LED state
46 * 0 | 0 | 0 | blink (rate 300ms)
55 struct ns2_led_mode_value
{
56 enum ns2_led_modes mode
;
61 static struct ns2_led_mode_value ns2_led_modval
[] = {
62 { NS_V2_LED_OFF
, 1, 0 },
63 { NS_V2_LED_ON
, 0, 1 },
64 { NS_V2_LED_ON
, 1, 1 },
65 { NS_V2_LED_SATA
, 0, 0 },
69 struct led_classdev cdev
;
72 unsigned char sata
; /* True when SATA mode active. */
73 rwlock_t rw_lock
; /* Lock GPIOs. */
76 static int ns2_led_get_mode(struct ns2_led_data
*led_dat
,
77 enum ns2_led_modes
*mode
)
84 read_lock(&led_dat
->rw_lock
);
86 cmd_level
= gpio_get_value(led_dat
->cmd
);
87 slow_level
= gpio_get_value(led_dat
->slow
);
89 for (i
= 0; i
< ARRAY_SIZE(ns2_led_modval
); i
++) {
90 if (cmd_level
== ns2_led_modval
[i
].cmd_level
&&
91 slow_level
== ns2_led_modval
[i
].slow_level
) {
92 *mode
= ns2_led_modval
[i
].mode
;
98 read_unlock(&led_dat
->rw_lock
);
103 static void ns2_led_set_mode(struct ns2_led_data
*led_dat
,
104 enum ns2_led_modes mode
)
108 write_lock(&led_dat
->rw_lock
);
110 for (i
= 0; i
< ARRAY_SIZE(ns2_led_modval
); i
++) {
111 if (mode
== ns2_led_modval
[i
].mode
) {
112 gpio_set_value(led_dat
->cmd
,
113 ns2_led_modval
[i
].cmd_level
);
114 gpio_set_value(led_dat
->slow
,
115 ns2_led_modval
[i
].slow_level
);
119 write_unlock(&led_dat
->rw_lock
);
122 static void ns2_led_set(struct led_classdev
*led_cdev
,
123 enum led_brightness value
)
125 struct ns2_led_data
*led_dat
=
126 container_of(led_cdev
, struct ns2_led_data
, cdev
);
127 enum ns2_led_modes mode
;
129 if (value
== LED_OFF
)
130 mode
= NS_V2_LED_OFF
;
131 else if (led_dat
->sata
)
132 mode
= NS_V2_LED_SATA
;
136 ns2_led_set_mode(led_dat
, mode
);
139 static ssize_t
ns2_led_sata_store(struct device
*dev
,
140 struct device_attribute
*attr
,
141 const char *buff
, size_t count
)
144 unsigned long enable
;
145 enum ns2_led_modes mode
;
146 struct ns2_led_data
*led_dat
= dev_get_drvdata(dev
);
148 ret
= strict_strtoul(buff
, 10, &enable
);
154 if (led_dat
->sata
== enable
)
157 ret
= ns2_led_get_mode(led_dat
, &mode
);
161 if (enable
&& mode
== NS_V2_LED_ON
)
162 ns2_led_set_mode(led_dat
, NS_V2_LED_SATA
);
163 if (!enable
&& mode
== NS_V2_LED_SATA
)
164 ns2_led_set_mode(led_dat
, NS_V2_LED_ON
);
166 led_dat
->sata
= enable
;
171 static ssize_t
ns2_led_sata_show(struct device
*dev
,
172 struct device_attribute
*attr
, char *buf
)
174 struct ns2_led_data
*led_dat
= dev_get_drvdata(dev
);
176 return sprintf(buf
, "%d\n", led_dat
->sata
);
179 static DEVICE_ATTR(sata
, 0644, ns2_led_sata_show
, ns2_led_sata_store
);
182 create_ns2_led(struct platform_device
*pdev
, struct ns2_led_data
*led_dat
,
183 const struct ns2_led
*template)
186 enum ns2_led_modes mode
;
188 ret
= gpio_request(template->cmd
, template->name
);
190 ret
= gpio_direction_output(template->cmd
,
191 gpio_get_value(template->cmd
));
193 gpio_free(template->cmd
);
196 dev_err(&pdev
->dev
, "%s: failed to setup command GPIO\n",
200 ret
= gpio_request(template->slow
, template->name
);
202 ret
= gpio_direction_output(template->slow
,
203 gpio_get_value(template->slow
));
205 gpio_free(template->slow
);
208 dev_err(&pdev
->dev
, "%s: failed to setup slow GPIO\n",
213 rwlock_init(&led_dat
->rw_lock
);
215 led_dat
->cdev
.name
= template->name
;
216 led_dat
->cdev
.default_trigger
= template->default_trigger
;
217 led_dat
->cdev
.blink_set
= NULL
;
218 led_dat
->cdev
.brightness_set
= ns2_led_set
;
219 led_dat
->cdev
.flags
|= LED_CORE_SUSPENDRESUME
;
220 led_dat
->cmd
= template->cmd
;
221 led_dat
->slow
= template->slow
;
223 ret
= ns2_led_get_mode(led_dat
, &mode
);
227 /* Set LED initial state. */
228 led_dat
->sata
= (mode
== NS_V2_LED_SATA
) ? 1 : 0;
229 led_dat
->cdev
.brightness
=
230 (mode
== NS_V2_LED_OFF
) ? LED_OFF
: LED_FULL
;
232 ret
= led_classdev_register(&pdev
->dev
, &led_dat
->cdev
);
236 dev_set_drvdata(led_dat
->cdev
.dev
, led_dat
);
237 ret
= device_create_file(led_dat
->cdev
.dev
, &dev_attr_sata
);
244 led_classdev_unregister(&led_dat
->cdev
);
246 gpio_free(led_dat
->slow
);
248 gpio_free(led_dat
->cmd
);
253 static void __devexit
delete_ns2_led(struct ns2_led_data
*led_dat
)
255 device_remove_file(led_dat
->cdev
.dev
, &dev_attr_sata
);
256 led_classdev_unregister(&led_dat
->cdev
);
257 gpio_free(led_dat
->cmd
);
258 gpio_free(led_dat
->slow
);
261 static int __devinit
ns2_led_probe(struct platform_device
*pdev
)
263 struct ns2_led_platform_data
*pdata
= pdev
->dev
.platform_data
;
264 struct ns2_led_data
*leds_data
;
271 leds_data
= kzalloc(sizeof(struct ns2_led_data
) *
272 pdata
->num_leds
, GFP_KERNEL
);
276 for (i
= 0; i
< pdata
->num_leds
; i
++) {
277 ret
= create_ns2_led(pdev
, &leds_data
[i
], &pdata
->leds
[i
]);
283 platform_set_drvdata(pdev
, leds_data
);
288 for (i
= i
- 1; i
>= 0; i
--)
289 delete_ns2_led(&leds_data
[i
]);
296 static int __devexit
ns2_led_remove(struct platform_device
*pdev
)
299 struct ns2_led_platform_data
*pdata
= pdev
->dev
.platform_data
;
300 struct ns2_led_data
*leds_data
;
302 leds_data
= platform_get_drvdata(pdev
);
304 for (i
= 0; i
< pdata
->num_leds
; i
++)
305 delete_ns2_led(&leds_data
[i
]);
308 platform_set_drvdata(pdev
, NULL
);
313 static struct platform_driver ns2_led_driver
= {
314 .probe
= ns2_led_probe
,
315 .remove
= __devexit_p(ns2_led_remove
),
318 .owner
= THIS_MODULE
,
321 MODULE_ALIAS("platform:leds-ns2");
323 static int __init
ns2_led_init(void)
325 return platform_driver_register(&ns2_led_driver
);
328 static void __exit
ns2_led_exit(void)
330 platform_driver_unregister(&ns2_led_driver
);
333 module_init(ns2_led_init
);
334 module_exit(ns2_led_exit
);
336 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
337 MODULE_DESCRIPTION("Network Space v2 LED driver");
338 MODULE_LICENSE("GPL");