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