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