]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/pci_lpc.c
Initial import of DTS files from Linux
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / pci_lpc.c
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 #include <machine/vmm_dev.h>
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #include <vmmapi.h>
42
43 #include "acpi.h"
44 #include "inout.h"
45 #include "pci_emul.h"
46 #include "pci_lpc.h"
47 #include "uart_emul.h"
48
49 SET_DECLARE(lpc_dsdt_set, struct lpc_dsdt);
50 SET_DECLARE(lpc_sysres_set, struct lpc_sysres);
51
52 static struct pci_devinst *lpc_bridge;
53
54 #define LPC_UART_NUM    2
55 static struct lpc_uart_softc {
56         struct uart_softc *uart_softc;
57         const char *opts;
58         int     iobase;
59         int     irq;
60         int     enabled;
61 } lpc_uart_softc[LPC_UART_NUM];
62
63 static const char *lpc_uart_names[LPC_UART_NUM] = { "COM1", "COM2" };
64
65 /*
66  * LPC device configuration is in the following form:
67  * <lpc_device_name>[,<options>]
68  * For e.g. "com1,stdio"
69  */
70 int
71 lpc_device_parse(const char *opts)
72 {
73         int unit, error;
74         char *str, *cpy, *lpcdev;
75
76         error = -1;
77         str = cpy = strdup(opts);
78         lpcdev = strsep(&str, ",");
79         if (lpcdev != NULL) {
80                 for (unit = 0; unit < LPC_UART_NUM; unit++) {
81                         if (strcasecmp(lpcdev, lpc_uart_names[unit]) == 0) {
82                                 lpc_uart_softc[unit].opts = str;
83                                 error = 0;
84                                 goto done;
85                         }
86                 }
87         }
88
89 done:
90         if (error)
91                 free(cpy);
92
93         return (error);
94 }
95
96 static void
97 lpc_uart_intr_assert(void *arg)
98 {
99         struct lpc_uart_softc *sc = arg;
100
101         assert(sc->irq >= 0);
102
103         vm_ioapic_pulse_irq(lpc_bridge->pi_vmctx, sc->irq);
104 }
105
106 static void
107 lpc_uart_intr_deassert(void *arg)
108 {
109         /* 
110          * The COM devices on the LPC bus generate edge triggered interrupts,
111          * so nothing more to do here.
112          */
113 }
114
115 static int
116 lpc_uart_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
117                     uint32_t *eax, void *arg)
118 {
119         int offset;
120         struct lpc_uart_softc *sc = arg;
121
122         if (bytes != 1)
123                 return (-1);
124
125         offset = port - sc->iobase;
126
127         if (in)
128                 *eax = uart_read(sc->uart_softc, offset); 
129         else
130                 uart_write(sc->uart_softc, offset, *eax);
131
132         return (0);
133 }
134
135 static int
136 lpc_init(void)
137 {
138         struct lpc_uart_softc *sc;
139         struct inout_port iop;
140         const char *name;
141         int unit, error;
142
143         /* COM1 and COM2 */
144         for (unit = 0; unit < LPC_UART_NUM; unit++) {
145                 sc = &lpc_uart_softc[unit];
146                 name = lpc_uart_names[unit];
147
148                 if (uart_legacy_alloc(unit, &sc->iobase, &sc->irq) != 0) {
149                         fprintf(stderr, "Unable to allocate resources for "
150                             "LPC device %s\n", name);
151                         return (-1);
152                 }
153
154                 sc->uart_softc = uart_init(lpc_uart_intr_assert,
155                                     lpc_uart_intr_deassert, sc);
156
157                 if (uart_set_backend(sc->uart_softc, sc->opts) != 0) {
158                         fprintf(stderr, "Unable to initialize backend '%s' "
159                             "for LPC device %s\n", sc->opts, name);
160                         return (-1);
161                 }
162
163                 bzero(&iop, sizeof(struct inout_port));
164                 iop.name = name;
165                 iop.port = sc->iobase;
166                 iop.size = UART_IO_BAR_SIZE;
167                 iop.flags = IOPORT_F_INOUT;
168                 iop.handler = lpc_uart_io_handler;
169                 iop.arg = sc;
170
171                 error = register_inout(&iop);
172                 assert(error == 0);
173                 sc->enabled = 1;
174         }
175
176         return (0);
177 }
178
179 static void
180 pci_lpc_write_dsdt(struct pci_devinst *pi)
181 {
182         struct lpc_dsdt **ldpp, *ldp;
183
184         dsdt_line("");
185         dsdt_line("Device (ISA)");
186         dsdt_line("{");
187         dsdt_line("  Name (_ADR, 0x%04X%04X)", pi->pi_slot, pi->pi_func);
188         dsdt_line("  OperationRegion (P40C, PCI_Config, 0x60, 0x04)");
189
190         dsdt_indent(1);
191         SET_FOREACH(ldpp, lpc_dsdt_set) {
192                 ldp = *ldpp;
193                 ldp->handler();
194         }
195         dsdt_unindent(1);
196
197         dsdt_line("}");
198 }
199
200 static void
201 pci_lpc_sysres_dsdt(void)
202 {
203         struct lpc_sysres **lspp, *lsp;
204
205         dsdt_line("");
206         dsdt_line("Device (SIO)");
207         dsdt_line("{");
208         dsdt_line("  Name (_HID, EisaId (\"PNP0C02\"))");
209         dsdt_line("  Name (_CRS, ResourceTemplate ()");
210         dsdt_line("  {");
211
212         dsdt_indent(2);
213         SET_FOREACH(lspp, lpc_sysres_set) {
214                 lsp = *lspp;
215                 switch (lsp->type) {
216                 case LPC_SYSRES_IO:
217                         dsdt_fixed_ioport(lsp->base, lsp->length);
218                         break;
219                 case LPC_SYSRES_MEM:
220                         dsdt_fixed_mem32(lsp->base, lsp->length);
221                         break;
222                 }
223         }
224         dsdt_unindent(2);
225
226         dsdt_line("  })");
227         dsdt_line("}");
228 }
229 LPC_DSDT(pci_lpc_sysres_dsdt);
230
231 static void
232 pci_lpc_uart_dsdt(void)
233 {
234         struct lpc_uart_softc *sc;
235         int unit;
236
237         for (unit = 0; unit < LPC_UART_NUM; unit++) {
238                 sc = &lpc_uart_softc[unit];
239                 if (!sc->enabled)
240                         continue;
241                 dsdt_line("");
242                 dsdt_line("Device (%s)", lpc_uart_names[unit]);
243                 dsdt_line("{");
244                 dsdt_line("  Name (_HID, EisaId (\"PNP0501\"))");
245                 dsdt_line("  Name (_UID, %d)", unit + 1);
246                 dsdt_line("  Name (_CRS, ResourceTemplate ()");
247                 dsdt_line("  {");
248                 dsdt_indent(2);
249                 dsdt_fixed_ioport(sc->iobase, UART_IO_BAR_SIZE);
250                 dsdt_fixed_irq(sc->irq);
251                 dsdt_unindent(2);
252                 dsdt_line("  })");
253                 dsdt_line("}");
254         }
255 }
256 LPC_DSDT(pci_lpc_uart_dsdt);
257
258 static void
259 pci_lpc_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
260                int baridx, uint64_t offset, int size, uint64_t value)
261 {
262 }
263
264 uint64_t
265 pci_lpc_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
266               int baridx, uint64_t offset, int size)
267 {
268         return (0);
269 }
270
271 #define LPC_DEV         0x7000
272 #define LPC_VENDOR      0x8086
273
274 static int
275 pci_lpc_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
276 {
277         /*
278          * Do not allow more than one LPC bridge to be configured.
279          */
280         if (lpc_bridge != NULL) {
281                 fprintf(stderr, "Only one LPC bridge is allowed.\n");
282                 return (-1);
283         }
284
285         /*
286          * Enforce that the LPC can only be configured on bus 0. This
287          * simplifies the ACPI DSDT because it can provide a decode for
288          * all legacy i/o ports behind bus 0.
289          */
290         if (pi->pi_bus != 0) {
291                 fprintf(stderr, "LPC bridge can be present only on bus 0.\n");
292                 return (-1);
293         }
294
295         if (lpc_init() != 0)
296                 return (-1);
297
298         /* initialize config space */
299         pci_set_cfgdata16(pi, PCIR_DEVICE, LPC_DEV);
300         pci_set_cfgdata16(pi, PCIR_VENDOR, LPC_VENDOR);
301         pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_BRIDGE);
302         pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_BRIDGE_ISA);
303
304         lpc_bridge = pi;
305
306         return (0);
307 }
308
309 struct pci_devemu pci_de_lpc = {
310         .pe_emu =       "lpc",
311         .pe_init =      pci_lpc_init,
312         .pe_write_dsdt = pci_lpc_write_dsdt,
313         .pe_barwrite =  pci_lpc_write,
314         .pe_barread =   pci_lpc_read
315 };
316 PCI_EMUL_SET(pci_de_lpc);