summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Wildt <patrick@cvs.openbsd.org>2024-03-25 17:24:04 +0000
committerPatrick Wildt <patrick@cvs.openbsd.org>2024-03-25 17:24:04 +0000
commit003b93e13797f12042e617988e136edbe09a3f70 (patch)
treeefb401957417179de9b0feba02c742d62bdd752e
parenta7a4a4b0ff14437bc94699063b8127eba17d8c01 (diff)
Add rpigpio(4), a driver for the RP1 GPIO controller on the Raspberry Pi 5.
With this, GPIOs can be correctly configured and engaged. Complete pinctrl as well as IRQ functionality is yet to be implemented. ok kettenis@
-rw-r--r--sys/arch/arm64/conf/GENERIC3
-rw-r--r--sys/arch/arm64/conf/RAMDISK3
-rw-r--r--sys/arch/arm64/conf/files.arm646
-rw-r--r--sys/arch/arm64/dev/rpigpio.c287
4 files changed, 296 insertions, 3 deletions
diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC
index be2f71ed588..b6421e6e22c 100644
--- a/sys/arch/arm64/conf/GENERIC
+++ b/sys/arch/arm64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.285 2024/03/24 22:34:48 patrick Exp $
+# $OpenBSD: GENERIC,v 1.286 2024/03/25 17:24:03 patrick Exp $
#
# GENERIC machine description file
#
@@ -244,6 +244,7 @@ bse* at fdt?
bse* at acpi?
dwctwo* at fdt?
usb* at dwctwo?
+rpigpio* at fdt? early 1
# Amlogic SoCs
amlclock* at fdt? early 1
diff --git a/sys/arch/arm64/conf/RAMDISK b/sys/arch/arm64/conf/RAMDISK
index 23be641497f..c25a704337d 100644
--- a/sys/arch/arm64/conf/RAMDISK
+++ b/sys/arch/arm64/conf/RAMDISK
@@ -1,4 +1,4 @@
-# $OpenBSD: RAMDISK,v 1.215 2024/03/24 22:34:48 patrick Exp $
+# $OpenBSD: RAMDISK,v 1.216 2024/03/25 17:24:03 patrick Exp $
machine arm64
maxusers 4
@@ -180,6 +180,7 @@ bse* at fdt?
bse* at acpi?
dwctwo* at fdt?
usb* at dwctwo?
+rpigpio* at fdt? early 1
# Amlogic SoCs
amlclock* at fdt? early 1
diff --git a/sys/arch/arm64/conf/files.arm64 b/sys/arch/arm64/conf/files.arm64
index 117a99b10e4..833ea3f050f 100644
--- a/sys/arch/arm64/conf/files.arm64
+++ b/sys/arch/arm64/conf/files.arm64
@@ -1,4 +1,4 @@
-# $OpenBSD: files.arm64,v 1.68 2024/01/01 18:25:50 kettenis Exp $
+# $OpenBSD: files.arm64,v 1.69 2024/03/25 17:24:03 patrick Exp $
maxpartitions 16
maxusers 2 8 128
@@ -259,6 +259,10 @@ attach smmu at fdt with smmu_fdt
file arch/arm64/dev/smmu.c smmu
file arch/arm64/dev/smmu_fdt.c smmu_fdt
+device rpigpio
+attach rpigpio at fdt
+file arch/arm64/dev/rpigpio.c rpigpio
+
# ACPI
include "dev/acpi/files.acpi"
diff --git a/sys/arch/arm64/dev/rpigpio.c b/sys/arch/arm64/dev/rpigpio.c
new file mode 100644
index 00000000000..34f75e12f66
--- /dev/null
+++ b/sys/arch/arm64/dev/rpigpio.c
@@ -0,0 +1,287 @@
+/* $OpenBSD: rpigpio.c,v 1.1 2024/03/25 17:24:03 patrick Exp $ */
+/*
+ * Copyright (c) 2024 Patrick Wildt <patrick@blueri.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_gpio.h>
+#include <dev/ofw/fdt.h>
+
+#define GPIOx_STATUS(x) (0x000 + (x) * 0x8)
+#define GPIOx_STATUS_OUTFROMPERI (1 << 8)
+#define GPIOx_STATUS_OUTFROMPAD (1 << 9)
+#define GPIOx_STATUS_OEFROMPERI (1 << 12)
+#define GPIOx_STATUS_OEFROMPAD (1 << 13)
+#define GPIOx_STATUS_INISDIRECT (1 << 16)
+#define GPIOx_STATUS_INFROMPAD (1 << 17)
+#define GPIOx_STATUS_INFILTERED (1 << 18)
+#define GPIOx_STATUS_INTOPERI (1 << 19)
+#define GPIOx_STATUS_EVENT_EDGE_LOW (1 << 20)
+#define GPIOx_STATUS_EVENT_EDGE_HIGH (1 << 21)
+#define GPIOx_STATUS_EVENT_LEVEL_LOW (1 << 22)
+#define GPIOx_STATUS_EVENT_LEVEL_HIGH (1 << 23)
+#define GPIOx_STATUS_EVENT_F_EDGE_LOW (1 << 24)
+#define GPIOx_STATUS_EVENT_F_EDGE_HIGH (1 << 25)
+#define GPIOx_STATUS_EVENT_DB_LEVEL_LOW (1 << 26)
+#define GPIOx_STATUS_EVENT_DB_LEVEL_HIGH (1 << 27)
+#define GPIOx_STATUS_IRQCOMBINED (1 << 28)
+#define GPIOx_STATUS_IRQTOPROC (1 << 29)
+#define GPIOx_CTRL(x) (0x004 + (x) * 0x8)
+#define GPIOx_CTRL_FUNCSEL_MASK (0x1f << 0)
+#define GPIOx_CTRL_FUNCSEL(x) ((x) << 0)
+#define GPIOx_CTRL_FUNCSEL_GPIO ((5) << 0)
+#define GPIOx_CTRL_OUTOVER_MASK (0x3 << 12)
+#define GPIOx_CTRL_OUTOVER_NRMFUNC (0 << 12)
+#define GPIOx_CTRL_OUTOVER_INVFUNC (1 << 12)
+#define GPIOx_CTRL_OUTOVER_LOW (2 << 12)
+#define GPIOx_CTRL_OUTOVER_HIGH (3 << 12)
+#define GPIOx_CTRL_OEOVER_MASK (0x3 << 14)
+#define GPIOx_CTRL_OEOVER_NRMFUNC (0 << 14)
+#define GPIOx_CTRL_OEOVER_INVFUNC (1 << 14)
+#define GPIOx_CTRL_OEOVER_DIS (2 << 14)
+#define GPIOx_CTRL_OEOVER_ENA (3 << 14)
+#define GPIOx_CTRL_INOVER_MASK (0x3 << 16)
+#define GPIOx_CTRL_INOVER_NRMFUNC (0 << 16)
+#define GPIOx_CTRL_INOVER_INVFUNC (1 << 16)
+#define GPIOx_CTRL_INOVER_LOW (2 << 16)
+#define GPIOx_CTRL_INOVER_HIGH (3 << 16)
+#define GPIOx_CTRL_IRQMASK_EDGE_LOW (1 << 20)
+#define GPIOx_CTRL_IRQMASK_EDGE_HIGH (1 << 21)
+#define GPIOx_CTRL_IRQMASK_LEVEL_LOW (1 << 22)
+#define GPIOx_CTRL_IRQMASK_LEVEL_HIGH (1 << 23)
+#define GPIOx_CTRL_IRQMASK_F_EDGE_LOW (1 << 24)
+#define GPIOx_CTRL_IRQMASK_F_EDGE_HIGH (1 << 25)
+#define GPIOx_CTRL_IRQMASK_DB_LEVEL_LOW (1 << 26)
+#define GPIOx_CTRL_IRQMASK_DB_LEVEL_HIGH (1 << 27)
+#define GPIOx_CTRL_IRQRESET (1 << 28)
+#define GPIOx_CTRL_IRQOVER_MASK (0x3U << 30)
+#define GPIOx_CTRL_IRQOVER_NRMFUNC (0U << 30)
+#define GPIOx_CTRL_IRQOVER_INVFUNC (1U << 30)
+#define GPIOx_CTRL_IRQOVER_LOW (2U << 30)
+#define GPIOx_CTRL_IRQOVER_HIGH (3U << 30)
+
+#define RIO_OUT 0x000
+#define RIO_OE 0x004
+#define RIO_IN 0x008
+
+#define PAD_CTRL(x) (0x000 + (x) * 0x4)
+#define PAD_CTRL_IN_EN (1 << 6)
+#define PAD_CTRL_OUT_DIS (1 << 7)
+
+#define GPIO_NUM_PINS 54
+
+struct rpigpio_softc {
+ struct device sc_dev;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_gpio_ioh;
+ bus_space_handle_t sc_rio_ioh;
+ bus_space_handle_t sc_pads_ioh;
+ int sc_node;
+
+ struct gpio_controller sc_gc;
+};
+
+int rpigpio_match(struct device *, void *, void *);
+void rpigpio_attach(struct device *, struct device *, void *);
+
+const struct rpigpio_bank *rpigpio_get_bank(struct rpigpio_softc *, uint32_t);
+void rpigpio_config_pin(void *, uint32_t *, int);
+int rpigpio_get_pin(void *, uint32_t *);
+void rpigpio_set_pin(void *, uint32_t *, int);
+
+const struct cfattach rpigpio_ca = {
+ sizeof (struct rpigpio_softc), rpigpio_match, rpigpio_attach
+};
+
+struct cfdriver rpigpio_cd = {
+ NULL, "rpigpio", DV_DULL
+};
+
+int
+rpigpio_match(struct device *parent, void *match, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+
+ return OF_is_compatible(faa->fa_node, "raspberrypi,rp1-gpio");
+}
+
+void
+rpigpio_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct rpigpio_softc *sc = (struct rpigpio_softc *)self;
+ struct fdt_attach_args *faa = aux;
+
+ if (faa->fa_nreg < 3)
+ return;
+
+ sc->sc_node = faa->fa_node;
+ sc->sc_iot = faa->fa_iot;
+ if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
+ faa->fa_reg[0].size, 0, &sc->sc_gpio_ioh)) {
+ printf(": can't map GPIO registers\n");
+ return;
+ }
+ if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
+ faa->fa_reg[1].size, 0, &sc->sc_rio_ioh)) {
+ bus_space_unmap(sc->sc_iot, sc->sc_gpio_ioh,
+ faa->fa_reg[0].size);
+ printf(": can't map RIO registers\n");
+ return;
+ }
+ if (bus_space_map(sc->sc_iot, faa->fa_reg[2].addr,
+ faa->fa_reg[2].size, 0, &sc->sc_pads_ioh)) {
+ bus_space_unmap(sc->sc_iot, sc->sc_rio_ioh,
+ faa->fa_reg[1].size);
+ bus_space_unmap(sc->sc_iot, sc->sc_gpio_ioh,
+ faa->fa_reg[0].size);
+ printf(": can't map PADS registers\n");
+ return;
+ }
+
+ sc->sc_gc.gc_node = faa->fa_node;
+ sc->sc_gc.gc_cookie = sc;
+ sc->sc_gc.gc_config_pin = rpigpio_config_pin;
+ sc->sc_gc.gc_get_pin = rpigpio_get_pin;
+ sc->sc_gc.gc_set_pin = rpigpio_set_pin;
+ gpio_controller_register(&sc->sc_gc);
+
+ printf("\n");
+}
+
+struct rpigpio_bank {
+ uint32_t start;
+ uint32_t num;
+ uint32_t gpio;
+ uint32_t rio;
+ uint32_t pads;
+};
+
+const struct rpigpio_bank rpigpio_banks[] = {
+ { 0, 28, 0x0000, 0x0000, 0x0004 },
+ { 28, 6, 0x4000, 0x4000, 0x4004 },
+ { 34, 20, 0x8000, 0x8000, 0x8004 },
+};
+
+const struct rpigpio_bank *
+rpigpio_get_bank(struct rpigpio_softc *sc, uint32_t pin)
+{
+ const struct rpigpio_bank *bank;
+ int i;
+
+ for (i = 0; i < nitems(rpigpio_banks); i++) {
+ bank = &rpigpio_banks[i];
+ if (pin >= bank->start && pin < bank->start + bank->num) {
+ return bank;
+ }
+ }
+
+ printf("%s: can't find pin %d\n", sc->sc_dev.dv_xname, pin);
+ return NULL;
+}
+
+void
+rpigpio_config_pin(void *cookie, uint32_t *cells, int config)
+{
+ struct rpigpio_softc *sc = cookie;
+ const struct rpigpio_bank *bank;
+ uint32_t pin = cells[0];
+ uint32_t val;
+
+ bank = rpigpio_get_bank(sc, pin);
+ if (bank == NULL)
+ return;
+ pin -= bank->start;
+
+ /* Configure pin to be input or output */
+ val = bus_space_read_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_OE);
+ if (config & GPIO_CONFIG_OUTPUT)
+ val |= 1 << pin;
+ else
+ val &= ~(1 << pin);
+ bus_space_write_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_OE, val);
+
+ /* Enable input/output on pad */
+ val = bus_space_read_4(sc->sc_iot, sc->sc_pads_ioh, bank->pads +
+ PAD_CTRL(pin));
+ val &= ~PAD_CTRL_OUT_DIS;
+ val |= PAD_CTRL_IN_EN;
+ bus_space_write_4(sc->sc_iot, sc->sc_pads_ioh, bank->pads +
+ PAD_CTRL(pin), val);
+
+ /* Configure pin as GPIO in standard mode */
+ val = bus_space_read_4(sc->sc_iot, sc->sc_gpio_ioh, bank->gpio +
+ GPIOx_CTRL(pin));
+ val &= ~(GPIOx_CTRL_FUNCSEL_MASK | GPIOx_CTRL_OUTOVER_MASK |
+ GPIOx_CTRL_OEOVER_MASK);
+ val |= (GPIOx_CTRL_FUNCSEL_GPIO | GPIOx_CTRL_OUTOVER_NRMFUNC |
+ GPIOx_CTRL_OEOVER_NRMFUNC);
+ bus_space_write_4(sc->sc_iot, sc->sc_gpio_ioh, bank->gpio +
+ GPIOx_CTRL(pin), val);
+}
+
+int
+rpigpio_get_pin(void *cookie, uint32_t *cells)
+{
+ struct rpigpio_softc *sc = cookie;
+ const struct rpigpio_bank *bank;
+ uint32_t pin = cells[0];
+ uint32_t flags = cells[1];
+ uint32_t reg;
+ int val;
+
+ bank = rpigpio_get_bank(sc, pin);
+ if (bank == NULL)
+ return 0;
+ pin -= bank->start;
+
+ reg = bus_space_read_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_IN);
+ reg &= (1 << pin);
+ val = (reg >> pin) & 1;
+ if (flags & GPIO_ACTIVE_LOW)
+ val = !val;
+ return val;
+}
+
+void
+rpigpio_set_pin(void *cookie, uint32_t *cells, int val)
+{
+ struct rpigpio_softc *sc = cookie;
+ const struct rpigpio_bank *bank;
+ uint32_t pin = cells[0];
+ uint32_t flags = cells[1];
+ uint32_t reg;
+
+ bank = rpigpio_get_bank(sc, pin);
+ if (bank == NULL)
+ return;
+ pin -= bank->start;
+
+ reg = bus_space_read_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_OUT);
+ if (flags & GPIO_ACTIVE_LOW)
+ val = !val;
+ if (val)
+ reg |= (1 << pin);
+ else
+ reg &= ~(1 << pin);
+ bus_space_write_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_OUT, reg);
+}