Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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 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
 39static const struct software_node geode_restart_key_node = {
 40	.parent = &geode_gpio_keys_node,
 41	.properties = geode_restart_key_props,
 42};
 43
 44static 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 */
 58int __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
 90static const struct software_node geode_gpio_leds_node = {
 91	.name = "geode-leds",
 92};
 93
 94#define MAX_LEDS	3
 95
 96int __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
169err_unregister_group:
170	software_node_unregister_node_group(group);
171err_free_names:
172	while (--i >= 0)
173		kfree(swnodes[i].name);
174	kfree(props);
175err_free_swnodes:
176	kfree(swnodes);
177	return err;
178}