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/irq.h>
25 #include <linux/slab.h>
26 #include <linux/spinlock.h>
27 #include <linux/platform_device.h>
28 #include <linux/gpio.h>
29 #include <linux/of_gpio.h>
30 #include <linux/leds.h>
31 #include <linux/platform_data/leds-kirkwood-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 gpio_ext_init(struct platform_device
*pdev
,
75 struct netxbig_gpio_ext
*gpio_ext
)
80 if (unlikely(!gpio_ext
))
83 /* Configure address GPIOs. */
84 for (i
= 0; i
< gpio_ext
->num_addr
; i
++) {
85 err
= devm_gpio_request_one(&pdev
->dev
, gpio_ext
->addr
[i
],
87 "GPIO extension addr");
91 /* Configure data GPIOs. */
92 for (i
= 0; i
< gpio_ext
->num_data
; i
++) {
93 err
= devm_gpio_request_one(&pdev
->dev
, gpio_ext
->data
[i
],
95 "GPIO extension data");
99 /* Configure "enable select" GPIO. */
100 err
= devm_gpio_request_one(&pdev
->dev
, gpio_ext
->enable
,
102 "GPIO extension enable");
113 struct netxbig_led_data
{
114 struct netxbig_gpio_ext
*gpio_ext
;
115 struct led_classdev cdev
;
119 struct netxbig_led_timer
*timer
;
121 enum netxbig_led_mode mode
;
126 static int netxbig_led_get_timer_mode(enum netxbig_led_mode
*mode
,
127 unsigned long delay_on
,
128 unsigned long delay_off
,
129 struct netxbig_led_timer
*timer
,
134 for (i
= 0; i
< num_timer
; i
++) {
135 if (timer
[i
].delay_on
== delay_on
&&
136 timer
[i
].delay_off
== delay_off
) {
137 *mode
= timer
[i
].mode
;
144 static int netxbig_led_blink_set(struct led_classdev
*led_cdev
,
145 unsigned long *delay_on
,
146 unsigned long *delay_off
)
148 struct netxbig_led_data
*led_dat
=
149 container_of(led_cdev
, struct netxbig_led_data
, cdev
);
150 enum netxbig_led_mode mode
;
154 /* Look for a LED mode with the requested timer frequency. */
155 ret
= netxbig_led_get_timer_mode(&mode
, *delay_on
, *delay_off
,
156 led_dat
->timer
, led_dat
->num_timer
);
160 mode_val
= led_dat
->mode_val
[mode
];
161 if (mode_val
== NETXBIG_LED_INVALID_MODE
)
164 spin_lock_irq(&led_dat
->lock
);
166 gpio_ext_set_value(led_dat
->gpio_ext
, led_dat
->mode_addr
, mode_val
);
167 led_dat
->mode
= mode
;
169 spin_unlock_irq(&led_dat
->lock
);
174 static void netxbig_led_set(struct led_classdev
*led_cdev
,
175 enum led_brightness value
)
177 struct netxbig_led_data
*led_dat
=
178 container_of(led_cdev
, struct netxbig_led_data
, cdev
);
179 enum netxbig_led_mode mode
;
181 int set_brightness
= 1;
184 spin_lock_irqsave(&led_dat
->lock
, flags
);
186 if (value
== LED_OFF
) {
187 mode
= NETXBIG_LED_OFF
;
191 mode
= NETXBIG_LED_SATA
;
192 else if (led_dat
->mode
== NETXBIG_LED_OFF
)
193 mode
= NETXBIG_LED_ON
;
194 else /* Keep 'timer' mode. */
195 mode
= led_dat
->mode
;
197 mode_val
= led_dat
->mode_val
[mode
];
199 gpio_ext_set_value(led_dat
->gpio_ext
, led_dat
->mode_addr
, mode_val
);
200 led_dat
->mode
= mode
;
202 * Note that the brightness register is shared between all the
203 * SATA LEDs. So, change the brightness setting for a single
204 * SATA LED will affect all the others.
207 gpio_ext_set_value(led_dat
->gpio_ext
,
208 led_dat
->bright_addr
, value
);
210 spin_unlock_irqrestore(&led_dat
->lock
, flags
);
213 static ssize_t
netxbig_led_sata_store(struct device
*dev
,
214 struct device_attribute
*attr
,
215 const char *buff
, size_t count
)
217 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
218 struct netxbig_led_data
*led_dat
=
219 container_of(led_cdev
, struct netxbig_led_data
, cdev
);
220 unsigned long enable
;
221 enum netxbig_led_mode mode
;
225 ret
= kstrtoul(buff
, 10, &enable
);
231 spin_lock_irq(&led_dat
->lock
);
233 if (led_dat
->sata
== enable
) {
238 if (led_dat
->mode
!= NETXBIG_LED_ON
&&
239 led_dat
->mode
!= NETXBIG_LED_SATA
)
240 mode
= led_dat
->mode
; /* Keep modes 'off' and 'timer'. */
242 mode
= NETXBIG_LED_SATA
;
244 mode
= NETXBIG_LED_ON
;
246 mode_val
= led_dat
->mode_val
[mode
];
247 if (mode_val
== NETXBIG_LED_INVALID_MODE
) {
252 gpio_ext_set_value(led_dat
->gpio_ext
, led_dat
->mode_addr
, mode_val
);
253 led_dat
->mode
= mode
;
254 led_dat
->sata
= enable
;
259 spin_unlock_irq(&led_dat
->lock
);
264 static ssize_t
netxbig_led_sata_show(struct device
*dev
,
265 struct device_attribute
*attr
, char *buf
)
267 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
268 struct netxbig_led_data
*led_dat
=
269 container_of(led_cdev
, struct netxbig_led_data
, cdev
);
271 return sprintf(buf
, "%d\n", led_dat
->sata
);
274 static DEVICE_ATTR(sata
, 0644, netxbig_led_sata_show
, netxbig_led_sata_store
);
276 static struct attribute
*netxbig_led_attrs
[] = {
280 ATTRIBUTE_GROUPS(netxbig_led
);
282 static int create_netxbig_led(struct platform_device
*pdev
,
283 struct netxbig_led_platform_data
*pdata
,
284 struct netxbig_led_data
*led_dat
,
285 const struct netxbig_led
*template)
287 spin_lock_init(&led_dat
->lock
);
288 led_dat
->gpio_ext
= pdata
->gpio_ext
;
289 led_dat
->cdev
.name
= template->name
;
290 led_dat
->cdev
.default_trigger
= template->default_trigger
;
291 led_dat
->cdev
.blink_set
= netxbig_led_blink_set
;
292 led_dat
->cdev
.brightness_set
= netxbig_led_set
;
294 * Because the GPIO extension bus don't allow to read registers
295 * value, there is no way to probe the LED initial state.
296 * So, the initial sysfs LED value for the "brightness" and "sata"
297 * attributes are inconsistent.
299 * Note that the initial LED state can't be reconfigured.
300 * The reason is that the LED behaviour must stay uniform during
301 * the whole boot process (bootloader+linux).
304 led_dat
->cdev
.brightness
= LED_OFF
;
305 led_dat
->cdev
.max_brightness
= template->bright_max
;
306 led_dat
->cdev
.flags
|= LED_CORE_SUSPENDRESUME
;
307 led_dat
->mode_addr
= template->mode_addr
;
308 led_dat
->mode_val
= template->mode_val
;
309 led_dat
->bright_addr
= template->bright_addr
;
310 led_dat
->timer
= pdata
->timer
;
311 led_dat
->num_timer
= pdata
->num_timer
;
313 * If available, expose the SATA activity blink capability through
314 * a "sata" sysfs attribute.
316 if (led_dat
->mode_val
[NETXBIG_LED_SATA
] != NETXBIG_LED_INVALID_MODE
)
317 led_dat
->cdev
.groups
= netxbig_led_groups
;
319 return devm_led_classdev_register(&pdev
->dev
, &led_dat
->cdev
);
322 #ifdef CONFIG_OF_GPIO
323 static int gpio_ext_get_of_pdata(struct device
*dev
, struct device_node
*np
,
324 struct netxbig_gpio_ext
*gpio_ext
)
327 int num_addr
, num_data
;
331 ret
= of_gpio_named_count(np
, "addr-gpios");
334 "Failed to count GPIOs in DT property addr-gpios\n");
338 addr
= devm_kzalloc(dev
, num_addr
* sizeof(*addr
), GFP_KERNEL
);
342 for (i
= 0; i
< num_addr
; i
++) {
343 ret
= of_get_named_gpio(np
, "addr-gpios", i
);
348 gpio_ext
->addr
= addr
;
349 gpio_ext
->num_addr
= num_addr
;
351 ret
= of_gpio_named_count(np
, "data-gpios");
354 "Failed to count GPIOs in DT property data-gpios\n");
358 data
= devm_kzalloc(dev
, num_data
* sizeof(*data
), GFP_KERNEL
);
362 for (i
= 0; i
< num_data
; i
++) {
363 ret
= of_get_named_gpio(np
, "data-gpios", i
);
368 gpio_ext
->data
= data
;
369 gpio_ext
->num_data
= num_data
;
371 ret
= of_get_named_gpio(np
, "enable-gpio", 0);
374 "Failed to get GPIO from DT property enable-gpio\n");
377 gpio_ext
->enable
= ret
;
382 static int netxbig_leds_get_of_pdata(struct device
*dev
,
383 struct netxbig_led_platform_data
*pdata
)
385 struct device_node
*np
= dev
->of_node
;
386 struct device_node
*gpio_ext_np
;
387 struct device_node
*child
;
388 struct netxbig_gpio_ext
*gpio_ext
;
389 struct netxbig_led_timer
*timers
;
390 struct netxbig_led
*leds
, *led
;
397 gpio_ext_np
= of_parse_phandle(np
, "gpio-ext", 0);
399 dev_err(dev
, "Failed to get DT handle gpio-ext\n");
403 gpio_ext
= devm_kzalloc(dev
, sizeof(*gpio_ext
), GFP_KERNEL
);
406 ret
= gpio_ext_get_of_pdata(dev
, gpio_ext_np
, gpio_ext
);
409 of_node_put(gpio_ext_np
);
410 pdata
->gpio_ext
= gpio_ext
;
412 /* Timers (optional) */
413 ret
= of_property_count_u32_elems(np
, "timers");
417 num_timers
= ret
/ 3;
418 timers
= devm_kzalloc(dev
, num_timers
* sizeof(*timers
),
422 for (i
= 0; i
< num_timers
; i
++) {
425 of_property_read_u32_index(np
, "timers", 3 * i
,
427 if (timers
[i
].mode
>= NETXBIG_LED_MODE_NUM
)
429 of_property_read_u32_index(np
, "timers",
431 timers
[i
].delay_on
= tmp
;
432 of_property_read_u32_index(np
, "timers",
434 timers
[i
].delay_off
= tmp
;
436 pdata
->timer
= timers
;
437 pdata
->num_timer
= num_timers
;
441 num_leds
= of_get_child_count(np
);
443 dev_err(dev
, "No LED subnodes found in DT\n");
447 leds
= devm_kzalloc(dev
, num_leds
* sizeof(*leds
), GFP_KERNEL
);
452 for_each_child_of_node(np
, child
) {
457 ret
= of_property_read_u32(child
, "mode-addr",
462 ret
= of_property_read_u32(child
, "bright-addr",
467 ret
= of_property_read_u32(child
, "max-brightness",
474 NETXBIG_LED_MODE_NUM
* sizeof(*mode_val
),
481 for (i
= 0; i
< NETXBIG_LED_MODE_NUM
; i
++)
482 mode_val
[i
] = NETXBIG_LED_INVALID_MODE
;
484 ret
= of_property_count_u32_elems(child
, "mode-val");
485 if (ret
< 0 || ret
% 2) {
490 if (num_modes
> NETXBIG_LED_MODE_NUM
) {
495 for (i
= 0; i
< num_modes
; i
++) {
499 of_property_read_u32_index(child
,
500 "mode-val", 2 * i
, &mode
);
501 of_property_read_u32_index(child
,
502 "mode-val", 2 * i
+ 1, &val
);
503 if (mode
>= NETXBIG_LED_MODE_NUM
) {
507 mode_val
[mode
] = val
;
509 led
->mode_val
= mode_val
;
511 if (!of_property_read_string(child
, "label", &string
))
514 led
->name
= child
->name
;
516 if (!of_property_read_string(child
,
517 "linux,default-trigger", &string
))
518 led
->default_trigger
= string
;
524 pdata
->num_leds
= num_leds
;
533 static const struct of_device_id of_netxbig_leds_match
[] = {
534 { .compatible
= "lacie,netxbig-leds", },
539 netxbig_leds_get_of_pdata(struct device
*dev
,
540 struct netxbig_led_platform_data
*pdata
)
544 #endif /* CONFIG_OF_GPIO */
546 static int netxbig_led_probe(struct platform_device
*pdev
)
548 struct netxbig_led_platform_data
*pdata
= dev_get_platdata(&pdev
->dev
);
549 struct netxbig_led_data
*leds_data
;
554 pdata
= devm_kzalloc(&pdev
->dev
, sizeof(*pdata
), GFP_KERNEL
);
557 ret
= netxbig_leds_get_of_pdata(&pdev
->dev
, pdata
);
562 leds_data
= devm_kzalloc(&pdev
->dev
,
563 pdata
->num_leds
* sizeof(*leds_data
),
568 ret
= gpio_ext_init(pdev
, pdata
->gpio_ext
);
572 for (i
= 0; i
< pdata
->num_leds
; i
++) {
573 ret
= create_netxbig_led(pdev
, pdata
,
574 &leds_data
[i
], &pdata
->leds
[i
]);
582 static struct platform_driver netxbig_led_driver
= {
583 .probe
= netxbig_led_probe
,
585 .name
= "leds-netxbig",
586 .of_match_table
= of_match_ptr(of_netxbig_leds_match
),
590 module_platform_driver(netxbig_led_driver
);
592 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
593 MODULE_DESCRIPTION("LED driver for LaCie xBig Network boards");
594 MODULE_LICENSE("GPL");
595 MODULE_ALIAS("platform:leds-netxbig");