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 	int linux_code;
17 };
18 
button_gpio_get_state(struct udevice * dev)19 static enum button_state_t button_gpio_get_state(struct udevice *dev)
20 {
21 	struct button_gpio_priv *priv = dev_get_priv(dev);
22 	int ret;
23 
24 	if (!dm_gpio_is_valid(&priv->gpio))
25 		return -EREMOTEIO;
26 	ret = dm_gpio_get_value(&priv->gpio);
27 	if (ret < 0)
28 		return ret;
29 
30 	return ret ? BUTTON_ON : BUTTON_OFF;
31 }
32 
button_gpio_get_code(struct udevice * dev)33 static int button_gpio_get_code(struct udevice *dev)
34 {
35 	struct button_gpio_priv *priv = dev_get_priv(dev);
36 	int code = priv->linux_code;
37 
38 	if (!code)
39 		return -ENODATA;
40 
41 	return code;
42 }
43 
button_gpio_probe(struct udevice * dev)44 static int button_gpio_probe(struct udevice *dev)
45 {
46 	struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev);
47 	struct button_gpio_priv *priv = dev_get_priv(dev);
48 	int ret;
49 
50 	/* Ignore the top-level button node */
51 	if (!uc_plat->label)
52 		return 0;
53 
54 	ret = gpio_request_by_name(dev, "gpios", 0, &priv->gpio, GPIOD_IS_IN);
55 	if (ret)
56 		return ret;
57 
58 	ret = dev_read_u32(dev, "linux,code", &priv->linux_code);
59 
60 	return ret;
61 }
62 
button_gpio_remove(struct udevice * dev)63 static int button_gpio_remove(struct udevice *dev)
64 {
65 	/*
66 	 * The GPIO driver may have already been removed. We will need to
67 	 * address this more generally.
68 	 */
69 	if (!IS_ENABLED(CONFIG_SANDBOX)) {
70 		struct button_gpio_priv *priv = dev_get_priv(dev);
71 
72 		if (dm_gpio_is_valid(&priv->gpio))
73 			dm_gpio_free(dev, &priv->gpio);
74 	}
75 
76 	return 0;
77 }
78 
button_gpio_bind(struct udevice * parent)79 static int button_gpio_bind(struct udevice *parent)
80 {
81 	struct udevice *dev;
82 	ofnode node;
83 	int ret;
84 
85 	dev_for_each_subnode(node, parent) {
86 		struct button_uc_plat *uc_plat;
87 		const char *label;
88 
89 		label = ofnode_read_string(node, "label");
90 		if (!label) {
91 			debug("%s: node %s has no label\n", __func__,
92 			      ofnode_get_name(node));
93 			return -EINVAL;
94 		}
95 		ret = device_bind_driver_to_node(parent, "button_gpio",
96 						 ofnode_get_name(node),
97 						 node, &dev);
98 		if (ret)
99 			return ret;
100 		uc_plat = dev_get_uclass_plat(dev);
101 		uc_plat->label = label;
102 	}
103 
104 	return 0;
105 }
106 
107 static const struct button_ops button_gpio_ops = {
108 	.get_state	= button_gpio_get_state,
109 	.get_code	= button_gpio_get_code,
110 };
111 
112 static const struct udevice_id button_gpio_ids[] = {
113 	{ .compatible = "gpio-keys" },
114 	{ .compatible = "gpio-keys-polled" },
115 	{ }
116 };
117 
118 U_BOOT_DRIVER(button_gpio) = {
119 	.name		= "button_gpio",
120 	.id		= UCLASS_BUTTON,
121 	.of_match	= button_gpio_ids,
122 	.ops		= &button_gpio_ops,
123 	.priv_auto	= sizeof(struct button_gpio_priv),
124 	.bind		= button_gpio_bind,
125 	.probe		= button_gpio_probe,
126 	.remove		= button_gpio_remove,
127 };
128