Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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