meraki-mx100.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Cisco Meraki MX100 (Tinkerbell) board platform driver
  4. *
  5. * Based off of arch/x86/platform/meraki/tink.c from the
  6. * Meraki GPL release meraki-firmware-sources-r23-20150601
  7. *
  8. * Format inspired by platform/x86/pcengines-apuv2.c
  9. *
  10. * Copyright (C) 2021 Chris Blake <chrisrblake93@gmail.com>
  11. */
  12. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  13. #include <linux/dmi.h>
  14. #include <linux/err.h>
  15. #include <linux/gpio/machine.h>
  16. #include <linux/gpio/property.h>
  17. #include <linux/input-event-codes.h>
  18. #include <linux/io.h>
  19. #include <linux/kernel.h>
  20. #include <linux/module.h>
  21. #include <linux/platform_device.h>
  22. #include <linux/property.h>
  23. #define TINK_GPIO_DRIVER_NAME "gpio_ich"
  24. static const struct software_node gpio_ich_node = {
  25. .name = TINK_GPIO_DRIVER_NAME,
  26. };
  27. /* LEDs */
  28. static const struct software_node tink_gpio_leds_node = {
  29. .name = "meraki-mx100-leds",
  30. };
  31. static const struct property_entry tink_internet_led_props[] = {
  32. PROPERTY_ENTRY_STRING("label", "mx100:green:internet"),
  33. PROPERTY_ENTRY_STRING("linux,default-trigger", "default-on"),
  34. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 11, GPIO_ACTIVE_LOW),
  35. { }
  36. };
  37. static const struct software_node tink_internet_led_node = {
  38. .name = "internet-led",
  39. .parent = &tink_gpio_leds_node,
  40. .properties = tink_internet_led_props,
  41. };
  42. static const struct property_entry tink_lan2_led_props[] = {
  43. PROPERTY_ENTRY_STRING("label", "mx100:green:lan2"),
  44. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 18, GPIO_ACTIVE_HIGH),
  45. { }
  46. };
  47. static const struct software_node tink_lan2_led_node = {
  48. .name = "lan2-led",
  49. .parent = &tink_gpio_leds_node,
  50. .properties = tink_lan2_led_props,
  51. };
  52. static const struct property_entry tink_lan3_led_props[] = {
  53. PROPERTY_ENTRY_STRING("label", "mx100:green:lan3"),
  54. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 20, GPIO_ACTIVE_HIGH),
  55. { }
  56. };
  57. static const struct software_node tink_lan3_led_node = {
  58. .name = "lan3-led",
  59. .parent = &tink_gpio_leds_node,
  60. .properties = tink_lan3_led_props,
  61. };
  62. static const struct property_entry tink_lan4_led_props[] = {
  63. PROPERTY_ENTRY_STRING("label", "mx100:green:lan4"),
  64. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 22, GPIO_ACTIVE_HIGH),
  65. { }
  66. };
  67. static const struct software_node tink_lan4_led_node = {
  68. .name = "lan4-led",
  69. .parent = &tink_gpio_leds_node,
  70. .properties = tink_lan4_led_props,
  71. };
  72. static const struct property_entry tink_lan5_led_props[] = {
  73. PROPERTY_ENTRY_STRING("label", "mx100:green:lan5"),
  74. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 23, GPIO_ACTIVE_HIGH),
  75. { }
  76. };
  77. static const struct software_node tink_lan5_led_node = {
  78. .name = "lan5-led",
  79. .parent = &tink_gpio_leds_node,
  80. .properties = tink_lan5_led_props,
  81. };
  82. static const struct property_entry tink_lan6_led_props[] = {
  83. PROPERTY_ENTRY_STRING("label", "mx100:green:lan6"),
  84. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 32, GPIO_ACTIVE_HIGH),
  85. { }
  86. };
  87. static const struct software_node tink_lan6_led_node = {
  88. .name = "lan6-led",
  89. .parent = &tink_gpio_leds_node,
  90. .properties = tink_lan6_led_props,
  91. };
  92. static const struct property_entry tink_lan7_led_props[] = {
  93. PROPERTY_ENTRY_STRING("label", "mx100:green:lan7"),
  94. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 34, GPIO_ACTIVE_HIGH),
  95. { }
  96. };
  97. static const struct software_node tink_lan7_led_node = {
  98. .name = "lan7-led",
  99. .parent = &tink_gpio_leds_node,
  100. .properties = tink_lan7_led_props,
  101. };
  102. static const struct property_entry tink_lan8_led_props[] = {
  103. PROPERTY_ENTRY_STRING("label", "mx100:green:lan8"),
  104. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 35, GPIO_ACTIVE_HIGH),
  105. { }
  106. };
  107. static const struct software_node tink_lan8_led_node = {
  108. .name = "lan8-led",
  109. .parent = &tink_gpio_leds_node,
  110. .properties = tink_lan8_led_props,
  111. };
  112. static const struct property_entry tink_lan9_led_props[] = {
  113. PROPERTY_ENTRY_STRING("label", "mx100:green:lan9"),
  114. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 36, GPIO_ACTIVE_HIGH),
  115. { }
  116. };
  117. static const struct software_node tink_lan9_led_node = {
  118. .name = "lan9-led",
  119. .parent = &tink_gpio_leds_node,
  120. .properties = tink_lan9_led_props,
  121. };
  122. static const struct property_entry tink_lan10_led_props[] = {
  123. PROPERTY_ENTRY_STRING("label", "mx100:green:lan10"),
  124. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 37, GPIO_ACTIVE_HIGH),
  125. { }
  126. };
  127. static const struct software_node tink_lan10_led_node = {
  128. .name = "lan10-led",
  129. .parent = &tink_gpio_leds_node,
  130. .properties = tink_lan10_led_props,
  131. };
  132. static const struct property_entry tink_lan11_led_props[] = {
  133. PROPERTY_ENTRY_STRING("label", "mx100:green:lan11"),
  134. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 48, GPIO_ACTIVE_HIGH),
  135. { }
  136. };
  137. static const struct software_node tink_lan11_led_node = {
  138. .name = "lan11-led",
  139. .parent = &tink_gpio_leds_node,
  140. .properties = tink_lan11_led_props,
  141. };
  142. static const struct property_entry tink_ha_green_led_props[] = {
  143. PROPERTY_ENTRY_STRING("label", "mx100:green:ha"),
  144. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 16, GPIO_ACTIVE_LOW),
  145. { }
  146. };
  147. static const struct software_node tink_ha_green_led_node = {
  148. .name = "ha-green-led",
  149. .parent = &tink_gpio_leds_node,
  150. .properties = tink_ha_green_led_props,
  151. };
  152. static const struct property_entry tink_ha_orange_led_props[] = {
  153. PROPERTY_ENTRY_STRING("label", "mx100:orange:ha"),
  154. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 7, GPIO_ACTIVE_LOW),
  155. { }
  156. };
  157. static const struct software_node tink_ha_orange_led_node = {
  158. .name = "ha-orange-led",
  159. .parent = &tink_gpio_leds_node,
  160. .properties = tink_ha_orange_led_props,
  161. };
  162. static const struct property_entry tink_usb_green_led_props[] = {
  163. PROPERTY_ENTRY_STRING("label", "mx100:green:usb"),
  164. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 21, GPIO_ACTIVE_LOW),
  165. { }
  166. };
  167. static const struct software_node tink_usb_green_led_node = {
  168. .name = "usb-green-led",
  169. .parent = &tink_gpio_leds_node,
  170. .properties = tink_usb_green_led_props,
  171. };
  172. static const struct property_entry tink_usb_orange_led_props[] = {
  173. PROPERTY_ENTRY_STRING("label", "mx100:orange:usb"),
  174. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 19, GPIO_ACTIVE_LOW),
  175. { }
  176. };
  177. static const struct software_node tink_usb_orange_led_node = {
  178. .name = "usb-orange-led",
  179. .parent = &tink_gpio_leds_node,
  180. .properties = tink_usb_orange_led_props,
  181. };
  182. /* Reset Button */
  183. static const struct property_entry tink_gpio_keys_props[] = {
  184. PROPERTY_ENTRY_U32("poll-interval", 20),
  185. { }
  186. };
  187. static const struct software_node tink_gpio_keys_node = {
  188. .name = "mx100-keys",
  189. .properties = tink_gpio_keys_props,
  190. };
  191. static const struct property_entry tink_reset_key_props[] = {
  192. PROPERTY_ENTRY_U32("linux,code", KEY_RESTART),
  193. PROPERTY_ENTRY_STRING("label", "Reset"),
  194. PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 60, GPIO_ACTIVE_LOW),
  195. PROPERTY_ENTRY_U32("linux,input-type", EV_KEY),
  196. PROPERTY_ENTRY_U32("debounce-interval", 100),
  197. { }
  198. };
  199. static const struct software_node tink_reset_key_node = {
  200. .name = "reset",
  201. .parent = &tink_gpio_keys_node,
  202. .properties = tink_reset_key_props,
  203. };
  204. static const struct software_node *tink_swnodes[] = {
  205. &gpio_ich_node,
  206. /* LEDs nodes */
  207. &tink_gpio_leds_node,
  208. &tink_internet_led_node,
  209. &tink_lan2_led_node,
  210. &tink_lan3_led_node,
  211. &tink_lan4_led_node,
  212. &tink_lan5_led_node,
  213. &tink_lan6_led_node,
  214. &tink_lan7_led_node,
  215. &tink_lan8_led_node,
  216. &tink_lan9_led_node,
  217. &tink_lan10_led_node,
  218. &tink_lan11_led_node,
  219. &tink_ha_green_led_node,
  220. &tink_ha_orange_led_node,
  221. &tink_usb_green_led_node,
  222. &tink_usb_orange_led_node,
  223. /* Keys nodes */
  224. &tink_gpio_keys_node,
  225. &tink_reset_key_node,
  226. NULL
  227. };
  228. /* Board setup */
  229. static const struct dmi_system_id tink_systems[] __initconst = {
  230. {
  231. .matches = {
  232. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Cisco"),
  233. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MX100-HW"),
  234. },
  235. },
  236. {} /* Terminating entry */
  237. };
  238. MODULE_DEVICE_TABLE(dmi, tink_systems);
  239. static struct platform_device *tink_leds_pdev;
  240. static struct platform_device *tink_keys_pdev;
  241. static int __init tink_board_init(void)
  242. {
  243. struct platform_device_info keys_info = {
  244. .name = "gpio-keys-polled",
  245. .id = PLATFORM_DEVID_NONE,
  246. };
  247. struct platform_device_info leds_info = {
  248. .name = "leds-gpio",
  249. .id = PLATFORM_DEVID_NONE,
  250. };
  251. int err;
  252. if (!dmi_first_match(tink_systems))
  253. return -ENODEV;
  254. /*
  255. * We need to make sure that GPIO60 isn't set to native mode as is default since it's our
  256. * Reset Button. To do this, write to GPIO_USE_SEL2 to have GPIO60 set to GPIO mode.
  257. * This is documented on page 1609 of the PCH datasheet, order number 327879-005US
  258. */
  259. outl(inl(0x530) | BIT(28), 0x530);
  260. err = software_node_register_node_group(tink_swnodes);
  261. if (err) {
  262. pr_err("failed to register software nodes: %d\n", err);
  263. return err;
  264. }
  265. leds_info.fwnode = software_node_fwnode(&tink_gpio_leds_node);
  266. tink_leds_pdev = platform_device_register_full(&leds_info);
  267. if (IS_ERR(tink_leds_pdev)) {
  268. err = PTR_ERR(tink_leds_pdev);
  269. pr_err("failed to create LED device: %d\n", err);
  270. goto err_unregister_swnodes;
  271. }
  272. keys_info.fwnode = software_node_fwnode(&tink_gpio_keys_node);
  273. tink_keys_pdev = platform_device_register_full(&keys_info);
  274. if (IS_ERR(tink_keys_pdev)) {
  275. err = PTR_ERR(tink_keys_pdev);
  276. pr_err("failed to create key device: %d\n", err);
  277. goto err_unregister_leds;
  278. }
  279. return 0;
  280. err_unregister_leds:
  281. platform_device_unregister(tink_leds_pdev);
  282. err_unregister_swnodes:
  283. software_node_unregister_node_group(tink_swnodes);
  284. return err;
  285. }
  286. module_init(tink_board_init);
  287. static void __exit tink_board_exit(void)
  288. {
  289. platform_device_unregister(tink_keys_pdev);
  290. platform_device_unregister(tink_leds_pdev);
  291. software_node_unregister_node_group(tink_swnodes);
  292. }
  293. module_exit(tink_board_exit);
  294. MODULE_AUTHOR("Chris Blake <chrisrblake93@gmail.com>");
  295. MODULE_DESCRIPTION("Cisco Meraki MX100 Platform Driver");
  296. MODULE_LICENSE("GPL");
  297. MODULE_ALIAS("platform:meraki-mx100");