diff --git a/sys/riscv/conf/GENERIC b/sys/riscv/conf/GENERIC index b65c0317f28d..360d6b163d45 100644 --- a/sys/riscv/conf/GENERIC +++ b/sys/riscv/conf/GENERIC @@ -1,178 +1,182 @@ # # GENERIC -- Generic kernel configuration file for FreeBSD/RISC-V # # For more information on this file, please read the config(5) manual page, # and/or the handbook section on Kernel Configuration Files: # # https://docs.freebsd.org/en/books/handbook/kernelconfig/#kernelconfig-config # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (https://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the ../../conf/NOTES and NOTES files. # If you are in doubt as to the purpose or necessity of a line, check first # in NOTES. # # $FreeBSD$ cpu RISCV ident GENERIC makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols makeoptions WITH_CTF=1 # Run ctfconvert(1) for DTrace support options SCHED_ULE # ULE scheduler options PREEMPTION # Enable kernel thread preemption options VIMAGE # Subsystem virtualization, e.g. VNET options INET # InterNETworking options INET6 # IPv6 communications protocols options TCP_HHOOK # hhook(9) framework for TCP options IPSEC_SUPPORT # Allow kldload of ipsec and tcpmd5 options ROUTE_MPATH # Multipath routing support options TCP_OFFLOAD # TCP offload options SCTP_SUPPORT # Allow kldload of SCTP options FFS # Berkeley Fast Filesystem options SOFTUPDATES # Enable FFS soft updates support options UFS_ACL # Support for access control lists options UFS_DIRHASH # Improve performance on big directories options UFS_GJOURNAL # Enable gjournal-based UFS journaling options QUOTA # Enable disk quotas for UFS options NFSCL # Network Filesystem Client options NFSD # Network Filesystem Server options NFSLOCKD # Network Lock Manager options NFS_ROOT # NFS usable as /, requires NFSCL options MSDOSFS # MSDOS Filesystem options CD9660 # ISO 9660 Filesystem options PROCFS # Process filesystem (requires PSEUDOFS) options PSEUDOFS # Pseudo-filesystem framework options TMPFS # Efficient memory filesystem options GEOM_PART_GPT # GUID Partition Tables. options GEOM_RAID # Soft RAID functionality. options GEOM_LABEL # Provides labelization options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI options KTRACE # ktrace(1) support options STACK # stack(9) support options SYSVSHM # SYSV-style shared memory options SYSVMSG # SYSV-style message queues options SYSVSEM # SYSV-style semaphores options _KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions options PRINTF_BUFR_SIZE=128 # Prevent printf output being interspersed. options KBD_INSTALL_CDEV # install a CDEV entry in /dev # options HWPMC_HOOKS # Necessary kernel hooks for hwpmc(4) options AUDIT # Security event auditing options CAPABILITY_MODE # Capsicum capability mode options CAPABILITIES # Capsicum capabilities options MAC # TrustedBSD MAC Framework options KDTRACE_FRAME # Ensure frames are compiled in options KDTRACE_HOOKS # Kernel DTrace hooks options DDB_CTF # Kernel ELF linker loads CTF data options FPE # Floating-point extension support options RACCT # Resource accounting framework options RACCT_DEFAULT_TO_DISABLED # Set kern.racct.enable=0 by default options RCTL # Resource limits options SMP options INTRNG # RISC-V SBI console device rcons # EXT_RESOURCES pseudo devices options EXT_RESOURCES device clk device hwreset device syscon device syscon_power device riscv_syscon # Bus drivers device pci # VirtIO support device virtio # Generic VirtIO bus (required) device virtio_pci # VirtIO PCI device device vtnet # VirtIO Ethernet device device virtio_blk # VirtIO Block device device virtio_mmio # VirtIO MMIO bus # DTrace support # device dtrace # device dtrace_profile # device dtrace_sdt # device dtrace_fbt # device dtrace_systrace # device dtrace_prototype # device dtraceall # Serial (COM) ports device uart # Generic UART driver device uart_lowrisc # lowRISC UART driver device uart_ns8250 # ns8250-type UART driver # RTC device goldfish_rtc # QEMU RTC # Ethernet drivers device cgem # Cadence GEM Gigabit Ethernet device device miibus # MII bus support device xae # Xilinx AXI Ethernet MAC # DMA support device xdma # DMA interface device axidma # Xilinx AXI DMA Controller +# GPIO +device gpio + # SPI device spibus device spigen # Uncomment for memory disk # options MD_ROOT # options MD_ROOT_SIZE=32768 # 32MB ram disk # makeoptions MFS_IMAGE=/path/to/img # options ROOTDEVNAME=\"ufs:/dev/md0\" # Uncomment for virtio block device # options ROOTDEVNAME=\"ufs:/dev/vtbd0\" # Debugging support. Always need this: options KDB # Enable kernel debugger support. options KDB_TRACE # Print a stack trace for a panic. # For full debugger support use (turn off in stable branch): options DDB # Support DDB. # options GDB # Support remote GDB. options DEADLKRES # Enable the deadlock resolver options INVARIANTS # Enable calls of extra sanity checking options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS options WITNESS # Enable checks to detect deadlocks and cycles options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed options MALLOC_DEBUG_MAXZONES=8 # Separate malloc(9) zones # options EARLY_PRINTF options VERBOSE_SYSINIT=0 # Support debug.verbose_sysinit, off by default # Kernel dump features. options ZSTDIO # zstd-compressed kernel and user dumps # Pseudo devices. device crypto # core crypto support device loop # Network loopback device ether # Ethernet support device vlan # 802.1Q VLAN support device tuntap # Packet tunnel. device md # Memory "disks" device gif # IPv6 and IPv4 tunneling device firmware # firmware assist module # The `bpf' device enables the Berkeley Packet Filter. # Be aware of the administrative consequences of enabling this! # Note that 'bpf' is required for DHCP. device bpf # Berkeley packet filter # Flattened Device Tree options FDT makeoptions MODULES_EXTRA+="dtb/sifive" # SiFive device drivers +device sifive_gpio device sifive_spi include "../sifive/std.sifive" diff --git a/sys/riscv/sifive/files.sifive b/sys/riscv/sifive/files.sifive index 2eb73f1607ac..9ea71c4fcd27 100644 --- a/sys/riscv/sifive/files.sifive +++ b/sys/riscv/sifive/files.sifive @@ -1,6 +1,7 @@ # $FreeBSD$ riscv/sifive/fe310_aon.c optional fe310aon +riscv/sifive/sifive_gpio.c optional sifive_gpio gpio riscv/sifive/sifive_prci.c standard riscv/sifive/sifive_spi.c optional sifive_spi spibus riscv/sifive/sifive_uart.c standard diff --git a/sys/riscv/sifive/sifive_gpio.c b/sys/riscv/sifive/sifive_gpio.c new file mode 100644 index 000000000000..47d03ca448d5 --- /dev/null +++ b/sys/riscv/sifive/sifive_gpio.c @@ -0,0 +1,464 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Jessica Clarke + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* TODO: Provide interrupt controller interface */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* Registers are 32-bit so can only fit 32 pins */ +#define SFGPIO_MAX_PINS 32 + +#define SFGPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) + +#define SFGPIO_INPUT_VAL 0x0 +#define SFGPIO_INPUT_EN 0x4 +#define SFGPIO_OUTPUT_EN 0x8 +#define SFGPIO_OUTPUT_VAL 0xc +#define SFGPIO_RISE_IE 0x18 +#define SFGPIO_RISE_IP 0x1c +#define SFGPIO_FALL_IE 0x20 +#define SFGPIO_FALL_IP 0x24 +#define SFGPIO_HIGH_IE 0x28 +#define SFGPIO_HIGH_IP 0x2c +#define SFGPIO_LOW_IE 0x30 +#define SFGPIO_LOW_IP 0x34 + +struct sfgpio_softc { + device_t dev; + device_t busdev; + struct mtx mtx; + struct resource *mem_res; + int mem_rid; + struct resource *irq_res; + int irq_rid; + int npins; + struct gpio_pin gpio_pins[SFGPIO_MAX_PINS]; +}; + +#define SFGPIO_LOCK(_sc) mtx_lock(&(_sc)->mtx) +#define SFGPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) + +#define SFGPIO_READ(_sc, _off) \ + bus_read_4((_sc)->mem_res, (_off)) +#define SFGPIO_WRITE(_sc, _off, _val) \ + bus_write_4((_sc)->mem_res, (_off), (_val)) + +static struct ofw_compat_data compat_data[] = { + { "sifive,gpio0", 1 }, + { NULL, 0 }, +}; + +static int +sfgpio_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "SiFive GPIO Controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +sfgpio_attach(device_t dev) +{ + struct sfgpio_softc *sc; + phandle_t node; + int error, i; + pcell_t npins; + uint32_t input_en, output_en; + + sc = device_get_softc(dev); + sc->dev = dev; + + node = ofw_bus_get_node(dev); + + mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF); + + sc->mem_rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->mem_rid, RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resource\n"); + error = ENXIO; + goto fail; + } + + if (OF_getencprop(node, "ngpios", &npins, sizeof(npins)) <= 0) { + /* Optional; defaults to 16 */ + npins = 16; + } else if (npins > SFGPIO_MAX_PINS) { + device_printf(dev, "Too many pins: %d\n", npins); + error = ENXIO; + goto fail; + } + sc->npins = npins; + + sc->irq_rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, + RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate IRQ resource\n"); + error = ENXIO; + goto fail; + } + + input_en = SFGPIO_READ(sc, SFGPIO_INPUT_EN); + output_en = SFGPIO_READ(sc, SFGPIO_OUTPUT_EN); + for (i = 0; i < sc->npins; ++i) { + sc->gpio_pins[i].gp_pin = i; + sc->gpio_pins[i].gp_caps = SFGPIO_DEFAULT_CAPS; + sc->gpio_pins[i].gp_flags = + ((input_en & (1u << i)) ? GPIO_PIN_INPUT : 0) | + ((output_en & (1u << i)) ? GPIO_PIN_OUTPUT : 0); + snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "GPIO%d", i); + sc->gpio_pins[i].gp_name[GPIOMAXNAME - 1] = '\0'; + } + + sc->busdev = gpiobus_attach_bus(dev); + if (sc->busdev == NULL) { + device_printf(dev, "Cannot attach gpiobus\n"); + error = ENXIO; + goto fail; + } + + return (0); + +fail: + if (sc->busdev != NULL) + gpiobus_detach_bus(dev); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, + sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, + sc->mem_res); + mtx_destroy(&sc->mtx); + return (error); +} + +static device_t +sfgpio_get_bus(device_t dev) +{ + struct sfgpio_softc *sc; + + sc = device_get_softc(dev); + + return (sc->busdev); +} + +static int +sfgpio_pin_max(device_t dev, int *maxpin) +{ + struct sfgpio_softc *sc; + + sc = device_get_softc(dev); + + *maxpin = sc->npins - 1; + + return (0); +} + +static int +sfgpio_pin_set(device_t dev, uint32_t pin, unsigned int val) +{ + struct sfgpio_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + SFGPIO_LOCK(sc); + reg = SFGPIO_READ(sc, SFGPIO_OUTPUT_VAL); + if (val) + reg |= (1u << pin); + else + reg &= ~(1u << pin); + SFGPIO_WRITE(sc, SFGPIO_OUTPUT_VAL, reg); + SFGPIO_UNLOCK(sc); + + return (0); +} + +static int +sfgpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct sfgpio_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + SFGPIO_LOCK(sc); + if (sc->gpio_pins[pin].gp_flags & GPIO_PIN_OUTPUT) + reg = SFGPIO_READ(sc, SFGPIO_OUTPUT_VAL); + else + reg = SFGPIO_READ(sc, SFGPIO_INPUT_VAL); + *val = (reg & (1u << pin)) ? 1 : 0; + SFGPIO_UNLOCK(sc); + + return (0); +} + +static int +sfgpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct sfgpio_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + SFGPIO_LOCK(sc); + reg = SFGPIO_READ(sc, SFGPIO_OUTPUT_VAL); + reg ^= (1u << pin); + SFGPIO_WRITE(sc, SFGPIO_OUTPUT_VAL, reg); + SFGPIO_UNLOCK(sc); + + return (0); +} + +static int +sfgpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct sfgpio_softc *sc; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + SFGPIO_LOCK(sc); + *caps = sc->gpio_pins[pin].gp_caps; + SFGPIO_UNLOCK(sc); + + return (0); +} + +static int +sfgpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct sfgpio_softc *sc; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + SFGPIO_LOCK(sc); + *flags = sc->gpio_pins[pin].gp_flags; + SFGPIO_UNLOCK(sc); + + return (0); +} + +static int +sfgpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct sfgpio_softc *sc; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + SFGPIO_LOCK(sc); + memcpy(name, sc->gpio_pins[pin].gp_name, GPIOMAXNAME); + SFGPIO_UNLOCK(sc); + + return (0); +} + +static int +sfgpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct sfgpio_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + SFGPIO_LOCK(sc); + + reg = SFGPIO_READ(sc, SFGPIO_INPUT_EN); + if (flags & GPIO_PIN_INPUT) { + reg |= (1u << pin); + sc->gpio_pins[pin].gp_flags |= GPIO_PIN_INPUT; + } else { + reg &= ~(1u << pin); + sc->gpio_pins[pin].gp_flags &= ~GPIO_PIN_INPUT; + } + SFGPIO_WRITE(sc, SFGPIO_INPUT_EN, reg); + + reg = SFGPIO_READ(sc, SFGPIO_OUTPUT_EN); + if (flags & GPIO_PIN_OUTPUT) { + reg |= (1u << pin); + sc->gpio_pins[pin].gp_flags |= GPIO_PIN_OUTPUT; + } else { + reg &= ~(1u << pin); + sc->gpio_pins[pin].gp_flags &= ~GPIO_PIN_OUTPUT; + } + SFGPIO_WRITE(sc, SFGPIO_OUTPUT_EN, reg); + + SFGPIO_UNLOCK(sc); + + return (0); +} + +static int +sfgpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins, + uint32_t change_pins, uint32_t *orig_pins) +{ + struct sfgpio_softc *sc; + uint32_t reg; + + if (first_pin != 0) + return (EINVAL); + + sc = device_get_softc(dev); + + SFGPIO_LOCK(sc); + + reg = SFGPIO_READ(sc, SFGPIO_OUTPUT_VAL); + + if (orig_pins != NULL) + /* Only input_val is implicitly masked by input_en */ + *orig_pins = SFGPIO_READ(sc, SFGPIO_INPUT_VAL) | + (reg & SFGPIO_READ(sc, SFGPIO_OUTPUT_EN)); + + if ((clear_pins | change_pins) != 0) + SFGPIO_WRITE(sc, SFGPIO_OUTPUT_VAL, + (reg & ~clear_pins) ^ change_pins); + + SFGPIO_UNLOCK(sc); + + return (0); +} + +static int +sfgpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins, + uint32_t *pin_flags) +{ + struct sfgpio_softc *sc; + uint32_t ireg, oreg; + int i; + + sc = device_get_softc(dev); + + if (first_pin != 0 || num_pins > sc->npins) + return (EINVAL); + + SFGPIO_LOCK(sc); + + ireg = SFGPIO_READ(sc, SFGPIO_INPUT_EN); + oreg = SFGPIO_READ(sc, SFGPIO_OUTPUT_EN); + for (i = 0; i < num_pins; ++i) { + if (pin_flags[i] & GPIO_PIN_INPUT) { + ireg |= (1u << i); + oreg &= ~(1u << i); + sc->gpio_pins[i].gp_flags |= GPIO_PIN_INPUT; + sc->gpio_pins[i].gp_flags &= ~GPIO_PIN_OUTPUT; + } else if (pin_flags[i] & GPIO_PIN_OUTPUT) { + ireg &= ~(1u << i); + oreg |= (1u << i); + sc->gpio_pins[i].gp_flags &= ~GPIO_PIN_INPUT; + sc->gpio_pins[i].gp_flags |= GPIO_PIN_OUTPUT; + } + } + SFGPIO_WRITE(sc, SFGPIO_INPUT_EN, ireg); + SFGPIO_WRITE(sc, SFGPIO_OUTPUT_EN, oreg); + + SFGPIO_UNLOCK(sc); + + return (0); +} + +static phandle_t +sfgpio_get_node(device_t bus, device_t dev) +{ + return (ofw_bus_get_node(bus)); +} + +static device_method_t sfgpio_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, sfgpio_probe), + DEVMETHOD(device_attach, sfgpio_attach), + + /* GPIO protocol */ + DEVMETHOD(gpio_get_bus, sfgpio_get_bus), + DEVMETHOD(gpio_pin_max, sfgpio_pin_max), + DEVMETHOD(gpio_pin_set, sfgpio_pin_set), + DEVMETHOD(gpio_pin_get, sfgpio_pin_get), + DEVMETHOD(gpio_pin_toggle, sfgpio_pin_toggle), + DEVMETHOD(gpio_pin_getcaps, sfgpio_pin_getcaps), + DEVMETHOD(gpio_pin_getflags, sfgpio_pin_getflags), + DEVMETHOD(gpio_pin_getname, sfgpio_pin_getname), + DEVMETHOD(gpio_pin_setflags, sfgpio_pin_setflags), + DEVMETHOD(gpio_pin_access_32, sfgpio_pin_access_32), + DEVMETHOD(gpio_pin_config_32, sfgpio_pin_config_32), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, sfgpio_get_node), + + DEVMETHOD_END +}; + +static devclass_t sfgpio_devclass; +DEFINE_CLASS_0(gpio, sfgpio_driver, sfgpio_methods, + sizeof(struct sfgpio_softc)); +EARLY_DRIVER_MODULE(gpio, simplebus, sfgpio_driver, sfgpio_devclass, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); +MODULE_DEPEND(sfgpio, gpiobus, 1, 1, 1);