xref: /linux/arch/x86/platform/geode/geode-common.c (revision 80bb50e2d459213cccff3111d5ef98ed4238c0d5)
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 software_node_ref_args geode_restart_gpio_ref;
32 
33 static 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 
41 static const struct software_node geode_restart_key_node = {
42 	.parent = &geode_gpio_keys_node,
43 	.properties = geode_restart_key_props,
44 };
45 
46 static 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  */
60 int __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 
91 static const struct software_node geode_gpio_leds_node = {
92 	.name = "geode-leds",
93 };
94 
95 #define MAX_LEDS	3
96 
97 int __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 
179 err_unregister_group:
180 	software_node_unregister_node_group(group);
181 err_free_names:
182 	while (--i >= 0)
183 		kfree(swnodes[i].name);
184 	kfree(gpio_refs);
185 err_free_props:
186 	kfree(props);
187 err_free_swnodes:
188 	kfree(swnodes);
189 	return err;
190 }
191