1ea7f1c8cSNeel Natu /*- 2ea7f1c8cSNeel Natu * Copyright (c) 2012 NetApp, Inc. 3ea7f1c8cSNeel Natu * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 4ea7f1c8cSNeel Natu * All rights reserved. 5ea7f1c8cSNeel Natu * 6ea7f1c8cSNeel Natu * Redistribution and use in source and binary forms, with or without 7ea7f1c8cSNeel Natu * modification, are permitted provided that the following conditions 8ea7f1c8cSNeel Natu * are met: 9ea7f1c8cSNeel Natu * 1. Redistributions of source code must retain the above copyright 10ea7f1c8cSNeel Natu * notice, this list of conditions and the following disclaimer. 11ea7f1c8cSNeel Natu * 2. Redistributions in binary form must reproduce the above copyright 12ea7f1c8cSNeel Natu * notice, this list of conditions and the following disclaimer in the 13ea7f1c8cSNeel Natu * documentation and/or other materials provided with the distribution. 14ea7f1c8cSNeel Natu * 15ea7f1c8cSNeel Natu * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 16ea7f1c8cSNeel Natu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17ea7f1c8cSNeel Natu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18ea7f1c8cSNeel Natu * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 19ea7f1c8cSNeel Natu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20ea7f1c8cSNeel Natu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21ea7f1c8cSNeel Natu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22ea7f1c8cSNeel Natu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23ea7f1c8cSNeel Natu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24ea7f1c8cSNeel Natu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25ea7f1c8cSNeel Natu * SUCH DAMAGE. 26ea7f1c8cSNeel Natu * 27ea7f1c8cSNeel Natu * $FreeBSD$ 28ea7f1c8cSNeel Natu */ 29ea7f1c8cSNeel Natu 30ea7f1c8cSNeel Natu #include <sys/cdefs.h> 31ea7f1c8cSNeel Natu __FBSDID("$FreeBSD$"); 32ea7f1c8cSNeel Natu 33ea7f1c8cSNeel Natu #include <sys/types.h> 34ea7f1c8cSNeel Natu #include <dev/ic/ns16550.h> 35ea7f1c8cSNeel Natu 36ea7f1c8cSNeel Natu #include <stdio.h> 37ea7f1c8cSNeel Natu #include <stdlib.h> 38ea7f1c8cSNeel Natu #include <assert.h> 396380102cSPeter Grehan #include <fcntl.h> 40ea7f1c8cSNeel Natu #include <termios.h> 41ea7f1c8cSNeel Natu #include <unistd.h> 42ea7f1c8cSNeel Natu #include <stdbool.h> 43ea7f1c8cSNeel Natu #include <string.h> 44ea7f1c8cSNeel Natu #include <pthread.h> 45ea7f1c8cSNeel Natu 46ea7f1c8cSNeel Natu #include "mevent.h" 47ea7f1c8cSNeel Natu #include "uart_emul.h" 48ea7f1c8cSNeel Natu 49ea7f1c8cSNeel Natu #define COM1_BASE 0x3F8 50ea7f1c8cSNeel Natu #define COM1_IRQ 4 51ea7f1c8cSNeel Natu #define COM2_BASE 0x2F8 52ea7f1c8cSNeel Natu #define COM2_IRQ 3 53ea7f1c8cSNeel Natu 54ea7f1c8cSNeel Natu #define DEFAULT_RCLK 1843200 55ea7f1c8cSNeel Natu #define DEFAULT_BAUD 9600 56ea7f1c8cSNeel Natu 57ea7f1c8cSNeel Natu #define FCR_RX_MASK 0xC0 58ea7f1c8cSNeel Natu 59ea7f1c8cSNeel Natu #define MCR_OUT1 0x04 60ea7f1c8cSNeel Natu #define MCR_OUT2 0x08 61ea7f1c8cSNeel Natu 62ea7f1c8cSNeel Natu #define MSR_DELTA_MASK 0x0f 63ea7f1c8cSNeel Natu 64ea7f1c8cSNeel Natu #ifndef REG_SCR 65ea7f1c8cSNeel Natu #define REG_SCR com_scr 66ea7f1c8cSNeel Natu #endif 67ea7f1c8cSNeel Natu 68ea7f1c8cSNeel Natu #define FIFOSZ 16 69ea7f1c8cSNeel Natu 70ea7f1c8cSNeel Natu static bool uart_stdio; /* stdio in use for i/o */ 716380102cSPeter Grehan static struct termios tio_stdio_orig; 72ea7f1c8cSNeel Natu 73ea7f1c8cSNeel Natu static struct { 74ea7f1c8cSNeel Natu int baseaddr; 75ea7f1c8cSNeel Natu int irq; 76ea7f1c8cSNeel Natu bool inuse; 77ea7f1c8cSNeel Natu } uart_lres[] = { 78ea7f1c8cSNeel Natu { COM1_BASE, COM1_IRQ, false}, 79ea7f1c8cSNeel Natu { COM2_BASE, COM2_IRQ, false}, 80ea7f1c8cSNeel Natu }; 81ea7f1c8cSNeel Natu 82ea7f1c8cSNeel Natu #define UART_NLDEVS (sizeof(uart_lres) / sizeof(uart_lres[0])) 83ea7f1c8cSNeel Natu 84ea7f1c8cSNeel Natu struct fifo { 85ea7f1c8cSNeel Natu uint8_t buf[FIFOSZ]; 86ea7f1c8cSNeel Natu int rindex; /* index to read from */ 87ea7f1c8cSNeel Natu int windex; /* index to write to */ 88ea7f1c8cSNeel Natu int num; /* number of characters in the fifo */ 89ea7f1c8cSNeel Natu int size; /* size of the fifo */ 90ea7f1c8cSNeel Natu }; 91ea7f1c8cSNeel Natu 926380102cSPeter Grehan struct ttyfd { 936380102cSPeter Grehan bool opened; 946380102cSPeter Grehan int fd; /* tty device file descriptor */ 956380102cSPeter Grehan struct termios tio_orig, tio_new; /* I/O Terminals */ 966380102cSPeter Grehan }; 976380102cSPeter Grehan 98ea7f1c8cSNeel Natu struct uart_softc { 99ea7f1c8cSNeel Natu pthread_mutex_t mtx; /* protects all softc elements */ 100ea7f1c8cSNeel Natu uint8_t data; /* Data register (R/W) */ 101ea7f1c8cSNeel Natu uint8_t ier; /* Interrupt enable register (R/W) */ 102ea7f1c8cSNeel Natu uint8_t lcr; /* Line control register (R/W) */ 103ea7f1c8cSNeel Natu uint8_t mcr; /* Modem control register (R/W) */ 104ea7f1c8cSNeel Natu uint8_t lsr; /* Line status register (R/W) */ 105ea7f1c8cSNeel Natu uint8_t msr; /* Modem status register (R/W) */ 106ea7f1c8cSNeel Natu uint8_t fcr; /* FIFO control register (W) */ 107ea7f1c8cSNeel Natu uint8_t scr; /* Scratch register (R/W) */ 108ea7f1c8cSNeel Natu 109ea7f1c8cSNeel Natu uint8_t dll; /* Baudrate divisor latch LSB */ 110ea7f1c8cSNeel Natu uint8_t dlh; /* Baudrate divisor latch MSB */ 111ea7f1c8cSNeel Natu 112ea7f1c8cSNeel Natu struct fifo rxfifo; 1132bd073e1SNeel Natu struct mevent *mev; 114ea7f1c8cSNeel Natu 1156380102cSPeter Grehan struct ttyfd tty; 116ea7f1c8cSNeel Natu bool thre_int_pending; /* THRE interrupt pending */ 117ea7f1c8cSNeel Natu 118ea7f1c8cSNeel Natu void *arg; 119ea7f1c8cSNeel Natu uart_intr_func_t intr_assert; 120ea7f1c8cSNeel Natu uart_intr_func_t intr_deassert; 121ea7f1c8cSNeel Natu }; 122ea7f1c8cSNeel Natu 123ea7f1c8cSNeel Natu static void uart_drain(int fd, enum ev_type ev, void *arg); 124ea7f1c8cSNeel Natu 125ea7f1c8cSNeel Natu static void 126ea7f1c8cSNeel Natu ttyclose(void) 127ea7f1c8cSNeel Natu { 128ea7f1c8cSNeel Natu 1296380102cSPeter Grehan tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); 130ea7f1c8cSNeel Natu } 131ea7f1c8cSNeel Natu 132ea7f1c8cSNeel Natu static void 1336380102cSPeter Grehan ttyopen(struct ttyfd *tf) 134ea7f1c8cSNeel Natu { 135ea7f1c8cSNeel Natu 1366380102cSPeter Grehan tcgetattr(tf->fd, &tf->tio_orig); 137ea7f1c8cSNeel Natu 1386380102cSPeter Grehan tf->tio_new = tf->tio_orig; 1396380102cSPeter Grehan cfmakeraw(&tf->tio_new); 1406380102cSPeter Grehan tf->tio_new.c_cflag |= CLOCAL; 1416380102cSPeter Grehan tcsetattr(tf->fd, TCSANOW, &tf->tio_new); 142ea7f1c8cSNeel Natu 1436380102cSPeter Grehan if (tf->fd == STDIN_FILENO) { 1446380102cSPeter Grehan tio_stdio_orig = tf->tio_orig; 145ea7f1c8cSNeel Natu atexit(ttyclose); 146ea7f1c8cSNeel Natu } 1476380102cSPeter Grehan } 148ea7f1c8cSNeel Natu 149ea7f1c8cSNeel Natu static int 1506380102cSPeter Grehan ttyread(struct ttyfd *tf) 151ea7f1c8cSNeel Natu { 1522bd073e1SNeel Natu unsigned char rb; 153ea7f1c8cSNeel Natu 1542bd073e1SNeel Natu if (read(tf->fd, &rb, 1) == 1) 1552bd073e1SNeel Natu return (rb); 1562bd073e1SNeel Natu else 157ea7f1c8cSNeel Natu return (-1); 158ea7f1c8cSNeel Natu } 159ea7f1c8cSNeel Natu 160ea7f1c8cSNeel Natu static void 1616380102cSPeter Grehan ttywrite(struct ttyfd *tf, unsigned char wb) 162ea7f1c8cSNeel Natu { 163ea7f1c8cSNeel Natu 1646380102cSPeter Grehan (void)write(tf->fd, &wb, 1); 165ea7f1c8cSNeel Natu } 166ea7f1c8cSNeel Natu 167ea7f1c8cSNeel Natu static void 1682bd073e1SNeel Natu rxfifo_reset(struct uart_softc *sc, int size) 169ea7f1c8cSNeel Natu { 1702bd073e1SNeel Natu char flushbuf[32]; 1712bd073e1SNeel Natu struct fifo *fifo; 1722bd073e1SNeel Natu ssize_t nread; 1732bd073e1SNeel Natu int error; 174ea7f1c8cSNeel Natu 1752bd073e1SNeel Natu fifo = &sc->rxfifo; 176ea7f1c8cSNeel Natu bzero(fifo, sizeof(struct fifo)); 177ea7f1c8cSNeel Natu fifo->size = size; 1782bd073e1SNeel Natu 1792bd073e1SNeel Natu if (sc->tty.opened) { 1802bd073e1SNeel Natu /* 1812bd073e1SNeel Natu * Flush any unread input from the tty buffer. 1822bd073e1SNeel Natu */ 1832bd073e1SNeel Natu while (1) { 1842bd073e1SNeel Natu nread = read(sc->tty.fd, flushbuf, sizeof(flushbuf)); 1852bd073e1SNeel Natu if (nread != sizeof(flushbuf)) 1862bd073e1SNeel Natu break; 1872bd073e1SNeel Natu } 1882bd073e1SNeel Natu 1892bd073e1SNeel Natu /* 1902bd073e1SNeel Natu * Enable mevent to trigger when new characters are available 1912bd073e1SNeel Natu * on the tty fd. 1922bd073e1SNeel Natu */ 1932bd073e1SNeel Natu error = mevent_enable(sc->mev); 1942bd073e1SNeel Natu assert(error == 0); 1952bd073e1SNeel Natu } 196ea7f1c8cSNeel Natu } 197ea7f1c8cSNeel Natu 198ea7f1c8cSNeel Natu static int 1992bd073e1SNeel Natu rxfifo_available(struct uart_softc *sc) 200ea7f1c8cSNeel Natu { 2012bd073e1SNeel Natu struct fifo *fifo; 2022bd073e1SNeel Natu 2032bd073e1SNeel Natu fifo = &sc->rxfifo; 2042bd073e1SNeel Natu return (fifo->num < fifo->size); 2052bd073e1SNeel Natu } 2062bd073e1SNeel Natu 2072bd073e1SNeel Natu static int 2082bd073e1SNeel Natu rxfifo_putchar(struct uart_softc *sc, uint8_t ch) 2092bd073e1SNeel Natu { 2102bd073e1SNeel Natu struct fifo *fifo; 2112bd073e1SNeel Natu int error; 2122bd073e1SNeel Natu 2132bd073e1SNeel Natu fifo = &sc->rxfifo; 214ea7f1c8cSNeel Natu 215ea7f1c8cSNeel Natu if (fifo->num < fifo->size) { 216ea7f1c8cSNeel Natu fifo->buf[fifo->windex] = ch; 217ea7f1c8cSNeel Natu fifo->windex = (fifo->windex + 1) % fifo->size; 218ea7f1c8cSNeel Natu fifo->num++; 2192bd073e1SNeel Natu if (!rxfifo_available(sc)) { 2202bd073e1SNeel Natu if (sc->tty.opened) { 2212bd073e1SNeel Natu /* 2222bd073e1SNeel Natu * Disable mevent callback if the FIFO is full. 2232bd073e1SNeel Natu */ 2242bd073e1SNeel Natu error = mevent_disable(sc->mev); 2252bd073e1SNeel Natu assert(error == 0); 2262bd073e1SNeel Natu } 2272bd073e1SNeel Natu } 228ea7f1c8cSNeel Natu return (0); 229ea7f1c8cSNeel Natu } else 230ea7f1c8cSNeel Natu return (-1); 231ea7f1c8cSNeel Natu } 232ea7f1c8cSNeel Natu 233ea7f1c8cSNeel Natu static int 2342bd073e1SNeel Natu rxfifo_getchar(struct uart_softc *sc) 235ea7f1c8cSNeel Natu { 2362bd073e1SNeel Natu struct fifo *fifo; 2372bd073e1SNeel Natu int c, error, wasfull; 238ea7f1c8cSNeel Natu 2392bd073e1SNeel Natu wasfull = 0; 2402bd073e1SNeel Natu fifo = &sc->rxfifo; 241ea7f1c8cSNeel Natu if (fifo->num > 0) { 2422bd073e1SNeel Natu if (!rxfifo_available(sc)) 2432bd073e1SNeel Natu wasfull = 1; 244ea7f1c8cSNeel Natu c = fifo->buf[fifo->rindex]; 245ea7f1c8cSNeel Natu fifo->rindex = (fifo->rindex + 1) % fifo->size; 246ea7f1c8cSNeel Natu fifo->num--; 2472bd073e1SNeel Natu if (wasfull) { 2482bd073e1SNeel Natu if (sc->tty.opened) { 2492bd073e1SNeel Natu error = mevent_enable(sc->mev); 2502bd073e1SNeel Natu assert(error == 0); 2512bd073e1SNeel Natu } 2522bd073e1SNeel Natu } 253ea7f1c8cSNeel Natu return (c); 254ea7f1c8cSNeel Natu } else 255ea7f1c8cSNeel Natu return (-1); 256ea7f1c8cSNeel Natu } 257ea7f1c8cSNeel Natu 258ea7f1c8cSNeel Natu static int 2592bd073e1SNeel Natu rxfifo_numchars(struct uart_softc *sc) 260ea7f1c8cSNeel Natu { 2612bd073e1SNeel Natu struct fifo *fifo = &sc->rxfifo; 262ea7f1c8cSNeel Natu 263ea7f1c8cSNeel Natu return (fifo->num); 264ea7f1c8cSNeel Natu } 265ea7f1c8cSNeel Natu 266ea7f1c8cSNeel Natu static void 267ea7f1c8cSNeel Natu uart_opentty(struct uart_softc *sc) 268ea7f1c8cSNeel Natu { 269ea7f1c8cSNeel Natu 2706380102cSPeter Grehan ttyopen(&sc->tty); 2712bd073e1SNeel Natu sc->mev = mevent_add(sc->tty.fd, EVF_READ, uart_drain, sc); 2722bd073e1SNeel Natu assert(sc->mev != NULL); 273ea7f1c8cSNeel Natu } 274ea7f1c8cSNeel Natu 275*ccfe4c3fSNeel Natu static uint8_t 276*ccfe4c3fSNeel Natu modem_status(uint8_t mcr) 277*ccfe4c3fSNeel Natu { 278*ccfe4c3fSNeel Natu uint8_t msr; 279*ccfe4c3fSNeel Natu 280*ccfe4c3fSNeel Natu if (mcr & MCR_LOOPBACK) { 281*ccfe4c3fSNeel Natu /* 282*ccfe4c3fSNeel Natu * In the loopback mode certain bits from the MCR are 283*ccfe4c3fSNeel Natu * reflected back into MSR. 284*ccfe4c3fSNeel Natu */ 285*ccfe4c3fSNeel Natu msr = 0; 286*ccfe4c3fSNeel Natu if (mcr & MCR_RTS) 287*ccfe4c3fSNeel Natu msr |= MSR_CTS; 288*ccfe4c3fSNeel Natu if (mcr & MCR_DTR) 289*ccfe4c3fSNeel Natu msr |= MSR_DSR; 290*ccfe4c3fSNeel Natu if (mcr & MCR_OUT1) 291*ccfe4c3fSNeel Natu msr |= MSR_RI; 292*ccfe4c3fSNeel Natu if (mcr & MCR_OUT2) 293*ccfe4c3fSNeel Natu msr |= MSR_DCD; 294*ccfe4c3fSNeel Natu } else { 295*ccfe4c3fSNeel Natu /* 296*ccfe4c3fSNeel Natu * Always assert DCD and DSR so tty open doesn't block 297*ccfe4c3fSNeel Natu * even if CLOCAL is turned off. 298*ccfe4c3fSNeel Natu */ 299*ccfe4c3fSNeel Natu msr = MSR_DCD | MSR_DSR; 300*ccfe4c3fSNeel Natu } 301*ccfe4c3fSNeel Natu assert((msr & MSR_DELTA_MASK) == 0); 302*ccfe4c3fSNeel Natu 303*ccfe4c3fSNeel Natu return (msr); 304*ccfe4c3fSNeel Natu } 305*ccfe4c3fSNeel Natu 306ea7f1c8cSNeel Natu /* 307ea7f1c8cSNeel Natu * The IIR returns a prioritized interrupt reason: 308ea7f1c8cSNeel Natu * - receive data available 309ea7f1c8cSNeel Natu * - transmit holding register empty 310ea7f1c8cSNeel Natu * - modem status change 311ea7f1c8cSNeel Natu * 312ea7f1c8cSNeel Natu * Return an interrupt reason if one is available. 313ea7f1c8cSNeel Natu */ 314ea7f1c8cSNeel Natu static int 315ea7f1c8cSNeel Natu uart_intr_reason(struct uart_softc *sc) 316ea7f1c8cSNeel Natu { 317ea7f1c8cSNeel Natu 318ea7f1c8cSNeel Natu if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0) 319ea7f1c8cSNeel Natu return (IIR_RLS); 3202bd073e1SNeel Natu else if (rxfifo_numchars(sc) > 0 && (sc->ier & IER_ERXRDY) != 0) 321ea7f1c8cSNeel Natu return (IIR_RXTOUT); 322ea7f1c8cSNeel Natu else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0) 323ea7f1c8cSNeel Natu return (IIR_TXRDY); 324ea7f1c8cSNeel Natu else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0) 325ea7f1c8cSNeel Natu return (IIR_MLSC); 326ea7f1c8cSNeel Natu else 327ea7f1c8cSNeel Natu return (IIR_NOPEND); 328ea7f1c8cSNeel Natu } 329ea7f1c8cSNeel Natu 330ea7f1c8cSNeel Natu static void 331ea7f1c8cSNeel Natu uart_reset(struct uart_softc *sc) 332ea7f1c8cSNeel Natu { 333ea7f1c8cSNeel Natu uint16_t divisor; 334ea7f1c8cSNeel Natu 335ea7f1c8cSNeel Natu divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16; 336ea7f1c8cSNeel Natu sc->dll = divisor; 337ea7f1c8cSNeel Natu sc->dlh = divisor >> 16; 338*ccfe4c3fSNeel Natu sc->msr = modem_status(sc->mcr); 339ea7f1c8cSNeel Natu 3402bd073e1SNeel Natu rxfifo_reset(sc, 1); /* no fifo until enabled by software */ 341ea7f1c8cSNeel Natu } 342ea7f1c8cSNeel Natu 343ea7f1c8cSNeel Natu /* 344ea7f1c8cSNeel Natu * Toggle the COM port's intr pin depending on whether or not we have an 345ea7f1c8cSNeel Natu * interrupt condition to report to the processor. 346ea7f1c8cSNeel Natu */ 347ea7f1c8cSNeel Natu static void 348ea7f1c8cSNeel Natu uart_toggle_intr(struct uart_softc *sc) 349ea7f1c8cSNeel Natu { 350ea7f1c8cSNeel Natu uint8_t intr_reason; 351ea7f1c8cSNeel Natu 352ea7f1c8cSNeel Natu intr_reason = uart_intr_reason(sc); 353ea7f1c8cSNeel Natu 354ea7f1c8cSNeel Natu if (intr_reason == IIR_NOPEND) 355ea7f1c8cSNeel Natu (*sc->intr_deassert)(sc->arg); 356ea7f1c8cSNeel Natu else 357ea7f1c8cSNeel Natu (*sc->intr_assert)(sc->arg); 358ea7f1c8cSNeel Natu } 359ea7f1c8cSNeel Natu 360ea7f1c8cSNeel Natu static void 361ea7f1c8cSNeel Natu uart_drain(int fd, enum ev_type ev, void *arg) 362ea7f1c8cSNeel Natu { 363ea7f1c8cSNeel Natu struct uart_softc *sc; 364ea7f1c8cSNeel Natu int ch; 365ea7f1c8cSNeel Natu 366ea7f1c8cSNeel Natu sc = arg; 367ea7f1c8cSNeel Natu 3686380102cSPeter Grehan assert(fd == sc->tty.fd); 369ea7f1c8cSNeel Natu assert(ev == EVF_READ); 370ea7f1c8cSNeel Natu 371ea7f1c8cSNeel Natu /* 372ea7f1c8cSNeel Natu * This routine is called in the context of the mevent thread 373ea7f1c8cSNeel Natu * to take out the softc lock to protect against concurrent 374ea7f1c8cSNeel Natu * access from a vCPU i/o exit 375ea7f1c8cSNeel Natu */ 376ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 377ea7f1c8cSNeel Natu 378ea7f1c8cSNeel Natu if ((sc->mcr & MCR_LOOPBACK) != 0) { 3796380102cSPeter Grehan (void) ttyread(&sc->tty); 380ea7f1c8cSNeel Natu } else { 3812bd073e1SNeel Natu while (rxfifo_available(sc) && 3826380102cSPeter Grehan ((ch = ttyread(&sc->tty)) != -1)) { 3832bd073e1SNeel Natu rxfifo_putchar(sc, ch); 384ea7f1c8cSNeel Natu } 385ea7f1c8cSNeel Natu uart_toggle_intr(sc); 386ea7f1c8cSNeel Natu } 387ea7f1c8cSNeel Natu 388ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 389ea7f1c8cSNeel Natu } 390ea7f1c8cSNeel Natu 391ea7f1c8cSNeel Natu void 392ea7f1c8cSNeel Natu uart_write(struct uart_softc *sc, int offset, uint8_t value) 393ea7f1c8cSNeel Natu { 394ea7f1c8cSNeel Natu int fifosz; 395ea7f1c8cSNeel Natu uint8_t msr; 396ea7f1c8cSNeel Natu 397ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 398ea7f1c8cSNeel Natu 399ea7f1c8cSNeel Natu /* 400ea7f1c8cSNeel Natu * Take care of the special case DLAB accesses first 401ea7f1c8cSNeel Natu */ 402ea7f1c8cSNeel Natu if ((sc->lcr & LCR_DLAB) != 0) { 403ea7f1c8cSNeel Natu if (offset == REG_DLL) { 404ea7f1c8cSNeel Natu sc->dll = value; 405ea7f1c8cSNeel Natu goto done; 406ea7f1c8cSNeel Natu } 407ea7f1c8cSNeel Natu 408ea7f1c8cSNeel Natu if (offset == REG_DLH) { 409ea7f1c8cSNeel Natu sc->dlh = value; 410ea7f1c8cSNeel Natu goto done; 411ea7f1c8cSNeel Natu } 412ea7f1c8cSNeel Natu } 413ea7f1c8cSNeel Natu 414ea7f1c8cSNeel Natu switch (offset) { 415ea7f1c8cSNeel Natu case REG_DATA: 416ea7f1c8cSNeel Natu if (sc->mcr & MCR_LOOPBACK) { 4172bd073e1SNeel Natu if (rxfifo_putchar(sc, value) != 0) 418ea7f1c8cSNeel Natu sc->lsr |= LSR_OE; 4196380102cSPeter Grehan } else if (sc->tty.opened) { 4206380102cSPeter Grehan ttywrite(&sc->tty, value); 421ea7f1c8cSNeel Natu } /* else drop on floor */ 422ea7f1c8cSNeel Natu sc->thre_int_pending = true; 423ea7f1c8cSNeel Natu break; 424ea7f1c8cSNeel Natu case REG_IER: 425ea7f1c8cSNeel Natu /* 426ea7f1c8cSNeel Natu * Apply mask so that bits 4-7 are 0 427ea7f1c8cSNeel Natu * Also enables bits 0-3 only if they're 1 428ea7f1c8cSNeel Natu */ 429ea7f1c8cSNeel Natu sc->ier = value & 0x0F; 430ea7f1c8cSNeel Natu break; 431ea7f1c8cSNeel Natu case REG_FCR: 432ea7f1c8cSNeel Natu /* 433ea7f1c8cSNeel Natu * When moving from FIFO and 16450 mode and vice versa, 434ea7f1c8cSNeel Natu * the FIFO contents are reset. 435ea7f1c8cSNeel Natu */ 436ea7f1c8cSNeel Natu if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) { 437ea7f1c8cSNeel Natu fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1; 4382bd073e1SNeel Natu rxfifo_reset(sc, fifosz); 439ea7f1c8cSNeel Natu } 440ea7f1c8cSNeel Natu 441ea7f1c8cSNeel Natu /* 442ea7f1c8cSNeel Natu * The FCR_ENABLE bit must be '1' for the programming 443ea7f1c8cSNeel Natu * of other FCR bits to be effective. 444ea7f1c8cSNeel Natu */ 445ea7f1c8cSNeel Natu if ((value & FCR_ENABLE) == 0) { 446ea7f1c8cSNeel Natu sc->fcr = 0; 447ea7f1c8cSNeel Natu } else { 448ea7f1c8cSNeel Natu if ((value & FCR_RCV_RST) != 0) 4492bd073e1SNeel Natu rxfifo_reset(sc, FIFOSZ); 450ea7f1c8cSNeel Natu 451ea7f1c8cSNeel Natu sc->fcr = value & 452ea7f1c8cSNeel Natu (FCR_ENABLE | FCR_DMA | FCR_RX_MASK); 453ea7f1c8cSNeel Natu } 454ea7f1c8cSNeel Natu break; 455ea7f1c8cSNeel Natu case REG_LCR: 456ea7f1c8cSNeel Natu sc->lcr = value; 457ea7f1c8cSNeel Natu break; 458ea7f1c8cSNeel Natu case REG_MCR: 459ea7f1c8cSNeel Natu /* Apply mask so that bits 5-7 are 0 */ 460ea7f1c8cSNeel Natu sc->mcr = value & 0x1F; 461*ccfe4c3fSNeel Natu msr = modem_status(sc->mcr); 462ea7f1c8cSNeel Natu 463ea7f1c8cSNeel Natu /* 464ea7f1c8cSNeel Natu * Detect if there has been any change between the 465ea7f1c8cSNeel Natu * previous and the new value of MSR. If there is 466ea7f1c8cSNeel Natu * then assert the appropriate MSR delta bit. 467ea7f1c8cSNeel Natu */ 468ea7f1c8cSNeel Natu if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS)) 469ea7f1c8cSNeel Natu sc->msr |= MSR_DCTS; 470ea7f1c8cSNeel Natu if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR)) 471ea7f1c8cSNeel Natu sc->msr |= MSR_DDSR; 472ea7f1c8cSNeel Natu if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD)) 473ea7f1c8cSNeel Natu sc->msr |= MSR_DDCD; 474ea7f1c8cSNeel Natu if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0) 475ea7f1c8cSNeel Natu sc->msr |= MSR_TERI; 476ea7f1c8cSNeel Natu 477ea7f1c8cSNeel Natu /* 478ea7f1c8cSNeel Natu * Update the value of MSR while retaining the delta 479ea7f1c8cSNeel Natu * bits. 480ea7f1c8cSNeel Natu */ 481ea7f1c8cSNeel Natu sc->msr &= MSR_DELTA_MASK; 482ea7f1c8cSNeel Natu sc->msr |= msr; 483ea7f1c8cSNeel Natu break; 484ea7f1c8cSNeel Natu case REG_LSR: 485ea7f1c8cSNeel Natu /* 486ea7f1c8cSNeel Natu * Line status register is not meant to be written to 487ea7f1c8cSNeel Natu * during normal operation. 488ea7f1c8cSNeel Natu */ 489ea7f1c8cSNeel Natu break; 490ea7f1c8cSNeel Natu case REG_MSR: 491ea7f1c8cSNeel Natu /* 492ea7f1c8cSNeel Natu * As far as I can tell MSR is a read-only register. 493ea7f1c8cSNeel Natu */ 494ea7f1c8cSNeel Natu break; 495ea7f1c8cSNeel Natu case REG_SCR: 496ea7f1c8cSNeel Natu sc->scr = value; 497ea7f1c8cSNeel Natu break; 498ea7f1c8cSNeel Natu default: 499ea7f1c8cSNeel Natu break; 500ea7f1c8cSNeel Natu } 501ea7f1c8cSNeel Natu 502ea7f1c8cSNeel Natu done: 503ea7f1c8cSNeel Natu uart_toggle_intr(sc); 504ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 505ea7f1c8cSNeel Natu } 506ea7f1c8cSNeel Natu 507ea7f1c8cSNeel Natu uint8_t 508ea7f1c8cSNeel Natu uart_read(struct uart_softc *sc, int offset) 509ea7f1c8cSNeel Natu { 510ea7f1c8cSNeel Natu uint8_t iir, intr_reason, reg; 511ea7f1c8cSNeel Natu 512ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 513ea7f1c8cSNeel Natu 514ea7f1c8cSNeel Natu /* 515ea7f1c8cSNeel Natu * Take care of the special case DLAB accesses first 516ea7f1c8cSNeel Natu */ 517ea7f1c8cSNeel Natu if ((sc->lcr & LCR_DLAB) != 0) { 518ea7f1c8cSNeel Natu if (offset == REG_DLL) { 519ea7f1c8cSNeel Natu reg = sc->dll; 520ea7f1c8cSNeel Natu goto done; 521ea7f1c8cSNeel Natu } 522ea7f1c8cSNeel Natu 523ea7f1c8cSNeel Natu if (offset == REG_DLH) { 524ea7f1c8cSNeel Natu reg = sc->dlh; 525ea7f1c8cSNeel Natu goto done; 526ea7f1c8cSNeel Natu } 527ea7f1c8cSNeel Natu } 528ea7f1c8cSNeel Natu 529ea7f1c8cSNeel Natu switch (offset) { 530ea7f1c8cSNeel Natu case REG_DATA: 5312bd073e1SNeel Natu reg = rxfifo_getchar(sc); 532ea7f1c8cSNeel Natu break; 533ea7f1c8cSNeel Natu case REG_IER: 534ea7f1c8cSNeel Natu reg = sc->ier; 535ea7f1c8cSNeel Natu break; 536ea7f1c8cSNeel Natu case REG_IIR: 537ea7f1c8cSNeel Natu iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0; 538ea7f1c8cSNeel Natu 539ea7f1c8cSNeel Natu intr_reason = uart_intr_reason(sc); 540ea7f1c8cSNeel Natu 541ea7f1c8cSNeel Natu /* 542ea7f1c8cSNeel Natu * Deal with side effects of reading the IIR register 543ea7f1c8cSNeel Natu */ 544ea7f1c8cSNeel Natu if (intr_reason == IIR_TXRDY) 545ea7f1c8cSNeel Natu sc->thre_int_pending = false; 546ea7f1c8cSNeel Natu 547ea7f1c8cSNeel Natu iir |= intr_reason; 548ea7f1c8cSNeel Natu 549ea7f1c8cSNeel Natu reg = iir; 550ea7f1c8cSNeel Natu break; 551ea7f1c8cSNeel Natu case REG_LCR: 552ea7f1c8cSNeel Natu reg = sc->lcr; 553ea7f1c8cSNeel Natu break; 554ea7f1c8cSNeel Natu case REG_MCR: 555ea7f1c8cSNeel Natu reg = sc->mcr; 556ea7f1c8cSNeel Natu break; 557ea7f1c8cSNeel Natu case REG_LSR: 558ea7f1c8cSNeel Natu /* Transmitter is always ready for more data */ 559ea7f1c8cSNeel Natu sc->lsr |= LSR_TEMT | LSR_THRE; 560ea7f1c8cSNeel Natu 561ea7f1c8cSNeel Natu /* Check for new receive data */ 5622bd073e1SNeel Natu if (rxfifo_numchars(sc) > 0) 563ea7f1c8cSNeel Natu sc->lsr |= LSR_RXRDY; 564ea7f1c8cSNeel Natu else 565ea7f1c8cSNeel Natu sc->lsr &= ~LSR_RXRDY; 566ea7f1c8cSNeel Natu 567ea7f1c8cSNeel Natu reg = sc->lsr; 568ea7f1c8cSNeel Natu 569ea7f1c8cSNeel Natu /* The LSR_OE bit is cleared on LSR read */ 570ea7f1c8cSNeel Natu sc->lsr &= ~LSR_OE; 571ea7f1c8cSNeel Natu break; 572ea7f1c8cSNeel Natu case REG_MSR: 573ea7f1c8cSNeel Natu /* 574ea7f1c8cSNeel Natu * MSR delta bits are cleared on read 575ea7f1c8cSNeel Natu */ 576ea7f1c8cSNeel Natu reg = sc->msr; 577ea7f1c8cSNeel Natu sc->msr &= ~MSR_DELTA_MASK; 578ea7f1c8cSNeel Natu break; 579ea7f1c8cSNeel Natu case REG_SCR: 580ea7f1c8cSNeel Natu reg = sc->scr; 581ea7f1c8cSNeel Natu break; 582ea7f1c8cSNeel Natu default: 583ea7f1c8cSNeel Natu reg = 0xFF; 584ea7f1c8cSNeel Natu break; 585ea7f1c8cSNeel Natu } 586ea7f1c8cSNeel Natu 587ea7f1c8cSNeel Natu done: 588ea7f1c8cSNeel Natu uart_toggle_intr(sc); 589ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 590ea7f1c8cSNeel Natu 591ea7f1c8cSNeel Natu return (reg); 592ea7f1c8cSNeel Natu } 593ea7f1c8cSNeel Natu 594ea7f1c8cSNeel Natu int 595ea7f1c8cSNeel Natu uart_legacy_alloc(int which, int *baseaddr, int *irq) 596ea7f1c8cSNeel Natu { 597ea7f1c8cSNeel Natu 598ea7f1c8cSNeel Natu if (which < 0 || which >= UART_NLDEVS || uart_lres[which].inuse) 599ea7f1c8cSNeel Natu return (-1); 600ea7f1c8cSNeel Natu 601ea7f1c8cSNeel Natu uart_lres[which].inuse = true; 602ea7f1c8cSNeel Natu *baseaddr = uart_lres[which].baseaddr; 603ea7f1c8cSNeel Natu *irq = uart_lres[which].irq; 604ea7f1c8cSNeel Natu 605ea7f1c8cSNeel Natu return (0); 606ea7f1c8cSNeel Natu } 607ea7f1c8cSNeel Natu 608ea7f1c8cSNeel Natu struct uart_softc * 609ea7f1c8cSNeel Natu uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert, 610ea7f1c8cSNeel Natu void *arg) 611ea7f1c8cSNeel Natu { 612ea7f1c8cSNeel Natu struct uart_softc *sc; 613ea7f1c8cSNeel Natu 614994f858aSXin LI sc = calloc(1, sizeof(struct uart_softc)); 615ea7f1c8cSNeel Natu 616ea7f1c8cSNeel Natu sc->arg = arg; 617ea7f1c8cSNeel Natu sc->intr_assert = intr_assert; 618ea7f1c8cSNeel Natu sc->intr_deassert = intr_deassert; 619ea7f1c8cSNeel Natu 620ea7f1c8cSNeel Natu pthread_mutex_init(&sc->mtx, NULL); 621ea7f1c8cSNeel Natu 622ea7f1c8cSNeel Natu uart_reset(sc); 623ea7f1c8cSNeel Natu 624ea7f1c8cSNeel Natu return (sc); 625ea7f1c8cSNeel Natu } 626ea7f1c8cSNeel Natu 6276380102cSPeter Grehan static int 6286380102cSPeter Grehan uart_tty_backend(struct uart_softc *sc, const char *opts) 6296380102cSPeter Grehan { 6306380102cSPeter Grehan int fd; 6316380102cSPeter Grehan int retval; 6326380102cSPeter Grehan 6336380102cSPeter Grehan retval = -1; 6346380102cSPeter Grehan 635f4959d35SPeter Grehan fd = open(opts, O_RDWR | O_NONBLOCK); 6366380102cSPeter Grehan if (fd > 0 && isatty(fd)) { 6376380102cSPeter Grehan sc->tty.fd = fd; 6386380102cSPeter Grehan sc->tty.opened = true; 6396380102cSPeter Grehan retval = 0; 6406380102cSPeter Grehan } 6416380102cSPeter Grehan 6426380102cSPeter Grehan return (retval); 6436380102cSPeter Grehan } 6446380102cSPeter Grehan 645ea7f1c8cSNeel Natu int 646ea7f1c8cSNeel Natu uart_set_backend(struct uart_softc *sc, const char *opts) 647ea7f1c8cSNeel Natu { 6486380102cSPeter Grehan int retval; 6496380102cSPeter Grehan 6506380102cSPeter Grehan retval = -1; 6516380102cSPeter Grehan 652ea7f1c8cSNeel Natu if (opts == NULL) 653ea7f1c8cSNeel Natu return (0); 654ea7f1c8cSNeel Natu 6556380102cSPeter Grehan if (strcmp("stdio", opts) == 0) { 6566380102cSPeter Grehan if (!uart_stdio) { 6576380102cSPeter Grehan sc->tty.fd = STDIN_FILENO; 6586380102cSPeter Grehan sc->tty.opened = true; 659ea7f1c8cSNeel Natu uart_stdio = true; 6606380102cSPeter Grehan retval = 0; 6616380102cSPeter Grehan } 6626380102cSPeter Grehan } else if (uart_tty_backend(sc, opts) == 0) { 6636380102cSPeter Grehan retval = 0; 6646380102cSPeter Grehan } 6656380102cSPeter Grehan 6662bd073e1SNeel Natu /* Make the backend file descriptor non-blocking */ 6672bd073e1SNeel Natu if (retval == 0) 6682bd073e1SNeel Natu retval = fcntl(sc->tty.fd, F_SETFL, O_NONBLOCK); 6692bd073e1SNeel Natu 6706380102cSPeter Grehan if (retval == 0) 6716380102cSPeter Grehan uart_opentty(sc); 6726380102cSPeter Grehan 6736380102cSPeter Grehan return (retval); 674ea7f1c8cSNeel Natu } 675