leds-alix2.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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. static int force = 0;
  14. module_param(force, bool, 0444);
  15. MODULE_PARM_DESC(force, "Assume system has ALIX.2 style LEDs");
  16. struct alix_led {
  17. struct led_classdev cdev;
  18. unsigned short port;
  19. unsigned int on_value;
  20. unsigned int off_value;
  21. };
  22. static void alix_led_set(struct led_classdev *led_cdev,
  23. enum led_brightness brightness)
  24. {
  25. struct alix_led *led_dev =
  26. container_of(led_cdev, struct alix_led, cdev);
  27. if (brightness)
  28. outl(led_dev->on_value, led_dev->port);
  29. else
  30. outl(led_dev->off_value, led_dev->port);
  31. }
  32. static struct alix_led alix_leds[] = {
  33. {
  34. .cdev = {
  35. .name = "alix:1",
  36. .brightness_set = alix_led_set,
  37. },
  38. .port = 0x6100,
  39. .on_value = 1 << 22,
  40. .off_value = 1 << 6,
  41. },
  42. {
  43. .cdev = {
  44. .name = "alix:2",
  45. .brightness_set = alix_led_set,
  46. },
  47. .port = 0x6180,
  48. .on_value = 1 << 25,
  49. .off_value = 1 << 9,
  50. },
  51. {
  52. .cdev = {
  53. .name = "alix:3",
  54. .brightness_set = alix_led_set,
  55. },
  56. .port = 0x6180,
  57. .on_value = 1 << 27,
  58. .off_value = 1 << 11,
  59. },
  60. };
  61. #ifdef CONFIG_PM
  62. static int alix_led_suspend(struct platform_device *dev, pm_message_t state)
  63. {
  64. int i;
  65. for (i = 0; i < ARRAY_SIZE(alix_leds); i++)
  66. led_classdev_suspend(&alix_leds[i].cdev);
  67. return 0;
  68. }
  69. static int alix_led_resume(struct platform_device *dev)
  70. {
  71. int i;
  72. for (i = 0; i < ARRAY_SIZE(alix_leds); i++)
  73. led_classdev_resume(&alix_leds[i].cdev);
  74. return 0;
  75. }
  76. #else
  77. #define alix_led_suspend NULL
  78. #define alix_led_resume NULL
  79. #endif
  80. static int __init alix_led_probe(struct platform_device *pdev)
  81. {
  82. int i;
  83. int ret;
  84. for (i = 0; i < ARRAY_SIZE(alix_leds); i++) {
  85. ret = led_classdev_register(&pdev->dev, &alix_leds[i].cdev);
  86. if (ret < 0)
  87. goto fail;
  88. }
  89. return 0;
  90. fail:
  91. while (--i >= 0)
  92. led_classdev_unregister(&alix_leds[i].cdev);
  93. return ret;
  94. }
  95. static int alix_led_remove(struct platform_device *pdev)
  96. {
  97. int i;
  98. for (i = 0; i < ARRAY_SIZE(alix_leds); i++)
  99. led_classdev_unregister(&alix_leds[i].cdev);
  100. return 0;
  101. }
  102. static struct platform_driver alix_led_driver = {
  103. .remove = alix_led_remove,
  104. .suspend = alix_led_suspend,
  105. .resume = alix_led_resume,
  106. .driver = {
  107. .name = KBUILD_MODNAME,
  108. .owner = THIS_MODULE,
  109. },
  110. };
  111. static int __init alix_present(void)
  112. {
  113. const unsigned long bios_phys = 0x000f0000;
  114. const size_t bios_len = 0x00010000;
  115. const char alix_sig[] = "PC Engines ALIX.";
  116. const size_t alix_sig_len = sizeof(alix_sig) - 1;
  117. const char *bios_virt;
  118. const char *scan_end;
  119. const char *p;
  120. int ret = 0;
  121. if (force) {
  122. printk(KERN_NOTICE "%s: forced to skip BIOS test, "
  123. "assume system has ALIX.2 style LEDs\n",
  124. KBUILD_MODNAME);
  125. ret = 1;
  126. goto out;
  127. }
  128. bios_virt = phys_to_virt(bios_phys);
  129. scan_end = bios_virt + bios_len - (alix_sig_len + 2);
  130. for (p = bios_virt; p < scan_end; p++) {
  131. const char *tail;
  132. if (memcmp(p, alix_sig, alix_sig_len) != 0) {
  133. continue;
  134. }
  135. tail = p + alix_sig_len;
  136. if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') {
  137. printk(KERN_INFO
  138. "%s: system is recognized as \"%s\"\n",
  139. KBUILD_MODNAME, p);
  140. ret = 1;
  141. break;
  142. }
  143. }
  144. out:
  145. return ret;
  146. }
  147. static struct platform_device *pdev;
  148. static int __init alix_led_init(void)
  149. {
  150. int ret;
  151. if (!alix_present()) {
  152. ret = -ENODEV;
  153. goto out;
  154. }
  155. pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
  156. if (!IS_ERR(pdev)) {
  157. ret = platform_driver_probe(&alix_led_driver, alix_led_probe);
  158. if (ret)
  159. platform_device_unregister(pdev);
  160. } else
  161. ret = PTR_ERR(pdev);
  162. out:
  163. return ret;
  164. }
  165. static void __exit alix_led_exit(void)
  166. {
  167. platform_device_unregister(pdev);
  168. platform_driver_unregister(&alix_led_driver);
  169. }
  170. module_init(alix_led_init);
  171. module_exit(alix_led_exit);
  172. MODULE_AUTHOR("Constantin Baranov <const@mimas.ru>");
  173. MODULE_DESCRIPTION("PCEngines ALIX.2 and ALIX.3 LED driver");
  174. MODULE_LICENSE("GPL");