xref: /openbsd/sys/arch/i386/pci/pci_addr_fixup.c (revision 898184e3)
1 /*	$OpenBSD: pci_addr_fixup.c,v 1.22 2010/07/02 16:11:19 jsg Exp $	*/
2 /*	$NetBSD: pci_addr_fixup.c,v 1.7 2000/08/03 20:10:45 nathanw Exp $	*/
3 
4 /*-
5  * Copyright (c) 2000 UCHIYAMA Yasushi.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/malloc.h>
33 #include <sys/kernel.h>
34 #include <sys/device.h>
35 #include <sys/extent.h>
36 
37 #include <uvm/uvm_extern.h>
38 
39 #include <machine/bus.h>
40 
41 #include <dev/pci/pcireg.h>
42 #include <dev/pci/pcivar.h>
43 #include <dev/pci/pcidevs.h>
44 
45 #include <i386/pci/pcibiosvar.h>
46 
47 typedef int (*pciaddr_resource_manage_func_t)(struct pcibios_softc *, pci_chipset_tag_t, pcitag_t, int,
48 	struct extent *, int, bus_addr_t *, bus_size_t);
49 void	pciaddr_resource_manage(struct pcibios_softc *,
50     pci_chipset_tag_t, pcitag_t, pciaddr_resource_manage_func_t);
51 void	pciaddr_resource_reserve(struct pcibios_softc *,
52     pci_chipset_tag_t, pcitag_t);
53 void	pciaddr_resource_reserve_disabled(struct pcibios_softc *,
54     pci_chipset_tag_t, pcitag_t);
55 int	pciaddr_do_resource_reserve(struct pcibios_softc *,
56     pci_chipset_tag_t, pcitag_t, int, struct extent *, int,
57     bus_addr_t *, bus_size_t);
58 int	pciaddr_do_resource_reserve_disabled(struct pcibios_softc *,
59     pci_chipset_tag_t, pcitag_t, int, struct extent *, int, u_long *,
60     bus_size_t);
61 void	pciaddr_resource_allocate(struct pcibios_softc *,
62     pci_chipset_tag_t, pcitag_t);
63 int	pciaddr_do_resource_allocate(struct pcibios_softc *,
64     pci_chipset_tag_t, pcitag_t, int, struct extent *, int, bus_addr_t *,
65     bus_size_t);
66 bus_addr_t pciaddr_ioaddr(u_int32_t);
67 void	pciaddr_print_devid(pci_chipset_tag_t, pcitag_t);
68 
69 int	pciaddr_device_is_agp(pci_chipset_tag_t, pcitag_t);
70 
71 #define PCIADDR_MEM_START	0x0
72 #define PCIADDR_MEM_END		0xffffffff
73 #define PCIADDR_PORT_START	0x0
74 #define PCIADDR_PORT_END	0xffff
75 
76 /* for ISA devices */
77 #define PCIADDR_ISAPORT_RESERVE	0x5800 /* empirical value */
78 #define PCIADDR_ISAMEM_RESERVE	(16 * 1024 * 1024)
79 
80 void
81 pci_addr_fixup(struct pcibios_softc *sc, pci_chipset_tag_t pc, int maxbus)
82 {
83 	extern paddr_t avail_end;
84 	const char *verbose_header =
85 		"[%s]-----------------------\n"
86 		"  device vendor product\n"
87 		"  register space address    size\n"
88 		"--------------------------------------------\n";
89 	const char *verbose_footer =
90 		"--------------------------[%3d devices bogus]\n";
91 
92 	const struct {
93 		bus_addr_t start;
94 		bus_size_t size;
95 		char *name;
96 	} system_reserve [] = {
97 		{ 0xfec00000, 0x100000, "I/O APIC" },
98 		{ 0xfee00000, 0x100000, "Local APIC" },
99 		{ 0xfffe0000, 0x20000, "BIOS PROM" },
100 		{ 0, 0, 0 }, /* terminator */
101 	}, *srp;
102 	paddr_t start;
103 	int error;
104 
105 	sc->extent_mem = extent_create("PCI I/O memory space",
106 	    PCIADDR_MEM_START, PCIADDR_MEM_END, M_DEVBUF, 0, 0, EX_NOWAIT);
107 	KASSERT(sc->extent_mem);
108 	sc->extent_port = extent_create("PCI I/O port space",
109 	    PCIADDR_PORT_START, PCIADDR_PORT_END, M_DEVBUF, 0, 0, EX_NOWAIT);
110 	KASSERT(sc->extent_port);
111 
112 	/*
113 	 * 1. check & reserve system BIOS setting.
114 	 */
115 	PCIBIOS_PRINTV((verbose_header, "System BIOS Setting"));
116 	pci_device_foreach(sc, pc, maxbus, pciaddr_resource_reserve);
117 	pci_device_foreach(sc, pc, maxbus, pciaddr_resource_reserve_disabled);
118 	PCIBIOS_PRINTV((verbose_footer, sc->nbogus));
119 
120 	/*
121 	 * 2. reserve non-PCI area.
122 	 */
123 	for (srp = system_reserve; srp->size; srp++) {
124 		error = extent_alloc_region(sc->extent_mem, srp->start,
125 		    srp->size, EX_NOWAIT| EX_MALLOCOK);
126 		if (error != 0)
127 			printf("WARNING: can't reserve area for %s.\n",
128 			       srp->name);
129 	}
130 
131 	/*
132 	 * 3. determine allocation space
133 	 */
134 	start = round_page(avail_end + 1);
135 	if (start < PCIADDR_ISAMEM_RESERVE)
136 		start = PCIADDR_ISAMEM_RESERVE;
137 	sc->mem_alloc_start = (start + 0x100000 + 1) & ~(0x100000 - 1);
138 	sc->port_alloc_start = PCIADDR_ISAPORT_RESERVE;
139 	PCIBIOS_PRINTV((" Physical memory end: 0x%08x\n PCI memory mapped I/O "
140 	    "space start: 0x%08x\n", avail_end, sc->mem_alloc_start));
141 
142 	/*
143 	 * 4. do fixup
144 	 */
145 	PCIBIOS_PRINTV((verbose_header, "PCIBIOS fixup stage"));
146 	sc->nbogus = 0;
147 	pci_device_foreach(sc, pc, maxbus, pciaddr_resource_allocate);
148 	PCIBIOS_PRINTV((verbose_footer, sc->nbogus));
149 
150 }
151 
152 void
153 pciaddr_resource_reserve(struct pcibios_softc *sc, pci_chipset_tag_t pc,
154     pcitag_t tag)
155 {
156 	if (pcibios_flags & PCIBIOS_VERBOSE)
157 		pciaddr_print_devid(pc, tag);
158 	pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_reserve);
159 }
160 
161 void
162 pciaddr_resource_reserve_disabled(struct pcibios_softc *sc,
163     pci_chipset_tag_t pc, pcitag_t tag)
164 {
165 	if (pcibios_flags & PCIBIOS_VERBOSE)
166 		pciaddr_print_devid(pc, tag);
167 	pciaddr_resource_manage(sc, pc, tag,
168 	    pciaddr_do_resource_reserve_disabled);
169 }
170 
171 void
172 pciaddr_resource_allocate(struct pcibios_softc *sc, pci_chipset_tag_t pc,
173     pcitag_t tag)
174 {
175 	if (pcibios_flags & PCIBIOS_VERBOSE)
176 		pciaddr_print_devid(pc, tag);
177 	pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_allocate);
178 }
179 
180 void
181 pciaddr_resource_manage(struct pcibios_softc *sc, pci_chipset_tag_t pc,
182     pcitag_t tag, pciaddr_resource_manage_func_t func)
183 {
184 	struct extent *ex;
185 	pcireg_t val, mask;
186 	bus_addr_t addr;
187 	bus_size_t size;
188 	int error, mapreg, type, reg_start, reg_end, width;
189 
190 	val = pci_conf_read(pc, tag, PCI_BHLC_REG);
191 	switch (PCI_HDRTYPE_TYPE(val)) {
192 	default:
193 		printf("WARNING: unknown PCI device header 0x%x.\n",
194 		    PCI_HDRTYPE_TYPE(val));
195 		sc->nbogus++;
196 		return;
197 	case 0:
198 		reg_start = PCI_MAPREG_START;
199 		reg_end   = PCI_MAPREG_END;
200 		break;
201 	case 1: /* PCI-PCI bridge */
202 		reg_start = PCI_MAPREG_START;
203 		reg_end   = PCI_MAPREG_PPB_END;
204 		break;
205 	case 2: /* PCI-CardBus bridge */
206 		reg_start = PCI_MAPREG_START;
207 		reg_end   = PCI_MAPREG_PCB_END;
208 		break;
209 	}
210 	error = 0;
211 
212 	for (mapreg = reg_start; mapreg < reg_end; mapreg += width) {
213 		/* inquire PCI device bus space requirement */
214 		val = pci_conf_read(pc, tag, mapreg);
215 		pci_conf_write(pc, tag, mapreg, ~0);
216 
217 		mask = pci_conf_read(pc, tag, mapreg);
218 		pci_conf_write(pc, tag, mapreg, val);
219 
220 		type = PCI_MAPREG_TYPE(val);
221 		width = 4;
222 		if (type == PCI_MAPREG_TYPE_MEM) {
223 			if (PCI_MAPREG_MEM_TYPE(val) ==
224 			    PCI_MAPREG_MEM_TYPE_64BIT) {
225 				/* XXX We could examine the upper 32 bits
226 				 * XXX of the BAR here, but we are totally
227 				 * XXX unprepared to handle a non-zero value,
228 				 * XXX either here or anywhere else in
229 				 * XXX i386-land.
230 				 * XXX So just arrange to not look at the
231 				 * XXX upper 32 bits, lest we misinterpret
232 				 * XXX it as a 32-bit BAR set to zero.
233 				 */
234 			    width = 8;
235 			}
236 			addr = PCI_MAPREG_MEM_ADDR(val);
237 			size = PCI_MAPREG_MEM_SIZE(mask);
238 			ex = sc->extent_mem;
239 		} else {
240 			/* XXX some devices give 32bit value */
241 			addr = PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END;
242 			size = PCI_MAPREG_IO_SIZE(mask);
243 			ex = sc->extent_port;
244 		}
245 
246 		if (!size) /* unused register */
247 			continue;
248 
249 		/* reservation/allocation phase */
250 		error += (*func) (sc, pc, tag, mapreg, ex, type, &addr, size);
251 
252 		PCIBIOS_PRINTV(("\t%02xh %s 0x%08x 0x%08x\n",
253 				mapreg, type ? "port" : "mem ",
254 				(unsigned int)addr, (unsigned int)size));
255 	}
256 
257 	if (error)
258 		sc->nbogus++;
259 
260 	PCIBIOS_PRINTV(("\t\t[%s]\n", error ? "NG" : "OK"));
261 }
262 
263 int
264 pciaddr_do_resource_allocate(struct pcibios_softc *sc, pci_chipset_tag_t pc,
265     pcitag_t tag, int mapreg, struct extent *ex, int type, bus_addr_t *addr,
266     bus_size_t size)
267 {
268 	bus_addr_t start;
269 	int error;
270 
271 	if (*addr) /* no need to allocate */
272 		return (0);
273 
274 	/* XXX Don't allocate if device is AGP device to avoid conflict. */
275 	if (pciaddr_device_is_agp(pc, tag))
276 		return (0);
277 
278 	start = (type == PCI_MAPREG_TYPE_MEM ? sc->mem_alloc_start
279 		: sc->port_alloc_start);
280 	if (start < ex->ex_start || start + size - 1 >= ex->ex_end) {
281 		PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
282 		return (1);
283 	}
284 	error = extent_alloc_subregion(ex, start, ex->ex_end, size, size, 0, 0,
285 	    EX_FAST|EX_NOWAIT|EX_MALLOCOK, addr);
286 	if (error) {
287 		PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
288 		return (1);
289 	}
290 
291 	/* write new address to PCI device configuration header */
292 	pci_conf_write(pc, tag, mapreg, *addr);
293 	/* check */
294 	if (pcibios_flags & PCIBIOS_VERBOSE) {
295 		printf("pci_addr_fixup: ");
296 		pciaddr_print_devid(pc, tag);
297 	}
298 
299 	if (pciaddr_ioaddr(pci_conf_read(pc, tag, mapreg)) != *addr) {
300 		pci_conf_write(pc, tag, mapreg, 0); /* clear */
301 		PCIBIOS_PRINTV(("fixup failed. (new address=%#x)\n", *addr));
302 		return (1);
303 	}
304 	PCIBIOS_PRINTV(("new address 0x%08x\n", *addr));
305 
306 	return (0);
307 }
308 
309 int
310 pciaddr_do_resource_reserve(struct pcibios_softc *sc, pci_chipset_tag_t pc,
311     pcitag_t tag, int mapreg, struct extent *ex, int type, bus_addr_t *addr,
312     bus_size_t size)
313 {
314 	pcireg_t val;
315 	int error;
316 
317 	if (*addr == 0)
318 		return (0);
319 
320 	val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
321 	if (type == PCI_MAPREG_TYPE_MEM &&
322 	    (val & PCI_COMMAND_MEM_ENABLE) != PCI_COMMAND_MEM_ENABLE)
323 		return (0);
324 	if (type == PCI_MAPREG_TYPE_IO &&
325 	    (val & PCI_COMMAND_IO_ENABLE) != PCI_COMMAND_IO_ENABLE)
326 		return (0);
327 
328 	error = extent_alloc_region(ex, *addr, size, EX_NOWAIT | EX_MALLOCOK);
329 	if (error) {
330 		PCIBIOS_PRINTV(("Resource conflict.\n"));
331 		pci_conf_write(pc, tag, mapreg, 0); /* clear */
332 		return (1);
333 	}
334 
335 	return (0);
336 }
337 
338 int
339 pciaddr_do_resource_reserve_disabled(struct pcibios_softc *sc,
340     pci_chipset_tag_t pc, pcitag_t tag, int mapreg, struct extent *ex, int type,
341     u_long *addr, bus_size_t size)
342 {
343 	pcireg_t val;
344 	int error;
345 
346 	if (*addr == 0)
347 		return (0);
348 
349 	val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
350 	if (type == PCI_MAPREG_TYPE_MEM &&
351 	    (val & PCI_COMMAND_MEM_ENABLE) == PCI_COMMAND_MEM_ENABLE)
352 		return (0);
353 	if (type == PCI_MAPREG_TYPE_IO &&
354 	    (val & PCI_COMMAND_IO_ENABLE) == PCI_COMMAND_IO_ENABLE)
355 		return (0);
356 
357 	PCIBIOS_PRINTV(("disabled %s space at addr 0x%x size 0x%x\n",
358 	    type == PCI_MAPREG_TYPE_MEM ? "mem" : "io", *addr, size));
359 
360 	error = extent_alloc_region(ex, *addr, size, EX_NOWAIT | EX_MALLOCOK);
361 	if (error) {
362 		PCIBIOS_PRINTV(("Resource conflict.\n"));
363 		pci_conf_write(pc, tag, mapreg, 0); /* clear */
364 		return (1);
365 	}
366 
367 	return (0);
368 }
369 
370 bus_addr_t
371 pciaddr_ioaddr(u_int32_t val)
372 {
373 	return ((PCI_MAPREG_TYPE(val) == PCI_MAPREG_TYPE_MEM)
374 		? PCI_MAPREG_MEM_ADDR(val)
375 		: (PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END));
376 }
377 
378 void
379 pciaddr_print_devid(pci_chipset_tag_t pc, pcitag_t tag)
380 {
381 	int bus, device, function;
382 	pcireg_t id;
383 
384 	id = pci_conf_read(pc, tag, PCI_ID_REG);
385 	pci_decompose_tag(pc, tag, &bus, &device, &function);
386 	printf("%03d:%02d:%d %04x:%04x\n", bus, device, function,
387 	       PCI_VENDOR(id), PCI_PRODUCT(id));
388 }
389 
390 int
391 pciaddr_device_is_agp(pci_chipset_tag_t pc, pcitag_t tag)
392 {
393 	pcireg_t class, status, rval;
394 	int off;
395 
396 	/* Check AGP device. */
397 	class = pci_conf_read(pc, tag, PCI_CLASS_REG);
398 	if (PCI_CLASS(class) == PCI_CLASS_DISPLAY) {
399 		status = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
400 		if (status & PCI_STATUS_CAPLIST_SUPPORT) {
401 			rval = pci_conf_read(pc, tag, PCI_CAPLISTPTR_REG);
402 			for (off = PCI_CAPLIST_PTR(rval);
403 			    off != 0;
404 			    off = PCI_CAPLIST_NEXT(rval) ) {
405 				rval = pci_conf_read(pc, tag, off);
406 				if (PCI_CAPLIST_CAP(rval) == PCI_CAP_AGP)
407 					return (1);
408 			}
409 		}
410 	}
411 	return (0);
412 }
413