xref: /linux/arch/x86/platform/geode/geode-common.c (revision 442d60df742a597dca7cca89a28a4843ce935f09)
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