leds-alix2.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /*
  2. * LEDs driver for PCEngines ALIX.2 and ALIX.3
  3. *
  4. * Copyright (C) 2008 Constantin Baranov <const@mimas.ru>
  5. */
  6. #include <linux/err.h>
  7. #include <linux/io.h>
  8. #include <linux/kernel.h>
  9. #include <linux/leds.h>
  10. #include <linux/module.h>
  11. #include <linux/platform_device.h>
  12. #include <linux/string.h>
  13. #include <linux/pci.h>
  14. static int force = 0;
  15. module_param(force, bool, 0444);
  16. MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs");
  17. #define MSR_LBAR_GPIO 0x5140000C
  18. #define CS5535_GPIO_SIZE 256
  19. static u32 gpio_base;
  20. static struct pci_device_id divil_pci[] = {
  21. { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
  22. { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
  23. { } /* NULL entry */
  24. };
  25. MODULE_DEVICE_TABLE(pci, divil_pci);
  26. struct alix_led {
  27. struct led_classdev cdev;
  28. unsigned short port;
  29. unsigned int on_value;
  30. unsigned int off_value;
  31. };
  32. static void alix_led_set(struct led_classdev *led_cdev,
  33. enum led_brightness brightness)
  34. {
  35. struct alix_led *led_dev =
  36. container_of(led_cdev, struct alix_led, cdev);
  37. if (brightness)
  38. outl(led_dev->on_value, gpio_base + led_dev->port);
  39. else
  40. outl(led_dev->off_value, gpio_base + led_dev->port);
  41. }
  42. static struct alix_led alix_leds[] = {
  43. {
  44. .cdev = {
  45. .name = "alix:1",
  46. .brightness_set = alix_led_set,
  47. },
  48. .port = 0x00,
  49. .on_value = 1 << 22,
  50. .off_value = 1 << 6,
  51. },
  52. {
  53. .cdev = {
  54. .name = "alix:2",
  55. .brightness_set = alix_led_set,
  56. },
  57. .port = 0x80,
  58. .on_value = 1 << 25,
  59. .off_value = 1 << 9,
  60. },
  61. {
  62. .cdev = {
  63. .name = "alix:3",
  64. .brightness_set = alix_led_set,
  65. },
  66. .port = 0x80,
  67. .on_value = 1 << 27,
  68. .off_value = 1 << 11,
  69. },
  70. };
  71. static int __init alix_led_probe(struct platform_device *pdev)
  72. {
  73. int i;
  74. int ret;
  75. for (i = 0; i < ARRAY_SIZE(alix_leds); i++) {
  76. alix_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
  77. ret = led_classdev_register(&pdev->dev, &alix_leds[i].cdev);
  78. if (ret < 0)
  79. goto fail;
  80. }
  81. return 0;
  82. fail:
  83. while (--i >= 0)
  84. led_classdev_unregister(&alix_leds[i].cdev);
  85. return ret;
  86. }
  87. static int alix_led_remove(struct platform_device *pdev)
  88. {
  89. int i;
  90. for (i = 0; i < ARRAY_SIZE(alix_leds); i++)
  91. led_classdev_unregister(&alix_leds[i].cdev);
  92. return 0;
  93. }
  94. static struct platform_driver alix_led_driver = {
  95. .remove = alix_led_remove,
  96. .driver = {
  97. .name = KBUILD_MODNAME,
  98. .owner = THIS_MODULE,
  99. },
  100. };
  101. static int __init alix_present(unsigned long bios_phys,
  102. const char *alix_sig,
  103. size_t alix_sig_len)
  104. {
  105. const size_t bios_len = 0x00010000;
  106. const char *bios_virt;
  107. const char *scan_end;
  108. const char *p;
  109. char name[64];
  110. if (force) {
  111. printk(KERN_NOTICE "%s: forced to skip BIOS test, "
  112. "assume system has ALIX.2 style LEDs\n",
  113. KBUILD_MODNAME);
  114. return 1;
  115. }
  116. bios_virt = phys_to_virt(bios_phys);
  117. scan_end = bios_virt + bios_len - (alix_sig_len + 2);
  118. for (p = bios_virt; p < scan_end; p++) {
  119. const char *tail;
  120. char *a;
  121. if (memcmp(p, alix_sig, alix_sig_len) != 0)
  122. continue;
  123. memcpy(name, p, sizeof(name));
  124. /* remove the first \0 character from string */
  125. a = strchr(name, '\0');
  126. if (a)
  127. *a = ' ';
  128. /* cut the string at a newline */
  129. a = strchr(name, '\r');
  130. if (a)
  131. *a = '\0';
  132. tail = p + alix_sig_len;
  133. if ((tail[0] == '2' || tail[0] == '3')) {
  134. printk(KERN_INFO
  135. "%s: system is recognized as \"%s\"\n",
  136. KBUILD_MODNAME, name);
  137. return 1;
  138. }
  139. }
  140. return 0;
  141. }
  142. static struct platform_device *pdev;
  143. static int __init alix_pci_led_init(void)
  144. {
  145. u32 low, hi;
  146. if (pci_dev_present(divil_pci) == 0) {
  147. printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n");
  148. return -ENODEV;
  149. }
  150. /* Grab the GPIO I/O range */
  151. rdmsr(MSR_LBAR_GPIO, low, hi);
  152. /* Check the mask and whether GPIO is enabled (sanity check) */
  153. if (hi != 0x0000f001) {
  154. printk(KERN_WARNING KBUILD_MODNAME": GPIO not enabled\n");
  155. return -ENODEV;
  156. }
  157. /* Mask off the IO base address */
  158. gpio_base = low & 0x0000ff00;
  159. if (!request_region(gpio_base, CS5535_GPIO_SIZE, KBUILD_MODNAME)) {
  160. printk(KERN_ERR KBUILD_MODNAME": can't allocate I/O for GPIO\n");
  161. return -ENODEV;
  162. }
  163. /* Set GPIO function to output */
  164. outl(1 << 6, gpio_base + 0x04);
  165. outl(1 << 9, gpio_base + 0x84);
  166. outl(1 << 11, gpio_base + 0x84);
  167. return 0;
  168. }
  169. static int __init alix_led_init(void)
  170. {
  171. int ret = -ENODEV;
  172. const char tinybios_sig[] = "PC Engines ALIX.";
  173. const char coreboot_sig[] = "PC Engines\0ALIX.";
  174. if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) ||
  175. alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1))
  176. ret = alix_pci_led_init();
  177. if (ret < 0)
  178. return ret;
  179. pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
  180. if (!IS_ERR(pdev)) {
  181. ret = platform_driver_probe(&alix_led_driver, alix_led_probe);
  182. if (ret)
  183. platform_device_unregister(pdev);
  184. } else
  185. ret = PTR_ERR(pdev);
  186. return ret;
  187. }
  188. static void __exit alix_led_exit(void)
  189. {
  190. platform_device_unregister(pdev);
  191. platform_driver_unregister(&alix_led_driver);
  192. release_region(gpio_base, CS5535_GPIO_SIZE);
  193. }
  194. module_init(alix_led_init);
  195. module_exit(alix_led_exit);
  196. MODULE_AUTHOR("Constantin Baranov <const@mimas.ru>");
  197. MODULE_DESCRIPTION("PCEngines ALIX.2 and ALIX.3 LED driver");
  198. MODULE_LICENSE("GPL");