From: kettenis Date: Mon, 30 May 2022 17:39:09 +0000 (+0000) Subject: Add sfgpio(4), a driver for the GPIO controller found on the X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=79e45e3f203d46c697f2ea83a687e6fd54f0883b;p=openbsd Add sfgpio(4), a driver for the GPIO controller found on the SiFive FU740 SoC. ok jca@ --- diff --git a/sys/arch/riscv64/conf/GENERIC b/sys/arch/riscv64/conf/GENERIC index 2435be44119..3968370b5c8 100644 --- a/sys/arch/riscv64/conf/GENERIC +++ b/sys/arch/riscv64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.35 2022/02/18 10:51:43 visa Exp $ +# $OpenBSD: GENERIC,v 1.36 2022/05/30 17:39:09 kettenis Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -56,6 +56,7 @@ iic* at mpfiic? # SiFive SoCs sfclock* at fdt? early 1 # PRCI sfcc* at fdt? early 1 # L2 Cache Controller +sfgpio* at fdt? sfuart* at fdt? virtio* at fdt? diff --git a/sys/arch/riscv64/conf/files.riscv64 b/sys/arch/riscv64/conf/files.riscv64 index 3f4bd1464b6..593af764a80 100644 --- a/sys/arch/riscv64/conf/files.riscv64 +++ b/sys/arch/riscv64/conf/files.riscv64 @@ -1,4 +1,4 @@ -# $OpenBSD: files.riscv64,v 1.20 2022/02/18 10:51:43 visa Exp $ +# $OpenBSD: files.riscv64,v 1.21 2022/05/30 17:39:09 kettenis Exp $ # Standard stanzas config(8) can't run without maxpartitions 16 @@ -109,6 +109,11 @@ device sfclock attach sfclock at fdt file arch/riscv64/dev/sfclock.c sfclock +# SiFive GPIO +device sfgpio +attach sfgpio at fdt +file arch/riscv64/dev/sfgpio.c sfgpio + # SiFive uart device sfuart attach sfuart at fdt diff --git a/sys/arch/riscv64/dev/sfgpio.c b/sys/arch/riscv64/dev/sfgpio.c new file mode 100644 index 00000000000..1445b9a14c1 --- /dev/null +++ b/sys/arch/riscv64/dev/sfgpio.c @@ -0,0 +1,361 @@ +/* $OpenBSD: sfgpio.c,v 1.1 2022/05/30 17:39:09 kettenis Exp $ */ +/* + * Copyright (c) 2022 Mark Kettenis + * Copyright (c) 2019 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 +#include +#include + +/* Registers. */ +#define GPIO_INPUT_VAL 0x0000 +#define GPIO_INPUT_EN 0x0004 +#define GPIO_OUTPUT_EN 0x0008 +#define GPIO_OUTPUT_VAL 0x000c +#define GPIO_PUE 0x0010 +#define GPIO_DS 0x0014 +#define GPIO_RISE_IE 0x0018 +#define GPIO_RISE_IP 0x001c +#define GPIO_FALL_IE 0x0020 +#define GPIO_FALL_IP 0x0024 +#define GPIO_HIGH_IE 0x0028 +#define GPIO_HIGH_IP 0x002c +#define GPIO_LOW_IE 0x0030 +#define GPIO_LOW_IP 0x0034 +#define GPIO_IOF_EN 0x0038 +#define GPIO_IOF_SEL 0x003C +#define GPIO_OUT_XOR 0x0040 + +#define GPIO_NUM_PINS 16 + +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +struct intrhand { + int (*ih_func)(void *); /* handler */ + void *ih_arg; /* arg for handler */ + int ih_pin; /* pin number */ + int ih_level; /* trigger level */ + void *ih_sc; +}; + +struct sfgpio_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + int sc_node; + + void *sc_ih[GPIO_NUM_PINS]; + struct interrupt_controller sc_ic; + + struct gpio_controller sc_gc; +}; + +int sfgpio_match(struct device *, void *, void *); +void sfgpio_attach(struct device *, struct device *, void *); + +const struct cfattach sfgpio_ca = { + sizeof (struct sfgpio_softc), sfgpio_match, sfgpio_attach +}; + +struct cfdriver sfgpio_cd = { + NULL, "sfgpio", DV_DULL +}; + +void sfgpio_config_pin(void *, uint32_t *, int); +int sfgpio_get_pin(void *, uint32_t *); +void sfgpio_set_pin(void *, uint32_t *, int); + +int sfgpio_intr(void *); +void *sfgpio_intr_establish(void *, int *, int, struct cpu_info *, + int (*)(void *), void *, char *); +void sfgpio_intr_disestablish(void *); +void sfgpio_recalc_ipl(struct sfgpio_softc *); +void sfgpio_intr_enable(void *); +void sfgpio_intr_disable(void *); +void sfgpio_intr_barrier(void *); + +int +sfgpio_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return OF_is_compatible(faa->fa_node, "sifive,gpio0"); +} + +void +sfgpio_attach(struct device *parent, struct device *self, void *aux) +{ + struct sfgpio_softc *sc = (struct sfgpio_softc *)self; + struct fdt_attach_args *faa = aux; + + if (faa->fa_nreg < 1) { + printf(": no registers\n"); + 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_ioh)) { + printf(": can't map registers\n"); + return; + } + + printf("\n"); + + sc->sc_gc.gc_node = faa->fa_node; + sc->sc_gc.gc_cookie = sc; + sc->sc_gc.gc_config_pin = sfgpio_config_pin; + sc->sc_gc.gc_get_pin = sfgpio_get_pin; + sc->sc_gc.gc_set_pin = sfgpio_set_pin; + gpio_controller_register(&sc->sc_gc); + + /* Disable all interrupts. */ + HWRITE4(sc, GPIO_RISE_IE, 0); + HWRITE4(sc, GPIO_FALL_IE, 0); + HWRITE4(sc, GPIO_HIGH_IE, 0); + HWRITE4(sc, GPIO_LOW_IE, 0); + + sc->sc_ic.ic_node = faa->fa_node; + sc->sc_ic.ic_cookie = sc; + sc->sc_ic.ic_establish = sfgpio_intr_establish; + sc->sc_ic.ic_disestablish = sfgpio_intr_disestablish; + sc->sc_ic.ic_enable = sfgpio_intr_enable; + sc->sc_ic.ic_disable = sfgpio_intr_disable; + sc->sc_ic.ic_barrier = sfgpio_intr_barrier; + fdt_intr_register(&sc->sc_ic); +} + +void +sfgpio_config_pin(void *cookie, uint32_t *cells, int config) +{ + struct sfgpio_softc *sc = cookie; + uint32_t pin = cells[0]; + + if (pin >= GPIO_NUM_PINS) + return; + + if (config & GPIO_CONFIG_OUTPUT) { + HSET4(sc, GPIO_OUTPUT_EN, (1 << pin)); + HCLR4(sc, GPIO_INPUT_EN, (1 << pin)); + } else { + HSET4(sc, GPIO_INPUT_EN, (1 << pin)); + HCLR4(sc, GPIO_OUTPUT_EN, (1 << pin)); + } +} + +int +sfgpio_get_pin(void *cookie, uint32_t *cells) +{ + struct sfgpio_softc *sc = cookie; + uint32_t pin = cells[0]; + uint32_t flags = cells[1]; + uint32_t reg; + int val; + + if (pin >= GPIO_NUM_PINS) + return 0; + + reg = HREAD4(sc, GPIO_INPUT_VAL); + val = (reg >> pin) & 1; + if (flags & GPIO_ACTIVE_LOW) + val = !val; + return val; +} + +void +sfgpio_set_pin(void *cookie, uint32_t *cells, int val) +{ + struct sfgpio_softc *sc = cookie; + uint32_t pin = cells[0]; + uint32_t flags = cells[1]; + + if (pin >= GPIO_NUM_PINS) + return; + + if (flags & GPIO_ACTIVE_LOW) + val = !val; + if (val) + HSET4(sc, GPIO_OUTPUT_VAL, (1 << pin)); + else + HCLR4(sc, GPIO_OUTPUT_VAL, (1 << pin)); +} + +int +sfgpio_intr(void *cookie) +{ + struct intrhand *ih = cookie; + struct sfgpio_softc *sc = ih->ih_sc; + int handled; + + handled = ih->ih_func(ih->ih_arg); + + switch (ih->ih_level) { + case 1: /* rising */ + HSET4(sc, GPIO_RISE_IP, (1 << ih->ih_pin)); + break; + case 2: /* falling */ + HSET4(sc, GPIO_FALL_IP, (1 << ih->ih_pin)); + break; + case 4: /* high */ + HSET4(sc, GPIO_HIGH_IP, (1 << ih->ih_pin)); + break; + case 8: /* low */ + HSET4(sc, GPIO_LOW_IP, (1 << ih->ih_pin)); + break; + } + + return handled; +} + +void * +sfgpio_intr_establish(void *cookie, int *cells, int ipl, + struct cpu_info *ci, int (*func)(void *), void *arg, char *name) +{ + struct sfgpio_softc *sc = (struct sfgpio_softc *)cookie; + struct intrhand *ih; + int pin = cells[0]; + int level = cells[1]; + + if (pin < 0 || pin >= GPIO_NUM_PINS) + panic("%s: bogus pin %d: %s", __func__, pin, name); + + if (sc->sc_ih[pin] != NULL) + panic("%s: pin %d reused: %s", __func__, pin, name); + + ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); + ih->ih_func = func; + ih->ih_arg = arg; + ih->ih_pin = pin; + ih->ih_level = level; + ih->ih_sc = sc; + + sc->sc_ih[pin] = fdt_intr_establish_idx_cpu(sc->sc_node, pin, ipl, + ci, sfgpio_intr, ih, name); + if (sc->sc_ih[pin] == NULL) { + free(ih, M_DEVBUF, sizeof(*ih)); + return NULL; + } + + HSET4(sc, GPIO_INPUT_EN, (1 << pin)); + + switch (level) { + case 1: /* rising */ + HSET4(sc, GPIO_RISE_IP, (1 << pin)); + HSET4(sc, GPIO_RISE_IE, (1 << pin)); + break; + case 2: /* falling */ + HSET4(sc, GPIO_FALL_IP, (1 << pin)); + HSET4(sc, GPIO_FALL_IE, (1 << pin)); + break; + case 4: /* high */ + HSET4(sc, GPIO_HIGH_IP, (1 << pin)); + HSET4(sc, GPIO_HIGH_IE, (1 << pin)); + break; + case 8: /* low */ + HSET4(sc, GPIO_LOW_IP, (1 << pin)); + HSET4(sc, GPIO_LOW_IE, (1 << pin)); + break; + default: + panic("%s: unsupported trigger type", __func__); + } + + return ih; +} + +void +sfgpio_intr_disestablish(void *cookie) +{ + struct intrhand *ih = cookie; + struct sfgpio_softc *sc = ih->ih_sc; + + sfgpio_intr_disable(ih); + + fdt_intr_disestablish(sc->sc_ih[ih->ih_pin]); + sc->sc_ih[ih->ih_pin] = NULL; + free(ih, M_DEVBUF, sizeof(*ih)); +} + +void +sfgpio_intr_enable(void *cookie) +{ + struct intrhand *ih = cookie; + struct sfgpio_softc *sc = ih->ih_sc; + + switch (ih->ih_level) { + case 1: /* rising */ + HSET4(sc, GPIO_RISE_IE, (1 << ih->ih_pin)); + break; + case 2: /* falling */ + HSET4(sc, GPIO_FALL_IE, (1 << ih->ih_pin)); + break; + case 4: /* high */ + HSET4(sc, GPIO_HIGH_IE, (1 << ih->ih_pin)); + break; + case 8: /* low */ + HSET4(sc, GPIO_LOW_IE, (1 << ih->ih_pin)); + break; + } +} + +void +sfgpio_intr_disable(void *cookie) +{ + struct intrhand *ih = cookie; + struct sfgpio_softc *sc = ih->ih_sc; + + switch (ih->ih_level) { + case 1: /* rising */ + HCLR4(sc, GPIO_RISE_IE, (1 << ih->ih_pin)); + break; + case 2: /* falling */ + HCLR4(sc, GPIO_FALL_IE, (1 << ih->ih_pin)); + break; + case 4: /* high */ + HCLR4(sc, GPIO_HIGH_IE, (1 << ih->ih_pin)); + break; + case 8: /* low */ + HCLR4(sc, GPIO_LOW_IE, (1 << ih->ih_pin)); + break; + } +} + +void +sfgpio_intr_barrier(void *cookie) +{ + struct intrhand *ih = cookie; + struct sfgpio_softc *sc = ih->ih_sc; + + intr_barrier(sc->sc_ih[ih->ih_pin]); +} diff --git a/sys/arch/riscv64/include/fdt.h b/sys/arch/riscv64/include/fdt.h index 237550b0ed7..dc9159afd84 100644 --- a/sys/arch/riscv64/include/fdt.h +++ b/sys/arch/riscv64/include/fdt.h @@ -1,4 +1,4 @@ -/* $OpenBSD: fdt.h,v 1.3 2021/05/19 17:39:50 kettenis Exp $ */ +/* $OpenBSD: fdt.h,v 1.4 2022/05/30 17:39:09 kettenis Exp $ */ /* * Copyright (c) 2016 Patrick Wildt @@ -44,6 +44,7 @@ void *fdt_find_cons(const char *); #define fdt_intr_enable riscv_intr_enable #define fdt_intr_establish riscv_intr_establish_fdt #define fdt_intr_establish_idx riscv_intr_establish_fdt_idx +#define fdt_intr_establish_idx_cpu riscv_intr_establish_fdt_idx_cpu #define fdt_intr_establish_imap riscv_intr_establish_fdt_imap #define fdt_intr_establish_imap_cpu riscv_intr_establish_fdt_imap_cpu #define fdt_intr_establish_msi riscv_intr_establish_fdt_msi