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