aboutsummaryrefslogtreecommitdiff
path: root/sys/arm64/rockchip/rk_grf_gpio.c
blob: 6818bd85bb953870a8a1b56fcab3108794a08a45 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*
 * Copyright (c) 2025 Stephen Hurd <shurd@FreeBSD.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <sys/param.h>
#include <sys/bus.h>
#include <sys/gpio.h>
#include <sys/kernel.h>
#include <sys/module.h>

#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>

#include <dev/gpio/gpiobusvar.h>
#include <dev/syscon/syscon.h>

#include "syscon_if.h"

#define	GRF_SOC_CON10			0x0428
#define	 SOC_CON10_GPIOMUT		(1 << 1)
#define	 SOC_CON10_GPIOMUT_MASK		((1 << 1) << 16)
#define	 SOC_CON10_GPIOMUT_EN		(1 << 0)
#define	 SOC_CON10_GPIOMUT_EN_MASK	((1 << 0) << 16)

struct rk_grf_gpio_softc {
	device_t		sc_dev;
	device_t		sc_busdev;
	struct syscon		*sc_grf;
	bool			active_high;
};

static struct ofw_compat_data compat_data[] = {
	{"rockchip,rk3328-grf-gpio", 1},
	{NULL,             0}
};

static device_t
rk_grf_gpio_get_bus(device_t dev)
{
	struct rk_grf_gpio_softc *sc;

	sc = device_get_softc(dev);

	return (sc->sc_busdev);
}

static int
rk_grf_gpio_pin_max(device_t dev, int *maxpin)
{
	*maxpin = 1;
	return (0);
}

static int
rk_grf_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
{
	if (pin)
		return (EINVAL);

	snprintf(name, GPIOMAXNAME, "GPIO_MUTE");

	return (0);
}

static int
rk_grf_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
{
	if (pin)
		return (EINVAL);
	*flags = GPIO_PIN_OUTPUT;
	return (0);
}

static int
rk_grf_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
{
	if (pin)
		return (EINVAL);
	if (flags != GPIO_PIN_OUTPUT)
		return (EINVAL);

	return (0);
}

static int
rk_grf_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
{
	if (pin)
		return (EINVAL);

	*caps = GPIO_PIN_OUTPUT;
	return (0);
}

static int
rk_grf_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
{
	struct rk_grf_gpio_softc *sc;
	uint32_t reg;

	sc = device_get_softc(dev);

	if (pin)
		return (EINVAL);

	reg = SYSCON_READ_4(sc->sc_grf, GRF_SOC_CON10);
	if (reg & SOC_CON10_GPIOMUT)
		*val = 1;
	else
		*val = 0;

	return (0);
}

static int
rk_grf_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
{
	struct rk_grf_gpio_softc *sc;
	uint32_t val;

	sc = device_get_softc(dev);

	if (pin)
		return (EINVAL);

	val = SOC_CON10_GPIOMUT_MASK;
	if (value)
		val |= SOC_CON10_GPIOMUT;
	SYSCON_WRITE_4(sc->sc_grf, GRF_SOC_CON10, val);

	return (0);
}

static int
rk_grf_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells,
    pcell_t *gpios, uint32_t *pin, uint32_t *flags)
{
	if (gpios[0])
		return (EINVAL);

	/* The gpios are mapped as <pin flags> */
	*pin = 0;
	/* TODO: The only valid flags are active low or active high */
	*flags = GPIO_PIN_OUTPUT;
	return (0);
}

static int
rk_grf_gpio_probe(device_t dev)
{

	if (!ofw_bus_status_okay(dev))
		return (ENXIO);
	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
		return (ENXIO);

	device_set_desc(dev, "RockChip General Register File GPIO (GPIO_MUTE)");
	return (BUS_PROBE_DEFAULT);
}

static int
rk_grf_gpio_attach(device_t dev)
{
	struct rk_grf_gpio_softc *sc;
	phandle_t parent_node, node;
	device_t pdev;

	sc = device_get_softc(dev);
	sc->sc_dev = dev;

	node = ofw_bus_get_node(sc->sc_dev);
	if (!OF_hasprop(node, "gpio-controller"))
		return (ENXIO);
	pdev = device_get_parent(dev);
	parent_node = ofw_bus_get_node(pdev);
	if (syscon_get_by_ofw_node(dev, parent_node, &sc->sc_grf) != 0) {
		device_printf(dev, "cannot get parent syscon handle\n");
		return (ENXIO);
	}

	sc->sc_busdev = gpiobus_attach_bus(dev);
	if (sc->sc_busdev == NULL) {
		return (ENXIO);
	}

	return (0);
}

static int
rk_grf_gpio_detach(device_t dev)
{
	struct rk_grf_gpio_softc *sc;

	sc = device_get_softc(dev);

	if (sc->sc_busdev)
		gpiobus_detach_bus(dev);

	return(0);
}

static device_method_t rk_grf_gpio_methods[] = {
	DEVMETHOD(device_probe, rk_grf_gpio_probe),
	DEVMETHOD(device_attach, rk_grf_gpio_attach),
	DEVMETHOD(device_detach, rk_grf_gpio_detach),

	/* GPIO protocol */
	DEVMETHOD(gpio_get_bus,		rk_grf_gpio_get_bus),
	DEVMETHOD(gpio_pin_max,		rk_grf_gpio_pin_max),
	DEVMETHOD(gpio_pin_getname,	rk_grf_gpio_pin_getname),
	DEVMETHOD(gpio_pin_getflags,	rk_grf_gpio_pin_getflags),
	DEVMETHOD(gpio_pin_setflags,	rk_grf_gpio_pin_setflags),
	DEVMETHOD(gpio_pin_getcaps,	rk_grf_gpio_pin_getcaps),
	DEVMETHOD(gpio_pin_get,		rk_grf_gpio_pin_get),
	DEVMETHOD(gpio_pin_set,		rk_grf_gpio_pin_set),
	DEVMETHOD(gpio_map_gpios,	rk_grf_gpio_map_gpios),

	DEVMETHOD_END
};

static driver_t rk_grf_gpio_driver = {
	"gpio",
	rk_grf_gpio_methods,
	sizeof(struct rk_grf_gpio_softc),
};

/*
 * GPIO driver is always a child of rk_grf driver and should be probed
 * and attached within rk_grf function. Due to this, bus pass order
 * must be same as bus pass order of rk_grf driver.
 */
EARLY_DRIVER_MODULE(rk_grf_gpio, simplebus, rk_grf_gpio_driver, 0, 0,
    BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);