xref: /openbsd-src/sys/arch/arm64/dev/rpigpio.c (revision 6b382705f6a0a25d512acdc202a123ffdb05e9e0)
1*6b382705Spatrick /* $OpenBSD: rpigpio.c,v 1.1 2024/03/25 17:24:03 patrick Exp $ */
2*6b382705Spatrick /*
3*6b382705Spatrick  * Copyright (c) 2024 Patrick Wildt <patrick@blueri.se>
4*6b382705Spatrick  *
5*6b382705Spatrick  * Permission to use, copy, modify, and distribute this software for any
6*6b382705Spatrick  * purpose with or without fee is hereby granted, provided that the above
7*6b382705Spatrick  * copyright notice and this permission notice appear in all copies.
8*6b382705Spatrick  *
9*6b382705Spatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10*6b382705Spatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*6b382705Spatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12*6b382705Spatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*6b382705Spatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14*6b382705Spatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15*6b382705Spatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*6b382705Spatrick  */
17*6b382705Spatrick 
18*6b382705Spatrick #include <sys/param.h>
19*6b382705Spatrick #include <sys/systm.h>
20*6b382705Spatrick #include <sys/device.h>
21*6b382705Spatrick #include <sys/malloc.h>
22*6b382705Spatrick 
23*6b382705Spatrick #include <machine/bus.h>
24*6b382705Spatrick #include <machine/fdt.h>
25*6b382705Spatrick 
26*6b382705Spatrick #include <dev/ofw/openfirm.h>
27*6b382705Spatrick #include <dev/ofw/ofw_gpio.h>
28*6b382705Spatrick #include <dev/ofw/fdt.h>
29*6b382705Spatrick 
30*6b382705Spatrick #define GPIOx_STATUS(x)			(0x000 + (x) * 0x8)
31*6b382705Spatrick #define  GPIOx_STATUS_OUTFROMPERI		(1 << 8)
32*6b382705Spatrick #define  GPIOx_STATUS_OUTFROMPAD		(1 << 9)
33*6b382705Spatrick #define  GPIOx_STATUS_OEFROMPERI		(1 << 12)
34*6b382705Spatrick #define  GPIOx_STATUS_OEFROMPAD			(1 << 13)
35*6b382705Spatrick #define  GPIOx_STATUS_INISDIRECT		(1 << 16)
36*6b382705Spatrick #define  GPIOx_STATUS_INFROMPAD			(1 << 17)
37*6b382705Spatrick #define  GPIOx_STATUS_INFILTERED		(1 << 18)
38*6b382705Spatrick #define  GPIOx_STATUS_INTOPERI			(1 << 19)
39*6b382705Spatrick #define  GPIOx_STATUS_EVENT_EDGE_LOW		(1 << 20)
40*6b382705Spatrick #define  GPIOx_STATUS_EVENT_EDGE_HIGH		(1 << 21)
41*6b382705Spatrick #define  GPIOx_STATUS_EVENT_LEVEL_LOW		(1 << 22)
42*6b382705Spatrick #define  GPIOx_STATUS_EVENT_LEVEL_HIGH		(1 << 23)
43*6b382705Spatrick #define  GPIOx_STATUS_EVENT_F_EDGE_LOW		(1 << 24)
44*6b382705Spatrick #define  GPIOx_STATUS_EVENT_F_EDGE_HIGH		(1 << 25)
45*6b382705Spatrick #define  GPIOx_STATUS_EVENT_DB_LEVEL_LOW	(1 << 26)
46*6b382705Spatrick #define  GPIOx_STATUS_EVENT_DB_LEVEL_HIGH	(1 << 27)
47*6b382705Spatrick #define  GPIOx_STATUS_IRQCOMBINED		(1 << 28)
48*6b382705Spatrick #define  GPIOx_STATUS_IRQTOPROC			(1 << 29)
49*6b382705Spatrick #define GPIOx_CTRL(x)			(0x004 + (x) * 0x8)
50*6b382705Spatrick #define  GPIOx_CTRL_FUNCSEL_MASK		(0x1f << 0)
51*6b382705Spatrick #define  GPIOx_CTRL_FUNCSEL(x)			((x) << 0)
52*6b382705Spatrick #define  GPIOx_CTRL_FUNCSEL_GPIO		((5) << 0)
53*6b382705Spatrick #define  GPIOx_CTRL_OUTOVER_MASK		(0x3 << 12)
54*6b382705Spatrick #define  GPIOx_CTRL_OUTOVER_NRMFUNC		(0 << 12)
55*6b382705Spatrick #define  GPIOx_CTRL_OUTOVER_INVFUNC		(1 << 12)
56*6b382705Spatrick #define  GPIOx_CTRL_OUTOVER_LOW			(2 << 12)
57*6b382705Spatrick #define  GPIOx_CTRL_OUTOVER_HIGH		(3 << 12)
58*6b382705Spatrick #define  GPIOx_CTRL_OEOVER_MASK			(0x3 << 14)
59*6b382705Spatrick #define  GPIOx_CTRL_OEOVER_NRMFUNC		(0 << 14)
60*6b382705Spatrick #define  GPIOx_CTRL_OEOVER_INVFUNC		(1 << 14)
61*6b382705Spatrick #define  GPIOx_CTRL_OEOVER_DIS			(2 << 14)
62*6b382705Spatrick #define  GPIOx_CTRL_OEOVER_ENA			(3 << 14)
63*6b382705Spatrick #define  GPIOx_CTRL_INOVER_MASK			(0x3 << 16)
64*6b382705Spatrick #define  GPIOx_CTRL_INOVER_NRMFUNC		(0 << 16)
65*6b382705Spatrick #define  GPIOx_CTRL_INOVER_INVFUNC		(1 << 16)
66*6b382705Spatrick #define  GPIOx_CTRL_INOVER_LOW			(2 << 16)
67*6b382705Spatrick #define  GPIOx_CTRL_INOVER_HIGH			(3 << 16)
68*6b382705Spatrick #define  GPIOx_CTRL_IRQMASK_EDGE_LOW		(1 << 20)
69*6b382705Spatrick #define  GPIOx_CTRL_IRQMASK_EDGE_HIGH		(1 << 21)
70*6b382705Spatrick #define  GPIOx_CTRL_IRQMASK_LEVEL_LOW		(1 << 22)
71*6b382705Spatrick #define  GPIOx_CTRL_IRQMASK_LEVEL_HIGH		(1 << 23)
72*6b382705Spatrick #define  GPIOx_CTRL_IRQMASK_F_EDGE_LOW		(1 << 24)
73*6b382705Spatrick #define  GPIOx_CTRL_IRQMASK_F_EDGE_HIGH		(1 << 25)
74*6b382705Spatrick #define  GPIOx_CTRL_IRQMASK_DB_LEVEL_LOW	(1 << 26)
75*6b382705Spatrick #define  GPIOx_CTRL_IRQMASK_DB_LEVEL_HIGH	(1 << 27)
76*6b382705Spatrick #define  GPIOx_CTRL_IRQRESET			(1 << 28)
77*6b382705Spatrick #define  GPIOx_CTRL_IRQOVER_MASK		(0x3U << 30)
78*6b382705Spatrick #define  GPIOx_CTRL_IRQOVER_NRMFUNC		(0U << 30)
79*6b382705Spatrick #define  GPIOx_CTRL_IRQOVER_INVFUNC		(1U << 30)
80*6b382705Spatrick #define  GPIOx_CTRL_IRQOVER_LOW			(2U << 30)
81*6b382705Spatrick #define  GPIOx_CTRL_IRQOVER_HIGH		(3U << 30)
82*6b382705Spatrick 
83*6b382705Spatrick #define RIO_OUT				0x000
84*6b382705Spatrick #define RIO_OE				0x004
85*6b382705Spatrick #define RIO_IN				0x008
86*6b382705Spatrick 
87*6b382705Spatrick #define PAD_CTRL(x)			(0x000 + (x) * 0x4)
88*6b382705Spatrick #define  PAD_CTRL_IN_EN				(1 << 6)
89*6b382705Spatrick #define  PAD_CTRL_OUT_DIS			(1 << 7)
90*6b382705Spatrick 
91*6b382705Spatrick #define GPIO_NUM_PINS		54
92*6b382705Spatrick 
93*6b382705Spatrick struct rpigpio_softc {
94*6b382705Spatrick 	struct device		sc_dev;
95*6b382705Spatrick 	bus_space_tag_t		sc_iot;
96*6b382705Spatrick 	bus_space_handle_t	sc_gpio_ioh;
97*6b382705Spatrick 	bus_space_handle_t	sc_rio_ioh;
98*6b382705Spatrick 	bus_space_handle_t	sc_pads_ioh;
99*6b382705Spatrick 	int			sc_node;
100*6b382705Spatrick 
101*6b382705Spatrick 	struct gpio_controller	sc_gc;
102*6b382705Spatrick };
103*6b382705Spatrick 
104*6b382705Spatrick int rpigpio_match(struct device *, void *, void *);
105*6b382705Spatrick void rpigpio_attach(struct device *, struct device *, void *);
106*6b382705Spatrick 
107*6b382705Spatrick const struct rpigpio_bank *rpigpio_get_bank(struct rpigpio_softc *, uint32_t);
108*6b382705Spatrick void rpigpio_config_pin(void *, uint32_t *, int);
109*6b382705Spatrick int rpigpio_get_pin(void *, uint32_t *);
110*6b382705Spatrick void rpigpio_set_pin(void *, uint32_t *, int);
111*6b382705Spatrick 
112*6b382705Spatrick const struct cfattach rpigpio_ca = {
113*6b382705Spatrick 	sizeof (struct rpigpio_softc), rpigpio_match, rpigpio_attach
114*6b382705Spatrick };
115*6b382705Spatrick 
116*6b382705Spatrick struct cfdriver rpigpio_cd = {
117*6b382705Spatrick 	NULL, "rpigpio", DV_DULL
118*6b382705Spatrick };
119*6b382705Spatrick 
120*6b382705Spatrick int
rpigpio_match(struct device * parent,void * match,void * aux)121*6b382705Spatrick rpigpio_match(struct device *parent, void *match, void *aux)
122*6b382705Spatrick {
123*6b382705Spatrick 	struct fdt_attach_args *faa = aux;
124*6b382705Spatrick 
125*6b382705Spatrick 	return OF_is_compatible(faa->fa_node, "raspberrypi,rp1-gpio");
126*6b382705Spatrick }
127*6b382705Spatrick 
128*6b382705Spatrick void
rpigpio_attach(struct device * parent,struct device * self,void * aux)129*6b382705Spatrick rpigpio_attach(struct device *parent, struct device *self, void *aux)
130*6b382705Spatrick {
131*6b382705Spatrick 	struct rpigpio_softc *sc = (struct rpigpio_softc *)self;
132*6b382705Spatrick 	struct fdt_attach_args *faa = aux;
133*6b382705Spatrick 
134*6b382705Spatrick 	if (faa->fa_nreg < 3)
135*6b382705Spatrick 		return;
136*6b382705Spatrick 
137*6b382705Spatrick 	sc->sc_node = faa->fa_node;
138*6b382705Spatrick 	sc->sc_iot = faa->fa_iot;
139*6b382705Spatrick 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
140*6b382705Spatrick 	    faa->fa_reg[0].size, 0, &sc->sc_gpio_ioh)) {
141*6b382705Spatrick 		printf(": can't map GPIO registers\n");
142*6b382705Spatrick 		return;
143*6b382705Spatrick 	}
144*6b382705Spatrick 	if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
145*6b382705Spatrick 	    faa->fa_reg[1].size, 0, &sc->sc_rio_ioh)) {
146*6b382705Spatrick 		bus_space_unmap(sc->sc_iot, sc->sc_gpio_ioh,
147*6b382705Spatrick 		    faa->fa_reg[0].size);
148*6b382705Spatrick 		printf(": can't map RIO registers\n");
149*6b382705Spatrick 		return;
150*6b382705Spatrick 	}
151*6b382705Spatrick 	if (bus_space_map(sc->sc_iot, faa->fa_reg[2].addr,
152*6b382705Spatrick 	    faa->fa_reg[2].size, 0, &sc->sc_pads_ioh)) {
153*6b382705Spatrick 		bus_space_unmap(sc->sc_iot, sc->sc_rio_ioh,
154*6b382705Spatrick 		    faa->fa_reg[1].size);
155*6b382705Spatrick 		bus_space_unmap(sc->sc_iot, sc->sc_gpio_ioh,
156*6b382705Spatrick 		    faa->fa_reg[0].size);
157*6b382705Spatrick 		printf(": can't map PADS registers\n");
158*6b382705Spatrick 		return;
159*6b382705Spatrick 	}
160*6b382705Spatrick 
161*6b382705Spatrick 	sc->sc_gc.gc_node = faa->fa_node;
162*6b382705Spatrick 	sc->sc_gc.gc_cookie = sc;
163*6b382705Spatrick 	sc->sc_gc.gc_config_pin = rpigpio_config_pin;
164*6b382705Spatrick 	sc->sc_gc.gc_get_pin = rpigpio_get_pin;
165*6b382705Spatrick 	sc->sc_gc.gc_set_pin = rpigpio_set_pin;
166*6b382705Spatrick 	gpio_controller_register(&sc->sc_gc);
167*6b382705Spatrick 
168*6b382705Spatrick 	printf("\n");
169*6b382705Spatrick }
170*6b382705Spatrick 
171*6b382705Spatrick struct rpigpio_bank {
172*6b382705Spatrick 	uint32_t start;
173*6b382705Spatrick 	uint32_t num;
174*6b382705Spatrick 	uint32_t gpio;
175*6b382705Spatrick 	uint32_t rio;
176*6b382705Spatrick 	uint32_t pads;
177*6b382705Spatrick };
178*6b382705Spatrick 
179*6b382705Spatrick const struct rpigpio_bank rpigpio_banks[] = {
180*6b382705Spatrick 	{  0, 28, 0x0000, 0x0000, 0x0004 },
181*6b382705Spatrick 	{ 28,  6, 0x4000, 0x4000, 0x4004 },
182*6b382705Spatrick 	{ 34, 20, 0x8000, 0x8000, 0x8004 },
183*6b382705Spatrick };
184*6b382705Spatrick 
185*6b382705Spatrick const struct rpigpio_bank *
rpigpio_get_bank(struct rpigpio_softc * sc,uint32_t pin)186*6b382705Spatrick rpigpio_get_bank(struct rpigpio_softc *sc, uint32_t pin)
187*6b382705Spatrick {
188*6b382705Spatrick 	const struct rpigpio_bank *bank;
189*6b382705Spatrick 	int i;
190*6b382705Spatrick 
191*6b382705Spatrick 	for (i = 0; i < nitems(rpigpio_banks); i++) {
192*6b382705Spatrick 		bank = &rpigpio_banks[i];
193*6b382705Spatrick 		if (pin >= bank->start && pin < bank->start + bank->num) {
194*6b382705Spatrick 			return bank;
195*6b382705Spatrick 		}
196*6b382705Spatrick 	}
197*6b382705Spatrick 
198*6b382705Spatrick 	printf("%s: can't find pin %d\n", sc->sc_dev.dv_xname, pin);
199*6b382705Spatrick 	return NULL;
200*6b382705Spatrick }
201*6b382705Spatrick 
202*6b382705Spatrick void
rpigpio_config_pin(void * cookie,uint32_t * cells,int config)203*6b382705Spatrick rpigpio_config_pin(void *cookie, uint32_t *cells, int config)
204*6b382705Spatrick {
205*6b382705Spatrick 	struct rpigpio_softc *sc = cookie;
206*6b382705Spatrick 	const struct rpigpio_bank *bank;
207*6b382705Spatrick 	uint32_t pin = cells[0];
208*6b382705Spatrick 	uint32_t val;
209*6b382705Spatrick 
210*6b382705Spatrick 	bank = rpigpio_get_bank(sc, pin);
211*6b382705Spatrick 	if (bank == NULL)
212*6b382705Spatrick 		return;
213*6b382705Spatrick 	pin -= bank->start;
214*6b382705Spatrick 
215*6b382705Spatrick 	/* Configure pin to be input or output */
216*6b382705Spatrick 	val = bus_space_read_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_OE);
217*6b382705Spatrick 	if (config & GPIO_CONFIG_OUTPUT)
218*6b382705Spatrick 		val |= 1 << pin;
219*6b382705Spatrick 	else
220*6b382705Spatrick 		val &= ~(1 << pin);
221*6b382705Spatrick 	bus_space_write_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_OE, val);
222*6b382705Spatrick 
223*6b382705Spatrick 	/* Enable input/output on pad */
224*6b382705Spatrick 	val = bus_space_read_4(sc->sc_iot, sc->sc_pads_ioh, bank->pads +
225*6b382705Spatrick 	    PAD_CTRL(pin));
226*6b382705Spatrick 	val &= ~PAD_CTRL_OUT_DIS;
227*6b382705Spatrick 	val |= PAD_CTRL_IN_EN;
228*6b382705Spatrick 	bus_space_write_4(sc->sc_iot, sc->sc_pads_ioh, bank->pads +
229*6b382705Spatrick 	    PAD_CTRL(pin), val);
230*6b382705Spatrick 
231*6b382705Spatrick 	/* Configure pin as GPIO in standard mode */
232*6b382705Spatrick 	val = bus_space_read_4(sc->sc_iot, sc->sc_gpio_ioh, bank->gpio +
233*6b382705Spatrick 	    GPIOx_CTRL(pin));
234*6b382705Spatrick 	val &= ~(GPIOx_CTRL_FUNCSEL_MASK | GPIOx_CTRL_OUTOVER_MASK |
235*6b382705Spatrick 	    GPIOx_CTRL_OEOVER_MASK);
236*6b382705Spatrick 	val |= (GPIOx_CTRL_FUNCSEL_GPIO | GPIOx_CTRL_OUTOVER_NRMFUNC |
237*6b382705Spatrick 	    GPIOx_CTRL_OEOVER_NRMFUNC);
238*6b382705Spatrick 	bus_space_write_4(sc->sc_iot, sc->sc_gpio_ioh, bank->gpio +
239*6b382705Spatrick 	    GPIOx_CTRL(pin), val);
240*6b382705Spatrick }
241*6b382705Spatrick 
242*6b382705Spatrick int
rpigpio_get_pin(void * cookie,uint32_t * cells)243*6b382705Spatrick rpigpio_get_pin(void *cookie, uint32_t *cells)
244*6b382705Spatrick {
245*6b382705Spatrick 	struct rpigpio_softc *sc = cookie;
246*6b382705Spatrick 	const struct rpigpio_bank *bank;
247*6b382705Spatrick 	uint32_t pin = cells[0];
248*6b382705Spatrick 	uint32_t flags = cells[1];
249*6b382705Spatrick 	uint32_t reg;
250*6b382705Spatrick 	int val;
251*6b382705Spatrick 
252*6b382705Spatrick 	bank = rpigpio_get_bank(sc, pin);
253*6b382705Spatrick 	if (bank == NULL)
254*6b382705Spatrick 		return 0;
255*6b382705Spatrick 	pin -= bank->start;
256*6b382705Spatrick 
257*6b382705Spatrick 	reg = bus_space_read_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_IN);
258*6b382705Spatrick 	reg &= (1 << pin);
259*6b382705Spatrick 	val = (reg >> pin) & 1;
260*6b382705Spatrick 	if (flags & GPIO_ACTIVE_LOW)
261*6b382705Spatrick 		val = !val;
262*6b382705Spatrick 	return val;
263*6b382705Spatrick }
264*6b382705Spatrick 
265*6b382705Spatrick void
rpigpio_set_pin(void * cookie,uint32_t * cells,int val)266*6b382705Spatrick rpigpio_set_pin(void *cookie, uint32_t *cells, int val)
267*6b382705Spatrick {
268*6b382705Spatrick 	struct rpigpio_softc *sc = cookie;
269*6b382705Spatrick 	const struct rpigpio_bank *bank;
270*6b382705Spatrick 	uint32_t pin = cells[0];
271*6b382705Spatrick 	uint32_t flags = cells[1];
272*6b382705Spatrick 	uint32_t reg;
273*6b382705Spatrick 
274*6b382705Spatrick 	bank = rpigpio_get_bank(sc, pin);
275*6b382705Spatrick 	if (bank == NULL)
276*6b382705Spatrick 		return;
277*6b382705Spatrick 	pin -= bank->start;
278*6b382705Spatrick 
279*6b382705Spatrick 	reg = bus_space_read_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_OUT);
280*6b382705Spatrick 	if (flags & GPIO_ACTIVE_LOW)
281*6b382705Spatrick 		val = !val;
282*6b382705Spatrick 	if (val)
283*6b382705Spatrick 		reg |= (1 << pin);
284*6b382705Spatrick 	else
285*6b382705Spatrick 		reg &= ~(1 << pin);
286*6b382705Spatrick 	bus_space_write_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_OUT, reg);
287*6b382705Spatrick }
288