xref: /illumos-gate/usr/src/cmd/bhyve/pci_lpc.c (revision 32640292)
1bf21cd93STycho Nightingale /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
34c87aefeSPatrick Mooney  *
4bf21cd93STycho Nightingale  * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
5bf21cd93STycho Nightingale  * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
6bf21cd93STycho Nightingale  * All rights reserved.
7bf21cd93STycho Nightingale  *
8bf21cd93STycho Nightingale  * Redistribution and use in source and binary forms, with or without
9bf21cd93STycho Nightingale  * modification, are permitted provided that the following conditions
10bf21cd93STycho Nightingale  * are met:
11bf21cd93STycho Nightingale  * 1. Redistributions of source code must retain the above copyright
12bf21cd93STycho Nightingale  *    notice, this list of conditions and the following disclaimer.
13bf21cd93STycho Nightingale  * 2. Redistributions in binary form must reproduce the above copyright
14bf21cd93STycho Nightingale  *    notice, this list of conditions and the following disclaimer in the
15bf21cd93STycho Nightingale  *    documentation and/or other materials provided with the distribution.
16bf21cd93STycho Nightingale  *
17bf21cd93STycho Nightingale  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
18bf21cd93STycho Nightingale  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19bf21cd93STycho Nightingale  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20bf21cd93STycho Nightingale  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
21bf21cd93STycho Nightingale  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22bf21cd93STycho Nightingale  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23bf21cd93STycho Nightingale  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24bf21cd93STycho Nightingale  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25bf21cd93STycho Nightingale  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26bf21cd93STycho Nightingale  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27bf21cd93STycho Nightingale  * SUCH DAMAGE.
284c87aefeSPatrick Mooney  */
294c87aefeSPatrick Mooney 
304c87aefeSPatrick Mooney /*
314c87aefeSPatrick Mooney  * Copyright 2018 Joyent, Inc.
32bf21cd93STycho Nightingale  */
33bf21cd93STycho Nightingale 
34bf21cd93STycho Nightingale #include <sys/cdefs.h>
35bf21cd93STycho Nightingale 
36bf21cd93STycho Nightingale #include <sys/types.h>
37bf21cd93STycho Nightingale #include <machine/vmm.h>
38bf21cd93STycho Nightingale 
39d7b72f7bSAndy Fiddaman #include <err.h>
40bf21cd93STycho Nightingale #include <stdio.h>
41bf21cd93STycho Nightingale #include <stdlib.h>
42bf21cd93STycho Nightingale #include <string.h>
43bf21cd93STycho Nightingale 
44bf21cd93STycho Nightingale #include <vmmapi.h>
45bf21cd93STycho Nightingale 
46bf21cd93STycho Nightingale #include "acpi.h"
47154972afSPatrick Mooney #include "debug.h"
484c87aefeSPatrick Mooney #include "bootrom.h"
492b948146SAndy Fiddaman #include "config.h"
50bf21cd93STycho Nightingale #include "inout.h"
51bf21cd93STycho Nightingale #include "pci_emul.h"
52bf21cd93STycho Nightingale #include "pci_irq.h"
53bf21cd93STycho Nightingale #include "pci_lpc.h"
54*32640292SAndy Fiddaman #include "pci_passthru.h"
556960cd89SAndy Fiddaman #include "pctestdev.h"
56*32640292SAndy Fiddaman #include "tpm_device.h"
57bf21cd93STycho Nightingale #include "uart_emul.h"
58bf21cd93STycho Nightingale 
59bf21cd93STycho Nightingale #define	IO_ICU1		0x20
60bf21cd93STycho Nightingale #define	IO_ICU2		0xA0
61bf21cd93STycho Nightingale 
62bf21cd93STycho Nightingale SET_DECLARE(lpc_dsdt_set, struct lpc_dsdt);
63bf21cd93STycho Nightingale SET_DECLARE(lpc_sysres_set, struct lpc_sysres);
64bf21cd93STycho Nightingale 
65bf21cd93STycho Nightingale #define	ELCR_PORT	0x4d0
66bf21cd93STycho Nightingale SYSRES_IO(ELCR_PORT, 2);
67bf21cd93STycho Nightingale 
68bf21cd93STycho Nightingale #define	IO_TIMER1_PORT	0x40
69bf21cd93STycho Nightingale 
70bf21cd93STycho Nightingale #define	NMISC_PORT	0x61
71bf21cd93STycho Nightingale SYSRES_IO(NMISC_PORT, 1);
72bf21cd93STycho Nightingale 
73bf21cd93STycho Nightingale static struct pci_devinst *lpc_bridge;
74bf21cd93STycho Nightingale 
752b948146SAndy Fiddaman #define	LPC_UART_NUM	4
76bf21cd93STycho Nightingale static struct lpc_uart_softc {
77bf21cd93STycho Nightingale 	struct uart_softc *uart_softc;
78bf21cd93STycho Nightingale 	int	iobase;
79bf21cd93STycho Nightingale 	int	irq;
80bf21cd93STycho Nightingale 	int	enabled;
81bf21cd93STycho Nightingale } lpc_uart_softc[LPC_UART_NUM];
82bf21cd93STycho Nightingale 
832b948146SAndy Fiddaman static const char *lpc_uart_names[LPC_UART_NUM] = {
842b948146SAndy Fiddaman 	"com1", "com2", "com3", "com4"
852b948146SAndy Fiddaman };
86bf21cd93STycho Nightingale 
872b948146SAndy Fiddaman static const char *lpc_uart_acpi_names[LPC_UART_NUM] = {
882b948146SAndy Fiddaman 	"COM1", "COM2", "COM3", "COM4"
892b948146SAndy Fiddaman };
906960cd89SAndy Fiddaman 
91bf21cd93STycho Nightingale /*
92bf21cd93STycho Nightingale  * LPC device configuration is in the following form:
93bf21cd93STycho Nightingale  * <lpc_device_name>[,<options>]
944c87aefeSPatrick Mooney  * For e.g. "com1,stdio" or "bootrom,/var/romfile"
95bf21cd93STycho Nightingale  */
96bf21cd93STycho Nightingale int
lpc_device_parse(const char * opts)97bf21cd93STycho Nightingale lpc_device_parse(const char *opts)
98bf21cd93STycho Nightingale {
99bf21cd93STycho Nightingale 	int unit, error;
1002b948146SAndy Fiddaman 	char *str, *cpy, *lpcdev, *node_name;
101*32640292SAndy Fiddaman 	const char *romfile, *varfile, *tpm_type, *tpm_path;
102bf21cd93STycho Nightingale 
103bf21cd93STycho Nightingale 	error = -1;
104bf21cd93STycho Nightingale 	str = cpy = strdup(opts);
105bf21cd93STycho Nightingale 	lpcdev = strsep(&str, ",");
106bf21cd93STycho Nightingale 	if (lpcdev != NULL) {
1074c87aefeSPatrick Mooney 		if (strcasecmp(lpcdev, "bootrom") == 0) {
108d7b72f7bSAndy Fiddaman 			romfile = strsep(&str, ",");
109d7b72f7bSAndy Fiddaman 			if (romfile == NULL) {
110d7b72f7bSAndy Fiddaman 				errx(4, "invalid bootrom option \"%s\"", opts);
111d7b72f7bSAndy Fiddaman 			}
112d7b72f7bSAndy Fiddaman 			set_config_value("lpc.bootrom", romfile);
113d7b72f7bSAndy Fiddaman 
114d7b72f7bSAndy Fiddaman 			varfile = strsep(&str, ",");
115*32640292SAndy Fiddaman 			if (varfile == NULL) {
116*32640292SAndy Fiddaman 				error = 0;
117*32640292SAndy Fiddaman 				goto done;
118*32640292SAndy Fiddaman 			}
119*32640292SAndy Fiddaman 			if (strchr(varfile, '=') == NULL) {
120d7b72f7bSAndy Fiddaman 				set_config_value("lpc.bootvars", varfile);
121*32640292SAndy Fiddaman 			} else {
122*32640292SAndy Fiddaman 				/* varfile doesn't exist, it's another config
123*32640292SAndy Fiddaman 				 * option */
124*32640292SAndy Fiddaman 				pci_parse_legacy_config(find_config_node("lpc"),
125*32640292SAndy Fiddaman 				    varfile);
126*32640292SAndy Fiddaman 			}
127*32640292SAndy Fiddaman 
128*32640292SAndy Fiddaman 			pci_parse_legacy_config(find_config_node("lpc"), str);
129*32640292SAndy Fiddaman 			error = 0;
130*32640292SAndy Fiddaman 			goto done;
131*32640292SAndy Fiddaman 		}
132*32640292SAndy Fiddaman 		if (strcasecmp(lpcdev, "tpm") == 0) {
133*32640292SAndy Fiddaman 			nvlist_t *nvl = create_config_node("tpm");
134*32640292SAndy Fiddaman 
135*32640292SAndy Fiddaman 			tpm_type = strsep(&str, ",");
136*32640292SAndy Fiddaman 			if (tpm_type == NULL) {
137*32640292SAndy Fiddaman 				errx(4, "invalid tpm type \"%s\"", opts);
138*32640292SAndy Fiddaman 			}
139*32640292SAndy Fiddaman 			set_config_value_node(nvl, "type", tpm_type);
140*32640292SAndy Fiddaman 
141*32640292SAndy Fiddaman 			tpm_path = strsep(&str, ",");
142*32640292SAndy Fiddaman 			if (tpm_path == NULL) {
143*32640292SAndy Fiddaman 				errx(4, "invalid tpm path \"%s\"", opts);
144d7b72f7bSAndy Fiddaman 			}
145*32640292SAndy Fiddaman 			set_config_value_node(nvl, "path", tpm_path);
146*32640292SAndy Fiddaman 
147*32640292SAndy Fiddaman 			pci_parse_legacy_config(find_config_node("tpm"), str);
148d7b72f7bSAndy Fiddaman 
149*32640292SAndy Fiddaman 			set_config_value_node_if_unset(nvl, "version", "2.0");
1504c87aefeSPatrick Mooney 			error = 0;
1514c87aefeSPatrick Mooney 			goto done;
1524c87aefeSPatrick Mooney 		}
153bf21cd93STycho Nightingale 		for (unit = 0; unit < LPC_UART_NUM; unit++) {
154bf21cd93STycho Nightingale 			if (strcasecmp(lpcdev, lpc_uart_names[unit]) == 0) {
1552b948146SAndy Fiddaman 				asprintf(&node_name, "lpc.%s.path",
1562b948146SAndy Fiddaman 				    lpc_uart_names[unit]);
1572b948146SAndy Fiddaman 				set_config_value(node_name, str);
1582b948146SAndy Fiddaman 				free(node_name);
159bf21cd93STycho Nightingale 				error = 0;
160bf21cd93STycho Nightingale 				goto done;
161bf21cd93STycho Nightingale 			}
162bf21cd93STycho Nightingale 		}
1636960cd89SAndy Fiddaman 		if (strcasecmp(lpcdev, pctestdev_getname()) == 0) {
1642b948146SAndy Fiddaman 			asprintf(&node_name, "lpc.%s", pctestdev_getname());
1652b948146SAndy Fiddaman 			set_config_bool(node_name, true);
1662b948146SAndy Fiddaman 			free(node_name);
1672b948146SAndy Fiddaman 			error = 0;
1682b948146SAndy Fiddaman 			goto done;
1696960cd89SAndy Fiddaman 		}
170bf21cd93STycho Nightingale 	}
171bf21cd93STycho Nightingale 
172bf21cd93STycho Nightingale done:
1732b948146SAndy Fiddaman 	free(cpy);
174bf21cd93STycho Nightingale 
175bf21cd93STycho Nightingale 	return (error);
176bf21cd93STycho Nightingale }
177bf21cd93STycho Nightingale 
1784c87aefeSPatrick Mooney void
lpc_print_supported_devices(void)1794f3f3e9aSAndy Fiddaman lpc_print_supported_devices(void)
1804c87aefeSPatrick Mooney {
1814c87aefeSPatrick Mooney 	size_t i;
1824c87aefeSPatrick Mooney 
1834c87aefeSPatrick Mooney 	printf("bootrom\n");
1844c87aefeSPatrick Mooney 	for (i = 0; i < LPC_UART_NUM; i++)
1854c87aefeSPatrick Mooney 		printf("%s\n", lpc_uart_names[i]);
186*32640292SAndy Fiddaman 	printf("tpm\n");
1876960cd89SAndy Fiddaman 	printf("%s\n", pctestdev_getname());
1884c87aefeSPatrick Mooney }
1894c87aefeSPatrick Mooney 
1904c87aefeSPatrick Mooney const char *
lpc_bootrom(void)1914c87aefeSPatrick Mooney lpc_bootrom(void)
1924c87aefeSPatrick Mooney {
1934c87aefeSPatrick Mooney 
1942b948146SAndy Fiddaman 	return (get_config_value("lpc.bootrom"));
1954c87aefeSPatrick Mooney }
1964c87aefeSPatrick Mooney 
197*32640292SAndy Fiddaman const char *
lpc_fwcfg(void)198*32640292SAndy Fiddaman lpc_fwcfg(void)
199*32640292SAndy Fiddaman {
200*32640292SAndy Fiddaman 	return (get_config_value("lpc.fwcfg"));
201*32640292SAndy Fiddaman }
202*32640292SAndy Fiddaman 
203bf21cd93STycho Nightingale static void
lpc_uart_intr_assert(void * arg)204bf21cd93STycho Nightingale lpc_uart_intr_assert(void *arg)
205bf21cd93STycho Nightingale {
206bf21cd93STycho Nightingale 	struct lpc_uart_softc *sc = arg;
207bf21cd93STycho Nightingale 
208bf21cd93STycho Nightingale 	assert(sc->irq >= 0);
209bf21cd93STycho Nightingale 
210bf21cd93STycho Nightingale 	vm_isa_pulse_irq(lpc_bridge->pi_vmctx, sc->irq, sc->irq);
211bf21cd93STycho Nightingale }
212bf21cd93STycho Nightingale 
213bf21cd93STycho Nightingale static void
lpc_uart_intr_deassert(void * arg __unused)21459d65d31SAndy Fiddaman lpc_uart_intr_deassert(void *arg __unused)
215bf21cd93STycho Nightingale {
2166dc98349SAndy Fiddaman 	/*
217bf21cd93STycho Nightingale 	 * The COM devices on the LPC bus generate edge triggered interrupts,
218bf21cd93STycho Nightingale 	 * so nothing more to do here.
219bf21cd93STycho Nightingale 	 */
220bf21cd93STycho Nightingale }
221bf21cd93STycho Nightingale 
222bf21cd93STycho Nightingale static int
lpc_uart_io_handler(struct vmctx * ctx __unused,int in,int port,int bytes,uint32_t * eax,void * arg)22359d65d31SAndy Fiddaman lpc_uart_io_handler(struct vmctx *ctx __unused, int in,
22459d65d31SAndy Fiddaman     int port, int bytes, uint32_t *eax, void *arg)
225bf21cd93STycho Nightingale {
226bf21cd93STycho Nightingale 	int offset;
227bf21cd93STycho Nightingale 	struct lpc_uart_softc *sc = arg;
228bf21cd93STycho Nightingale 
229bf21cd93STycho Nightingale 	offset = port - sc->iobase;
230bf21cd93STycho Nightingale 
231bf21cd93STycho Nightingale 	switch (bytes) {
232bf21cd93STycho Nightingale 	case 1:
233bf21cd93STycho Nightingale 		if (in)
234bf21cd93STycho Nightingale 			*eax = uart_read(sc->uart_softc, offset);
235bf21cd93STycho Nightingale 		else
236bf21cd93STycho Nightingale 			uart_write(sc->uart_softc, offset, *eax);
237bf21cd93STycho Nightingale 		break;
238bf21cd93STycho Nightingale 	case 2:
239bf21cd93STycho Nightingale 		if (in) {
240bf21cd93STycho Nightingale 			*eax = uart_read(sc->uart_softc, offset);
241bf21cd93STycho Nightingale 			*eax |= uart_read(sc->uart_softc, offset + 1) << 8;
242bf21cd93STycho Nightingale 		} else {
243bf21cd93STycho Nightingale 			uart_write(sc->uart_softc, offset, *eax);
244bf21cd93STycho Nightingale 			uart_write(sc->uart_softc, offset + 1, *eax >> 8);
245bf21cd93STycho Nightingale 		}
246bf21cd93STycho Nightingale 		break;
2474c87aefeSPatrick Mooney #ifndef __FreeBSD__
2484c87aefeSPatrick Mooney 	case 4:
2494c87aefeSPatrick Mooney 		if (in) {
2504c87aefeSPatrick Mooney 			*eax = uart_read(sc->uart_softc, offset);
2514c87aefeSPatrick Mooney 			*eax |= uart_read(sc->uart_softc, offset + 1) << 8;
2524c87aefeSPatrick Mooney 			*eax |= uart_read(sc->uart_softc, offset + 2) << 16;
2534c87aefeSPatrick Mooney 			*eax |= uart_read(sc->uart_softc, offset + 3) << 24;
2544c87aefeSPatrick Mooney 		} else {
2554c87aefeSPatrick Mooney 			uart_write(sc->uart_softc, offset, *eax);
2564c87aefeSPatrick Mooney 			uart_write(sc->uart_softc, offset + 1, *eax >> 8);
2574c87aefeSPatrick Mooney 			uart_write(sc->uart_softc, offset + 2, *eax >> 16);
2584c87aefeSPatrick Mooney 			uart_write(sc->uart_softc, offset + 3, *eax >> 24);
2594c87aefeSPatrick Mooney 		}
2604c87aefeSPatrick Mooney 		break;
2614c87aefeSPatrick Mooney #endif
262bf21cd93STycho Nightingale 	default:
263bf21cd93STycho Nightingale 		return (-1);
264bf21cd93STycho Nightingale 	}
265bf21cd93STycho Nightingale 
266bf21cd93STycho Nightingale 	return (0);
267bf21cd93STycho Nightingale }
268bf21cd93STycho Nightingale 
269bf21cd93STycho Nightingale static int
lpc_init(struct vmctx * ctx)2704c87aefeSPatrick Mooney lpc_init(struct vmctx *ctx)
271bf21cd93STycho Nightingale {
272bf21cd93STycho Nightingale 	struct lpc_uart_softc *sc;
273bf21cd93STycho Nightingale 	struct inout_port iop;
274d7b72f7bSAndy Fiddaman 	const char *backend, *name;
2752b948146SAndy Fiddaman 	char *node_name;
276bf21cd93STycho Nightingale 	int unit, error;
277d7b72f7bSAndy Fiddaman 	const nvlist_t *nvl;
278bf21cd93STycho Nightingale 
279d7b72f7bSAndy Fiddaman 	nvl = find_config_node("lpc");
280d7b72f7bSAndy Fiddaman #ifndef __FreeBSD__
281d7b72f7bSAndy Fiddaman 	if (nvl != NULL && nvlist_exists((nvlist_t *)nvl, "bootrom")) {
282d7b72f7bSAndy Fiddaman 		error = bootrom_loadrom(ctx, nvl);
2834c87aefeSPatrick Mooney 		if (error)
2844c87aefeSPatrick Mooney 			return (error);
2854c87aefeSPatrick Mooney 	}
286d7b72f7bSAndy Fiddaman #else
287d7b72f7bSAndy Fiddaman 	if (nvl != NULL && nvlist_exists(nvl, "bootrom")) {
288d7b72f7bSAndy Fiddaman 		error = bootrom_loadrom(ctx, nvl);
289d7b72f7bSAndy Fiddaman 		if (error)
290d7b72f7bSAndy Fiddaman 			return (error);
291d7b72f7bSAndy Fiddaman 	}
292d7b72f7bSAndy Fiddaman #endif
2934c87aefeSPatrick Mooney 
294bf21cd93STycho Nightingale 	/* COM1 and COM2 */
295bf21cd93STycho Nightingale 	for (unit = 0; unit < LPC_UART_NUM; unit++) {
296bf21cd93STycho Nightingale 		sc = &lpc_uart_softc[unit];
297bf21cd93STycho Nightingale 		name = lpc_uart_names[unit];
298bf21cd93STycho Nightingale 
299bf21cd93STycho Nightingale 		if (uart_legacy_alloc(unit, &sc->iobase, &sc->irq) != 0) {
300154972afSPatrick Mooney 			EPRINTLN("Unable to allocate resources for "
301154972afSPatrick Mooney 			    "LPC device %s", name);
302bf21cd93STycho Nightingale 			return (-1);
303bf21cd93STycho Nightingale 		}
304bf21cd93STycho Nightingale 		pci_irq_reserve(sc->irq);
305bf21cd93STycho Nightingale 
306bf21cd93STycho Nightingale 		sc->uart_softc = uart_init(lpc_uart_intr_assert,
307bf21cd93STycho Nightingale 				    lpc_uart_intr_deassert, sc);
308bf21cd93STycho Nightingale 
3092b948146SAndy Fiddaman 		asprintf(&node_name, "lpc.%s.path", name);
3102b948146SAndy Fiddaman 		backend = get_config_value(node_name);
3112b948146SAndy Fiddaman 		free(node_name);
3122b948146SAndy Fiddaman 		if (uart_set_backend(sc->uart_softc, backend) != 0) {
313154972afSPatrick Mooney 			EPRINTLN("Unable to initialize backend '%s' "
3142b948146SAndy Fiddaman 			    "for LPC device %s", backend, name);
315bf21cd93STycho Nightingale 			return (-1);
316bf21cd93STycho Nightingale 		}
317bf21cd93STycho Nightingale 
318bf21cd93STycho Nightingale 		bzero(&iop, sizeof(struct inout_port));
319bf21cd93STycho Nightingale 		iop.name = name;
320bf21cd93STycho Nightingale 		iop.port = sc->iobase;
321bf21cd93STycho Nightingale 		iop.size = UART_IO_BAR_SIZE;
322bf21cd93STycho Nightingale 		iop.flags = IOPORT_F_INOUT;
323bf21cd93STycho Nightingale 		iop.handler = lpc_uart_io_handler;
324bf21cd93STycho Nightingale 		iop.arg = sc;
325bf21cd93STycho Nightingale 
326bf21cd93STycho Nightingale 		error = register_inout(&iop);
327bf21cd93STycho Nightingale 		assert(error == 0);
328bf21cd93STycho Nightingale 		sc->enabled = 1;
329bf21cd93STycho Nightingale 	}
330bf21cd93STycho Nightingale 
3316960cd89SAndy Fiddaman 	/* pc-testdev */
3322b948146SAndy Fiddaman 	asprintf(&node_name, "lpc.%s", pctestdev_getname());
3332b948146SAndy Fiddaman 	if (get_config_bool_default(node_name, false)) {
3346960cd89SAndy Fiddaman 		error = pctestdev_init(ctx);
3356960cd89SAndy Fiddaman 		if (error)
3366960cd89SAndy Fiddaman 			return (error);
3376960cd89SAndy Fiddaman 	}
3382b948146SAndy Fiddaman 	free(node_name);
3396960cd89SAndy Fiddaman 
340bf21cd93STycho Nightingale 	return (0);
341bf21cd93STycho Nightingale }
342bf21cd93STycho Nightingale 
343bf21cd93STycho Nightingale static void
pci_lpc_write_dsdt(struct pci_devinst * pi)344bf21cd93STycho Nightingale pci_lpc_write_dsdt(struct pci_devinst *pi)
345bf21cd93STycho Nightingale {
346bf21cd93STycho Nightingale 	struct lpc_dsdt **ldpp, *ldp;
347bf21cd93STycho Nightingale 
348bf21cd93STycho Nightingale 	dsdt_line("");
349bf21cd93STycho Nightingale 	dsdt_line("Device (ISA)");
350bf21cd93STycho Nightingale 	dsdt_line("{");
351bf21cd93STycho Nightingale 	dsdt_line("  Name (_ADR, 0x%04X%04X)", pi->pi_slot, pi->pi_func);
352bf21cd93STycho Nightingale 	dsdt_line("  OperationRegion (LPCR, PCI_Config, 0x00, 0x100)");
353bf21cd93STycho Nightingale 	dsdt_line("  Field (LPCR, AnyAcc, NoLock, Preserve)");
354bf21cd93STycho Nightingale 	dsdt_line("  {");
355bf21cd93STycho Nightingale 	dsdt_line("    Offset (0x60),");
356bf21cd93STycho Nightingale 	dsdt_line("    PIRA,   8,");
357bf21cd93STycho Nightingale 	dsdt_line("    PIRB,   8,");
358bf21cd93STycho Nightingale 	dsdt_line("    PIRC,   8,");
359bf21cd93STycho Nightingale 	dsdt_line("    PIRD,   8,");
360bf21cd93STycho Nightingale 	dsdt_line("    Offset (0x68),");
361bf21cd93STycho Nightingale 	dsdt_line("    PIRE,   8,");
362bf21cd93STycho Nightingale 	dsdt_line("    PIRF,   8,");
363bf21cd93STycho Nightingale 	dsdt_line("    PIRG,   8,");
364bf21cd93STycho Nightingale 	dsdt_line("    PIRH,   8");
365bf21cd93STycho Nightingale 	dsdt_line("  }");
366bf21cd93STycho Nightingale 	dsdt_line("");
367bf21cd93STycho Nightingale 
368bf21cd93STycho Nightingale 	dsdt_indent(1);
369bf21cd93STycho Nightingale 	SET_FOREACH(ldpp, lpc_dsdt_set) {
370bf21cd93STycho Nightingale 		ldp = *ldpp;
371bf21cd93STycho Nightingale 		ldp->handler();
372bf21cd93STycho Nightingale 	}
373bf21cd93STycho Nightingale 
374bf21cd93STycho Nightingale 	dsdt_line("");
375bf21cd93STycho Nightingale 	dsdt_line("Device (PIC)");
376bf21cd93STycho Nightingale 	dsdt_line("{");
377bf21cd93STycho Nightingale 	dsdt_line("  Name (_HID, EisaId (\"PNP0000\"))");
378bf21cd93STycho Nightingale 	dsdt_line("  Name (_CRS, ResourceTemplate ()");
379bf21cd93STycho Nightingale 	dsdt_line("  {");
380bf21cd93STycho Nightingale 	dsdt_indent(2);
381bf21cd93STycho Nightingale 	dsdt_fixed_ioport(IO_ICU1, 2);
382bf21cd93STycho Nightingale 	dsdt_fixed_ioport(IO_ICU2, 2);
383bf21cd93STycho Nightingale 	dsdt_fixed_irq(2);
384bf21cd93STycho Nightingale 	dsdt_unindent(2);
385bf21cd93STycho Nightingale 	dsdt_line("  })");
386bf21cd93STycho Nightingale 	dsdt_line("}");
387bf21cd93STycho Nightingale 
388bf21cd93STycho Nightingale 	dsdt_line("");
389bf21cd93STycho Nightingale 	dsdt_line("Device (TIMR)");
390bf21cd93STycho Nightingale 	dsdt_line("{");
391bf21cd93STycho Nightingale 	dsdt_line("  Name (_HID, EisaId (\"PNP0100\"))");
392bf21cd93STycho Nightingale 	dsdt_line("  Name (_CRS, ResourceTemplate ()");
393bf21cd93STycho Nightingale 	dsdt_line("  {");
394bf21cd93STycho Nightingale 	dsdt_indent(2);
395bf21cd93STycho Nightingale 	dsdt_fixed_ioport(IO_TIMER1_PORT, 4);
396bf21cd93STycho Nightingale 	dsdt_fixed_irq(0);
397bf21cd93STycho Nightingale 	dsdt_unindent(2);
398bf21cd93STycho Nightingale 	dsdt_line("  })");
399bf21cd93STycho Nightingale 	dsdt_line("}");
400bf21cd93STycho Nightingale 	dsdt_unindent(1);
401bf21cd93STycho Nightingale 
402bf21cd93STycho Nightingale 	dsdt_line("}");
403bf21cd93STycho Nightingale }
404bf21cd93STycho Nightingale 
405bf21cd93STycho Nightingale static void
pci_lpc_sysres_dsdt(void)406bf21cd93STycho Nightingale pci_lpc_sysres_dsdt(void)
407bf21cd93STycho Nightingale {
408bf21cd93STycho Nightingale 	struct lpc_sysres **lspp, *lsp;
409bf21cd93STycho Nightingale 
410bf21cd93STycho Nightingale 	dsdt_line("");
411bf21cd93STycho Nightingale 	dsdt_line("Device (SIO)");
412bf21cd93STycho Nightingale 	dsdt_line("{");
413bf21cd93STycho Nightingale 	dsdt_line("  Name (_HID, EisaId (\"PNP0C02\"))");
414bf21cd93STycho Nightingale 	dsdt_line("  Name (_CRS, ResourceTemplate ()");
415bf21cd93STycho Nightingale 	dsdt_line("  {");
416bf21cd93STycho Nightingale 
417bf21cd93STycho Nightingale 	dsdt_indent(2);
418bf21cd93STycho Nightingale 	SET_FOREACH(lspp, lpc_sysres_set) {
419bf21cd93STycho Nightingale 		lsp = *lspp;
420bf21cd93STycho Nightingale 		switch (lsp->type) {
421bf21cd93STycho Nightingale 		case LPC_SYSRES_IO:
422bf21cd93STycho Nightingale 			dsdt_fixed_ioport(lsp->base, lsp->length);
423bf21cd93STycho Nightingale 			break;
424bf21cd93STycho Nightingale 		case LPC_SYSRES_MEM:
425bf21cd93STycho Nightingale 			dsdt_fixed_mem32(lsp->base, lsp->length);
426bf21cd93STycho Nightingale 			break;
427bf21cd93STycho Nightingale 		}
428bf21cd93STycho Nightingale 	}
429bf21cd93STycho Nightingale 	dsdt_unindent(2);
430bf21cd93STycho Nightingale 
431bf21cd93STycho Nightingale 	dsdt_line("  })");
432bf21cd93STycho Nightingale 	dsdt_line("}");
433bf21cd93STycho Nightingale }
434bf21cd93STycho Nightingale LPC_DSDT(pci_lpc_sysres_dsdt);
435bf21cd93STycho Nightingale 
436bf21cd93STycho Nightingale static void
pci_lpc_uart_dsdt(void)437bf21cd93STycho Nightingale pci_lpc_uart_dsdt(void)
438bf21cd93STycho Nightingale {
439bf21cd93STycho Nightingale 	struct lpc_uart_softc *sc;
440bf21cd93STycho Nightingale 	int unit;
441bf21cd93STycho Nightingale 
442bf21cd93STycho Nightingale 	for (unit = 0; unit < LPC_UART_NUM; unit++) {
443bf21cd93STycho Nightingale 		sc = &lpc_uart_softc[unit];
444bf21cd93STycho Nightingale 		if (!sc->enabled)
445bf21cd93STycho Nightingale 			continue;
446bf21cd93STycho Nightingale 		dsdt_line("");
4472b948146SAndy Fiddaman 		dsdt_line("Device (%s)", lpc_uart_acpi_names[unit]);
448bf21cd93STycho Nightingale 		dsdt_line("{");
449bf21cd93STycho Nightingale 		dsdt_line("  Name (_HID, EisaId (\"PNP0501\"))");
450bf21cd93STycho Nightingale 		dsdt_line("  Name (_UID, %d)", unit + 1);
451bf21cd93STycho Nightingale 		dsdt_line("  Name (_CRS, ResourceTemplate ()");
452bf21cd93STycho Nightingale 		dsdt_line("  {");
453bf21cd93STycho Nightingale 		dsdt_indent(2);
454bf21cd93STycho Nightingale 		dsdt_fixed_ioport(sc->iobase, UART_IO_BAR_SIZE);
455bf21cd93STycho Nightingale 		dsdt_fixed_irq(sc->irq);
456bf21cd93STycho Nightingale 		dsdt_unindent(2);
457bf21cd93STycho Nightingale 		dsdt_line("  })");
458bf21cd93STycho Nightingale 		dsdt_line("}");
459bf21cd93STycho Nightingale 	}
460bf21cd93STycho Nightingale }
461bf21cd93STycho Nightingale LPC_DSDT(pci_lpc_uart_dsdt);
462bf21cd93STycho Nightingale 
463bf21cd93STycho Nightingale static int
pci_lpc_cfgwrite(struct pci_devinst * pi,int coff,int bytes,uint32_t val)464*32640292SAndy Fiddaman pci_lpc_cfgwrite(struct pci_devinst *pi, int coff, int bytes, uint32_t val)
465bf21cd93STycho Nightingale {
466bf21cd93STycho Nightingale 	int pirq_pin;
467bf21cd93STycho Nightingale 
468bf21cd93STycho Nightingale 	if (bytes == 1) {
469bf21cd93STycho Nightingale 		pirq_pin = 0;
470bf21cd93STycho Nightingale 		if (coff >= 0x60 && coff <= 0x63)
471bf21cd93STycho Nightingale 			pirq_pin = coff - 0x60 + 1;
472bf21cd93STycho Nightingale 		if (coff >= 0x68 && coff <= 0x6b)
473bf21cd93STycho Nightingale 			pirq_pin = coff - 0x68 + 5;
474bf21cd93STycho Nightingale 		if (pirq_pin != 0) {
475*32640292SAndy Fiddaman 			pirq_write(pi->pi_vmctx, pirq_pin, val);
476bf21cd93STycho Nightingale 			pci_set_cfgdata8(pi, coff, pirq_read(pirq_pin));
477bf21cd93STycho Nightingale 			return (0);
478bf21cd93STycho Nightingale 		}
479bf21cd93STycho Nightingale 	}
480bf21cd93STycho Nightingale 	return (-1);
481bf21cd93STycho Nightingale }
482bf21cd93STycho Nightingale 
483bf21cd93STycho Nightingale static void
pci_lpc_write(struct pci_devinst * pi __unused,int baridx __unused,uint64_t offset __unused,int size __unused,uint64_t value __unused)484*32640292SAndy Fiddaman pci_lpc_write(struct pci_devinst *pi __unused, int baridx __unused,
48559d65d31SAndy Fiddaman     uint64_t offset __unused, int size __unused, uint64_t value __unused)
486bf21cd93STycho Nightingale {
487bf21cd93STycho Nightingale }
488bf21cd93STycho Nightingale 
489bf21cd93STycho Nightingale static uint64_t
pci_lpc_read(struct pci_devinst * pi __unused,int baridx __unused,uint64_t offset __unused,int size __unused)490*32640292SAndy Fiddaman pci_lpc_read(struct pci_devinst *pi __unused, int baridx __unused,
491*32640292SAndy Fiddaman     uint64_t offset __unused, int size __unused)
492bf21cd93STycho Nightingale {
493bf21cd93STycho Nightingale 	return (0);
494bf21cd93STycho Nightingale }
495bf21cd93STycho Nightingale 
496bf21cd93STycho Nightingale #define	LPC_DEV		0x7000
497bf21cd93STycho Nightingale #define	LPC_VENDOR	0x8086
498*32640292SAndy Fiddaman #define LPC_REVID	0x00
499*32640292SAndy Fiddaman #define LPC_SUBVEND_0	0x0000
500*32640292SAndy Fiddaman #define LPC_SUBDEV_0	0x0000
501*32640292SAndy Fiddaman 
502*32640292SAndy Fiddaman #ifdef	__FreeBSD__
503*32640292SAndy Fiddaman static int
pci_lpc_get_sel(struct pcisel * const sel)504*32640292SAndy Fiddaman pci_lpc_get_sel(struct pcisel *const sel)
505*32640292SAndy Fiddaman {
506*32640292SAndy Fiddaman 	assert(sel != NULL);
507*32640292SAndy Fiddaman 
508*32640292SAndy Fiddaman 	memset(sel, 0, sizeof(*sel));
509*32640292SAndy Fiddaman 
510*32640292SAndy Fiddaman 	for (uint8_t slot = 0; slot <= PCI_SLOTMAX; ++slot) {
511*32640292SAndy Fiddaman 		uint8_t max_func = 0;
512bf21cd93STycho Nightingale 
513*32640292SAndy Fiddaman 		sel->pc_dev = slot;
514*32640292SAndy Fiddaman 		sel->pc_func = 0;
515*32640292SAndy Fiddaman 
516*32640292SAndy Fiddaman 		if (read_config(sel, PCIR_HDRTYPE, 1) & PCIM_MFDEV)
517*32640292SAndy Fiddaman 			max_func = PCI_FUNCMAX;
518*32640292SAndy Fiddaman 
519*32640292SAndy Fiddaman 		for (uint8_t func = 0; func <= max_func; ++func) {
520*32640292SAndy Fiddaman 			sel->pc_func = func;
521*32640292SAndy Fiddaman 
522*32640292SAndy Fiddaman 			if ((read_config(sel, PCIR_CLASS, 1) == PCIC_BRIDGE) &&
523*32640292SAndy Fiddaman 			    (read_config(sel, PCIR_SUBCLASS, 1) ==
524*32640292SAndy Fiddaman 				PCIS_BRIDGE_ISA)) {
525*32640292SAndy Fiddaman 				return (0);
526*32640292SAndy Fiddaman 			}
527*32640292SAndy Fiddaman 		}
528*32640292SAndy Fiddaman 	}
529*32640292SAndy Fiddaman 
530*32640292SAndy Fiddaman 	warnx("%s: Unable to find host selector of LPC bridge.", __func__);
531*32640292SAndy Fiddaman 
532*32640292SAndy Fiddaman 	return (-1);
533*32640292SAndy Fiddaman }
534*32640292SAndy Fiddaman #else
535*32640292SAndy Fiddaman /*
536*32640292SAndy Fiddaman  * This function is used to find the PCI selector for the host's LPC so that
537*32640292SAndy Fiddaman  * its various IDs can be used to configure the guest LPC with the same values
538*32640292SAndy Fiddaman  * when the `host` keyword is used in the configuration.
539*32640292SAndy Fiddaman  * On illumos we always just report that we cannot find the host LPC. This is
540*32640292SAndy Fiddaman  * likely to be true in the case that we're running in a zone anyway.
541*32640292SAndy Fiddaman  */
542bf21cd93STycho Nightingale static int
pci_lpc_get_sel(struct pcisel * const sel __unused)543*32640292SAndy Fiddaman pci_lpc_get_sel(struct pcisel *const sel __unused)
544bf21cd93STycho Nightingale {
545*32640292SAndy Fiddaman 	return (-1);
546*32640292SAndy Fiddaman }
547*32640292SAndy Fiddaman #endif
548*32640292SAndy Fiddaman 
549*32640292SAndy Fiddaman static int
pci_lpc_init(struct pci_devinst * pi,nvlist_t * nvl)550*32640292SAndy Fiddaman pci_lpc_init(struct pci_devinst *pi, nvlist_t *nvl)
551*32640292SAndy Fiddaman {
552*32640292SAndy Fiddaman 	struct pcisel sel = { 0 };
553*32640292SAndy Fiddaman 	struct pcisel *selp = NULL;
554*32640292SAndy Fiddaman 	uint16_t device, subdevice, subvendor, vendor;
555*32640292SAndy Fiddaman 	uint8_t revid;
556*32640292SAndy Fiddaman 
557bf21cd93STycho Nightingale 	/*
558bf21cd93STycho Nightingale 	 * Do not allow more than one LPC bridge to be configured.
559bf21cd93STycho Nightingale 	 */
560bf21cd93STycho Nightingale 	if (lpc_bridge != NULL) {
561154972afSPatrick Mooney 		EPRINTLN("Only one LPC bridge is allowed.");
562bf21cd93STycho Nightingale 		return (-1);
563bf21cd93STycho Nightingale 	}
564bf21cd93STycho Nightingale 
565bf21cd93STycho Nightingale 	/*
566bf21cd93STycho Nightingale 	 * Enforce that the LPC can only be configured on bus 0. This
567bf21cd93STycho Nightingale 	 * simplifies the ACPI DSDT because it can provide a decode for
568bf21cd93STycho Nightingale 	 * all legacy i/o ports behind bus 0.
569bf21cd93STycho Nightingale 	 */
570bf21cd93STycho Nightingale 	if (pi->pi_bus != 0) {
571154972afSPatrick Mooney 		EPRINTLN("LPC bridge can be present only on bus 0.");
572bf21cd93STycho Nightingale 		return (-1);
573bf21cd93STycho Nightingale 	}
574bf21cd93STycho Nightingale 
575*32640292SAndy Fiddaman 	if (lpc_init(pi->pi_vmctx) != 0)
576bf21cd93STycho Nightingale 		return (-1);
577bf21cd93STycho Nightingale 
578*32640292SAndy Fiddaman 	if (pci_lpc_get_sel(&sel) == 0)
579*32640292SAndy Fiddaman 		selp = &sel;
580*32640292SAndy Fiddaman 
581*32640292SAndy Fiddaman 	vendor = pci_config_read_reg(selp, nvl, PCIR_VENDOR, 2, LPC_VENDOR);
582*32640292SAndy Fiddaman 	device = pci_config_read_reg(selp, nvl, PCIR_DEVICE, 2, LPC_DEV);
583*32640292SAndy Fiddaman 	revid = pci_config_read_reg(selp, nvl, PCIR_REVID, 1, LPC_REVID);
584*32640292SAndy Fiddaman 	subvendor = pci_config_read_reg(selp, nvl, PCIR_SUBVEND_0, 2,
585*32640292SAndy Fiddaman 	    LPC_SUBVEND_0);
586*32640292SAndy Fiddaman 	subdevice = pci_config_read_reg(selp, nvl, PCIR_SUBDEV_0, 2,
587*32640292SAndy Fiddaman 	    LPC_SUBDEV_0);
588*32640292SAndy Fiddaman 
589bf21cd93STycho Nightingale 	/* initialize config space */
590*32640292SAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_VENDOR, vendor);
591*32640292SAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_DEVICE, device);
592bf21cd93STycho Nightingale 	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE);
593bf21cd93STycho Nightingale 	pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_ISA);
594*32640292SAndy Fiddaman 	pci_set_cfgdata8(pi, PCIR_REVID, revid);
595*32640292SAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_SUBVEND_0, subvendor);
596*32640292SAndy Fiddaman 	pci_set_cfgdata16(pi, PCIR_SUBDEV_0, subdevice);
597bf21cd93STycho Nightingale 
598bf21cd93STycho Nightingale 	lpc_bridge = pi;
599bf21cd93STycho Nightingale 
600bf21cd93STycho Nightingale 	return (0);
601bf21cd93STycho Nightingale }
602bf21cd93STycho Nightingale 
603bf21cd93STycho Nightingale char *
lpc_pirq_name(int pin)604bf21cd93STycho Nightingale lpc_pirq_name(int pin)
605bf21cd93STycho Nightingale {
606bf21cd93STycho Nightingale 	char *name;
607bf21cd93STycho Nightingale 
608bf21cd93STycho Nightingale 	if (lpc_bridge == NULL)
609bf21cd93STycho Nightingale 		return (NULL);
610bf21cd93STycho Nightingale 	asprintf(&name, "\\_SB.PC00.ISA.LNK%c,", 'A' + pin - 1);
611bf21cd93STycho Nightingale 	return (name);
612bf21cd93STycho Nightingale }
613bf21cd93STycho Nightingale 
614bf21cd93STycho Nightingale void
lpc_pirq_routed(void)615bf21cd93STycho Nightingale lpc_pirq_routed(void)
616bf21cd93STycho Nightingale {
617bf21cd93STycho Nightingale 	int pin;
618bf21cd93STycho Nightingale 
619bf21cd93STycho Nightingale 	if (lpc_bridge == NULL)
620bf21cd93STycho Nightingale 		return;
621bf21cd93STycho Nightingale 
622bf21cd93STycho Nightingale  	for (pin = 0; pin < 4; pin++)
623bf21cd93STycho Nightingale 		pci_set_cfgdata8(lpc_bridge, 0x60 + pin, pirq_read(pin + 1));
624bf21cd93STycho Nightingale 	for (pin = 0; pin < 4; pin++)
625bf21cd93STycho Nightingale 		pci_set_cfgdata8(lpc_bridge, 0x68 + pin, pirq_read(pin + 5));
626bf21cd93STycho Nightingale }
627bf21cd93STycho Nightingale 
6284f3f3e9aSAndy Fiddaman static const struct pci_devemu pci_de_lpc = {
629bf21cd93STycho Nightingale 	.pe_emu =	"lpc",
630bf21cd93STycho Nightingale 	.pe_init =	pci_lpc_init,
631bf21cd93STycho Nightingale 	.pe_write_dsdt = pci_lpc_write_dsdt,
632bf21cd93STycho Nightingale 	.pe_cfgwrite =	pci_lpc_cfgwrite,
633bf21cd93STycho Nightingale 	.pe_barwrite =	pci_lpc_write,
634bf21cd93STycho Nightingale 	.pe_barread =	pci_lpc_read
635bf21cd93STycho Nightingale };
636bf21cd93STycho Nightingale PCI_EMUL_SET(pci_de_lpc);
637