1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c), Vaisala Oyj
4 */
5
6#include <asm/gpio.h>
7#include <dm.h>
8#include <dm/devres.h>
9#include <errno.h>
10#include <reboot-mode/reboot-mode-gpio.h>
11#include <reboot-mode/reboot-mode.h>
12
13DECLARE_GLOBAL_DATA_PTR;
14
15static int reboot_mode_get(struct udevice *dev, u32 *buf)
16{
17	int ret;
18	struct reboot_mode_gpio_platdata *plat_data;
19
20	if (!buf)
21		return -EINVAL;
22
23	plat_data = dev_get_plat(dev);
24	if (!plat_data)
25		return -EINVAL;
26
27	ret = dm_gpio_get_values_as_int(plat_data->gpio_desc,
28					plat_data->gpio_count);
29	if (ret < 0)
30		return ret;
31
32	*buf = ret;
33
34	return 0;
35}
36
37static int reboot_mode_probe(struct udevice *dev)
38{
39	struct reboot_mode_gpio_platdata *plat_data;
40
41	plat_data = dev_get_plat(dev);
42	if (!plat_data)
43		return -EINVAL;
44
45	int ret;
46
47#if CONFIG_IS_ENABLED(OF_CONTROL)
48	ret = gpio_get_list_count(dev, "gpios");
49	if (ret < 0)
50		return ret;
51
52	plat_data->gpio_count = ret;
53#endif
54
55	if (plat_data->gpio_count <= 0)
56		return -EINVAL;
57
58	plat_data->gpio_desc = devm_kcalloc(dev, plat_data->gpio_count,
59					    sizeof(struct gpio_desc), 0);
60	if (!plat_data->gpio_desc)
61		return -ENOMEM;
62
63#if CONFIG_IS_ENABLED(OF_CONTROL)
64	ret = gpio_request_list_by_name(dev, "gpios", plat_data->gpio_desc,
65					plat_data->gpio_count, GPIOD_IS_IN);
66	if (ret < 0)
67		return ret;
68#else
69	for (int i = 0; i < plat_data->gpio_count; i++) {
70		struct reboot_mode_gpio_config *gpio =
71			plat_data->gpios_config + i;
72		struct gpio_desc *desc = plat_data->gpio_desc + i;
73
74		ret = uclass_get_device_by_seq(UCLASS_GPIO,
75					       gpio->gpio_dev_offset,
76					       &desc->dev);
77		if (ret < 0)
78			return ret;
79
80		desc->flags = gpio->flags;
81		desc->offset = gpio->gpio_offset;
82
83		ret = dm_gpio_request(desc, "");
84		if (ret < 0)
85			return ret;
86
87		ret = dm_gpio_set_dir(desc);
88		if (ret < 0)
89			return ret;
90	}
91#endif
92	return 0;
93}
94
95static int reboot_mode_remove(struct udevice *dev)
96{
97	struct reboot_mode_gpio_platdata *plat_data;
98
99	plat_data = dev_get_plat(dev);
100	if (!plat_data)
101		return -EINVAL;
102
103	return gpio_free_list(dev, plat_data->gpio_desc, plat_data->gpio_count);
104}
105
106#if CONFIG_IS_ENABLED(OF_CONTROL)
107static const struct udevice_id reboot_mode_ids[] = {
108	{ .compatible = "reboot-mode-gpio", 0 },
109	{ }
110};
111#endif
112
113static const struct reboot_mode_ops reboot_mode_gpio_ops = {
114	.get = reboot_mode_get,
115};
116
117U_BOOT_DRIVER(reboot_mode_gpio) = {
118	.name = "reboot-mode-gpio",
119	.id = UCLASS_REBOOT_MODE,
120	.probe = reboot_mode_probe,
121	.remove = reboot_mode_remove,
122#if CONFIG_IS_ENABLED(OF_CONTROL)
123	.of_match = reboot_mode_ids,
124#endif
125	.plat_auto = sizeof(struct reboot_mode_gpio_platdata),
126	.ops = &reboot_mode_gpio_ops,
127};
128