~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/leds/leds-gpio.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * LEDs driver for GPIOs
 
3
 *
 
4
 * Copyright (C) 2007 8D Technologies inc.
 
5
 * Raphael Assenat <raph@8d.com>
 
6
 * Copyright (C) 2008 Freescale Semiconductor, Inc.
 
7
 *
 
8
 * This program is free software; you can redistribute it and/or modify
 
9
 * it under the terms of the GNU General Public License version 2 as
 
10
 * published by the Free Software Foundation.
 
11
 *
 
12
 */
 
13
#include <linux/kernel.h>
 
14
#include <linux/init.h>
 
15
#include <linux/platform_device.h>
 
16
#include <linux/leds.h>
 
17
#include <linux/of_platform.h>
 
18
#include <linux/of_gpio.h>
 
19
#include <linux/slab.h>
 
20
#include <linux/workqueue.h>
 
21
#include <linux/module.h>
 
22
 
 
23
#include <asm/gpio.h>
 
24
 
 
25
struct gpio_led_data {
 
26
        struct led_classdev cdev;
 
27
        unsigned gpio;
 
28
        struct work_struct work;
 
29
        u8 new_level;
 
30
        u8 can_sleep;
 
31
        u8 active_low;
 
32
        u8 blinking;
 
33
        int (*platform_gpio_blink_set)(unsigned gpio, int state,
 
34
                        unsigned long *delay_on, unsigned long *delay_off);
 
35
};
 
36
 
 
37
static void gpio_led_work(struct work_struct *work)
 
38
{
 
39
        struct gpio_led_data    *led_dat =
 
40
                container_of(work, struct gpio_led_data, work);
 
41
 
 
42
        if (led_dat->blinking) {
 
43
                led_dat->platform_gpio_blink_set(led_dat->gpio,
 
44
                                                 led_dat->new_level,
 
45
                                                 NULL, NULL);
 
46
                led_dat->blinking = 0;
 
47
        } else
 
48
                gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level);
 
49
}
 
50
 
 
51
static void gpio_led_set(struct led_classdev *led_cdev,
 
52
        enum led_brightness value)
 
53
{
 
54
        struct gpio_led_data *led_dat =
 
55
                container_of(led_cdev, struct gpio_led_data, cdev);
 
56
        int level;
 
57
 
 
58
        if (value == LED_OFF)
 
59
                level = 0;
 
60
        else
 
61
                level = 1;
 
62
 
 
63
        if (led_dat->active_low)
 
64
                level = !level;
 
65
 
 
66
        /* Setting GPIOs with I2C/etc requires a task context, and we don't
 
67
         * seem to have a reliable way to know if we're already in one; so
 
68
         * let's just assume the worst.
 
69
         */
 
70
        if (led_dat->can_sleep) {
 
71
                led_dat->new_level = level;
 
72
                schedule_work(&led_dat->work);
 
73
        } else {
 
74
                if (led_dat->blinking) {
 
75
                        led_dat->platform_gpio_blink_set(led_dat->gpio, level,
 
76
                                                         NULL, NULL);
 
77
                        led_dat->blinking = 0;
 
78
                } else
 
79
                        gpio_set_value(led_dat->gpio, level);
 
80
        }
 
81
}
 
82
 
 
83
static int gpio_blink_set(struct led_classdev *led_cdev,
 
84
        unsigned long *delay_on, unsigned long *delay_off)
 
85
{
 
86
        struct gpio_led_data *led_dat =
 
87
                container_of(led_cdev, struct gpio_led_data, cdev);
 
88
 
 
89
        led_dat->blinking = 1;
 
90
        return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK,
 
91
                                                delay_on, delay_off);
 
92
}
 
93
 
 
94
static int __devinit create_gpio_led(const struct gpio_led *template,
 
95
        struct gpio_led_data *led_dat, struct device *parent,
 
96
        int (*blink_set)(unsigned, int, unsigned long *, unsigned long *))
 
