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 */
geode_create_restart_key(unsigned int pin)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
geode_create_leds(const char * label,const struct geode_led * leds,unsigned int n_leds)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