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 <linux/module.h>
32 #include <mach/leds-ns2.h>
35 * The Network Space v2 dual-GPIO LED is wired to a CPLD and can blink in
36 * relation with the SATA activity. This capability is exposed through the
37 * "sata" sysfs attribute.
39 * The following array detail the different LED registers and the combination
40 * of their possible values:
42 * cmd_led | slow_led | /SATA active | LED state
47 * 0 | 0 | 0 | blink (rate 300ms)
56 struct ns2_led_mode_value
{
57 enum ns2_led_modes mode
;
62 static struct ns2_led_mode_value ns2_led_modval
[] = {
63 { NS_V2_LED_OFF
, 1, 0 },
64 { NS_V2_LED_ON
, 0, 1 },
65 { NS_V2_LED_ON
, 1, 1 },
66 { NS_V2_LED_SATA
, 0, 0 },
70 struct led_classdev cdev
;
73 unsigned char sata
; /* True when SATA mode active. */
74 rwlock_t rw_lock
; /* Lock GPIOs. */
77 static int ns2_led_get_mode(struct ns2_led_data
*led_dat
,
78 enum ns2_led_modes
*mode
)
85 read_lock_irq(&led_dat
->rw_lock
);
87 cmd_level
= gpio_get_value(led_dat
->cmd
);
88 slow_level
= gpio_get_value(led_dat
->slow
);
90 for (i
= 0; i
< ARRAY_SIZE(ns2_led_modval
); i
++) {
91 if (cmd_level
== ns2_led_modval
[i
].cmd_level
&&
92 slow_level
== ns2_led_modval
[i
].slow_level
) {
93 *mode
= ns2_led_modval
[i
].mode
;
99 read_unlock_irq(&led_dat
->rw_lock
);
104 static void ns2_led_set_mode(struct ns2_led_data
*led_dat
,
105 enum ns2_led_modes mode
)
110 write_lock_irqsave(&led_dat
->rw_lock
, flags
);
112 for (i
= 0; i
< ARRAY_SIZE(ns2_led_modval
); i
++) {
113 if (mode
== ns2_led_modval
[i
].mode
) {
114 gpio_set_value(led_dat
->cmd
,
115 ns2_led_modval
[i
].cmd_level
);
116 gpio_set_value(led_dat
->slow
,
117 ns2_led_modval
[i
].slow_level
);
121 write_unlock_irqrestore(&led_dat
->rw_lock
, flags
);
124 static void ns2_led_set(struct led_classdev
*led_cdev
,
125 enum led_brightness value
)
127 struct ns2_led_data
*led_dat
=
128 container_of(led_cdev
, struct ns2_led_data
, cdev
);
129 enum ns2_led_modes mode
;
131 if (value
== LED_OFF
)
132 mode
= NS_V2_LED_OFF
;
133 else if (led_dat
->sata
)
134 mode
= NS_V2_LED_SATA
;
138 ns2_led_set_mode(led_dat
, mode
);
141 static ssize_t
ns2_led_sata_store(struct device
*dev
,
142 struct device_attribute
*attr
,
143 const char *buff
, size_t count
)
145 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
146 struct ns2_led_data
*led_dat
=
147 container_of(led_cdev
, struct ns2_led_data
, cdev
);
149 unsigned long enable
;
150 enum ns2_led_modes mode
;
152 ret
= strict_strtoul(buff
, 10, &enable
);
158 if (led_dat
->sata
== enable
)
161 ret
= ns2_led_get_mode(led_dat
, &mode
);
165 if (enable
&& mode
== NS_V2_LED_ON
)
166 ns2_led_set_mode(led_dat
, NS_V2_LED_SATA
);
167 if (!enable
&& mode
== NS_V2_LED_SATA
)
168 ns2_led_set_mode(led_dat
, NS_V2_LED_ON
);
170 led_dat
->sata
= enable
;
175 static ssize_t
ns2_led_sata_show(struct device
*dev
,
176 struct device_attribute
*attr
, char *buf
)
178 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
179 struct ns2_led_data
*led_dat
=
180 container_of(led_cdev
, struct ns2_led_data
, cdev
);
182 return sprintf(buf
, "%d\n", led_dat
->sata
);
185 static DEVICE_ATTR(sata
, 0644, ns2_led_sata_show
, ns2_led_sata_store
);
188 create_ns2_led(struct platform_device
*pdev
, struct ns2_led_data
*led_dat
,
189 const struct ns2_led
*template)
192 enum ns2_led_modes mode
;
194 ret
= gpio_request(template->cmd
, template->name
);
196 ret
= gpio_direction_output(template->cmd
,
197 gpio_get_value(template->cmd
));
199 gpio_free(template->cmd
);
202 dev_err(&pdev
->dev
, "%s: failed to setup command GPIO\n",
206 ret
= gpio_request(template->slow
, template->name
);
208 ret
= gpio_direction_output(template->slow
,
209 gpio_get_value(template->slow
));
211 gpio_free(template->slow
);
214 dev_err(&pdev
->dev
, "%s: failed to setup slow GPIO\n",
219 rwlock_init(&led_dat
->rw_lock
);
221 led_dat
->cdev
.name
= template->name
;
222 led_dat
->cdev
.default_trigger
= template->default_trigger
;
223 led_dat
->cdev
.blink_set
= NULL
;
224 led_dat
->cdev
.brightness_set
= ns2_led_set
;
225 led_dat
->cdev
.flags
|= LED_CORE_SUSPENDRESUME
;
226 led_dat
->cmd
= template->cmd
;
227 led_dat
->slow
= template->slow
;
229 ret
= ns2_led_get_mode(led_dat
, &mode
);
233 /* Set LED initial state. */
234 led_dat
->sata
= (mode
== NS_V2_LED_SATA
) ? 1 : 0;
235 led_dat
->cdev
.brightness
=
236 (mode
== NS_V2_LED_OFF
) ? LED_OFF
: LED_FULL
;
238 ret
= led_classdev_register(&pdev
->dev
, &led_dat
->cdev
);
242 ret
= device_create_file(led_dat
->cdev
.dev
, &dev_attr_sata
);
249 led_classdev_unregister(&led_dat
->cdev
);
251 gpio_free(led_dat
->slow
);
253 gpio_free(led_dat
->cmd
);
258 static void __devexit
delete_ns2_led(struct ns2_led_data
*led_dat
)
260 device_remove_file(led_dat
->cdev
.dev
, &dev_attr_sata
);
261 led_classdev_unregister(&led_dat
->cdev
);
262 gpio_free(led_dat
->cmd
);
263 gpio_free(led_dat
->slow
);
266 static int __devinit
ns2_led_probe(struct platform_device
*pdev
)
268 struct ns2_led_platform_data
*pdata
= pdev
->dev
.platform_data
;
269 struct ns2_led_data
*leds_data
;
276 leds_data
= kzalloc(sizeof(struct ns2_led_data
) *
277 pdata
->num_leds
, GFP_KERNEL
);
281 for (i
= 0; i
< pdata
->num_leds
; i
++) {
282 ret
= create_ns2_led(pdev
, &leds_data
[i
], &pdata
->leds
[i
]);
288 platform_set_drvdata(pdev
, leds_data
);
293 for (i
= i
- 1; i
>= 0; i
--)
294 delete_ns2_led(&leds_data
[i
]);
301 static int __devexit
ns2_led_remove(struct platform_device
*pdev
)
304 struct ns2_led_platform_data
*pdata
= pdev
->dev
.platform_data
;
305 struct ns2_led_data
*leds_data
;
307 leds_data
= platform_get_drvdata(pdev
);
309 for (i
= 0; i
< pdata
->num_leds
; i
++)
310 delete_ns2_led(&leds_data
[i
]);
313 platform_set_drvdata(pdev
, NULL
);
318 static struct platform_driver ns2_led_driver
= {
319 .probe
= ns2_led_probe
,
320 .remove
= __devexit_p(ns2_led_remove
),
323 .owner
= THIS_MODULE
,
326 MODULE_ALIAS("platform:leds-ns2");
328 static int __init
ns2_led_init(void)
330 return platform_driver_register(&ns2_led_driver
);
333 static void __exit
ns2_led_exit(void)
335 platform_driver_unregister(&ns2_led_driver
);
338 module_init(ns2_led_init
);
339 module_exit(ns2_led_exit
);
341 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
342 MODULE_DESCRIPTION("Network Space v2 LED driver");
343 MODULE_LICENSE("GPL");