leds-alix2.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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. static int __init alix_led_probe(struct platform_device *pdev)
  62. {
  63. int i;
  64. int ret;
  65. for (i = 0; i < ARRAY_SIZE(alix_leds); i++) {
  66. alix_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
  67. ret = led_classdev_register(&pdev->dev, &alix_leds[i].cdev);
  68. if (ret < 0)
  69. goto fail;
  70. }
  71. return 0;
  72. fail:
  73. while (--i >= 0)
  74. led_classdev_unregister(&alix_leds[i].cdev);
  75. return ret;
  76. }
  77. static int alix_led_remove(struct platform_device *pdev)
  78. {
  79. int i;
  80. for (i = 0; i < ARRAY_SIZE(alix_leds); i++)
  81. led_classdev_unregister(&alix_leds[i].cdev);
  82. return 0;
  83. }
  84. static struct platform_driver alix_led_driver = {
  85. .remove = alix_led_remove,
  86. .driver = {
  87. .name = KBUILD_MODNAME,
  88. .owner = THIS_MODULE,
  89. },
  90. };
  91. static int __init alix_present(void)
  92. {
  93. const unsigned long bios_phys = 0x000f0000;
  94. const size_t bios_len = 0x00010000;
  95. const char alix_sig[] = "PC Engines ALIX.";
  96. const size_t alix_sig_len = sizeof(alix_sig) - 1;
  97. const char *bios_virt;
  98. const char *scan_end;
  99. const char *p;
  100. int ret = 0;
  101. if (force) {
  102. printk(KERN_NOTICE "%s: forced to skip BIOS test, "
  103. "assume system has ALIX.2 style LEDs\n",
  104. KBUILD_MODNAME);
  105. ret = 1;
  106. goto out;
  107. }
  108. bios_virt = phys_to_virt(bios_phys);
  109. scan_end = bios_virt + bios_len - (alix_sig_len + 2);
  110. for (p = bios_virt; p < scan_end; p++) {
  111. const char *tail;
  112. if (memcmp(p, alix_sig, alix_sig_len) != 0) {
  113. continue;
  114. }
  115. tail = p + alix_sig_len;
  116. if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') {
  117. printk(KERN_INFO
  118. "%s: system is recognized as \"%s\"\n",
  119. KBUILD_MODNAME, p);
  120. ret = 1;
  121. break;
  122. }
  123. }
  124. out:
  125. return ret;
  126. }
  127. static struct platform_device *pdev;
  128. static int __init alix_led_init(void)
  129. {
  130. int ret;
  131. if (!alix_present()) {
  132. ret = -ENODEV;
  133. goto out;
  134. }
  135. pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
  136. if (!IS_ERR(pdev)) {
  137. ret = platform_driver_probe(&alix_led_driver, alix_led_probe);
  138. if (ret)
  139. platform_device_unregister(pdev);
  140. } else
  141. ret = PTR_ERR(pdev);
  142. out:
  143. return ret;
  144. }
  145. static void __exit alix_led_exit(void)
  146. {
  147. platform_device_unregister(pdev);
  148. platform_driver_unregister(&alix_led_driver);
  149. }
  150. module_init(alix_led_init);
  151. module_exit(alix_led_exit);
  152. MODULE_AUTHOR("Constantin Baranov <const@mimas.ru>");
  153. MODULE_DESCRIPTION("PCEngines ALIX.2 and ALIX.3 LED driver");
  154. MODULE_LICENSE("GPL");