1 /*-
2  * Copyright (c) 2014 Hudson River Trading LLC
3  * Written by: John H. Baldwin <jhb@FreeBSD.org>
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 THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR 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 
28 
29 #include <pthread.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 
34 #include "types.h"
35 #include "acpi.h"
36 #include "vmmapi.h"
37 #include "pci_core.h"
38 #include "lpc.h"
39 
40 /*
41  * Implement an 8 pin PCI interrupt router compatible with the router
42  * present on Intel's ICH10 chip.
43  */
44 
45 /* Fields in each PIRQ register. */
46 #define	PIRQ_DIS	0x80
47 #define	PIRQ_IRQ	0x0f
48 
49 /* Only IRQs 3-7, 9-12, and 14-15 are permitted. */
50 #define	PERMITTED_IRQS	0xdef8
51 #define	IRQ_PERMITTED(irq)	(((1U << (irq)) & PERMITTED_IRQS) != 0)
52 
53 /* IRQ count to disable an IRQ. */
54 #define	IRQ_DISABLED	0xff
55 
56 static struct pirq {
57 	uint8_t	reg;
58 	int	use_count;
59 	int	active_count;
60 	pthread_mutex_t lock;
61 } pirqs[8];
62 
63 static u_char irq_counts[16];
64 static int pirq_cold = 1;
65 
66 /*
67  * Returns true if this pin is enabled with a valid IRQ.  Setting the
68  * register to a reserved IRQ causes interrupts to not be asserted as
69  * if the pin was disabled.
70  */
71 static bool
pirq_valid_irq(int reg)72 pirq_valid_irq(int reg)
73 {
74 	if (reg & PIRQ_DIS)
75 		return false;
76 	return IRQ_PERMITTED(reg & PIRQ_IRQ);
77 }
78 
79 uint8_t
pirq_read(int pin)80 pirq_read(int pin)
81 {
82 	if (pin <= 0 || pin > nitems(pirqs))
83 		return PIRQ_DIS;
84 
85 	return pirqs[pin - 1].reg;
86 }
87 
88 void
pirq_write(struct vmctx * ctx,int pin,uint8_t val)89 pirq_write(struct vmctx *ctx, int pin, uint8_t val)
90 {
91 	struct pirq *pirq;
92 
93 	if (pin <= 0 || pin > nitems(pirqs))
94 		return;
95 
96 	pirq = &pirqs[pin - 1];
97 	pthread_mutex_lock(&pirq->lock);
98 	if (pirq->reg != (val & (PIRQ_DIS | PIRQ_IRQ))) {
99 		if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
100 			vm_set_gsi_irq(ctx, pirq->reg & PIRQ_IRQ, GSI_SET_LOW);
101 		pirq->reg = val & (PIRQ_DIS | PIRQ_IRQ);
102 		if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
103 			vm_set_gsi_irq(ctx, pirq->reg & PIRQ_IRQ, GSI_SET_HIGH);
104 	}
105 	pthread_mutex_unlock(&pirq->lock);
106 }
107 
108 void
pci_irq_reserve(int irq)109 pci_irq_reserve(int irq) {
110 	if ((irq >= 0 && irq < nitems(irq_counts)) && pirq_cold
111 		&& (irq_counts[irq] == 0 || irq_counts[irq] == IRQ_DISABLED))
112 		irq_counts[irq] = IRQ_DISABLED;
113 }
114 
115 void
pci_irq_use(int irq)116 pci_irq_use(int irq)
117 {
118 	if ((irq >= 0 && irq < nitems(irq_counts)) && pirq_cold
119 		&& (irq_counts[irq] != IRQ_DISABLED))
120 		irq_counts[irq]++;
121 }
122 
123 void
pci_irq_init(struct vmctx * ctx)124 pci_irq_init(struct vmctx *ctx)
125 {
126 	int i;
127 
128 	for (i = 0; i < nitems(pirqs); i++) {
129 		pirqs[i].reg = PIRQ_DIS;
130 		pirqs[i].use_count = 0;
131 		pirqs[i].active_count = 0;
132 		pthread_mutex_init(&pirqs[i].lock, NULL);
133 	}
134 	for (i = 0; i < nitems(irq_counts); i++) {
135 		if (IRQ_PERMITTED(i))
136 			irq_counts[i] = 0;
137 		else
138 			irq_counts[i] = IRQ_DISABLED;
139 	}
140 }
141 
pci_irq_deinit(struct vmctx * ctx)142 void pci_irq_deinit(struct vmctx *ctx)
143 {
144 	pirq_cold = 1;
145 }
146 
147 void
pci_irq_assert(struct pci_vdev * dev)148 pci_irq_assert(struct pci_vdev *dev)
149 {
150 	vm_set_gsi_irq(dev->vmctx, dev->lintr.ioapic_irq, GSI_SET_LOW);
151 }
152 
153 void
pci_irq_deassert(struct pci_vdev * dev)154 pci_irq_deassert(struct pci_vdev *dev)
155 {
156 	vm_set_gsi_irq(dev->vmctx, dev->lintr.ioapic_irq, GSI_SET_HIGH);
157 }
158 
159 int
pirq_alloc_pin(struct pci_vdev * dev)160 pirq_alloc_pin(struct pci_vdev *dev)
161 {
162 	int best_count, best_irq, best_pin, irq, pin;
163 
164 	pirq_cold = 0;
165 
166 	/* Find the least-used PIRQ pin. */
167 	best_pin = 0;
168 	best_count = pirqs[0].use_count;
169 	for (pin = 1; pin < nitems(pirqs); pin++) {
170 		if (pirqs[pin].use_count < best_count) {
171 			best_pin = pin;
172 			best_count = pirqs[pin].use_count;
173 		}
174 	}
175 	pirqs[best_pin].use_count++;
176 
177 	/* Second, route this pin to an IRQ. */
178 	if (pirqs[best_pin].reg == PIRQ_DIS) {
179 		best_irq = -1;
180 		best_count = 0;
181 		for (irq = 0; irq < nitems(irq_counts); irq++) {
182 			if (irq_counts[irq] == IRQ_DISABLED)
183 				continue;
184 			if (best_irq == -1 || irq_counts[irq] < best_count) {
185 				best_irq = irq;
186 				best_count = irq_counts[irq];
187 			}
188 		}
189 		if (best_irq < 0)
190 			return -1;
191 
192 		irq_counts[best_irq]++;
193 		pirqs[best_pin].reg = best_irq;
194 	}
195 
196 	return (best_pin + 1);
197 }
198 
199 int
pirq_irq(int pin)200 pirq_irq(int pin)
201 {
202 	if (pin <= 0 || pin > nitems(pirqs))
203 		return 0xFF;
204 
205 	return (pirqs[pin - 1].reg & PIRQ_IRQ);
206 }
207 
208 /* XXX: Generate $PIR table. */
209 
210 static void
pirq_dsdt(void)211 pirq_dsdt(void)
212 {
213 	char *irq_prs, *old;
214 	int irq, pin;
215 
216 	irq_prs = NULL;
217 	for (irq = 0; irq < nitems(irq_counts); irq++) {
218 		if (!IRQ_PERMITTED(irq))
219 			continue;
220 		if (irq_prs == NULL) {
221 			if (asprintf(&irq_prs, "%d", irq) < 0) {
222 				/*error*/
223 				if (irq_prs != NULL)
224 					free(irq_prs);
225 
226 				return;
227 			}
228 		} else {
229 			old = irq_prs;
230 			if (asprintf(&irq_prs, "%s,%d", old, irq) < 0) {
231 				/*error*/
232 				if (irq_prs != NULL)
233 					free(irq_prs);
234 
235 				free(old);
236 				return;
237 			}
238 			free(old);
239 		}
240 	}
241 
242 	/*
243 	 * A helper method to validate a link register's value.  This
244 	 * duplicates pirq_valid_irq().
245 	 */
246 	dsdt_line("");
247 	dsdt_line("Method (PIRV, 1, NotSerialized)");
248 	dsdt_line("{");
249 	dsdt_line("  If (And (Arg0, 0x%02X))", PIRQ_DIS);
250 	dsdt_line("  {");
251 	dsdt_line("    Return (0x00)");
252 	dsdt_line("  }");
253 	dsdt_line("  And (Arg0, 0x%02X, Local0)", PIRQ_IRQ);
254 	dsdt_line("  If (LLess (Local0, 0x03))");
255 	dsdt_line("  {");
256 	dsdt_line("    Return (0x00)");
257 	dsdt_line("  }");
258 	dsdt_line("  If (LEqual (Local0, 0x08))");
259 	dsdt_line("  {");
260 	dsdt_line("    Return (0x00)");
261 	dsdt_line("  }");
262 	dsdt_line("  If (LEqual (Local0, 0x0D))");
263 	dsdt_line("  {");
264 	dsdt_line("    Return (0x00)");
265 	dsdt_line("  }");
266 	dsdt_line("  Return (0x01)");
267 	dsdt_line("}");
268 
269 	for (pin = 0; pin < nitems(pirqs); pin++) {
270 		dsdt_line("");
271 		dsdt_line("Device (LNK%c)", 'A' + pin);
272 		dsdt_line("{");
273 		dsdt_line("  Name (_HID, EisaId (\"PNP0C0F\"))");
274 		dsdt_line("  Name (_UID, 0x%02X)", pin + 1);
275 		dsdt_line("  Method (_STA, 0, NotSerialized)");
276 		dsdt_line("  {");
277 		dsdt_line("    If (PIRV (PIR%c))", 'A' + pin);
278 		dsdt_line("    {");
279 		dsdt_line("       Return (0x0B)");
280 		dsdt_line("    }");
281 		dsdt_line("    Else");
282 		dsdt_line("    {");
283 		dsdt_line("       Return (0x09)");
284 		dsdt_line("    }");
285 		dsdt_line("  }");
286 		dsdt_line("  Name (_PRS, ResourceTemplate ()");
287 		dsdt_line("  {");
288 		dsdt_line("    IRQ (Level, ActiveLow, Shared, )");
289 		dsdt_line("      {%s}", irq_prs);
290 		dsdt_line("  })");
291 		dsdt_line("  Name (CB%02X, ResourceTemplate ()", pin + 1);
292 		dsdt_line("  {");
293 		dsdt_line("    IRQ (Level, ActiveLow, Shared, )");
294 		dsdt_line("      {}");
295 		dsdt_line("  })");
296 		dsdt_line("  CreateWordField (CB%02X, 0x01, CIR%c)",
297 		    pin + 1, 'A' + pin);
298 		dsdt_line("  Method (_CRS, 0, NotSerialized)");
299 		dsdt_line("  {");
300 		dsdt_line("    And (PIR%c, 0x%02X, Local0)", 'A' + pin,
301 		    PIRQ_DIS | PIRQ_IRQ);
302 		dsdt_line("    If (PIRV (Local0))");
303 		dsdt_line("    {");
304 		dsdt_line("      ShiftLeft (0x01, Local0, CIR%c)", 'A' + pin);
305 		dsdt_line("    }");
306 		dsdt_line("    Else");
307 		dsdt_line("    {");
308 		dsdt_line("      Store (0x00, CIR%c)", 'A' + pin);
309 		dsdt_line("    }");
310 		dsdt_line("    Return (CB%02X)", pin + 1);
311 		dsdt_line("  }");
312 		dsdt_line("  Method (_DIS, 0, NotSerialized)");
313 		dsdt_line("  {");
314 		dsdt_line("    Store (0x80, PIR%c)", 'A' + pin);
315 		dsdt_line("  }");
316 		dsdt_line("  Method (_SRS, 1, NotSerialized)");
317 		dsdt_line("  {");
318 		dsdt_line("    CreateWordField (Arg0, 0x01, SIR%c)", 'A' + pin);
319 		dsdt_line("    FindSetRightBit (SIR%c, Local0)", 'A' + pin);
320 		dsdt_line("    Store (Decrement (Local0), PIR%c)", 'A' + pin);
321 		dsdt_line("  }");
322 		dsdt_line("}");
323 	}
324 	free(irq_prs);
325 }
326 LPC_DSDT(pirq_dsdt);
327