2 * leds-netxbig.c - Driver for the 2Big and 5Big Network series LEDs
4 * Copyright (C) 2010 LaCie
6 * Author: Simon Guinot <sguinot@lacie.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <linux/module.h>
24 #include <linux/init.h>
25 #include <linux/irq.h>
26 #include <linux/slab.h>
27 #include <linux/spinlock.h>
28 #include <linux/platform_device.h>
29 #include <linux/gpio.h>
30 #include <linux/leds.h>
31 #include <mach/leds-netxbig.h>
37 static DEFINE_SPINLOCK(gpio_ext_lock
);
39 static void gpio_ext_set_addr(struct netxbig_gpio_ext
*gpio_ext
, int addr
)
43 for (pin
= 0; pin
< gpio_ext
->num_addr
; pin
++)
44 gpio_set_value(gpio_ext
->addr
[pin
], (addr
>> pin
) & 1);
47 static void gpio_ext_set_data(struct netxbig_gpio_ext
*gpio_ext
, int data
)
51 for (pin
= 0; pin
< gpio_ext
->num_data
; pin
++)
52 gpio_set_value(gpio_ext
->data
[pin
], (data
>> pin
) & 1);
55 static void gpio_ext_enable_select(struct netxbig_gpio_ext
*gpio_ext
)
57 /* Enable select is done on the raising edge. */
58 gpio_set_value(gpio_ext
->enable
, 0);
59 gpio_set_value(gpio_ext
->enable
, 1);
62 static void gpio_ext_set_value(struct netxbig_gpio_ext
*gpio_ext
,
67 spin_lock_irqsave(&gpio_ext_lock
, flags
);
68 gpio_ext_set_addr(gpio_ext
, addr
);
69 gpio_ext_set_data(gpio_ext
, value
);
70 gpio_ext_enable_select(gpio_ext
);
71 spin_unlock_irqrestore(&gpio_ext_lock
, flags
);
74 static int __devinit
gpio_ext_init(struct netxbig_gpio_ext
*gpio_ext
)
79 if (unlikely(!gpio_ext
))
82 /* Configure address GPIOs. */
83 for (i
= 0; i
< gpio_ext
->num_addr
; i
++) {
84 err
= gpio_request(gpio_ext
->addr
[i
], "GPIO extension addr");
87 err
= gpio_direction_output(gpio_ext
->addr
[i
], 0);
89 gpio_free(gpio_ext
->addr
[i
]);
93 /* Configure data GPIOs. */
94 for (i
= 0; i
< gpio_ext
->num_data
; i
++) {
95 err
= gpio_request(gpio_ext
->data
[i
], "GPIO extension data");
98 err
= gpio_direction_output(gpio_ext
->data
[i
], 0);
100 gpio_free(gpio_ext
->data
[i
]);
104 /* Configure "enable select" GPIO. */
105 err
= gpio_request(gpio_ext
->enable
, "GPIO extension enable");
108 err
= gpio_direction_output(gpio_ext
->enable
, 0);
110 gpio_free(gpio_ext
->enable
);
117 for (i
= i
- 1; i
>= 0; i
--)
118 gpio_free(gpio_ext
->data
[i
]);
119 i
= gpio_ext
->num_addr
;
121 for (i
= i
- 1; i
>= 0; i
--)
122 gpio_free(gpio_ext
->addr
[i
]);
127 static void __devexit
gpio_ext_free(struct netxbig_gpio_ext
*gpio_ext
)
131 gpio_free(gpio_ext
->enable
);
132 for (i
= gpio_ext
->num_addr
- 1; i
>= 0; i
--)
133 gpio_free(gpio_ext
->addr
[i
]);
134 for (i
= gpio_ext
->num_data
- 1; i
>= 0; i
--)
135 gpio_free(gpio_ext
->data
[i
]);
142 struct netxbig_led_data
{
143 struct netxbig_gpio_ext
*gpio_ext
;
144 struct led_classdev cdev
;
149 struct netxbig_led_timer
*timer
;
151 enum netxbig_led_mode mode
;
156 static int netxbig_led_get_timer_mode(enum netxbig_led_mode
*mode
,
157 unsigned long delay_on
,
158 unsigned long delay_off
,
159 struct netxbig_led_timer
*timer
,
164 for (i
= 0; i
< num_timer
; i
++) {
165 if (timer
[i
].delay_on
== delay_on
&&
166 timer
[i
].delay_off
== delay_off
) {
167 *mode
= timer
[i
].mode
;
174 static int netxbig_led_blink_set(struct led_classdev
*led_cdev
,
175 unsigned long *delay_on
,
176 unsigned long *delay_off
)
178 struct netxbig_led_data
*led_dat
=
179 container_of(led_cdev
, struct netxbig_led_data
, cdev
);
180 enum netxbig_led_mode mode
;
184 /* Look for a LED mode with the requested timer frequency. */
185 ret
= netxbig_led_get_timer_mode(&mode
, *delay_on
, *delay_off
,
186 led_dat
->timer
, led_dat
->num_timer
);
190 mode_val
= led_dat
->mode_val
[mode
];
191 if (mode_val
== NETXBIG_LED_INVALID_MODE
)
194 spin_lock_irq(&led_dat
->lock
);
196 gpio_ext_set_value(led_dat
->gpio_ext
, led_dat
->mode_addr
, mode_val
);
197 led_dat
->mode
= mode
;
199 spin_unlock_irq(&led_dat
->lock
);
204 static void netxbig_led_set(struct led_classdev
*led_cdev
,
205 enum led_brightness value
)
207 struct netxbig_led_data
*led_dat
=
208 container_of(led_cdev
, struct netxbig_led_data
, cdev
);
209 enum netxbig_led_mode mode
;
210 int mode_val
, bright_val
;
211 int set_brightness
= 1;
214 spin_lock_irqsave(&led_dat
->lock
, flags
);
216 if (value
== LED_OFF
) {
217 mode
= NETXBIG_LED_OFF
;
221 mode
= NETXBIG_LED_SATA
;
222 else if (led_dat
->mode
== NETXBIG_LED_OFF
)
223 mode
= NETXBIG_LED_ON
;
224 else /* Keep 'timer' mode. */
225 mode
= led_dat
->mode
;
227 mode_val
= led_dat
->mode_val
[mode
];
229 gpio_ext_set_value(led_dat
->gpio_ext
, led_dat
->mode_addr
, mode_val
);
230 led_dat
->mode
= mode
;
232 * Note that the brightness register is shared between all the
233 * SATA LEDs. So, change the brightness setting for a single
234 * SATA LED will affect all the others.
236 if (set_brightness
) {
237 bright_val
= DIV_ROUND_UP(value
* led_dat
->bright_max
,
239 gpio_ext_set_value(led_dat
->gpio_ext
,
240 led_dat
->bright_addr
, bright_val
);
243 spin_unlock_irqrestore(&led_dat
->lock
, flags
);
246 static ssize_t
netxbig_led_sata_store(struct device
*dev
,
247 struct device_attribute
*attr
,
248 const char *buff
, size_t count
)
250 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
251 struct netxbig_led_data
*led_dat
=
252 container_of(led_cdev
, struct netxbig_led_data
, cdev
);
253 unsigned long enable
;
254 enum netxbig_led_mode mode
;
258 ret
= strict_strtoul(buff
, 10, &enable
);
264 spin_lock_irq(&led_dat
->lock
);
266 if (led_dat
->sata
== enable
) {
271 if (led_dat
->mode
!= NETXBIG_LED_ON
&&
272 led_dat
->mode
!= NETXBIG_LED_SATA
)
273 mode
= led_dat
->mode
; /* Keep modes 'off' and 'timer'. */
275 mode
= NETXBIG_LED_SATA
;
277 mode
= NETXBIG_LED_ON
;
279 mode_val
= led_dat
->mode_val
[mode
];
280 if (mode_val
== NETXBIG_LED_INVALID_MODE
) {
285 gpio_ext_set_value(led_dat
->gpio_ext
, led_dat
->mode_addr
, mode_val
);
286 led_dat
->mode
= mode
;
287 led_dat
->sata
= enable
;
292 spin_unlock_irq(&led_dat
->lock
);
297 static ssize_t
netxbig_led_sata_show(struct device
*dev
,
298 struct device_attribute
*attr
, char *buf
)
300 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
301 struct netxbig_led_data
*led_dat
=
302 container_of(led_cdev
, struct netxbig_led_data
, cdev
);
304 return sprintf(buf
, "%d\n", led_dat
->sata
);
307 static DEVICE_ATTR(sata
, 0644, netxbig_led_sata_show
, netxbig_led_sata_store
);
309 static void __devexit
delete_netxbig_led(struct netxbig_led_data
*led_dat
)
311 if (led_dat
->mode_val
[NETXBIG_LED_SATA
] != NETXBIG_LED_INVALID_MODE
)
312 device_remove_file(led_dat
->cdev
.dev
, &dev_attr_sata
);
313 led_classdev_unregister(&led_dat
->cdev
);
317 create_netxbig_led(struct platform_device
*pdev
,
318 struct netxbig_led_data
*led_dat
,
319 const struct netxbig_led
*template)
321 struct netxbig_led_platform_data
*pdata
= pdev
->dev
.platform_data
;
324 spin_lock_init(&led_dat
->lock
);
325 led_dat
->gpio_ext
= pdata
->gpio_ext
;
326 led_dat
->cdev
.name
= template->name
;
327 led_dat
->cdev
.default_trigger
= template->default_trigger
;
328 led_dat
->cdev
.blink_set
= netxbig_led_blink_set
;
329 led_dat
->cdev
.brightness_set
= netxbig_led_set
;
331 * Because the GPIO extension bus don't allow to read registers
332 * value, there is no way to probe the LED initial state.
333 * So, the initial sysfs LED value for the "brightness" and "sata"
334 * attributes are inconsistent.
336 * Note that the initial LED state can't be reconfigured.
337 * The reason is that the LED behaviour must stay uniform during
338 * the whole boot process (bootloader+linux).
341 led_dat
->cdev
.brightness
= LED_OFF
;
342 led_dat
->cdev
.flags
|= LED_CORE_SUSPENDRESUME
;
343 led_dat
->mode_addr
= template->mode_addr
;
344 led_dat
->mode_val
= template->mode_val
;
345 led_dat
->bright_addr
= template->bright_addr
;
346 led_dat
->bright_max
= (1 << pdata
->gpio_ext
->num_data
) - 1;
347 led_dat
->timer
= pdata
->timer
;
348 led_dat
->num_timer
= pdata
->num_timer
;
350 ret
= led_classdev_register(&pdev
->dev
, &led_dat
->cdev
);
355 * If available, expose the SATA activity blink capability through
356 * a "sata" sysfs attribute.
358 if (led_dat
->mode_val
[NETXBIG_LED_SATA
] != NETXBIG_LED_INVALID_MODE
) {
359 ret
= device_create_file(led_dat
->cdev
.dev
, &dev_attr_sata
);
361 led_classdev_unregister(&led_dat
->cdev
);
367 static int __devinit
netxbig_led_probe(struct platform_device
*pdev
)
369 struct netxbig_led_platform_data
*pdata
= pdev
->dev
.platform_data
;
370 struct netxbig_led_data
*leds_data
;
377 leds_data
= kzalloc(sizeof(struct netxbig_led_data
) * pdata
->num_leds
,
382 ret
= gpio_ext_init(pdata
->gpio_ext
);
386 for (i
= 0; i
< pdata
->num_leds
; i
++) {
387 ret
= create_netxbig_led(pdev
, &leds_data
[i
], &pdata
->leds
[i
]);
392 platform_set_drvdata(pdev
, leds_data
);
397 for (i
= i
- 1; i
>= 0; i
--)
398 delete_netxbig_led(&leds_data
[i
]);
400 gpio_ext_free(pdata
->gpio_ext
);
407 static int __devexit
netxbig_led_remove(struct platform_device
*pdev
)
409 struct netxbig_led_platform_data
*pdata
= pdev
->dev
.platform_data
;
410 struct netxbig_led_data
*leds_data
;
413 leds_data
= platform_get_drvdata(pdev
);
415 for (i
= 0; i
< pdata
->num_leds
; i
++)
416 delete_netxbig_led(&leds_data
[i
]);
418 gpio_ext_free(pdata
->gpio_ext
);
424 static struct platform_driver netxbig_led_driver
= {
425 .probe
= netxbig_led_probe
,
426 .remove
= __devexit_p(netxbig_led_remove
),
428 .name
= "leds-netxbig",
429 .owner
= THIS_MODULE
,
432 MODULE_ALIAS("platform:leds-netxbig");
434 static int __init
netxbig_led_init(void)
436 return platform_driver_register(&netxbig_led_driver
);
439 static void __exit
netxbig_led_exit(void)
441 platform_driver_unregister(&netxbig_led_driver
);
444 module_init(netxbig_led_init
);
445 module_exit(netxbig_led_exit
);
447 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
448 MODULE_DESCRIPTION("LED driver for LaCie xBig Network boards");
449 MODULE_LICENSE("GPL");