1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2020 Philippe Reynes <philippe.reynes@softathome.com>
4  */
5 
6 #include <common.h>
7 #include <button.h>
8 #include <dm.h>
9 #include <dm/lists.h>
10 #include <dm/uclass-internal.h>
11 #include <log.h>
12 #include <asm/gpio.h>
13 
14 struct button_gpio_priv {
15 	struct gpio_desc gpio;
16 };
17 
button_gpio_get_state(struct udevice * dev)18 static enum button_state_t button_gpio_get_state(struct udevice *dev)
19 {
20 	struct button_gpio_priv *priv = dev_get_priv(dev);
21 	int ret;
22 
23 	if (!dm_gpio_is_valid(&priv->gpio))
24 		return -EREMOTEIO;
25 	ret = dm_gpio_get_value(&priv->gpio);
26 	if (ret < 0)
27 		return ret;
28 
29 	return ret ? BUTTON_ON : BUTTON_OFF;
30 }
31 
button_gpio_probe(struct udevice * dev)32 static int button_gpio_probe(struct udevice *dev)
33 {
34 	struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev);
35 	struct button_gpio_priv *priv = dev_get_priv(dev);
36 	int ret;
37 
38 	/* Ignore the top-level button node */
39 	if (!uc_plat->label)
40 		return 0;
41 
42 	ret = gpio_request_by_name(dev, "gpios", 0, &priv->gpio, GPIOD_IS_IN);
43 	if (ret)
44 		return ret;
45 
46 	return 0;
47 }
48 
button_gpio_remove(struct udevice * dev)49 static int button_gpio_remove(struct udevice *dev)
50 {
51 	/*
52 	 * The GPIO driver may have already been removed. We will need to
53 	 * address this more generally.
54 	 */
55 	if (!IS_ENABLED(CONFIG_SANDBOX)) {
56 		struct button_gpio_priv *priv = dev_get_priv(dev);
57 
58 		if (dm_gpio_is_valid(&priv->gpio))
59 			dm_gpio_free(dev, &priv->gpio);
60 	}
61 
62 	return 0;
63 }
64 
button_gpio_bind(struct udevice * parent)65 static int button_gpio_bind(struct udevice *parent)
66 {
67 	struct udevice *dev;
68 	ofnode node;
69 	int ret;
70 
71 	dev_for_each_subnode(node, parent) {
72 		struct button_uc_plat *uc_plat;
73 		const char *label;
74 
75 		label = ofnode_read_string(node, "label");
76 		if (!label) {
77 			debug("%s: node %s has no label\n", __func__,
78 			      ofnode_get_name(node));
79 			return -EINVAL;
80 		}
81 		ret = device_bind_driver_to_node(parent, "button_gpio",
82 						 ofnode_get_name(node),
83 						 node, &dev);
84 		if (ret)
85 			return ret;
86 		uc_plat = dev_get_uclass_plat(dev);
87 		uc_plat->label = label;
88 	}
89 
90 	return 0;
91 }
92 
93 static const struct button_ops button_gpio_ops = {
94 	.get_state	= button_gpio_get_state,
95 };
96 
97 static const struct udevice_id button_gpio_ids[] = {
98 	{ .compatible = "gpio-keys" },
99 	{ .compatible = "gpio-keys-polled" },
100 	{ }
101 };
102 
103 U_BOOT_DRIVER(button_gpio) = {
104 	.name		= "button_gpio",
105 	.id		= UCLASS_BUTTON,
106 	.of_match	= button_gpio_ids,
107 	.ops		= &button_gpio_ops,
108 	.priv_auto	= sizeof(struct button_gpio_priv),
109 	.bind		= button_gpio_bind,
110 	.probe		= button_gpio_probe,
111 	.remove		= button_gpio_remove,
112 };
113