xref: /openbsd-src/sys/arch/i386/pci/pci_addr_fixup.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: pci_addr_fixup.c,v 1.11 2001/07/05 10:00:33 art 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 <machine/bus.h>
38 
39 #include <dev/pci/pcireg.h>
40 #include <dev/pci/pcivar.h>
41 #include <dev/pci/pcidevs.h>
42 
43 #include <i386/pci/pcibiosvar.h>
44 
45 typedef int (*pciaddr_resource_manage_func_t)
46 	__P((struct pcibios_softc *, pci_chipset_tag_t, pcitag_t, int,
47 	struct extent *, int, bus_addr_t *, bus_size_t));
48 void	pciaddr_resource_manage __P((struct pcibios_softc *,
49     pci_chipset_tag_t, pcitag_t, pciaddr_resource_manage_func_t));
50 void	pciaddr_resource_reserve __P((struct pcibios_softc *,
51     pci_chipset_tag_t, pcitag_t));
52 int	pciaddr_do_resource_reserve __P((struct pcibios_softc *,
53     pci_chipset_tag_t, pcitag_t, int, struct extent *, int,
54     bus_addr_t *, bus_size_t));
55 void	pciaddr_resource_allocate __P((struct pcibios_softc *,
56     pci_chipset_tag_t, pcitag_t));
57 int	pciaddr_do_resource_allocate __P((struct pcibios_softc *,
58     pci_chipset_tag_t, pcitag_t, int, struct extent *, int, bus_addr_t *,
59     bus_size_t));
60 bus_addr_t pciaddr_ioaddr __P((u_int32_t));
61 void	pciaddr_print_devid __P((pci_chipset_tag_t, pcitag_t));
62 
63 int	pciaddr_device_is_agp __P((pci_chipset_tag_t, pcitag_t));
64 
65 #define PCIADDR_MEM_START	0x0
66 #define PCIADDR_MEM_END		0xffffffff
67 #define PCIADDR_PORT_START	0x0
68 #define PCIADDR_PORT_END	0xffff
69 
70 /* for ISA devices */
71 #define PCIADDR_ISAPORT_RESERVE	0x5800 /* empirical value */
72 #define PCIADDR_ISAMEM_RESERVE	(16 * 1024 * 1024)
73 
74 void
75 pci_addr_fixup(sc, pc, maxbus)
76 	struct pcibios_softc *sc;
77 	pci_chipset_tag_t pc;
78 	int maxbus;
79 {
80 	extern paddr_t avail_end;
81 	const char *verbose_header =
82 		"[%s]-----------------------\n"
83 		"  device vendor product\n"
84 		"  register space address    size\n"
85 		"--------------------------------------------\n";
86 	const char *verbose_footer =
87 		"--------------------------[%3d devices bogus]\n";
88 
89 	const struct {
90 		bus_addr_t start;
91 		bus_size_t size;
92 		char *name;
93 	} system_reserve [] = {
94 		{ 0xfec00000, 0x100000, "I/O APIC" },
95 		{ 0xfee00000, 0x100000, "Local APIC" },
96 		{ 0xfffe0000, 0x20000, "BIOS PROM" },
97 		{ 0, 0, 0 }, /* terminator */
98 	}, *srp;
99 	paddr_t start;
100 	int error;
101 
102 	sc->extent_mem = extent_create("PCI I/O memory space",
103 	    PCIADDR_MEM_START, PCIADDR_MEM_END, M_DEVBUF, 0, 0, EX_NOWAIT);
104 	KASSERT(sc->extent_mem);
105 	sc->extent_port = extent_create("PCI I/O port space",
106 	    PCIADDR_PORT_START, PCIADDR_PORT_END, M_DEVBUF, 0, 0, EX_NOWAIT);
107 	KASSERT(sc->extent_port);
108 
109 	/*
110 	 * 1. check & reserve system BIOS setting.
111 	 */
112 	PCIBIOS_PRINTV((verbose_header, "System BIOS Setting"));
113 	pci_device_foreach(sc, pc, maxbus, pciaddr_resource_reserve);
114 	PCIBIOS_PRINTV((verbose_footer, sc->nbogus));
115 
116 	/*
117 	 * 2. reserve non-PCI area.
118 	 */
119 	for (srp = system_reserve; srp->size; srp++) {
120 		error = extent_alloc_region(sc->extent_mem, srp->start,
121 		    srp->size, EX_NOWAIT| EX_MALLOCOK);
122 		if (error != 0)
123 			printf("WARNING: can't reserve area for %s.\n",
124 			       srp->name);
125 	}
126 
127 	/*
128 	 * 3. determine allocation space
129 	 */
130 	start = i386_round_page(avail_end + 1);
131 	if (start < PCIADDR_ISAMEM_RESERVE)
132 		start = PCIADDR_ISAMEM_RESERVE;
133 	sc->mem_alloc_start = (start + 0x100000 + 1) & ~(0x100000 - 1);
134 	sc->port_alloc_start = PCIADDR_ISAPORT_RESERVE;
135 	PCIBIOS_PRINTV((" Physical memory end: 0x%08x\n PCI memory mapped I/O "
136 	    "space start: 0x%08x\n", avail_end, sc->mem_alloc_start));
137 
138 	if (sc->nbogus == 0)
139 		return; /* no need to fixup */
140 
141 	/*
142 	 * 4. do fixup
143 	 */
144 	PCIBIOS_PRINTV((verbose_header, "PCIBIOS fixup stage"));
145 	sc->nbogus = 0;
146 	pci_device_foreach(sc, pc, maxbus, pciaddr_resource_allocate);
147 	PCIBIOS_PRINTV((verbose_footer, sc->nbogus));
148 
149 }
150 
151 void
152 pciaddr_resource_reserve(sc, pc, tag)
153 	struct pcibios_softc *sc;
154 	pci_chipset_tag_t pc;
155 	pcitag_t tag;
156 {
157 	if (pcibios_flags & PCIBIOS_VERBOSE)
158 		pciaddr_print_devid(pc, tag);
159 	pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_reserve);
160 }
161 
162 void
163 pciaddr_resource_allocate(sc, pc, tag)
164 	struct pcibios_softc *sc;
165 	pci_chipset_tag_t pc;
166 	pcitag_t tag;
167 {
168 	if (pcibios_flags & PCIBIOS_VERBOSE)
169 		pciaddr_print_devid(pc, tag);
170 	pciaddr_resource_manage(sc, pc, tag, pciaddr_do_resource_allocate);
171 }
172 
173 void
174 pciaddr_resource_manage(sc, pc, tag, func)
175 	struct pcibios_softc *sc;
176 	pci_chipset_tag_t pc;
177 	pcitag_t tag;
178 	pciaddr_resource_manage_func_t func;
179 {
180 	struct extent *ex;
181 	pcireg_t val, mask;
182 	bus_addr_t addr;
183 	bus_size_t size;
184 	int error, mapreg, type, reg_start, reg_end, width;
185 
186 	val = pci_conf_read(pc, tag, PCI_BHLC_REG);
187 	switch (PCI_HDRTYPE_TYPE(val)) {
188 	default:
189 		printf("WARNING: unknown PCI device header.");
190 		sc->nbogus++;
191 		return;
192 	case 0:
193 		reg_start = PCI_MAPREG_START;
194 		reg_end   = PCI_MAPREG_END;
195 		break;
196 	case 1: /* PCI-PCI bridge */
197 		reg_start = PCI_MAPREG_START;
198 		reg_end   = PCI_MAPREG_PPB_END;
199 		break;
200 	case 2: /* PCI-CardBus bridge */
201 		reg_start = PCI_MAPREG_START;
202 		reg_end   = PCI_MAPREG_PCB_END;
203 		break;
204 	}
205 	error = 0;
206 
207 	for (mapreg = reg_start; mapreg < reg_end; mapreg += width) {
208 		/* inquire PCI device bus space requirement */
209 		val = pci_conf_read(pc, tag, mapreg);
210 		pci_conf_write(pc, tag, mapreg, ~0);
211 
212 		mask = pci_conf_read(pc, tag, mapreg);
213 		pci_conf_write(pc, tag, mapreg, val);
214 
215 		type = PCI_MAPREG_TYPE(val);
216 		width = 4;
217 		if (type == PCI_MAPREG_TYPE_MEM) {
218 			if (PCI_MAPREG_MEM_TYPE(val) ==
219 			    PCI_MAPREG_MEM_TYPE_64BIT) {
220 				/* XXX We could examine the upper 32 bits
221 				 * XXX of the BAR here, but we are totally
222 				 * XXX unprepared to handle a non-zero value,
223 				 * XXX either here or anywhere else in
224 				 * XXX i386-land.
225 				 * XXX So just arrange to not look at the
226 				 * XXX upper 32 bits, lest we misinterpret
227 				 * XXX it as a 32-bit BAR set to zero.
228 				 */
229 			    width = 8;
230 			}
231 			addr = PCI_MAPREG_MEM_ADDR(val);
232 			size = PCI_MAPREG_MEM_SIZE(mask);
233 			ex = sc->extent_mem;
234 		} else {
235 			/* XXX some devices give 32bit value */
236 			addr = PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END;
237 			size = PCI_MAPREG_IO_SIZE(mask);
238 			ex = sc->extent_port;
239 		}
240 
241 		if (!size) /* unused register */
242 			continue;
243 
244 		/* reservation/allocation phase */
245 		error += (*func) (sc, pc, tag, mapreg, ex, type, &addr, size);
246 
247 		PCIBIOS_PRINTV(("\t%02xh %s 0x%08x 0x%08x\n",
248 				mapreg, type ? "port" : "mem ",
249 				(unsigned int)addr, (unsigned int)size));
250 	}
251 
252 	/* enable/disable PCI device */
253 	val = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
254 	if (error == 0)
255 		val |= (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE |
256 			PCI_COMMAND_MASTER_ENABLE);
257 	else
258 		val &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE |
259 			 PCI_COMMAND_MASTER_ENABLE);
260 	pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, val);
261 
262 	if (error)
263 		sc->nbogus++;
264 
265 	PCIBIOS_PRINTV(("\t\t[%s]\n", error ? "NG" : "OK"));
266 }
267 
268 int
269 pciaddr_do_resource_allocate(sc, pc, tag, mapreg, ex, type, addr, size)
270 	struct pcibios_softc *sc;
271 	pci_chipset_tag_t pc;
272 	pcitag_t tag;
273 	struct extent *ex;
274 	int mapreg, type;
275 	bus_addr_t *addr;
276 	bus_size_t size;
277 {
278 	bus_addr_t start;
279 	int error;
280 
281 	if (*addr) /* no need to allocate */
282 		return (0);
283 
284 	/* XXX Don't allocate if device is AGP device to avoid conflict. */
285 	if (pciaddr_device_is_agp(pc, tag))
286 		return (0);
287 
288 	start = (type == PCI_MAPREG_TYPE_MEM ? sc->mem_alloc_start
289 		: sc->port_alloc_start);
290 	if (start < ex->ex_start || start + size - 1 >= ex->ex_end) {
291 		PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
292 		return (1);
293 	}
294 	error = extent_alloc_subregion(ex, start, ex->ex_end, size, size, 0, 0,
295 	    EX_FAST|EX_NOWAIT|EX_MALLOCOK, addr);
296 	if (error) {
297 		PCIBIOS_PRINTV(("No available resources. fixup failed\n"));
298 		return (1);
299 	}
300 
301 	/* write new address to PCI device configuration header */
302 	pci_conf_write(pc, tag, mapreg, *addr);
303 	/* check */
304 	if (!pcibios_flags & PCIBIOS_VERBOSE)
305 	{
306 		printf("pci_addr_fixup: ");
307 		pciaddr_print_devid(pc, tag);
308 	}
309 
310 	if (pciaddr_ioaddr(pci_conf_read(pc, tag, mapreg)) != *addr) {
311 		pci_conf_write(pc, tag, mapreg, 0); /* clear */
312 		printf("fixup failed. (new address=%#x)\n", *addr);
313 		return (1);
314 	}
315 	if (!pcibios_flags & PCIBIOS_VERBOSE)
316 		printf("new address 0x%08x\n", *addr);
317 
318 	return (0);
319 }
320 
321 int
322 pciaddr_do_resource_reserve(sc, pc, tag, mapreg, ex, type, addr, size)
323 	struct pcibios_softc *sc;
324 	pci_chipset_tag_t pc;
325 	pcitag_t tag;
326 	struct extent *ex;
327 	int type, mapreg;
328 	bus_addr_t *addr;
329 	bus_size_t size;
330 {
331 	int error;
332 
333 	if (*addr == 0)
334 		return (1);
335 
336 	error = extent_alloc_region(ex, *addr, size, EX_NOWAIT | EX_MALLOCOK);
337 	if (error) {
338 		PCIBIOS_PRINTV(("Resource conflict.\n"));
339 		pci_conf_write(pc, tag, mapreg, 0); /* clear */
340 		return (1);
341 	}
342 
343 	return (0);
344 }
345 
346 bus_addr_t
347 pciaddr_ioaddr(val)
348 	u_int32_t val;
349 {
350 	return ((PCI_MAPREG_TYPE(val) == PCI_MAPREG_TYPE_MEM)
351 		? PCI_MAPREG_MEM_ADDR(val)
352 		: (PCI_MAPREG_IO_ADDR(val) & PCIADDR_PORT_END));
353 }
354 
355 void
356 pciaddr_print_devid(pc, tag)
357 	pci_chipset_tag_t pc;
358 	pcitag_t tag;
359 {
360 	int bus, device, function;
361 	pcireg_t id;
362 
363 	id = pci_conf_read(pc, tag, PCI_ID_REG);
364 	pci_decompose_tag(pc, tag, &bus, &device, &function);
365 	printf("%03d:%02d:%d %04x:%04x\n", bus, device, function,
366 	       PCI_VENDOR(id), PCI_PRODUCT(id));
367 }
368 
369 int
370 pciaddr_device_is_agp(pc, tag)
371 	pci_chipset_tag_t pc;
372 	pcitag_t tag;
373 {
374 	pcireg_t class, status, rval;
375 	int off;
376 
377 	/* Check AGP device. */
378 	class = pci_conf_read(pc, tag, PCI_CLASS_REG);
379 	if (PCI_CLASS(class) == PCI_CLASS_DISPLAY) {
380 		status = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
381 		if (status & PCI_STATUS_CAPLIST_SUPPORT) {
382 			rval = pci_conf_read(pc, tag, PCI_CAPLISTPTR_REG);
383 			for (off = PCI_CAPLIST_PTR(rval);
384 			    off != 0;
385 			    off = PCI_CAPLIST_NEXT(rval) ) {
386 				rval = pci_conf_read(pc, tag, off);
387 				if (PCI_CAPLIST_CAP(rval) == PCI_CAP_AGP)
388 					return (1);
389 			}
390 		}
391 	}
392 	return (0);
393 }
394 
395 
396 struct extent *
397 pciaddr_search(mem_port, startp, size)
398 	int mem_port;
399 	bus_addr_t *startp;
400 	bus_size_t size;
401 {
402 	extern struct cfdriver pcibios_cd;
403 	struct pcibios_softc *sc;
404 
405 	sc = (struct pcibios_softc *)device_lookup(&pcibios_cd, 0);
406 	if (sc && !(pcibios_flags & PCIBIOS_ADDR_FIXUP)) {
407 		struct extent_region *rp;
408 		struct extent *ex = mem_port? sc->extent_mem : sc->extent_port;
409 
410 		/* Search the PCI I/O memory space extent for free
411 		 * space that will accomodate size.  Remember that the
412 		 * extent stores allocated space and we're searching
413 		 * for the gaps.
414 		 *
415 		 * If we're at the end or the gap between this region
416 		 * and the next region big enough, then we're done
417 		 */
418 		for (rp = LIST_FIRST(&ex->ex_regions);
419 		    rp && *startp + size > rp->er_start;
420 		    rp = LIST_NEXT(rp, er_link)) {
421 			bus_addr_t new_start;
422 
423 			new_start = (rp->er_end - 1 + size) & ~(size - 1);
424 			if (new_start > *startp)
425 				*startp = new_start;
426 		}
427 
428 		return (ex);
429 	}
430 
431 	return (NULL);
432 }
433