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