1*ea7f1c8cSNeel Natu /*- 2*ea7f1c8cSNeel Natu * Copyright (c) 2012 NetApp, Inc. 3*ea7f1c8cSNeel Natu * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 4*ea7f1c8cSNeel Natu * All rights reserved. 5*ea7f1c8cSNeel Natu * 6*ea7f1c8cSNeel Natu * Redistribution and use in source and binary forms, with or without 7*ea7f1c8cSNeel Natu * modification, are permitted provided that the following conditions 8*ea7f1c8cSNeel Natu * are met: 9*ea7f1c8cSNeel Natu * 1. Redistributions of source code must retain the above copyright 10*ea7f1c8cSNeel Natu * notice, this list of conditions and the following disclaimer. 11*ea7f1c8cSNeel Natu * 2. Redistributions in binary form must reproduce the above copyright 12*ea7f1c8cSNeel Natu * notice, this list of conditions and the following disclaimer in the 13*ea7f1c8cSNeel Natu * documentation and/or other materials provided with the distribution. 14*ea7f1c8cSNeel Natu * 15*ea7f1c8cSNeel Natu * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 16*ea7f1c8cSNeel Natu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17*ea7f1c8cSNeel Natu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*ea7f1c8cSNeel Natu * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 19*ea7f1c8cSNeel Natu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*ea7f1c8cSNeel Natu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21*ea7f1c8cSNeel Natu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*ea7f1c8cSNeel Natu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*ea7f1c8cSNeel Natu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*ea7f1c8cSNeel Natu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*ea7f1c8cSNeel Natu * SUCH DAMAGE. 26*ea7f1c8cSNeel Natu * 27*ea7f1c8cSNeel Natu * $FreeBSD$ 28*ea7f1c8cSNeel Natu */ 29*ea7f1c8cSNeel Natu 30*ea7f1c8cSNeel Natu #include <sys/cdefs.h> 31*ea7f1c8cSNeel Natu __FBSDID("$FreeBSD$"); 32*ea7f1c8cSNeel Natu 33*ea7f1c8cSNeel Natu #include <sys/types.h> 34*ea7f1c8cSNeel Natu #include <dev/ic/ns16550.h> 35*ea7f1c8cSNeel Natu 36*ea7f1c8cSNeel Natu #include <stdio.h> 37*ea7f1c8cSNeel Natu #include <stdlib.h> 38*ea7f1c8cSNeel Natu #include <assert.h> 39*ea7f1c8cSNeel Natu #include <termios.h> 40*ea7f1c8cSNeel Natu #include <unistd.h> 41*ea7f1c8cSNeel Natu #include <stdbool.h> 42*ea7f1c8cSNeel Natu #include <string.h> 43*ea7f1c8cSNeel Natu #include <pthread.h> 44*ea7f1c8cSNeel Natu 45*ea7f1c8cSNeel Natu #include "mevent.h" 46*ea7f1c8cSNeel Natu #include "uart_emul.h" 47*ea7f1c8cSNeel Natu 48*ea7f1c8cSNeel Natu #define COM1_BASE 0x3F8 49*ea7f1c8cSNeel Natu #define COM1_IRQ 4 50*ea7f1c8cSNeel Natu #define COM2_BASE 0x2F8 51*ea7f1c8cSNeel Natu #define COM2_IRQ 3 52*ea7f1c8cSNeel Natu 53*ea7f1c8cSNeel Natu #define DEFAULT_RCLK 1843200 54*ea7f1c8cSNeel Natu #define DEFAULT_BAUD 9600 55*ea7f1c8cSNeel Natu 56*ea7f1c8cSNeel Natu #define FCR_RX_MASK 0xC0 57*ea7f1c8cSNeel Natu 58*ea7f1c8cSNeel Natu #define MCR_OUT1 0x04 59*ea7f1c8cSNeel Natu #define MCR_OUT2 0x08 60*ea7f1c8cSNeel Natu 61*ea7f1c8cSNeel Natu #define MSR_DELTA_MASK 0x0f 62*ea7f1c8cSNeel Natu 63*ea7f1c8cSNeel Natu #ifndef REG_SCR 64*ea7f1c8cSNeel Natu #define REG_SCR com_scr 65*ea7f1c8cSNeel Natu #endif 66*ea7f1c8cSNeel Natu 67*ea7f1c8cSNeel Natu #define FIFOSZ 16 68*ea7f1c8cSNeel Natu 69*ea7f1c8cSNeel Natu static bool uart_stdio; /* stdio in use for i/o */ 70*ea7f1c8cSNeel Natu 71*ea7f1c8cSNeel Natu static struct { 72*ea7f1c8cSNeel Natu int baseaddr; 73*ea7f1c8cSNeel Natu int irq; 74*ea7f1c8cSNeel Natu bool inuse; 75*ea7f1c8cSNeel Natu } uart_lres[] = { 76*ea7f1c8cSNeel Natu { COM1_BASE, COM1_IRQ, false}, 77*ea7f1c8cSNeel Natu { COM2_BASE, COM2_IRQ, false}, 78*ea7f1c8cSNeel Natu }; 79*ea7f1c8cSNeel Natu 80*ea7f1c8cSNeel Natu #define UART_NLDEVS (sizeof(uart_lres) / sizeof(uart_lres[0])) 81*ea7f1c8cSNeel Natu 82*ea7f1c8cSNeel Natu struct fifo { 83*ea7f1c8cSNeel Natu uint8_t buf[FIFOSZ]; 84*ea7f1c8cSNeel Natu int rindex; /* index to read from */ 85*ea7f1c8cSNeel Natu int windex; /* index to write to */ 86*ea7f1c8cSNeel Natu int num; /* number of characters in the fifo */ 87*ea7f1c8cSNeel Natu int size; /* size of the fifo */ 88*ea7f1c8cSNeel Natu }; 89*ea7f1c8cSNeel Natu 90*ea7f1c8cSNeel Natu struct uart_softc { 91*ea7f1c8cSNeel Natu pthread_mutex_t mtx; /* protects all softc elements */ 92*ea7f1c8cSNeel Natu uint8_t data; /* Data register (R/W) */ 93*ea7f1c8cSNeel Natu uint8_t ier; /* Interrupt enable register (R/W) */ 94*ea7f1c8cSNeel Natu uint8_t lcr; /* Line control register (R/W) */ 95*ea7f1c8cSNeel Natu uint8_t mcr; /* Modem control register (R/W) */ 96*ea7f1c8cSNeel Natu uint8_t lsr; /* Line status register (R/W) */ 97*ea7f1c8cSNeel Natu uint8_t msr; /* Modem status register (R/W) */ 98*ea7f1c8cSNeel Natu uint8_t fcr; /* FIFO control register (W) */ 99*ea7f1c8cSNeel Natu uint8_t scr; /* Scratch register (R/W) */ 100*ea7f1c8cSNeel Natu 101*ea7f1c8cSNeel Natu uint8_t dll; /* Baudrate divisor latch LSB */ 102*ea7f1c8cSNeel Natu uint8_t dlh; /* Baudrate divisor latch MSB */ 103*ea7f1c8cSNeel Natu 104*ea7f1c8cSNeel Natu struct fifo rxfifo; 105*ea7f1c8cSNeel Natu 106*ea7f1c8cSNeel Natu bool opened; 107*ea7f1c8cSNeel Natu bool stdio; 108*ea7f1c8cSNeel Natu bool thre_int_pending; /* THRE interrupt pending */ 109*ea7f1c8cSNeel Natu 110*ea7f1c8cSNeel Natu void *arg; 111*ea7f1c8cSNeel Natu uart_intr_func_t intr_assert; 112*ea7f1c8cSNeel Natu uart_intr_func_t intr_deassert; 113*ea7f1c8cSNeel Natu }; 114*ea7f1c8cSNeel Natu 115*ea7f1c8cSNeel Natu static void uart_drain(int fd, enum ev_type ev, void *arg); 116*ea7f1c8cSNeel Natu 117*ea7f1c8cSNeel Natu static struct termios tio_orig, tio_new; /* I/O Terminals */ 118*ea7f1c8cSNeel Natu 119*ea7f1c8cSNeel Natu static void 120*ea7f1c8cSNeel Natu ttyclose(void) 121*ea7f1c8cSNeel Natu { 122*ea7f1c8cSNeel Natu 123*ea7f1c8cSNeel Natu tcsetattr(STDIN_FILENO, TCSANOW, &tio_orig); 124*ea7f1c8cSNeel Natu } 125*ea7f1c8cSNeel Natu 126*ea7f1c8cSNeel Natu static void 127*ea7f1c8cSNeel Natu ttyopen(void) 128*ea7f1c8cSNeel Natu { 129*ea7f1c8cSNeel Natu 130*ea7f1c8cSNeel Natu tcgetattr(STDIN_FILENO, &tio_orig); 131*ea7f1c8cSNeel Natu 132*ea7f1c8cSNeel Natu cfmakeraw(&tio_new); 133*ea7f1c8cSNeel Natu tcsetattr(STDIN_FILENO, TCSANOW, &tio_new); 134*ea7f1c8cSNeel Natu 135*ea7f1c8cSNeel Natu atexit(ttyclose); 136*ea7f1c8cSNeel Natu } 137*ea7f1c8cSNeel Natu 138*ea7f1c8cSNeel Natu static bool 139*ea7f1c8cSNeel Natu tty_char_available(void) 140*ea7f1c8cSNeel Natu { 141*ea7f1c8cSNeel Natu fd_set rfds; 142*ea7f1c8cSNeel Natu struct timeval tv; 143*ea7f1c8cSNeel Natu 144*ea7f1c8cSNeel Natu FD_ZERO(&rfds); 145*ea7f1c8cSNeel Natu FD_SET(STDIN_FILENO, &rfds); 146*ea7f1c8cSNeel Natu tv.tv_sec = 0; 147*ea7f1c8cSNeel Natu tv.tv_usec = 0; 148*ea7f1c8cSNeel Natu if (select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv) > 0 ) { 149*ea7f1c8cSNeel Natu return (true); 150*ea7f1c8cSNeel Natu } else { 151*ea7f1c8cSNeel Natu return (false); 152*ea7f1c8cSNeel Natu } 153*ea7f1c8cSNeel Natu } 154*ea7f1c8cSNeel Natu 155*ea7f1c8cSNeel Natu static int 156*ea7f1c8cSNeel Natu ttyread(void) 157*ea7f1c8cSNeel Natu { 158*ea7f1c8cSNeel Natu char rb; 159*ea7f1c8cSNeel Natu 160*ea7f1c8cSNeel Natu if (tty_char_available()) { 161*ea7f1c8cSNeel Natu read(STDIN_FILENO, &rb, 1); 162*ea7f1c8cSNeel Natu return (rb & 0xff); 163*ea7f1c8cSNeel Natu } else { 164*ea7f1c8cSNeel Natu return (-1); 165*ea7f1c8cSNeel Natu } 166*ea7f1c8cSNeel Natu } 167*ea7f1c8cSNeel Natu 168*ea7f1c8cSNeel Natu static void 169*ea7f1c8cSNeel Natu ttywrite(unsigned char wb) 170*ea7f1c8cSNeel Natu { 171*ea7f1c8cSNeel Natu 172*ea7f1c8cSNeel Natu (void)write(STDIN_FILENO, &wb, 1); 173*ea7f1c8cSNeel Natu } 174*ea7f1c8cSNeel Natu 175*ea7f1c8cSNeel Natu static void 176*ea7f1c8cSNeel Natu fifo_reset(struct fifo *fifo, int size) 177*ea7f1c8cSNeel Natu { 178*ea7f1c8cSNeel Natu 179*ea7f1c8cSNeel Natu bzero(fifo, sizeof(struct fifo)); 180*ea7f1c8cSNeel Natu fifo->size = size; 181*ea7f1c8cSNeel Natu } 182*ea7f1c8cSNeel Natu 183*ea7f1c8cSNeel Natu static int 184*ea7f1c8cSNeel Natu fifo_putchar(struct fifo *fifo, uint8_t ch) 185*ea7f1c8cSNeel Natu { 186*ea7f1c8cSNeel Natu 187*ea7f1c8cSNeel Natu if (fifo->num < fifo->size) { 188*ea7f1c8cSNeel Natu fifo->buf[fifo->windex] = ch; 189*ea7f1c8cSNeel Natu fifo->windex = (fifo->windex + 1) % fifo->size; 190*ea7f1c8cSNeel Natu fifo->num++; 191*ea7f1c8cSNeel Natu return (0); 192*ea7f1c8cSNeel Natu } else 193*ea7f1c8cSNeel Natu return (-1); 194*ea7f1c8cSNeel Natu } 195*ea7f1c8cSNeel Natu 196*ea7f1c8cSNeel Natu static int 197*ea7f1c8cSNeel Natu fifo_getchar(struct fifo *fifo) 198*ea7f1c8cSNeel Natu { 199*ea7f1c8cSNeel Natu int c; 200*ea7f1c8cSNeel Natu 201*ea7f1c8cSNeel Natu if (fifo->num > 0) { 202*ea7f1c8cSNeel Natu c = fifo->buf[fifo->rindex]; 203*ea7f1c8cSNeel Natu fifo->rindex = (fifo->rindex + 1) % fifo->size; 204*ea7f1c8cSNeel Natu fifo->num--; 205*ea7f1c8cSNeel Natu return (c); 206*ea7f1c8cSNeel Natu } else 207*ea7f1c8cSNeel Natu return (-1); 208*ea7f1c8cSNeel Natu } 209*ea7f1c8cSNeel Natu 210*ea7f1c8cSNeel Natu static int 211*ea7f1c8cSNeel Natu fifo_numchars(struct fifo *fifo) 212*ea7f1c8cSNeel Natu { 213*ea7f1c8cSNeel Natu 214*ea7f1c8cSNeel Natu return (fifo->num); 215*ea7f1c8cSNeel Natu } 216*ea7f1c8cSNeel Natu 217*ea7f1c8cSNeel Natu static int 218*ea7f1c8cSNeel Natu fifo_available(struct fifo *fifo) 219*ea7f1c8cSNeel Natu { 220*ea7f1c8cSNeel Natu 221*ea7f1c8cSNeel Natu return (fifo->num < fifo->size); 222*ea7f1c8cSNeel Natu } 223*ea7f1c8cSNeel Natu 224*ea7f1c8cSNeel Natu static void 225*ea7f1c8cSNeel Natu uart_opentty(struct uart_softc *sc) 226*ea7f1c8cSNeel Natu { 227*ea7f1c8cSNeel Natu struct mevent *mev; 228*ea7f1c8cSNeel Natu 229*ea7f1c8cSNeel Natu assert(!sc->opened && sc->stdio); 230*ea7f1c8cSNeel Natu 231*ea7f1c8cSNeel Natu ttyopen(); 232*ea7f1c8cSNeel Natu mev = mevent_add(STDIN_FILENO, EVF_READ, uart_drain, sc); 233*ea7f1c8cSNeel Natu assert(mev); 234*ea7f1c8cSNeel Natu } 235*ea7f1c8cSNeel Natu 236*ea7f1c8cSNeel Natu /* 237*ea7f1c8cSNeel Natu * The IIR returns a prioritized interrupt reason: 238*ea7f1c8cSNeel Natu * - receive data available 239*ea7f1c8cSNeel Natu * - transmit holding register empty 240*ea7f1c8cSNeel Natu * - modem status change 241*ea7f1c8cSNeel Natu * 242*ea7f1c8cSNeel Natu * Return an interrupt reason if one is available. 243*ea7f1c8cSNeel Natu */ 244*ea7f1c8cSNeel Natu static int 245*ea7f1c8cSNeel Natu uart_intr_reason(struct uart_softc *sc) 246*ea7f1c8cSNeel Natu { 247*ea7f1c8cSNeel Natu 248*ea7f1c8cSNeel Natu if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0) 249*ea7f1c8cSNeel Natu return (IIR_RLS); 250*ea7f1c8cSNeel Natu else if (fifo_numchars(&sc->rxfifo) > 0 && (sc->ier & IER_ERXRDY) != 0) 251*ea7f1c8cSNeel Natu return (IIR_RXTOUT); 252*ea7f1c8cSNeel Natu else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0) 253*ea7f1c8cSNeel Natu return (IIR_TXRDY); 254*ea7f1c8cSNeel Natu else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0) 255*ea7f1c8cSNeel Natu return (IIR_MLSC); 256*ea7f1c8cSNeel Natu else 257*ea7f1c8cSNeel Natu return (IIR_NOPEND); 258*ea7f1c8cSNeel Natu } 259*ea7f1c8cSNeel Natu 260*ea7f1c8cSNeel Natu static void 261*ea7f1c8cSNeel Natu uart_reset(struct uart_softc *sc) 262*ea7f1c8cSNeel Natu { 263*ea7f1c8cSNeel Natu uint16_t divisor; 264*ea7f1c8cSNeel Natu 265*ea7f1c8cSNeel Natu divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16; 266*ea7f1c8cSNeel Natu sc->dll = divisor; 267*ea7f1c8cSNeel Natu sc->dlh = divisor >> 16; 268*ea7f1c8cSNeel Natu 269*ea7f1c8cSNeel Natu fifo_reset(&sc->rxfifo, 1); /* no fifo until enabled by software */ 270*ea7f1c8cSNeel Natu } 271*ea7f1c8cSNeel Natu 272*ea7f1c8cSNeel Natu /* 273*ea7f1c8cSNeel Natu * Toggle the COM port's intr pin depending on whether or not we have an 274*ea7f1c8cSNeel Natu * interrupt condition to report to the processor. 275*ea7f1c8cSNeel Natu */ 276*ea7f1c8cSNeel Natu static void 277*ea7f1c8cSNeel Natu uart_toggle_intr(struct uart_softc *sc) 278*ea7f1c8cSNeel Natu { 279*ea7f1c8cSNeel Natu uint8_t intr_reason; 280*ea7f1c8cSNeel Natu 281*ea7f1c8cSNeel Natu intr_reason = uart_intr_reason(sc); 282*ea7f1c8cSNeel Natu 283*ea7f1c8cSNeel Natu if (intr_reason == IIR_NOPEND) 284*ea7f1c8cSNeel Natu (*sc->intr_deassert)(sc->arg); 285*ea7f1c8cSNeel Natu else 286*ea7f1c8cSNeel Natu (*sc->intr_assert)(sc->arg); 287*ea7f1c8cSNeel Natu } 288*ea7f1c8cSNeel Natu 289*ea7f1c8cSNeel Natu static void 290*ea7f1c8cSNeel Natu uart_drain(int fd, enum ev_type ev, void *arg) 291*ea7f1c8cSNeel Natu { 292*ea7f1c8cSNeel Natu struct uart_softc *sc; 293*ea7f1c8cSNeel Natu int ch; 294*ea7f1c8cSNeel Natu 295*ea7f1c8cSNeel Natu sc = arg; 296*ea7f1c8cSNeel Natu 297*ea7f1c8cSNeel Natu assert(fd == STDIN_FILENO); 298*ea7f1c8cSNeel Natu assert(ev == EVF_READ); 299*ea7f1c8cSNeel Natu 300*ea7f1c8cSNeel Natu /* 301*ea7f1c8cSNeel Natu * This routine is called in the context of the mevent thread 302*ea7f1c8cSNeel Natu * to take out the softc lock to protect against concurrent 303*ea7f1c8cSNeel Natu * access from a vCPU i/o exit 304*ea7f1c8cSNeel Natu */ 305*ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 306*ea7f1c8cSNeel Natu 307*ea7f1c8cSNeel Natu if ((sc->mcr & MCR_LOOPBACK) != 0) { 308*ea7f1c8cSNeel Natu (void) ttyread(); 309*ea7f1c8cSNeel Natu } else { 310*ea7f1c8cSNeel Natu while (fifo_available(&sc->rxfifo) && 311*ea7f1c8cSNeel Natu ((ch = ttyread()) != -1)) { 312*ea7f1c8cSNeel Natu fifo_putchar(&sc->rxfifo, ch); 313*ea7f1c8cSNeel Natu } 314*ea7f1c8cSNeel Natu uart_toggle_intr(sc); 315*ea7f1c8cSNeel Natu } 316*ea7f1c8cSNeel Natu 317*ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 318*ea7f1c8cSNeel Natu } 319*ea7f1c8cSNeel Natu 320*ea7f1c8cSNeel Natu void 321*ea7f1c8cSNeel Natu uart_write(struct uart_softc *sc, int offset, uint8_t value) 322*ea7f1c8cSNeel Natu { 323*ea7f1c8cSNeel Natu int fifosz; 324*ea7f1c8cSNeel Natu uint8_t msr; 325*ea7f1c8cSNeel Natu 326*ea7f1c8cSNeel Natu /* Open terminal */ 327*ea7f1c8cSNeel Natu if (!sc->opened && sc->stdio) { 328*ea7f1c8cSNeel Natu uart_opentty(sc); 329*ea7f1c8cSNeel Natu sc->opened = true; 330*ea7f1c8cSNeel Natu } 331*ea7f1c8cSNeel Natu 332*ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 333*ea7f1c8cSNeel Natu 334*ea7f1c8cSNeel Natu /* 335*ea7f1c8cSNeel Natu * Take care of the special case DLAB accesses first 336*ea7f1c8cSNeel Natu */ 337*ea7f1c8cSNeel Natu if ((sc->lcr & LCR_DLAB) != 0) { 338*ea7f1c8cSNeel Natu if (offset == REG_DLL) { 339*ea7f1c8cSNeel Natu sc->dll = value; 340*ea7f1c8cSNeel Natu goto done; 341*ea7f1c8cSNeel Natu } 342*ea7f1c8cSNeel Natu 343*ea7f1c8cSNeel Natu if (offset == REG_DLH) { 344*ea7f1c8cSNeel Natu sc->dlh = value; 345*ea7f1c8cSNeel Natu goto done; 346*ea7f1c8cSNeel Natu } 347*ea7f1c8cSNeel Natu } 348*ea7f1c8cSNeel Natu 349*ea7f1c8cSNeel Natu switch (offset) { 350*ea7f1c8cSNeel Natu case REG_DATA: 351*ea7f1c8cSNeel Natu if (sc->mcr & MCR_LOOPBACK) { 352*ea7f1c8cSNeel Natu if (fifo_putchar(&sc->rxfifo, value) != 0) 353*ea7f1c8cSNeel Natu sc->lsr |= LSR_OE; 354*ea7f1c8cSNeel Natu } else if (sc->stdio) { 355*ea7f1c8cSNeel Natu ttywrite(value); 356*ea7f1c8cSNeel Natu } /* else drop on floor */ 357*ea7f1c8cSNeel Natu sc->thre_int_pending = true; 358*ea7f1c8cSNeel Natu break; 359*ea7f1c8cSNeel Natu case REG_IER: 360*ea7f1c8cSNeel Natu /* 361*ea7f1c8cSNeel Natu * Apply mask so that bits 4-7 are 0 362*ea7f1c8cSNeel Natu * Also enables bits 0-3 only if they're 1 363*ea7f1c8cSNeel Natu */ 364*ea7f1c8cSNeel Natu sc->ier = value & 0x0F; 365*ea7f1c8cSNeel Natu break; 366*ea7f1c8cSNeel Natu case REG_FCR: 367*ea7f1c8cSNeel Natu /* 368*ea7f1c8cSNeel Natu * When moving from FIFO and 16450 mode and vice versa, 369*ea7f1c8cSNeel Natu * the FIFO contents are reset. 370*ea7f1c8cSNeel Natu */ 371*ea7f1c8cSNeel Natu if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) { 372*ea7f1c8cSNeel Natu fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1; 373*ea7f1c8cSNeel Natu fifo_reset(&sc->rxfifo, fifosz); 374*ea7f1c8cSNeel Natu } 375*ea7f1c8cSNeel Natu 376*ea7f1c8cSNeel Natu /* 377*ea7f1c8cSNeel Natu * The FCR_ENABLE bit must be '1' for the programming 378*ea7f1c8cSNeel Natu * of other FCR bits to be effective. 379*ea7f1c8cSNeel Natu */ 380*ea7f1c8cSNeel Natu if ((value & FCR_ENABLE) == 0) { 381*ea7f1c8cSNeel Natu sc->fcr = 0; 382*ea7f1c8cSNeel Natu } else { 383*ea7f1c8cSNeel Natu if ((value & FCR_RCV_RST) != 0) 384*ea7f1c8cSNeel Natu fifo_reset(&sc->rxfifo, FIFOSZ); 385*ea7f1c8cSNeel Natu 386*ea7f1c8cSNeel Natu sc->fcr = value & 387*ea7f1c8cSNeel Natu (FCR_ENABLE | FCR_DMA | FCR_RX_MASK); 388*ea7f1c8cSNeel Natu } 389*ea7f1c8cSNeel Natu break; 390*ea7f1c8cSNeel Natu case REG_LCR: 391*ea7f1c8cSNeel Natu sc->lcr = value; 392*ea7f1c8cSNeel Natu break; 393*ea7f1c8cSNeel Natu case REG_MCR: 394*ea7f1c8cSNeel Natu /* Apply mask so that bits 5-7 are 0 */ 395*ea7f1c8cSNeel Natu sc->mcr = value & 0x1F; 396*ea7f1c8cSNeel Natu 397*ea7f1c8cSNeel Natu msr = 0; 398*ea7f1c8cSNeel Natu if (sc->mcr & MCR_LOOPBACK) { 399*ea7f1c8cSNeel Natu /* 400*ea7f1c8cSNeel Natu * In the loopback mode certain bits from the 401*ea7f1c8cSNeel Natu * MCR are reflected back into MSR 402*ea7f1c8cSNeel Natu */ 403*ea7f1c8cSNeel Natu if (sc->mcr & MCR_RTS) 404*ea7f1c8cSNeel Natu msr |= MSR_CTS; 405*ea7f1c8cSNeel Natu if (sc->mcr & MCR_DTR) 406*ea7f1c8cSNeel Natu msr |= MSR_DSR; 407*ea7f1c8cSNeel Natu if (sc->mcr & MCR_OUT1) 408*ea7f1c8cSNeel Natu msr |= MSR_RI; 409*ea7f1c8cSNeel Natu if (sc->mcr & MCR_OUT2) 410*ea7f1c8cSNeel Natu msr |= MSR_DCD; 411*ea7f1c8cSNeel Natu } 412*ea7f1c8cSNeel Natu 413*ea7f1c8cSNeel Natu /* 414*ea7f1c8cSNeel Natu * Detect if there has been any change between the 415*ea7f1c8cSNeel Natu * previous and the new value of MSR. If there is 416*ea7f1c8cSNeel Natu * then assert the appropriate MSR delta bit. 417*ea7f1c8cSNeel Natu */ 418*ea7f1c8cSNeel Natu if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS)) 419*ea7f1c8cSNeel Natu sc->msr |= MSR_DCTS; 420*ea7f1c8cSNeel Natu if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR)) 421*ea7f1c8cSNeel Natu sc->msr |= MSR_DDSR; 422*ea7f1c8cSNeel Natu if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD)) 423*ea7f1c8cSNeel Natu sc->msr |= MSR_DDCD; 424*ea7f1c8cSNeel Natu if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0) 425*ea7f1c8cSNeel Natu sc->msr |= MSR_TERI; 426*ea7f1c8cSNeel Natu 427*ea7f1c8cSNeel Natu /* 428*ea7f1c8cSNeel Natu * Update the value of MSR while retaining the delta 429*ea7f1c8cSNeel Natu * bits. 430*ea7f1c8cSNeel Natu */ 431*ea7f1c8cSNeel Natu sc->msr &= MSR_DELTA_MASK; 432*ea7f1c8cSNeel Natu sc->msr |= msr; 433*ea7f1c8cSNeel Natu break; 434*ea7f1c8cSNeel Natu case REG_LSR: 435*ea7f1c8cSNeel Natu /* 436*ea7f1c8cSNeel Natu * Line status register is not meant to be written to 437*ea7f1c8cSNeel Natu * during normal operation. 438*ea7f1c8cSNeel Natu */ 439*ea7f1c8cSNeel Natu break; 440*ea7f1c8cSNeel Natu case REG_MSR: 441*ea7f1c8cSNeel Natu /* 442*ea7f1c8cSNeel Natu * As far as I can tell MSR is a read-only register. 443*ea7f1c8cSNeel Natu */ 444*ea7f1c8cSNeel Natu break; 445*ea7f1c8cSNeel Natu case REG_SCR: 446*ea7f1c8cSNeel Natu sc->scr = value; 447*ea7f1c8cSNeel Natu break; 448*ea7f1c8cSNeel Natu default: 449*ea7f1c8cSNeel Natu break; 450*ea7f1c8cSNeel Natu } 451*ea7f1c8cSNeel Natu 452*ea7f1c8cSNeel Natu done: 453*ea7f1c8cSNeel Natu uart_toggle_intr(sc); 454*ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 455*ea7f1c8cSNeel Natu } 456*ea7f1c8cSNeel Natu 457*ea7f1c8cSNeel Natu uint8_t 458*ea7f1c8cSNeel Natu uart_read(struct uart_softc *sc, int offset) 459*ea7f1c8cSNeel Natu { 460*ea7f1c8cSNeel Natu uint8_t iir, intr_reason, reg; 461*ea7f1c8cSNeel Natu 462*ea7f1c8cSNeel Natu /* Open terminal */ 463*ea7f1c8cSNeel Natu if (!sc->opened && sc->stdio) { 464*ea7f1c8cSNeel Natu uart_opentty(sc); 465*ea7f1c8cSNeel Natu sc->opened = true; 466*ea7f1c8cSNeel Natu } 467*ea7f1c8cSNeel Natu 468*ea7f1c8cSNeel Natu pthread_mutex_lock(&sc->mtx); 469*ea7f1c8cSNeel Natu 470*ea7f1c8cSNeel Natu /* 471*ea7f1c8cSNeel Natu * Take care of the special case DLAB accesses first 472*ea7f1c8cSNeel Natu */ 473*ea7f1c8cSNeel Natu if ((sc->lcr & LCR_DLAB) != 0) { 474*ea7f1c8cSNeel Natu if (offset == REG_DLL) { 475*ea7f1c8cSNeel Natu reg = sc->dll; 476*ea7f1c8cSNeel Natu goto done; 477*ea7f1c8cSNeel Natu } 478*ea7f1c8cSNeel Natu 479*ea7f1c8cSNeel Natu if (offset == REG_DLH) { 480*ea7f1c8cSNeel Natu reg = sc->dlh; 481*ea7f1c8cSNeel Natu goto done; 482*ea7f1c8cSNeel Natu } 483*ea7f1c8cSNeel Natu } 484*ea7f1c8cSNeel Natu 485*ea7f1c8cSNeel Natu switch (offset) { 486*ea7f1c8cSNeel Natu case REG_DATA: 487*ea7f1c8cSNeel Natu reg = fifo_getchar(&sc->rxfifo); 488*ea7f1c8cSNeel Natu break; 489*ea7f1c8cSNeel Natu case REG_IER: 490*ea7f1c8cSNeel Natu reg = sc->ier; 491*ea7f1c8cSNeel Natu break; 492*ea7f1c8cSNeel Natu case REG_IIR: 493*ea7f1c8cSNeel Natu iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0; 494*ea7f1c8cSNeel Natu 495*ea7f1c8cSNeel Natu intr_reason = uart_intr_reason(sc); 496*ea7f1c8cSNeel Natu 497*ea7f1c8cSNeel Natu /* 498*ea7f1c8cSNeel Natu * Deal with side effects of reading the IIR register 499*ea7f1c8cSNeel Natu */ 500*ea7f1c8cSNeel Natu if (intr_reason == IIR_TXRDY) 501*ea7f1c8cSNeel Natu sc->thre_int_pending = false; 502*ea7f1c8cSNeel Natu 503*ea7f1c8cSNeel Natu iir |= intr_reason; 504*ea7f1c8cSNeel Natu 505*ea7f1c8cSNeel Natu reg = iir; 506*ea7f1c8cSNeel Natu break; 507*ea7f1c8cSNeel Natu case REG_LCR: 508*ea7f1c8cSNeel Natu reg = sc->lcr; 509*ea7f1c8cSNeel Natu break; 510*ea7f1c8cSNeel Natu case REG_MCR: 511*ea7f1c8cSNeel Natu reg = sc->mcr; 512*ea7f1c8cSNeel Natu break; 513*ea7f1c8cSNeel Natu case REG_LSR: 514*ea7f1c8cSNeel Natu /* Transmitter is always ready for more data */ 515*ea7f1c8cSNeel Natu sc->lsr |= LSR_TEMT | LSR_THRE; 516*ea7f1c8cSNeel Natu 517*ea7f1c8cSNeel Natu /* Check for new receive data */ 518*ea7f1c8cSNeel Natu if (fifo_numchars(&sc->rxfifo) > 0) 519*ea7f1c8cSNeel Natu sc->lsr |= LSR_RXRDY; 520*ea7f1c8cSNeel Natu else 521*ea7f1c8cSNeel Natu sc->lsr &= ~LSR_RXRDY; 522*ea7f1c8cSNeel Natu 523*ea7f1c8cSNeel Natu reg = sc->lsr; 524*ea7f1c8cSNeel Natu 525*ea7f1c8cSNeel Natu /* The LSR_OE bit is cleared on LSR read */ 526*ea7f1c8cSNeel Natu sc->lsr &= ~LSR_OE; 527*ea7f1c8cSNeel Natu break; 528*ea7f1c8cSNeel Natu case REG_MSR: 529*ea7f1c8cSNeel Natu /* 530*ea7f1c8cSNeel Natu * MSR delta bits are cleared on read 531*ea7f1c8cSNeel Natu */ 532*ea7f1c8cSNeel Natu reg = sc->msr; 533*ea7f1c8cSNeel Natu sc->msr &= ~MSR_DELTA_MASK; 534*ea7f1c8cSNeel Natu break; 535*ea7f1c8cSNeel Natu case REG_SCR: 536*ea7f1c8cSNeel Natu reg = sc->scr; 537*ea7f1c8cSNeel Natu break; 538*ea7f1c8cSNeel Natu default: 539*ea7f1c8cSNeel Natu reg = 0xFF; 540*ea7f1c8cSNeel Natu break; 541*ea7f1c8cSNeel Natu } 542*ea7f1c8cSNeel Natu 543*ea7f1c8cSNeel Natu done: 544*ea7f1c8cSNeel Natu uart_toggle_intr(sc); 545*ea7f1c8cSNeel Natu pthread_mutex_unlock(&sc->mtx); 546*ea7f1c8cSNeel Natu 547*ea7f1c8cSNeel Natu return (reg); 548*ea7f1c8cSNeel Natu } 549*ea7f1c8cSNeel Natu 550*ea7f1c8cSNeel Natu int 551*ea7f1c8cSNeel Natu uart_legacy_alloc(int which, int *baseaddr, int *irq) 552*ea7f1c8cSNeel Natu { 553*ea7f1c8cSNeel Natu 554*ea7f1c8cSNeel Natu if (which < 0 || which >= UART_NLDEVS || uart_lres[which].inuse) 555*ea7f1c8cSNeel Natu return (-1); 556*ea7f1c8cSNeel Natu 557*ea7f1c8cSNeel Natu uart_lres[which].inuse = true; 558*ea7f1c8cSNeel Natu *baseaddr = uart_lres[which].baseaddr; 559*ea7f1c8cSNeel Natu *irq = uart_lres[which].irq; 560*ea7f1c8cSNeel Natu 561*ea7f1c8cSNeel Natu return (0); 562*ea7f1c8cSNeel Natu } 563*ea7f1c8cSNeel Natu 564*ea7f1c8cSNeel Natu struct uart_softc * 565*ea7f1c8cSNeel Natu uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert, 566*ea7f1c8cSNeel Natu void *arg) 567*ea7f1c8cSNeel Natu { 568*ea7f1c8cSNeel Natu struct uart_softc *sc; 569*ea7f1c8cSNeel Natu 570*ea7f1c8cSNeel Natu sc = malloc(sizeof(struct uart_softc)); 571*ea7f1c8cSNeel Natu bzero(sc, sizeof(struct uart_softc)); 572*ea7f1c8cSNeel Natu 573*ea7f1c8cSNeel Natu sc->arg = arg; 574*ea7f1c8cSNeel Natu sc->intr_assert = intr_assert; 575*ea7f1c8cSNeel Natu sc->intr_deassert = intr_deassert; 576*ea7f1c8cSNeel Natu 577*ea7f1c8cSNeel Natu pthread_mutex_init(&sc->mtx, NULL); 578*ea7f1c8cSNeel Natu 579*ea7f1c8cSNeel Natu uart_reset(sc); 580*ea7f1c8cSNeel Natu 581*ea7f1c8cSNeel Natu return (sc); 582*ea7f1c8cSNeel Natu } 583*ea7f1c8cSNeel Natu 584*ea7f1c8cSNeel Natu int 585*ea7f1c8cSNeel Natu uart_set_backend(struct uart_softc *sc, const char *opts) 586*ea7f1c8cSNeel Natu { 587*ea7f1c8cSNeel Natu /* 588*ea7f1c8cSNeel Natu * XXX one stdio backend supported at this time. 589*ea7f1c8cSNeel Natu */ 590*ea7f1c8cSNeel Natu if (opts == NULL) 591*ea7f1c8cSNeel Natu return (0); 592*ea7f1c8cSNeel Natu 593*ea7f1c8cSNeel Natu if (strcmp("stdio", opts) == 0 && !uart_stdio) { 594*ea7f1c8cSNeel Natu sc->stdio = true; 595*ea7f1c8cSNeel Natu uart_stdio = true; 596*ea7f1c8cSNeel Natu return (0); 597*ea7f1c8cSNeel Natu } else 598*ea7f1c8cSNeel Natu return (-1); 599*ea7f1c8cSNeel Natu } 600