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