pcengines-apuv2.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  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 <info@metux.net>
  8. */
  9. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  10. #include <linux/dmi.h>
  11. #include <linux/err.h>
  12. #include <linux/gpio/machine.h>
  13. #include <linux/gpio/property.h>
  14. #include <linux/input-event-codes.h>
  15. #include <linux/kernel.h>
  16. #include <linux/module.h>
  17. #include <linux/platform_device.h>
  18. #include <linux/property.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. static const struct software_node apu2_gpiochip_node = {
  65. .name = AMD_FCH_GPIO_DRIVER_NAME,
  66. };
  67. /* GPIO LEDs device */
  68. static const struct software_node apu2_leds_node = {
  69. .name = "apu2-leds",
  70. };
  71. static const struct property_entry apu2_led1_props[] = {
  72. PROPERTY_ENTRY_STRING("label", "apu:green:1"),
  73. PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node,
  74. APU2_GPIO_LINE_LED1, GPIO_ACTIVE_LOW),
  75. { }
  76. };
  77. static const struct software_node apu2_led1_swnode = {
  78. .name = "led-1",
  79. .parent = &apu2_leds_node,
  80. .properties = apu2_led1_props,
  81. };
  82. static const struct property_entry apu2_led2_props[] = {
  83. PROPERTY_ENTRY_STRING("label", "apu:green:2"),
  84. PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node,
  85. APU2_GPIO_LINE_LED2, GPIO_ACTIVE_LOW),
  86. { }
  87. };
  88. static const struct software_node apu2_led2_swnode = {
  89. .name = "led-2",
  90. .parent = &apu2_leds_node,
  91. .properties = apu2_led2_props,
  92. };
  93. static const struct property_entry apu2_led3_props[] = {
  94. PROPERTY_ENTRY_STRING("label", "apu:green:3"),
  95. PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node,
  96. APU2_GPIO_LINE_LED3, GPIO_ACTIVE_LOW),
  97. { }
  98. };
  99. static const struct software_node apu2_led3_swnode = {
  100. .name = "led-3",
  101. .parent = &apu2_leds_node,
  102. .properties = apu2_led3_props,
  103. };
  104. /* GPIO keyboard device */
  105. static const struct property_entry apu2_keys_props[] = {
  106. PROPERTY_ENTRY_U32("poll-interval", 100),
  107. { }
  108. };
  109. static const struct software_node apu2_keys_node = {
  110. .name = "apu2-keys",
  111. .properties = apu2_keys_props,
  112. };
  113. static const struct property_entry apu2_front_button_props[] = {
  114. PROPERTY_ENTRY_STRING("label", "front button"),
  115. PROPERTY_ENTRY_U32("linux,code", KEY_RESTART),
  116. PROPERTY_ENTRY_GPIO("gpios", &apu2_gpiochip_node,
  117. APU2_GPIO_LINE_MODESW, GPIO_ACTIVE_LOW),
  118. PROPERTY_ENTRY_U32("debounce-interval", 10),
  119. { }
  120. };
  121. static const struct software_node apu2_front_button_swnode = {
  122. .name = "front-button",
  123. .parent = &apu2_keys_node,
  124. .properties = apu2_front_button_props,
  125. };
  126. static const struct software_node *apu2_swnodes[] = {
  127. &apu2_gpiochip_node,
  128. /* LEDs nodes */
  129. &apu2_leds_node,
  130. &apu2_led1_swnode,
  131. &apu2_led2_swnode,
  132. &apu2_led3_swnode,
  133. /* Keys nodes */
  134. &apu2_keys_node,
  135. &apu2_front_button_swnode,
  136. NULL
  137. };
  138. /* Board setup */
  139. /* Note: matching works on string prefix, so "apu2" must come before "apu" */
  140. static const struct dmi_system_id apu_gpio_dmi_table[] __initconst = {
  141. /* APU2 w/ legacy BIOS < 4.0.8 */
  142. {
  143. .ident = "apu2",
  144. .matches = {
  145. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  146. DMI_MATCH(DMI_BOARD_NAME, "APU2")
  147. },
  148. .driver_data = (void *)&board_apu2,
  149. },
  150. /* APU2 w/ legacy BIOS >= 4.0.8 */
  151. {
  152. .ident = "apu2",
  153. .matches = {
  154. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  155. DMI_MATCH(DMI_BOARD_NAME, "apu2")
  156. },
  157. .driver_data = (void *)&board_apu2,
  158. },
  159. /* APU2 w/ mainline BIOS */
  160. {
  161. .ident = "apu2",
  162. .matches = {
  163. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  164. DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu2")
  165. },
  166. .driver_data = (void *)&board_apu2,
  167. },
  168. /* APU3 w/ legacy BIOS < 4.0.8 */
  169. {
  170. .ident = "apu3",
  171. .matches = {
  172. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  173. DMI_MATCH(DMI_BOARD_NAME, "APU3")
  174. },
  175. .driver_data = (void *)&board_apu2,
  176. },
  177. /* APU3 w/ legacy BIOS >= 4.0.8 */
  178. {
  179. .ident = "apu3",
  180. .matches = {
  181. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  182. DMI_MATCH(DMI_BOARD_NAME, "apu3")
  183. },
  184. .driver_data = (void *)&board_apu2,
  185. },
  186. /* APU3 w/ mainline BIOS */
  187. {
  188. .ident = "apu3",
  189. .matches = {
  190. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  191. DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu3")
  192. },
  193. .driver_data = (void *)&board_apu2,
  194. },
  195. /* APU4 w/ legacy BIOS < 4.0.8 */
  196. {
  197. .ident = "apu4",
  198. .matches = {
  199. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  200. DMI_MATCH(DMI_BOARD_NAME, "APU4")
  201. },
  202. .driver_data = (void *)&board_apu2,
  203. },
  204. /* APU4 w/ legacy BIOS >= 4.0.8 */
  205. {
  206. .ident = "apu4",
  207. .matches = {
  208. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  209. DMI_MATCH(DMI_BOARD_NAME, "apu4")
  210. },
  211. .driver_data = (void *)&board_apu2,
  212. },
  213. /* APU4 w/ mainline BIOS */
  214. {
  215. .ident = "apu4",
  216. .matches = {
  217. DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
  218. DMI_MATCH(DMI_BOARD_NAME, "PC Engines apu4")
  219. },
  220. .driver_data = (void *)&board_apu2,
  221. },
  222. {}
  223. };
  224. static struct platform_device *apu_gpio_pdev;
  225. static struct platform_device *apu_leds_pdev;
  226. static struct platform_device *apu_keys_pdev;
  227. static struct platform_device * __init apu_create_pdev(const char *name,
  228. const void *data, size_t size,
  229. const struct software_node *swnode)
  230. {
  231. struct platform_device_info pdev_info = {
  232. .name = name,
  233. .id = PLATFORM_DEVID_NONE,
  234. .data = data,
  235. .size_data = size,
  236. .fwnode = software_node_fwnode(swnode),
  237. };
  238. struct platform_device *pdev;
  239. int err;
  240. pdev = platform_device_register_full(&pdev_info);
  241. err = PTR_ERR_OR_ZERO(pdev);
  242. if (err)
  243. pr_err("failed registering %s: %d\n", name, err);
  244. return pdev;
  245. }
  246. static int __init apu_board_init(void)
  247. {
  248. const struct dmi_system_id *id;
  249. int err;
  250. id = dmi_first_match(apu_gpio_dmi_table);
  251. if (!id) {
  252. pr_err("failed to detect APU board via DMI\n");
  253. return -ENODEV;
  254. }
  255. err = software_node_register_node_group(apu2_swnodes);
  256. if (err) {
  257. pr_err("failed to register software nodes: %d\n", err);
  258. return err;
  259. }
  260. apu_gpio_pdev = apu_create_pdev(AMD_FCH_GPIO_DRIVER_NAME,
  261. id->driver_data, sizeof(struct amd_fch_gpio_pdata), NULL);
  262. err = PTR_ERR_OR_ZERO(apu_gpio_pdev);
  263. if (err)
  264. goto err_unregister_swnodes;
  265. apu_leds_pdev = apu_create_pdev("leds-gpio", NULL, 0, &apu2_leds_node);
  266. err = PTR_ERR_OR_ZERO(apu_leds_pdev);
  267. if (err)
  268. goto err_unregister_gpio;
  269. apu_keys_pdev = apu_create_pdev("gpio-keys-polled", NULL, 0, &apu2_keys_node);
  270. err = PTR_ERR_OR_ZERO(apu_keys_pdev);
  271. if (err)
  272. goto err_unregister_leds;
  273. return 0;
  274. err_unregister_leds:
  275. platform_device_unregister(apu_leds_pdev);
  276. err_unregister_gpio:
  277. platform_device_unregister(apu_gpio_pdev);
  278. err_unregister_swnodes:
  279. software_node_unregister_node_group(apu2_swnodes);
  280. return err;
  281. }
  282. static void __exit apu_board_exit(void)
  283. {
  284. platform_device_unregister(apu_keys_pdev);
  285. platform_device_unregister(apu_leds_pdev);
  286. platform_device_unregister(apu_gpio_pdev);
  287. software_node_unregister_node_group(apu2_swnodes);
  288. }
  289. module_init(apu_board_init);
  290. module_exit(apu_board_exit);
  291. MODULE_AUTHOR("Enrico Weigelt, metux IT consult <info@metux.net>");
  292. MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver");
  293. MODULE_LICENSE("GPL");
  294. MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table);
  295. MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled");