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