meraki-mx100.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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 <[email protected]>
  11. */
  12. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  13. #include <linux/dmi.h>
  14. #include <linux/err.h>
  15. #include <linux/gpio_keys.h>
  16. #include <linux/gpio/machine.h>
  17. #include <linux/input.h>
  18. #include <linux/io.h>
  19. #include <linux/kernel.h>
  20. #include <linux/leds.h>
  21. #include <linux/module.h>
  22. #include <linux/platform_device.h>
  23. #define TINK_GPIO_DRIVER_NAME "gpio_ich"
  24. /* LEDs */
  25. static const struct gpio_led tink_leds[] = {
  26. {
  27. .name = "mx100:green:internet",
  28. .default_trigger = "default-on",
  29. },
  30. {
  31. .name = "mx100:green:lan2",
  32. },
  33. {
  34. .name = "mx100:green:lan3",
  35. },
  36. {
  37. .name = "mx100:green:lan4",
  38. },
  39. {
  40. .name = "mx100:green:lan5",
  41. },
  42. {
  43. .name = "mx100:green:lan6",
  44. },
  45. {
  46. .name = "mx100:green:lan7",
  47. },
  48. {
  49. .name = "mx100:green:lan8",
  50. },
  51. {
  52. .name = "mx100:green:lan9",
  53. },
  54. {
  55. .name = "mx100:green:lan10",
  56. },
  57. {
  58. .name = "mx100:green:lan11",
  59. },
  60. {
  61. .name = "mx100:green:ha",
  62. },
  63. {
  64. .name = "mx100:orange:ha",
  65. },
  66. {
  67. .name = "mx100:green:usb",
  68. },
  69. {
  70. .name = "mx100:orange:usb",
  71. },
  72. };
  73. static const struct gpio_led_platform_data tink_leds_pdata = {
  74. .num_leds = ARRAY_SIZE(tink_leds),
  75. .leds = tink_leds,
  76. };
  77. static struct gpiod_lookup_table tink_leds_table = {
  78. .dev_id = "leds-gpio",
  79. .table = {
  80. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 11,
  81. NULL, 0, GPIO_ACTIVE_LOW),
  82. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 18,
  83. NULL, 1, GPIO_ACTIVE_HIGH),
  84. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 20,
  85. NULL, 2, GPIO_ACTIVE_HIGH),
  86. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 22,
  87. NULL, 3, GPIO_ACTIVE_HIGH),
  88. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 23,
  89. NULL, 4, GPIO_ACTIVE_HIGH),
  90. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 32,
  91. NULL, 5, GPIO_ACTIVE_HIGH),
  92. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 34,
  93. NULL, 6, GPIO_ACTIVE_HIGH),
  94. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 35,
  95. NULL, 7, GPIO_ACTIVE_HIGH),
  96. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 36,
  97. NULL, 8, GPIO_ACTIVE_HIGH),
  98. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 37,
  99. NULL, 9, GPIO_ACTIVE_HIGH),
  100. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 48,
  101. NULL, 10, GPIO_ACTIVE_HIGH),
  102. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 16,
  103. NULL, 11, GPIO_ACTIVE_LOW),
  104. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 7,
  105. NULL, 12, GPIO_ACTIVE_LOW),
  106. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 21,
  107. NULL, 13, GPIO_ACTIVE_LOW),
  108. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 19,
  109. NULL, 14, GPIO_ACTIVE_LOW),
  110. {} /* Terminating entry */
  111. }
  112. };
  113. /* Reset Button */
  114. static struct gpio_keys_button tink_buttons[] = {
  115. {
  116. .desc = "Reset",
  117. .type = EV_KEY,
  118. .code = KEY_RESTART,
  119. .active_low = 1,
  120. .debounce_interval = 100,
  121. },
  122. };
  123. static const struct gpio_keys_platform_data tink_buttons_pdata = {
  124. .buttons = tink_buttons,
  125. .nbuttons = ARRAY_SIZE(tink_buttons),
  126. .poll_interval = 20,
  127. .rep = 0,
  128. .name = "mx100-keys",
  129. };
  130. static struct gpiod_lookup_table tink_keys_table = {
  131. .dev_id = "gpio-keys-polled",
  132. .table = {
  133. GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 60,
  134. NULL, 0, GPIO_ACTIVE_LOW),
  135. {} /* Terminating entry */
  136. }
  137. };
  138. /* Board setup */
  139. static const struct dmi_system_id tink_systems[] __initconst = {
  140. {
  141. .matches = {
  142. DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Cisco"),
  143. DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MX100-HW"),
  144. },
  145. },
  146. {} /* Terminating entry */
  147. };
  148. MODULE_DEVICE_TABLE(dmi, tink_systems);
  149. static struct platform_device *tink_leds_pdev;
  150. static struct platform_device *tink_keys_pdev;
  151. static struct platform_device * __init tink_create_dev(
  152. const char *name, const void *pdata, size_t sz)
  153. {
  154. struct platform_device *pdev;
  155. pdev = platform_device_register_data(NULL,
  156. name, PLATFORM_DEVID_NONE, pdata, sz);
  157. if (IS_ERR(pdev))
  158. pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev));
  159. return pdev;
  160. }
  161. static int __init tink_board_init(void)
  162. {
  163. int ret;
  164. if (!dmi_first_match(tink_systems))
  165. return -ENODEV;
  166. /*
  167. * We need to make sure that GPIO60 isn't set to native mode as is default since it's our
  168. * Reset Button. To do this, write to GPIO_USE_SEL2 to have GPIO60 set to GPIO mode.
  169. * This is documented on page 1609 of the PCH datasheet, order number 327879-005US
  170. */
  171. outl(inl(0x530) | BIT(28), 0x530);
  172. gpiod_add_lookup_table(&tink_leds_table);
  173. gpiod_add_lookup_table(&tink_keys_table);
  174. tink_leds_pdev = tink_create_dev("leds-gpio",
  175. &tink_leds_pdata, sizeof(tink_leds_pdata));
  176. if (IS_ERR(tink_leds_pdev)) {
  177. ret = PTR_ERR(tink_leds_pdev);
  178. goto err;
  179. }
  180. tink_keys_pdev = tink_create_dev("gpio-keys-polled",
  181. &tink_buttons_pdata, sizeof(tink_buttons_pdata));
  182. if (IS_ERR(tink_keys_pdev)) {
  183. ret = PTR_ERR(tink_keys_pdev);
  184. platform_device_unregister(tink_leds_pdev);
  185. goto err;
  186. }
  187. return 0;
  188. err:
  189. gpiod_remove_lookup_table(&tink_keys_table);
  190. gpiod_remove_lookup_table(&tink_leds_table);
  191. return ret;
  192. }
  193. module_init(tink_board_init);
  194. static void __exit tink_board_exit(void)
  195. {
  196. platform_device_unregister(tink_keys_pdev);
  197. platform_device_unregister(tink_leds_pdev);
  198. gpiod_remove_lookup_table(&tink_keys_table);
  199. gpiod_remove_lookup_table(&tink_leds_table);
  200. }
  201. module_exit(tink_board_exit);
  202. MODULE_AUTHOR("Chris Blake <[email protected]>");
  203. MODULE_DESCRIPTION("Cisco Meraki MX100 Platform Driver");
  204. MODULE_LICENSE("GPL");
  205. MODULE_ALIAS("platform:meraki-mx100");