From 8f424995a5201e0e179e1262ef3cae1b68532571 Mon Sep 17 00:00:00 2001 From: jsg Date: Mon, 8 Jun 2015 06:33:16 +0000 Subject: [PATCH] Add initial support for the ARM Versatile Express boards as emulated by qemu with virtio memory ranges. Unfortunately the vexpress-a9 and vexpress-a15 boards/targets have different load addresses and memory maps. Code for the PL011 UART and mmio virtio attachment from Patrick Wildt in bitrig. --- sys/arch/armv7/armv7/armv7var.h | 3 +- sys/arch/armv7/armv7/platform.c | 7 +- sys/arch/armv7/conf/Makefile.armv7 | 6 +- sys/arch/armv7/conf/files.armv7 | 6 +- sys/arch/armv7/vexpress/files.vexpress | 20 + sys/arch/armv7/vexpress/pl011.c | 830 +++++++++++++++++++++ sys/arch/armv7/vexpress/pl011reg.h | 97 +++ sys/arch/armv7/vexpress/pl011var.h | 25 + sys/arch/armv7/vexpress/sysreg.c | 125 ++++ sys/arch/armv7/vexpress/vexpress.c | 110 +++ sys/arch/armv7/vexpress/vexpress_a15.c | 103 +++ sys/arch/armv7/vexpress/vexpress_a9.c | 103 +++ sys/arch/armv7/vexpress/vexpress_machdep.c | 120 +++ sys/arch/armv7/vexpress/virtio_mmio.c | 482 ++++++++++++ 14 files changed, 2032 insertions(+), 5 deletions(-) create mode 100644 sys/arch/armv7/vexpress/files.vexpress create mode 100644 sys/arch/armv7/vexpress/pl011.c create mode 100644 sys/arch/armv7/vexpress/pl011reg.h create mode 100644 sys/arch/armv7/vexpress/pl011var.h create mode 100644 sys/arch/armv7/vexpress/sysreg.c create mode 100644 sys/arch/armv7/vexpress/vexpress.c create mode 100644 sys/arch/armv7/vexpress/vexpress_a15.c create mode 100644 sys/arch/armv7/vexpress/vexpress_a9.c create mode 100644 sys/arch/armv7/vexpress/vexpress_machdep.c create mode 100644 sys/arch/armv7/vexpress/virtio_mmio.c diff --git a/sys/arch/armv7/armv7/armv7var.h b/sys/arch/armv7/armv7/armv7var.h index 1c2f14a6e02..b75358f69af 100644 --- a/sys/arch/armv7/armv7/armv7var.h +++ b/sys/arch/armv7/armv7/armv7var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: armv7var.h,v 1.8 2015/06/07 16:54:16 jsg Exp $ */ +/* $OpenBSD: armv7var.h,v 1.9 2015/06/08 06:33:16 jsg Exp $ */ /* * Copyright (c) 2005,2008 Dale Rahn * Copyright (c) 2012-2013 Patrick Wildt @@ -77,6 +77,7 @@ int armv7_submatch(struct device *, void *, void *); /* board identification - from uboot */ #define BOARD_ID_OMAP3_BEAGLE 1546 #define BOARD_ID_OMAP3_OVERO 1798 +#define BOARD_ID_VEXPRESS 2272 #define BOARD_ID_OMAP4_PANDA 2791 #define BOARD_ID_EXYNOS4_SMDKC210 2838 #define BOARD_ID_EXYNOS4_NURI 3379 diff --git a/sys/arch/armv7/armv7/platform.c b/sys/arch/armv7/armv7/platform.c index 8caec7bb3c5..4c384ce665f 100644 --- a/sys/arch/armv7/armv7/platform.c +++ b/sys/arch/armv7/armv7/platform.c @@ -1,4 +1,4 @@ -/* $OpenBSD: platform.c,v 1.2 2015/05/27 08:03:43 jsg Exp $ */ +/* $OpenBSD: platform.c,v 1.3 2015/06/08 06:33:16 jsg Exp $ */ /* * Copyright (c) 2014 Patrick Wildt * @@ -29,6 +29,7 @@ #include "omap.h" #include "sunxi.h" #include "exynos.h" +#include "vexpress.h" static struct armv7_platform *platform; @@ -36,6 +37,7 @@ struct armv7_platform *imx_platform_match(void); struct armv7_platform *omap_platform_match(void); struct armv7_platform *sunxi_platform_match(void); struct armv7_platform *exynos_platform_match(void); +struct armv7_platform *vexpress_platform_match(void); struct armv7_platform * (*plat_match[])(void) = { #if NIMX > 0 @@ -50,6 +52,9 @@ struct armv7_platform * (*plat_match[])(void) = { #if NEXYNOS > 0 exynos_platform_match, #endif +#if NVEXPRESS > 0 + vexpress_platform_match, +#endif }; void diff --git a/sys/arch/armv7/conf/Makefile.armv7 b/sys/arch/armv7/conf/Makefile.armv7 index 26c05ae1519..504d8227a02 100644 --- a/sys/arch/armv7/conf/Makefile.armv7 +++ b/sys/arch/armv7/conf/Makefile.armv7 @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile.armv7,v 1.11 2015/06/02 02:30:16 jsg Exp $ +# $OpenBSD: Makefile.armv7,v 1.12 2015/06/08 06:33:16 jsg Exp $ # For instructions on building kernels consult the config(8) and options(4) # manual pages. @@ -180,8 +180,10 @@ KERNADDR_OMAP=0x80300000 KERNADDR_IMX=0x10300000 KERNADDR_SUNXI=0x40300000 KERNADDR_EXYNOS=0x40300000 +KERNADDR_VEXPRESSA15=0x80300000 +KERNADDR_VEXPRESSA9=0x60300000 -.for SOC in IMX OMAP SUNXI EXYNOS +.for SOC in IMX OMAP SUNXI EXYNOS VEXPRESSA9 VEXPRESSA15 bsd.${SOC}.umg: bsd mkuboot -a arm -o linux -e ${KERNADDR_${SOC}} -l ${KERNADDR_${SOC}} \ bsd bsd.${SOC}.umg diff --git a/sys/arch/armv7/conf/files.armv7 b/sys/arch/armv7/conf/files.armv7 index 514ebd187f1..ad44ce021b0 100644 --- a/sys/arch/armv7/conf/files.armv7 +++ b/sys/arch/armv7/conf/files.armv7 @@ -1,4 +1,4 @@ -# $OpenBSD: files.armv7,v 1.12 2015/05/27 00:06:14 jsg Exp $ +# $OpenBSD: files.armv7,v 1.13 2015/06/08 06:33:16 jsg Exp $ maxpartitions 16 maxusers 2 8 64 @@ -53,6 +53,9 @@ include "dev/wsfont/files.wsfont" # Include USB stuff include "dev/usb/files.usb" +# PCI and virtio +include "dev/pci/files.pci" + # Machine-independent GPIO drivers include "dev/gpio/files.gpio" @@ -64,3 +67,4 @@ include "arch/armv7/imx/files.imx" include "arch/armv7/omap/files.omap" include "arch/armv7/sunxi/files.sunxi" include "arch/armv7/exynos/files.exynos" +include "arch/armv7/vexpress/files.vexpress" diff --git a/sys/arch/armv7/vexpress/files.vexpress b/sys/arch/armv7/vexpress/files.vexpress new file mode 100644 index 00000000000..d4cd4eb0912 --- /dev/null +++ b/sys/arch/armv7/vexpress/files.vexpress @@ -0,0 +1,20 @@ +# $OpenBSD: files.vexpress,v 1.1 2015/06/08 06:33:16 jsg Exp $ + +define vexpress {} +device vexpress: vexpress +attach vexpress at mainbus +file arch/armv7/vexpress/vexpress_machdep.c vexpress needs-flag +file arch/armv7/vexpress/vexpress.c vexpress +file arch/armv7/vexpress/vexpress_a9.c vexpress +file arch/armv7/vexpress/vexpress_a15.c vexpress + +device pluart +attach pluart at vexpress +file arch/armv7/vexpress/pl011.c pluart + +attach virtio at vexpress with virtio_mmio +file arch/armv7/vexpress/virtio_mmio.c virtio_mmio + +device sysreg +attach sysreg at vexpress +file arch/armv7/vexpress/sysreg.c sysreg diff --git a/sys/arch/armv7/vexpress/pl011.c b/sys/arch/armv7/vexpress/pl011.c new file mode 100644 index 00000000000..97b16986c63 --- /dev/null +++ b/sys/arch/armv7/vexpress/pl011.c @@ -0,0 +1,830 @@ +/* $OpenBSD: pl011.c,v 1.1 2015/06/08 06:33:16 jsg Exp $ */ + +/* + * Copyright (c) 2014 Patrick Wildt + * Copyright (c) 2005 Dale Rahn + * + * 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 "fdt.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef DDB +#include +#endif + +#include +//#include +#if NFDT > 0 +#include +#endif +#include +#include +#include + +#define DEVUNIT(x) (minor(x) & 0x7f) +#define DEVCUA(x) (minor(x) & 0x80) + +struct pl011_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + struct soft_intrhand *sc_si; + void *sc_irq; + struct tty *sc_tty; + struct timeout sc_diag_tmo; + struct timeout sc_dtr_tmo; + int sc_overflows; + int sc_floods; + int sc_errors; + int sc_halt; + u_int16_t sc_ucr1; + u_int16_t sc_ucr2; + u_int16_t sc_ucr3; + u_int16_t sc_ucr4; + u_int8_t sc_hwflags; +#define COM_HW_NOIEN 0x01 +#define COM_HW_FIFO 0x02 +#define COM_HW_SIR 0x20 +#define COM_HW_CONSOLE 0x40 +#define COM_HW_KGDB 0x80 + u_int8_t sc_swflags; +#define COM_SW_SOFTCAR 0x01 +#define COM_SW_CLOCAL 0x02 +#define COM_SW_CRTSCTS 0x04 +#define COM_SW_MDMBUF 0x08 +#define COM_SW_PPS 0x10 + int sc_fifolen; + + u_int8_t sc_initialize; + u_int8_t sc_cua; + u_int16_t *sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend; +#define UART_IBUFSIZE 128 +#define UART_IHIGHWATER 100 + u_int16_t sc_ibufs[2][UART_IBUFSIZE]; + + struct clk *sc_clk; +}; + + +int pl011probe(struct device *parent, void *self, void *aux); +void pl011attach(struct device *parent, struct device *self, void *aux); + +void pl011cnprobe(struct consdev *cp); +void pl011cnprobe(struct consdev *cp); +void pl011cninit(struct consdev *cp); +int pl011cnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, + tcflag_t cflag); +int pl011cngetc(dev_t dev); +void pl011cnputc(dev_t dev, int c); +void pl011cnpollc(dev_t dev, int on); +int pl011_param(struct tty *tp, struct termios *t); +void pl011_start(struct tty *); +void pl011_pwroff(struct pl011_softc *sc); +void pl011_diag(void *arg); +void pl011_raisedtr(void *arg); +void pl011_softint(void *arg); +struct pl011_softc *pl011_sc(dev_t dev); + +int pl011_intr(void *); + + +/* XXX - we imitate 'com' serial ports and take over their entry points */ +/* XXX: These belong elsewhere */ +cdev_decl(pl011); + +struct cfdriver pluart_cd = { + NULL, "pluart", DV_TTY +}; + +struct cfattach pluart_ca = { + sizeof(struct pl011_softc), pl011probe, pl011attach +}; + +bus_space_tag_t pl011consiot; +bus_space_handle_t pl011consioh; +bus_addr_t pl011consaddr; +tcflag_t pl011conscflag = TTYDEF_CFLAG; +int pl011defaultrate = B38400; + +int +pl011probe(struct device *parent, void *self, void *aux) +{ +#if NFDT > 0 + struct armv7_attach_args *aa = aux; + + if (fdt_node_compatible("arm,pl011", aa->aa_node)) + return 1; +#endif + + return 0; +} + +struct cdevsw pl011dev = + cdev_tty_init(3/*XXX NUART */ ,pl011); /* 12: serial port */ + +void +pl011attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct pl011_softc *sc = (struct pl011_softc *) self; + struct armv7mem mem; + int irq; + +#if NFDT > 0 + if (aa->aa_node) { + struct fdt_memory fdtmem; + + if (fdt_get_memory_address(aa->aa_node, 0, &fdtmem)) + panic("%s: could not extract memory data from FDT", + __func__); + mem.addr = fdtmem.addr; + mem.size = fdtmem.size; + + if (fdt_node_property_ints(aa->aa_node, "interrupts", + ints, 3) != 3) + panic("%s: could not extract interrupt data from FDT", + __func__); + irq = ints[1]; + } else +#endif + { + irq = aa->aa_dev->irq[0]; + mem.addr = aa->aa_dev->mem[0].addr; + mem.size = aa->aa_dev->mem[0].size; + } + + sc->sc_irq = arm_intr_establish(irq, IPL_TTY, pl011_intr, + sc, sc->sc_dev.dv_xname); + + sc->sc_iot = aa->aa_iot; + if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh)) + panic("pl011attach: bus_space_map failed!"); + + if (mem.addr == pl011consaddr) + printf(" console"); + + timeout_set(&sc->sc_diag_tmo, pl011_diag, sc); + timeout_set(&sc->sc_dtr_tmo, pl011_raisedtr, sc); + sc->sc_si = softintr_establish(IPL_TTY, pl011_softint, sc); + + if(sc->sc_si == NULL) + panic("%s: can't establish soft interrupt.", + sc->sc_dev.dv_xname); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, UART_IMSC, (UART_IMSC_RXIM | UART_IMSC_TXIM)); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, UART_ICR, 0x7ff); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, UART_LCR_H, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, UART_LCR_H) & + ~UART_LCR_H_FEN); + + printf("\n"); +} + +int +pl011_intr(void *arg) +{ + struct pl011_softc *sc = arg; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + struct tty *tp = sc->sc_tty; + u_int16_t fr; + u_int16_t *p; + u_int16_t c; + + bus_space_write_4(iot, ioh, UART_ICR, -1); + + if (sc->sc_tty == NULL) + return(0); + + fr = bus_space_read_4(iot, ioh, UART_FR); + if (ISSET(fr, UART_FR_TXFE) && ISSET(tp->t_state, TS_BUSY)) { + CLR(tp->t_state, TS_BUSY | TS_FLUSH); + if (sc->sc_halt > 0) + wakeup(&tp->t_outq); + (*linesw[tp->t_line].l_start)(tp); + } + + if(!ISSET(bus_space_read_4(iot, ioh, UART_FR), UART_FR_RXFF)) + return 0; + + p = sc->sc_ibufp; + + while(ISSET(bus_space_read_4(iot, ioh, UART_FR), UART_FR_RXFF)) { + c = bus_space_read_1(iot, ioh, UART_DR); + if (p >= sc->sc_ibufend) { + sc->sc_floods++; + if (sc->sc_errors++ == 0) + timeout_add(&sc->sc_diag_tmo, 60 * hz); + } else { + *p++ = c; + if (p == sc->sc_ibufhigh && ISSET(tp->t_cflag, CRTSCTS)) { + /* XXX */ + //CLR(sc->sc_ucr3, IMXUART_CR3_DSR); + //bus_space_write_4(iot, ioh, IMXUART_UCR3, + // sc->sc_ucr3); + } + } + /* XXX - msr stuff ? */ + } + sc->sc_ibufp = p; + + softintr_schedule(sc->sc_si); + + return 1; +} + +int +pl011_param(struct tty *tp, struct termios *t) +{ + struct pl011_softc *sc = pluart_cd.cd_devs[DEVUNIT(tp->t_dev)]; + //bus_space_tag_t iot = sc->sc_iot; + //bus_space_handle_t ioh = sc->sc_ioh; + int ospeed = t->c_ospeed; + int error; + tcflag_t oldcflag; + + + if (t->c_ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) + return EINVAL; + + switch (ISSET(t->c_cflag, CSIZE)) { + case CS5: + return EINVAL; + case CS6: + return EINVAL; + case CS7: + //CLR(sc->sc_ucr2, IMXUART_CR2_WS); + break; + case CS8: + //SET(sc->sc_ucr2, IMXUART_CR2_WS); + break; + } +// bus_space_write_4(iot, ioh, IMXUART_UCR2, sc->sc_ucr2); + + /* + if (ISSET(t->c_cflag, PARENB)) { + SET(sc->sc_ucr2, IMXUART_CR2_PREN); + bus_space_write_4(iot, ioh, IMXUART_UCR2, sc->sc_ucr2); + } + */ + /* STOPB - XXX */ + if (ospeed == 0) { + /* lower dtr */ + } + + if (ospeed != 0) { + while (ISSET(tp->t_state, TS_BUSY)) { + ++sc->sc_halt; + error = ttysleep(tp, &tp->t_outq, + TTOPRI | PCATCH, "pl011prm", 0); + --sc->sc_halt; + if (error) { + pl011_start(tp); + return (error); + } + } + /* set speed */ + } + + /* setup fifo */ + + /* When not using CRTSCTS, RTS follows DTR. */ + /* sc->sc_dtr = MCR_DTR; */ + + + /* and copy to tty */ + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + oldcflag = tp->t_cflag; + tp->t_cflag = t->c_cflag; + + /* + * If DCD is off and MDMBUF is changed, ask the tty layer if we should + * stop the device. + */ + /* XXX */ + + pl011_start(tp); + + return 0; +} + +void +pl011_start(struct tty *tp) +{ + struct pl011_softc *sc = pluart_cd.cd_devs[DEVUNIT(tp->t_dev)]; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + int s; + s = spltty(); + if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) + goto out; + if (tp->t_outq.c_cc <= tp->t_lowat) { + if (ISSET(tp->t_state, TS_ASLEEP)) { + CLR(tp->t_state, TS_ASLEEP); + wakeup(&tp->t_outq); + } + if (tp->t_outq.c_cc == 0) + goto out; + selwakeup(&tp->t_wsel); + } + SET(tp->t_state, TS_BUSY); + + if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) { + u_char buffer[64]; /* largest fifo */ + int i, n; + + n = q_to_b(&tp->t_outq, buffer, + min(sc->sc_fifolen, sizeof buffer)); + for (i = 0; i < n; i++) { + bus_space_write_4(iot, ioh, UART_DR, buffer[i]); + } + bzero(buffer, n); + } else if (tp->t_outq.c_cc != 0) + bus_space_write_4(iot, ioh, UART_DR, getc(&tp->t_outq)); + +out: + splx(s); +} + +void +pl011_pwroff(struct pl011_softc *sc) +{ +} + +void +pl011_diag(void *arg) +{ + struct pl011_softc *sc = arg; + int overflows, floods; + int s; + + s = spltty(); + sc->sc_errors = 0; + overflows = sc->sc_overflows; + sc->sc_overflows = 0; + floods = sc->sc_floods; + sc->sc_floods = 0; + splx(s); + log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf overflow%s\n", + sc->sc_dev.dv_xname, + overflows, overflows == 1 ? "" : "s", + floods, floods == 1 ? "" : "s"); +} + +void +pl011_raisedtr(void *arg) +{ + //struct pl011_softc *sc = arg; + + //SET(sc->sc_ucr3, IMXUART_CR3_DSR); /* XXX */ + //bus_space_write_4(sc->sc_iot, sc->sc_ioh, IMXUART_UCR3, sc->sc_ucr3); +} + +void +pl011_softint(void *arg) +{ + struct pl011_softc *sc = arg; + struct tty *tp; + u_int16_t *ibufp; + u_int16_t *ibufend; + int c; + int err; + int s; + + if (sc == NULL || sc->sc_ibufp == sc->sc_ibuf) + return; + + tp = sc->sc_tty; + s = spltty(); + + ibufp = sc->sc_ibuf; + ibufend = sc->sc_ibufp; + + if (ibufp == ibufend || tp == NULL || !ISSET(tp->t_state, TS_ISOPEN)) { + splx(s); + return; + } + + sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ? + sc->sc_ibufs[1] : sc->sc_ibufs[0]; + sc->sc_ibufhigh = sc->sc_ibuf + UART_IHIGHWATER; + sc->sc_ibufend = sc->sc_ibuf + UART_IBUFSIZE; + +#if 0 + if (ISSET(tp->t_cflag, CRTSCTS) && + !ISSET(sc->sc_ucr3, IMXUART_CR3_DSR)) { + /* XXX */ + SET(sc->sc_ucr3, IMXUART_CR3_DSR); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IMXUART_UCR3, + sc->sc_ucr3); + } +#endif + + splx(s); + + while (ibufp < ibufend) { + c = *ibufp++; + /* + if (ISSET(c, IMXUART_RX_OVERRUN)) { + sc->sc_overflows++; + if (sc->sc_errors++ == 0) + timeout_add(&sc->sc_diag_tmo, 60 * hz); + } + */ + /* This is ugly, but fast. */ + + err = 0; + /* + if (ISSET(c, IMXUART_RX_PRERR)) + err |= TTY_PE; + if (ISSET(c, IMXUART_RX_FRMERR)) + err |= TTY_FE; + */ + c = (c & 0xff) | err; + (*linesw[tp->t_line].l_rint)(c, tp); + } +} + +int +pl011open(dev_t dev, int flag, int mode, struct proc *p) +{ + int unit = DEVUNIT(dev); + struct pl011_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; + struct tty *tp; + int s; + int error = 0; + + if (unit >= pluart_cd.cd_ndevs) + return ENXIO; + sc = pluart_cd.cd_devs[unit]; + if (sc == NULL) + return ENXIO; + + s = spltty(); + if (sc->sc_tty == NULL) + tp = sc->sc_tty = ttymalloc(0); + else + tp = sc->sc_tty; + + splx(s); + + tp->t_oproc = pl011_start; + tp->t_param = pl011_param; + tp->t_dev = dev; + + if (!ISSET(tp->t_state, TS_ISOPEN)) { + SET(tp->t_state, TS_WOPEN); + ttychars(tp); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + tp->t_cflag = pl011conscflag; + else + tp->t_cflag = TTYDEF_CFLAG; + if (ISSET(sc->sc_swflags, COM_SW_CLOCAL)) + SET(tp->t_cflag, CLOCAL); + if (ISSET(sc->sc_swflags, COM_SW_CRTSCTS)) + SET(tp->t_cflag, CRTSCTS); + if (ISSET(sc->sc_swflags, COM_SW_MDMBUF)) + SET(tp->t_cflag, MDMBUF); + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = pl011defaultrate; + + s = spltty(); + + sc->sc_initialize = 1; + pl011_param(tp, &tp->t_termios); + ttsetwater(tp); + sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0]; + sc->sc_ibufhigh = sc->sc_ibuf + UART_IHIGHWATER; + sc->sc_ibufend = sc->sc_ibuf + UART_IBUFSIZE; + + iot = sc->sc_iot; + ioh = sc->sc_ioh; + +#if 0 + sc->sc_ucr1 = bus_space_read_4(iot, ioh, IMXUART_UCR1); + sc->sc_ucr2 = bus_space_read_4(iot, ioh, IMXUART_UCR2); + sc->sc_ucr3 = bus_space_read_4(iot, ioh, IMXUART_UCR3); + sc->sc_ucr4 = bus_space_read_4(iot, ioh, IMXUART_UCR4); + + /* interrupt after one char on tx/rx */ + /* reference frequency divider: 1 */ + bus_space_write_4(iot, ioh, IMXUART_UFCR, + 1 << IMXUART_FCR_TXTL_SH | + 5 << IMXUART_FCR_RFDIV_SH | + 1 << IMXUART_FCR_RXTL_SH); + + bus_space_write_4(iot, ioh, IMXUART_UBIR, + (pl011defaultrate / 100) - 1); + + /* formula: clk / (rfdiv * 1600) */ + bus_space_write_4(iot, ioh, IMXUART_UBMR, + (clk_get_rate(sc->sc_clk) * 1000) / 1600); + + SET(sc->sc_ucr1, IMXUART_CR1_EN|IMXUART_CR1_RRDYEN); + SET(sc->sc_ucr2, IMXUART_CR2_TXEN|IMXUART_CR2_RXEN); + bus_space_write_4(iot, ioh, IMXUART_UCR1, sc->sc_ucr1); + bus_space_write_4(iot, ioh, IMXUART_UCR2, sc->sc_ucr2); + + /* sc->sc_mcr = MCR_DTR | MCR_RTS; XXX */ + SET(sc->sc_ucr3, IMXUART_CR3_DSR); /* XXX */ + bus_space_write_4(iot, ioh, IMXUART_UCR3, sc->sc_ucr3); +#endif + + SET(tp->t_state, TS_CARR_ON); /* XXX */ + + + } else if (ISSET(tp->t_state, TS_XCLUDE) && p->p_ucred->cr_uid != 0) + return EBUSY; + else + s = spltty(); + + if (DEVCUA(dev)) { + if (ISSET(tp->t_state, TS_ISOPEN)) { + splx(s); + return EBUSY; + } + sc->sc_cua = 1; + } else { + /* tty (not cua) device; wait for carrier if necessary */ + if (ISSET(flag, O_NONBLOCK)) { + if (sc->sc_cua) { + /* Opening TTY non-blocking... but the CUA is busy */ + splx(s); + return EBUSY; + } + } else { + while (sc->sc_cua || + (!ISSET(tp->t_cflag, CLOCAL) && + !ISSET(tp->t_state, TS_CARR_ON))) { + SET(tp->t_state, TS_WOPEN); + error = ttysleep(tp, &tp->t_rawq, + TTIPRI | PCATCH, ttopen, 0); + /* + * If TS_WOPEN has been reset, that means the + * cua device has been closed. We don't want + * to fail in that case, + * so just go around again. + */ + if (error && ISSET(tp->t_state, TS_WOPEN)) { + CLR(tp->t_state, TS_WOPEN); + if (!sc->sc_cua && !ISSET(tp->t_state, + TS_ISOPEN)) + pl011_pwroff(sc); + splx(s); + return error; + } + } + } + } + splx(s); + return (*linesw[tp->t_line].l_open)(dev,tp,p); +} + +int +pl011close(dev_t dev, int flag, int mode, struct proc *p) +{ + int unit = DEVUNIT(dev); + struct pl011_softc *sc = pluart_cd.cd_devs[unit]; + //bus_space_tag_t iot = sc->sc_iot; + //bus_space_handle_t ioh = sc->sc_ioh; + struct tty *tp = sc->sc_tty; + int s; + + /* XXX This is for cons.c. */ + if (!ISSET(tp->t_state, TS_ISOPEN)) + return 0; + + (*linesw[tp->t_line].l_close)(tp, flag, p); + s = spltty(); + if (ISSET(tp->t_state, TS_WOPEN)) { + /* tty device is waiting for carrier; drop dtr then re-raise */ + //CLR(sc->sc_ucr3, IMXUART_CR3_DSR); + //bus_space_write_4(iot, ioh, IMXUART_UCR3, sc->sc_ucr3); + timeout_add(&sc->sc_dtr_tmo, hz * 2); + } else { + /* no one else waiting; turn off the uart */ + pl011_pwroff(sc); + } + CLR(tp->t_state, TS_BUSY | TS_FLUSH); + + sc->sc_cua = 0; + splx(s); + ttyclose(tp); + + return 0; +} + +int +pl011read(dev_t dev, struct uio *uio, int flag) +{ + struct tty *tty; + + tty = pl011tty(dev); + if (tty == NULL) + return ENODEV; + + return((*linesw[tty->t_line].l_read)(tty, uio, flag)); +} + +int +pl011write(dev_t dev, struct uio *uio, int flag) +{ + struct tty *tty; + + tty = pl011tty(dev); + if (tty == NULL) + return ENODEV; + + return((*linesw[tty->t_line].l_write)(tty, uio, flag)); +} + +int +pl011ioctl( dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct pl011_softc *sc; + struct tty *tp; + int error; + + sc = pl011_sc(dev); + if (sc == NULL) + return (ENODEV); + + tp = sc->sc_tty; + if (tp == NULL) + return (ENXIO); + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + error = ttioctl(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + switch(cmd) { + case TIOCSBRK: + break; + case TIOCCBRK: + break; + case TIOCSDTR: + break; + case TIOCCDTR: + break; + case TIOCMSET: + break; + case TIOCMBIS: + break; + case TIOCMBIC: + break; + case TIOCMGET: + break; + case TIOCGFLAGS: + break; + case TIOCSFLAGS: + error = suser(p, 0); + if (error != 0) + return(EPERM); + break; + default: + return (ENOTTY); + } + + return 0; +} + +int +pl011stop(struct tty *tp, int flag) +{ + return 0; +} + +struct tty * +pl011tty(dev_t dev) +{ + int unit; + struct pl011_softc *sc; + unit = DEVUNIT(dev); + if (unit >= pluart_cd.cd_ndevs) + return NULL; + sc = (struct pl011_softc *)pluart_cd.cd_devs[unit]; + if (sc == NULL) + return NULL; + return sc->sc_tty; +} + +struct pl011_softc * +pl011_sc(dev_t dev) +{ + int unit; + struct pl011_softc *sc; + unit = DEVUNIT(dev); + if (unit >= pluart_cd.cd_ndevs) + return NULL; + sc = (struct pl011_softc *)pluart_cd.cd_devs[unit]; + return sc; +} + + +/* serial console */ +void +pl011cnprobe(struct consdev *cp) +{ + cp->cn_dev = makedev(12 /* XXX */, 0); + cp->cn_pri = CN_MIDPRI; +} + +void +pl011cninit(struct consdev *cp) +{ +} + +int +pl011cnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, tcflag_t cflag) +{ + static struct consdev pl011cons = { + NULL, NULL, pl011cngetc, pl011cnputc, pl011cnpollc, NULL, + NODEV, CN_MIDPRI + }; + + if (bus_space_map(iot, iobase, UART_SPACE, 0, &pl011consioh)) + return ENOMEM; + + /* Disable FIFO. */ + bus_space_write_4(iot, pl011consioh, UART_LCR_H, + bus_space_read_4(iot, pl011consioh, UART_LCR_H) & ~UART_LCR_H_FEN); + + cn_tab = &pl011cons; + cn_tab->cn_dev = makedev(12 /* XXX */, 0); + cdevsw[12] = pl011dev; /* KLUDGE */ + + pl011consiot = iot; + pl011consaddr = iobase; + pl011conscflag = cflag; + + return 0; +} + +int +pl011cngetc(dev_t dev) +{ + int c; + int s; + s = splhigh(); + while((bus_space_read_4(pl011consiot, pl011consioh, UART_FR) & + UART_FR_RXFF) == 0) + ; + c = bus_space_read_4(pl011consiot, pl011consioh, UART_DR); + splx(s); + return c; +} + +void +pl011cnputc(dev_t dev, int c) +{ + int s; + s = splhigh(); + while((bus_space_read_4(pl011consiot, pl011consioh, UART_FR) & + UART_FR_TXFE) == 0) + ; + bus_space_write_4(pl011consiot, pl011consioh, UART_DR, (uint8_t)c); + splx(s); +} + +void +pl011cnpollc(dev_t dev, int on) +{ +} diff --git a/sys/arch/armv7/vexpress/pl011reg.h b/sys/arch/armv7/vexpress/pl011reg.h new file mode 100644 index 00000000000..0a79cf94d56 --- /dev/null +++ b/sys/arch/armv7/vexpress/pl011reg.h @@ -0,0 +1,97 @@ +/* $OpenBSD: pl011reg.h,v 1.1 2015/06/08 06:33:16 jsg Exp $ */ + +/* + * Copyright (c) 2014 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. + */ + +#ifndef PL011REG_H +#define PL011REG_H + +#define UART_DR 0x00 /* Data register */ +#define UART_DR_DATA(x) ((x) & 0xf) +#define UART_DR_FE (1 << 8) /* Framing error */ +#define UART_DR_PE (1 << 9) /* Parity error */ +#define UART_DR_BE (1 << 10) /* Break error */ +#define UART_DR_OE (1 << 11) /* Overrun error */ +#define UART_RSR 0x04 /* Receive status register */ +#define UART_RSR_FE (1 << 0) /* Framing error */ +#define UART_RSR_PE (1 << 1) /* Parity error */ +#define UART_RSR_BE (1 << 2) /* Break error */ +#define UART_RSR_OE (1 << 3) /* Overrun error */ +#define UART_ECR 0x04 /* Error clear register */ +#define UART_ECR_FE (1 << 0) /* Framing error */ +#define UART_ECR_PE (1 << 1) /* Parity error */ +#define UART_ECR_BE (1 << 2) /* Break error */ +#define UART_ECR_OE (1 << 3) /* Overrun error */ +#define UART_FR 0x18 /* Flag register */ +#define UART_FR_CTS (1 << 0) /* Clear to send */ +#define UART_FR_DSR (1 << 1) /* Data set ready */ +#define UART_FR_DCD (1 << 2) /* Data carrier detect */ +#define UART_FR_BUSY (1 << 3) /* UART busy */ +#define UART_FR_RXFE (1 << 4) /* Receive FIFO empty */ +#define UART_FR_TXFF (1 << 5) /* Transmit FIFO full */ +#define UART_FR_RXFF (1 << 6) /* Receive FIFO full */ +#define UART_FR_TXFE (1 << 7) /* Transmit FIFO empty */ +#define UART_FR_RI (1 << 8) /* Ring indicator */ +#define UART_ILPR 0x20 /* IrDA low-power counter register */ +#define UART_ILPR_ILPDVSR ((x) & 0xf) /* IrDA low-power divisor */ +#define UART_IBRD 0x24 /* Integer baud rate register */ +#define UART_IBRD_DIVINT ((x) & 0xff) /* Integer baud rate divisor */ +#define UART_FBRD 0x28 /* Fractional baud rate register */ +#define UART_FBRD_DIVFRAC ((x) & 0x3f) /* Fractional baud rate divisor */ +#define UART_LCR_H 0x2c /* Line control register */ +#define UART_LCR_H_BRK (1 << 0) /* Send break */ +#define UART_LCR_H_PEN (1 << 1) /* Parity enable */ +#define UART_LCR_H_EPS (1 << 2) /* Even parity select */ +#define UART_LCR_H_STP2 (1 << 3) /* Two stop bits select */ +#define UART_LCR_H_FEN (1 << 4) /* Enable FIFOs */ +#define UART_LCR_H_WLEN5 (0x0 << 5) /* Word length: 5 bits */ +#define UART_LCR_H_WLEN6 (0x1 << 5) /* Word length: 6 bits */ +#define UART_LCR_H_WLEN7 (0x2 << 5) /* Word length: 7 bits */ +#define UART_LCR_H_WLEN8 (0x3 << 5) /* Word length: 8 bits */ +#define UART_LCR_H_SPS (1 << 7) /* Stick parity select */ +#define UART_CR 0x30 /* Control register */ +#define UART_CR_UARTEN (1 << 0) /* UART enable */ +#define UART_CR_SIREN (1 << 1) /* SIR enable */ +#define UART_CR_SIRLP (1 << 2) /* IrDA SIR low power mode */ +#define UART_CR_LBE (1 << 7) /* Loop back enable */ +#define UART_CR_TXE (1 << 8) /* Transmit enable */ +#define UART_CR_RXE (1 << 9) /* Receive enable */ +#define UART_CR_DTR (1 << 10) /* Data transmit enable */ +#define UART_CR_RTS (1 << 11) /* Request to send */ +#define UART_CR_OUT1 (1 << 12) +#define UART_CR_OUT2 (1 << 13) +#define UART_CR_CTSE (1 << 14) /* CTS hardware flow control enable */ +#define UART_CR_RTSE (1 << 15) /* RTS hardware flow control enable */ +#define UART_IFLS 0x34 /* Interrupt FIFO level select register */ +#define UART_IMSC 0x38 /* Interrupt mask set/clear register */ +#define UART_IMSC_RIMIM (1 << 0) +#define UART_IMSC_CTSMIM (1 << 1) +#define UART_IMSC_DCDMIM (1 << 2) +#define UART_IMSC_DSRMIM (1 << 3) +#define UART_IMSC_RXIM (1 << 4) +#define UART_IMSC_TXIM (1 << 5) +#define UART_IMSC_RTIM (1 << 6) +#define UART_IMSC_FEIM (1 << 7) +#define UART_IMSC_PEIM (1 << 8) +#define UART_IMSC_BEIM (1 << 9) +#define UART_IMSC_OEIM (1 << 10) +#define UART_RIS 0x3c /* Raw interrupt status register */ +#define UART_MIS 0x40 /* Masked interrupt status register */ +#define UART_ICR 0x44 /* Interrupt clear register */ +#define UART_DMACR 0x48 /* DMA control register */ +#define UART_SPACE 0x100 + +#endif /* !PL011REG_H */ diff --git a/sys/arch/armv7/vexpress/pl011var.h b/sys/arch/armv7/vexpress/pl011var.h new file mode 100644 index 00000000000..ebdf82e24e9 --- /dev/null +++ b/sys/arch/armv7/vexpress/pl011var.h @@ -0,0 +1,25 @@ +/* $OpenBSD: pl011var.h,v 1.1 2015/06/08 06:33:16 jsg Exp $ */ + +/* + * Copyright (c) 2014 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. + */ + +#ifndef PL011VAR_H +#define PL011VAR_H + +int pl011cnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, + tcflag_t cflag); + +#endif /* !PL011VAR_H */ diff --git a/sys/arch/armv7/vexpress/sysreg.c b/sys/arch/armv7/vexpress/sysreg.c new file mode 100644 index 00000000000..4479ae0b550 --- /dev/null +++ b/sys/arch/armv7/vexpress/sysreg.c @@ -0,0 +1,125 @@ +/* $OpenBSD: sysreg.c,v 1.1 2015/06/08 06:33:16 jsg Exp $ */ + +/* + * Copyright (c) 2015 Jonathan Gray + * + * 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 + +#define SYS_ID 0x00 +#define SYS_PROCID0 0x84 +#define SYS_PROCID1 0x88 +#define SYS_CFGDATA 0xa0 +#define SYS_CFGCTRL 0xa4 +#define SYS_CFGSTAT 0xa8 + +#define SYS_CFG_WRITE (1 << 30) +#define SYS_CFG_START (1U << 31) + +#define SYS_CFG_RESET 5 +#define SYS_CFG_SHUTDOWN 8 +#define SYS_CFG_REBOOT 9 + +#define SYS_CFGSTAT_COMPLETE (1 << 0) +#define SYS_CFGSTAT_ERROR (1 << 1) + +struct sysreg_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +struct sysreg_softc *sysreg_sc; + +void sysreg_attach(struct device *, struct device *, void *); +void sysreg_reset(void); + +struct cfattach sysreg_ca = { + sizeof (struct sysreg_softc), NULL, sysreg_attach +}; + +struct cfdriver sysreg_cd = { + NULL, "sysreg", DV_DULL +}; + +void +sysreg_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct sysreg_softc *sc = (struct sysreg_softc *)self; + uint32_t id; + + sc->sc_iot = aa->aa_iot; + if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr, + aa->aa_dev->mem[0].size, 0, &sc->sc_ioh)) + panic(": bus_space_map failed!"); + sysreg_sc = sc; + + id = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SYS_ID); + printf(": ID 0x%x", id); + + id = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SYS_PROCID0); + printf(" PROCID0 0x%x\n", id); +} + +void +sysconf_function(struct sysreg_softc *sc, int function) +{ + int dcc, site, position, device; + + dcc = 0; + site = 0; + position = 0; + device = 0; + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, SYS_CFGSTAT, 0); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, SYS_CFGCTRL, + SYS_CFG_START | SYS_CFG_WRITE | + (dcc << 26) | (function << 20) | (site << 16) | + (position << 12) | device); + + while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, SYS_CFGSTAT) & + SYS_CFGSTAT_COMPLETE) == 0); + + if (bus_space_read_4(sc->sc_iot, sc->sc_ioh, SYS_CFGSTAT) & + SYS_CFGSTAT_ERROR) + printf("SYS_CFGSTAT error\n"); +} + +void +sysconf_reboot(void) +{ + struct sysreg_softc *sc = sysreg_sc; + + if (sc == NULL) + return; + + sysconf_function(sc, SYS_CFG_REBOOT); +} + +void +sysconf_shutdown(void) +{ + struct sysreg_softc *sc = sysreg_sc; + + if (sc == NULL) + return; + + sysconf_function(sc, SYS_CFG_SHUTDOWN); +} diff --git a/sys/arch/armv7/vexpress/vexpress.c b/sys/arch/armv7/vexpress/vexpress.c new file mode 100644 index 00000000000..fc9481916c2 --- /dev/null +++ b/sys/arch/armv7/vexpress/vexpress.c @@ -0,0 +1,110 @@ +/* $OpenBSD: vexpress.c,v 1.1 2015/06/08 06:33:16 jsg Exp $ */ + +/* + * Copyright (c) 2015 Jonathan Gray + * + * 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 + +int vexpress_match(struct device *, void *, void *); +void vexpress_a9_init(); +void vexpress_a15_init(); + +struct cfattach vexpress_ca = { + sizeof(struct armv7_softc), vexpress_match, armv7_attach, NULL, + config_activate_children +}; + +struct cfdriver vexpress_cd = { + NULL, "vexpress", DV_DULL +}; + +struct board_dev vexpress_devs[] = { + { "sysreg", 0 }, + { "pluart", 0 }, + { "virtio", 0 }, + { "virtio", 1 }, + { "virtio", 2 }, + { "virtio", 3 }, + { NULL, 0 } +}; + +struct armv7_board vexpress_boards[] = { + { + BOARD_ID_VEXPRESS, + "ARM Versatile Express", + vexpress_devs, + NULL, + }, + { 0, NULL, NULL, NULL }, +}; + +struct board_dev * +vexpress_board_devs(void) +{ + int i; + + for (i = 0; vexpress_boards[i].name != NULL; i++) { + if (vexpress_boards[i].board_id == board_id) + return (vexpress_boards[i].devs); + } + return (NULL); +} + +extern vaddr_t physical_start; + +int +vexpress_legacy_map(void) +{ + return ((cpufunc_id() & CPU_ID_CORTEX_A9_MASK) == CPU_ID_CORTEX_A9); +} + +void +vexpress_board_init(void) +{ + if (board_id != BOARD_ID_VEXPRESS) + return; + + if (vexpress_legacy_map()) + vexpress_a9_init(); + else + vexpress_a15_init(); +} + +const char * +vexpress_board_name(void) +{ + int i; + + for (i = 0; vexpress_boards[i].name != NULL; i++) { + if (vexpress_boards[i].board_id == board_id) { + return (vexpress_boards[i].name); + break; + } + } + return (NULL); +} + +int +vexpress_match(struct device *parent, void *cfdata, void *aux) +{ + return (vexpress_board_devs() != NULL); +} diff --git a/sys/arch/armv7/vexpress/vexpress_a15.c b/sys/arch/armv7/vexpress/vexpress_a15.c new file mode 100644 index 00000000000..b6025cb3484 --- /dev/null +++ b/sys/arch/armv7/vexpress/vexpress_a15.c @@ -0,0 +1,103 @@ +/* $OpenBSD: vexpress_a15.c,v 1.1 2015/06/08 06:33:16 jsg Exp $ */ + +/* + * Copyright (c) 2015 Jonathan Gray + * + * 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 + +#define SYSREG_ADDR 0x1c010000 +#define SYSREG_SIZE 0x1000 + +#define UARTx_SIZE 0x1000 +#define UART0_ADDR 0x1c090000 +#define UART1_ADDR 0x1c0a0000 +#define UART2_ADDR 0x1c0b0000 +#define UART3_ADDR 0x1c0c0000 + +#define UART0_IRQ 5 +#define UART1_IRQ 6 +#define UART2_IRQ 7 +#define UART3_IRQ 8 + +#define VIRTIO0_ADDR 0x1c130000 +#define VIRTIO1_ADDR 0x1c130200 +#define VIRTIO2_ADDR 0x1c130400 +#define VIRTIO3_ADDR 0x1c130600 +#define VIRTIO_SIZE 0x200 + +#define VIRTIO0_IRQ 40 +#define VIRTIO1_IRQ 41 +#define VIRTIO2_IRQ 42 +#define VIRTIO3_IRQ 43 + +struct armv7_dev vexpress_a15_devs[] = { + { .name = "sysreg", + .unit = 0, + .mem = { { SYSREG_ADDR, SYSREG_SIZE } }, + }, + { .name = "pluart", + .unit = 0, + .mem = { { UART0_ADDR, UARTx_SIZE } }, + .irq = { UART0_IRQ } + }, + { .name = "pluart", + .unit = 1, + .mem = { { UART1_ADDR, UARTx_SIZE } }, + .irq = { UART1_IRQ } + }, + { .name = "pluart", + .unit = 2, + .mem = { { UART1_ADDR, UARTx_SIZE } }, + .irq = { UART2_IRQ } + }, + { .name = "pluart", + .unit = 3, + .mem = { { UART1_ADDR, UARTx_SIZE } }, + .irq = { UART3_IRQ } + }, + { .name = "virtio", + .unit = 0, + .mem = { { VIRTIO0_ADDR, VIRTIO_SIZE } }, + .irq = { VIRTIO0_IRQ } + }, + { .name = "virtio", + .unit = 1, + .mem = { { VIRTIO1_ADDR, VIRTIO_SIZE } }, + .irq = { VIRTIO1_IRQ } + }, + { .name = "virtio", + .unit = 2, + .mem = { { VIRTIO2_ADDR, VIRTIO_SIZE } }, + .irq = { VIRTIO2_IRQ } + }, + { .name = "virtio", + .unit = 3, + .mem = { { VIRTIO3_ADDR, VIRTIO_SIZE } }, + .irq = { VIRTIO3_IRQ } + }, +}; + +void +vexpress_a15_init(void) +{ + armv7_set_devs(vexpress_a15_devs); +} diff --git a/sys/arch/armv7/vexpress/vexpress_a9.c b/sys/arch/armv7/vexpress/vexpress_a9.c new file mode 100644 index 00000000000..3204e10ed94 --- /dev/null +++ b/sys/arch/armv7/vexpress/vexpress_a9.c @@ -0,0 +1,103 @@ +/* $OpenBSD: vexpress_a9.c,v 1.1 2015/06/08 06:33:16 jsg Exp $ */ + +/* + * Copyright (c) 2015 Jonathan Gray + * + * 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 + +#define SYSREG_ADDR 0x10000000 +#define SYSREG_SIZE 0x1000 + +#define UARTx_SIZE 0x1000 +#define UART0_ADDR 0x10009000 +#define UART1_ADDR 0x1000a000 +#define UART2_ADDR 0x1000b000 +#define UART3_ADDR 0x1000c000 + +#define UART0_IRQ 5 +#define UART1_IRQ 6 +#define UART2_IRQ 7 +#define UART3_IRQ 8 + +#define VIRTIO0_ADDR 0x10013000 +#define VIRTIO1_ADDR 0x10013200 +#define VIRTIO2_ADDR 0x10013400 +#define VIRTIO3_ADDR 0x10013600 +#define VIRTIO_SIZE 0x200 + +#define VIRTIO0_IRQ 40 +#define VIRTIO1_IRQ 41 +#define VIRTIO2_IRQ 42 +#define VIRTIO3_IRQ 43 + +struct armv7_dev vexpress_a9_devs[] = { + { .name = "sysreg", + .unit = 0, + .mem = { { SYSREG_ADDR, SYSREG_SIZE } }, + }, + { .name = "pluart", + .unit = 0, + .mem = { { UART0_ADDR, UARTx_SIZE } }, + .irq = { UART0_IRQ } + }, + { .name = "pluart", + .unit = 1, + .mem = { { UART1_ADDR, UARTx_SIZE } }, + .irq = { UART1_IRQ } + }, + { .name = "pluart", + .unit = 2, + .mem = { { UART1_ADDR, UARTx_SIZE } }, + .irq = { UART2_IRQ } + }, + { .name = "pluart", + .unit = 3, + .mem = { { UART1_ADDR, UARTx_SIZE } }, + .irq = { UART3_IRQ } + }, + { .name = "virtio", + .unit = 0, + .mem = { { VIRTIO0_ADDR, VIRTIO_SIZE } }, + .irq = { VIRTIO0_IRQ } + }, + { .name = "virtio", + .unit = 1, + .mem = { { VIRTIO1_ADDR, VIRTIO_SIZE } }, + .irq = { VIRTIO1_IRQ } + }, + { .name = "virtio", + .unit = 2, + .mem = { { VIRTIO2_ADDR, VIRTIO_SIZE } }, + .irq = { VIRTIO2_IRQ } + }, + { .name = "virtio", + .unit = 3, + .mem = { { VIRTIO3_ADDR, VIRTIO_SIZE } }, + .irq = { VIRTIO3_IRQ } + }, +}; + +void +vexpress_a9_init(void) +{ + armv7_set_devs(vexpress_a9_devs); +} diff --git a/sys/arch/armv7/vexpress/vexpress_machdep.c b/sys/arch/armv7/vexpress/vexpress_machdep.c new file mode 100644 index 00000000000..836712db247 --- /dev/null +++ b/sys/arch/armv7/vexpress/vexpress_machdep.c @@ -0,0 +1,120 @@ +/* $OpenBSD: vexpress_machdep.c,v 1.1 2015/06/08 06:33:16 jsg Exp $ */ +/* + * Copyright (c) 2013 Sylvestre Gallon + * + * 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 +#include +#include + +extern void sysconf_reboot(void); +extern void sysconf_shutdown(void); +extern char *vexpress_board_name(void); +extern struct board_dev *vexpress_board_devs(void); +extern void vexpress_board_init(void); +extern int vexpress_legacy_map(void); +extern int comcnspeed; +extern int comcnmode; + +void +vexpress_platform_smc_write(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off, + uint32_t op, uint32_t val) +{ + bus_space_write_4(iot, ioh, off, val); +} + +void +vexpress_platform_init_cons(void) +{ + paddr_t paddr; + + switch (board_id) { + default: + case BOARD_ID_VEXPRESS: + if (vexpress_legacy_map()) + paddr = 0x10009000; + else + paddr = 0x1c090000; + break; + } + pl011cnattach(&armv7_bs_tag, paddr, comcnspeed, comcnmode); +} + +void +vexpress_platform_watchdog_reset(void) +{ + sysconf_reboot(); +} + +void +vexpress_platform_powerdown(void) +{ + sysconf_shutdown(); +} + +const char * +vexpress_platform_board_name(void) +{ + return (vexpress_board_name()); +} + +void +vexpress_platform_disable_l2_if_needed(void) +{ + +} + +void +vexpress_platform_board_init(void) +{ + vexpress_board_init(); +} + +struct armv7_platform vexpress_platform = { + .boot_name = "OpenBSD/vexpress", + .board_name = vexpress_platform_board_name, + .board_init = vexpress_platform_board_init, + .smc_write = vexpress_platform_smc_write, + .init_cons = vexpress_platform_init_cons, + .watchdog_reset = vexpress_platform_watchdog_reset, + .powerdown = vexpress_platform_powerdown, + .disable_l2_if_needed = vexpress_platform_disable_l2_if_needed, +}; + +struct armv7_platform * +vexpress_platform_match(void) +{ + struct board_dev *devs; + + devs = vexpress_board_devs(); + if (devs == NULL) + return (NULL); + + vexpress_platform.devs = devs; + return (&vexpress_platform); +} diff --git a/sys/arch/armv7/vexpress/virtio_mmio.c b/sys/arch/armv7/vexpress/virtio_mmio.c new file mode 100644 index 00000000000..1a6971da173 --- /dev/null +++ b/sys/arch/armv7/vexpress/virtio_mmio.c @@ -0,0 +1,482 @@ +/* $OpenBSD: virtio_mmio.c,v 1.1 2015/06/08 06:33:16 jsg Exp $ */ +/* $NetBSD: virtio.c,v 1.3 2011/11/02 23:05:52 njoly Exp $ */ + +/* + * Copyright (c) 2014 Patrick Wildt + * Copyright (c) 2012 Stefan Fritsch. + * Copyright (c) 2010 Minoura Makoto. + * All rights reserved. + * + * 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 ``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 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. + */ +#include "fdt.h" + +#include +#include +#include +#include +#include + +#include +#include + +#if NFDT > 0 +#include +#endif +#include + +#define VIRTIO_MMIO_MAGIC ('v' | 'i' << 8 | 'r' << 16 | 't' << 24) + +#define VIRTIO_MMIO_MAGIC_VALUE 0x000 +#define VIRTIO_MMIO_VERSION 0x004 +#define VIRTIO_MMIO_DEVICE_ID 0x008 +#define VIRTIO_MMIO_VENDOR_ID 0x00c +#define VIRTIO_MMIO_HOST_FEATURES 0x010 +#define VIRTIO_MMIO_HOST_FEATURES_SEL 0x014 +#define VIRTIO_MMIO_GUEST_FEATURES 0x020 +#define VIRTIO_MMIO_GUEST_FEATURES_SEL 0x024 +#define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 +#define VIRTIO_MMIO_QUEUE_SEL 0x030 +#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 +#define VIRTIO_MMIO_QUEUE_NUM 0x038 +#define VIRTIO_MMIO_QUEUE_ALIGN 0x03c +#define VIRTIO_MMIO_QUEUE_PFN 0x040 +#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 +#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 +#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 +#define VIRTIO_MMIO_STATUS 0x070 +#define VIRTIO_MMIO_CONFIG 0x100 + +#define VIRTIO_MMIO_INT_VRING (1 << 0) +#define VIRTIO_MMIO_INT_CONFIG (1 << 1) + +#define DEVNAME(sc) (sc)->sc_dev.dv_xname + +/* + * XXX: Before being used on big endian arches, the access to config registers + * XXX: needs to be reviewed/fixed. The non-device specific registers are + * XXX: PCI-endian while the device specific registers are native endian. + */ + +#define virtio_set_status(sc, s) virtio_mmio_set_status(sc, s) +#define virtio_device_reset(sc) virtio_set_status((sc), 0) + +int virtio_mmio_match(struct device *, void *, void *); +void virtio_mmio_attach(struct device *, struct device *, void *); +int virtio_mmio_detach(struct device *, int); + +void virtio_mmio_kick(struct virtio_softc *, uint16_t); +uint8_t virtio_mmio_read_device_config_1(struct virtio_softc *, int); +uint16_t virtio_mmio_read_device_config_2(struct virtio_softc *, int); +uint32_t virtio_mmio_read_device_config_4(struct virtio_softc *, int); +uint64_t virtio_mmio_read_device_config_8(struct virtio_softc *, int); +void virtio_mmio_write_device_config_1(struct virtio_softc *, int, uint8_t); +void virtio_mmio_write_device_config_2(struct virtio_softc *, int, uint16_t); +void virtio_mmio_write_device_config_4(struct virtio_softc *, int, uint32_t); +void virtio_mmio_write_device_config_8(struct virtio_softc *, int, uint64_t); +uint16_t virtio_mmio_read_queue_size(struct virtio_softc *, uint16_t); +void virtio_mmio_setup_queue(struct virtio_softc *, uint16_t, uint32_t); +void virtio_mmio_set_status(struct virtio_softc *, int); +uint32_t virtio_mmio_negotiate_features(struct virtio_softc *, uint32_t, + const struct virtio_feature_name *); +int virtio_mmio_intr(void *); + +struct virtio_mmio_softc { + struct virtio_softc sc_sc; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_size_t sc_iosize; + bus_dma_tag_t sc_dmat; + + void *sc_ih; + + int sc_config_offset; +}; + +struct cfattach virtio_mmio_ca = { + sizeof(struct virtio_mmio_softc), + virtio_mmio_match, + virtio_mmio_attach, + virtio_mmio_detach, + NULL +}; + +struct cfattach virtio_mmio_fdt_ca = { + sizeof(struct virtio_mmio_softc), + NULL, + virtio_mmio_attach, + virtio_mmio_detach, + NULL +}; + +struct virtio_ops virtio_mmio_ops = { + virtio_mmio_kick, + virtio_mmio_read_device_config_1, + virtio_mmio_read_device_config_2, + virtio_mmio_read_device_config_4, + virtio_mmio_read_device_config_8, + virtio_mmio_write_device_config_1, + virtio_mmio_write_device_config_2, + virtio_mmio_write_device_config_4, + virtio_mmio_write_device_config_8, + virtio_mmio_read_queue_size, + virtio_mmio_setup_queue, + virtio_mmio_set_status, + virtio_mmio_negotiate_features, + virtio_mmio_intr, +}; + +uint16_t +virtio_mmio_read_queue_size(struct virtio_softc *vsc, uint16_t idx) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_SEL, idx); + return bus_space_read_4(sc->sc_iot, sc->sc_ioh, + VIRTIO_MMIO_QUEUE_NUM_MAX); +} + +void +virtio_mmio_setup_queue(struct virtio_softc *vsc, uint16_t idx, uint32_t addr) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_SEL, idx); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_NUM, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_NUM_MAX)); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_ALIGN, + PAGE_SIZE); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_PFN, addr); +} + +void +virtio_mmio_set_status(struct virtio_softc *vsc, int status) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + int old = 0; + + if (status != 0) + old = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + VIRTIO_MMIO_STATUS); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_STATUS, + status|old); +} + +int +virtio_mmio_match(struct device *parent, void *cfdata, void *aux) +{ +#if NFDT > 0 + struct armv7_attach_args *aa = aux; + + if (fdt_node_compatible("virtio,mmio", aa->aa_node)) + return 1; +#endif + + return 0; +} + +void +virtio_mmio_attach(struct device *parent, struct device *self, void *aux) +{ + struct armv7_attach_args *aa = aux; + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)self; + struct virtio_softc *vsc = &sc->sc_sc; + struct armv7mem mem; + uint32_t id, magic, version; + int irq; + +#if NFDT > 0 + if (aa->aa_node) { + struct fdt_memory fdtmem; + uint32_t ints[3]; + + if (fdt_get_memory_address(aa->aa_node, 0, &fdtmem)) + panic("%s: could not extract memory data from FDT", __func__); + + if (fdt_node_property_ints(aa->aa_node, "interrupts", + ints, 3) != 3) + panic("%s: could not extract interrupt data from FDT", + __func__); + + mem.addr = fdtmem.addr; + mem.size = fdtmem.size; + + irq = ints[1]; + } else +#endif + { + mem.addr = aa->aa_dev->mem[0].addr; + mem.size = aa->aa_dev->mem[0].size; + + irq = aa->aa_dev->irq[0]; + } + + sc->sc_iosize = mem.size; + sc->sc_iot = aa->aa_iot; + sc->sc_dmat = aa->aa_dmat; + if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + magic = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + VIRTIO_MMIO_MAGIC_VALUE); + if (magic != VIRTIO_MMIO_MAGIC) { + printf(": wrong magic value 0x%08x; giving up\n", magic); + return; + } + + version = bus_space_read_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_VERSION); + if (version != 1) { + printf(": unknown version 0x%02x; giving up\n", version); + return; + } + + id = bus_space_read_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_DEVICE_ID); + printf(": Virtio %s Device", virtio_device_string(id)); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_GUEST_PAGE_SIZE, + PAGE_SIZE); + + printf("\n"); + + /* No device connected. */ + if (id == 0) + return; + + vsc->sc_ops = &virtio_mmio_ops; + vsc->sc_dmat = sc->sc_dmat; + sc->sc_config_offset = VIRTIO_MMIO_CONFIG; + + virtio_device_reset(vsc); + virtio_mmio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_ACK); + virtio_mmio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER); + + /* XXX: use softc as aux... */ + vsc->sc_childdevid = id; + vsc->sc_child = NULL; + config_found(self, sc, NULL); + if (vsc->sc_child == NULL) { + printf("%s: no matching child driver; not configured\n", + vsc->sc_dev.dv_xname); + goto fail_1; + } + if (vsc->sc_child == VIRTIO_CHILD_ERROR) { + printf("%s: virtio configuration failed\n", + vsc->sc_dev.dv_xname); + goto fail_1; + } + + sc->sc_ih = arm_intr_establish(irq, vsc->sc_ipl, + virtio_mmio_intr, sc, vsc->sc_dev.dv_xname); + if (sc->sc_ih == NULL) { + printf("%s: couldn't establish interrupt\n", + vsc->sc_dev.dv_xname); + goto fail_2; + } + + virtio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK); + return; + +fail_2: + config_detach(vsc->sc_child, 0); +fail_1: + /* no mmio_mapreg_unmap() or mmio_intr_unmap() */ + virtio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_FAILED); +} + +int +virtio_mmio_detach(struct device *self, int flags) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)self; + struct virtio_softc *vsc = &sc->sc_sc; + int r; + + if (vsc->sc_child != 0 && vsc->sc_child != VIRTIO_CHILD_ERROR) { + r = config_detach(vsc->sc_child, flags); + if (r) + return r; + } + KASSERT(vsc->sc_child == 0 || vsc->sc_child == VIRTIO_CHILD_ERROR); + KASSERT(vsc->sc_vqs == 0); + arm_intr_disestablish(sc->sc_ih); + sc->sc_ih = 0; + if (sc->sc_iosize) + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize); + sc->sc_iosize = 0; + + return 0; +} + +/* + * Feature negotiation. + * Prints available / negotiated features if guest_feature_names != NULL and + * VIRTIO_DEBUG is 1 + */ +uint32_t +virtio_mmio_negotiate_features(struct virtio_softc *vsc, uint32_t guest_features, + const struct virtio_feature_name *guest_feature_names) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + uint32_t host, neg; + + /* + * indirect descriptors can be switched off by setting bit 1 in the + * driver flags, see config(8) + */ + if (!(vsc->sc_dev.dv_cfdata->cf_flags & 1) && + !(vsc->sc_child->dv_cfdata->cf_flags & 1)) { + guest_features |= VIRTIO_F_RING_INDIRECT_DESC; + } else { + printf("RingIndirectDesc disabled by UKC\n"); + } + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + VIRTIO_MMIO_HOST_FEATURES_SEL, 0); + host = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + VIRTIO_MMIO_HOST_FEATURES); + neg = host & guest_features; +#if VIRTIO_DEBUG + if (guest_feature_names) + virtio_log_features(host, neg, guest_feature_names); +#endif + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + VIRTIO_MMIO_GUEST_FEATURES_SEL, 0); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + VIRTIO_MMIO_GUEST_FEATURES, neg); + vsc->sc_features = neg; + if (neg & VIRTIO_F_RING_INDIRECT_DESC) + vsc->sc_indirect = 1; + else + vsc->sc_indirect = 0; + + return neg; +} + +/* + * Device configuration registers. + */ +uint8_t +virtio_mmio_read_device_config_1(struct virtio_softc *vsc, int index) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + return bus_space_read_1(sc->sc_iot, sc->sc_ioh, + sc->sc_config_offset + index); +} + +uint16_t +virtio_mmio_read_device_config_2(struct virtio_softc *vsc, int index) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + return bus_space_read_2(sc->sc_iot, sc->sc_ioh, + sc->sc_config_offset + index); +} + +uint32_t +virtio_mmio_read_device_config_4(struct virtio_softc *vsc, int index) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + return bus_space_read_4(sc->sc_iot, sc->sc_ioh, + sc->sc_config_offset + index); +} + +uint64_t +virtio_mmio_read_device_config_8(struct virtio_softc *vsc, int index) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + uint64_t r; + + r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + sc->sc_config_offset + index + sizeof(uint32_t)); + r <<= 32; + r += bus_space_read_4(sc->sc_iot, sc->sc_ioh, + sc->sc_config_offset + index); + return r; +} + +void +virtio_mmio_write_device_config_1(struct virtio_softc *vsc, + int index, uint8_t value) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + bus_space_write_1(sc->sc_iot, sc->sc_ioh, + sc->sc_config_offset + index, value); +} + +void +virtio_mmio_write_device_config_2(struct virtio_softc *vsc, + int index, uint16_t value) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + bus_space_write_2(sc->sc_iot, sc->sc_ioh, + sc->sc_config_offset + index, value); +} + +void +virtio_mmio_write_device_config_4(struct virtio_softc *vsc, + int index, uint32_t value) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + sc->sc_config_offset + index, value); +} + +void +virtio_mmio_write_device_config_8(struct virtio_softc *vsc, + int index, uint64_t value) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + sc->sc_config_offset + index, + value & 0xffffffff); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + sc->sc_config_offset + index + sizeof(uint32_t), + value >> 32); +} + +/* + * Interrupt handler. + */ +int +virtio_mmio_intr(void *arg) +{ + struct virtio_mmio_softc *sc = arg; + struct virtio_softc *vsc = &sc->sc_sc; + int isr, r = 0; + + /* check and ack the interrupt */ + isr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + VIRTIO_MMIO_INTERRUPT_STATUS); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + VIRTIO_MMIO_INTERRUPT_ACK, isr); + if ((isr & VIRTIO_MMIO_INT_CONFIG) && + (vsc->sc_config_change != NULL)) + r = (vsc->sc_config_change)(vsc); + if ((isr & VIRTIO_MMIO_INT_VRING) && + (vsc->sc_intrhand != NULL)) + r |= (vsc->sc_intrhand)(vsc); + + return r; +} + +void +virtio_mmio_kick(struct virtio_softc *vsc, uint16_t idx) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_NOTIFY, + idx); +} -- 2.20.1