Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/broadcom/bcm2835/raspberrypi_gpio.c
39566 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2012 Oleksandr Tymoshenko <[email protected]>
5
* Copyright (c) 2012-2015 Luiz Otavio O Souza <[email protected]>
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
#include "opt_platform.h"
32
33
#include <sys/param.h>
34
#include <sys/systm.h>
35
#include <sys/bus.h>
36
#include <sys/gpio.h>
37
#include <sys/kernel.h>
38
#include <sys/lock.h>
39
#include <sys/module.h>
40
#include <sys/sx.h>
41
#include <sys/proc.h>
42
43
#include <dev/gpio/gpiobusvar.h>
44
#include <dev/ofw/ofw_bus.h>
45
46
#include <arm/broadcom/bcm2835/bcm2835_firmware.h>
47
48
#include "gpio_if.h"
49
50
#define RPI_FW_GPIO_PINS 8
51
#define RPI_FW_GPIO_BASE 128
52
#define RPI_FW_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
53
54
struct rpi_fw_gpio_softc {
55
device_t sc_busdev;
56
device_t sc_firmware;
57
struct sx sc_sx;
58
struct gpio_pin sc_gpio_pins[RPI_FW_GPIO_PINS];
59
uint8_t sc_gpio_state;
60
};
61
62
#define RPI_FW_GPIO_LOCK(_sc) sx_xlock(&(_sc)->sc_sx)
63
#define RPI_FW_GPIO_UNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx)
64
65
static struct ofw_compat_data compat_data[] = {
66
{"raspberrypi,firmware-gpio", 1},
67
{NULL, 0}
68
};
69
70
static int
71
rpi_fw_gpio_pin_configure(struct rpi_fw_gpio_softc *sc, struct gpio_pin *pin,
72
unsigned int flags)
73
{
74
union msg_get_gpio_config old_cfg;
75
union msg_set_gpio_config new_cfg;
76
int rv;
77
78
bzero(&old_cfg, sizeof(old_cfg));
79
bzero(&new_cfg, sizeof(new_cfg));
80
old_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;
81
82
RPI_FW_GPIO_LOCK(sc);
83
rv = bcm2835_firmware_property(sc->sc_firmware,
84
BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &old_cfg, sizeof(old_cfg));
85
if (rv == 0 && old_cfg.resp.gpio != 0)
86
rv = EIO;
87
if (rv != 0)
88
goto fail;
89
90
new_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;
91
if (flags & GPIO_PIN_INPUT) {
92
new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_IN;
93
new_cfg.req.state = 0;
94
pin->gp_flags = GPIO_PIN_INPUT;
95
} else if (flags & GPIO_PIN_OUTPUT) {
96
new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_OUT;
97
if (flags & (GPIO_PIN_PRESET_HIGH | GPIO_PIN_PRESET_LOW)) {
98
if (flags & GPIO_PIN_PRESET_HIGH) {
99
new_cfg.req.state = 1;
100
sc->sc_gpio_state |= (1 << pin->gp_pin);
101
} else {
102
new_cfg.req.state = 0;
103
sc->sc_gpio_state &= ~(1 << pin->gp_pin);
104
}
105
} else {
106
if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0) {
107
new_cfg.req.state = 1;
108
} else {
109
new_cfg.req.state = 0;
110
}
111
}
112
pin->gp_flags = GPIO_PIN_OUTPUT;
113
} else {
114
new_cfg.req.dir = old_cfg.resp.dir;
115
/* Use the old state to decide high/low */
116
if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0)
117
new_cfg.req.state = 1;
118
else
119
new_cfg.req.state = 0;
120
}
121
new_cfg.req.pol = old_cfg.resp.pol;
122
new_cfg.req.term_en = 0;
123
new_cfg.req.term_pull_up = 0;
124
125
rv = bcm2835_firmware_property(sc->sc_firmware,
126
BCM2835_FIRMWARE_TAG_SET_GPIO_CONFIG, &new_cfg, sizeof(new_cfg));
127
128
fail:
129
RPI_FW_GPIO_UNLOCK(sc);
130
131
return (rv);
132
}
133
134
static device_t
135
rpi_fw_gpio_get_bus(device_t dev)
136
{
137
struct rpi_fw_gpio_softc *sc;
138
139
sc = device_get_softc(dev);
140
141
return (sc->sc_busdev);
142
}
143
144
static int
145
rpi_fw_gpio_pin_max(device_t dev, int *maxpin)
146
{
147
148
*maxpin = RPI_FW_GPIO_PINS - 1;
149
return (0);
150
}
151
152
static int
153
rpi_fw_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
154
{
155
struct rpi_fw_gpio_softc *sc;
156
int i;
157
158
sc = device_get_softc(dev);
159
for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
160
if (sc->sc_gpio_pins[i].gp_pin == pin)
161
break;
162
}
163
164
if (i >= RPI_FW_GPIO_PINS)
165
return (EINVAL);
166
167
*caps = RPI_FW_GPIO_DEFAULT_CAPS;
168
return (0);
169
}
170
171
static int
172
rpi_fw_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
173
{
174
struct rpi_fw_gpio_softc *sc = device_get_softc(dev);
175
int i;
176
177
for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
178
if (sc->sc_gpio_pins[i].gp_pin == pin)
179
break;
180
}
181
182
if (i >= RPI_FW_GPIO_PINS)
183
return (EINVAL);
184
185
RPI_FW_GPIO_LOCK(sc);
186
*flags = sc->sc_gpio_pins[i].gp_flags;
187
RPI_FW_GPIO_UNLOCK(sc);
188
189
return (0);
190
}
191
192
static int
193
rpi_fw_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
194
{
195
struct rpi_fw_gpio_softc *sc;
196
int i;
197
198
sc = device_get_softc(dev);
199
for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
200
if (sc->sc_gpio_pins[i].gp_pin == pin)
201
break;
202
}
203
204
if (i >= RPI_FW_GPIO_PINS)
205
return (EINVAL);
206
207
RPI_FW_GPIO_LOCK(sc);
208
memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME);
209
RPI_FW_GPIO_UNLOCK(sc);
210
211
return (0);
212
}
213
214
static int
215
rpi_fw_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
216
{
217
struct rpi_fw_gpio_softc *sc;
218
int i;
219
220
sc = device_get_softc(dev);
221
for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
222
if (sc->sc_gpio_pins[i].gp_pin == pin)
223
break;
224
}
225
226
if (i >= RPI_FW_GPIO_PINS)
227
return (EINVAL);
228
229
return (rpi_fw_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags));
230
}
231
232
static int
233
rpi_fw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
234
{
235
struct rpi_fw_gpio_softc *sc;
236
union msg_set_gpio_state state;
237
int i, rv;
238
239
sc = device_get_softc(dev);
240
for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
241
if (sc->sc_gpio_pins[i].gp_pin == pin)
242
break;
243
}
244
if (i >= RPI_FW_GPIO_PINS)
245
return (EINVAL);
246
247
state.req.gpio = RPI_FW_GPIO_BASE + pin;
248
state.req.state = value;
249
250
RPI_FW_GPIO_LOCK(sc);
251
rv = bcm2835_firmware_property(sc->sc_firmware,
252
BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &state, sizeof(state));
253
/* The firmware sets gpio to 0 on success */
254
if (rv == 0 && state.resp.gpio != 0)
255
rv = EINVAL;
256
if (rv == 0) {
257
sc->sc_gpio_pins[i].gp_flags &= ~(GPIO_PIN_PRESET_HIGH |
258
GPIO_PIN_PRESET_LOW);
259
if (value)
260
sc->sc_gpio_state |= (1 << i);
261
else
262
sc->sc_gpio_state &= ~(1 << i);
263
}
264
RPI_FW_GPIO_UNLOCK(sc);
265
266
return (rv);
267
}
268
269
static int
270
rpi_fw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
271
{
272
struct rpi_fw_gpio_softc *sc;
273
union msg_get_gpio_state state;
274
int i, rv;
275
276
sc = device_get_softc(dev);
277
for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
278
if (sc->sc_gpio_pins[i].gp_pin == pin)
279
break;
280
}
281
if (i >= RPI_FW_GPIO_PINS)
282
return (EINVAL);
283
284
bzero(&state, sizeof(state));
285
state.req.gpio = RPI_FW_GPIO_BASE + pin;
286
287
RPI_FW_GPIO_LOCK(sc);
288
rv = bcm2835_firmware_property(sc->sc_firmware,
289
BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &state, sizeof(state));
290
RPI_FW_GPIO_UNLOCK(sc);
291
292
/* The firmware sets gpio to 0 on success */
293
if (rv == 0 && state.resp.gpio != 0)
294
rv = EINVAL;
295
if (rv == 0)
296
*val = !state.resp.state;
297
298
return (rv);
299
}
300
301
static int
302
rpi_fw_gpio_pin_toggle(device_t dev, uint32_t pin)
303
{
304
struct rpi_fw_gpio_softc *sc;
305
union msg_get_gpio_state old_state;
306
union msg_set_gpio_state new_state;
307
int i, rv;
308
309
sc = device_get_softc(dev);
310
for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
311
if (sc->sc_gpio_pins[i].gp_pin == pin)
312
break;
313
}
314
if (i >= RPI_FW_GPIO_PINS)
315
return (EINVAL);
316
317
bzero(&old_state, sizeof(old_state));
318
bzero(&new_state, sizeof(new_state));
319
320
old_state.req.gpio = RPI_FW_GPIO_BASE + pin;
321
new_state.req.gpio = RPI_FW_GPIO_BASE + pin;
322
323
RPI_FW_GPIO_LOCK(sc);
324
rv = bcm2835_firmware_property(sc->sc_firmware,
325
BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &old_state, sizeof(old_state));
326
/* The firmware sets gpio to 0 on success */
327
if (rv == 0 && old_state.resp.gpio == 0) {
328
/* Set the new state to invert the GPIO */
329
new_state.req.state = !old_state.resp.state;
330
rv = bcm2835_firmware_property(sc->sc_firmware,
331
BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &new_state,
332
sizeof(new_state));
333
}
334
if (rv == 0 && (old_state.resp.gpio != 0 || new_state.resp.gpio != 0))
335
rv = EINVAL;
336
RPI_FW_GPIO_UNLOCK(sc);
337
338
return (rv);
339
}
340
341
static int
342
rpi_fw_gpio_probe(device_t dev)
343
{
344
345
if (!ofw_bus_status_okay(dev))
346
return (ENXIO);
347
348
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
349
return (ENXIO);
350
351
device_set_desc(dev, "Raspberry Pi Firmware GPIO controller");
352
return (BUS_PROBE_DEFAULT);
353
}
354
355
static int
356
rpi_fw_gpio_attach(device_t dev)
357
{
358
union msg_get_gpio_config cfg;
359
struct rpi_fw_gpio_softc *sc;
360
char *names;
361
phandle_t gpio;
362
int i, nelems, elm_pos, rv;
363
364
sc = device_get_softc(dev);
365
sc->sc_firmware = device_get_parent(dev);
366
sx_init(&sc->sc_sx, "Raspberry Pi firmware gpio");
367
/* Find our node. */
368
gpio = ofw_bus_get_node(dev);
369
if (!OF_hasprop(gpio, "gpio-controller"))
370
/* This is not a GPIO controller. */
371
goto fail;
372
373
nelems = OF_getprop_alloc(gpio, "gpio-line-names", (void **)&names);
374
if (nelems <= 0)
375
names = NULL;
376
elm_pos = 0;
377
for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
378
/* Set the current pin name */
379
if (names != NULL && elm_pos < nelems &&
380
names[elm_pos] != '\0') {
381
snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
382
"%s", names + elm_pos);
383
/* Find the next pin name */
384
elm_pos += strlen(names + elm_pos) + 1;
385
} else {
386
snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
387
"pin %d", i);
388
}
389
390
sc->sc_gpio_pins[i].gp_pin = i;
391
sc->sc_gpio_pins[i].gp_caps = RPI_FW_GPIO_DEFAULT_CAPS;
392
393
bzero(&cfg, sizeof(cfg));
394
cfg.req.gpio = RPI_FW_GPIO_BASE + i;
395
rv = bcm2835_firmware_property(sc->sc_firmware,
396
BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &cfg, sizeof(cfg));
397
if (rv == 0 && cfg.resp.gpio == 0) {
398
if (cfg.resp.dir == BCM2835_FIRMWARE_GPIO_IN)
399
sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
400
else
401
sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT;
402
} else {
403
sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
404
}
405
}
406
free(names, M_OFWPROP);
407
sc->sc_busdev = gpiobus_add_bus(dev);
408
if (sc->sc_busdev == NULL)
409
goto fail;
410
411
bus_attach_children(dev);
412
return (0);
413
414
fail:
415
sx_destroy(&sc->sc_sx);
416
417
return (ENXIO);
418
}
419
420
static int
421
rpi_fw_gpio_detach(device_t dev)
422
{
423
424
return (EBUSY);
425
}
426
427
static device_method_t rpi_fw_gpio_methods[] = {
428
/* Device interface */
429
DEVMETHOD(device_probe, rpi_fw_gpio_probe),
430
DEVMETHOD(device_attach, rpi_fw_gpio_attach),
431
DEVMETHOD(device_detach, rpi_fw_gpio_detach),
432
433
/* GPIO protocol */
434
DEVMETHOD(gpio_get_bus, rpi_fw_gpio_get_bus),
435
DEVMETHOD(gpio_pin_max, rpi_fw_gpio_pin_max),
436
DEVMETHOD(gpio_pin_getname, rpi_fw_gpio_pin_getname),
437
DEVMETHOD(gpio_pin_getflags, rpi_fw_gpio_pin_getflags),
438
DEVMETHOD(gpio_pin_getcaps, rpi_fw_gpio_pin_getcaps),
439
DEVMETHOD(gpio_pin_setflags, rpi_fw_gpio_pin_setflags),
440
DEVMETHOD(gpio_pin_get, rpi_fw_gpio_pin_get),
441
DEVMETHOD(gpio_pin_set, rpi_fw_gpio_pin_set),
442
DEVMETHOD(gpio_pin_toggle, rpi_fw_gpio_pin_toggle),
443
444
DEVMETHOD_END
445
};
446
447
static driver_t rpi_fw_gpio_driver = {
448
"gpio",
449
rpi_fw_gpio_methods,
450
sizeof(struct rpi_fw_gpio_softc),
451
};
452
453
EARLY_DRIVER_MODULE(rpi_fw_gpio, bcm2835_firmware, rpi_fw_gpio_driver, 0, 0,
454
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
455
456