geode-common.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Shared helpers to register GPIO-connected buttons and LEDs
  4. * on AMD Geode boards.
  5. */
  6. #include <linux/err.h>
  7. #include <linux/gpio/machine.h>
  8. #include <linux/gpio/property.h>
  9. #include <linux/input.h>
  10. #include <linux/leds.h>
  11. #include <linux/platform_device.h>
  12. #include <linux/slab.h>
  13. #include "geode-common.h"
  14. static const struct software_node geode_gpiochip_node = {
  15. .name = "cs5535-gpio",
  16. };
  17. static const struct property_entry geode_gpio_keys_props[] = {
  18. PROPERTY_ENTRY_U32("poll-interval", 20),
  19. { }
  20. };
  21. static const struct software_node geode_gpio_keys_node = {
  22. .name = "geode-gpio-keys",
  23. .properties = geode_gpio_keys_props,
  24. };
  25. static struct property_entry geode_restart_key_props[] = {
  26. { /* Placeholder for GPIO property */ },
  27. PROPERTY_ENTRY_U32("linux,code", KEY_RESTART),
  28. PROPERTY_ENTRY_STRING("label", "Reset button"),
  29. PROPERTY_ENTRY_U32("debounce-interval", 100),
  30. { }
  31. };
  32. static const struct software_node geode_restart_key_node = {
  33. .parent = &geode_gpio_keys_node,
  34. .properties = geode_restart_key_props,
  35. };
  36. static const struct software_node *geode_gpio_keys_swnodes[] __initconst = {
  37. &geode_gpiochip_node,
  38. &geode_gpio_keys_node,
  39. &geode_restart_key_node,
  40. NULL
  41. };
  42. /*
  43. * Creates gpio-keys-polled device for the restart key.
  44. *
  45. * Note that it needs to be called first, before geode_create_leds(),
  46. * because it registers gpiochip software node used by both gpio-keys and
  47. * leds-gpio devices.
  48. */
  49. int __init geode_create_restart_key(unsigned int pin)
  50. {
  51. struct platform_device_info keys_info = {
  52. .name = "gpio-keys-polled",
  53. .id = 1,
  54. };
  55. struct platform_device *pd;
  56. int err;
  57. geode_restart_key_props[0] = PROPERTY_ENTRY_GPIO("gpios",
  58. &geode_gpiochip_node,
  59. pin, GPIO_ACTIVE_LOW);
  60. err = software_node_register_node_group(geode_gpio_keys_swnodes);
  61. if (err) {
  62. pr_err("failed to register gpio-keys software nodes: %d\n", err);
  63. return err;
  64. }
  65. keys_info.fwnode = software_node_fwnode(&geode_gpio_keys_node);
  66. pd = platform_device_register_full(&keys_info);
  67. err = PTR_ERR_OR_ZERO(pd);
  68. if (err) {
  69. pr_err("failed to create gpio-keys device: %d\n", err);
  70. software_node_unregister_node_group(geode_gpio_keys_swnodes);
  71. return err;
  72. }
  73. return 0;
  74. }
  75. static const struct software_node geode_gpio_leds_node = {
  76. .name = "geode-leds",
  77. };
  78. #define MAX_LEDS 3
  79. int __init geode_create_leds(const char *label, const struct geode_led *leds,
  80. unsigned int n_leds)
  81. {
  82. const struct software_node *group[MAX_LEDS + 2] = { 0 };
  83. struct software_node *swnodes;
  84. struct property_entry *props;
  85. struct platform_device_info led_info = {
  86. .name = "leds-gpio",
  87. .id = PLATFORM_DEVID_NONE,
  88. };
  89. struct platform_device *led_dev;
  90. const char *node_name;
  91. int err;
  92. int i;
  93. if (n_leds > MAX_LEDS) {
  94. pr_err("%s: too many LEDs\n", __func__);
  95. return -EINVAL;
  96. }
  97. swnodes = kcalloc(n_leds, sizeof(*swnodes), GFP_KERNEL);
  98. if (!swnodes)
  99. return -ENOMEM;
  100. /*
  101. * Each LED is represented by 3 properties: "gpios",
  102. * "linux,default-trigger", and am empty terminator.
  103. */
  104. props = kcalloc(n_leds * 3, sizeof(*props), GFP_KERNEL);
  105. if (!props) {
  106. err = -ENOMEM;
  107. goto err_free_swnodes;
  108. }
  109. group[0] = &geode_gpio_leds_node;
  110. for (i = 0; i < n_leds; i++) {
  111. node_name = kasprintf(GFP_KERNEL, "%s:%d", label, i);
  112. if (!node_name) {
  113. err = -ENOMEM;
  114. goto err_free_names;
  115. }
  116. props[i * 3 + 0] =
  117. PROPERTY_ENTRY_GPIO("gpios", &geode_gpiochip_node,
  118. leds[i].pin, GPIO_ACTIVE_LOW);
  119. props[i * 3 + 1] =
  120. PROPERTY_ENTRY_STRING("linux,default-trigger",
  121. leds[i].default_on ?
  122. "default-on" : "default-off");
  123. /* props[i * 3 + 2] is an empty terminator */
  124. swnodes[i] = SOFTWARE_NODE(node_name, &props[i * 3],
  125. &geode_gpio_leds_node);
  126. group[i + 1] = &swnodes[i];
  127. }
  128. err = software_node_register_node_group(group);
  129. if (err) {
  130. pr_err("failed to register LED software nodes: %d\n", err);
  131. goto err_free_names;
  132. }
  133. led_info.fwnode = software_node_fwnode(&geode_gpio_leds_node);
  134. led_dev = platform_device_register_full(&led_info);
  135. err = PTR_ERR_OR_ZERO(led_dev);
  136. if (err) {
  137. pr_err("failed to create LED device: %d\n", err);
  138. goto err_unregister_group;
  139. }
  140. return 0;
  141. err_unregister_group:
  142. software_node_unregister_node_group(group);
  143. err_free_names:
  144. while (--i >= 0)
  145. kfree(swnodes[i].name);
  146. kfree(props);
  147. err_free_swnodes:
  148. kfree(swnodes);
  149. return err;
  150. }