97
{
 
98
        int ret, state;
 
99
 
 
100
        led_dat->gpio = -1;
 
101
 
 
102
        /* skip leds that aren't available */
 
103
        if (!gpio_is_valid(template->gpio)) {
 
104
                printk(KERN_INFO "Skipping unavailable LED gpio %d (%s)\n",
 
105
                                template->gpio, template->name);
 
106
                return 0;
 
107
        }
 
108
 
 
109
        ret = gpio_request(template->gpio, template->name);
 
110
        if (ret < 0)
 
111
                return ret;
 
112
 
 
113
        led_dat->cdev.name = template->name;
 
114
        led_dat->cdev.default_trigger = template->default_trigger;
 
115
        led_dat->gpio = template->gpio;
 
116
        led_dat->can_sleep = gpio_cansleep(template->gpio);
 
117
        led_dat->active_low = template->active_low;
 
118
        led_dat->blinking = 0;
 
119
        if (blink_set) {
 
120
                led_dat->platform_gpio_blink_set = blink_set;
 
121
                led_dat->cdev.blink_set = gpio_blink_set;
 
122
        }
 
123
        led_dat->cdev.brightness_set = gpio_led_set;
 
124
        if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
 
125
                state = !!gpio_get_value_cansleep(led_dat->gpio) ^ led_dat->active_low;
 
126
        else
 
127
                state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
 
128
        led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
 
129
        if (!template->retain_state_suspended)
 
130
                led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
 
131
 
 
132
        ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
 
133
        if (ret < 0)
 
134
                goto err;
 
135
                
 
136
        INIT_WORK(&led_dat->work, gpio_led_work);
 
137
 
 
138
        ret = led_classdev_register(parent, &led_dat->cdev);
 
139
        if (ret < 0)
 
140
                goto err;
 
141
 
 
142
        return 0;
 
143
err:
 
144
        gpio_free(led_dat->gpio);
 
145
        return ret;
 
146
}
 
147
 
 
148
static void delete_gpio_led(struct gpio_led_data *led)
 
149
{
 
150
        if (!gpio_is_valid(led->gpio))
 
151
                return;
 
152
        led_classdev_unregister(&led->cdev);
 
153
        cancel_work_sync(&led->work);
 
154
        gpio_free(led->gpio);
 
155
}
 
156
 
 
157
struct gpio_leds_priv {
 
158
        int num_leds;
 
159
        struct gpio_led_data leds[];
 
160
};
 
161
 
 
162
static inline int sizeof_gpio_leds_priv(int num_leds)
 
163
{
 
164
        return sizeof(struct gpio_leds_priv) +
 
165
                (sizeof(struct gpio_led_data) * num_leds);
 
166
}
 
167
 
 
168
/* Code to create from OpenFirmware platform devices */
 
169
#ifdef CONFIG_OF_GPIO
 
170
static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_device *pdev)
 
171
{
 
172
        struct device_node *np = pdev->dev.of_node, *child;
 
173
        struct gpio_leds_priv *priv;
 
174
        int count = 0, ret;
 
175
 
 
176
        /* count LEDs in this device, so we know how much to allocate */
 
177
        for_each_child_of_node(np, child)
 
178
                count++;
 
179
        if (!count)
 
180
                return NULL;
 
181
 
 
182
        priv = kzalloc(sizeof_gpio_leds_priv(count), GFP_KERNEL);
 
183
        if (!priv)
 
184
                return NULL;
 
185
 
 
186
        for_each_child_of_node(np, child) {
 
187
                struct gpio_led led = {};
 
188
                enum of_gpio_flags flags;
 
189
                const char *state;
 
190
 
 
191
                led.gpio = of_get_gpio_flags(child, 0, &flags);
 
192
                led.active_low = flags & OF_GPIO_ACTIVE_LOW;
 
193
                led.name = of_get_property(child, "label", NULL) ? : child->name;
 
194
                led.default_trigger =
 
195
                        of_get_property(child, "linux,default-trigger", NULL);
 
196
                state = of_get_property(child, "default-state", NULL);
 
197
                if (state) {
 
198
                        if (!strcmp(state, "keep"))
 
199
                                led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
 
200
                        else if (!strcmp(state, "on"))
 
201
                                led.default_state = LEDS_GPIO_DEFSTATE_ON;
 
202
                        else
 
203
                                led.default_state = LEDS_GPIO_DEFSTATE_OFF;
 
204
                }
 
205
 
 
206
                ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
 
207
                                      &pdev->dev, NULL);
 
208
                if (ret < 0) {
 
209
                        of_node_put(child);
 
210
                        goto err;
 
211
                }
 
212
        }
 
