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