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_irq(&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_irq(&led_dat
->rw_lock
);
103 static void ns2_led_set_mode(struct ns2_led_data
*led_dat
,
104 enum ns2_led_modes mode
)
109 write_lock_irqsave(&led_dat
->rw_lock
, flags
);
111 for (i
= 0; i
< ARRAY_SIZE(ns2_led_modval
); i
++) {
112 if (mode
== ns2_led_modval
[i
].mode
) {
113 gpio_set_value(led_dat
->cmd
,
114 ns2_led_modval
[i
].cmd_level
);
115 gpio_set_value(led_dat
->slow
,
116 ns2_led_modval
[i
].slow_level
);
120 write_unlock_irqrestore(&led_dat
->rw_lock
, flags
);
123 static void ns2_led_set(struct led_classdev
*led_cdev
,
124 enum led_brightness value
)
126 struct ns2_led_data
*led_dat
=
127 container_of(led_cdev
, struct ns2_led_data
, cdev
);
128 enum ns2_led_modes mode
;
130 if (value
== LED_OFF
)
131 mode
= NS_V2_LED_OFF
;
132 else if (led_dat
->sata
)
133 mode
= NS_V2_LED_SATA
;
137 ns2_led_set_mode(led_dat
, mode
);
140 static ssize_t
ns2_led_sata_store(struct device
*dev
,
141 struct device_attribute
*attr
,
142 const char *buff
, size_t count
)
144 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
145 struct ns2_led_data
*led_dat
=
146 container_of(led_cdev
, struct ns2_led_data
, cdev
);
148 unsigned long enable
;
149 enum ns2_led_modes mode
;
151 ret
= strict_strtoul(buff
, 10, &enable
);
157 if (led_dat
->sata
== enable
)
160 ret
= ns2_led_get_mode(led_dat
, &mode
);
164 if (enable
&& mode
== NS_V2_LED_ON
)
165 ns2_led_set_mode(led_dat
, NS_V2_LED_SATA
);
166 if (!enable
&& mode
== NS_V2_LED_SATA
)
167 ns2_led_set_mode(led_dat
, NS_V2_LED_ON
);
169 led_dat
->sata
= enable
;
174 static ssize_t
ns2_led_sata_show(struct device
*dev
,
175 struct device_attribute
*attr
, char *buf
)
177 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
178 struct ns2_led_data
*led_dat
=
179 container_of(led_cdev
, struct ns2_led_data
, cdev
);
181 return sprintf(buf
, "%d\n", led_dat
->sata
);
184 static DEVICE_ATTR(sata
, 0644, ns2_led_sata_show
, ns2_led_sata_store
);
187 create_ns2_led(struct platform_device
*pdev
, struct ns2_led_data
*led_dat
,
188 const struct ns2_led
*template)
191 enum ns2_led_modes mode
;
193 ret
= gpio_request(template->cmd
, template->name
);
195 ret
= gpio_direction_output(template->cmd
,
196 gpio_get_value(template->cmd
));
198 gpio_free(template->cmd
);
201 dev_err(&pdev
->dev
, "%s: failed to setup command GPIO\n",
205 ret
= gpio_request(template->slow
, template->name
);
207 ret
= gpio_direction_output(template->slow
,
208 gpio_get_value(template->slow
));
210 gpio_free(template->slow
);
213 dev_err(&pdev
->dev
, "%s: failed to setup slow GPIO\n",
218 rwlock_init(&led_dat
->rw_lock
);
220 led_dat
->cdev
.name
= template->name
;
221 led_dat
->cdev
.default_trigger
= template->default_trigger
;
222 led_dat
->cdev
.blink_set
= NULL
;
223 led_dat
->cdev
.brightness_set
= ns2_led_set
;
224 led_dat
->cdev
.flags
|= LED_CORE_SUSPENDRESUME
;
225 led_dat
->cmd
= template->cmd
;
226 led_dat
->slow
= template->slow
;
228 ret
= ns2_led_get_mode(led_dat
, &mode
);
232 /* Set LED initial state. */
233 led_dat
->sata
= (mode
== NS_V2_LED_SATA
) ? 1 : 0;
234 led_dat
->cdev
.brightness
=
235 (mode
== NS_V2_LED_OFF
) ? LED_OFF
: LED_FULL
;
237 ret
= led_classdev_register(&pdev
->dev
, &led_dat
->cdev
);
241 ret
= device_create_file(led_dat
->cdev
.dev
, &dev_attr_sata
);
248 led_classdev_unregister(&led_dat
->cdev
);
250 gpio_free(led_dat
->slow
);
252 gpio_free(led_dat
->cmd
);
257 static void __devexit
delete_ns2_led(struct ns2_led_data
*led_dat
)
259 device_remove_file(led_dat
->cdev
.dev
, &dev_attr_sata
);
260 led_classdev_unregister(&led_dat
->cdev
);
261 gpio_free(led_dat
->cmd
);
262 gpio_free(led_dat
->slow
);
265 static int __devinit
ns2_led_probe(struct platform_device
*pdev
)
267 struct ns2_led_platform_data
*pdata
= pdev
->dev
.platform_data
;
268 struct ns2_led_data
*leds_data
;
275 leds_data
= kzalloc(sizeof(struct ns2_led_data
) *
276 pdata
->num_leds
, GFP_KERNEL
);
280 for (i
= 0; i
< pdata
->num_leds
; i
++) {
281 ret
= create_ns2_led(pdev
, &leds_data
[i
], &pdata
->leds
[i
]);
287 platform_set_drvdata(pdev
, leds_data
);
292 for (i
= i
- 1; i
>= 0; i
--)
293 delete_ns2_led(&leds_data
[i
]);
300 static int __devexit
ns2_led_remove(struct platform_device
*pdev
)
303 struct ns2_led_platform_data
*pdata
= pdev
->dev
.platform_data
;
304 struct ns2_led_data
*leds_data
;
306 leds_data
= platform_get_drvdata(pdev
);
308 for (i
= 0; i
< pdata
->num_leds
; i
++)
309 delete_ns2_led(&leds_data
[i
]);
312 platform_set_drvdata(pdev
, NULL
);
317 static struct platform_driver ns2_led_driver
= {
318 .probe
= ns2_led_probe
,
319 .remove
= __devexit_p(ns2_led_remove
),
322 .owner
= THIS_MODULE
,
325 MODULE_ALIAS("platform:leds-ns2");
327 static int __init
ns2_led_init(void)
329 return platform_driver_register(&ns2_led_driver
);
332 static void __exit
ns2_led_exit(void)
334 platform_driver_unregister(&ns2_led_driver
);
337 module_init(ns2_led_init
);
338 module_exit(ns2_led_exit
);
340 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
341 MODULE_DESCRIPTION("Network Space v2 LED driver");
342 MODULE_LICENSE("GPL");