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 <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 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_one(gpio_ext
->addr
[i
], GPIOF_OUT_INIT_LOW
,
85 "GPIO extension addr");
89 /* Configure data GPIOs. */
90 for (i
= 0; i
< gpio_ext
->num_data
; i
++) {
91 err
= gpio_request_one(gpio_ext
->data
[i
], GPIOF_OUT_INIT_LOW
,
92 "GPIO extension data");
96 /* Configure "enable select" GPIO. */
97 err
= gpio_request_one(gpio_ext
->enable
, GPIOF_OUT_INIT_LOW
,
98 "GPIO extension enable");
105 for (i
= i
- 1; i
>= 0; i
--)
106 gpio_free(gpio_ext
->data
[i
]);
107 i
= gpio_ext
->num_addr
;
109 for (i
= i
- 1; i
>= 0; i
--)
110 gpio_free(gpio_ext
->addr
[i
]);
115 static void gpio_ext_free(struct netxbig_gpio_ext
*gpio_ext
)
119 gpio_free(gpio_ext
->enable
);
120 for (i
= gpio_ext
->num_addr
- 1; i
>= 0; i
--)
121 gpio_free(gpio_ext
->addr
[i
]);
122 for (i
= gpio_ext
->num_data
- 1; i
>= 0; i
--)
123 gpio_free(gpio_ext
->data
[i
]);
130 struct netxbig_led_data
{
131 struct netxbig_gpio_ext
*gpio_ext
;
132 struct led_classdev cdev
;
137 struct netxbig_led_timer
*timer
;
139 enum netxbig_led_mode mode
;
144 static int netxbig_led_get_timer_mode(enum netxbig_led_mode
*mode
,
145 unsigned long delay_on
,
146 unsigned long delay_off
,
147 struct netxbig_led_timer
*timer
,
152 for (i
= 0; i
< num_timer
; i
++) {
153 if (timer
[i
].delay_on
== delay_on
&&
154 timer
[i
].delay_off
== delay_off
) {
155 *mode
= timer
[i
].mode
;
162 static int netxbig_led_blink_set(struct led_classdev
*led_cdev
,
163 unsigned long *delay_on
,
164 unsigned long *delay_off
)
166 struct netxbig_led_data
*led_dat
=
167 container_of(led_cdev
, struct netxbig_led_data
, cdev
);
168 enum netxbig_led_mode mode
;
172 /* Look for a LED mode with the requested timer frequency. */
173 ret
= netxbig_led_get_timer_mode(&mode
, *delay_on
, *delay_off
,
174 led_dat
->timer
, led_dat
->num_timer
);
178 mode_val
= led_dat
->mode_val
[mode
];
179 if (mode_val
== NETXBIG_LED_INVALID_MODE
)
182 spin_lock_irq(&led_dat
->lock
);
184 gpio_ext_set_value(led_dat
->gpio_ext
, led_dat
->mode_addr
, mode_val
);
185 led_dat
->mode
= mode
;
187 spin_unlock_irq(&led_dat
->lock
);
192 static void netxbig_led_set(struct led_classdev
*led_cdev
,
193 enum led_brightness value
)
195 struct netxbig_led_data
*led_dat
=
196 container_of(led_cdev
, struct netxbig_led_data
, cdev
);
197 enum netxbig_led_mode mode
;
198 int mode_val
, bright_val
;
199 int set_brightness
= 1;
202 spin_lock_irqsave(&led_dat
->lock
, flags
);
204 if (value
== LED_OFF
) {
205 mode
= NETXBIG_LED_OFF
;
209 mode
= NETXBIG_LED_SATA
;
210 else if (led_dat
->mode
== NETXBIG_LED_OFF
)
211 mode
= NETXBIG_LED_ON
;
212 else /* Keep 'timer' mode. */
213 mode
= led_dat
->mode
;
215 mode_val
= led_dat
->mode_val
[mode
];
217 gpio_ext_set_value(led_dat
->gpio_ext
, led_dat
->mode_addr
, mode_val
);
218 led_dat
->mode
= mode
;
220 * Note that the brightness register is shared between all the
221 * SATA LEDs. So, change the brightness setting for a single
222 * SATA LED will affect all the others.
224 if (set_brightness
) {
225 bright_val
= DIV_ROUND_UP(value
* led_dat
->bright_max
,
227 gpio_ext_set_value(led_dat
->gpio_ext
,
228 led_dat
->bright_addr
, bright_val
);
231 spin_unlock_irqrestore(&led_dat
->lock
, flags
);
234 static ssize_t
netxbig_led_sata_store(struct device
*dev
,
235 struct device_attribute
*attr
,
236 const char *buff
, size_t count
)
238 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
239 struct netxbig_led_data
*led_dat
=
240 container_of(led_cdev
, struct netxbig_led_data
, cdev
);
241 unsigned long enable
;
242 enum netxbig_led_mode mode
;
246 ret
= kstrtoul(buff
, 10, &enable
);
252 spin_lock_irq(&led_dat
->lock
);
254 if (led_dat
->sata
== enable
) {
259 if (led_dat
->mode
!= NETXBIG_LED_ON
&&
260 led_dat
->mode
!= NETXBIG_LED_SATA
)
261 mode
= led_dat
->mode
; /* Keep modes 'off' and 'timer'. */
263 mode
= NETXBIG_LED_SATA
;
265 mode
= NETXBIG_LED_ON
;
267 mode_val
= led_dat
->mode_val
[mode
];
268 if (mode_val
== NETXBIG_LED_INVALID_MODE
) {
273 gpio_ext_set_value(led_dat
->gpio_ext
, led_dat
->mode_addr
, mode_val
);
274 led_dat
->mode
= mode
;
275 led_dat
->sata
= enable
;
280 spin_unlock_irq(&led_dat
->lock
);
285 static ssize_t
netxbig_led_sata_show(struct device
*dev
,
286 struct device_attribute
*attr
, char *buf
)
288 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
289 struct netxbig_led_data
*led_dat
=
290 container_of(led_cdev
, struct netxbig_led_data
, cdev
);
292 return sprintf(buf
, "%d\n", led_dat
->sata
);
295 static DEVICE_ATTR(sata
, 0644, netxbig_led_sata_show
, netxbig_led_sata_store
);
297 static void delete_netxbig_led(struct netxbig_led_data
*led_dat
)
299 if (led_dat
->mode_val
[NETXBIG_LED_SATA
] != NETXBIG_LED_INVALID_MODE
)
300 device_remove_file(led_dat
->cdev
.dev
, &dev_attr_sata
);
301 led_classdev_unregister(&led_dat
->cdev
);
305 create_netxbig_led(struct platform_device
*pdev
,
306 struct netxbig_led_data
*led_dat
,
307 const struct netxbig_led
*template)
309 struct netxbig_led_platform_data
*pdata
= pdev
->dev
.platform_data
;
312 spin_lock_init(&led_dat
->lock
);
313 led_dat
->gpio_ext
= pdata
->gpio_ext
;
314 led_dat
->cdev
.name
= template->name
;
315 led_dat
->cdev
.default_trigger
= template->default_trigger
;
316 led_dat
->cdev
.blink_set
= netxbig_led_blink_set
;
317 led_dat
->cdev
.brightness_set
= netxbig_led_set
;
319 * Because the GPIO extension bus don't allow to read registers
320 * value, there is no way to probe the LED initial state.
321 * So, the initial sysfs LED value for the "brightness" and "sata"
322 * attributes are inconsistent.
324 * Note that the initial LED state can't be reconfigured.
325 * The reason is that the LED behaviour must stay uniform during
326 * the whole boot process (bootloader+linux).
329 led_dat
->cdev
.brightness
= LED_OFF
;
330 led_dat
->cdev
.flags
|= LED_CORE_SUSPENDRESUME
;
331 led_dat
->mode_addr
= template->mode_addr
;
332 led_dat
->mode_val
= template->mode_val
;
333 led_dat
->bright_addr
= template->bright_addr
;
334 led_dat
->bright_max
= (1 << pdata
->gpio_ext
->num_data
) - 1;
335 led_dat
->timer
= pdata
->timer
;
336 led_dat
->num_timer
= pdata
->num_timer
;
338 ret
= led_classdev_register(&pdev
->dev
, &led_dat
->cdev
);
343 * If available, expose the SATA activity blink capability through
344 * a "sata" sysfs attribute.
346 if (led_dat
->mode_val
[NETXBIG_LED_SATA
] != NETXBIG_LED_INVALID_MODE
) {
347 ret
= device_create_file(led_dat
->cdev
.dev
, &dev_attr_sata
);
349 led_classdev_unregister(&led_dat
->cdev
);
355 static int netxbig_led_probe(struct platform_device
*pdev
)
357 struct netxbig_led_platform_data
*pdata
= pdev
->dev
.platform_data
;
358 struct netxbig_led_data
*leds_data
;
365 leds_data
= devm_kzalloc(&pdev
->dev
,
366 sizeof(struct netxbig_led_data
) * pdata
->num_leds
, GFP_KERNEL
);
370 ret
= gpio_ext_init(pdata
->gpio_ext
);
374 for (i
= 0; i
< pdata
->num_leds
; i
++) {
375 ret
= create_netxbig_led(pdev
, &leds_data
[i
], &pdata
->leds
[i
]);
380 platform_set_drvdata(pdev
, leds_data
);
385 for (i
= i
- 1; i
>= 0; i
--)
386 delete_netxbig_led(&leds_data
[i
]);
388 gpio_ext_free(pdata
->gpio_ext
);
392 static int netxbig_led_remove(struct platform_device
*pdev
)
394 struct netxbig_led_platform_data
*pdata
= pdev
->dev
.platform_data
;
395 struct netxbig_led_data
*leds_data
;
398 leds_data
= platform_get_drvdata(pdev
);
400 for (i
= 0; i
< pdata
->num_leds
; i
++)
401 delete_netxbig_led(&leds_data
[i
]);
403 gpio_ext_free(pdata
->gpio_ext
);
408 static struct platform_driver netxbig_led_driver
= {
409 .probe
= netxbig_led_probe
,
410 .remove
= netxbig_led_remove
,
412 .name
= "leds-netxbig",
413 .owner
= THIS_MODULE
,
417 module_platform_driver(netxbig_led_driver
);
419 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
420 MODULE_DESCRIPTION("LED driver for LaCie xBig Network boards");
421 MODULE_LICENSE("GPL");
422 MODULE_ALIAS("platform:leds-netxbig");