1 /* $NetBSD: pci_addr_fixup.c,v 1.3 2008/12/19 12:58:43 cegger Exp $ */
4 * Copyright (c) 2000 UCHIYAMA Yasushi. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: pci_addr_fixup.c,v 1.3 2008/12/19 12:58:43 cegger Exp $");
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/malloc.h>
35 #include <sys/kernel.h>
36 #include <sys/device.h>
37 #include <sys/extent.h>
39 #include <machine/bus.h>
41 #include <dev/pci/pcireg.h>
42 #include <dev/pci/pcivar.h>
43 #include <dev/pci/pcidevs.h>
45 #include <x86/pci/pci_addr_fixup.h>
47 struct pciaddr pciaddr
;
49 static int pciaddrverbose
= 0;
51 void pciaddr_resource_reserve(pci_chipset_tag_t
, pcitag_t
, void *context
);
52 int pciaddr_do_resource_reserve(pci_chipset_tag_t
, pcitag_t
, int,
53 void *, int, bus_addr_t
*, bus_size_t
);
54 void pciaddr_resource_allocate(pci_chipset_tag_t
, pcitag_t
, void *context
);
55 int pciaddr_do_resource_allocate(pci_chipset_tag_t
, pcitag_t
, int,
56 void *, int, bus_addr_t
*, bus_size_t
);
57 int device_is_agp(pci_chipset_tag_t
, pcitag_t
);
59 int device_is_agp(pci_chipset_tag_t
, pcitag_t
);
61 #define PCIADDR_MEM_START 0x0
62 #define PCIADDR_MEM_END 0xffffffff
63 #define PCIADDR_PORT_START 0x0
64 #define PCIADDR_PORT_END 0xffff
67 #define PCIADDR_ISAPORT_RESERVE 0x5800 /* empirical value */
68 #define PCIADDR_ISAMEM_RESERVE (16 * 1024 * 1024)
71 pci_addr_fixup(pci_chipset_tag_t pc
, int maxbus
)
73 extern paddr_t avail_end
;
74 const char *verbose_header
=
75 "[%s]-----------------------\n"
76 " device vendor product\n"
77 " register space address size\n"
78 "--------------------------------------------\n";
79 const char *verbose_footer
=
80 "--------------------------[%3d devices bogus]\n";
85 } system_reserve
[] = {
86 { 0xfec00000, 0x100000, "I/O APIC" },
87 { 0xfee00000, 0x100000, "Local APIC" },
88 { 0xfffe0000, 0x20000, "BIOS PROM" },
89 { 0, 0, 0 }, /* terminator */
94 pciaddr
.extent_mem
= extent_create("PCI I/O memory space",
97 M_DEVBUF
, 0, 0, EX_NOWAIT
);
98 KASSERT(pciaddr
.extent_mem
);
99 pciaddr
.extent_port
= extent_create("PCI I/O port space",
102 M_DEVBUF
, 0, 0, EX_NOWAIT
);
103 KASSERT(pciaddr
.extent_port
);
106 * 1. check & reserve system BIOS setting.
108 aprint_debug(verbose_header
, "System BIOS Setting");
109 pci_device_foreach(pc
, maxbus
, pciaddr_resource_reserve
, NULL
);
110 aprint_debug(verbose_footer
, pciaddr
.nbogus
);
113 * 2. reserve non-PCI area.
115 for (srp
= system_reserve
; srp
->size
; srp
++) {
116 error
= extent_alloc_region(pciaddr
.extent_mem
, srp
->start
,
118 EX_NOWAIT
| EX_MALLOCOK
);
120 aprint_error("WARNING: can't reserve area for %s.\n",
126 * 3. determine allocation space
128 start
= x86_round_page(avail_end
+ 1);
129 if (start
< PCIADDR_ISAMEM_RESERVE
)
130 start
= PCIADDR_ISAMEM_RESERVE
;
131 pciaddr
.mem_alloc_start
= (start
+ 0x100000 + 1) & ~(0x100000 - 1);
132 pciaddr
.port_alloc_start
= PCIADDR_ISAPORT_RESERVE
;
133 aprint_debug(" Physical memory end: 0x%08x\n PCI memory mapped I/O "
134 "space start: 0x%08x\n", (unsigned)avail_end
,
135 (unsigned)pciaddr
.mem_alloc_start
);
137 if (pciaddr
.nbogus
== 0)
138 return; /* no need to fixup */
143 aprint_debug(verbose_header
, "PCIBIOS fixup stage");
145 pci_device_foreach_min(pc
, 0, maxbus
, pciaddr_resource_allocate
, NULL
);
146 aprint_debug(verbose_footer
, pciaddr
.nbogus
);
151 pciaddr_resource_reserve(pci_chipset_tag_t pc
, pcitag_t tag
,
155 pciaddr_print_devid(pc
, tag
);
156 pciaddr_resource_manage(pc
, tag
,
157 pciaddr_do_resource_reserve
,
162 pciaddr_resource_allocate(pci_chipset_tag_t pc
, pcitag_t tag
,
166 pciaddr_print_devid(pc
, tag
);
167 pciaddr_resource_manage(pc
, tag
,
168 pciaddr_do_resource_allocate
,
173 pciaddr_resource_manage(pci_chipset_tag_t pc
, pcitag_t tag
,
174 pciaddr_resource_manage_func_t func
, void *ctx
)
179 int error
, useport
, usemem
, mapreg
, type
, reg_start
, reg_end
, width
;
181 val
= pci_conf_read(pc
, tag
, PCI_BHLC_REG
);
182 switch (PCI_HDRTYPE_TYPE(val
)) {
184 aprint_error("WARNING: unknown PCI device header.");
188 reg_start
= PCI_MAPREG_START
;
189 reg_end
= PCI_MAPREG_END
;
191 case 1: /* PCI-PCI bridge */
192 reg_start
= PCI_MAPREG_START
;
193 reg_end
= PCI_MAPREG_PPB_END
;
195 case 2: /* PCI-CardBus bridge */
196 reg_start
= PCI_MAPREG_START
;
197 reg_end
= PCI_MAPREG_PCB_END
;
200 error
= useport
= usemem
= 0;
202 for (mapreg
= reg_start
; mapreg
< reg_end
; mapreg
+= width
) {
203 /* inquire PCI device bus space requirement */
204 val
= pci_conf_read(pc
, tag
, mapreg
);
205 pci_conf_write(pc
, tag
, mapreg
, ~0);
207 mask
= pci_conf_read(pc
, tag
, mapreg
);
208 pci_conf_write(pc
, tag
, mapreg
, val
);
210 type
= PCI_MAPREG_TYPE(val
);
212 if (type
== PCI_MAPREG_TYPE_MEM
) {
213 if (PCI_MAPREG_MEM_TYPE(val
) ==
214 PCI_MAPREG_MEM_TYPE_64BIT
) {
215 /* XXX We could examine the upper 32 bits
216 * XXX of the BAR here, but we are totally
217 * XXX unprepared to handle a non-zero value,
218 * XXX either here or anywhere else in
220 * XXX So just arrange to not look at the
221 * XXX upper 32 bits, lest we misinterpret
222 * XXX it as a 32-bit BAR set to zero.
226 size
= PCI_MAPREG_MEM_SIZE(mask
);
228 size
= PCI_MAPREG_IO_SIZE(mask
);
230 addr
= pciaddr_ioaddr(val
);
232 if (!size
) /* unused register */
235 if (type
== PCI_MAPREG_TYPE_MEM
)
240 /* reservation/allocation phase */
241 error
+= (*func
) (pc
, tag
, mapreg
, ctx
, type
, &addr
, size
);
243 aprint_debug("\n\t%02xh %s 0x%08x 0x%08x",
244 mapreg
, type
? "port" : "mem ",
245 (unsigned int)addr
, (unsigned int)size
);
248 /* enable/disable PCI device */
249 val
= pci_conf_read(pc
, tag
, PCI_COMMAND_STATUS_REG
);
251 val
|= (PCI_COMMAND_IO_ENABLE
| PCI_COMMAND_MEM_ENABLE
|
252 PCI_COMMAND_MASTER_ENABLE
);
254 val
&= ~(PCI_COMMAND_IO_ENABLE
| PCI_COMMAND_MEM_ENABLE
|
255 PCI_COMMAND_MASTER_ENABLE
);
256 pci_conf_write(pc
, tag
, PCI_COMMAND_STATUS_REG
, val
);
261 aprint_debug("\n\t\t[%s]\n", error
? "NG" : "OK");
265 pciaddr_do_resource_allocate(pci_chipset_tag_t pc
, pcitag_t tag
,
266 int mapreg
, void *ctx
, int type
, bus_addr_t
*addr
, bus_size_t size
)
268 struct pciaddr
*pciaddrmap
= (struct pciaddr
*)ctx
;
273 if (*addr
) /* no need to allocate */
276 ex
= (type
== PCI_MAPREG_TYPE_MEM
?
277 pciaddrmap
->extent_mem
: pciaddrmap
->extent_port
);
279 /* XXX Don't allocate if device is AGP device to avoid conflict. */
280 if (device_is_agp(pc
, tag
))
283 start
= (type
== PCI_MAPREG_TYPE_MEM
?
284 pciaddrmap
->mem_alloc_start
: pciaddrmap
->port_alloc_start
);
286 if (start
< ex
->ex_start
|| start
+ size
- 1 >= ex
->ex_end
) {
287 aprint_debug("No available resources. fixup failed\n");
290 error
= extent_alloc_subregion(ex
, start
, ex
->ex_end
, size
,
292 EX_FAST
|EX_NOWAIT
|EX_MALLOCOK
, addr
);
294 aprint_debug("No available resources. fixup failed\n");
298 /* write new address to PCI device configuration header */
299 pci_conf_write(pc
, tag
, mapreg
, *addr
);
303 aprint_verbose("pci_addr_fixup: ");
304 pciaddr_print_devid(pc
, tag
);
306 if (pciaddr_ioaddr(pci_conf_read(pc
, tag
, mapreg
)) != *addr
) {
307 pci_conf_write(pc
, tag
, mapreg
, 0); /* clear */
308 aprint_error("fixup failed. (new address=%#x)\n", (unsigned)*addr
);
312 aprint_verbose("new address 0x%08x\n", (unsigned)*addr
);
318 pciaddr_do_resource_reserve(pci_chipset_tag_t pc
, pcitag_t tag
,
319 int mapreg
, void *ctx
, int type
, bus_addr_t
*addr
, bus_size_t size
)
322 struct pciaddr
*pciaddrmap
= (struct pciaddr
*)ctx
;
328 ex
= (type
== PCI_MAPREG_TYPE_MEM
?
329 pciaddrmap
->extent_mem
: pciaddrmap
->extent_port
);
331 error
= extent_alloc_region(ex
, *addr
, size
, EX_NOWAIT
| EX_MALLOCOK
);
333 aprint_debug("Resource conflict.\n");
334 pci_conf_write(pc
, tag
, mapreg
, 0); /* clear */
342 pciaddr_ioaddr(uint32_t val
)
344 return ((PCI_MAPREG_TYPE(val
) == PCI_MAPREG_TYPE_MEM
)
345 ? PCI_MAPREG_MEM_ADDR(val
)
346 : PCI_MAPREG_IO_ADDR(val
));
350 pciaddr_print_devid(pci_chipset_tag_t pc
, pcitag_t tag
)
352 int bus
, device
, function
;
355 id
= pci_conf_read(pc
, tag
, PCI_ID_REG
);
356 pci_decompose_tag(pc
, tag
, &bus
, &device
, &function
);
357 aprint_verbose("%03d:%02d:%d 0x%04x 0x%04x ", bus
, device
, function
,
358 PCI_VENDOR(id
), PCI_PRODUCT(id
));
362 device_is_agp(pci_chipset_tag_t pc
, pcitag_t tag
)
364 pcireg_t
class, status
, rval
;
367 /* Check AGP device. */
368 class = pci_conf_read(pc
, tag
, PCI_CLASS_REG
);
369 if (PCI_CLASS(class) == PCI_CLASS_DISPLAY
) {
370 status
= pci_conf_read(pc
, tag
, PCI_COMMAND_STATUS_REG
);
371 if (status
& PCI_STATUS_CAPLIST_SUPPORT
) {
372 rval
= pci_conf_read(pc
, tag
, PCI_CAPLISTPTR_REG
);
373 for (off
= PCI_CAPLIST_PTR(rval
);
375 off
= PCI_CAPLIST_NEXT(rval
) ) {
376 rval
= pci_conf_read(pc
, tag
, off
);
377 if (PCI_CAPLIST_CAP(rval
) == PCI_CAP_AGP
)