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