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