leds-ixp4xx-gpio.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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. #include <linux/kernel.h>
  34. #include <linux/init.h>
  35. #include <linux/platform_device.h>
  36. #include <linux/spinlock.h>
  37. #include <linux/leds.h>
  38. #include <asm/arch/hardware.h>
  39. extern spinlock_t gpio_lock;
  40. /* Up to 16 gpio lines are possible. */
  41. #define GPIO_MAX 16
  42. static struct ixp4xxgpioled_device {
  43. struct led_classdev ancestor;
  44. int flags;
  45. } ixp4xxgpioled_devices[GPIO_MAX];
  46. void ixp4xxgpioled_brightness_set(struct led_classdev *pled,
  47. enum led_brightness value)
  48. {
  49. const struct ixp4xxgpioled_device *const ixp4xx_dev =
  50. container_of(pled, struct ixp4xxgpioled_device, ancestor);
  51. const u32 gpio_pin = ixp4xx_dev - ixp4xxgpioled_devices;
  52. if (gpio_pin < GPIO_MAX && ixp4xx_dev->ancestor.name != 0) {
  53. /* Set or clear the 'gpio_pin' bit according to the style
  54. * and the required setting (value > 0 == on)
  55. */
  56. const int gpio_value =
  57. (value > 0) == (ixp4xx_dev->flags != IXP4XX_GPIO_LOW) ?
  58. IXP4XX_GPIO_HIGH : IXP4XX_GPIO_LOW;
  59. {
  60. unsigned long flags;
  61. spin_lock_irqsave(&gpio_lock, flags);
  62. gpio_line_set(gpio_pin, gpio_value);
  63. spin_unlock_irqrestore(&gpio_lock, flags);
  64. }
  65. }
  66. }
  67. /* LEDs are described in resources, the following iterates over the valid
  68. * LED resources.
  69. */
  70. #define for_all_leds(i, pdev) \
  71. for (i=0; i<pdev->num_resources; ++i) \
  72. if (pdev->resource[i].start < GPIO_MAX && \
  73. pdev->resource[i].name != 0)
  74. /* The following applies 'operation' to each LED from the given platform,
  75. * the function always returns 0 to allow tail call elimination.
  76. */
  77. static int apply_to_all_leds(struct platform_device *pdev,
  78. void (*operation)(struct led_classdev *pled))
  79. {
  80. int i;
  81. for_all_leds(i, pdev)
  82. operation(&ixp4xxgpioled_devices[pdev->resource[i].start].ancestor);
  83. return 0;
  84. }
  85. #ifdef CONFIG_PM
  86. static int ixp4xxgpioled_suspend(struct platform_device *pdev,
  87. pm_message_t state)
  88. {
  89. return apply_to_all_leds(pdev, led_classdev_suspend);
  90. }
  91. static int ixp4xxgpioled_resume(struct platform_device *pdev)
  92. {
  93. return apply_to_all_leds(pdev, led_classdev_resume);
  94. }
  95. #endif
  96. static void ixp4xxgpioled_remove_one_led(struct led_classdev *pled)
  97. {
  98. led_classdev_unregister(pled);
  99. pled->name = 0;
  100. }
  101. static int ixp4xxgpioled_remove(struct platform_device *pdev)
  102. {
  103. return apply_to_all_leds(pdev, ixp4xxgpioled_remove_one_led);
  104. }
  105. static int ixp4xxgpioled_probe(struct platform_device *pdev)
  106. {
  107. /* The board level has to tell the driver where the
  108. * LEDs are connected - there is no way to find out
  109. * electrically. It must also say whether the GPIO
  110. * lines are active high or active low.
  111. *
  112. * To do this read the num_resources (the number of
  113. * LEDs) and the struct resource (the data for each
  114. * LED). The name comes from the resource, and it
  115. * isn't copied.
  116. */
  117. int i;
  118. for_all_leds(i, pdev) {
  119. const u8 gpio_pin = pdev->resource[i].start;
  120. int rc;
  121. if (ixp4xxgpioled_devices[gpio_pin].ancestor.name == 0) {
  122. unsigned long flags;
  123. spin_lock_irqsave(&gpio_lock, flags);
  124. gpio_line_config(gpio_pin, IXP4XX_GPIO_OUT);
  125. /* The config can, apparently, reset the state,
  126. * I suspect the gpio line may be an input and
  127. * the config may cause the line to be latched,
  128. * so the setting depends on how the LED is
  129. * connected to the line (which affects how it
  130. * floats if not driven).
  131. */
  132. gpio_line_set(gpio_pin, IXP4XX_GPIO_HIGH);
  133. spin_unlock_irqrestore(&gpio_lock, flags);
  134. ixp4xxgpioled_devices[gpio_pin].flags =
  135. pdev->resource[i].flags & IORESOURCE_BITS;
  136. ixp4xxgpioled_devices[gpio_pin].ancestor.name =
  137. pdev->resource[i].name;
  138. /* This is how a board manufacturer makes the LED
  139. * come on on reset - the GPIO line will be high, so
  140. * make the LED light when the line is low...
  141. */
  142. if (ixp4xxgpioled_devices[gpio_pin].flags != IXP4XX_GPIO_LOW)
  143. ixp4xxgpioled_devices[gpio_pin].ancestor.brightness = 100;
  144. else
  145. ixp4xxgpioled_devices[gpio_pin].ancestor.brightness = 0;
  146. ixp4xxgpioled_devices[gpio_pin].ancestor.flags = 0;
  147. ixp4xxgpioled_devices[gpio_pin].ancestor.brightness_set =
  148. ixp4xxgpioled_brightness_set;
  149. ixp4xxgpioled_devices[gpio_pin].ancestor.default_trigger = 0;
  150. }
  151. rc = led_classdev_register(&pdev->dev,
  152. &ixp4xxgpioled_devices[gpio_pin].ancestor);
  153. if (rc < 0) {
  154. ixp4xxgpioled_devices[gpio_pin].ancestor.name = 0;
  155. ixp4xxgpioled_remove(pdev);
  156. return rc;
  157. }
  158. }
  159. return 0;
  160. }
  161. static struct platform_driver ixp4xxgpioled_driver = {
  162. .probe = ixp4xxgpioled_probe,
  163. .remove = ixp4xxgpioled_remove,
  164. #ifdef CONFIG_PM
  165. .suspend = ixp4xxgpioled_suspend,
  166. .resume = ixp4xxgpioled_resume,
  167. #endif
  168. .driver = {
  169. .name = "IXP4XX-GPIO-LED",
  170. },
  171. };
  172. static int __init ixp4xxgpioled_init(void)
  173. {
  174. return platform_driver_register(&ixp4xxgpioled_driver);
  175. }
  176. static void __exit ixp4xxgpioled_exit(void)
  177. {
  178. platform_driver_unregister(&ixp4xxgpioled_driver);
  179. }
  180. module_init(ixp4xxgpioled_init);
  181. module_exit(ixp4xxgpioled_exit);
  182. MODULE_AUTHOR("John Bowler <jbowler@acm.org>");
  183. MODULE_DESCRIPTION("IXP4XX GPIO LED driver");
  184. MODULE_LICENSE("Dual MIT/GPL");