Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/platform/geode/geode-common.c
26493 views
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
*/
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
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
180