Annotation of sys/arch/i386/pci/pci_addr_fixup.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: pci_addr_fixup.c,v 1.21 2007/02/20 21:15:01 tom 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:
414:
415: struct extent *
416: pciaddr_search(int mem_port, bus_addr_t *startp, bus_size_t size)
417: {
418: extern struct cfdriver pcibios_cd;
419: struct pcibios_softc *sc;
420:
421: sc = (struct pcibios_softc *)device_lookup(&pcibios_cd, 0);
422: if (sc && !(pcibios_flags & PCIBIOS_ADDR_FIXUP)) {
423: struct extent_region *rp;
424: struct extent *ex = mem_port? sc->extent_mem : sc->extent_port;
425:
426: /* Search the PCI I/O memory space extent for free
427: * space that will accommodate size. Remember that the
428: * extent stores allocated space and we're searching
429: * for the gaps.
430: *
431: * If we're at the end or the gap between this region
432: * and the next region big enough, then we're done
433: */
434: for (rp = LIST_FIRST(&ex->ex_regions);
435: rp && *startp + size > rp->er_start;
436: rp = LIST_NEXT(rp, er_link)) {
437: bus_addr_t new_start;
438:
439: new_start = (rp->er_end - 1 + size) & ~(size - 1);
440: if (new_start > *startp)
441: *startp = new_start;
442: }
443:
444: return (ex);
445: }
446:
447: return (NULL);
448: }
CVSweb