Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
17static const struct software_node geode_gpiochip_node = {
18 .name = "cs5535-gpio",
19};
20
21static const struct property_entry geode_gpio_keys_props[] = {
22 PROPERTY_ENTRY_U32("poll-interval", 20),
23 { }
24};
25
26static const struct software_node geode_gpio_keys_node = {
27 .name = "geode-gpio-keys",
28 .properties = geode_gpio_keys_props,
29};
30
31static struct software_node_ref_args geode_restart_gpio_ref;
32
33static 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
41static const struct software_node geode_restart_key_node = {
42 .parent = &geode_gpio_keys_node,
43 .properties = geode_restart_key_props,
44};
45
46static 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 */
60int __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
91static const struct software_node geode_gpio_leds_node = {
92 .name = "geode-leds",
93};
94
95#define MAX_LEDS 3
96
97int __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
179err_unregister_group:
180 software_node_unregister_node_group(group);
181err_free_names:
182 while (--i >= 0)
183 kfree(swnodes[i].name);
184 kfree(gpio_refs);
185err_free_props:
186 kfree(props);
187err_free_swnodes:
188 kfree(swnodes);
189 return err;
190}