pcengines-apuv2.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * PC-Engines APUv2/APUv3 board platform driver
  4. * for GPIO buttons and LEDs
  5. *
  6. * Copyright (C) 2018 metux IT consult
  7. * Author: Enrico Weigelt <[email protected]>
  8. */
  9. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  10. #include <linux/dmi.h>
  11. #include <linux/err.h>
  12. #include <linux/kernel.h>
  13. #include <linux/leds.h>
  14. #include <linux/module.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/gpio_keys.h>
  17. #include <linux/gpio/machine.h>
  18. #include <linux/input.h>
  19. #include <linux/platform_data/gpio/gpio-amd-fch.h>
  20. /*
  21. * NOTE: this driver only supports APUv2/3 - not APUv1, as this one
  22. * has completely different register layouts.
  23. */
  24. /* Register mappings */
  25. #define APU2_GPIO_REG_LED1 AMD_FCH_GPIO_REG_GPIO57
  26. #define APU2_GPIO_REG_LED2 AMD_FCH_GPIO_REG_GPIO58
  27. #define APU2_GPIO_REG_LED3 AMD_FCH_GPIO_REG_GPIO59_DEVSLP1
  28. #define APU2_GPIO_REG_MODESW AMD_FCH_GPIO_REG_GPIO32_GE1
  29. #define APU2_GPIO_REG_SIMSWAP AMD_FCH_GPIO_REG_GPIO33_GE2
  30. #define APU2_GPIO_REG_MPCIE2 AMD_FCH_GPIO_REG_GPIO55_DEVSLP0
  31. #define APU2_GPIO_REG_MPCIE3 AMD_FCH_GPIO_REG_GPIO51
  32. /* Order in which the GPIO lines are defined in the register list */
  33. #define APU2_GPIO_LINE_LED1 0
  34. #define APU2_GPIO_LINE_LED2 1
  35. #define APU2_GPIO_LINE_LED3 2
  36. #define APU2_GPIO_LINE_MODESW 3
  37. #define APU2_GPIO_LINE_SIMSWAP 4
  38. #define APU2_GPIO_LINE_MPCIE2 5
  39. #define APU2_GPIO_LINE_MPCIE3 6
  40. /* GPIO device */
  41. static int apu2_gpio_regs[] = {
  42. [APU2_GPIO_LINE_LED1] = APU2_GPIO_REG_LED1,
  43. [APU2_GPIO_LINE_LED2] = APU2_GPIO_REG_LED2,
  44. [APU2_GPIO_LINE_LED3] = APU2_GPIO_REG_LED3,
  45. [APU2_GPIO_LINE_MODESW] = APU2_GPIO_REG_MODESW,
  46. [APU2_GPIO_LINE_SIMSWAP] = APU2_GPIO_REG_SIMSWAP,
  47. [APU2_GPIO_LINE_MPCIE2] = APU2_GPIO_REG_MPCIE2,
  48. [APU2_GPIO_LINE_MPCIE3] = APU2_GPIO_REG_MPCIE3,
  49. };
  50. static const char * const apu2_gpio_names[] = {
  51. [APU2_GPIO_LINE_LED1] = "front-led1",
  52. [APU2_GPIO_LINE_LED2] = "front-led2",
  53. [APU2_GPIO_LINE_LED3] = "front-led3",
  54. [APU2_GPIO_LINE_MODESW] = "front-button",
  55. [APU2_GPIO_LINE_SIMSWAP] = "simswap",
  56. [APU2_GPIO_LINE_MPCIE2] = "mpcie2_reset",
  57. [APU2_GPIO_LINE_MPCIE3] = "mpcie3_reset",
  58. };
  59. static const struct amd_fch_gpio_pdata board_apu2 = {
  60. .gpio_num = ARRAY_SIZE(apu2_gpio_regs),
  61. .gpio_reg = apu2_gpio_regs,
  62. .gpio_names = apu2_gpio_names,
  63. };
  64. /* GPIO LEDs device */
  65. static const struct gpio_led apu2_leds[] = {
  66. { .name = "apu:green:1" },
  67. { .name = "apu:green:2" },
  68. { .name = "apu:green:3" },
  69. };
  70. static const struct gpio_led_platform_data apu2_leds_pdata = {
  71. .num_leds = ARRAY_SIZE(apu2_leds),
  72. .leds = apu2_leds,
  73. };
  74. static struct gpiod_lookup_table gpios_led_table = {
  75. .dev_id = "leds-gpio",
  76. .table = {
  77. GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED1,
  78. NULL, 0, GPIO_ACTIVE_LOW),
  79. GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED2,
  80. NULL, 1, GPIO_ACTIVE_LOW),
  81. GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED3,
  82. NULL, 2, GPIO_ACTIVE_LOW),
  83. {} /* Terminating entry */
  84. }
  85. };
  86. /* GPIO keyboard device */
  87. static struct gpio_keys_button apu2_keys_buttons[] = {
  88. {
  89. .code = KEY_RESTART,
  90. .active_low = 1,
  91. .desc = "front button",
  92. .type = EV_KEY,
  93. .debounce_interval = 10,
  94. .value = 1,
  95. },
  96. };
  97. static const struct gpio_keys_platform_data apu2_keys_pdata = {
  98. .buttons = apu2_keys_buttons,
  99. .nbuttons = ARRAY_SIZE(apu2_keys_buttons),
  100. .poll_interval = 100,
  101. .rep = 0,
  102. .name = "apu2-keys",
  103. };
  104. static struct gpiod_lookup_table gpios_key_table = {
  105. .dev_id = "gpio-keys-polled",
  106. .table = {
  107. GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_MODESW,
  108. NULL, 0, GPIO_ACTIVE_LOW),
  109. {} /* Terminating entry */
  110. }
  111. };
  112. /* Board setup */
  113. /* Note: matching works on string prefix, so "apu2" must come before "apu" */
  114. static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = {
  115. /* APU2 w/ legacy BIOS < 4.0.8 */
  116. {
  117. .ident = "apu2",
  118. .matches = {
  119. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  120. DMI_MATCH(DMI_BOARD_NAME, "APU2")
  121. },
  122. .driver_data = (void *)&board_apu2,
  123. },
  124. /* APU2 w/ legacy BIOS >= 4.0.8 */
  125. {
  126. .ident = "apu2",
  127. .matches = {
  128. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  129. DMI_MATCH(DMI_BOARD_NAME, "apu2")
  130. },
  131. .driver_data = (void *)&board_apu2,
  132. },
  133. /* APU2 w/ mainline BIOS */
  134. {
  135. .ident = "apu2",
  136. .matches = {
  137. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  138. DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu2")
  139. },
  140. .driver_data = (void *)&board_apu2,
  141. },
  142. /* APU3 w/ legacy BIOS < 4.0.8 */
  143. {
  144. .ident = "apu3",
  145. .matches = {
  146. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  147. DMI_MATCH(DMI_BOARD_NAME, "APU3")
  148. },
  149. .driver_data = (void *)&board_apu2,
  150. },
  151. /* APU3 w/ legacy BIOS >= 4.0.8 */
  152. {
  153. .ident = "apu3",
  154. .matches = {
  155. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  156. DMI_MATCH(DMI_BOARD_NAME, "apu3")
  157. },
  158. .driver_data = (void *)&board_apu2,
  159. },
  160. /* APU3 w/ mainline BIOS */
  161. {
  162. .ident = "apu3",
  163. .matches = {
  164. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  165. DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu3")
  166. },
  167. .driver_data = (void *)&board_apu2,
  168. },
  169. /* APU4 w/ legacy BIOS < 4.0.8 */
  170. {
  171. .ident = "apu4",
  172. .matches = {
  173. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  174. DMI_MATCH(DMI_BOARD_NAME, "APU4")
  175. },
  176. .driver_data = (void *)&board_apu2,
  177. },
  178. /* APU4 w/ legacy BIOS >= 4.0.8 */
  179. {
  180. .ident = "apu4",
  181. .matches = {
  182. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  183. DMI_MATCH(DMI_BOARD_NAME, "apu4")
  184. },
  185. .driver_data = (void *)&board_apu2,
  186. },
  187. /* APU4 w/ mainline BIOS */
  188. {
  189. .ident = "apu4",
  190. .matches = {
  191. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  192. DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu4")
  193. },
  194. .driver_data = (void *)&board_apu2,
  195. },
  196. {}
  197. };
  198. static struct platform_device *apu_gpio_pdev;
  199. static struct platform_device *apu_leds_pdev;
  200. static struct platform_device *apu_keys_pdev;
  201. static struct platform_device * __init apu_create_pdev(
  202. const char *name,
  203. const void *pdata,
  204. size_t sz)
  205. {
  206. struct platform_device *pdev;
  207. pdev = platform_device_register_resndata(NULL,
  208. name,
  209. PLATFORM_DEVID_NONE,
  210. NULL,
  211. 0,
  212. pdata,
  213. sz);
  214. if (IS_ERR(pdev))
  215. pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev));
  216. return pdev;
  217. }
  218. static int __init apu_board_init(void)
  219. {
  220. const struct dmi_system_id *id;
  221. id = dmi_first_match(apu_gpio_dmi_table);
  222. if (!id) {
  223. pr_err("failed to detect APU board via DMI\n");
  224. return -ENODEV;
  225. }
  226. gpiod_add_lookup_table(&gpios_led_table);
  227. gpiod_add_lookup_table(&gpios_key_table);
  228. apu_gpio_pdev = apu_create_pdev(
  229. AMD_FCH_GPIO_DRIVER_NAME,
  230. id->driver_data,
  231. sizeof(struct amd_fch_gpio_pdata));
  232. apu_leds_pdev = apu_create_pdev(
  233. "leds-gpio",
  234. &apu2_leds_pdata,
  235. sizeof(apu2_leds_pdata));
  236. apu_keys_pdev = apu_create_pdev(
  237. "gpio-keys-polled",
  238. &apu2_keys_pdata,
  239. sizeof(apu2_keys_pdata));
  240. return 0;
  241. }
  242. static void __exit apu_board_exit(void)
  243. {
  244. gpiod_remove_lookup_table(&gpios_led_table);
  245. gpiod_remove_lookup_table(&gpios_key_table);
  246. platform_device_unregister(apu_keys_pdev);
  247. platform_device_unregister(apu_leds_pdev);
  248. platform_device_unregister(apu_gpio_pdev);
  249. }
  250. module_init(apu_board_init);
  251. module_exit(apu_board_exit);
  252. MODULE_AUTHOR("Enrico Weigelt, metux IT consult <[email protected]>");
  253. MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver");
  254. MODULE_LICENSE("GPL");
  255. MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table);
  256. MODULE_ALIAS("platform:pcengines-apuv2");
  257. MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled");