1221828Sgrehan/*- 2221828Sgrehan * SPDX-License-Identifier: BSD-2-Clause 3221828Sgrehan * 4221828Sgrehan * Copyright (c) 2012 NetApp, Inc. 5221828Sgrehan * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 6221828Sgrehan * All rights reserved. 7221828Sgrehan * 8221828Sgrehan * Redistribution and use in source and binary forms, with or without 9221828Sgrehan * modification, are permitted provided that the following conditions 10221828Sgrehan * are met: 11221828Sgrehan * 1. Redistributions of source code must retain the above copyright 12221828Sgrehan * notice, this list of conditions and the following disclaimer. 13221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 14221828Sgrehan * notice, this list of conditions and the following disclaimer in the 15221828Sgrehan * documentation and/or other materials provided with the distribution. 16221828Sgrehan * 17221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 18221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20221828Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 21221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27221828Sgrehan * SUCH DAMAGE. 28221828Sgrehan */ 29221828Sgrehan 30221828Sgrehan#include <sys/types.h> 31221828Sgrehan#include <dev/ic/ns16550.h> 32221828Sgrehan 33221828Sgrehan#include <machine/vmm_snapshot.h> 34221828Sgrehan 35221828Sgrehan#include <assert.h> 36221828Sgrehan#include <stdio.h> 37221828Sgrehan#include <stdlib.h> 38221828Sgrehan#include <errno.h> 39221828Sgrehan#include <unistd.h> 40221828Sgrehan#include <stdbool.h> 41221828Sgrehan#include <string.h> 42221828Sgrehan#include <pthread.h> 43221828Sgrehan 44221828Sgrehan#include "uart_backend.h" 45221828Sgrehan#include "uart_emul.h" 46221828Sgrehan 47256072Sneel#define COM1_BASE 0x3F8 48221828Sgrehan#define COM1_IRQ 4 49221828Sgrehan#define COM2_BASE 0x2F8 50221828Sgrehan#define COM2_IRQ 3 51261088Sjhb#define COM3_BASE 0x3E8 52261088Sjhb#define COM3_IRQ 4 53221828Sgrehan#define COM4_BASE 0x2E8 54221828Sgrehan#define COM4_IRQ 3 55221828Sgrehan 56239700Sgrehan#define DEFAULT_RCLK 1843200 57221828Sgrehan#define DEFAULT_BAUD 115200 58261088Sjhb 59261088Sjhb#define FCR_RX_MASK 0xC0 60221828Sgrehan 61221828Sgrehan#define MCR_OUT1 0x04 62221828Sgrehan#define MCR_OUT2 0x08 63221828Sgrehan 64221828Sgrehan#define MSR_DELTA_MASK 0x0f 65256651Sneel 66221828Sgrehan#ifndef REG_SCR 67256651Sneel#define REG_SCR com_scr 68256651Sneel#endif 69221828Sgrehan 70221828Sgrehanstatic struct { 71221828Sgrehan int baseaddr; 72221828Sgrehan int irq; 73221828Sgrehan bool inuse; 74221828Sgrehan} uart_lres[] = { 75221828Sgrehan { COM1_BASE, COM1_IRQ, false}, 76221828Sgrehan { COM2_BASE, COM2_IRQ, false}, 77221828Sgrehan { COM3_BASE, COM3_IRQ, false}, 78221828Sgrehan { COM4_BASE, COM4_IRQ, false}, 79221828Sgrehan}; 80221828Sgrehan 81221828Sgrehan#define UART_NLDEVS (sizeof(uart_lres) / sizeof(uart_lres[0])) 82221828Sgrehan 83221828Sgrehanstruct uart_ns16550_softc { 84221828Sgrehan struct uart_softc *backend; 85221828Sgrehan 86221828Sgrehan uint8_t data; /* Data register (R/W) */ 87221828Sgrehan uint8_t ier; /* Interrupt enable register (R/W) */ 88221828Sgrehan uint8_t lcr; /* Line control register (R/W) */ 89221828Sgrehan uint8_t mcr; /* Modem control register (R/W) */ 90221828Sgrehan uint8_t lsr; /* Line status register (R/W) */ 91221828Sgrehan uint8_t msr; /* Modem status register (R/W) */ 92221828Sgrehan uint8_t fcr; /* FIFO control register (W) */ 93221828Sgrehan uint8_t scr; /* Scratch register (R/W) */ 94221828Sgrehan 95221828Sgrehan uint8_t dll; /* Baudrate divisor latch LSB */ 96221828Sgrehan uint8_t dlh; /* Baudrate divisor latch MSB */ 97221828Sgrehan 98241454Sneel bool thre_int_pending; /* THRE interrupt pending */ 99221828Sgrehan 100221828Sgrehan void *arg; 101221828Sgrehan uart_intr_func_t intr_assert; 102221828Sgrehan uart_intr_func_t intr_deassert; 103221828Sgrehan}; 104256072Sneel 105256072Sneelstatic uint8_t 106256072Sneelmodem_status(uint8_t mcr) 107221828Sgrehan{ 108221828Sgrehan uint8_t msr; 109221828Sgrehan 110221828Sgrehan if (mcr & MCR_LOOPBACK) { 111221828Sgrehan /* 112221828Sgrehan * In the loopback mode certain bits from the MCR are 113241454Sneel * reflected back into MSR. 114241454Sneel */ 115221828Sgrehan msr = 0; 116256072Sneel if (mcr & MCR_RTS) 117221828Sgrehan msr |= MSR_CTS; 118221828Sgrehan if (mcr & MCR_DTR) 119221828Sgrehan msr |= MSR_DSR; 120221828Sgrehan if (mcr & MCR_OUT1) 121221828Sgrehan msr |= MSR_RI; 122221828Sgrehan if (mcr & MCR_OUT2) 123221828Sgrehan msr |= MSR_DCD; 124221828Sgrehan } else { 125221828Sgrehan /* 126221828Sgrehan * Always assert DCD and DSR so tty open doesn't block 127221828Sgrehan * even if CLOCAL is turned off. 128221828Sgrehan */ 129221828Sgrehan msr = MSR_DCD | MSR_DSR; 130256072Sneel } 131256072Sneel assert((msr & MSR_DELTA_MASK) == 0); 132221828Sgrehan 133221828Sgrehan return (msr); 134221828Sgrehan} 135221828Sgrehan 136256072Sneel/* 137256072Sneel * The IIR returns a prioritized interrupt reason: 138256072Sneel * - receive data available 139256072Sneel * - transmit holding register empty 140221828Sgrehan * - modem status change 141221828Sgrehan * 142221828Sgrehan * Return an interrupt reason if one is available. 143221828Sgrehan */ 144221828Sgrehanstatic int 145221828Sgrehanuart_intr_reason(struct uart_ns16550_softc *sc) 146221828Sgrehan{ 147221828Sgrehan 148241489Sneel if ((sc->lsr & LSR_OE) != 0 && (sc->ier & IER_ERLS) != 0) 149221828Sgrehan return (IIR_RLS); 150221828Sgrehan else if (uart_rxfifo_numchars(sc->backend) > 0 && 151221828Sgrehan (sc->ier & IER_ERXRDY) != 0) 152261088Sjhb return (IIR_RXTOUT); 153221828Sgrehan else if (sc->thre_int_pending && (sc->ier & IER_ETXRDY) != 0) 154221828Sgrehan return (IIR_TXRDY); 155221828Sgrehan else if ((sc->msr & MSR_DELTA_MASK) != 0 && (sc->ier & IER_EMSC) != 0) 156261088Sjhb return (IIR_MLSC); 157221828Sgrehan else 158221828Sgrehan return (IIR_NOPEND); 159221828Sgrehan} 160221828Sgrehan 161234761Sgrehanstatic void 162221828Sgrehanuart_reset(struct uart_ns16550_softc *sc) 163221828Sgrehan{ 164221828Sgrehan uint16_t divisor; 165240922Sneel 166256072Sneel divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16; 167221828Sgrehan sc->dll = divisor; 168221828Sgrehan sc->dlh = divisor >> 16; 169241489Sneel sc->msr = modem_status(sc->mcr); 170221828Sgrehan 171221828Sgrehan uart_rxfifo_reset(sc->backend, 1); 172241489Sneel} 173241489Sneel 174241489Sneel/* 175221828Sgrehan * Toggle the COM port's intr pin depending on whether or not we have an 176221828Sgrehan * interrupt condition to report to the processor. 177221828Sgrehan */ 178221828Sgrehanstatic void 179221828Sgrehanuart_toggle_intr(struct uart_ns16550_softc *sc) 180221828Sgrehan{ 181221828Sgrehan uint8_t intr_reason; 182221828Sgrehan 183221828Sgrehan intr_reason = uart_intr_reason(sc); 184221828Sgrehan 185221828Sgrehan if (intr_reason == IIR_NOPEND) 186221828Sgrehan (*sc->intr_deassert)(sc->arg); 187221828Sgrehan else 188241489Sneel (*sc->intr_assert)(sc->arg); 189240922Sneel} 190221828Sgrehan 191221828Sgrehanstatic void 192221828Sgrehanuart_drain(int fd __unused, enum ev_type ev, void *arg) 193221828Sgrehan{ 194221828Sgrehan struct uart_ns16550_softc *sc; 195221828Sgrehan bool loopback; 196221828Sgrehan 197221828Sgrehan sc = arg; 198221828Sgrehan 199221828Sgrehan assert(ev == EVF_READ); 200256072Sneel 201241489Sneel /* 202221828Sgrehan * This routine is called in the context of the mevent thread 203241489Sneel * to take out the softc lock to protect against concurrent 204241489Sneel * access from a vCPU i/o exit 205241489Sneel */ 206241489Sneel uart_softc_lock(sc->backend); 207241489Sneel 208241489Sneel loopback = (sc->mcr & MCR_LOOPBACK) != 0; 209241489Sneel uart_rxfifo_drain(sc->backend, loopback); 210241489Sneel if (!loopback) 211241489Sneel uart_toggle_intr(sc); 212241489Sneel 213241489Sneel uart_softc_unlock(sc->backend); 214241489Sneel} 215241489Sneel 216241489Sneelvoid 217256072Sneeluart_ns16550_write(struct uart_ns16550_softc *sc, int offset, uint8_t value) 218241489Sneel{ 219241489Sneel int fifosz; 220221828Sgrehan uint8_t msr; 221241489Sneel 222241489Sneel uart_softc_lock(sc->backend); 223241489Sneel 224241489Sneel /* 225241489Sneel * Take care of the special case DLAB accesses first 226241489Sneel */ 227241489Sneel if ((sc->lcr & LCR_DLAB) != 0) { 228241489Sneel if (offset == REG_DLL) { 229221828Sgrehan sc->dll = value; 230241489Sneel goto done; 231221828Sgrehan } 232221828Sgrehan 233221828Sgrehan if (offset == REG_DLH) { 234221828Sgrehan sc->dlh = value; 235221828Sgrehan goto done; 236221828Sgrehan } 237221828Sgrehan } 238221828Sgrehan 239221828Sgrehan switch (offset) { 240221828Sgrehan case REG_DATA: 241221828Sgrehan if (uart_rxfifo_putchar(sc->backend, value, 242250427Sneel (sc->mcr & MCR_LOOPBACK) != 0)) 243250427Sneel sc->lsr |= LSR_OE; 244221828Sgrehan sc->thre_int_pending = true; 245221828Sgrehan break; 246221828Sgrehan case REG_IER: 247250427Sneel /* Set pending when IER_ETXRDY is raised (edge-triggered). */ 248221828Sgrehan if ((sc->ier & IER_ETXRDY) == 0 && (value & IER_ETXRDY) != 0) 249221828Sgrehan sc->thre_int_pending = true; 250221828Sgrehan /* 251221828Sgrehan * Apply mask so that bits 4-7 are 0 252221828Sgrehan * Also enables bits 0-3 only if they're 1 253221828Sgrehan */ 254221828Sgrehan sc->ier = value & 0x0F; 255221828Sgrehan break; 256221828Sgrehan case REG_FCR: 257221828Sgrehan /* 258221828Sgrehan * When moving from FIFO and 16450 mode and vice versa, 259221828Sgrehan * the FIFO contents are reset. 260221828Sgrehan */ 261234761Sgrehan if ((sc->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) { 262234761Sgrehan fifosz = (value & FCR_ENABLE) ? 263234761Sgrehan uart_rxfifo_size(sc->backend) : 1; 264234761Sgrehan uart_rxfifo_reset(sc->backend, fifosz); 265234761Sgrehan } 266234761Sgrehan 267234761Sgrehan /* 268234761Sgrehan * The FCR_ENABLE bit must be '1' for the programming 269221828Sgrehan * of other FCR bits to be effective. 270221828Sgrehan */ 271221828Sgrehan if ((value & FCR_ENABLE) == 0) { 272221828Sgrehan sc->fcr = 0; 273221828Sgrehan } else { 274221828Sgrehan if ((value & FCR_RCV_RST) != 0) 275221828Sgrehan uart_rxfifo_reset(sc->backend, 276221828Sgrehan uart_rxfifo_size(sc->backend)); 277256072Sneel 278256072Sneel sc->fcr = value & 279221828Sgrehan (FCR_ENABLE | FCR_DMA | FCR_RX_MASK); 280221828Sgrehan } 281221828Sgrehan break; 282256072Sneel case REG_LCR: 283256072Sneel sc->lcr = value; 284221828Sgrehan break; 285221828Sgrehan case REG_MCR: 286221828Sgrehan /* Apply mask so that bits 5-7 are 0 */ 287221828Sgrehan sc->mcr = value & 0x1F; 288221828Sgrehan msr = modem_status(sc->mcr); 289221828Sgrehan 290221828Sgrehan /* 291221828Sgrehan * Detect if there has been any change between the 292221828Sgrehan * previous and the new value of MSR. If there is 293221828Sgrehan * then assert the appropriate MSR delta bit. 294221828Sgrehan */ 295221828Sgrehan if ((msr & MSR_CTS) ^ (sc->msr & MSR_CTS)) 296221828Sgrehan sc->msr |= MSR_DCTS; 297221828Sgrehan if ((msr & MSR_DSR) ^ (sc->msr & MSR_DSR)) 298261088Sjhb sc->msr |= MSR_DDSR; 299221828Sgrehan if ((msr & MSR_DCD) ^ (sc->msr & MSR_DCD)) 300261088Sjhb sc->msr |= MSR_DDCD; 301261088Sjhb if ((sc->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0) 302261088Sjhb sc->msr |= MSR_TERI; 303261088Sjhb 304261088Sjhb /* 305261088Sjhb * Update the value of MSR while retaining the delta 306261088Sjhb * bits. 307261088Sjhb */ 308261088Sjhb sc->msr &= MSR_DELTA_MASK; 309261088Sjhb sc->msr |= msr; 310261088Sjhb break; 311261088Sjhb case REG_LSR: 312221828Sgrehan /* 313221828Sgrehan * Line status register is not meant to be written to 314241041Sneel * during normal operation. 315221828Sgrehan */ 316221828Sgrehan break; 317221828Sgrehan case REG_MSR: 318241178Sneel /* 319221828Sgrehan * As far as I can tell MSR is a read-only register. 320221828Sgrehan */ 321221828Sgrehan break; 322221828Sgrehan case REG_SCR: 323221828Sgrehan sc->scr = value; 324221828Sgrehan break; 325221828Sgrehan default: 326221828Sgrehan break; 327221828Sgrehan } 328221828Sgrehan 329221828Sgrehandone: 330221828Sgrehan uart_toggle_intr(sc); 331221828Sgrehan uart_softc_unlock(sc->backend); 332221828Sgrehan} 333221828Sgrehan 334221828Sgrehanuint8_t 335221828Sgrehanuart_ns16550_read(struct uart_ns16550_softc *sc, int offset) 336221828Sgrehan{ 337221828Sgrehan uint8_t iir, intr_reason, reg; 338221828Sgrehan 339221828Sgrehan uart_softc_lock(sc->backend); 340221828Sgrehan 341221828Sgrehan /* 342221828Sgrehan * Take care of the special case DLAB accesses first 343221828Sgrehan */ 344221828Sgrehan if ((sc->lcr & LCR_DLAB) != 0) { 345221828Sgrehan if (offset == REG_DLL) { 346221828Sgrehan reg = sc->dll; 347221828Sgrehan goto done; 348221828Sgrehan } 349221828Sgrehan 350221828Sgrehan if (offset == REG_DLH) { 351221828Sgrehan reg = sc->dlh; 352221828Sgrehan goto done; 353221828Sgrehan } 354221828Sgrehan } 355221828Sgrehan 356240922Sneel switch (offset) { 357240922Sneel case REG_DATA: 358240922Sneel reg = uart_rxfifo_getchar(sc->backend); 359240922Sneel break; 360240922Sneel case REG_IER: 361240922Sneel reg = sc->ier; 362240922Sneel break; 363240922Sneel case REG_IIR: 364240922Sneel iir = (sc->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0; 365240922Sneel 366256072Sneel intr_reason = uart_intr_reason(sc); 367256072Sneel 368256072Sneel /* 369256072Sneel * Deal with side effects of reading the IIR register 370256072Sneel */ 371256072Sneel if (intr_reason == IIR_TXRDY) 372261088Sjhb sc->thre_int_pending = false; 373261088Sjhb 374261088Sjhb iir |= intr_reason; 375221828Sgrehan 376221828Sgrehan reg = iir; 377221828Sgrehan break; 378221828Sgrehan case REG_LCR: 379241489Sneel reg = sc->lcr; 380241489Sneel break; 381241489Sneel case REG_MCR: 382241489Sneel reg = sc->mcr; 383241489Sneel break; 384241489Sneel case REG_LSR: 385241489Sneel /* Transmitter is always ready for more data */ 386241489Sneel sc->lsr |= LSR_TEMT | LSR_THRE; 387221828Sgrehan 388256072Sneel /* Check for new receive data */ 389256072Sneel if (uart_rxfifo_numchars(sc->backend) > 0) 390221828Sgrehan sc->lsr |= LSR_RXRDY; 391221828Sgrehan else 392221828Sgrehan sc->lsr &= ~LSR_RXRDY; 393221828Sgrehan 394256072Sneel reg = sc->lsr; 395256072Sneel 396221828Sgrehan /* The LSR_OE bit is cleared on LSR read */ 397221828Sgrehan sc->lsr &= ~LSR_OE; 398221828Sgrehan break; 399221828Sgrehan case REG_MSR: 400221828Sgrehan /* 401256072Sneel * MSR delta bits are cleared on read 402256072Sneel */ 403256072Sneel reg = sc->msr; 404256072Sneel sc->msr &= ~MSR_DELTA_MASK; 405221828Sgrehan break; 406221828Sgrehan case REG_SCR: 407221828Sgrehan reg = sc->scr; 408221828Sgrehan break; 409221828Sgrehan default: 410256651Sneel reg = 0xFF; 411221828Sgrehan break; 412221828Sgrehan } 413256651Sneel 414256651Sneeldone: 415256651Sneel uart_toggle_intr(sc); 416241454Sneel uart_softc_unlock(sc->backend); 417241454Sneel 418256651Sneel return (reg); 419241454Sneel} 420241454Sneel 421256651Sneelint 422241454Sneeluart_legacy_alloc(int which, int *baseaddr, int *irq) 423241454Sneel{ 424241454Sneel 425241454Sneel if (which < 0 || which >= (int)UART_NLDEVS || uart_lres[which].inuse) 426241454Sneel return (-1); 427221828Sgrehan 428221828Sgrehan uart_lres[which].inuse = true; 429221828Sgrehan *baseaddr = uart_lres[which].baseaddr; 430221828Sgrehan *irq = uart_lres[which].irq; 431221828Sgrehan 432221828Sgrehan return (0); 433221828Sgrehan} 434221828Sgrehan 435221828Sgrehanstruct uart_ns16550_softc * 436256651Sneeluart_ns16550_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert, 437221828Sgrehan void *arg) 438221828Sgrehan{ 439221828Sgrehan struct uart_ns16550_softc *sc; 440221828Sgrehan 441221828Sgrehan sc = calloc(1, sizeof(struct uart_ns16550_softc)); 442221828Sgrehan 443221828Sgrehan sc->arg = arg; 444221828Sgrehan sc->intr_assert = intr_assert; 445256651Sneel sc->intr_deassert = intr_deassert; 446221828Sgrehan sc->backend = uart_init(); 447221828Sgrehan 448221828Sgrehan uart_reset(sc); 449241454Sneel 450256651Sneel return (sc); 451256651Sneel} 452256651Sneel 453256651Sneelint 454256651Sneeluart_ns16550_tty_open(struct uart_ns16550_softc *sc, const char *device) 455256651Sneel{ 456221828Sgrehan return (uart_tty_open(sc->backend, device, uart_drain, sc)); 457241454Sneel} 458256651Sneel 459256651Sneel#ifdef BHYVE_SNAPSHOT 460256651Sneelint 461256651Sneeluart_ns16550_snapshot(struct uart_ns16550_softc *sc, 462256651Sneel struct vm_snapshot_meta *meta) 463256651Sneel{ 464256651Sneel int ret; 465256651Sneel 466256651Sneel SNAPSHOT_VAR_OR_LEAVE(sc->data, meta, ret, done); 467256651Sneel SNAPSHOT_VAR_OR_LEAVE(sc->ier, meta, ret, done); 468241454Sneel SNAPSHOT_VAR_OR_LEAVE(sc->lcr, meta, ret, done); 469221828Sgrehan SNAPSHOT_VAR_OR_LEAVE(sc->mcr, meta, ret, done); 470221828Sgrehan SNAPSHOT_VAR_OR_LEAVE(sc->lsr, meta, ret, done); 471221828Sgrehan SNAPSHOT_VAR_OR_LEAVE(sc->msr, meta, ret, done); 472221828Sgrehan SNAPSHOT_VAR_OR_LEAVE(sc->fcr, meta, ret, done); 473221828Sgrehan SNAPSHOT_VAR_OR_LEAVE(sc->scr, meta, ret, done); 474221828Sgrehan 475221828Sgrehan SNAPSHOT_VAR_OR_LEAVE(sc->dll, meta, ret, done); 476221828Sgrehan SNAPSHOT_VAR_OR_LEAVE(sc->dlh, meta, ret, done); 477221828Sgrehan 478256072Sneel ret = uart_rxfifo_snapshot(sc->backend, meta); 479221828Sgrehan 480221828Sgrehan sc->thre_int_pending = 1; 481221828Sgrehan 482221828Sgrehandone: 483221828Sgrehan return (ret); 484221828Sgrehan} 485221828Sgrehan#endif 486221828Sgrehan