1/* 2 * IXP4XX GPIO driver LED driver 3 * 4 * Author: John Bowler <jbowler@acm.org> 5 * 6 * Copyright (c) 2006 John Bowler 7 * 8 * Permission is hereby granted, free of charge, to any 9 * person obtaining a copy of this software and associated 10 * documentation files (the "Software"), to deal in the 11 * Software without restriction, including without 12 * limitation the rights to use, copy, modify, merge, 13 * publish, distribute, sublicense, and/or sell copies of 14 * the Software, and to permit persons to whom the 15 * Software is furnished to do so, subject to the 16 * following conditions: 17 * 18 * The above copyright notice and this permission notice 19 * shall be included in all copies or substantial portions 20 * of the Software. 21 * 22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 23 * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 24 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 25 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 26 * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 27 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 30 * OTHER DEALINGS IN THE SOFTWARE. 31 * 32 */ 33 34#include <linux/kernel.h> 35#include <linux/init.h> 36#include <linux/platform_device.h> 37#include <linux/spinlock.h> 38#include <linux/leds.h> 39#include <asm/arch/hardware.h> 40 41extern spinlock_t gpio_lock; 42 43/* Up to 16 gpio lines are possible. */ 44#define GPIO_MAX 16 45static struct ixp4xxgpioled_device { 46 struct led_classdev ancestor; 47 int flags; 48} ixp4xxgpioled_devices[GPIO_MAX]; 49 50void ixp4xxgpioled_brightness_set(struct led_classdev *pled, 51 enum led_brightness value) 52{ 53 const struct ixp4xxgpioled_device *const ixp4xx_dev = 54 container_of(pled, struct ixp4xxgpioled_device, ancestor); 55 const u32 gpio_pin = ixp4xx_dev - ixp4xxgpioled_devices; 56 57 if (gpio_pin < GPIO_MAX && ixp4xx_dev->ancestor.name != 0) { 58 /* Set or clear the 'gpio_pin' bit according to the style 59 * and the required setting (value > 0 == on) 60 */ 61 const int gpio_value = 62 (value > 0) == (ixp4xx_dev->flags != IXP4XX_GPIO_LOW) ? 63 IXP4XX_GPIO_HIGH : IXP4XX_GPIO_LOW; 64 65 { 66 unsigned long flags; 67 spin_lock_irqsave(&gpio_lock, flags); 68 gpio_line_set(gpio_pin, gpio_value); 69 spin_unlock_irqrestore(&gpio_lock, flags); 70 } 71 } 72} 73 74/* LEDs are described in resources, the following iterates over the valid 75 * LED resources. 76 */ 77#define for_all_leds(i, pdev) \ 78 for (i=0; i<pdev->num_resources; ++i) \ 79 if (pdev->resource[i].start < GPIO_MAX && \ 80 pdev->resource[i].name != 0) 81 82/* The following applies 'operation' to each LED from the given platform, 83 * the function always returns 0 to allow tail call elimination. 84 */ 85static int apply_to_all_leds(struct platform_device *pdev, 86 void (*operation)(struct led_classdev *pled)) 87{ 88 int i; 89 90 for_all_leds(i, pdev) 91 operation(&ixp4xxgpioled_devices[pdev->resource[i].start].ancestor); 92 return 0; 93} 94 95#ifdef CONFIG_PM 96static int ixp4xxgpioled_suspend(struct platform_device *pdev, 97 pm_message_t state) 98{ 99 return apply_to_all_leds(pdev, led_classdev_suspend); 100} 101 102static int ixp4xxgpioled_resume(struct platform_device *pdev) 103{ 104 return apply_to_all_leds(pdev, led_classdev_resume); 105} 106#endif 107 108static void ixp4xxgpioled_remove_one_led(struct led_classdev *pled) 109{ 110 led_classdev_unregister(pled); 111 pled->name = 0; 112} 113 114static int ixp4xxgpioled_remove(struct platform_device *pdev) 115{ 116 return apply_to_all_leds(pdev, ixp4xxgpioled_remove_one_led); 117} 118 119static int ixp4xxgpioled_probe(struct platform_device *pdev) 120{ 121 /* The board level has to tell the driver where the 122 * LEDs are connected - there is no way to find out 123 * electrically. It must also say whether the GPIO 124 * lines are active high or active low. 125 * 126 * To do this read the num_resources (the number of 127 * LEDs) and the struct resource (the data for each 128 * LED). The name comes from the resource, and it 129 * isn't copied. 130 */ 131 int i; 132 133 for_all_leds(i, pdev) { 134 const u8 gpio_pin = pdev->resource[i].start; 135 int rc; 136 137 if (ixp4xxgpioled_devices[gpio_pin].ancestor.name == 0) { 138 unsigned long flags; 139 140 spin_lock_irqsave(&gpio_lock, flags); 141 gpio_line_config(gpio_pin, IXP4XX_GPIO_OUT); 142 /* The config can, apparently, reset the state, 143 * I suspect the gpio line may be an input and 144 * the config may cause the line to be latched, 145 * so the setting depends on how the LED is 146 * connected to the line (which affects how it 147 * floats if not driven). 148 */ 149 gpio_line_set(gpio_pin, IXP4XX_GPIO_HIGH); 150 spin_unlock_irqrestore(&gpio_lock, flags); 151 152 ixp4xxgpioled_devices[gpio_pin].flags = 153 pdev->resource[i].flags & IORESOURCE_BITS; 154 155 ixp4xxgpioled_devices[gpio_pin].ancestor.name = 156 pdev->resource[i].name; 157 158 /* This is how a board manufacturer makes the LED 159 * come on on reset - the GPIO line will be high, so 160 * make the LED light when the line is low... 161 */ 162 if (ixp4xxgpioled_devices[gpio_pin].flags != IXP4XX_GPIO_LOW) 163 ixp4xxgpioled_devices[gpio_pin].ancestor.brightness = 100; 164 else 165 ixp4xxgpioled_devices[gpio_pin].ancestor.brightness = 0; 166 167 ixp4xxgpioled_devices[gpio_pin].ancestor.flags = 0; 168 169 ixp4xxgpioled_devices[gpio_pin].ancestor.brightness_set = 170 ixp4xxgpioled_brightness_set; 171 172 ixp4xxgpioled_devices[gpio_pin].ancestor.default_trigger = 0; 173 } 174 175 rc = led_classdev_register(&pdev->dev, 176 &ixp4xxgpioled_devices[gpio_pin].ancestor); 177 if (rc < 0) { 178 ixp4xxgpioled_devices[gpio_pin].ancestor.name = 0; 179 ixp4xxgpioled_remove(pdev); 180 return rc; 181 } 182 } 183 184 return 0; 185} 186 187static struct platform_driver ixp4xxgpioled_driver = { 188 .probe = ixp4xxgpioled_probe, 189 .remove = ixp4xxgpioled_remove, 190#ifdef CONFIG_PM 191 .suspend = ixp4xxgpioled_suspend, 192 .resume = ixp4xxgpioled_resume, 193#endif 194 .driver = { 195 .name = "IXP4XX-GPIO-LED", 196 }, 197}; 198 199static int __init ixp4xxgpioled_init(void) 200{ 201 return platform_driver_register(&ixp4xxgpioled_driver); 202} 203 204static void __exit ixp4xxgpioled_exit(void) 205{ 206 platform_driver_unregister(&ixp4xxgpioled_driver); 207} 208 209module_init(ixp4xxgpioled_init); 210module_exit(ixp4xxgpioled_exit); 211 212MODULE_AUTHOR("John Bowler <jbowler@acm.org>"); 213MODULE_DESCRIPTION("IXP4XX GPIO LED driver"); 214MODULE_LICENSE("Dual MIT/GPL"); 215