1/*- 2 * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 3 * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD$"); 32 33#include <sys/types.h> 34#include <machine/vmm.h> 35 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39 40#include <vmmapi.h> 41 42#include "acpi.h" 43#include "inout.h" 44#include "pci_emul.h" 45#include "pci_irq.h" 46#include "pci_lpc.h" 47#include "uart_emul.h" 48 49#define IO_ICU1 0x20 50#define IO_ICU2 0xA0 51 52SET_DECLARE(lpc_dsdt_set, struct lpc_dsdt); 53SET_DECLARE(lpc_sysres_set, struct lpc_sysres); 54 55#define ELCR_PORT 0x4d0 56SYSRES_IO(ELCR_PORT, 2); 57 58#define IO_TIMER1_PORT 0x40 59 60#define NMISC_PORT 0x61 61SYSRES_IO(NMISC_PORT, 1); 62 63static struct pci_devinst *lpc_bridge; 64 65#define LPC_UART_NUM 2 66static struct lpc_uart_softc { 67 struct uart_softc *uart_softc; 68 const char *opts; 69 int iobase; 70 int irq; 71 int enabled; 72} lpc_uart_softc[LPC_UART_NUM]; 73 74static const char *lpc_uart_names[LPC_UART_NUM] = { "COM1", "COM2" }; 75 76/* 77 * LPC device configuration is in the following form: 78 * <lpc_device_name>[,<options>] 79 * For e.g. "com1,stdio" 80 */ 81int 82lpc_device_parse(const char *opts) 83{ 84 int unit, error; 85 char *str, *cpy, *lpcdev; 86 87 error = -1; 88 str = cpy = strdup(opts); 89 lpcdev = strsep(&str, ","); 90 if (lpcdev != NULL) { 91 for (unit = 0; unit < LPC_UART_NUM; unit++) { 92 if (strcasecmp(lpcdev, lpc_uart_names[unit]) == 0) { 93 lpc_uart_softc[unit].opts = str; 94 error = 0; 95 goto done; 96 } 97 } 98 } 99 100done: 101 if (error) 102 free(cpy); 103 104 return (error); 105} 106 107static void 108lpc_uart_intr_assert(void *arg) 109{ 110 struct lpc_uart_softc *sc = arg; 111 112 assert(sc->irq >= 0); 113 114 vm_isa_pulse_irq(lpc_bridge->pi_vmctx, sc->irq, sc->irq); 115} 116 117static void 118lpc_uart_intr_deassert(void *arg) 119{ 120 /* 121 * The COM devices on the LPC bus generate edge triggered interrupts, 122 * so nothing more to do here. 123 */ 124} 125 126static int 127lpc_uart_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 128 uint32_t *eax, void *arg) 129{ 130 int offset; 131 struct lpc_uart_softc *sc = arg; 132 133 offset = port - sc->iobase; 134 135 switch (bytes) { 136 case 1: 137 if (in) 138 *eax = uart_read(sc->uart_softc, offset); 139 else 140 uart_write(sc->uart_softc, offset, *eax); 141 break; 142 case 2: 143 if (in) { 144 *eax = uart_read(sc->uart_softc, offset); 145 *eax |= uart_read(sc->uart_softc, offset + 1) << 8; 146 } else { 147 uart_write(sc->uart_softc, offset, *eax); 148 uart_write(sc->uart_softc, offset + 1, *eax >> 8); 149 } 150 break; 151 default: 152 return (-1); 153 } 154 155 return (0); 156} 157 158static int 159lpc_init(void) 160{ 161 struct lpc_uart_softc *sc; 162 struct inout_port iop; 163 const char *name; 164 int unit, error; 165 166 /* COM1 and COM2 */ 167 for (unit = 0; unit < LPC_UART_NUM; unit++) { 168 sc = &lpc_uart_softc[unit]; 169 name = lpc_uart_names[unit]; 170 171 if (uart_legacy_alloc(unit, &sc->iobase, &sc->irq) != 0) { 172 fprintf(stderr, "Unable to allocate resources for " 173 "LPC device %s\n", name); 174 return (-1); 175 } 176 pci_irq_reserve(sc->irq); 177 178 sc->uart_softc = uart_init(lpc_uart_intr_assert, 179 lpc_uart_intr_deassert, sc); 180 181 if (uart_set_backend(sc->uart_softc, sc->opts) != 0) { 182 fprintf(stderr, "Unable to initialize backend '%s' " 183 "for LPC device %s\n", sc->opts, name); 184 return (-1); 185 } 186 187 bzero(&iop, sizeof(struct inout_port)); 188 iop.name = name; 189 iop.port = sc->iobase; 190 iop.size = UART_IO_BAR_SIZE; 191 iop.flags = IOPORT_F_INOUT; 192 iop.handler = lpc_uart_io_handler; 193 iop.arg = sc; 194 195 error = register_inout(&iop); 196 assert(error == 0); 197 sc->enabled = 1; 198 } 199 200 return (0); 201} 202 203static void 204pci_lpc_write_dsdt(struct pci_devinst *pi) 205{ 206 struct lpc_dsdt **ldpp, *ldp; 207 208 dsdt_line(""); 209 dsdt_line("Device (ISA)"); 210 dsdt_line("{"); 211 dsdt_line(" Name (_ADR, 0x%04X%04X)", pi->pi_slot, pi->pi_func); 212 dsdt_line(" OperationRegion (LPCR, PCI_Config, 0x00, 0x100)"); 213 dsdt_line(" Field (LPCR, AnyAcc, NoLock, Preserve)"); 214 dsdt_line(" {"); 215 dsdt_line(" Offset (0x60),"); 216 dsdt_line(" PIRA, 8,"); 217 dsdt_line(" PIRB, 8,"); 218 dsdt_line(" PIRC, 8,"); 219 dsdt_line(" PIRD, 8,"); 220 dsdt_line(" Offset (0x68),"); 221 dsdt_line(" PIRE, 8,"); 222 dsdt_line(" PIRF, 8,"); 223 dsdt_line(" PIRG, 8,"); 224 dsdt_line(" PIRH, 8"); 225 dsdt_line(" }"); 226 dsdt_line(""); 227 228 dsdt_indent(1); 229 SET_FOREACH(ldpp, lpc_dsdt_set) { 230 ldp = *ldpp; 231 ldp->handler(); 232 } 233 234 dsdt_line(""); 235 dsdt_line("Device (PIC)"); 236 dsdt_line("{"); 237 dsdt_line(" Name (_HID, EisaId (\"PNP0000\"))"); 238 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 239 dsdt_line(" {"); 240 dsdt_indent(2); 241 dsdt_fixed_ioport(IO_ICU1, 2); 242 dsdt_fixed_ioport(IO_ICU2, 2); 243 dsdt_fixed_irq(2); 244 dsdt_unindent(2); 245 dsdt_line(" })"); 246 dsdt_line("}"); 247 248 dsdt_line(""); 249 dsdt_line("Device (TIMR)"); 250 dsdt_line("{"); 251 dsdt_line(" Name (_HID, EisaId (\"PNP0100\"))"); 252 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 253 dsdt_line(" {"); 254 dsdt_indent(2); 255 dsdt_fixed_ioport(IO_TIMER1_PORT, 4); 256 dsdt_fixed_irq(0); 257 dsdt_unindent(2); 258 dsdt_line(" })"); 259 dsdt_line("}"); 260 dsdt_unindent(1); 261 262 dsdt_line("}"); 263} 264 265static void 266pci_lpc_sysres_dsdt(void) 267{ 268 struct lpc_sysres **lspp, *lsp; 269 270 dsdt_line(""); 271 dsdt_line("Device (SIO)"); 272 dsdt_line("{"); 273 dsdt_line(" Name (_HID, EisaId (\"PNP0C02\"))"); 274 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 275 dsdt_line(" {"); 276 277 dsdt_indent(2); 278 SET_FOREACH(lspp, lpc_sysres_set) { 279 lsp = *lspp; 280 switch (lsp->type) { 281 case LPC_SYSRES_IO: 282 dsdt_fixed_ioport(lsp->base, lsp->length); 283 break; 284 case LPC_SYSRES_MEM: 285 dsdt_fixed_mem32(lsp->base, lsp->length); 286 break; 287 } 288 } 289 dsdt_unindent(2); 290 291 dsdt_line(" })"); 292 dsdt_line("}"); 293} 294LPC_DSDT(pci_lpc_sysres_dsdt); 295 296static void 297pci_lpc_uart_dsdt(void) 298{ 299 struct lpc_uart_softc *sc; 300 int unit; 301 302 for (unit = 0; unit < LPC_UART_NUM; unit++) { 303 sc = &lpc_uart_softc[unit]; 304 if (!sc->enabled) 305 continue; 306 dsdt_line(""); 307 dsdt_line("Device (%s)", lpc_uart_names[unit]); 308 dsdt_line("{"); 309 dsdt_line(" Name (_HID, EisaId (\"PNP0501\"))"); 310 dsdt_line(" Name (_UID, %d)", unit + 1); 311 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 312 dsdt_line(" {"); 313 dsdt_indent(2); 314 dsdt_fixed_ioport(sc->iobase, UART_IO_BAR_SIZE); 315 dsdt_fixed_irq(sc->irq); 316 dsdt_unindent(2); 317 dsdt_line(" })"); 318 dsdt_line("}"); 319 } 320} 321LPC_DSDT(pci_lpc_uart_dsdt); 322 323static int 324pci_lpc_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 325 int coff, int bytes, uint32_t val) 326{ 327 int pirq_pin; 328 329 if (bytes == 1) { 330 pirq_pin = 0; 331 if (coff >= 0x60 && coff <= 0x63) 332 pirq_pin = coff - 0x60 + 1; 333 if (coff >= 0x68 && coff <= 0x6b) 334 pirq_pin = coff - 0x68 + 5; 335 if (pirq_pin != 0) { 336 pirq_write(ctx, pirq_pin, val); 337 pci_set_cfgdata8(pi, coff, pirq_read(pirq_pin)); 338 return (0); 339 } 340 } 341 return (-1); 342} 343 344static void 345pci_lpc_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 346 int baridx, uint64_t offset, int size, uint64_t value) 347{ 348} 349 350static uint64_t 351pci_lpc_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, 352 int baridx, uint64_t offset, int size) 353{ 354 return (0); 355} 356 357#define LPC_DEV 0x7000 358#define LPC_VENDOR 0x8086 359 360static int 361pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 362{ 363 364 /* 365 * Do not allow more than one LPC bridge to be configured. 366 */ 367 if (lpc_bridge != NULL) { 368 fprintf(stderr, "Only one LPC bridge is allowed.\n"); 369 return (-1); 370 } 371 372 /* 373 * Enforce that the LPC can only be configured on bus 0. This 374 * simplifies the ACPI DSDT because it can provide a decode for 375 * all legacy i/o ports behind bus 0. 376 */ 377 if (pi->pi_bus != 0) { 378 fprintf(stderr, "LPC bridge can be present only on bus 0.\n"); 379 return (-1); 380 } 381 382 if (lpc_init() != 0) 383 return (-1); 384 385 /* initialize config space */ 386 pci_set_cfgdata16(pi, PCIR_DEVICE, LPC_DEV); 387 pci_set_cfgdata16(pi, PCIR_VENDOR, LPC_VENDOR); 388 pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE); 389 pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_ISA); 390 391 lpc_bridge = pi; 392 393 return (0); 394} 395 396char * 397lpc_pirq_name(int pin) 398{ 399 char *name; 400 401 if (lpc_bridge == NULL) 402 return (NULL); 403 asprintf(&name, "\\_SB.PC00.ISA.LNK%c,", 'A' + pin - 1); 404 return (name); 405} 406 407void 408lpc_pirq_routed(void) 409{ 410 int pin; 411 412 if (lpc_bridge == NULL) 413 return; 414 415 for (pin = 0; pin < 4; pin++) 416 pci_set_cfgdata8(lpc_bridge, 0x60 + pin, pirq_read(pin + 1)); 417 for (pin = 0; pin < 4; pin++) 418 pci_set_cfgdata8(lpc_bridge, 0x68 + pin, pirq_read(pin + 5)); 419} 420 421struct pci_devemu pci_de_lpc = { 422 .pe_emu = "lpc", 423 .pe_init = pci_lpc_init, 424 .pe_write_dsdt = pci_lpc_write_dsdt, 425 .pe_cfgwrite = pci_lpc_cfgwrite, 426 .pe_barwrite = pci_lpc_write, 427 .pe_barread = pci_lpc_read 428}; 429PCI_EMUL_SET(pci_de_lpc); 430