leds: Add generic GPIO LED driver

This patch adds support for GPIO connected leds via the new GPIO framework.

Information about leds (gpio, polarity, name, default trigger) is passed
to the driver via platform_data.

Signed-off-by: Raphael Assenat <raph@8d.com>
Signed-off-by: Richard Purdie <rpurdie@rpsys.net>

authored by Raphael Assenat and committed by Richard Purdie 22e03f3b 8f41958b

+197
+8
drivers/leds/Kconfig
··· 95 95 help 96 96 This option enables support for the front LED on Cobalt Server 97 97 98 + config LEDS_GPIO 99 + tristate "LED Support for GPIO connected LEDs" 100 + depends on LEDS_CLASS && GENERIC_GPIO 101 + help 102 + This option enables support for the LEDs connected to GPIO 103 + outputs. To be useful the particular board must have LEDs 104 + and they must be connected to the GPIO lines. 105 + 98 106 comment "LED Triggers" 99 107 100 108 config LEDS_TRIGGERS
+1
drivers/leds/Makefile
··· 16 16 obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o 17 17 obj-$(CONFIG_LEDS_H1940) += leds-h1940.o 18 18 obj-$(CONFIG_LEDS_COBALT) += leds-cobalt.o 19 + obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o 19 20 20 21 # LED Triggers 21 22 obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
+174
drivers/leds/leds-gpio.c
··· 1 + /* 2 + * LEDs driver for GPIOs 3 + * 4 + * Copyright (C) 2007 8D Technologies inc. 5 + * Raphael Assenat <raph@8d.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + * 11 + */ 12 + #include <linux/kernel.h> 13 + #include <linux/init.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/leds.h> 16 + #include <asm/gpio.h> 17 + 18 + struct gpio_led_data { 19 + struct led_classdev cdev; 20 + unsigned gpio; 21 + u8 active_low; 22 + }; 23 + 24 + 25 + static void gpio_led_set(struct led_classdev *led_cdev, 26 + enum led_brightness value) 27 + { 28 + struct gpio_led_data *led_dat = 29 + container_of(led_cdev, struct gpio_led_data, cdev); 30 + int level; 31 + 32 + if (value == LED_OFF) 33 + level = 0; 34 + else 35 + level = 1; 36 + 37 + if (led_dat->active_low) 38 + level = !level; 39 + 40 + gpio_set_value(led_dat->gpio, level); 41 + } 42 + 43 + static int __init gpio_led_probe(struct platform_device *pdev) 44 + { 45 + struct gpio_led_platform_data *pdata = pdev->dev.platform_data; 46 + struct gpio_led *cur_led; 47 + struct gpio_led_data *leds_data, *led_dat; 48 + int i, ret = 0; 49 + 50 + if (!pdata) 51 + return -EBUSY; 52 + 53 + leds_data = kzalloc(sizeof(struct gpio_led_data) * pdata->num_leds, 54 + GFP_KERNEL); 55 + if (!leds_data) 56 + return -ENOMEM; 57 + 58 + for (i = 0; i < pdata->num_leds; i++) { 59 + cur_led = &pdata->leds[i]; 60 + led_dat = &leds_data[i]; 61 + 62 + led_dat->cdev.name = cur_led->name; 63 + led_dat->cdev.default_trigger = cur_led->default_trigger; 64 + led_dat->gpio = cur_led->gpio; 65 + led_dat->active_low = cur_led->active_low; 66 + led_dat->cdev.brightness_set = gpio_led_set; 67 + led_dat->cdev.brightness = cur_led->active_low ? LED_FULL : LED_OFF; 68 + 69 + ret = gpio_request(led_dat->gpio, led_dat->cdev.name); 70 + if (ret < 0) 71 + goto err; 72 + 73 + gpio_direction_output(led_dat->gpio, led_dat->active_low); 74 + 75 + ret = led_classdev_register(&pdev->dev, &led_dat->cdev); 76 + if (ret < 0) { 77 + gpio_free(led_dat->gpio); 78 + goto err; 79 + } 80 + } 81 + 82 + platform_set_drvdata(pdev, leds_data); 83 + 84 + return 0; 85 + 86 + err: 87 + if (i > 0) { 88 + for (i = i - 1; i >= 0; i--) { 89 + led_classdev_unregister(&leds_data[i].cdev); 90 + gpio_free(leds_data[i].gpio); 91 + } 92 + } 93 + kfree(leds_data); 94 + 95 + return ret; 96 + } 97 + 98 + static int __exit gpio_led_remove(struct platform_device *pdev) 99 + { 100 + int i; 101 + struct gpio_led_platform_data *pdata = pdev->dev.platform_data; 102 + struct gpio_led_data *leds_data; 103 + 104 + leds_data = platform_get_drvdata(pdev); 105 + 106 + for (i = 0; i < pdata->num_leds; i++) { 107 + led_classdev_unregister(&leds_data[i].cdev); 108 + gpio_free(leds_data[i].gpio); 109 + } 110 + 111 + kfree(leds_data); 112 + 113 + return 0; 114 + } 115 + 116 + #ifdef CONFIG_PM 117 + static int gpio_led_suspend(struct platform_device *pdev, pm_message_t state) 118 + { 119 + struct gpio_led_platform_data *pdata = pdev->dev.platform_data; 120 + struct gpio_led_data *leds_data; 121 + int i; 122 + 123 + leds_data = platform_get_drvdata(pdev); 124 + 125 + for (i = 0; i < pdata->num_leds; i++) 126 + led_classdev_suspend(&leds_data[i].cdev); 127 + 128 + return 0; 129 + } 130 + 131 + static int gpio_led_resume(struct platform_device *pdev) 132 + { 133 + struct gpio_led_platform_data *pdata = pdev->dev.platform_data; 134 + struct gpio_led_data *leds_data; 135 + int i; 136 + 137 + leds_data = platform_get_drvdata(pdev); 138 + 139 + for (i = 0; i < pdata->num_leds; i++) 140 + led_classdev_resume(&leds_data[i].cdev); 141 + 142 + return 0; 143 + } 144 + #else 145 + #define gpio_led_suspend NULL 146 + #define gpio_led_resume NULL 147 + #endif 148 + 149 + static struct platform_driver gpio_led_driver = { 150 + .remove = __exit_p(gpio_led_remove), 151 + .suspend = gpio_led_suspend, 152 + .resume = gpio_led_resume, 153 + .driver = { 154 + .name = "leds-gpio", 155 + .owner = THIS_MODULE, 156 + }, 157 + }; 158 + 159 + static int __init gpio_led_init(void) 160 + { 161 + return platform_driver_probe(&gpio_led_driver, gpio_led_probe); 162 + } 163 + 164 + static void __exit gpio_led_exit(void) 165 + { 166 + platform_driver_unregister(&gpio_led_driver); 167 + } 168 + 169 + module_init(gpio_led_init); 170 + module_exit(gpio_led_exit); 171 + 172 + MODULE_AUTHOR("Raphael Assenat <raph@8d.com>"); 173 + MODULE_DESCRIPTION("GPIO LED driver"); 174 + MODULE_LICENSE("GPL");
+14
include/linux/leds.h
··· 110 110 #define ledtrig_ide_activity() do {} while(0) 111 111 #endif 112 112 113 + /* For the leds-gpio driver */ 114 + struct gpio_led { 115 + const char *name; 116 + char *default_trigger; 117 + unsigned gpio; 118 + u8 active_low; 119 + }; 120 + 121 + struct gpio_led_platform_data { 122 + int num_leds; 123 + struct gpio_led *leds; 124 + }; 125 + 126 + 113 127 #endif /* __LINUX_LEDS_H_INCLUDED */