xref: /netbsd-src/sys/dev/acpi/acpi_mcfg.c (revision 5c830e3fd78bcb6adf5299a680bc9a988100ac16)
1*5c830e3fSmlelstv /*	$NetBSD: acpi_mcfg.c,v 1.30 2024/11/10 10:45:37 mlelstv Exp $	*/
2605f564fSmsaitoh 
3605f564fSmsaitoh /*-
4605f564fSmsaitoh  * Copyright (C) 2015 NONAKA Kimihiro <nonaka@NetBSD.org>
5605f564fSmsaitoh  * All rights reserved.
6605f564fSmsaitoh  *
7605f564fSmsaitoh  * Redistribution and use in source and binary forms, with or without
8605f564fSmsaitoh  * modification, are permitted provided that the following conditions
9605f564fSmsaitoh  * are met:
10605f564fSmsaitoh  * 1. Redistributions of source code must retain the above copyright
11605f564fSmsaitoh  *    notice, this list of conditions and the following disclaimer.
12605f564fSmsaitoh  * 2. Redistributions in binary form must reproduce the above copyright
13605f564fSmsaitoh  *    notice, this list of conditions and the following disclaimer in the
14605f564fSmsaitoh  *    documentation and/or other materials provided with the distribution.
15605f564fSmsaitoh  *
16605f564fSmsaitoh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17605f564fSmsaitoh  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18605f564fSmsaitoh  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19605f564fSmsaitoh  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20605f564fSmsaitoh  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21605f564fSmsaitoh  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22605f564fSmsaitoh  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23605f564fSmsaitoh  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24605f564fSmsaitoh  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25605f564fSmsaitoh  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26605f564fSmsaitoh  */
27605f564fSmsaitoh 
28aaf945cfSjmcneill #include "opt_pci.h"
29aaf945cfSjmcneill 
30605f564fSmsaitoh #include <sys/cdefs.h>
31*5c830e3fSmlelstv __KERNEL_RCSID(0, "$NetBSD: acpi_mcfg.c,v 1.30 2024/11/10 10:45:37 mlelstv Exp $");
32605f564fSmsaitoh 
33605f564fSmsaitoh #include <sys/param.h>
34605f564fSmsaitoh #include <sys/device.h>
35605f564fSmsaitoh #include <sys/kmem.h>
36605f564fSmsaitoh #include <sys/systm.h>
37605f564fSmsaitoh 
38605f564fSmsaitoh #include <dev/pci/pcireg.h>
39605f564fSmsaitoh #include <dev/pci/pcivar.h>
401099f047Sjmcneill #include <dev/pci/pci_resource.h>
41605f564fSmsaitoh #include <dev/pci/pcidevs.h>
42605f564fSmsaitoh 
43605f564fSmsaitoh #include <dev/acpi/acpireg.h>
44605f564fSmsaitoh #include <dev/acpi/acpivar.h>
45605f564fSmsaitoh #include <dev/acpi/acpi_mcfg.h>
46605f564fSmsaitoh 
47605f564fSmsaitoh #include "locators.h"
48605f564fSmsaitoh 
4925babac1Schristos #define _COMPONENT      ACPI_RESOURCE_COMPONENT
5025babac1Schristos ACPI_MODULE_NAME	("acpi_mcfg")
5125babac1Schristos 
52605f564fSmsaitoh #define	EXTCONF_OFFSET(d, f, r)	((((d) * 8 + (f)) * PCI_EXTCONF_SIZE) + (r))
53605f564fSmsaitoh 
54da1fbcb7Sjmcneill #define	PCIDEV_SET_VALID(mb, d, f)	((mb)->valid_devs[(d)] |= __BIT((f)))
55da1fbcb7Sjmcneill #define	PCIDEV_SET_INVALID(mb, d, f)	((mb)->valid_devs[(d)] &= ~__BIT((f)))
56da1fbcb7Sjmcneill #define	PCIDEV_IS_VALID(mb, d, f)	((mb)->valid_devs[(d)] & __BIT((f)))
57da1fbcb7Sjmcneill 
58da1fbcb7Sjmcneill #define	EXTCONF_SET_VALID(mb, d, f)	((mb)->valid_extconf[(d)] |= __BIT((f)))
59da1fbcb7Sjmcneill #define	EXTCONF_SET_INVALID(mb, d, f)	((mb)->valid_extconf[(d)] &= ~__BIT((f)))
60da1fbcb7Sjmcneill #define	EXTCONF_IS_VALID(mb, d, f)	((mb)->valid_extconf[(d)] & __BIT((f)))
61605f564fSmsaitoh 
62605f564fSmsaitoh struct mcfg_segment {
63605f564fSmsaitoh 	uint64_t ms_address;		/* Base address */
64605f564fSmsaitoh 	int ms_segment;			/* Segment # */
65605f564fSmsaitoh 	int ms_bus_start;		/* Start bus # */
66605f564fSmsaitoh 	int ms_bus_end;			/* End bus # */
67605f564fSmsaitoh 	bus_space_tag_t ms_bst;
68605f564fSmsaitoh 	struct mcfg_bus {
69605f564fSmsaitoh 		bus_space_handle_t bsh[32][8];
70605f564fSmsaitoh 		uint8_t valid_devs[32];
71da1fbcb7Sjmcneill 		uint8_t valid_extconf[32];
72605f564fSmsaitoh 		int valid_ndevs;
73605f564fSmsaitoh 		pcitag_t last_probed;
74605f564fSmsaitoh 	} *ms_bus;
75605f564fSmsaitoh };
76605f564fSmsaitoh 
77605f564fSmsaitoh static struct mcfg_segment *mcfg_segs;
78605f564fSmsaitoh static int mcfg_nsegs;
79605f564fSmsaitoh static ACPI_TABLE_MCFG *mcfg;
80605f564fSmsaitoh static int mcfg_inited;
81605f564fSmsaitoh static struct acpi_softc *acpi_sc;
82605f564fSmsaitoh 
83605f564fSmsaitoh static const struct acpimcfg_ops mcfg_default_ops = {
84605f564fSmsaitoh 	.ao_validate = acpimcfg_default_validate,
85605f564fSmsaitoh 
86605f564fSmsaitoh 	.ao_read = acpimcfg_default_read,
87605f564fSmsaitoh 	.ao_write = acpimcfg_default_write,
88605f564fSmsaitoh };
89605f564fSmsaitoh static const struct acpimcfg_ops *mcfg_ops = &mcfg_default_ops;
90605f564fSmsaitoh 
91605f564fSmsaitoh /*
92605f564fSmsaitoh  * default operations.
93605f564fSmsaitoh  */
94605f564fSmsaitoh bool
95605f564fSmsaitoh acpimcfg_default_validate(uint64_t address, int bus_start, int *bus_end)
96605f564fSmsaitoh {
97605f564fSmsaitoh 
98605f564fSmsaitoh 	/* Always Ok */
99605f564fSmsaitoh 	return true;
100605f564fSmsaitoh }
101605f564fSmsaitoh 
102605f564fSmsaitoh uint32_t
103605f564fSmsaitoh acpimcfg_default_read(bus_space_tag_t bst, bus_space_handle_t bsh,
104605f564fSmsaitoh     bus_addr_t addr)
105605f564fSmsaitoh {
106605f564fSmsaitoh 
107605f564fSmsaitoh 	return bus_space_read_4(bst, bsh, addr);
108605f564fSmsaitoh }
109605f564fSmsaitoh 
110605f564fSmsaitoh void
111605f564fSmsaitoh acpimcfg_default_write(bus_space_tag_t bst, bus_space_handle_t bsh,
112605f564fSmsaitoh     bus_addr_t addr, uint32_t data)
113605f564fSmsaitoh {
114605f564fSmsaitoh 
115605f564fSmsaitoh 	bus_space_write_4(bst, bsh, addr, data);
116605f564fSmsaitoh }
117605f564fSmsaitoh 
118605f564fSmsaitoh 
119605f564fSmsaitoh /*
120605f564fSmsaitoh  * Check MCFG memory region at system resource
121605f564fSmsaitoh  */
122605f564fSmsaitoh struct acpimcfg_memrange {
123605f564fSmsaitoh 	const char	*hid;
124605f564fSmsaitoh 	uint64_t	address;
125605f564fSmsaitoh 	int		bus_start;
126605f564fSmsaitoh 	int		bus_end;
127605f564fSmsaitoh 	bool		found;
128605f564fSmsaitoh };
129605f564fSmsaitoh 
130605f564fSmsaitoh static ACPI_STATUS
131605f564fSmsaitoh acpimcfg_parse_callback(ACPI_RESOURCE *res, void *ctx)
132605f564fSmsaitoh {
133605f564fSmsaitoh 	struct acpimcfg_memrange *mr = ctx;
134605f564fSmsaitoh 	const char *type;
135605f564fSmsaitoh 	uint64_t size, mapaddr, mapsize;
136605f564fSmsaitoh 	int n;
137605f564fSmsaitoh 
138605f564fSmsaitoh 	switch (res->Type) {
139605f564fSmsaitoh 	case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
140605f564fSmsaitoh 		type = "FIXED_MEMORY32";
141605f564fSmsaitoh 		mapaddr = res->Data.FixedMemory32.Address;
142605f564fSmsaitoh 		mapsize = res->Data.FixedMemory32.AddressLength;
143605f564fSmsaitoh 		break;
144605f564fSmsaitoh 
145605f564fSmsaitoh 	case ACPI_RESOURCE_TYPE_ADDRESS32:
146605f564fSmsaitoh 		/* XXX Only fixed size supported for now */
147605f564fSmsaitoh 		if (res->Data.Address32.Address.AddressLength == 0 ||
148605f564fSmsaitoh 		    res->Data.Address32.ProducerConsumer != ACPI_CONSUMER)
149605f564fSmsaitoh 			goto out;
150605f564fSmsaitoh 
151605f564fSmsaitoh 		if (res->Data.Address32.ResourceType != ACPI_MEMORY_RANGE)
152605f564fSmsaitoh 			goto out;
153605f564fSmsaitoh 
154605f564fSmsaitoh 		if (res->Data.Address32.MinAddressFixed != ACPI_ADDRESS_FIXED ||
155605f564fSmsaitoh 		    res->Data.Address32.MaxAddressFixed != ACPI_ADDRESS_FIXED)
156605f564fSmsaitoh 			goto out;
157605f564fSmsaitoh 
158605f564fSmsaitoh 		type = "ADDRESS32";
159605f564fSmsaitoh 		mapaddr = res->Data.Address32.Address.Minimum;
160605f564fSmsaitoh 		mapsize = res->Data.Address32.Address.AddressLength;
161605f564fSmsaitoh 		break;
162605f564fSmsaitoh 
163605f564fSmsaitoh #ifdef _LP64
164605f564fSmsaitoh 	case ACPI_RESOURCE_TYPE_ADDRESS64:
165605f564fSmsaitoh 		/* XXX Only fixed size supported for now */
166605f564fSmsaitoh 		if (res->Data.Address64.Address.AddressLength == 0 ||
167605f564fSmsaitoh 		    res->Data.Address64.ProducerConsumer != ACPI_CONSUMER)
168605f564fSmsaitoh 			goto out;
169605f564fSmsaitoh 
170605f564fSmsaitoh 		if (res->Data.Address64.ResourceType != ACPI_MEMORY_RANGE)
171605f564fSmsaitoh 			goto out;
172605f564fSmsaitoh 
173605f564fSmsaitoh 		if (res->Data.Address64.MinAddressFixed != ACPI_ADDRESS_FIXED ||
174605f564fSmsaitoh 		    res->Data.Address64.MaxAddressFixed != ACPI_ADDRESS_FIXED)
175605f564fSmsaitoh 			goto out;
176605f564fSmsaitoh 
177605f564fSmsaitoh 		type = "ADDRESS64";
178605f564fSmsaitoh 		mapaddr = res->Data.Address64.Address.Minimum;
179605f564fSmsaitoh 		mapsize = res->Data.Address64.Address.AddressLength;
180605f564fSmsaitoh 		break;
181605f564fSmsaitoh #endif
182605f564fSmsaitoh 
183605f564fSmsaitoh 	default:
184605f564fSmsaitoh  out:
185605f564fSmsaitoh 		aprint_debug_dev(acpi_sc->sc_dev, "MCFG: %s: Type=%d\n",
186605f564fSmsaitoh 		    mr->hid, res->Type);
187605f564fSmsaitoh 		return_ACPI_STATUS(AE_OK);
188605f564fSmsaitoh 	}
189605f564fSmsaitoh 
190605f564fSmsaitoh 	aprint_debug_dev(acpi_sc->sc_dev, "MCFG: %s: Type=%d(%s), "
191605f564fSmsaitoh 	    "Address=0x%016" PRIx64 ", Length=0x%016" PRIx64 "\n",
192605f564fSmsaitoh 	    mr->hid, res->Type, type, mapaddr, mapsize);
193605f564fSmsaitoh 
194605f564fSmsaitoh 	if (mr->address < mapaddr || mr->address >= mapaddr + mapsize)
195605f564fSmsaitoh 		return_ACPI_STATUS(AE_OK);
196605f564fSmsaitoh 
197605f564fSmsaitoh 	size = (mr->bus_end - mr->bus_start + 1) * ACPIMCFG_SIZE_PER_BUS;
198605f564fSmsaitoh 
199605f564fSmsaitoh 	/* full map */
200605f564fSmsaitoh 	if (mr->address + size <= mapaddr + mapsize) {
201605f564fSmsaitoh 		mr->found = true;
202605f564fSmsaitoh 		return_ACPI_STATUS(AE_CTRL_TERMINATE);
203605f564fSmsaitoh 	}
204605f564fSmsaitoh 
205605f564fSmsaitoh 	/* partial map */
206605f564fSmsaitoh 	n = (mapsize - (mr->address - mapaddr)) / ACPIMCFG_SIZE_PER_BUS;
207605f564fSmsaitoh 	/* bus_start == bus_end is not allowed. */
208605f564fSmsaitoh 	if (n > 1) {
209605f564fSmsaitoh 		mr->bus_end = mr->bus_start + n - 1;
210605f564fSmsaitoh 		mr->found = true;
211605f564fSmsaitoh 		return_ACPI_STATUS(AE_CTRL_TERMINATE);
212605f564fSmsaitoh 	}
213605f564fSmsaitoh 
214605f564fSmsaitoh 	aprint_debug_dev(acpi_sc->sc_dev, "MCFG: bus %d-%d, "
215605f564fSmsaitoh 	    "address 0x%016" PRIx64 ": invalid size: request 0x%016" PRIx64
216605f564fSmsaitoh 	    ", actual 0x%016" PRIx64 "\n",
217605f564fSmsaitoh 	    mr->bus_start, mr->bus_end, mr->address, size, mapsize);
218605f564fSmsaitoh 
219605f564fSmsaitoh 	return_ACPI_STATUS(AE_OK);
220605f564fSmsaitoh }
221605f564fSmsaitoh 
222605f564fSmsaitoh static ACPI_STATUS
223605f564fSmsaitoh acpimcfg_check_system_resource(ACPI_HANDLE handle, UINT32 level, void *ctx,
224605f564fSmsaitoh     void **retval)
225605f564fSmsaitoh {
226605f564fSmsaitoh 	struct acpimcfg_memrange *mr = ctx;
227605f564fSmsaitoh 	ACPI_STATUS status;
228605f564fSmsaitoh 
229605f564fSmsaitoh 	status = AcpiWalkResources(handle, "_CRS", acpimcfg_parse_callback, mr);
230605f564fSmsaitoh 	if (ACPI_FAILURE(status))
231605f564fSmsaitoh 		return_ACPI_STATUS(status);
232605f564fSmsaitoh 
233605f564fSmsaitoh 	if (mr->found)
234605f564fSmsaitoh 		return_ACPI_STATUS(AE_CTRL_TERMINATE);
235605f564fSmsaitoh 
236605f564fSmsaitoh 	aprint_debug_dev(acpi_sc->sc_dev, "MCFG: %s: bus %d-%d, "
237605f564fSmsaitoh 	    "address 0x%016" PRIx64 ": no valid region\n", mr->hid,
238605f564fSmsaitoh 	    mr->bus_start, mr->bus_end, mr->address);
239605f564fSmsaitoh 
240605f564fSmsaitoh 	return_ACPI_STATUS(AE_OK);
241605f564fSmsaitoh }
242605f564fSmsaitoh 
243605f564fSmsaitoh static bool
244605f564fSmsaitoh acpimcfg_find_system_resource(uint64_t address, int bus_start, int *bus_end)
245605f564fSmsaitoh {
246605f564fSmsaitoh 	static const char *system_resource_hid[] = {
247605f564fSmsaitoh 		"PNP0C01",	/* System Board */
248605f564fSmsaitoh 		"PNP0C02"	/* General ID for reserving resources */
249605f564fSmsaitoh 	};
250605f564fSmsaitoh 	struct acpimcfg_memrange mr;
251605f564fSmsaitoh 	ACPI_STATUS status;
252605f564fSmsaitoh 	int i;
253605f564fSmsaitoh 
254605f564fSmsaitoh 	mr.address = address;
255605f564fSmsaitoh 	mr.bus_start = bus_start;
256605f564fSmsaitoh 	mr.bus_end = *bus_end;
257605f564fSmsaitoh 	mr.found = false;
258605f564fSmsaitoh 
259605f564fSmsaitoh 	for (i = 0; i < __arraycount(system_resource_hid); i++) {
260605f564fSmsaitoh 		mr.hid = system_resource_hid[i];
261605f564fSmsaitoh 		status = AcpiGetDevices(__UNCONST(system_resource_hid[i]),
262605f564fSmsaitoh 		    acpimcfg_check_system_resource, &mr, NULL);
263605f564fSmsaitoh 		if (ACPI_FAILURE(status))
264605f564fSmsaitoh 			continue;
265605f564fSmsaitoh 		if (mr.found) {
266605f564fSmsaitoh 			*bus_end = mr.bus_end;
267605f564fSmsaitoh 			return true;
268605f564fSmsaitoh 		}
269605f564fSmsaitoh 	}
270605f564fSmsaitoh 	return false;
271605f564fSmsaitoh }
272605f564fSmsaitoh 
273605f564fSmsaitoh 
274605f564fSmsaitoh /*
275605f564fSmsaitoh  * ACPI MCFG
276605f564fSmsaitoh  */
277605f564fSmsaitoh void
278605f564fSmsaitoh acpimcfg_probe(struct acpi_softc *sc)
279605f564fSmsaitoh {
280605f564fSmsaitoh 	ACPI_MCFG_ALLOCATION *ama;
281605f564fSmsaitoh 	ACPI_STATUS status;
282605f564fSmsaitoh 	uint32_t offset;
283605f564fSmsaitoh 	int i, nsegs;
284605f564fSmsaitoh 
285605f564fSmsaitoh 	if (acpi_sc != NULL)
286605f564fSmsaitoh 		panic("acpi_sc != NULL");
287605f564fSmsaitoh 	acpi_sc = sc;
288605f564fSmsaitoh 
289605f564fSmsaitoh 	status = AcpiGetTable(ACPI_SIG_MCFG, 0, (ACPI_TABLE_HEADER **)&mcfg);
290605f564fSmsaitoh 	if (ACPI_FAILURE(status)) {
291605f564fSmsaitoh 		mcfg = NULL;
292605f564fSmsaitoh 		return;
293605f564fSmsaitoh 	}
294605f564fSmsaitoh 
295605f564fSmsaitoh 	nsegs = 0;
296605f564fSmsaitoh 	offset = sizeof(ACPI_TABLE_MCFG);
297605f564fSmsaitoh 	ama = ACPI_ADD_PTR(ACPI_MCFG_ALLOCATION, mcfg, offset);
298144c24beSmaxv 	for (i = 0; offset + sizeof(ACPI_MCFG_ALLOCATION) <=
299144c24beSmaxv 	    mcfg->Header.Length; i++) {
300605f564fSmsaitoh 		aprint_debug_dev(sc->sc_dev,
301605f564fSmsaitoh 		    "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 "\n",
302605f564fSmsaitoh 		    ama->PciSegment, ama->StartBusNumber, ama->EndBusNumber,
303605f564fSmsaitoh 		    ama->Address);
304605f564fSmsaitoh 		nsegs++;
305605f564fSmsaitoh 		offset += sizeof(ACPI_MCFG_ALLOCATION);
306aaeacaa7Sjmcneill 		ama = ACPI_ADD_PTR(ACPI_MCFG_ALLOCATION, mcfg, offset);
307605f564fSmsaitoh 	}
308605f564fSmsaitoh 	if (nsegs == 0) {
309605f564fSmsaitoh 		mcfg = NULL;
310605f564fSmsaitoh 		return;
311605f564fSmsaitoh 	}
312605f564fSmsaitoh 
313605f564fSmsaitoh 	mcfg_segs = kmem_zalloc(sizeof(*mcfg_segs) * nsegs, KM_SLEEP);
314605f564fSmsaitoh 	mcfg_nsegs = nsegs;
315605f564fSmsaitoh }
316605f564fSmsaitoh 
317605f564fSmsaitoh int
318605f564fSmsaitoh acpimcfg_init(bus_space_tag_t memt, const struct acpimcfg_ops *ops)
319605f564fSmsaitoh {
320605f564fSmsaitoh 	ACPI_MCFG_ALLOCATION *ama;
321605f564fSmsaitoh 	struct mcfg_segment *seg;
322605f564fSmsaitoh 	uint32_t offset;
323605f564fSmsaitoh 	int i, n, nsegs, bus_end;
324605f564fSmsaitoh 
325605f564fSmsaitoh 	if (mcfg == NULL)
326605f564fSmsaitoh 		return ENXIO;
327605f564fSmsaitoh 
328605f564fSmsaitoh 	if (mcfg_inited)
329605f564fSmsaitoh 		return 0;
330605f564fSmsaitoh 
331605f564fSmsaitoh 	if (ops != NULL)
332605f564fSmsaitoh 		mcfg_ops = ops;
333605f564fSmsaitoh 
334605f564fSmsaitoh 	nsegs = 0;
335605f564fSmsaitoh 	offset = sizeof(ACPI_TABLE_MCFG);
336605f564fSmsaitoh 	ama = ACPI_ADD_PTR(ACPI_MCFG_ALLOCATION, mcfg, offset);
337605f564fSmsaitoh 	for (i = 0; offset < mcfg->Header.Length; i++) {
338605f564fSmsaitoh #ifndef _LP64
339605f564fSmsaitoh 		if (ama->Address >= 0x100000000ULL) {
340605f564fSmsaitoh 			aprint_debug_dev(acpi_sc->sc_dev,
341605f564fSmsaitoh 			    "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64
342605f564fSmsaitoh 			    ": ignore (64bit address)\n", ama->PciSegment,
343605f564fSmsaitoh 			    ama->StartBusNumber, ama->EndBusNumber,
344605f564fSmsaitoh 			    ama->Address);
345605f564fSmsaitoh 			goto next;
346605f564fSmsaitoh 		}
347605f564fSmsaitoh #endif
348605f564fSmsaitoh 		/*
349605f564fSmsaitoh 		 * Some (broken?) BIOSen have an MCFG table for an empty
350605f564fSmsaitoh 		 * bus range.  Ignore those tables.
351605f564fSmsaitoh 		 */
352605f564fSmsaitoh 		if (ama->StartBusNumber > ama->EndBusNumber) {
353605f564fSmsaitoh 			aprint_debug_dev(acpi_sc->sc_dev,
354605f564fSmsaitoh 			    "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64
355605f564fSmsaitoh 			    ": ignore (bus %d > %d)\n", ama->PciSegment,
356605f564fSmsaitoh 			    ama->StartBusNumber, ama->EndBusNumber,
357605f564fSmsaitoh 			    ama->Address, ama->StartBusNumber,
358605f564fSmsaitoh 			    ama->EndBusNumber);
359605f564fSmsaitoh 			goto next;
360605f564fSmsaitoh 		}
361605f564fSmsaitoh 
362605f564fSmsaitoh 		/* Validate MCFG memory range */
363605f564fSmsaitoh 		bus_end = ama->EndBusNumber;
364605f564fSmsaitoh 		if (mcfg_ops->ao_validate != NULL &&
365605f564fSmsaitoh 		    !mcfg_ops->ao_validate(ama->Address, ama->StartBusNumber,
366605f564fSmsaitoh 		      &bus_end)) {
367605f564fSmsaitoh 			if (!acpimcfg_find_system_resource(ama->Address,
368605f564fSmsaitoh 			    ama->StartBusNumber, &bus_end)) {
369605f564fSmsaitoh 				aprint_debug_dev(acpi_sc->sc_dev,
370605f564fSmsaitoh 				    "MCFG: segment %d, bus %d-%d, "
371605f564fSmsaitoh 				    "address 0x%016" PRIx64
372605f564fSmsaitoh 				    ": ignore (invalid address)\n",
373605f564fSmsaitoh 				    ama->PciSegment,
374605f564fSmsaitoh 				    ama->StartBusNumber, ama->EndBusNumber,
375605f564fSmsaitoh 				    ama->Address);
376605f564fSmsaitoh 				goto next;
377605f564fSmsaitoh 			}
378605f564fSmsaitoh 		}
379605f564fSmsaitoh 		if (ama->EndBusNumber != bus_end) {
380605f564fSmsaitoh 			aprint_debug_dev(acpi_sc->sc_dev,
381605f564fSmsaitoh 			    "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64
382605f564fSmsaitoh 			    " -> bus %d-%d\n", ama->PciSegment,
383605f564fSmsaitoh 			    ama->StartBusNumber, ama->EndBusNumber,
384605f564fSmsaitoh 			    ama->Address, ama->StartBusNumber, bus_end);
385605f564fSmsaitoh 		}
386605f564fSmsaitoh 
38703660f55Sjmcneill #ifndef __HAVE_PCI_GET_SEGMENT
388605f564fSmsaitoh 		if (ama->PciSegment != 0) {
389605f564fSmsaitoh 			aprint_debug_dev(acpi_sc->sc_dev,
390605f564fSmsaitoh 			    "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64
391605f564fSmsaitoh 			    ": ignore (non PCI segment 0)\n", ama->PciSegment,
392605f564fSmsaitoh 			    ama->StartBusNumber, bus_end, ama->Address);
393605f564fSmsaitoh 			goto next;
394605f564fSmsaitoh 		}
39503660f55Sjmcneill #endif
396605f564fSmsaitoh 
397605f564fSmsaitoh 		seg = &mcfg_segs[nsegs++];
398605f564fSmsaitoh 		seg->ms_address = ama->Address;
399605f564fSmsaitoh 		seg->ms_segment = ama->PciSegment;
400605f564fSmsaitoh 		seg->ms_bus_start = ama->StartBusNumber;
401605f564fSmsaitoh 		seg->ms_bus_end = bus_end;
402605f564fSmsaitoh 		seg->ms_bst = memt;
403605f564fSmsaitoh 		n = seg->ms_bus_end - seg->ms_bus_start + 1;
404605f564fSmsaitoh 		seg->ms_bus = kmem_zalloc(sizeof(*seg->ms_bus) * n, KM_SLEEP);
405605f564fSmsaitoh 
406605f564fSmsaitoh  next:
407605f564fSmsaitoh 		offset += sizeof(ACPI_MCFG_ALLOCATION);
408aaeacaa7Sjmcneill 		ama = ACPI_ADD_PTR(ACPI_MCFG_ALLOCATION, mcfg, offset);
409605f564fSmsaitoh 	}
410605f564fSmsaitoh 	if (nsegs == 0)
411605f564fSmsaitoh 		return ENOENT;
412605f564fSmsaitoh 
413605f564fSmsaitoh 	for (i = 0; i < nsegs; i++) {
414605f564fSmsaitoh 		seg = &mcfg_segs[i];
415605f564fSmsaitoh 		aprint_verbose_dev(acpi_sc->sc_dev,
416605f564fSmsaitoh 		    "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 "\n",
417605f564fSmsaitoh 		    seg->ms_segment, seg->ms_bus_start, seg->ms_bus_end,
418605f564fSmsaitoh 		    seg->ms_address);
419605f564fSmsaitoh 	}
420605f564fSmsaitoh 
421605f564fSmsaitoh 	/* Update # of segment */
422605f564fSmsaitoh 	mcfg_nsegs = nsegs;
423605f564fSmsaitoh 	mcfg_inited = true;
424605f564fSmsaitoh 
425605f564fSmsaitoh 	return 0;
426605f564fSmsaitoh }
427605f564fSmsaitoh 
428605f564fSmsaitoh static int
429605f564fSmsaitoh acpimcfg_ext_conf_is_aliased(pci_chipset_tag_t pc, pcitag_t tag)
430605f564fSmsaitoh {
431605f564fSmsaitoh 	pcireg_t id;
432605f564fSmsaitoh 	int i;
433605f564fSmsaitoh 
434605f564fSmsaitoh 	id = pci_conf_read(pc, tag, PCI_ID_REG);
435605f564fSmsaitoh 	for (i = PCI_CONF_SIZE; i < PCI_EXTCONF_SIZE; i += PCI_CONF_SIZE) {
436605f564fSmsaitoh 		if (pci_conf_read(pc, tag, i) != id)
437605f564fSmsaitoh 			return false;
438605f564fSmsaitoh 	}
439605f564fSmsaitoh 	return true;
440605f564fSmsaitoh }
441605f564fSmsaitoh 
442605f564fSmsaitoh static struct mcfg_segment *
44399199cfcSjmcneill acpimcfg_get_segment(pci_chipset_tag_t pc, int bus)
444605f564fSmsaitoh {
445605f564fSmsaitoh 	struct mcfg_segment *seg;
44699199cfcSjmcneill 	u_int segment;
447605f564fSmsaitoh 	int i;
448605f564fSmsaitoh 
44999199cfcSjmcneill 	segment = pci_get_segment(pc);
450605f564fSmsaitoh 	for (i = 0; i < mcfg_nsegs; i++) {
451605f564fSmsaitoh 		seg = &mcfg_segs[i];
45299199cfcSjmcneill 		if (segment == seg->ms_segment &&
45399199cfcSjmcneill 		    bus >= seg->ms_bus_start && bus <= seg->ms_bus_end)
454605f564fSmsaitoh 			return seg;
455605f564fSmsaitoh 	}
456605f564fSmsaitoh 	return NULL;
457605f564fSmsaitoh }
458605f564fSmsaitoh 
459605f564fSmsaitoh static int
460605f564fSmsaitoh acpimcfg_device_probe(const struct pci_attach_args *pa)
461605f564fSmsaitoh {
462605f564fSmsaitoh 	pci_chipset_tag_t pc = pa->pa_pc;
463605f564fSmsaitoh 	struct mcfg_segment *seg;
464605f564fSmsaitoh 	struct mcfg_bus *mb;
465605f564fSmsaitoh 	pcitag_t tag;
466605f564fSmsaitoh 	pcireg_t reg;
467605f564fSmsaitoh 	int bus = pa->pa_bus;
468605f564fSmsaitoh 	int dev = pa->pa_device;
469605f564fSmsaitoh 	int func = pa->pa_function;
470605f564fSmsaitoh 	int last_dev, last_func, end_func;
471605f564fSmsaitoh 	int alias = 0;
472e54d5373Smsaitoh 	const struct pci_quirkdata *qd;
473e54d5373Smsaitoh 	bool force_hasextcnf = false;
474e54d5373Smsaitoh 	bool force_noextcnf = false;
475605f564fSmsaitoh 	int i, j;
476605f564fSmsaitoh 
47799199cfcSjmcneill 	seg = acpimcfg_get_segment(pc, bus);
478605f564fSmsaitoh 	if (seg == NULL)
479605f564fSmsaitoh 		return 0;
480605f564fSmsaitoh 
481605f564fSmsaitoh 	mb = &seg->ms_bus[bus - seg->ms_bus_start];
482605f564fSmsaitoh 	tag = pci_make_tag(pc, bus, dev, func);
483605f564fSmsaitoh 
484605f564fSmsaitoh 	/* Mark invalid between last probed device to probed device. */
485605f564fSmsaitoh 	pci_decompose_tag(pc, mb->last_probed, NULL, &last_dev, &last_func);
486605f564fSmsaitoh 	if (dev != 0 || func != 0) {
487605f564fSmsaitoh 		for (i = last_dev; i <= dev; i++) {
488605f564fSmsaitoh 			end_func = (i == dev) ? func : 8;
489605f564fSmsaitoh 			for (j = last_func; j < end_func; j++) {
490605f564fSmsaitoh 				if (i == last_dev && j == last_func)
491605f564fSmsaitoh 					continue;
492da1fbcb7Sjmcneill 				PCIDEV_SET_INVALID(mb, i, j);
493605f564fSmsaitoh 			}
494605f564fSmsaitoh 			last_func = 0;
495605f564fSmsaitoh 		}
496605f564fSmsaitoh 	}
497605f564fSmsaitoh 	mb->last_probed = tag;
498605f564fSmsaitoh 
499e54d5373Smsaitoh 	reg = pci_conf_read(pc, tag, PCI_ID_REG);
500e54d5373Smsaitoh 	qd = pci_lookup_quirkdata(PCI_VENDOR(reg), PCI_PRODUCT(reg));
501e54d5373Smsaitoh 	if (qd != NULL && (qd->quirks & PCI_QUIRK_HASEXTCNF) != 0)
502e54d5373Smsaitoh 		force_hasextcnf = true;
503e54d5373Smsaitoh 	if (qd != NULL && (qd->quirks & PCI_QUIRK_NOEXTCNF) != 0)
504e54d5373Smsaitoh 		force_noextcnf = true;
505e54d5373Smsaitoh 
506605f564fSmsaitoh 	/* Probe extended configuration space. */
507e54d5373Smsaitoh 	if ((!force_hasextcnf) && ((force_noextcnf) ||
508e54d5373Smsaitoh 		((reg = pci_conf_read(pc, tag, PCI_CONF_SIZE)) == (pcireg_t)-1)
509e54d5373Smsaitoh 		|| (reg == 0)
510e54d5373Smsaitoh 		|| (alias = acpimcfg_ext_conf_is_aliased(pc, tag)))) {
511605f564fSmsaitoh 		aprint_debug_dev(acpi_sc->sc_dev,
512605f564fSmsaitoh 		    "MCFG: %03d:%02d:%d: invalid config space "
513605f564fSmsaitoh 		    "(cfg[0x%03x]=0x%08x, alias=%s)\n", bus, dev, func,
514605f564fSmsaitoh 		    PCI_CONF_SIZE, reg, alias ? "true" : "false");
515605f564fSmsaitoh 		EXTCONF_SET_INVALID(mb, dev, func);
516605f564fSmsaitoh 	}
517605f564fSmsaitoh 
518da1fbcb7Sjmcneill 	aprint_debug_dev(acpi_sc->sc_dev,
519da1fbcb7Sjmcneill 	    "MCFG: %03d:%02d:%d: Ok (cfg[0x%03x]=0x%08x extconf=%c)\n",
520da1fbcb7Sjmcneill 	    bus, dev, func, PCI_CONF_SIZE, reg,
521da1fbcb7Sjmcneill 	    EXTCONF_IS_VALID(mb, dev, func) ? 'Y' : 'N');
522da1fbcb7Sjmcneill 	mb->valid_ndevs++;
523da1fbcb7Sjmcneill 
524605f564fSmsaitoh 	return 0;
525605f564fSmsaitoh }
526605f564fSmsaitoh 
527605f564fSmsaitoh static void
528605f564fSmsaitoh acpimcfg_scan_bus(struct pci_softc *sc, pci_chipset_tag_t pc, int bus)
529605f564fSmsaitoh {
530605f564fSmsaitoh 	static const int wildcard[PCICF_NLOCS] = {
531605f564fSmsaitoh 		PCICF_DEV_DEFAULT, PCICF_FUNCTION_DEFAULT
532605f564fSmsaitoh 	};
533605f564fSmsaitoh 
534605f564fSmsaitoh 	sc->sc_bus = bus;	/* XXX */
53552b17e17Sjmcneill 	sc->sc_pc = pc;
536605f564fSmsaitoh 
537605f564fSmsaitoh 	pci_enumerate_bus(sc, wildcard, acpimcfg_device_probe, NULL);
538605f564fSmsaitoh }
539605f564fSmsaitoh 
540605f564fSmsaitoh int
541605f564fSmsaitoh acpimcfg_map_bus(device_t self, pci_chipset_tag_t pc, int bus)
542605f564fSmsaitoh {
543605f564fSmsaitoh 	struct pci_softc *sc = device_private(self);
544605f564fSmsaitoh 	struct mcfg_segment *seg = NULL;
545605f564fSmsaitoh 	struct mcfg_bus *mb;
546605f564fSmsaitoh 	bus_space_handle_t bsh;
547605f564fSmsaitoh 	bus_addr_t baddr;
548f0929e69Shannken 	pcitag_t tag;
549f0929e69Shannken 	pcireg_t reg;
550f0929e69Shannken 	bool is_e7520_mch;
551605f564fSmsaitoh 	int boff;
552605f564fSmsaitoh 	int last_dev, last_func;
553605f564fSmsaitoh 	int i, j;
554605f564fSmsaitoh 	int error;
555605f564fSmsaitoh 
556605f564fSmsaitoh 	if (!mcfg_inited)
557605f564fSmsaitoh 		return ENXIO;
558605f564fSmsaitoh 
55999199cfcSjmcneill 	seg = acpimcfg_get_segment(pc, bus);
560605f564fSmsaitoh 	if (seg == NULL)
561605f564fSmsaitoh 		return ENOENT;
562605f564fSmsaitoh 
563605f564fSmsaitoh 	boff = bus - seg->ms_bus_start;
564605f564fSmsaitoh 	if (seg->ms_bus[boff].valid_ndevs > 0)
565605f564fSmsaitoh 		return 0;
566605f564fSmsaitoh 
567605f564fSmsaitoh 	mb = &seg->ms_bus[boff];
568a266bf0eSjmcneill 	baddr = seg->ms_address + (bus * ACPIMCFG_SIZE_PER_BUS);
569605f564fSmsaitoh 
570773e7c3eSjmcneill 	/* Map extended configuration space of all dev/func. */
571605f564fSmsaitoh 	error = bus_space_map(seg->ms_bst, baddr, ACPIMCFG_SIZE_PER_BUS, 0,
572605f564fSmsaitoh 	    &bsh);
573605f564fSmsaitoh 	if (error != 0)
574605f564fSmsaitoh 		return error;
575605f564fSmsaitoh 	for (i = 0; i < 32; i++) {
576605f564fSmsaitoh 		for (j = 0; j < 8; j++) {
577605f564fSmsaitoh 			error = bus_space_subregion(seg->ms_bst, bsh,
578605f564fSmsaitoh 			    EXTCONF_OFFSET(i, j, 0), PCI_EXTCONF_SIZE,
579605f564fSmsaitoh 			    &mb->bsh[i][j]);
580605f564fSmsaitoh 			if (error != 0)
581605f564fSmsaitoh 				break;
582605f564fSmsaitoh 		}
583605f564fSmsaitoh 	}
584605f564fSmsaitoh 	if (error != 0)
585605f564fSmsaitoh 		return error;
586605f564fSmsaitoh 
587605f564fSmsaitoh 	aprint_debug("\n");
588605f564fSmsaitoh 
589605f564fSmsaitoh 	/* Probe extended configuration space of all devices. */
590605f564fSmsaitoh 	memset(mb->valid_devs, 0xff, sizeof(mb->valid_devs));
591da1fbcb7Sjmcneill 	memset(mb->valid_extconf, 0xff, sizeof(mb->valid_extconf));
592605f564fSmsaitoh 	mb->valid_ndevs = 0;
593605f564fSmsaitoh 	mb->last_probed = pci_make_tag(pc, bus, 0, 0);
594605f564fSmsaitoh 
595f0929e69Shannken 	/*
596f0929e69Shannken 	 * On an Intel E7520 we have to temporarily disable
597f0929e69Shannken 	 * Enhanced Config Access error detection and reporting
598f0929e69Shannken 	 * by setting the appropriate error mask in HI_ERRMASK register.
599f0929e69Shannken 	 *
600f0929e69Shannken 	 * See "Intel E7520 Memory Controller Hub (MCH) Datasheet",
601f0929e69Shannken 	 * Document 303006-002, pg. 82
602f0929e69Shannken 	 */
603f0929e69Shannken 	tag = pci_make_tag(pc, 0, 0, 1);
604f0929e69Shannken 	reg = pci_conf_read(pc, tag, PCI_ID_REG);
605f0929e69Shannken 	is_e7520_mch = (reg ==
606f0929e69Shannken 	    PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_E7525_MCHER));
607f0929e69Shannken 	if (is_e7520_mch) {
608f0929e69Shannken 		reg = pci_conf_read(pc, tag, 0x54);
609f0929e69Shannken 		pci_conf_write(pc, tag, 0x54, reg | 0x20);
610f0929e69Shannken 	}
611f0929e69Shannken 
612605f564fSmsaitoh 	acpimcfg_scan_bus(sc, pc, bus);
613605f564fSmsaitoh 
614f0929e69Shannken 	if (is_e7520_mch) {
615f0929e69Shannken 		pci_conf_write(pc, tag, 0x54, reg);
616f0929e69Shannken 	}
617f0929e69Shannken 
618da1fbcb7Sjmcneill 	/* Unmap configuration space of all dev/func. */
619605f564fSmsaitoh 	bus_space_unmap(seg->ms_bst, bsh, ACPIMCFG_SIZE_PER_BUS);
620605f564fSmsaitoh 	memset(mb->bsh, 0, sizeof(mb->bsh));
621605f564fSmsaitoh 
622605f564fSmsaitoh 	if (mb->valid_ndevs == 0) {
623605f564fSmsaitoh 		aprint_debug_dev(acpi_sc->sc_dev,
624605f564fSmsaitoh 		    "MCFG: bus %d: no valid devices.\n", bus);
625605f564fSmsaitoh 		memset(mb->valid_devs, 0, sizeof(mb->valid_devs));
626605f564fSmsaitoh 		goto out;
627605f564fSmsaitoh 	}
628605f564fSmsaitoh 
629605f564fSmsaitoh 	/* Mark invalid on remaining all devices. */
630605f564fSmsaitoh 	pci_decompose_tag(pc, mb->last_probed, NULL, &last_dev, &last_func);
631605f564fSmsaitoh 	for (i = last_dev; i < 32; i++) {
632605f564fSmsaitoh 		for (j = last_func; j < 8; j++) {
633605f564fSmsaitoh 			if (i == last_dev && j == last_func) {
634605f564fSmsaitoh 				/* Don't mark invalid to last probed device. */
635605f564fSmsaitoh 				continue;
636605f564fSmsaitoh 			}
637da1fbcb7Sjmcneill 			PCIDEV_SET_INVALID(mb, i, j);
638605f564fSmsaitoh 		}
639605f564fSmsaitoh 		last_func = 0;
640605f564fSmsaitoh 	}
641605f564fSmsaitoh 
642da1fbcb7Sjmcneill 	/* Map configuration space per dev/func. */
643605f564fSmsaitoh 	for (i = 0; i < 32; i++) {
644605f564fSmsaitoh 		for (j = 0; j < 8; j++) {
645da1fbcb7Sjmcneill 			if (!PCIDEV_IS_VALID(mb, i, j))
646605f564fSmsaitoh 				continue;
647605f564fSmsaitoh 			error = bus_space_map(seg->ms_bst,
648605f564fSmsaitoh 			    baddr + EXTCONF_OFFSET(i, j, 0), PCI_EXTCONF_SIZE,
649605f564fSmsaitoh 			    0, &mb->bsh[i][j]);
650605f564fSmsaitoh 			if (error != 0) {
651605f564fSmsaitoh 				/* Unmap all handles when map failed. */
652605f564fSmsaitoh 				do {
653605f564fSmsaitoh 					while (--j >= 0) {
654da1fbcb7Sjmcneill 						if (!PCIDEV_IS_VALID(mb, i, j))
655605f564fSmsaitoh 							continue;
656605f564fSmsaitoh 						bus_space_unmap(seg->ms_bst,
657605f564fSmsaitoh 						    mb->bsh[i][j],
658605f564fSmsaitoh 						    PCI_EXTCONF_SIZE);
659605f564fSmsaitoh 					}
660605f564fSmsaitoh 					j = 8;
661605f564fSmsaitoh 				} while (--i >= 0);
662605f564fSmsaitoh 				memset(mb->valid_devs, 0,
663605f564fSmsaitoh 				    sizeof(mb->valid_devs));
664605f564fSmsaitoh 				goto out;
665605f564fSmsaitoh 			}
666605f564fSmsaitoh 		}
667605f564fSmsaitoh 	}
668605f564fSmsaitoh 
669605f564fSmsaitoh 	aprint_debug_dev(acpi_sc->sc_dev, "MCFG: bus %d: valid devices\n", bus);
670605f564fSmsaitoh 	for (i = 0; i < 32; i++) {
671605f564fSmsaitoh 		for (j = 0; j < 8; j++) {
672da1fbcb7Sjmcneill 			if (PCIDEV_IS_VALID(mb, i, j)) {
673605f564fSmsaitoh 				aprint_debug_dev(acpi_sc->sc_dev,
674605f564fSmsaitoh 				    "MCFG: %03d:%02d:%d\n", bus, i, j);
675605f564fSmsaitoh 			}
676605f564fSmsaitoh 		}
677605f564fSmsaitoh 	}
678605f564fSmsaitoh 
679605f564fSmsaitoh 	error = 0;
680605f564fSmsaitoh out:
681605f564fSmsaitoh 
682605f564fSmsaitoh 	return error;
683605f564fSmsaitoh }
684605f564fSmsaitoh 
6851099f047Sjmcneill #ifdef PCI_RESOURCE
686bd4d5bc0Sjmcneill ACPI_STATUS
687aaf945cfSjmcneill acpimcfg_configure_bus_cb(ACPI_RESOURCE *res, void *ctx)
688aaf945cfSjmcneill {
6891099f047Sjmcneill 	struct pci_resource_info *pciinfo = ctx;
690ca8ce3aeSthorpej 	bus_addr_t addr;
691ca8ce3aeSthorpej 	bus_size_t size;
6921099f047Sjmcneill 	int type;
693aaf945cfSjmcneill 
6944f305a76Sjmcneill 	if (res->Type != ACPI_RESOURCE_TYPE_ADDRESS16 &&
6954f305a76Sjmcneill 	    res->Type != ACPI_RESOURCE_TYPE_ADDRESS32 &&
696aaf945cfSjmcneill 	    res->Type != ACPI_RESOURCE_TYPE_ADDRESS64)
697aaf945cfSjmcneill 		return AE_OK;
698aaf945cfSjmcneill 
699aaf945cfSjmcneill 	if (res->Data.Address.ProducerConsumer != ACPI_PRODUCER)
700aaf945cfSjmcneill 		return AE_OK;
701aaf945cfSjmcneill 
702aaf945cfSjmcneill 	if (res->Data.Address.ResourceType != ACPI_MEMORY_RANGE &&
7031099f047Sjmcneill 	    res->Data.Address.ResourceType != ACPI_IO_RANGE &&
7041099f047Sjmcneill 	    res->Data.Address.ResourceType != ACPI_BUS_NUMBER_RANGE)
705aaf945cfSjmcneill 		return AE_OK;
706aaf945cfSjmcneill 
707aaf945cfSjmcneill 	if (res->Data.Address.ResourceType == ACPI_MEMORY_RANGE &&
708aaf945cfSjmcneill 	    res->Data.Address.Info.Mem.Caching == ACPI_PREFETCHABLE_MEMORY) {
7091099f047Sjmcneill 		type = PCI_RANGE_PMEM;
710aaf945cfSjmcneill 	} else if (res->Data.Address.ResourceType == ACPI_MEMORY_RANGE &&
711aaf945cfSjmcneill 	    res->Data.Address.Info.Mem.Caching != ACPI_PREFETCHABLE_MEMORY) {
7121099f047Sjmcneill 		if (res->Type == ACPI_RESOURCE_TYPE_ADDRESS64) {
7131099f047Sjmcneill 			type = PCI_RANGE_PMEM;
7141099f047Sjmcneill 		} else {
7151099f047Sjmcneill 			type = PCI_RANGE_MEM;
7161099f047Sjmcneill 		}
7171099f047Sjmcneill 	} else if (res->Data.Address.ResourceType == ACPI_BUS_NUMBER_RANGE) {
7181099f047Sjmcneill 		type = PCI_RANGE_BUS;
7193d5831c7Sjmcneill 	} else {
7203d5831c7Sjmcneill 		KASSERT(res->Data.Address.ResourceType == ACPI_IO_RANGE);
7211099f047Sjmcneill 		type = PCI_RANGE_IO;
722aaf945cfSjmcneill 	}
723aaf945cfSjmcneill 
724aaf945cfSjmcneill 	switch (res->Type) {
725aaf945cfSjmcneill 	case ACPI_RESOURCE_TYPE_ADDRESS16:
726aaf945cfSjmcneill 		aprint_debug(
727aaf945cfSjmcneill 		    "MCFG: range 0x%04" PRIx16 " size %#" PRIx16 " (16-bit %s)\n",
728aaf945cfSjmcneill 		    res->Data.Address16.Address.Minimum,
729aaf945cfSjmcneill 		    res->Data.Address16.Address.AddressLength,
7301099f047Sjmcneill 		    pci_resource_typename(type));
731ca8ce3aeSthorpej 		addr = res->Data.Address16.Address.Minimum;
732ca8ce3aeSthorpej 		size = res->Data.Address16.Address.AddressLength;
733aaf945cfSjmcneill 		break;
734aaf945cfSjmcneill 	case ACPI_RESOURCE_TYPE_ADDRESS32:
735aaf945cfSjmcneill 		aprint_debug(
736aaf945cfSjmcneill 		    "MCFG: range 0x%08" PRIx32 " size %#" PRIx32 " (32-bit %s)\n",
737aaf945cfSjmcneill 		    res->Data.Address32.Address.Minimum,
738aaf945cfSjmcneill 		    res->Data.Address32.Address.AddressLength,
7391099f047Sjmcneill 		    pci_resource_typename(type));
740ca8ce3aeSthorpej 		addr = res->Data.Address32.Address.Minimum;
741ca8ce3aeSthorpej 		size = res->Data.Address32.Address.AddressLength;
742aaf945cfSjmcneill 		break;
743aaf945cfSjmcneill 	case ACPI_RESOURCE_TYPE_ADDRESS64:
744aaf945cfSjmcneill 		aprint_debug(
745aaf945cfSjmcneill 		    "MCFG: range 0x%016" PRIx64 " size %#" PRIx64 " (64-bit %s)\n",
746aaf945cfSjmcneill 		    res->Data.Address64.Address.Minimum,
747aaf945cfSjmcneill 		    res->Data.Address64.Address.AddressLength,
7481099f047Sjmcneill 		    pci_resource_typename(type));
749ca8ce3aeSthorpej 		addr = res->Data.Address64.Address.Minimum;
750ca8ce3aeSthorpej 		size = res->Data.Address64.Address.AddressLength;
751aaf945cfSjmcneill 		break;
752ca8ce3aeSthorpej 
753ca8ce3aeSthorpej 	default:
754ca8ce3aeSthorpej 		return AE_OK;
755aaf945cfSjmcneill 	}
756aaf945cfSjmcneill 
757*5c830e3fSmlelstv 	if (size > 0) {
7581099f047Sjmcneill 		pciinfo->ranges[type].start = addr;
7591099f047Sjmcneill 		pciinfo->ranges[type].end = addr + size - 1;
760*5c830e3fSmlelstv 	}
761ca8ce3aeSthorpej 
7621099f047Sjmcneill 	return AE_OK;
763aaf945cfSjmcneill }
764aaf945cfSjmcneill 
765aaf945cfSjmcneill int
766aaf945cfSjmcneill acpimcfg_configure_bus(device_t self, pci_chipset_tag_t pc, ACPI_HANDLE handle,
7671099f047Sjmcneill     int bus, bool mapcfgspace)
768aaf945cfSjmcneill {
7691099f047Sjmcneill 	struct pci_resource_info pciinfo;
770aaf945cfSjmcneill 	struct mcfg_segment *seg;
771aaf945cfSjmcneill 	struct mcfg_bus *mb;
772aaf945cfSjmcneill 	bus_space_handle_t bsh[256];
773aaf945cfSjmcneill 	bool bsh_mapped[256];
7741099f047Sjmcneill 	int error, boff, b, d, f, endbus;
775aaf945cfSjmcneill 	bus_addr_t baddr;
776aaf945cfSjmcneill 	ACPI_STATUS rv;
777aaf945cfSjmcneill 
7781099f047Sjmcneill 	if (mapcfgspace) {
77999199cfcSjmcneill 		seg = acpimcfg_get_segment(pc, bus);
780*5c830e3fSmlelstv 		aprint_debug_dev(acpi_sc->sc_dev, "MCFG: Bus=%d, Seg=%p\n",
781*5c830e3fSmlelstv 		    bus, seg);
7821099f047Sjmcneill 		if (seg == NULL) {
783aaf945cfSjmcneill 			return ENOENT;
7841099f047Sjmcneill 		}
7851099f047Sjmcneill 		endbus = seg->ms_bus_end;
786ca8ce3aeSthorpej 
787aaf945cfSjmcneill 		/*
7881099f047Sjmcneill 		 * Map config space for all possible busses and mark them valid
7891099f047Sjmcneill 		 * during configuration so pci_configure_bus can access them
7901099f047Sjmcneill 		 * through our chipset tag with acpimcfg_conf_read/write below.
791aaf945cfSjmcneill 		 */
792aaf945cfSjmcneill 		memset(bsh_mapped, 0, sizeof(bsh_mapped));
793aaf945cfSjmcneill 		for (b = seg->ms_bus_start; b <= seg->ms_bus_end; b++) {
794aaf945cfSjmcneill 			boff = b - seg->ms_bus_start;
795aaf945cfSjmcneill 			mb = &seg->ms_bus[boff];
796a266bf0eSjmcneill 			baddr = seg->ms_address + (b * ACPIMCFG_SIZE_PER_BUS);
797aaf945cfSjmcneill 
798773e7c3eSjmcneill 			/* Map extended configuration space of all dev/func. */
7991099f047Sjmcneill 			error = bus_space_map(seg->ms_bst, baddr,
8001099f047Sjmcneill 			    ACPIMCFG_SIZE_PER_BUS, 0, &bsh[b]);
8011099f047Sjmcneill 			if (error != 0) {
802aaf945cfSjmcneill 				goto cleanup;
8031099f047Sjmcneill 			}
804aaf945cfSjmcneill 			bsh_mapped[b] = true;
805aaf945cfSjmcneill 			for (d = 0; d < 32; d++) {
806aaf945cfSjmcneill 				for (f = 0; f < 8; f++) {
8071099f047Sjmcneill 					error = bus_space_subregion(seg->ms_bst,
8081099f047Sjmcneill 					    bsh[b], EXTCONF_OFFSET(d, f, 0),
8091099f047Sjmcneill 					    PCI_EXTCONF_SIZE, &mb->bsh[d][f]);
8101099f047Sjmcneill 					if (error != 0) {
811aaf945cfSjmcneill 						break;
812aaf945cfSjmcneill 					}
813aaf945cfSjmcneill 				}
8141099f047Sjmcneill 			}
8151099f047Sjmcneill 			if (error != 0) {
816aaf945cfSjmcneill 				goto cleanup;
8171099f047Sjmcneill 			}
818aaf945cfSjmcneill 
819aaf945cfSjmcneill 			memset(mb->valid_devs, 0xff, sizeof(mb->valid_devs));
820aaf945cfSjmcneill 		}
8211099f047Sjmcneill 	} else {
8221099f047Sjmcneill 		endbus = 255;
8231099f047Sjmcneill 	}
824aaf945cfSjmcneill 
8251099f047Sjmcneill 	memset(&pciinfo, 0, sizeof(pciinfo));
8261099f047Sjmcneill 	pciinfo.pc = pc;
8271099f047Sjmcneill 	pciinfo.ranges[PCI_RANGE_BUS].start = bus;
8281099f047Sjmcneill 	pciinfo.ranges[PCI_RANGE_BUS].end = endbus;
829ca8ce3aeSthorpej 	rv = AcpiWalkResources(handle, "_CRS", acpimcfg_configure_bus_cb,
8301099f047Sjmcneill 	    &pciinfo);
831aaf945cfSjmcneill 	if (ACPI_FAILURE(rv)) {
832*5c830e3fSmlelstv 		aprint_debug_dev(acpi_sc->sc_dev, "MCFG: Walk _CRS: %ld\n",
833*5c830e3fSmlelstv 		    (long)rv);
834aaf945cfSjmcneill 		error = ENXIO;
835aaf945cfSjmcneill 		goto cleanup;
836aaf945cfSjmcneill 	}
8371099f047Sjmcneill 	error = 0;
838aaf945cfSjmcneill 
8391099f047Sjmcneill 	pci_resource_init(&pciinfo);
840aaf945cfSjmcneill 
841aaf945cfSjmcneill cleanup:
8421099f047Sjmcneill 	if (mapcfgspace) {
843aaf945cfSjmcneill 		/*
8441099f047Sjmcneill 		 * Unmap config space for the segment's busses. Valid devices
8451099f047Sjmcneill 		 * will be re-mapped later on by acpimcfg_map_bus.
846aaf945cfSjmcneill 		 */
847aaf945cfSjmcneill 		for (b = seg->ms_bus_start; b <= seg->ms_bus_end; b++) {
848aaf945cfSjmcneill 			boff = b - seg->ms_bus_start;
849aaf945cfSjmcneill 			mb = &seg->ms_bus[boff];
850aaf945cfSjmcneill 			memset(mb->valid_devs, 0, sizeof(mb->valid_devs));
851aaf945cfSjmcneill 
8521099f047Sjmcneill 			if (bsh_mapped[b]) {
8531099f047Sjmcneill 				bus_space_unmap(seg->ms_bst, bsh[b],
8541099f047Sjmcneill 				    ACPIMCFG_SIZE_PER_BUS);
855aaf945cfSjmcneill 			}
8561099f047Sjmcneill 		}
8571099f047Sjmcneill 	}
858aaf945cfSjmcneill 
859aaf945cfSjmcneill 	return error;
860aaf945cfSjmcneill }
861aaf945cfSjmcneill #else
862aaf945cfSjmcneill int
863aaf945cfSjmcneill acpimcfg_configure_bus(device_t self, pci_chipset_tag_t pc, ACPI_HANDLE handle,
8641099f047Sjmcneill     int bus, bool mapcfgspace)
865aaf945cfSjmcneill {
866aaf945cfSjmcneill 	return ENXIO;
867aaf945cfSjmcneill }
868aaf945cfSjmcneill #endif
869aaf945cfSjmcneill 
870605f564fSmsaitoh int
871605f564fSmsaitoh acpimcfg_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t *data)
872605f564fSmsaitoh {
873605f564fSmsaitoh 	struct mcfg_segment *seg = NULL;
874605f564fSmsaitoh 	struct mcfg_bus *mb;
875605f564fSmsaitoh 	int bus, dev, func;
876605f564fSmsaitoh 
877605f564fSmsaitoh 	KASSERT(reg < PCI_EXTCONF_SIZE);
878605f564fSmsaitoh 	KASSERT((reg & 3) == 0);
879605f564fSmsaitoh 
880605f564fSmsaitoh 	if (!mcfg_inited) {
881605f564fSmsaitoh 		*data = -1;
882605f564fSmsaitoh 		return ENXIO;
883605f564fSmsaitoh 	}
884605f564fSmsaitoh 
885605f564fSmsaitoh 	pci_decompose_tag(pc, tag, &bus, &dev, &func);
886605f564fSmsaitoh 
88799199cfcSjmcneill 	seg = acpimcfg_get_segment(pc, bus);
888605f564fSmsaitoh 	if (seg == NULL) {
889605f564fSmsaitoh 		*data = -1;
890605f564fSmsaitoh 		return ERANGE;
891605f564fSmsaitoh 	}
892605f564fSmsaitoh 
893605f564fSmsaitoh 	mb = &seg->ms_bus[bus - seg->ms_bus_start];
894da1fbcb7Sjmcneill 	if (!PCIDEV_IS_VALID(mb, dev, func)) {
895da1fbcb7Sjmcneill 		*data = -1;
896da1fbcb7Sjmcneill 		return EINVAL;
897da1fbcb7Sjmcneill 	}
898da1fbcb7Sjmcneill 	if (!EXTCONF_IS_VALID(mb, dev, func) && reg >= PCI_CONF_SIZE) {
899605f564fSmsaitoh 		*data = -1;
900605f564fSmsaitoh 		return EINVAL;
901605f564fSmsaitoh 	}
902605f564fSmsaitoh 
903605f564fSmsaitoh 	*data = mcfg_ops->ao_read(seg->ms_bst, mb->bsh[dev][func], reg);
904605f564fSmsaitoh 	return 0;
905605f564fSmsaitoh }
906605f564fSmsaitoh 
907605f564fSmsaitoh int
908605f564fSmsaitoh acpimcfg_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data)
909605f564fSmsaitoh {
910605f564fSmsaitoh 	struct mcfg_segment *seg = NULL;
911605f564fSmsaitoh 	struct mcfg_bus *mb;
912605f564fSmsaitoh 	int bus, dev, func;
913605f564fSmsaitoh 
914605f564fSmsaitoh 	KASSERT(reg < PCI_EXTCONF_SIZE);
915605f564fSmsaitoh 	KASSERT((reg & 3) == 0);
916605f564fSmsaitoh 
917605f564fSmsaitoh 	if (!mcfg_inited)
918605f564fSmsaitoh 		return ENXIO;
919605f564fSmsaitoh 
920605f564fSmsaitoh 	pci_decompose_tag(pc, tag, &bus, &dev, &func);
921605f564fSmsaitoh 
92299199cfcSjmcneill 	seg = acpimcfg_get_segment(pc, bus);
923605f564fSmsaitoh 	if (seg == NULL)
924605f564fSmsaitoh 		return ERANGE;
925605f564fSmsaitoh 
926605f564fSmsaitoh 	mb = &seg->ms_bus[bus - seg->ms_bus_start];
927da1fbcb7Sjmcneill 	if (!PCIDEV_IS_VALID(mb, dev, func))
928da1fbcb7Sjmcneill 		return EINVAL;
929da1fbcb7Sjmcneill 	if (!EXTCONF_IS_VALID(mb, dev, func) && reg >= PCI_CONF_SIZE)
930605f564fSmsaitoh 		return EINVAL;
931605f564fSmsaitoh 
932605f564fSmsaitoh 	mcfg_ops->ao_write(seg->ms_bst, mb->bsh[dev][func], reg, data);
933605f564fSmsaitoh 	return 0;
934605f564fSmsaitoh }
935c57f707cSjmcneill 
936c57f707cSjmcneill bool
937c57f707cSjmcneill acpimcfg_conf_valid(pci_chipset_tag_t pc, pcitag_t tag, int reg)
938c57f707cSjmcneill {
939c57f707cSjmcneill 	struct mcfg_segment *seg = NULL;
940c57f707cSjmcneill 	struct mcfg_bus *mb;
941c57f707cSjmcneill 	int bus, dev, func;
942c57f707cSjmcneill 
943c57f707cSjmcneill 	if (!mcfg_inited)
944c57f707cSjmcneill 		return false;
945c57f707cSjmcneill 
946c57f707cSjmcneill 	pci_decompose_tag(pc, tag, &bus, &dev, &func);
947c57f707cSjmcneill 
948c57f707cSjmcneill 	seg = acpimcfg_get_segment(pc, bus);
949c57f707cSjmcneill 	if (seg == NULL)
950c57f707cSjmcneill 		return false;
951c57f707cSjmcneill 
952c57f707cSjmcneill 	mb = &seg->ms_bus[bus - seg->ms_bus_start];
953c57f707cSjmcneill 	if (!PCIDEV_IS_VALID(mb, dev, func))
954c57f707cSjmcneill 		return false;
955c57f707cSjmcneill 	if (!EXTCONF_IS_VALID(mb, dev, func) && reg >= PCI_CONF_SIZE)
956c57f707cSjmcneill 		return false;
957c57f707cSjmcneill 
958c57f707cSjmcneill 	return true;
959c57f707cSjmcneill }
960