From 6b382705f6a0a25d512acdc202a123ffdb05e9e0 Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 25 Mar 2024 17:24:03 +0000 Subject: [PATCH] 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@ --- sys/arch/arm64/conf/GENERIC | 3 +- sys/arch/arm64/conf/RAMDISK | 3 +- sys/arch/arm64/conf/files.arm64 | 6 +- sys/arch/arm64/dev/rpigpio.c | 287 ++++++++++++++++++++++++++++++++ 4 files changed, 296 insertions(+), 3 deletions(-) create mode 100644 sys/arch/arm64/dev/rpigpio.c 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 + * + * 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 +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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); +} -- 2.20.1