1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
5 * Copyright (c) 2012-2015 Luiz Otavio O Souza <loos@FreeBSD.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 */
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include "opt_platform.h"
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/gpio.h>
39 #include <sys/kernel.h>
40 #include <sys/lock.h>
41 #include <sys/module.h>
42 #include <sys/sx.h>
43 #include <sys/proc.h>
44
45 #include <dev/gpio/gpiobusvar.h>
46 #include <dev/ofw/ofw_bus.h>
47
48 #include <arm/broadcom/bcm2835/bcm2835_firmware.h>
49
50 #include "gpio_if.h"
51
52 #define RPI_FW_GPIO_PINS 8
53 #define RPI_FW_GPIO_BASE 128
54 #define RPI_FW_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
55
56 struct rpi_fw_gpio_softc {
57 device_t sc_busdev;
58 device_t sc_firmware;
59 struct sx sc_sx;
60 struct gpio_pin sc_gpio_pins[RPI_FW_GPIO_PINS];
61 uint8_t sc_gpio_state;
62 };
63
64 #define RPI_FW_GPIO_LOCK(_sc) sx_xlock(&(_sc)->sc_sx)
65 #define RPI_FW_GPIO_UNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx)
66
67 static struct ofw_compat_data compat_data[] = {
68 {"raspberrypi,firmware-gpio", 1},
69 {NULL, 0}
70 };
71
72 static int
73 rpi_fw_gpio_pin_configure(struct rpi_fw_gpio_softc *sc, struct gpio_pin *pin,
74 unsigned int flags)
75 {
76 union msg_get_gpio_config old_cfg;
77 union msg_set_gpio_config new_cfg;
78 int rv;
79
80 bzero(&old_cfg, sizeof(old_cfg));
81 bzero(&new_cfg, sizeof(new_cfg));
82 old_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;
83
84 RPI_FW_GPIO_LOCK(sc);
85 rv = bcm2835_firmware_property(sc->sc_firmware,
86 BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &old_cfg, sizeof(old_cfg));
87 if (rv == 0 && old_cfg.resp.gpio != 0)
88 rv = EIO;
89 if (rv != 0)
90 goto fail;
91
92 new_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;
93 if (flags & GPIO_PIN_INPUT) {
94 new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_IN;
95 new_cfg.req.state = 0;
96 pin->gp_flags = GPIO_PIN_INPUT;
97 } else if (flags & GPIO_PIN_OUTPUT) {
98 new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_OUT;
99 if (flags & (GPIO_PIN_PRESET_HIGH | GPIO_PIN_PRESET_LOW)) {
100 if (flags & GPIO_PIN_PRESET_HIGH) {
101 new_cfg.req.state = 1;
102 sc->sc_gpio_state |= (1 << pin->gp_pin);
103 } else {
104 new_cfg.req.state = 0;
105 sc->sc_gpio_state &= ~(1 << pin->gp_pin);
106 }
107 } else {
108 if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0) {
109 new_cfg.req.state = 1;
110 } else {
111 new_cfg.req.state = 0;
112 }
113 }
114 pin->gp_flags = GPIO_PIN_OUTPUT;
115 } else {
116 new_cfg.req.dir = old_cfg.resp.dir;
117 /* Use the old state to decide high/low */
118 if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0)
119 new_cfg.req.state = 1;
120 else
121 new_cfg.req.state = 0;
122 }
123 new_cfg.req.pol = old_cfg.resp.pol;
124 new_cfg.req.term_en = 0;
125 new_cfg.req.term_pull_up = 0;
126
127 rv = bcm2835_firmware_property(sc->sc_firmware,
128 BCM2835_FIRMWARE_TAG_SET_GPIO_CONFIG, &new_cfg, sizeof(new_cfg));
129
130 fail:
131 RPI_FW_GPIO_UNLOCK(sc);
132
133 return (rv);
134 }
135
136 static device_t
137 rpi_fw_gpio_get_bus(device_t dev)
138 {
139 struct rpi_fw_gpio_softc *sc;
140
141 sc = device_get_softc(dev);
142
143 return (sc->sc_busdev);
144 }
145
146 static int
147 rpi_fw_gpio_pin_max(device_t dev, int *maxpin)
148 {
149
150 *maxpin = RPI_FW_GPIO_PINS - 1;
151 return (0);
152 }
153
154 static int
155 rpi_fw_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
156 {
157 struct rpi_fw_gpio_softc *sc;
158 int i;
159
160 sc = device_get_softc(dev);
161 for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
162 if (sc->sc_gpio_pins[i].gp_pin == pin)
163 break;
164 }
165
166 if (i >= RPI_FW_GPIO_PINS)
167 return (EINVAL);
168
169 *caps = RPI_FW_GPIO_DEFAULT_CAPS;
170 return (0);
171 }
172
173 static int
174 rpi_fw_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
175 {
176 struct rpi_fw_gpio_softc *sc = device_get_softc(dev);
177 int i;
178
179 for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
180 if (sc->sc_gpio_pins[i].gp_pin == pin)
181 break;
182 }
183
184 if (i >= RPI_FW_GPIO_PINS)
185 return (EINVAL);
186
187 RPI_FW_GPIO_LOCK(sc);
188 *flags = sc->sc_gpio_pins[i].gp_flags;
189 RPI_FW_GPIO_UNLOCK(sc);
190
191 return (0);
192 }
193
194 static int
195 rpi_fw_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
196 {
197 struct rpi_fw_gpio_softc *sc;
198 int i;
199
200 sc = device_get_softc(dev);
201 for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
202 if (sc->sc_gpio_pins[i].gp_pin == pin)
203 break;
204 }
205
206 if (i >= RPI_FW_GPIO_PINS)
207 return (EINVAL);
208
209 RPI_FW_GPIO_LOCK(sc);
210 memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME);
211 RPI_FW_GPIO_UNLOCK(sc);
212
213 return (0);
214 }
215
216 static int
217 rpi_fw_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
218 {
219 struct rpi_fw_gpio_softc *sc;
220 int i;
221
222 sc = device_get_softc(dev);
223 for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
224 if (sc->sc_gpio_pins[i].gp_pin == pin)
225 break;
226 }
227
228 if (i >= RPI_FW_GPIO_PINS)
229 return (EINVAL);
230
231 return (rpi_fw_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags));
232 }
233
234 static int
235 rpi_fw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
236 {
237 struct rpi_fw_gpio_softc *sc;
238 union msg_set_gpio_state state;
239 int i, rv;
240
241 sc = device_get_softc(dev);
242 for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
243 if (sc->sc_gpio_pins[i].gp_pin == pin)
244 break;
245 }
246 if (i >= RPI_FW_GPIO_PINS)
247 return (EINVAL);
248
249 state.req.gpio = RPI_FW_GPIO_BASE + pin;
250 state.req.state = value;
251
252 RPI_FW_GPIO_LOCK(sc);
253 rv = bcm2835_firmware_property(sc->sc_firmware,
254 BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &state, sizeof(state));
255 /* The firmware sets gpio to 0 on success */
256 if (rv == 0 && state.resp.gpio != 0)
257 rv = EINVAL;
258 if (rv == 0) {
259 sc->sc_gpio_pins[i].gp_flags &= ~(GPIO_PIN_PRESET_HIGH |
260 GPIO_PIN_PRESET_LOW);
261 if (value)
262 sc->sc_gpio_state |= (1 << i);
263 else
264 sc->sc_gpio_state &= ~(1 << i);
265 }
266 RPI_FW_GPIO_UNLOCK(sc);
267
268 return (rv);
269 }
270
271 static int
272 rpi_fw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
273 {
274 struct rpi_fw_gpio_softc *sc;
275 union msg_get_gpio_state state;
276 int i, rv;
277
278 sc = device_get_softc(dev);
279 for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
280 if (sc->sc_gpio_pins[i].gp_pin == pin)
281 break;
282 }
283 if (i >= RPI_FW_GPIO_PINS)
284 return (EINVAL);
285
286 bzero(&state, sizeof(state));
287 state.req.gpio = RPI_FW_GPIO_BASE + pin;
288
289 RPI_FW_GPIO_LOCK(sc);
290 rv = bcm2835_firmware_property(sc->sc_firmware,
291 BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &state, sizeof(state));
292 RPI_FW_GPIO_UNLOCK(sc);
293
294 /* The firmware sets gpio to 0 on success */
295 if (rv == 0 && state.resp.gpio != 0)
296 rv = EINVAL;
297 if (rv == 0)
298 *val = !state.resp.state;
299
300 return (rv);
301 }
302
303 static int
304 rpi_fw_gpio_pin_toggle(device_t dev, uint32_t pin)
305 {
306 struct rpi_fw_gpio_softc *sc;
307 union msg_get_gpio_state old_state;
308 union msg_set_gpio_state new_state;
309 int i, rv;
310
311 sc = device_get_softc(dev);
312 for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
313 if (sc->sc_gpio_pins[i].gp_pin == pin)
314 break;
315 }
316 if (i >= RPI_FW_GPIO_PINS)
317 return (EINVAL);
318
319 bzero(&old_state, sizeof(old_state));
320 bzero(&new_state, sizeof(new_state));
321
322 old_state.req.gpio = RPI_FW_GPIO_BASE + pin;
323 new_state.req.gpio = RPI_FW_GPIO_BASE + pin;
324
325 RPI_FW_GPIO_LOCK(sc);
326 rv = bcm2835_firmware_property(sc->sc_firmware,
327 BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &old_state, sizeof(old_state));
328 /* The firmware sets gpio to 0 on success */
329 if (rv == 0 && old_state.resp.gpio == 0) {
330 /* Set the new state to invert the GPIO */
331 new_state.req.state = !old_state.resp.state;
332 rv = bcm2835_firmware_property(sc->sc_firmware,
333 BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &new_state,
334 sizeof(new_state));
335 }
336 if (rv == 0 && (old_state.resp.gpio != 0 || new_state.resp.gpio != 0))
337 rv = EINVAL;
338 RPI_FW_GPIO_UNLOCK(sc);
339
340 return (rv);
341 }
342
343 static int
344 rpi_fw_gpio_probe(device_t dev)
345 {
346
347 if (!ofw_bus_status_okay(dev))
348 return (ENXIO);
349
350 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
351 return (ENXIO);
352
353 device_set_desc(dev, "Raspberry Pi Firmware GPIO controller");
354 return (BUS_PROBE_DEFAULT);
355 }
356
357 static int
358 rpi_fw_gpio_attach(device_t dev)
359 {
360 union msg_get_gpio_config cfg;
361 struct rpi_fw_gpio_softc *sc;
362 char *names;
363 phandle_t gpio;
364 int i, nelems, elm_pos, rv;
365
366 sc = device_get_softc(dev);
367 sc->sc_firmware = device_get_parent(dev);
368 sx_init(&sc->sc_sx, "Raspberry Pi firmware gpio");
369 /* Find our node. */
370 gpio = ofw_bus_get_node(dev);
371 if (!OF_hasprop(gpio, "gpio-controller"))
372 /* This is not a GPIO controller. */
373 goto fail;
374
375 nelems = OF_getprop_alloc(gpio, "gpio-line-names", (void **)&names);
376 if (nelems <= 0)
377 names = NULL;
378 elm_pos = 0;
379 for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
380 /* Set the current pin name */
381 if (names != NULL && elm_pos < nelems &&
382 names[elm_pos] != '\0') {
383 snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
384 "%s", names + elm_pos);
385 /* Find the next pin name */
386 elm_pos += strlen(names + elm_pos) + 1;
387 } else {
388 snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
389 "pin %d", i);
390 }
391
392 sc->sc_gpio_pins[i].gp_pin = i;
393 sc->sc_gpio_pins[i].gp_caps = RPI_FW_GPIO_DEFAULT_CAPS;
394
395 bzero(&cfg, sizeof(cfg));
396 cfg.req.gpio = RPI_FW_GPIO_BASE + i;
397 rv = bcm2835_firmware_property(sc->sc_firmware,
398 BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &cfg, sizeof(cfg));
399 if (rv == 0 && cfg.resp.gpio == 0) {
400 if (cfg.resp.dir == BCM2835_FIRMWARE_GPIO_IN)
401 sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
402 else
403 sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT;
404 } else {
405 sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
406 }
407 }
408 free(names, M_OFWPROP);
409 sc->sc_busdev = gpiobus_attach_bus(dev);
410 if (sc->sc_busdev == NULL)
411 goto fail;
412
413 return (0);
414
415 fail:
416 sx_destroy(&sc->sc_sx);
417
418 return (ENXIO);
419 }
420
421 static int
422 rpi_fw_gpio_detach(device_t dev)
423 {
424
425 return (EBUSY);
426 }
427
428 static device_method_t rpi_fw_gpio_methods[] = {
429 /* Device interface */
430 DEVMETHOD(device_probe, rpi_fw_gpio_probe),
431 DEVMETHOD(device_attach, rpi_fw_gpio_attach),
432 DEVMETHOD(device_detach, rpi_fw_gpio_detach),
433
434 /* GPIO protocol */
435 DEVMETHOD(gpio_get_bus, rpi_fw_gpio_get_bus),
436 DEVMETHOD(gpio_pin_max, rpi_fw_gpio_pin_max),
437 DEVMETHOD(gpio_pin_getname, rpi_fw_gpio_pin_getname),
438 DEVMETHOD(gpio_pin_getflags, rpi_fw_gpio_pin_getflags),
439 DEVMETHOD(gpio_pin_getcaps, rpi_fw_gpio_pin_getcaps),
440 DEVMETHOD(gpio_pin_setflags, rpi_fw_gpio_pin_setflags),
441 DEVMETHOD(gpio_pin_get, rpi_fw_gpio_pin_get),
442 DEVMETHOD(gpio_pin_set, rpi_fw_gpio_pin_set),
443 DEVMETHOD(gpio_pin_toggle, rpi_fw_gpio_pin_toggle),
444
445 DEVMETHOD_END
446 };
447
448 static driver_t rpi_fw_gpio_driver = {
449 "gpio",
450 rpi_fw_gpio_methods,
451 sizeof(struct rpi_fw_gpio_softc),
452 };
453
454 EARLY_DRIVER_MODULE(rpi_fw_gpio, bcm2835_firmware, rpi_fw_gpio_driver, 0, 0,
455 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
Cache object: 3cc2179949a262b01688000e96a3dbfc
|