1257293Sneel/*- 2257293Sneel * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 3257396Sneel * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 4257293Sneel * All rights reserved. 5257293Sneel * 6257293Sneel * Redistribution and use in source and binary forms, with or without 7257293Sneel * modification, are permitted provided that the following conditions 8257293Sneel * are met: 9257293Sneel * 1. Redistributions of source code must retain the above copyright 10257293Sneel * notice, this list of conditions and the following disclaimer. 11257293Sneel * 2. Redistributions in binary form must reproduce the above copyright 12257293Sneel * notice, this list of conditions and the following disclaimer in the 13257293Sneel * documentation and/or other materials provided with the distribution. 14257293Sneel * 15257293Sneel * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 16257293Sneel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17257293Sneel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18257293Sneel * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 19257293Sneel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20257293Sneel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21257293Sneel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22257293Sneel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23257293Sneel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24257293Sneel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25257293Sneel * SUCH DAMAGE. 26257293Sneel * 27257293Sneel * $FreeBSD$ 28257293Sneel */ 29257293Sneel 30257293Sneel#include <sys/cdefs.h> 31257293Sneel__FBSDID("$FreeBSD$"); 32257293Sneel 33257293Sneel#include <sys/types.h> 34261088Sjhb#include <machine/vmm.h> 35257293Sneel 36257293Sneel#include <stdio.h> 37257293Sneel#include <stdlib.h> 38257293Sneel#include <string.h> 39257293Sneel 40261088Sjhb#include <vmmapi.h> 41261088Sjhb 42261265Sjhb#include "acpi.h" 43295124Sgrehan#include "bootrom.h" 44257293Sneel#include "inout.h" 45257293Sneel#include "pci_emul.h" 46268972Sjhb#include "pci_irq.h" 47261265Sjhb#include "pci_lpc.h" 48257293Sneel#include "uart_emul.h" 49257293Sneel 50268891Sjhb#define IO_ICU1 0x20 51268891Sjhb#define IO_ICU2 0xA0 52268891Sjhb 53261265SjhbSET_DECLARE(lpc_dsdt_set, struct lpc_dsdt); 54261265SjhbSET_DECLARE(lpc_sysres_set, struct lpc_sysres); 55261265Sjhb 56268891Sjhb#define ELCR_PORT 0x4d0 57268891SjhbSYSRES_IO(ELCR_PORT, 2); 58268891Sjhb 59268891Sjhb#define IO_TIMER1_PORT 0x40 60268891Sjhb 61268891Sjhb#define NMISC_PORT 0x61 62268891SjhbSYSRES_IO(NMISC_PORT, 1); 63268891Sjhb 64257293Sneelstatic struct pci_devinst *lpc_bridge; 65257293Sneel 66295124Sgrehanstatic const char *romfile; 67295124Sgrehan 68257293Sneel#define LPC_UART_NUM 2 69257293Sneelstatic struct lpc_uart_softc { 70257293Sneel struct uart_softc *uart_softc; 71257293Sneel const char *opts; 72257293Sneel int iobase; 73257293Sneel int irq; 74261265Sjhb int enabled; 75257293Sneel} lpc_uart_softc[LPC_UART_NUM]; 76257293Sneel 77257293Sneelstatic const char *lpc_uart_names[LPC_UART_NUM] = { "COM1", "COM2" }; 78257293Sneel 79257293Sneel/* 80257293Sneel * LPC device configuration is in the following form: 81257293Sneel * <lpc_device_name>[,<options>] 82295124Sgrehan * For e.g. "com1,stdio" or "bootrom,/var/romfile" 83257293Sneel */ 84257293Sneelint 85257293Sneellpc_device_parse(const char *opts) 86257293Sneel{ 87257293Sneel int unit, error; 88257293Sneel char *str, *cpy, *lpcdev; 89257293Sneel 90257293Sneel error = -1; 91257293Sneel str = cpy = strdup(opts); 92257293Sneel lpcdev = strsep(&str, ","); 93257293Sneel if (lpcdev != NULL) { 94295124Sgrehan if (strcasecmp(lpcdev, "bootrom") == 0) { 95295124Sgrehan romfile = str; 96295124Sgrehan error = 0; 97295124Sgrehan goto done; 98295124Sgrehan } 99257293Sneel for (unit = 0; unit < LPC_UART_NUM; unit++) { 100257293Sneel if (strcasecmp(lpcdev, lpc_uart_names[unit]) == 0) { 101257293Sneel lpc_uart_softc[unit].opts = str; 102257293Sneel error = 0; 103257293Sneel goto done; 104257293Sneel } 105257293Sneel } 106257293Sneel } 107257293Sneel 108257293Sneeldone: 109257293Sneel if (error) 110257293Sneel free(cpy); 111257293Sneel 112257293Sneel return (error); 113257293Sneel} 114257293Sneel 115295124Sgrehanconst char * 116295124Sgrehanlpc_bootrom(void) 117295124Sgrehan{ 118295124Sgrehan 119295124Sgrehan return (romfile); 120295124Sgrehan} 121295124Sgrehan 122257293Sneelstatic void 123257293Sneellpc_uart_intr_assert(void *arg) 124257293Sneel{ 125257293Sneel struct lpc_uart_softc *sc = arg; 126257293Sneel 127257293Sneel assert(sc->irq >= 0); 128257293Sneel 129268891Sjhb vm_isa_pulse_irq(lpc_bridge->pi_vmctx, sc->irq, sc->irq); 130257293Sneel} 131257293Sneel 132257293Sneelstatic void 133257293Sneellpc_uart_intr_deassert(void *arg) 134257293Sneel{ 135261088Sjhb /* 136261088Sjhb * The COM devices on the LPC bus generate edge triggered interrupts, 137261088Sjhb * so nothing more to do here. 138261088Sjhb */ 139257293Sneel} 140257293Sneel 141257293Sneelstatic int 142257293Sneellpc_uart_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 143257293Sneel uint32_t *eax, void *arg) 144257293Sneel{ 145257293Sneel int offset; 146257293Sneel struct lpc_uart_softc *sc = arg; 147257293Sneel 148257293Sneel offset = port - sc->iobase; 149257293Sneel 150268892Sjhb switch (bytes) { 151268892Sjhb case 1: 152268892Sjhb if (in) 153268892Sjhb *eax = uart_read(sc->uart_softc, offset); 154268892Sjhb else 155268892Sjhb uart_write(sc->uart_softc, offset, *eax); 156268892Sjhb break; 157268892Sjhb case 2: 158268892Sjhb if (in) { 159268892Sjhb *eax = uart_read(sc->uart_softc, offset); 160268892Sjhb *eax |= uart_read(sc->uart_softc, offset + 1) << 8; 161268892Sjhb } else { 162268892Sjhb uart_write(sc->uart_softc, offset, *eax); 163268892Sjhb uart_write(sc->uart_softc, offset + 1, *eax >> 8); 164268892Sjhb } 165268892Sjhb break; 166268892Sjhb default: 167268892Sjhb return (-1); 168268892Sjhb } 169257293Sneel 170257293Sneel return (0); 171257293Sneel} 172257293Sneel 173257293Sneelstatic int 174295124Sgrehanlpc_init(struct vmctx *ctx) 175257293Sneel{ 176257293Sneel struct lpc_uart_softc *sc; 177257293Sneel struct inout_port iop; 178257293Sneel const char *name; 179257293Sneel int unit, error; 180257293Sneel 181295124Sgrehan if (romfile != NULL) { 182295124Sgrehan error = bootrom_init(ctx, romfile); 183295124Sgrehan if (error) 184295124Sgrehan return (error); 185295124Sgrehan } 186295124Sgrehan 187257293Sneel /* COM1 and COM2 */ 188257293Sneel for (unit = 0; unit < LPC_UART_NUM; unit++) { 189257293Sneel sc = &lpc_uart_softc[unit]; 190257293Sneel name = lpc_uart_names[unit]; 191257293Sneel 192257293Sneel if (uart_legacy_alloc(unit, &sc->iobase, &sc->irq) != 0) { 193257293Sneel fprintf(stderr, "Unable to allocate resources for " 194257293Sneel "LPC device %s\n", name); 195257293Sneel return (-1); 196257293Sneel } 197268972Sjhb pci_irq_reserve(sc->irq); 198257293Sneel 199257293Sneel sc->uart_softc = uart_init(lpc_uart_intr_assert, 200257293Sneel lpc_uart_intr_deassert, sc); 201257293Sneel 202257293Sneel if (uart_set_backend(sc->uart_softc, sc->opts) != 0) { 203257293Sneel fprintf(stderr, "Unable to initialize backend '%s' " 204257293Sneel "for LPC device %s\n", sc->opts, name); 205257293Sneel return (-1); 206257293Sneel } 207257293Sneel 208257293Sneel bzero(&iop, sizeof(struct inout_port)); 209257293Sneel iop.name = name; 210257293Sneel iop.port = sc->iobase; 211257293Sneel iop.size = UART_IO_BAR_SIZE; 212257293Sneel iop.flags = IOPORT_F_INOUT; 213257293Sneel iop.handler = lpc_uart_io_handler; 214257293Sneel iop.arg = sc; 215257293Sneel 216257293Sneel error = register_inout(&iop); 217257293Sneel assert(error == 0); 218261265Sjhb sc->enabled = 1; 219257293Sneel } 220257293Sneel 221257293Sneel return (0); 222257293Sneel} 223257293Sneel 224257293Sneelstatic void 225261265Sjhbpci_lpc_write_dsdt(struct pci_devinst *pi) 226261265Sjhb{ 227261265Sjhb struct lpc_dsdt **ldpp, *ldp; 228261265Sjhb 229261265Sjhb dsdt_line(""); 230261265Sjhb dsdt_line("Device (ISA)"); 231261265Sjhb dsdt_line("{"); 232261265Sjhb dsdt_line(" Name (_ADR, 0x%04X%04X)", pi->pi_slot, pi->pi_func); 233268972Sjhb dsdt_line(" OperationRegion (LPCR, PCI_Config, 0x00, 0x100)"); 234268972Sjhb dsdt_line(" Field (LPCR, AnyAcc, NoLock, Preserve)"); 235268972Sjhb dsdt_line(" {"); 236268972Sjhb dsdt_line(" Offset (0x60),"); 237268972Sjhb dsdt_line(" PIRA, 8,"); 238268972Sjhb dsdt_line(" PIRB, 8,"); 239268972Sjhb dsdt_line(" PIRC, 8,"); 240268972Sjhb dsdt_line(" PIRD, 8,"); 241268972Sjhb dsdt_line(" Offset (0x68),"); 242268972Sjhb dsdt_line(" PIRE, 8,"); 243268972Sjhb dsdt_line(" PIRF, 8,"); 244268972Sjhb dsdt_line(" PIRG, 8,"); 245268972Sjhb dsdt_line(" PIRH, 8"); 246268972Sjhb dsdt_line(" }"); 247268972Sjhb dsdt_line(""); 248261265Sjhb 249261265Sjhb dsdt_indent(1); 250261265Sjhb SET_FOREACH(ldpp, lpc_dsdt_set) { 251261265Sjhb ldp = *ldpp; 252261265Sjhb ldp->handler(); 253261265Sjhb } 254268891Sjhb 255268891Sjhb dsdt_line(""); 256268891Sjhb dsdt_line("Device (PIC)"); 257268891Sjhb dsdt_line("{"); 258268891Sjhb dsdt_line(" Name (_HID, EisaId (\"PNP0000\"))"); 259268891Sjhb dsdt_line(" Name (_CRS, ResourceTemplate ()"); 260268891Sjhb dsdt_line(" {"); 261268891Sjhb dsdt_indent(2); 262268891Sjhb dsdt_fixed_ioport(IO_ICU1, 2); 263268891Sjhb dsdt_fixed_ioport(IO_ICU2, 2); 264268891Sjhb dsdt_fixed_irq(2); 265268891Sjhb dsdt_unindent(2); 266268891Sjhb dsdt_line(" })"); 267268891Sjhb dsdt_line("}"); 268268891Sjhb 269268891Sjhb dsdt_line(""); 270268891Sjhb dsdt_line("Device (TIMR)"); 271268891Sjhb dsdt_line("{"); 272268891Sjhb dsdt_line(" Name (_HID, EisaId (\"PNP0100\"))"); 273268891Sjhb dsdt_line(" Name (_CRS, ResourceTemplate ()"); 274268891Sjhb dsdt_line(" {"); 275268891Sjhb dsdt_indent(2); 276268891Sjhb dsdt_fixed_ioport(IO_TIMER1_PORT, 4); 277268891Sjhb dsdt_fixed_irq(0); 278268891Sjhb dsdt_unindent(2); 279268891Sjhb dsdt_line(" })"); 280268891Sjhb dsdt_line("}"); 281261265Sjhb dsdt_unindent(1); 282261265Sjhb 283261265Sjhb dsdt_line("}"); 284261265Sjhb} 285261265Sjhb 286261265Sjhbstatic void 287261265Sjhbpci_lpc_sysres_dsdt(void) 288261265Sjhb{ 289261265Sjhb struct lpc_sysres **lspp, *lsp; 290261265Sjhb 291261265Sjhb dsdt_line(""); 292261265Sjhb dsdt_line("Device (SIO)"); 293261265Sjhb dsdt_line("{"); 294261265Sjhb dsdt_line(" Name (_HID, EisaId (\"PNP0C02\"))"); 295261265Sjhb dsdt_line(" Name (_CRS, ResourceTemplate ()"); 296261265Sjhb dsdt_line(" {"); 297261265Sjhb 298261265Sjhb dsdt_indent(2); 299261265Sjhb SET_FOREACH(lspp, lpc_sysres_set) { 300261265Sjhb lsp = *lspp; 301261265Sjhb switch (lsp->type) { 302261265Sjhb case LPC_SYSRES_IO: 303261265Sjhb dsdt_fixed_ioport(lsp->base, lsp->length); 304261265Sjhb break; 305261265Sjhb case LPC_SYSRES_MEM: 306261265Sjhb dsdt_fixed_mem32(lsp->base, lsp->length); 307261265Sjhb break; 308261265Sjhb } 309261265Sjhb } 310261265Sjhb dsdt_unindent(2); 311261265Sjhb 312261265Sjhb dsdt_line(" })"); 313261265Sjhb dsdt_line("}"); 314261265Sjhb} 315261265SjhbLPC_DSDT(pci_lpc_sysres_dsdt); 316261265Sjhb 317261265Sjhbstatic void 318261265Sjhbpci_lpc_uart_dsdt(void) 319261265Sjhb{ 320261265Sjhb struct lpc_uart_softc *sc; 321261265Sjhb int unit; 322261265Sjhb 323261265Sjhb for (unit = 0; unit < LPC_UART_NUM; unit++) { 324261265Sjhb sc = &lpc_uart_softc[unit]; 325261265Sjhb if (!sc->enabled) 326261265Sjhb continue; 327261265Sjhb dsdt_line(""); 328261265Sjhb dsdt_line("Device (%s)", lpc_uart_names[unit]); 329261265Sjhb dsdt_line("{"); 330261265Sjhb dsdt_line(" Name (_HID, EisaId (\"PNP0501\"))"); 331261265Sjhb dsdt_line(" Name (_UID, %d)", unit + 1); 332261265Sjhb dsdt_line(" Name (_CRS, ResourceTemplate ()"); 333261265Sjhb dsdt_line(" {"); 334261265Sjhb dsdt_indent(2); 335261265Sjhb dsdt_fixed_ioport(sc->iobase, UART_IO_BAR_SIZE); 336261265Sjhb dsdt_fixed_irq(sc->irq); 337261265Sjhb dsdt_unindent(2); 338261265Sjhb dsdt_line(" })"); 339261265Sjhb dsdt_line("}"); 340261265Sjhb } 341261265Sjhb} 342261265SjhbLPC_DSDT(pci_lpc_uart_dsdt); 343261265Sjhb 344268972Sjhbstatic int 345268972Sjhbpci_lpc_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 346268972Sjhb int coff, int bytes, uint32_t val) 347268972Sjhb{ 348268972Sjhb int pirq_pin; 349268972Sjhb 350268972Sjhb if (bytes == 1) { 351268972Sjhb pirq_pin = 0; 352268972Sjhb if (coff >= 0x60 && coff <= 0x63) 353268972Sjhb pirq_pin = coff - 0x60 + 1; 354268972Sjhb if (coff >= 0x68 && coff <= 0x6b) 355268972Sjhb pirq_pin = coff - 0x68 + 5; 356268972Sjhb if (pirq_pin != 0) { 357268972Sjhb pirq_write(ctx, pirq_pin, val); 358268972Sjhb pci_set_cfgdata8(pi, coff, pirq_read(pirq_pin)); 359268972Sjhb return (0); 360268972Sjhb } 361268972Sjhb } 362268972Sjhb return (-1); 363268972Sjhb} 364268972Sjhb 365261265Sjhbstatic void 366257293Sneelpci_lpc_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 367257293Sneel int baridx, uint64_t offset, int size, uint64_t value) 368257293Sneel{ 369257293Sneel} 370257293Sneel 371268972Sjhbstatic uint64_t 372257293Sneelpci_lpc_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 373257293Sneel int baridx, uint64_t offset, int size) 374257293Sneel{ 375257293Sneel return (0); 376257293Sneel} 377257293Sneel 378257293Sneel#define LPC_DEV 0x7000 379257293Sneel#define LPC_VENDOR 0x8086 380257293Sneel 381257293Sneelstatic int 382257293Sneelpci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 383257293Sneel{ 384268972Sjhb 385257293Sneel /* 386257293Sneel * Do not allow more than one LPC bridge to be configured. 387257293Sneel */ 388268887Sjhb if (lpc_bridge != NULL) { 389268887Sjhb fprintf(stderr, "Only one LPC bridge is allowed.\n"); 390257293Sneel return (-1); 391268887Sjhb } 392257293Sneel 393268887Sjhb /* 394268887Sjhb * Enforce that the LPC can only be configured on bus 0. This 395268887Sjhb * simplifies the ACPI DSDT because it can provide a decode for 396268887Sjhb * all legacy i/o ports behind bus 0. 397268887Sjhb */ 398268887Sjhb if (pi->pi_bus != 0) { 399268887Sjhb fprintf(stderr, "LPC bridge can be present only on bus 0.\n"); 400268887Sjhb return (-1); 401268887Sjhb } 402268887Sjhb 403295124Sgrehan if (lpc_init(ctx) != 0) 404257293Sneel return (-1); 405257293Sneel 406257293Sneel /* initialize config space */ 407257293Sneel pci_set_cfgdata16(pi, PCIR_DEVICE, LPC_DEV); 408257293Sneel pci_set_cfgdata16(pi, PCIR_VENDOR, LPC_VENDOR); 409257293Sneel pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE); 410257293Sneel pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_ISA); 411257293Sneel 412257293Sneel lpc_bridge = pi; 413257293Sneel 414257293Sneel return (0); 415257293Sneel} 416257293Sneel 417268972Sjhbchar * 418268972Sjhblpc_pirq_name(int pin) 419268972Sjhb{ 420268972Sjhb char *name; 421268972Sjhb 422268972Sjhb if (lpc_bridge == NULL) 423268972Sjhb return (NULL); 424268972Sjhb asprintf(&name, "\\_SB.PC00.ISA.LNK%c,", 'A' + pin - 1); 425268972Sjhb return (name); 426268972Sjhb} 427268972Sjhb 428268972Sjhbvoid 429268972Sjhblpc_pirq_routed(void) 430268972Sjhb{ 431268972Sjhb int pin; 432268972Sjhb 433268972Sjhb if (lpc_bridge == NULL) 434268972Sjhb return; 435268972Sjhb 436268972Sjhb for (pin = 0; pin < 4; pin++) 437268972Sjhb pci_set_cfgdata8(lpc_bridge, 0x60 + pin, pirq_read(pin + 1)); 438268972Sjhb for (pin = 0; pin < 4; pin++) 439268972Sjhb pci_set_cfgdata8(lpc_bridge, 0x68 + pin, pirq_read(pin + 5)); 440268972Sjhb} 441268972Sjhb 442257293Sneelstruct pci_devemu pci_de_lpc = { 443257293Sneel .pe_emu = "lpc", 444257293Sneel .pe_init = pci_lpc_init, 445261265Sjhb .pe_write_dsdt = pci_lpc_write_dsdt, 446268972Sjhb .pe_cfgwrite = pci_lpc_cfgwrite, 447257293Sneel .pe_barwrite = pci_lpc_write, 448257293Sneel .pe_barread = pci_lpc_read 449257293Sneel}; 450257293SneelPCI_EMUL_SET(pci_de_lpc); 451