213
 
 
214
        return priv;
 
215
 
 
216
err:
 
217
        for (count = priv->num_leds - 2; count >= 0; count--)
 
218
                delete_gpio_led(&priv->leds[count]);
 
219
        kfree(priv);
 
220
        return NULL;
 
221
}
 
222
 
 
223
static const struct of_device_id of_gpio_leds_match[] = {
 
224
        { .compatible = "gpio-leds", },
 
225
        {},
 
226
};
 
227
#else /* CONFIG_OF_GPIO */
 
228
static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_device *pdev)
 
229
{
 
230
        return NULL;
 
231
}
 
232
#define of_gpio_leds_match NULL
 
233
#endif /* CONFIG_OF_GPIO */
 
234
 
 
235
 
 
236
static int __devinit gpio_led_probe(struct platform_device *pdev)
 
237
{
 
238
        struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
 
239
        struct gpio_leds_priv *priv;
 
240
        int i, ret = 0;
 
241
 
 
242
        if (pdata && pdata->num_leds) {
 
243
                priv = kzalloc(sizeof_gpio_leds_priv(pdata->num_leds),
 
244
                                GFP_KERNEL);
 
245
                if (!priv)
 
246
                        return -ENOMEM;
 
247
 
 
248
                priv->num_leds = pdata->num_leds;
 
249
                for (i = 0; i < priv->num_leds; i++) {
 
250
                        ret = create_gpio_led(&pdata->leds[i],
 
251
                                              &priv->leds[i],
 
252
                                              &pdev->dev, pdata->gpio_blink_set);
 
253
                        if (ret < 0) {
 
254
                                /* On failure: unwind the led creations */
 
255
                                for (i = i - 1; i >= 0; i--)
 
256
                                        delete_gpio_led(&priv->leds[i]);
 
257
                                kfree(priv);
 
258
                                return ret;
 
259
                        }
 
260
                }
 
261
        } else {
 
262
                priv = gpio_leds_create_of(pdev);
 
263
                if (!priv)
 
264
                        return -ENODEV;
 
265
        }
 
266
 
 
267
        platform_set_drvdata(pdev, priv);
 
268
 
 
269
        return 0;
 
270
}
 
271
 
 
272
static int __devexit gpio_led_remove(struct platform_device *pdev)
 
273
{
 
274
        struct gpio_leds_priv *priv = dev_get_drvdata(&pdev->dev);
 
275
        int i;
 
276
 
 
277
        for (i = 0; i < priv->num_leds; i++)
 
278
                delete_gpio_led(&priv->leds[i]);
 
279
 
 
280
        dev_set_drvdata(&pdev->dev, NULL);
 
281
        kfree(priv);
 
282
 
 
283
        return 0;
 
284
}
 
285
 
 
286
static struct platform_driver gpio_led_driver = {
 
287
        .probe          = gpio_led_probe,
 
288
        .remove         = __devexit_p(gpio_led_remove),
 
289
        .driver         = {
 
290
                .name   = "leds-gpio",
 
291
                .owner  = THIS_MODULE,
 
292
                .of_match_table = of_gpio_leds_match,
 
293
        },
 
294
};
 
295
 
 
296
MODULE_ALIAS("platform:leds-gpio");
 
297
 
 
298
static int __init gpio_led_init(void)
 
299
{
 
300
        return platform_driver_register(&gpio_led_driver);
 
301
}
 
302
 
 
303
static void __exit gpio_led_exit(void)
 
304
{
 
305
        platform_driver_unregister(&gpio_led_driver);
 
306
}
 
307
 
 
308
module_init(gpio_led_init);
 
309
module_exit(gpio_led_exit);
 
310
 
 
311
MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
 
312
MODULE_DESCRIPTION("GPIO LED driver");
 
313
MODULE_LICENSE("GPL");