xref: /netbsd-src/sys/dev/pci/pci.c (revision e82ab0a323e06ebefb43ff092e875bf6e0222c23)
1*e82ab0a3Sriastradh /*	$NetBSD: pci.c,v 1.168 2024/06/23 00:53:34 riastradh Exp $	*/
2f1f9317dScgd 
3f1f9317dScgd /*
486585ca5Scgd  * Copyright (c) 1995, 1996, 1997, 1998
5cc93b2c4Scgd  *     Christopher G. Demetriou.  All rights reserved.
66d3d8a13Smycroft  * Copyright (c) 1994 Charles M. Hannum.  All rights reserved.
7f1f9317dScgd  *
8f1f9317dScgd  * Redistribution and use in source and binary forms, with or without
9f1f9317dScgd  * modification, are permitted provided that the following conditions
10f1f9317dScgd  * are met:
11f1f9317dScgd  * 1. Redistributions of source code must retain the above copyright
12f1f9317dScgd  *    notice, this list of conditions and the following disclaimer.
13f1f9317dScgd  * 2. Redistributions in binary form must reproduce the above copyright
14f1f9317dScgd  *    notice, this list of conditions and the following disclaimer in the
15f1f9317dScgd  *    documentation and/or other materials provided with the distribution.
16f1f9317dScgd  * 3. All advertising materials mentioning features or use of this software
17f1f9317dScgd  *    must display the following acknowledgement:
186d3d8a13Smycroft  *	This product includes software developed by Charles M. Hannum.
19f1f9317dScgd  * 4. The name of the author may not be used to endorse or promote products
20f1f9317dScgd  *    derived from this software without specific prior written permission.
21f1f9317dScgd  *
22f1f9317dScgd  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23f1f9317dScgd  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24f1f9317dScgd  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25f1f9317dScgd  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26f1f9317dScgd  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27f1f9317dScgd  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28f1f9317dScgd  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29f1f9317dScgd  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30f1f9317dScgd  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31f1f9317dScgd  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32f1f9317dScgd  */
33f1f9317dScgd 
34f1f9317dScgd /*
35f1f9317dScgd  * PCI bus autoconfiguration.
36f1f9317dScgd  */
37f1f9317dScgd 
389048aaaeSlukem #include <sys/cdefs.h>
39*e82ab0a3Sriastradh __KERNEL_RCSID(0, "$NetBSD: pci.c,v 1.168 2024/06/23 00:53:34 riastradh Exp $");
409048aaaeSlukem 
414a6dab94Spooka #ifdef _KERNEL_OPT
4200f7a2d4Scgd #include "opt_pci.h"
434a6dab94Spooka #endif
4400f7a2d4Scgd 
45f1f9317dScgd #include <sys/param.h>
464c1d81b2Sjmcneill #include <sys/malloc.h>
47f1f9317dScgd #include <sys/systm.h>
48f1f9317dScgd #include <sys/device.h>
493076984fSjmcneill #include <sys/module.h>
50f1f9317dScgd 
51f1f9317dScgd #include <dev/pci/pcireg.h>
52f1f9317dScgd #include <dev/pci/pcivar.h>
532f8f14b3Scgd #include <dev/pci/pcidevs.h>
542f141151Smsaitoh #include <dev/pci/ppbvar.h>
55f1f9317dScgd 
564ecce2b2Sthorpej #include <dev/pci/pci_calls.h>
574ecce2b2Sthorpej 
584c1d81b2Sjmcneill #include <net/if.h>
594c1d81b2Sjmcneill 
60fb66693cSchristos #include "locators.h"
61fb66693cSchristos 
6231786995Sriastradh static void pci_child_register(device_t);
634c1d81b2Sjmcneill 
6400f7a2d4Scgd #ifdef PCI_CONFIG_DUMP
6500f7a2d4Scgd int pci_config_dump = 1;
6600f7a2d4Scgd #else
6700f7a2d4Scgd int pci_config_dump = 0;
6800f7a2d4Scgd #endif
6900f7a2d4Scgd 
7018db93c7Sperry int	pciprint(void *, const char *);
71f1f9317dScgd 
72*e82ab0a3Sriastradh #ifdef PCI_MACHDEP_ENUMERATE_BUS1
73*e82ab0a3Sriastradh #define pci_enumerate_bus1 PCI_MACHDEP_ENUMERATE_BUS1
7404a2fc0fSdrochner #endif
7504a2fc0fSdrochner 
76828f1f3aScgd /*
77ab548fbbSthorpej  * Important note about PCI-ISA bridges:
78ab548fbbSthorpej  *
79ab548fbbSthorpej  * Callbacks are used to configure these devices so that ISA/EISA bridges
80ab548fbbSthorpej  * can attach their child busses after PCI configuration is done.
81828f1f3aScgd  *
82828f1f3aScgd  * This works because:
83828f1f3aScgd  *	(1) there can be at most one ISA/EISA bridge per PCI bus, and
84828f1f3aScgd  *	(2) any ISA/EISA bridges must be attached to primary PCI
85828f1f3aScgd  *	    busses (i.e. bus zero).
86828f1f3aScgd  *
87828f1f3aScgd  * That boils down to: there can only be one of these outstanding
88828f1f3aScgd  * at a time, it is cleared when configuring PCI bus 0 before any
89828f1f3aScgd  * subdevices have been found, and it is run after all subdevices
90828f1f3aScgd  * of PCI bus 0 have been found.
91828f1f3aScgd  *
92828f1f3aScgd  * This is needed because there are some (legacy) PCI devices which
93828f1f3aScgd  * can show up as ISA/EISA devices as well (the prime example of which
94828f1f3aScgd  * are VGA controllers).  If you attach ISA from a PCI-ISA/EISA bridge,
95828f1f3aScgd  * and the bridge is seen before the video board is, the board can show
96828f1f3aScgd  * up as an ISA device, and that can (bogusly) complicate the PCI device's
97828f1f3aScgd  * attach code, or make the PCI device not be properly attached at all.
98ab548fbbSthorpej  *
99ab548fbbSthorpej  * We use the generic config_defer() facility to achieve this.
100828f1f3aScgd  */
101828f1f3aScgd 
10282efc281Sdyoung int
pcirescan(device_t self,const char * ifattr,const int * locators)1038ce2158bSdyoung pcirescan(device_t self, const char *ifattr, const int *locators)
104d36c43c5Sthorpej {
1058ce2158bSdyoung 	struct pci_softc *sc = device_private(self);
106d36c43c5Sthorpej 
107d36c43c5Sthorpej 	KASSERT(ifattr && !strcmp(ifattr, "pci"));
108d36c43c5Sthorpej 	KASSERT(locators);
109d36c43c5Sthorpej 
1108ce2158bSdyoung 	pci_enumerate_bus(sc, locators, NULL, NULL);
11172a7c70bSpgoyette 
1128ce2158bSdyoung 	return 0;
113d36c43c5Sthorpej }
114d36c43c5Sthorpej 
11582efc281Sdyoung int
pcimatch(device_t parent,cfdata_t cf,void * aux)11655ee8b25Scube pcimatch(device_t parent, cfdata_t cf, void *aux)
117f1f9317dScgd {
118f1f9317dScgd 	struct pcibus_attach_args *pba = aux;
119f1f9317dScgd 
120f1f9317dScgd 	/* Check the locators */
121a987cab2Sdrochner 	if (cf->cf_loc[PCIBUSCF_BUS] != PCIBUSCF_BUS_DEFAULT &&
122a987cab2Sdrochner 	    cf->cf_loc[PCIBUSCF_BUS] != pba->pba_bus)
1239b1a101dScegger 		return 0;
124f1f9317dScgd 
125f1f9317dScgd 	/* sanity */
126f1f9317dScgd 	if (pba->pba_bus < 0 || pba->pba_bus > 255)
1279b1a101dScegger 		return 0;
128f1f9317dScgd 
129f1f9317dScgd 	/*
130f1f9317dScgd 	 * XXX check other (hardware?) indicators
131f1f9317dScgd 	 */
132f1f9317dScgd 
1339b1a101dScegger 	return 1;
134f1f9317dScgd }
135f1f9317dScgd 
13682efc281Sdyoung void
pciattach(device_t parent,device_t self,void * aux)1378ce2158bSdyoung pciattach(device_t parent, device_t self, void *aux)
138928943b2Sdrochner {
139928943b2Sdrochner 	struct pcibus_attach_args *pba = aux;
1408ce2158bSdyoung 	struct pci_softc *sc = device_private(self);
141a4e1bde0Sthorpej 	int io_enabled, mem_enabled, mrl_enabled, mrm_enabled, mwi_enabled;
142a4e1bde0Sthorpej 	const char *sep = "";
14349126e9eSdrochner 	static const int wildcard[PCICF_NLOCS] = {
14449126e9eSdrochner 		PCICF_DEV_DEFAULT, PCICF_FUNCTION_DEFAULT
14549126e9eSdrochner 	};
146928943b2Sdrochner 
14755ee8b25Scube 	sc->sc_dev = self;
14855ee8b25Scube 
149928943b2Sdrochner 	pci_attach_hook(parent, self, pba);
150b43b1645Sthorpej 
151b43b1645Sthorpej 	aprint_naive("\n");
152b43b1645Sthorpej 	aprint_normal("\n");
153928943b2Sdrochner 
154a6b2b839Sdyoung 	io_enabled = (pba->pba_flags & PCI_FLAGS_IO_OKAY);
155a6b2b839Sdyoung 	mem_enabled = (pba->pba_flags & PCI_FLAGS_MEM_OKAY);
156a4e1bde0Sthorpej 	mrl_enabled = (pba->pba_flags & PCI_FLAGS_MRL_OKAY);
157a4e1bde0Sthorpej 	mrm_enabled = (pba->pba_flags & PCI_FLAGS_MRM_OKAY);
158a4e1bde0Sthorpej 	mwi_enabled = (pba->pba_flags & PCI_FLAGS_MWI_OKAY);
159928943b2Sdrochner 
160928943b2Sdrochner 	if (io_enabled == 0 && mem_enabled == 0) {
1618ce2158bSdyoung 		aprint_error_dev(self, "no spaces enabled!\n");
1624c1d81b2Sjmcneill 		goto fail;
163928943b2Sdrochner 	}
164928943b2Sdrochner 
165b43b1645Sthorpej #define	PRINT(str)							\
166b43b1645Sthorpej do {									\
167b4214e67Sad 	aprint_verbose("%s%s", sep, str);				\
168b43b1645Sthorpej 	sep = ", ";							\
169b43b1645Sthorpej } while (/*CONSTCOND*/0)
170a4e1bde0Sthorpej 
1718ce2158bSdyoung 	aprint_verbose_dev(self, "");
172a4e1bde0Sthorpej 
173928943b2Sdrochner 	if (io_enabled)
174a4e1bde0Sthorpej 		PRINT("i/o space");
175a4e1bde0Sthorpej 	if (mem_enabled)
176a4e1bde0Sthorpej 		PRINT("memory space");
177b4214e67Sad 	aprint_verbose(" enabled");
178a4e1bde0Sthorpej 
179a4e1bde0Sthorpej 	if (mrl_enabled || mrm_enabled || mwi_enabled) {
180a4e1bde0Sthorpej 		if (mrl_enabled)
181a4e1bde0Sthorpej 			PRINT("rd/line");
182a4e1bde0Sthorpej 		if (mrm_enabled)
183a4e1bde0Sthorpej 			PRINT("rd/mult");
184a4e1bde0Sthorpej 		if (mwi_enabled)
185a4e1bde0Sthorpej 			PRINT("wr/inv");
186b4214e67Sad 		aprint_verbose(" ok");
187928943b2Sdrochner 	}
188a4e1bde0Sthorpej 
189b4214e67Sad 	aprint_verbose("\n");
190928943b2Sdrochner 
191a4e1bde0Sthorpej #undef PRINT
192a4e1bde0Sthorpej 
193928943b2Sdrochner 	sc->sc_iot = pba->pba_iot;
194928943b2Sdrochner 	sc->sc_memt = pba->pba_memt;
195928943b2Sdrochner 	sc->sc_dmat = pba->pba_dmat;
1967dd7f8baSfvdl 	sc->sc_dmat64 = pba->pba_dmat64;
197928943b2Sdrochner 	sc->sc_pc = pba->pba_pc;
198928943b2Sdrochner 	sc->sc_bus = pba->pba_bus;
199204183c0Sthorpej 	sc->sc_bridgetag = pba->pba_bridgetag;
200928943b2Sdrochner 	sc->sc_maxndevs = pci_bus_maxdevs(pba->pba_pc, pba->pba_bus);
201928943b2Sdrochner 	sc->sc_intrswiz = pba->pba_intrswiz;
202928943b2Sdrochner 	sc->sc_intrtag = pba->pba_intrtag;
203928943b2Sdrochner 	sc->sc_flags = pba->pba_flags;
2048e37d48dSjmcneill 
20555ee8b25Scube 	device_pmf_driver_set_child_register(sc->sc_dev, pci_child_register);
2068e37d48dSjmcneill 
20755ee8b25Scube 	pcirescan(sc->sc_dev, "pci", wildcard);
2084c1d81b2Sjmcneill 
2094c1d81b2Sjmcneill fail:
2104c1d81b2Sjmcneill 	if (!pmf_device_register(self, NULL, NULL))
2114c1d81b2Sjmcneill 		aprint_error_dev(self, "couldn't establish power handler\n");
2124c1d81b2Sjmcneill }
2134c1d81b2Sjmcneill 
21482efc281Sdyoung int
pcidetach(device_t self,int flags)2158ce2158bSdyoung pcidetach(device_t self, int flags)
2164c1d81b2Sjmcneill {
217cf784f44Sdyoung 	int rc;
218cf784f44Sdyoung 
219cf784f44Sdyoung 	if ((rc = config_detach_children(self, flags)) != 0)
220cf784f44Sdyoung 		return rc;
2214c1d81b2Sjmcneill 	pmf_device_deregister(self);
2224c1d81b2Sjmcneill 	return 0;
223c67a72f6Sdrochner }
224c67a72f6Sdrochner 
225c67a72f6Sdrochner int
pciprint(void * aux,const char * pnp)226d36c43c5Sthorpej pciprint(void *aux, const char *pnp)
227f1f9317dScgd {
228169ac5b3Saugustss 	struct pci_attach_args *pa = aux;
229f1f9317dScgd 	char devinfo[256];
23086585ca5Scgd 	const struct pci_quirkdata *qd;
231f1f9317dScgd 
232f1f9317dScgd 	if (pnp) {
23361230437Sitojun 		pci_devinfo(pa->pa_id, pa->pa_class, 1, devinfo, sizeof(devinfo));
23472a7af27Sthorpej 		aprint_normal("%s at %s", devinfo, pnp);
235f1f9317dScgd 	}
23672a7af27Sthorpej 	aprint_normal(" dev %d function %d", pa->pa_device, pa->pa_function);
23700f7a2d4Scgd 	if (pci_config_dump) {
23899912336Scgd 		printf(": ");
23999912336Scgd 		pci_conf_print(pa->pa_pc, pa->pa_tag, NULL);
24099912336Scgd 		if (!pnp)
24161230437Sitojun 			pci_devinfo(pa->pa_id, pa->pa_class, 1, devinfo, sizeof(devinfo));
24299912336Scgd 		printf("%s at %s", devinfo, pnp ? pnp : "?");
24386585ca5Scgd 		printf(" dev %d function %d (", pa->pa_device, pa->pa_function);
24486585ca5Scgd #ifdef __i386__
24586585ca5Scgd 		printf("tag %#lx, intrtag %#lx, intrswiz %#lx, intrpin %#lx",
24686585ca5Scgd 		    *(long *)&pa->pa_tag, *(long *)&pa->pa_intrtag,
24786585ca5Scgd 		    (long)pa->pa_intrswiz, (long)pa->pa_intrpin);
24886585ca5Scgd #else
2490b1849a7Smrg 		printf("intrswiz %#lx, intrpin %#lx",
2500b1849a7Smrg 		    (long)pa->pa_intrswiz, (long)pa->pa_intrpin);
25199912336Scgd #endif
25286585ca5Scgd 		printf(", i/o %s, mem %s,",
253a6b2b839Sdyoung 		    pa->pa_flags & PCI_FLAGS_IO_OKAY ? "on" : "off",
254a6b2b839Sdyoung 		    pa->pa_flags & PCI_FLAGS_MEM_OKAY ? "on" : "off");
25586585ca5Scgd 		qd = pci_lookup_quirkdata(PCI_VENDOR(pa->pa_id),
25686585ca5Scgd 		    PCI_PRODUCT(pa->pa_id));
25786585ca5Scgd 		if (qd == NULL) {
25886585ca5Scgd 			printf(" no quirks");
25986585ca5Scgd 		} else {
2609a5d3f28Schristos 			snprintb(devinfo, sizeof (devinfo),
261dc4bf37aSitojun 			    "\002\001multifn\002singlefn\003skipfunc0"
262dc4bf37aSitojun 			    "\004skipfunc1\005skipfunc2\006skipfunc3"
263dc4bf37aSitojun 			    "\007skipfunc4\010skipfunc5\011skipfunc6"
2649a5d3f28Schristos 			    "\012skipfunc7", qd->quirks);
26586585ca5Scgd 			printf(" quirks %s", devinfo);
26686585ca5Scgd 		}
26786585ca5Scgd 		printf(")");
26800f7a2d4Scgd 	}
2699b1a101dScegger 	return UNCONF;
270f1f9317dScgd }
271f1f9317dScgd 
2724c5901a8Sthorpej static devhandle_t
pci_bus_get_child_devhandle(struct pci_softc * sc,pcitag_t tag)2734c5901a8Sthorpej pci_bus_get_child_devhandle(struct pci_softc *sc, pcitag_t tag)
2744c5901a8Sthorpej {
2754c5901a8Sthorpej 	struct pci_bus_get_child_devhandle_args args = {
2764c5901a8Sthorpej 		.pc = sc->sc_pc,
2774c5901a8Sthorpej 		.tag = tag,
2784c5901a8Sthorpej 	};
2794c5901a8Sthorpej 
2804ecce2b2Sthorpej 	if (device_call(sc->sc_dev, PCI_BUS_GET_CHILD_DEVHANDLE(&args)) != 0) {
2814c5901a8Sthorpej 		/*
2824c5901a8Sthorpej 		 * The call is either not supported or the requested
2834c5901a8Sthorpej 		 * device was not found in the platform device tree.
2844c5901a8Sthorpej 		 * Return an invalid handle.
2854c5901a8Sthorpej 		 */
28646457800Sthorpej 		return devhandle_invalid();
2874c5901a8Sthorpej 	}
2884c5901a8Sthorpej 
2894c5901a8Sthorpej 	return args.devhandle;
2904c5901a8Sthorpej }
2914c5901a8Sthorpej 
292f1f9317dScgd int
pci_probe_device1(struct pci_softc * sc,pcitag_t tag,int (* match)(void *,const struct pci_attach_args *),void * cookie,struct pci_attach_args * pap)293*e82ab0a3Sriastradh pci_probe_device1(struct pci_softc *sc, pcitag_t tag,
294*e82ab0a3Sriastradh     int (*match)(void *, const struct pci_attach_args *), void *cookie,
295d3e53912Sdyoung     struct pci_attach_args *pap)
2967c86eb62Sthorpej {
2977c86eb62Sthorpej 	pci_chipset_tag_t pc = sc->sc_pc;
2987c86eb62Sthorpej 	struct pci_attach_args pa;
299b4985db0Smatt 	pcireg_t id, /* csr, */ pciclass, intr, bhlcr, bar, endbar;
3008ec1d940Sknakahara #ifdef __HAVE_PCI_MSI_MSIX
3018ec1d940Sknakahara 	pcireg_t cap;
3028ec1d940Sknakahara 	int off;
3038ec1d940Sknakahara #endif
30461ccff52Sjmcneill 	int ret, pin, bus, device, function, i, width;
305fa3cb84dSdrochner 	int locs[PCICF_NLOCS];
3067c86eb62Sthorpej 
3077c86eb62Sthorpej 	pci_decompose_tag(pc, tag, &bus, &device, &function);
3087c86eb62Sthorpej 
309c67a72f6Sdrochner 	/* a driver already attached? */
310f05735daSdyoung 	if (sc->PCI_SC_DEVICESC(device, function).c_dev != NULL && !match)
3119b1a101dScegger 		return 0;
312c67a72f6Sdrochner 
3137c86eb62Sthorpej 	id = pci_conf_read(pc, tag, PCI_ID_REG);
3147c86eb62Sthorpej 
3157c86eb62Sthorpej 	/* Invalid vendor ID value? */
3167c86eb62Sthorpej 	if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
3179b1a101dScegger 		return 0;
3187c86eb62Sthorpej 	/* XXX Not invalid, but we've done this ~forever. */
3197c86eb62Sthorpej 	if (PCI_VENDOR(id) == 0)
3209b1a101dScegger 		return 0;
3217c86eb62Sthorpej 
32257cc42dcSjmcneill 	bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
32357cc42dcSjmcneill 	if (PCI_HDRTYPE_TYPE(bhlcr) > 2)
32457cc42dcSjmcneill 		return 0;
32557cc42dcSjmcneill 
32657cc42dcSjmcneill 	/* csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); */
32757cc42dcSjmcneill 	pciclass = pci_conf_read(pc, tag, PCI_CLASS_REG);
32857cc42dcSjmcneill 
32961ccff52Sjmcneill 	/* Collect memory range info */
33061ccff52Sjmcneill 	memset(sc->PCI_SC_DEVICESC(device, function).c_range, 0,
33161ccff52Sjmcneill 	    sizeof(sc->PCI_SC_DEVICESC(device, function).c_range));
33261ccff52Sjmcneill 	i = 0;
3335469ac9dSmatt 	switch (PCI_HDRTYPE_TYPE(bhlcr)) {
334bd9419d8Sdyoung 	case PCI_HDRTYPE_PPB:
335bd9419d8Sdyoung 		endbar = PCI_MAPREG_PPB_END;
336bd9419d8Sdyoung 		break;
337bd9419d8Sdyoung 	case PCI_HDRTYPE_PCB:
338bd9419d8Sdyoung 		endbar = PCI_MAPREG_PCB_END;
339bd9419d8Sdyoung 		break;
340bd9419d8Sdyoung 	default:
341bd9419d8Sdyoung 		endbar = PCI_MAPREG_END;
342bd9419d8Sdyoung 		break;
3435469ac9dSmatt 	}
3445469ac9dSmatt 	for (bar = PCI_MAPREG_START; bar < endbar; bar += width) {
34561ccff52Sjmcneill 		struct pci_range *r;
346c5d6c4ceSjmcneill 		pcireg_t type;
34761ccff52Sjmcneill 
34861ccff52Sjmcneill 		width = 4;
349c5d6c4ceSjmcneill 		if (pci_mapreg_probe(pc, tag, bar, &type) == 0)
350c5d6c4ceSjmcneill 			continue;
351c5d6c4ceSjmcneill 
35261ccff52Sjmcneill 		if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_MEM) {
35361ccff52Sjmcneill 			if (PCI_MAPREG_MEM_TYPE(type) ==
35461ccff52Sjmcneill 			    PCI_MAPREG_MEM_TYPE_64BIT)
35561ccff52Sjmcneill 				width = 8;
35661ccff52Sjmcneill 
35761ccff52Sjmcneill 			r = &sc->PCI_SC_DEVICESC(device, function).c_range[i++];
35861ccff52Sjmcneill 			if (pci_mapreg_info(pc, tag, bar, type,
35961ccff52Sjmcneill 			    &r->r_offset, &r->r_size, &r->r_flags) != 0)
36061ccff52Sjmcneill 				break;
361d78d6ba2Smacallan 			if ((PCI_VENDOR(id) == PCI_VENDOR_ATI) && (bar == 0x10)
362fdc4af24Smacallan 			    && (r->r_size == 0x1000000)) {
363d78d6ba2Smacallan 				struct pci_range *nr;
364d78d6ba2Smacallan 				/*
365d78d6ba2Smacallan 				 * this has to be a mach64
366d78d6ba2Smacallan 				 * split things up so each half-aperture can
367d78d6ba2Smacallan 				 * be mapped PREFETCHABLE except the last page
368d78d6ba2Smacallan 				 * which may contain registers
369d78d6ba2Smacallan 				 */
370d78d6ba2Smacallan 				r->r_size = 0x7ff000;
371d78d6ba2Smacallan 				r->r_flags = BUS_SPACE_MAP_LINEAR |
372d78d6ba2Smacallan 					     BUS_SPACE_MAP_PREFETCHABLE;
373d78d6ba2Smacallan 				nr = &sc->PCI_SC_DEVICESC(device,
374d78d6ba2Smacallan 				    function).c_range[i++];
375d78d6ba2Smacallan 				nr->r_offset = r->r_offset + 0x800000;
376d78d6ba2Smacallan 				nr->r_size = 0x7ff000;
377d78d6ba2Smacallan 				nr->r_flags = BUS_SPACE_MAP_LINEAR |
378d78d6ba2Smacallan 					      BUS_SPACE_MAP_PREFETCHABLE;
379c26dfe7dSmacallan 			} else if ((PCI_VENDOR(id) == PCI_VENDOR_SILMOTION) &&
380c26dfe7dSmacallan 			   (PCI_PRODUCT(id) == PCI_PRODUCT_SILMOTION_SM502) &&
381c26dfe7dSmacallan 			   (bar == 0x10)) {
382c26dfe7dSmacallan 			   	r->r_flags = BUS_SPACE_MAP_LINEAR |
383c26dfe7dSmacallan 					     BUS_SPACE_MAP_PREFETCHABLE;
384d78d6ba2Smacallan 			}
38561ccff52Sjmcneill 		}
38661ccff52Sjmcneill 	}
38761ccff52Sjmcneill 
3887c86eb62Sthorpej 	pa.pa_iot = sc->sc_iot;
3897c86eb62Sthorpej 	pa.pa_memt = sc->sc_memt;
3907c86eb62Sthorpej 	pa.pa_dmat = sc->sc_dmat;
3917dd7f8baSfvdl 	pa.pa_dmat64 = sc->sc_dmat64;
3927c86eb62Sthorpej 	pa.pa_pc = pc;
39386b4df33Sthorpej 	pa.pa_bus = bus;
3947c86eb62Sthorpej 	pa.pa_device = device;
3957c86eb62Sthorpej 	pa.pa_function = function;
3967c86eb62Sthorpej 	pa.pa_tag = tag;
3977c86eb62Sthorpej 	pa.pa_id = id;
398b4985db0Smatt 	pa.pa_class = pciclass;
3997c86eb62Sthorpej 
4007c86eb62Sthorpej 	/*
4017c86eb62Sthorpej 	 * Set up memory, I/O enable, and PCI command flags
4027c86eb62Sthorpej 	 * as appropriate.
4037c86eb62Sthorpej 	 */
4047c86eb62Sthorpej 	pa.pa_flags = sc->sc_flags;
4057c86eb62Sthorpej 
4067c86eb62Sthorpej 	/*
4077c86eb62Sthorpej 	 * If the cache line size is not configured, then
4087c86eb62Sthorpej 	 * clear the MRL/MRM/MWI command-ok flags.
4097c86eb62Sthorpej 	 */
410bd9419d8Sdyoung 	if (PCI_CACHELINE(bhlcr) == 0) {
4117c86eb62Sthorpej 		pa.pa_flags &= ~(PCI_FLAGS_MRL_OKAY|
4127c86eb62Sthorpej 		    PCI_FLAGS_MRM_OKAY|PCI_FLAGS_MWI_OKAY);
413bd9419d8Sdyoung 	}
4147c86eb62Sthorpej 
415314ef13cSsommerfeld 	if (sc->sc_bridgetag == NULL) {
4167c86eb62Sthorpej 		pa.pa_intrswiz = 0;
4177c86eb62Sthorpej 		pa.pa_intrtag = tag;
4187c86eb62Sthorpej 	} else {
4197c86eb62Sthorpej 		pa.pa_intrswiz = sc->sc_intrswiz + device;
4207c86eb62Sthorpej 		pa.pa_intrtag = sc->sc_intrtag;
4217c86eb62Sthorpej 	}
422c2ab035dSitojun 
423c2ab035dSitojun 	intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
424c2ab035dSitojun 
4257c86eb62Sthorpej 	pin = PCI_INTERRUPT_PIN(intr);
426ce6cc567Ssommerfeld 	pa.pa_rawintrpin = pin;
4277c86eb62Sthorpej 	if (pin == PCI_INTERRUPT_PIN_NONE) {
4287c86eb62Sthorpej 		/* no interrupt */
4297c86eb62Sthorpej 		pa.pa_intrpin = 0;
4307c86eb62Sthorpej 	} else {
4317c86eb62Sthorpej 		/*
4327c86eb62Sthorpej 		 * swizzle it based on the number of busses we're
4337c86eb62Sthorpej 		 * behind and our device number.
4347c86eb62Sthorpej 		 */
4357c86eb62Sthorpej 		pa.pa_intrpin = 	/* XXX */
4367c86eb62Sthorpej 		    ((pin + pa.pa_intrswiz - 1) % 4) + 1;
4377c86eb62Sthorpej 	}
4387c86eb62Sthorpej 	pa.pa_intrline = PCI_INTERRUPT_LINE(intr);
4397c86eb62Sthorpej 
4404c5901a8Sthorpej 	devhandle_t devhandle = pci_bus_get_child_devhandle(sc, pa.pa_tag);
4414c5901a8Sthorpej 
4428ec1d940Sknakahara #ifdef __HAVE_PCI_MSI_MSIX
4438ec1d940Sknakahara 	if (pci_get_ht_capability(pc, tag, PCI_HT_CAP_MSIMAP, &off, &cap)) {
4448ec1d940Sknakahara 		/*
4458ec1d940Sknakahara 		 * XXX Should we enable MSI mapping ourselves on
4468ec1d940Sknakahara 		 * systems that have it disabled?
4478ec1d940Sknakahara 		 */
4488ec1d940Sknakahara 		if (cap & PCI_HT_MSI_ENABLED) {
4498ec1d940Sknakahara 			uint64_t addr;
4508ec1d940Sknakahara 			if ((cap & PCI_HT_MSI_FIXED) == 0) {
4518ec1d940Sknakahara 				addr = pci_conf_read(pc, tag,
4528ec1d940Sknakahara 				    off + PCI_HT_MSI_ADDR_LO);
4538ec1d940Sknakahara 				addr |= (uint64_t)pci_conf_read(pc, tag,
4548ec1d940Sknakahara 				    off + PCI_HT_MSI_ADDR_HI) << 32;
4558ec1d940Sknakahara 			} else
4568ec1d940Sknakahara 				addr = PCI_HT_MSI_FIXED_ADDR;
4578ec1d940Sknakahara 
4588ec1d940Sknakahara 			/*
4598ec1d940Sknakahara 			 * XXX This will fail to enable MSI on systems
4608ec1d940Sknakahara 			 * that don't use the canonical address.
4618ec1d940Sknakahara 			 */
4628ec1d940Sknakahara 			if (addr == PCI_HT_MSI_FIXED_ADDR) {
4638ec1d940Sknakahara 				pa.pa_flags |= PCI_FLAGS_MSI_OKAY;
4648ec1d940Sknakahara 				pa.pa_flags |= PCI_FLAGS_MSIX_OKAY;
465191ca94eSknakahara 			} else
466191ca94eSknakahara 				aprint_verbose_dev(sc->sc_dev,
467191ca94eSknakahara 				    "HyperTransport MSI mapping is not supported yet. Disable MSI/MSI-X.\n");
4688ec1d940Sknakahara 		}
4698ec1d940Sknakahara 	}
4708ec1d940Sknakahara #endif
4718ec1d940Sknakahara 
4727c86eb62Sthorpej 	if (match != NULL) {
473*e82ab0a3Sriastradh 		ret = (*match)(cookie, &pa);
4747c86eb62Sthorpej 		if (ret != 0 && pap != NULL)
4757c86eb62Sthorpej 			*pap = pa;
4767c86eb62Sthorpej 	} else {
477f05735daSdyoung 		struct pci_child *c;
478fa3cb84dSdrochner 		locs[PCICF_DEV] = device;
479fa3cb84dSdrochner 		locs[PCICF_FUNCTION] = function;
480c67a72f6Sdrochner 
481f05735daSdyoung 		c = &sc->PCI_SC_DEVICESC(device, function);
482f05735daSdyoung 		pci_conf_capture(pc, tag, &c->c_conf);
483f05735daSdyoung 		if (pci_get_powerstate(pc, tag, &c->c_powerstate) == 0)
484f05735daSdyoung 			c->c_psok = true;
485f05735daSdyoung 		else
486f05735daSdyoung 			c->c_psok = false;
487933f8ac7Sdyoung 
4882685996bSthorpej 		c->c_dev = config_found(sc->sc_dev, &pa, pciprint,
489c7fb772bSthorpej 		    CFARGS(.submatch = config_stdsubmatch,
490c7fb772bSthorpej 			   .locators = locs,
491c7fb772bSthorpej 			   .devhandle = devhandle));
492933f8ac7Sdyoung 
493933f8ac7Sdyoung 		ret = (c->c_dev != NULL);
4947c86eb62Sthorpej 	}
4957c86eb62Sthorpej 
4969b1a101dScegger 	return ret;
4977c86eb62Sthorpej }
4987c86eb62Sthorpej 
49982efc281Sdyoung void
pcidevdetached(device_t self,device_t child)5008ce2158bSdyoung pcidevdetached(device_t self, device_t child)
501c67a72f6Sdrochner {
502f05735daSdyoung 	struct pci_softc *sc = device_private(self);
503c67a72f6Sdrochner 	int d, f;
504f05735daSdyoung 	pcitag_t tag;
505f05735daSdyoung 	struct pci_child *c;
506c67a72f6Sdrochner 
5078ce2158bSdyoung 	d = device_locator(child, PCICF_DEV);
5088ce2158bSdyoung 	f = device_locator(child, PCICF_FUNCTION);
509c67a72f6Sdrochner 
510f05735daSdyoung 	c = &sc->PCI_SC_DEVICESC(d, f);
511c67a72f6Sdrochner 
512f05735daSdyoung 	KASSERT(c->c_dev == child);
513f05735daSdyoung 
514f05735daSdyoung 	tag = pci_make_tag(sc->sc_pc, sc->sc_bus, d, f);
515f05735daSdyoung 	if (c->c_psok)
516f05735daSdyoung 		pci_set_powerstate(sc->sc_pc, tag, c->c_powerstate);
517f05735daSdyoung 	pci_conf_restore(sc->sc_pc, tag, &c->c_conf);
518f05735daSdyoung 	c->c_dev = NULL;
519c67a72f6Sdrochner }
520c67a72f6Sdrochner 
5210d1ba3e8Sdyoung CFATTACH_DECL3_NEW(pci, sizeof(struct pci_softc),
5220d1ba3e8Sdyoung     pcimatch, pciattach, pcidetach, NULL, pcirescan, pcidevdetached,
5230d1ba3e8Sdyoung     DVF_DETACH_SHUTDOWN);
5244c1d81b2Sjmcneill 
5257c86eb62Sthorpej int
pci_get_capability(pci_chipset_tag_t pc,pcitag_t tag,int capid,int * offset,pcireg_t * value)526d36c43c5Sthorpej pci_get_capability(pci_chipset_tag_t pc, pcitag_t tag, int capid,
527d36c43c5Sthorpej     int *offset, pcireg_t *value)
528bbe9d714Sdrochner {
529bbe9d714Sdrochner 	pcireg_t reg;
530bbe9d714Sdrochner 	unsigned int ofs;
531bbe9d714Sdrochner 
532bbe9d714Sdrochner 	reg = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
533bbe9d714Sdrochner 	if (!(reg & PCI_STATUS_CAPLIST_SUPPORT))
5349b1a101dScegger 		return 0;
535bbe9d714Sdrochner 
5367a6f0037Skleink 	/* Determine the Capability List Pointer register to start with. */
53705f0b175Skleink 	reg = pci_conf_read(pc, tag, PCI_BHLC_REG);
53805f0b175Skleink 	switch (PCI_HDRTYPE_TYPE(reg)) {
53905f0b175Skleink 	case 0:	/* standard device header */
5407f01b64cSjoerg 	case 1: /* PCI-PCI bridge header */
54105f0b175Skleink 		ofs = PCI_CAPLISTPTR_REG;
54205f0b175Skleink 		break;
54305f0b175Skleink 	case 2:	/* PCI-CardBus Bridge header */
54405f0b175Skleink 		ofs = PCI_CARDBUS_CAPLISTPTR_REG;
54505f0b175Skleink 		break;
54605f0b175Skleink 	default:
5479b1a101dScegger 		return 0;
54805f0b175Skleink 	}
54905f0b175Skleink 
55005f0b175Skleink 	ofs = PCI_CAPLIST_PTR(pci_conf_read(pc, tag, ofs));
551bbe9d714Sdrochner 	while (ofs != 0) {
552b8df2b2dSjoerg 		if ((ofs & 3) || (ofs < 0x40)) {
553b8df2b2dSjoerg 			int bus, device, function;
554b8df2b2dSjoerg 
555b8df2b2dSjoerg 			pci_decompose_tag(pc, tag, &bus, &device, &function);
556b8df2b2dSjoerg 
557b8df2b2dSjoerg 			printf("Skipping broken PCI header on %d:%d:%d\n",
558b8df2b2dSjoerg 			    bus, device, function);
559b8df2b2dSjoerg 			break;
560b8df2b2dSjoerg 		}
561bbe9d714Sdrochner 		reg = pci_conf_read(pc, tag, ofs);
562bbe9d714Sdrochner 		if (PCI_CAPLIST_CAP(reg) == capid) {
563bbe9d714Sdrochner 			if (offset)
564bbe9d714Sdrochner 				*offset = ofs;
565bbe9d714Sdrochner 			if (value)
566bbe9d714Sdrochner 				*value = reg;
5679b1a101dScegger 			return 1;
568bbe9d714Sdrochner 		}
569bbe9d714Sdrochner 		ofs = PCI_CAPLIST_NEXT(reg);
570bbe9d714Sdrochner 	}
571bbe9d714Sdrochner 
5729b1a101dScegger 	return 0;
573bbe9d714Sdrochner }
574ac50343cSfvdl 
575ac50343cSfvdl int
pci_get_ht_capability(pci_chipset_tag_t pc,pcitag_t tag,int capid,int * offset,pcireg_t * value)5768ec1d940Sknakahara pci_get_ht_capability(pci_chipset_tag_t pc, pcitag_t tag, int capid,
5778ec1d940Sknakahara     int *offset, pcireg_t *value)
5788ec1d940Sknakahara {
5798ec1d940Sknakahara 	pcireg_t reg;
5808ec1d940Sknakahara 	unsigned int ofs;
5818ec1d940Sknakahara 
5828ec1d940Sknakahara 	if (pci_get_capability(pc, tag, PCI_CAP_LDT, &ofs, NULL) == 0)
5838ec1d940Sknakahara 		return 0;
5848ec1d940Sknakahara 
5858ec1d940Sknakahara 	while (ofs != 0) {
5868ec1d940Sknakahara #ifdef DIAGNOSTIC
5878ec1d940Sknakahara 		if ((ofs & 3) || (ofs < 0x40))
5888ec1d940Sknakahara 			panic("pci_get_ht_capability");
5898ec1d940Sknakahara #endif
5908ec1d940Sknakahara 		reg = pci_conf_read(pc, tag, ofs);
5918ec1d940Sknakahara 		if (PCI_HT_CAP(reg) == capid) {
5928ec1d940Sknakahara 			if (offset)
5938ec1d940Sknakahara 				*offset = ofs;
5948ec1d940Sknakahara 			if (value)
5958ec1d940Sknakahara 				*value = reg;
5968ec1d940Sknakahara 			return 1;
5978ec1d940Sknakahara 		}
5988ec1d940Sknakahara 		ofs = PCI_CAPLIST_NEXT(reg);
5998ec1d940Sknakahara 	}
6008ec1d940Sknakahara 
6018ec1d940Sknakahara 	return 0;
6028ec1d940Sknakahara }
6038ec1d940Sknakahara 
604b62abb6eSmsaitoh /*
605b62abb6eSmsaitoh  * return number of the devices's MSI vectors
606b62abb6eSmsaitoh  * return 0 if the device does not support MSI
607b62abb6eSmsaitoh  */
608b62abb6eSmsaitoh int
pci_msi_count(pci_chipset_tag_t pc,pcitag_t tag)609b62abb6eSmsaitoh pci_msi_count(pci_chipset_tag_t pc, pcitag_t tag)
610b62abb6eSmsaitoh {
611b62abb6eSmsaitoh 	pcireg_t reg;
612b62abb6eSmsaitoh 	uint32_t mmc;
613b62abb6eSmsaitoh 	int count, offset;
614b62abb6eSmsaitoh 
615b62abb6eSmsaitoh 	if (pci_get_capability(pc, tag, PCI_CAP_MSI, &offset, NULL) == 0)
616b62abb6eSmsaitoh 		return 0;
617b62abb6eSmsaitoh 
618b62abb6eSmsaitoh 	reg = pci_conf_read(pc, tag, offset + PCI_MSI_CTL);
619b62abb6eSmsaitoh 	mmc = PCI_MSI_CTL_MMC(reg);
620b62abb6eSmsaitoh 	count = 1 << mmc;
621b62abb6eSmsaitoh 	if (count > PCI_MSI_MAX_VECTORS) {
622b62abb6eSmsaitoh 		aprint_error("detect an illegal device! The device use reserved MMC values.\n");
623b62abb6eSmsaitoh 		return 0;
624b62abb6eSmsaitoh 	}
625b62abb6eSmsaitoh 
626b62abb6eSmsaitoh 	return count;
627b62abb6eSmsaitoh }
628b62abb6eSmsaitoh 
629b62abb6eSmsaitoh /*
630b62abb6eSmsaitoh  * return number of the devices's MSI-X vectors
631b62abb6eSmsaitoh  * return 0 if the device does not support MSI-X
632b62abb6eSmsaitoh  */
633b62abb6eSmsaitoh int
pci_msix_count(pci_chipset_tag_t pc,pcitag_t tag)634b62abb6eSmsaitoh pci_msix_count(pci_chipset_tag_t pc, pcitag_t tag)
635b62abb6eSmsaitoh {
636b62abb6eSmsaitoh 	pcireg_t reg;
637b62abb6eSmsaitoh 	int offset;
638b62abb6eSmsaitoh 
639b62abb6eSmsaitoh 	if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &offset, NULL) == 0)
640b62abb6eSmsaitoh 		return 0;
641b62abb6eSmsaitoh 
642b62abb6eSmsaitoh 	reg = pci_conf_read(pc, tag, offset + PCI_MSIX_CTL);
643b62abb6eSmsaitoh 
644b62abb6eSmsaitoh 	return PCI_MSIX_CTL_TBLSIZE(reg);
645b62abb6eSmsaitoh }
646b62abb6eSmsaitoh 
6478ec1d940Sknakahara int
pci_get_ext_capability(pci_chipset_tag_t pc,pcitag_t tag,int capid,int * offset,pcireg_t * value)648605f564fSmsaitoh pci_get_ext_capability(pci_chipset_tag_t pc, pcitag_t tag, int capid,
649605f564fSmsaitoh     int *offset, pcireg_t *value)
650605f564fSmsaitoh {
651605f564fSmsaitoh 	pcireg_t reg;
652605f564fSmsaitoh 	unsigned int ofs;
653605f564fSmsaitoh 
654605f564fSmsaitoh 	/* Only supported for PCI-express devices */
655605f564fSmsaitoh 	if (!pci_get_capability(pc, tag, PCI_CAP_PCIEXPRESS, NULL, NULL))
656605f564fSmsaitoh 		return 0;
657605f564fSmsaitoh 
658605f564fSmsaitoh 	ofs = PCI_EXTCAPLIST_BASE;
659605f564fSmsaitoh 	reg = pci_conf_read(pc, tag, ofs);
660605f564fSmsaitoh 	if (reg == 0xffffffff || reg == 0)
661605f564fSmsaitoh 		return 0;
662605f564fSmsaitoh 
663605f564fSmsaitoh 	for (;;) {
664605f564fSmsaitoh #ifdef DIAGNOSTIC
665605f564fSmsaitoh 		if ((ofs & 3) || ofs < PCI_EXTCAPLIST_BASE)
666605f564fSmsaitoh 			panic("%s: invalid offset %u", __func__, ofs);
667605f564fSmsaitoh #endif
668605f564fSmsaitoh 		if (PCI_EXTCAPLIST_CAP(reg) == capid) {
669605f564fSmsaitoh 			if (offset != NULL)
670605f564fSmsaitoh 				*offset = ofs;
671605f564fSmsaitoh 			if (value != NULL)
672605f564fSmsaitoh 				*value = reg;
673605f564fSmsaitoh 			return 1;
674605f564fSmsaitoh 		}
675605f564fSmsaitoh 		ofs = PCI_EXTCAPLIST_NEXT(reg);
676605f564fSmsaitoh 		if (ofs == 0)
677605f564fSmsaitoh 			break;
678605f564fSmsaitoh 		reg = pci_conf_read(pc, tag, ofs);
679605f564fSmsaitoh 	}
680605f564fSmsaitoh 
681605f564fSmsaitoh 	return 0;
682605f564fSmsaitoh }
683605f564fSmsaitoh 
684*e82ab0a3Sriastradh static int
pci_match_cookieless(void * cookie,const struct pci_attach_args * pa)685*e82ab0a3Sriastradh pci_match_cookieless(void *cookie, const struct pci_attach_args *pa)
686*e82ab0a3Sriastradh {
687*e82ab0a3Sriastradh 	int (*match)(const struct pci_attach_args *) = cookie;
688*e82ab0a3Sriastradh 
689*e82ab0a3Sriastradh 	return (*match)(pa);
690*e82ab0a3Sriastradh }
691*e82ab0a3Sriastradh 
692605f564fSmsaitoh int
pci_find_device(struct pci_attach_args * pa,int (* match)(const struct pci_attach_args *))693ac50343cSfvdl pci_find_device(struct pci_attach_args *pa,
694d3e53912Sdyoung     int (*match)(const struct pci_attach_args *))
695ac50343cSfvdl {
696*e82ab0a3Sriastradh 	void *cookie = match;
697*e82ab0a3Sriastradh 
698*e82ab0a3Sriastradh 	return (match == NULL
699*e82ab0a3Sriastradh 	    ? pci_find_device1(pa, NULL, NULL)
700*e82ab0a3Sriastradh 	    : pci_find_device1(pa, &pci_match_cookieless, cookie));
701*e82ab0a3Sriastradh }
702*e82ab0a3Sriastradh 
703*e82ab0a3Sriastradh int
pci_find_device1(struct pci_attach_args * pa,int (* match)(void *,const struct pci_attach_args *),void * cookie)704*e82ab0a3Sriastradh pci_find_device1(struct pci_attach_args *pa,
705*e82ab0a3Sriastradh     int (*match)(void *, const struct pci_attach_args *), void *cookie)
706*e82ab0a3Sriastradh {
707ac50343cSfvdl 	extern struct cfdriver pci_cd;
7088ce2158bSdyoung 	device_t pcidev;
7097c86eb62Sthorpej 	int i;
710c67a72f6Sdrochner 	static const int wildcard[2] = {
711c67a72f6Sdrochner 		PCICF_DEV_DEFAULT,
712c67a72f6Sdrochner 		PCICF_FUNCTION_DEFAULT
713c67a72f6Sdrochner 	};
714ac50343cSfvdl 
715ac50343cSfvdl 	for (i = 0; i < pci_cd.cd_ndevs; i++) {
7163bde75a1Scegger 		pcidev = device_lookup(&pci_cd, i);
7177c86eb62Sthorpej 		if (pcidev != NULL &&
718*e82ab0a3Sriastradh 		    pci_enumerate_bus1(device_private(pcidev), wildcard,
719*e82ab0a3Sriastradh 			match, cookie, pa) != 0)
7209b1a101dScegger 			return 1;
721ac50343cSfvdl 	}
7229b1a101dScegger 	return 0;
7237c86eb62Sthorpej }
7247c86eb62Sthorpej 
725*e82ab0a3Sriastradh int
pci_enumerate_bus(struct pci_softc * sc,const int * locators,int (* match)(const struct pci_attach_args *),struct pci_attach_args * pap)726*e82ab0a3Sriastradh pci_enumerate_bus(struct pci_softc *sc, const int *locators,
727*e82ab0a3Sriastradh     int (*match)(const struct pci_attach_args *), struct pci_attach_args *pap)
728*e82ab0a3Sriastradh {
729*e82ab0a3Sriastradh 	void *cookie = match;
730*e82ab0a3Sriastradh 
731*e82ab0a3Sriastradh 	return (match == NULL
732*e82ab0a3Sriastradh 	    ? pci_enumerate_bus1(sc, locators, NULL, NULL, pap)
733*e82ab0a3Sriastradh 	    : pci_enumerate_bus1(sc, locators, &pci_match_cookieless, cookie,
734*e82ab0a3Sriastradh 		pap));
735*e82ab0a3Sriastradh }
736*e82ab0a3Sriastradh 
737*e82ab0a3Sriastradh #ifndef PCI_MACHDEP_ENUMERATE_BUS1
7387c86eb62Sthorpej /*
7397c86eb62Sthorpej  * Generic PCI bus enumeration routine.  Used unless machine-dependent
7407c86eb62Sthorpej  * code needs to provide something else.
7417c86eb62Sthorpej  */
7427c86eb62Sthorpej int
pci_enumerate_bus1(struct pci_softc * sc,const int * locators,int (* match)(void *,const struct pci_attach_args *),void * cookie,struct pci_attach_args * pap)743*e82ab0a3Sriastradh pci_enumerate_bus1(struct pci_softc *sc, const int *locators,
744*e82ab0a3Sriastradh     int (*match)(void *, const struct pci_attach_args *), void *cookie,
745*e82ab0a3Sriastradh     struct pci_attach_args *pap)
7467c86eb62Sthorpej {
7477c86eb62Sthorpej 	pci_chipset_tag_t pc = sc->sc_pc;
7487c86eb62Sthorpej 	int device, function, nfunctions, ret;
7497c86eb62Sthorpej 	const struct pci_quirkdata *qd;
7507c86eb62Sthorpej 	pcireg_t id, bhlcr;
7517c86eb62Sthorpej 	pcitag_t tag;
752a33d02a1Sdyoung 	uint8_t devs[32];
753a33d02a1Sdyoung 	int i, n;
7547c86eb62Sthorpej 
7552f141151Smsaitoh 	device_t bridgedev;
7562f141151Smsaitoh 	bool arien = false;
757b19ff0b6Sjmcneill 	bool downstream_port = false;
7582f141151Smsaitoh 
759b19ff0b6Sjmcneill 	/* Check PCIe ARI and port type */
7602f141151Smsaitoh 	bridgedev = device_parent(sc->sc_dev);
7612f141151Smsaitoh 	if (device_is_a(bridgedev, "ppb")) {
7622f141151Smsaitoh 		struct ppb_softc *ppbsc = device_private(bridgedev);
7632f141151Smsaitoh 		pci_chipset_tag_t ppbpc = ppbsc->sc_pc;
7642f141151Smsaitoh 		pcitag_t ppbtag = ppbsc->sc_tag;
765b19ff0b6Sjmcneill 		pcireg_t pciecap, capreg, reg;
7662f141151Smsaitoh 
7672f141151Smsaitoh 		if (pci_get_capability(ppbpc, ppbtag, PCI_CAP_PCIEXPRESS,
768b19ff0b6Sjmcneill 		    &pciecap, &capreg) != 0) {
769b19ff0b6Sjmcneill 			switch (PCIE_XCAP_TYPE(capreg)) {
7704aa8ca95Smsaitoh 			case PCIE_XCAP_TYPE_RP:
771b19ff0b6Sjmcneill 			case PCIE_XCAP_TYPE_DOWN:
772b19ff0b6Sjmcneill 			case PCIE_XCAP_TYPE_PCI2PCIE:
773b19ff0b6Sjmcneill 				downstream_port = true;
774b19ff0b6Sjmcneill 				break;
775b19ff0b6Sjmcneill 			}
776b19ff0b6Sjmcneill 
7772f141151Smsaitoh 			reg = pci_conf_read(ppbpc, ppbtag, pciecap
7782f141151Smsaitoh 			    + PCIE_DCSR2);
7792f141151Smsaitoh 			if ((reg & PCIE_DCSR2_ARI_FWD) != 0)
7802f141151Smsaitoh 				arien = true;
7812f141151Smsaitoh 		}
7822f141151Smsaitoh 	}
7832f141151Smsaitoh 
784a33d02a1Sdyoung 	n = pci_bus_devorder(sc->sc_pc, sc->sc_bus, devs, __arraycount(devs));
785b19ff0b6Sjmcneill 	if (downstream_port) {
786b19ff0b6Sjmcneill 		/* PCIe downstream ports only have a single child device */
787b19ff0b6Sjmcneill 		n = 1;
788b19ff0b6Sjmcneill 	}
789b19ff0b6Sjmcneill 
790a33d02a1Sdyoung 	for (i = 0; i < n; i++) {
791a33d02a1Sdyoung 		device = devs[i];
792a33d02a1Sdyoung 
793c67a72f6Sdrochner 		if ((locators[PCICF_DEV] != PCICF_DEV_DEFAULT) &&
794c67a72f6Sdrochner 		    (locators[PCICF_DEV] != device))
795c67a72f6Sdrochner 			continue;
796c67a72f6Sdrochner 
7977c86eb62Sthorpej 		tag = pci_make_tag(pc, sc->sc_bus, device, 0);
798c2ab035dSitojun 
7997c86eb62Sthorpej 		id = pci_conf_read(pc, tag, PCI_ID_REG);
8007c86eb62Sthorpej 
8017c86eb62Sthorpej 		/* Invalid vendor ID value? */
8027c86eb62Sthorpej 		if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
8037c86eb62Sthorpej 			continue;
8047c86eb62Sthorpej 		/* XXX Not invalid, but we've done this ~forever. */
8057c86eb62Sthorpej 		if (PCI_VENDOR(id) == 0)
8067c86eb62Sthorpej 			continue;
8077c86eb62Sthorpej 
808e6570364Sjmcneill 		bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
809e6570364Sjmcneill 		if (PCI_HDRTYPE_TYPE(bhlcr) > 2)
810e6570364Sjmcneill 			continue;
811e6570364Sjmcneill 
8127c86eb62Sthorpej 		qd = pci_lookup_quirkdata(PCI_VENDOR(id), PCI_PRODUCT(id));
8137c86eb62Sthorpej 
814c2ab035dSitojun 		if (qd != NULL &&
815c2ab035dSitojun 		      (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0)
8167c86eb62Sthorpej 			nfunctions = 8;
817c2ab035dSitojun 		else if (qd != NULL &&
818c2ab035dSitojun 		      (qd->quirks & PCI_QUIRK_MONOFUNCTION) != 0)
8197c86eb62Sthorpej 			nfunctions = 1;
8202f141151Smsaitoh 		else if (arien)
8212f141151Smsaitoh 			nfunctions = 8; /* Scan all if ARI is enabled */
822c2ab035dSitojun 		else
823c2ab035dSitojun 			nfunctions = PCI_HDRTYPE_MULTIFN(bhlcr) ? 8 : 1;
8247c86eb62Sthorpej 
8257d09ca48Smatt #ifdef __PCI_DEV_FUNCORDER
8267d09ca48Smatt 		char funcs[8];
8277d09ca48Smatt 		int j;
8287d09ca48Smatt 		for (j = 0; j < nfunctions; j++) {
8297d09ca48Smatt 			funcs[j] = j;
8307d09ca48Smatt 		}
8317d09ca48Smatt 		if (j < __arraycount(funcs))
8327d09ca48Smatt 			funcs[j] = -1;
8337d09ca48Smatt 		if (nfunctions > 1) {
8347d09ca48Smatt 			pci_dev_funcorder(sc->sc_pc, sc->sc_bus, device,
8357d09ca48Smatt 			    nfunctions, funcs);
8367d09ca48Smatt 		}
8377d09ca48Smatt 		for (j = 0;
8387d09ca48Smatt 		     j < 8 && (function = funcs[j]) < 8 && function >= 0;
8397d09ca48Smatt 		     j++) {
8407d09ca48Smatt #else
8417c86eb62Sthorpej 		for (function = 0; function < nfunctions; function++) {
8427d09ca48Smatt #endif
843c67a72f6Sdrochner 			if ((locators[PCICF_FUNCTION] != PCICF_FUNCTION_DEFAULT)
844c67a72f6Sdrochner 			    && (locators[PCICF_FUNCTION] != function))
845c67a72f6Sdrochner 				continue;
846c67a72f6Sdrochner 
847c2ab035dSitojun 			if (qd != NULL &&
848c2ab035dSitojun 			    (qd->quirks & PCI_QUIRK_SKIP_FUNC(function)) != 0)
849c2ab035dSitojun 				continue;
8507c86eb62Sthorpej 			tag = pci_make_tag(pc, sc->sc_bus, device, function);
851*e82ab0a3Sriastradh 			ret = pci_probe_device1(sc, tag, match, cookie, pap);
8527c86eb62Sthorpej 			if (match != NULL && ret != 0)
8539b1a101dScegger 				return ret;
8547c86eb62Sthorpej 		}
8557c86eb62Sthorpej 	}
8569b1a101dScegger 	return 0;
857ac50343cSfvdl }
858*e82ab0a3Sriastradh #endif /* PCI_MACHDEP_ENUMERATE_BUS1 */
859fe6f56bfStshiozak 
860ecb44c32Sthorpej 
861ecb44c32Sthorpej /*
862ecb44c32Sthorpej  * Vital Product Data (PCI 2.2)
863ecb44c32Sthorpej  */
864ecb44c32Sthorpej 
865ecb44c32Sthorpej int
866ecb44c32Sthorpej pci_vpd_read(pci_chipset_tag_t pc, pcitag_t tag, int offset, int count,
867ecb44c32Sthorpej     pcireg_t *data)
868ecb44c32Sthorpej {
869ecb44c32Sthorpej 	uint32_t reg;
870ecb44c32Sthorpej 	int ofs, i, j;
871ecb44c32Sthorpej 
872ecb44c32Sthorpej 	KASSERT(data != NULL);
873ecb44c32Sthorpej 	KASSERT((offset + count) < 0x7fff);
874ecb44c32Sthorpej 
875ecb44c32Sthorpej 	if (pci_get_capability(pc, tag, PCI_CAP_VPD, &ofs, &reg) == 0)
8769b1a101dScegger 		return 1;
877ecb44c32Sthorpej 
878ecb44c32Sthorpej 	for (i = 0; i < count; offset += sizeof(*data), i++) {
879ecb44c32Sthorpej 		reg &= 0x0000ffff;
880ecb44c32Sthorpej 		reg &= ~PCI_VPD_OPFLAG;
881ecb44c32Sthorpej 		reg |= PCI_VPD_ADDRESS(offset);
882ecb44c32Sthorpej 		pci_conf_write(pc, tag, ofs, reg);
883ecb44c32Sthorpej 
884ecb44c32Sthorpej 		/*
885ecb44c32Sthorpej 		 * PCI 2.2 does not specify how long we should poll
886ecb44c32Sthorpej 		 * for completion nor whether the operation can fail.
887ecb44c32Sthorpej 		 */
888ecb44c32Sthorpej 		j = 0;
889ecb44c32Sthorpej 		do {
890ecb44c32Sthorpej 			if (j++ == 20)
8919b1a101dScegger 				return 1;
892ecb44c32Sthorpej 			delay(4);
893ecb44c32Sthorpej 			reg = pci_conf_read(pc, tag, ofs);
894ecb44c32Sthorpej 		} while ((reg & PCI_VPD_OPFLAG) == 0);
895ecb44c32Sthorpej 		data[i] = pci_conf_read(pc, tag, PCI_VPD_DATAREG(ofs));
896ecb44c32Sthorpej 	}
897ecb44c32Sthorpej 
8989b1a101dScegger 	return 0;
899ecb44c32Sthorpej }
900ecb44c32Sthorpej 
901ecb44c32Sthorpej int
902ecb44c32Sthorpej pci_vpd_write(pci_chipset_tag_t pc, pcitag_t tag, int offset, int count,
903ecb44c32Sthorpej     pcireg_t *data)
904ecb44c32Sthorpej {
905ecb44c32Sthorpej 	pcireg_t reg;
906ecb44c32Sthorpej 	int ofs, i, j;
907ecb44c32Sthorpej 
908ecb44c32Sthorpej 	KASSERT(data != NULL);
909ecb44c32Sthorpej 	KASSERT((offset + count) < 0x7fff);
910ecb44c32Sthorpej 
911ecb44c32Sthorpej 	if (pci_get_capability(pc, tag, PCI_CAP_VPD, &ofs, &reg) == 0)
9129b1a101dScegger 		return 1;
913ecb44c32Sthorpej 
914ecb44c32Sthorpej 	for (i = 0; i < count; offset += sizeof(*data), i++) {
915ecb44c32Sthorpej 		pci_conf_write(pc, tag, PCI_VPD_DATAREG(ofs), data[i]);
916ecb44c32Sthorpej 
917ecb44c32Sthorpej 		reg &= 0x0000ffff;
9189039a931Sthorpej 		reg |= PCI_VPD_OPFLAG;
919ecb44c32Sthorpej 		reg |= PCI_VPD_ADDRESS(offset);
920ecb44c32Sthorpej 		pci_conf_write(pc, tag, ofs, reg);
921ecb44c32Sthorpej 
922ecb44c32Sthorpej 		/*
923ecb44c32Sthorpej 		 * PCI 2.2 does not specify how long we should poll
924ecb44c32Sthorpej 		 * for completion nor whether the operation can fail.
925ecb44c32Sthorpej 		 */
926ecb44c32Sthorpej 		j = 0;
927ecb44c32Sthorpej 		do {
928ecb44c32Sthorpej 			if (j++ == 20)
9299b1a101dScegger 				return 1;
930ecb44c32Sthorpej 			delay(1);
931ecb44c32Sthorpej 			reg = pci_conf_read(pc, tag, ofs);
9329039a931Sthorpej 		} while (reg & PCI_VPD_OPFLAG);
933ecb44c32Sthorpej 	}
934ecb44c32Sthorpej 
9359b1a101dScegger 	return 0;
936ecb44c32Sthorpej }
9377dd7f8baSfvdl 
9387dd7f8baSfvdl int
939d3e53912Sdyoung pci_dma64_available(const struct pci_attach_args *pa)
9407dd7f8baSfvdl {
9417dd7f8baSfvdl #ifdef _PCI_HAVE_DMA64
9425c26c1f7Sbouyer 	if (BUS_DMA_TAG_VALID(pa->pa_dmat64))
9437dd7f8baSfvdl                         return 1;
9447dd7f8baSfvdl #endif
9457dd7f8baSfvdl         return 0;
9467dd7f8baSfvdl }
9479c40186bSjmcneill 
9489c40186bSjmcneill void
9499c40186bSjmcneill pci_conf_capture(pci_chipset_tag_t pc, pcitag_t tag,
9509c40186bSjmcneill 		  struct pci_conf_state *pcs)
9519c40186bSjmcneill {
9529c40186bSjmcneill 	int off;
9539c40186bSjmcneill 
9549c40186bSjmcneill 	for (off = 0; off < 16; off++)
9559c40186bSjmcneill 		pcs->reg[off] = pci_conf_read(pc, tag, (off * 4));
9569c40186bSjmcneill 
9577ce00762Smsaitoh 	/* For PCI-X */
9587ce00762Smsaitoh 	if (pci_get_capability(pc, tag, PCI_CAP_PCIX, &off, NULL) != 0)
9597ce00762Smsaitoh 		pcs->x_csr = pci_conf_read(pc, tag, off + PCIX_CMD);
9607ce00762Smsaitoh 
9617ce00762Smsaitoh 	/* For PCIe */
9627ce00762Smsaitoh 	if (pci_get_capability(pc, tag, PCI_CAP_PCIEXPRESS, &off, NULL) != 0) {
9637ce00762Smsaitoh 		pcireg_t xcap = pci_conf_read(pc, tag, off + PCIE_XCAP);
9647ce00762Smsaitoh 		unsigned int devtype;
9657ce00762Smsaitoh 
9667ce00762Smsaitoh 		devtype = PCIE_XCAP_TYPE(xcap);
9677ce00762Smsaitoh 		pcs->e_dcr = (uint16_t)pci_conf_read(pc, tag, off + PCIE_DCSR);
9687ce00762Smsaitoh 
9697ce00762Smsaitoh 		if (PCIE_HAS_LINKREGS(devtype))
9707ce00762Smsaitoh 			pcs->e_lcr = (uint16_t)pci_conf_read(pc, tag,
9717ce00762Smsaitoh 			    off + PCIE_LCSR);
9727ce00762Smsaitoh 
9737ce00762Smsaitoh 		if ((xcap & PCIE_XCAP_SI) != 0)
9747ce00762Smsaitoh 			pcs->e_slcr = (uint16_t)pci_conf_read(pc, tag,
9757ce00762Smsaitoh 			    off + PCIE_SLCSR);
9767ce00762Smsaitoh 
9777ce00762Smsaitoh 		if (PCIE_HAS_ROOTREGS(devtype))
9787ce00762Smsaitoh 			pcs->e_rcr = (uint16_t)pci_conf_read(pc, tag,
9797ce00762Smsaitoh 			    off + PCIE_RCR);
9807ce00762Smsaitoh 
9817ce00762Smsaitoh 		if (__SHIFTOUT(xcap, PCIE_XCAP_VER_MASK) >= 2) {
9827ce00762Smsaitoh 			pcs->e_dcr2 = (uint16_t)pci_conf_read(pc, tag,
9837ce00762Smsaitoh 			    off + PCIE_DCSR2);
9847ce00762Smsaitoh 
9857ce00762Smsaitoh 			if (PCIE_HAS_LINKREGS(devtype))
9867ce00762Smsaitoh 				pcs->e_lcr2 = (uint16_t)pci_conf_read(pc, tag,
9877ce00762Smsaitoh 			    off + PCIE_LCSR2);
9887ce00762Smsaitoh 
9897ce00762Smsaitoh 			/* XXX PCIE_SLCSR2 (It's reserved by the PCIe spec) */
9907ce00762Smsaitoh 		}
9917ce00762Smsaitoh 	}
9927ce00762Smsaitoh 
9937ce00762Smsaitoh 	/* For MSI */
9947ce00762Smsaitoh 	if (pci_get_capability(pc, tag, PCI_CAP_MSI, &off, NULL) != 0) {
9957ce00762Smsaitoh 		bool bit64, pvmask;
9967ce00762Smsaitoh 
9977ce00762Smsaitoh 		pcs->msi_ctl = pci_conf_read(pc, tag, off + PCI_MSI_CTL);
9987ce00762Smsaitoh 
9997ce00762Smsaitoh 		bit64 = pcs->msi_ctl & PCI_MSI_CTL_64BIT_ADDR;
10007ce00762Smsaitoh 		pvmask = pcs->msi_ctl & PCI_MSI_CTL_PERVEC_MASK;
10017ce00762Smsaitoh 
10027ce00762Smsaitoh 		/* Address */
10037ce00762Smsaitoh 		pcs->msi_maddr = pci_conf_read(pc, tag, off + PCI_MSI_MADDR);
10047ce00762Smsaitoh 		if (bit64)
10057ce00762Smsaitoh 			pcs->msi_maddr64_hi = pci_conf_read(pc, tag,
10067ce00762Smsaitoh 			    off + PCI_MSI_MADDR64_HI);
10077ce00762Smsaitoh 
10087ce00762Smsaitoh 		/* Data */
10097ce00762Smsaitoh 		pcs->msi_mdata = pci_conf_read(pc, tag,
10107ce00762Smsaitoh 		    off + (bit64 ? PCI_MSI_MDATA64 : PCI_MSI_MDATA));
10117ce00762Smsaitoh 
10127ce00762Smsaitoh 		/* Per-vector masking */
10137ce00762Smsaitoh 		if (pvmask)
10147ce00762Smsaitoh 			pcs->msi_mask = pci_conf_read(pc, tag,
10157ce00762Smsaitoh 			    off + (bit64 ? PCI_MSI_MASK64 : PCI_MSI_MASK));
10167ce00762Smsaitoh 	}
10177ce00762Smsaitoh 
10187ce00762Smsaitoh 	/* For MSI-X */
10197ce00762Smsaitoh 	if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL) != 0)
10207ce00762Smsaitoh 		pcs->msix_ctl = pci_conf_read(pc, tag, off + PCI_MSIX_CTL);
10219c40186bSjmcneill }
10229c40186bSjmcneill 
10239c40186bSjmcneill void
10249c40186bSjmcneill pci_conf_restore(pci_chipset_tag_t pc, pcitag_t tag,
10259c40186bSjmcneill 		  struct pci_conf_state *pcs)
10269c40186bSjmcneill {
10279c40186bSjmcneill 	int off;
10284c1d81b2Sjmcneill 	pcireg_t val;
10299c40186bSjmcneill 
10304c1d81b2Sjmcneill 	for (off = 15; off >= 0; off--) {
10314c1d81b2Sjmcneill 		val = pci_conf_read(pc, tag, (off * 4));
10324c1d81b2Sjmcneill 		if (val != pcs->reg[off])
10339c40186bSjmcneill 			pci_conf_write(pc, tag, (off * 4), pcs->reg[off]);
10344c1d81b2Sjmcneill 	}
10359c40186bSjmcneill 
10367ce00762Smsaitoh 	/* For PCI-X */
10377ce00762Smsaitoh 	if (pci_get_capability(pc, tag, PCI_CAP_PCIX, &off, NULL) != 0)
10387ce00762Smsaitoh 		pci_conf_write(pc, tag, off + PCIX_CMD, pcs->x_csr);
10397ce00762Smsaitoh 
10407ce00762Smsaitoh 	/* For PCIe */
10417ce00762Smsaitoh 	if (pci_get_capability(pc, tag, PCI_CAP_PCIEXPRESS, &off, NULL) != 0) {
10427ce00762Smsaitoh 		pcireg_t xcap = pci_conf_read(pc, tag, off + PCIE_XCAP);
10437ce00762Smsaitoh 		unsigned int devtype;
10447ce00762Smsaitoh 
10457ce00762Smsaitoh 		devtype = PCIE_XCAP_TYPE(xcap);
10467ce00762Smsaitoh 		pci_conf_write(pc, tag, off + PCIE_DCSR, pcs->e_dcr);
10477ce00762Smsaitoh 
10487ce00762Smsaitoh 		/*
10497ce00762Smsaitoh 		 * PCIe capability is variable sized. To not to write the next
10507ce00762Smsaitoh 		 * area, check the existence of each register.
10517ce00762Smsaitoh 		 */
10527ce00762Smsaitoh 		if (PCIE_HAS_LINKREGS(devtype))
10537ce00762Smsaitoh 			pci_conf_write(pc, tag, off + PCIE_LCSR, pcs->e_lcr);
10547ce00762Smsaitoh 
10557ce00762Smsaitoh 		if ((xcap & PCIE_XCAP_SI) != 0)
10567ce00762Smsaitoh 			pci_conf_write(pc, tag, off + PCIE_SLCSR, pcs->e_slcr);
10577ce00762Smsaitoh 
10587ce00762Smsaitoh 		if (PCIE_HAS_ROOTREGS(devtype))
10597ce00762Smsaitoh 			pci_conf_write(pc, tag, off + PCIE_RCR, pcs->e_rcr);
10607ce00762Smsaitoh 
10617ce00762Smsaitoh 		if (__SHIFTOUT(xcap, PCIE_XCAP_VER_MASK) >= 2) {
10627ce00762Smsaitoh 			pci_conf_write(pc, tag, off + PCIE_DCSR2, pcs->e_dcr2);
10637ce00762Smsaitoh 
10647ce00762Smsaitoh 			if (PCIE_HAS_LINKREGS(devtype))
10657ce00762Smsaitoh 				pci_conf_write(pc, tag, off + PCIE_LCSR2,
10667ce00762Smsaitoh 				    pcs->e_lcr2);
10677ce00762Smsaitoh 
10687ce00762Smsaitoh 			/* XXX PCIE_SLCSR2 (It's reserved by the PCIe spec) */
10697ce00762Smsaitoh 		}
10707ce00762Smsaitoh 	}
10717ce00762Smsaitoh 
10727ce00762Smsaitoh 	/* For MSI */
10737ce00762Smsaitoh 	if (pci_get_capability(pc, tag, PCI_CAP_MSI, &off, NULL) != 0) {
10747ce00762Smsaitoh 		pcireg_t reg;
10757ce00762Smsaitoh 		bool bit64, pvmask;
10767ce00762Smsaitoh 
10777ce00762Smsaitoh 		/* First, drop Enable bit in case it's already set. */
10787ce00762Smsaitoh 		reg = pci_conf_read(pc, tag, off + PCI_MSI_CTL);
10797ce00762Smsaitoh 		pci_conf_write(pc, tag, off + PCI_MSI_CTL,
10807ce00762Smsaitoh 		    reg & ~PCI_MSI_CTL_MSI_ENABLE);
10817ce00762Smsaitoh 
10827ce00762Smsaitoh 		bit64 = pcs->msi_ctl & PCI_MSI_CTL_64BIT_ADDR;
10837ce00762Smsaitoh 		pvmask = pcs->msi_ctl & PCI_MSI_CTL_PERVEC_MASK;
10847ce00762Smsaitoh 
10857ce00762Smsaitoh 		/* Address */
10867ce00762Smsaitoh 		pci_conf_write(pc, tag, off + PCI_MSI_MADDR, pcs->msi_maddr);
10877ce00762Smsaitoh 
10887ce00762Smsaitoh 		if (bit64)
10897ce00762Smsaitoh 			pci_conf_write(pc, tag,
10907ce00762Smsaitoh 			    off + PCI_MSI_MADDR64_HI, pcs->msi_maddr64_hi);
10917ce00762Smsaitoh 
10927ce00762Smsaitoh 		/* Data */
10937ce00762Smsaitoh 		pci_conf_write(pc, tag,
10947ce00762Smsaitoh 		    off + (bit64 ? PCI_MSI_MDATA64 : PCI_MSI_MDATA),
10957ce00762Smsaitoh 		    pcs->msi_mdata);
10967ce00762Smsaitoh 
10977ce00762Smsaitoh 		/* Per-vector masking */
10987ce00762Smsaitoh 		if (pvmask)
10997ce00762Smsaitoh 			pci_conf_write(pc, tag,
11007ce00762Smsaitoh 			    off + (bit64 ? PCI_MSI_MASK64 : PCI_MSI_MASK),
11017ce00762Smsaitoh 			    pcs->msi_mask);
11027ce00762Smsaitoh 
11037ce00762Smsaitoh 		/* Write CTRL register in the end */
11047ce00762Smsaitoh 		pci_conf_write(pc, tag, off + PCI_MSI_CTL, pcs->msi_ctl);
11057ce00762Smsaitoh 	}
11067ce00762Smsaitoh 
11077ce00762Smsaitoh 	/* For MSI-X */
11087ce00762Smsaitoh 	if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL) != 0)
11097ce00762Smsaitoh 		pci_conf_write(pc, tag, off + PCI_MSIX_CTL, pcs->msix_ctl);
11109c40186bSjmcneill }
1111d36c43c5Sthorpej 
111263bbcb49Schristos /*
111363bbcb49Schristos  * Power Management Capability (Rev 2.2)
111463bbcb49Schristos  */
11154c1d81b2Sjmcneill static int
11164c1d81b2Sjmcneill pci_get_powerstate_int(pci_chipset_tag_t pc, pcitag_t tag , pcireg_t *state,
11174c1d81b2Sjmcneill     int offset)
111863bbcb49Schristos {
11194c1d81b2Sjmcneill 	pcireg_t value, now;
112063bbcb49Schristos 
112163bbcb49Schristos 	value = pci_conf_read(pc, tag, offset + PCI_PMCSR);
112263bbcb49Schristos 	now = value & PCI_PMCSR_STATE_MASK;
112363bbcb49Schristos 	switch (now) {
112463bbcb49Schristos 	case PCI_PMCSR_STATE_D0:
112563bbcb49Schristos 	case PCI_PMCSR_STATE_D1:
112663bbcb49Schristos 	case PCI_PMCSR_STATE_D2:
112763bbcb49Schristos 	case PCI_PMCSR_STATE_D3:
112863bbcb49Schristos 		*state = now;
112963bbcb49Schristos 		return 0;
113063bbcb49Schristos 	default:
113163bbcb49Schristos 		return EINVAL;
113263bbcb49Schristos 	}
113363bbcb49Schristos }
113463bbcb49Schristos 
113563bbcb49Schristos int
11364c1d81b2Sjmcneill pci_get_powerstate(pci_chipset_tag_t pc, pcitag_t tag , pcireg_t *state)
113763bbcb49Schristos {
113863bbcb49Schristos 	int offset;
11394c1d81b2Sjmcneill 	pcireg_t value;
114063bbcb49Schristos 
114163bbcb49Schristos 	if (!pci_get_capability(pc, tag, PCI_CAP_PWRMGMT, &offset, &value))
114263bbcb49Schristos 		return EOPNOTSUPP;
114363bbcb49Schristos 
11444c1d81b2Sjmcneill 	return pci_get_powerstate_int(pc, tag, state, offset);
11454c1d81b2Sjmcneill }
11464c1d81b2Sjmcneill 
11474c1d81b2Sjmcneill static int
11484c1d81b2Sjmcneill pci_set_powerstate_int(pci_chipset_tag_t pc, pcitag_t tag, pcireg_t state,
11494c1d81b2Sjmcneill     int offset, pcireg_t cap_reg)
11504c1d81b2Sjmcneill {
11514c1d81b2Sjmcneill 	pcireg_t value, cap, now;
11524c1d81b2Sjmcneill 
11534c1d81b2Sjmcneill 	cap = cap_reg >> PCI_PMCR_SHIFT;
115463bbcb49Schristos 	value = pci_conf_read(pc, tag, offset + PCI_PMCSR);
115563bbcb49Schristos 	now = value & PCI_PMCSR_STATE_MASK;
115663bbcb49Schristos 	value &= ~PCI_PMCSR_STATE_MASK;
115763bbcb49Schristos 
115863bbcb49Schristos 	if (now == state)
115963bbcb49Schristos 		return 0;
116063bbcb49Schristos 	switch (state) {
116163bbcb49Schristos 	case PCI_PMCSR_STATE_D0:
116263bbcb49Schristos 		break;
116363bbcb49Schristos 	case PCI_PMCSR_STATE_D1:
11644c1d81b2Sjmcneill 		if (now == PCI_PMCSR_STATE_D2 || now == PCI_PMCSR_STATE_D3) {
11654c1d81b2Sjmcneill 			printf("invalid transition from %d to D1\n", (int)now);
116663bbcb49Schristos 			return EINVAL;
11674c1d81b2Sjmcneill 		}
11684c1d81b2Sjmcneill 		if (!(cap & PCI_PMCR_D1SUPP)) {
11694c1d81b2Sjmcneill 			printf("D1 not supported\n");
117063bbcb49Schristos 			return EOPNOTSUPP;
11714c1d81b2Sjmcneill 		}
117263bbcb49Schristos 		break;
117363bbcb49Schristos 	case PCI_PMCSR_STATE_D2:
11744c1d81b2Sjmcneill 		if (now == PCI_PMCSR_STATE_D3) {
11754c1d81b2Sjmcneill 			printf("invalid transition from %d to D2\n", (int)now);
117663bbcb49Schristos 			return EINVAL;
11774c1d81b2Sjmcneill 		}
11784c1d81b2Sjmcneill 		if (!(cap & PCI_PMCR_D2SUPP)) {
11794c1d81b2Sjmcneill 			printf("D2 not supported\n");
118063bbcb49Schristos 			return EOPNOTSUPP;
11814c1d81b2Sjmcneill 		}
118263bbcb49Schristos 		break;
118363bbcb49Schristos 	case PCI_PMCSR_STATE_D3:
118463bbcb49Schristos 		break;
118563bbcb49Schristos 	default:
118663bbcb49Schristos 		return EINVAL;
118763bbcb49Schristos 	}
118886fcf72fSdyoung 	value |= state;
118963bbcb49Schristos 	pci_conf_write(pc, tag, offset + PCI_PMCSR, value);
1190efa71badSdrochner 	/* delay according to pcipm1.2, ch. 5.6.1 */
119186fcf72fSdyoung 	if (state == PCI_PMCSR_STATE_D3 || now == PCI_PMCSR_STATE_D3)
1192ab245e29Sjmcneill 		DELAY(10000);
119386fcf72fSdyoung 	else if (state == PCI_PMCSR_STATE_D2 || now == PCI_PMCSR_STATE_D2)
1194ab245e29Sjmcneill 		DELAY(200);
1195ab245e29Sjmcneill 
119663bbcb49Schristos 	return 0;
119763bbcb49Schristos }
119863bbcb49Schristos 
119963bbcb49Schristos int
12004c1d81b2Sjmcneill pci_set_powerstate(pci_chipset_tag_t pc, pcitag_t tag, pcireg_t state)
12014c1d81b2Sjmcneill {
12024c1d81b2Sjmcneill 	int offset;
12034c1d81b2Sjmcneill 	pcireg_t value;
12044c1d81b2Sjmcneill 
12054c1d81b2Sjmcneill 	if (!pci_get_capability(pc, tag, PCI_CAP_PWRMGMT, &offset, &value)) {
12064c1d81b2Sjmcneill 		printf("pci_set_powerstate not supported\n");
12074c1d81b2Sjmcneill 		return EOPNOTSUPP;
12084c1d81b2Sjmcneill 	}
12094c1d81b2Sjmcneill 
12104c1d81b2Sjmcneill 	return pci_set_powerstate_int(pc, tag, state, offset, value);
12114c1d81b2Sjmcneill }
12124c1d81b2Sjmcneill 
12134c1d81b2Sjmcneill int
12148ce2158bSdyoung pci_activate(pci_chipset_tag_t pc, pcitag_t tag, device_t dev,
12158ce2158bSdyoung     int (*wakefun)(pci_chipset_tag_t, pcitag_t, device_t, pcireg_t))
121663bbcb49Schristos {
121763bbcb49Schristos 	pcireg_t pmode;
121863bbcb49Schristos 	int error;
121963bbcb49Schristos 
122063bbcb49Schristos 	if ((error = pci_get_powerstate(pc, tag, &pmode)))
122163bbcb49Schristos 		return error;
122263bbcb49Schristos 
122363bbcb49Schristos 	switch (pmode) {
122463bbcb49Schristos 	case PCI_PMCSR_STATE_D0:
122563bbcb49Schristos 		break;
122663bbcb49Schristos 	case PCI_PMCSR_STATE_D3:
122763bbcb49Schristos 		if (wakefun == NULL) {
122863bbcb49Schristos 			/*
122963bbcb49Schristos 			 * The card has lost all configuration data in
123063bbcb49Schristos 			 * this state, so punt.
123163bbcb49Schristos 			 */
12328ce2158bSdyoung 			aprint_error_dev(dev,
12338ce2158bSdyoung 			    "unable to wake up from power state D3\n");
123463bbcb49Schristos 			return EOPNOTSUPP;
123563bbcb49Schristos 		}
123663bbcb49Schristos 		/*FALLTHROUGH*/
123763bbcb49Schristos 	default:
123863bbcb49Schristos 		if (wakefun) {
12398ce2158bSdyoung 			error = (*wakefun)(pc, tag, dev, pmode);
124063bbcb49Schristos 			if (error)
124163bbcb49Schristos 				return error;
124263bbcb49Schristos 		}
12438ce2158bSdyoung 		aprint_normal_dev(dev, "waking up from power state D%d\n",
12448ce2158bSdyoung 		    pmode);
124563bbcb49Schristos 		if ((error = pci_set_powerstate(pc, tag, PCI_PMCSR_STATE_D0)))
124663bbcb49Schristos 			return error;
124763bbcb49Schristos 	}
124863bbcb49Schristos 	return 0;
124963bbcb49Schristos }
125063bbcb49Schristos 
125163bbcb49Schristos int
1252168cd830Schristos pci_activate_null(pci_chipset_tag_t pc, pcitag_t tag,
12538ce2158bSdyoung     device_t dev, pcireg_t state)
125463bbcb49Schristos {
125563bbcb49Schristos 	return 0;
125663bbcb49Schristos }
125763bbcb49Schristos 
12584c1d81b2Sjmcneill struct pci_child_power {
12594c1d81b2Sjmcneill 	struct pci_conf_state p_pciconf;
12604c1d81b2Sjmcneill 	pci_chipset_tag_t p_pc;
12614c1d81b2Sjmcneill 	pcitag_t p_tag;
12624c1d81b2Sjmcneill 	bool p_has_pm;
12634c1d81b2Sjmcneill 	int p_pm_offset;
12644c1d81b2Sjmcneill 	pcireg_t p_pm_cap;
12654c1d81b2Sjmcneill 	pcireg_t p_class;
1266fd17b28cSdyoung 	pcireg_t p_csr;
12674c1d81b2Sjmcneill };
12684c1d81b2Sjmcneill 
12694c1d81b2Sjmcneill static bool
1270c1b390d4Sdyoung pci_child_suspend(device_t dv, const pmf_qual_t *qual)
12714c1d81b2Sjmcneill {
12724c1d81b2Sjmcneill 	struct pci_child_power *priv = device_pmf_bus_private(dv);
1273efa71badSdrochner 	pcireg_t ocsr, csr;
12744c1d81b2Sjmcneill 
12754c1d81b2Sjmcneill 	pci_conf_capture(priv->p_pc, priv->p_tag, &priv->p_pciconf);
12764c1d81b2Sjmcneill 
1277efa71badSdrochner 	if (!priv->p_has_pm)
1278efa71badSdrochner 		return true; /* ??? hopefully handled by ACPI */
1279efa71badSdrochner 	if (PCI_CLASS(priv->p_class) == PCI_CLASS_DISPLAY)
1280efa71badSdrochner 		return true; /* XXX */
1281efa71badSdrochner 
1282efa71badSdrochner 	/* disable decoding and busmastering, see pcipm1.2 ch. 8.2.1 */
1283efa71badSdrochner 	ocsr = pci_conf_read(priv->p_pc, priv->p_tag, PCI_COMMAND_STATUS_REG);
1284efa71badSdrochner 	csr = ocsr & ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE
1285efa71badSdrochner 		       | PCI_COMMAND_MASTER_ENABLE);
1286efa71badSdrochner 	pci_conf_write(priv->p_pc, priv->p_tag, PCI_COMMAND_STATUS_REG, csr);
1287efa71badSdrochner 	if (pci_set_powerstate_int(priv->p_pc, priv->p_tag,
12884c1d81b2Sjmcneill 	    PCI_PMCSR_STATE_D3, priv->p_pm_offset, priv->p_pm_cap)) {
1289efa71badSdrochner 		pci_conf_write(priv->p_pc, priv->p_tag,
1290efa71badSdrochner 			       PCI_COMMAND_STATUS_REG, ocsr);
12914c1d81b2Sjmcneill 		aprint_error_dev(dv, "unsupported state, continuing.\n");
12924c1d81b2Sjmcneill 		return false;
12934c1d81b2Sjmcneill 	}
12944c1d81b2Sjmcneill 	return true;
12954c1d81b2Sjmcneill }
12964c1d81b2Sjmcneill 
129794d88f2cSmsaitoh static void
129894d88f2cSmsaitoh pci_pme_check_and_clear(device_t dv, pci_chipset_tag_t pc, pcitag_t tag,
129994d88f2cSmsaitoh     int off)
130094d88f2cSmsaitoh {
130194d88f2cSmsaitoh 	pcireg_t pmcsr;
130294d88f2cSmsaitoh 
130394d88f2cSmsaitoh 	pmcsr = pci_conf_read(pc, tag, off + PCI_PMCSR);
130494d88f2cSmsaitoh 
130594d88f2cSmsaitoh 	if (pmcsr & PCI_PMCSR_PME_STS) {
130694d88f2cSmsaitoh 		/* Clear W1C bit */
130794d88f2cSmsaitoh 		pmcsr |= PCI_PMCSR_PME_STS;
130894d88f2cSmsaitoh 		pci_conf_write(pc, tag, off + PCI_PMCSR, pmcsr);
130994d88f2cSmsaitoh 		aprint_verbose_dev(dv, "Clear PME# now\n");
131094d88f2cSmsaitoh 	}
131194d88f2cSmsaitoh }
131294d88f2cSmsaitoh 
13134c1d81b2Sjmcneill static bool
1314c1b390d4Sdyoung pci_child_resume(device_t dv, const pmf_qual_t *qual)
13154c1d81b2Sjmcneill {
13164c1d81b2Sjmcneill 	struct pci_child_power *priv = device_pmf_bus_private(dv);
13174c1d81b2Sjmcneill 
131894d88f2cSmsaitoh 	if (priv->p_has_pm) {
131994d88f2cSmsaitoh 		if (pci_set_powerstate_int(priv->p_pc, priv->p_tag,
13204c1d81b2Sjmcneill 		    PCI_PMCSR_STATE_D0, priv->p_pm_offset, priv->p_pm_cap)) {
132194d88f2cSmsaitoh 			aprint_error_dev(dv,
132294d88f2cSmsaitoh 			    "unsupported state, continuing.\n");
13234c1d81b2Sjmcneill 			return false;
13244c1d81b2Sjmcneill 		}
132594d88f2cSmsaitoh 		pci_pme_check_and_clear(dv, priv->p_pc, priv->p_tag,
132694d88f2cSmsaitoh 		    priv->p_pm_offset);
132794d88f2cSmsaitoh 	}
13284c1d81b2Sjmcneill 
13294c1d81b2Sjmcneill 	pci_conf_restore(priv->p_pc, priv->p_tag, &priv->p_pciconf);
13304c1d81b2Sjmcneill 
13314c1d81b2Sjmcneill 	return true;
13324c1d81b2Sjmcneill }
13334c1d81b2Sjmcneill 
13340e748e63Sdrochner static bool
13350e748e63Sdrochner pci_child_shutdown(device_t dv, int how)
13360e748e63Sdrochner {
13370e748e63Sdrochner 	struct pci_child_power *priv = device_pmf_bus_private(dv);
13380e748e63Sdrochner 	pcireg_t csr;
13390e748e63Sdrochner 
1340fd17b28cSdyoung 	/* restore original bus-mastering state */
13410e748e63Sdrochner 	csr = pci_conf_read(priv->p_pc, priv->p_tag, PCI_COMMAND_STATUS_REG);
13420e748e63Sdrochner 	csr &= ~PCI_COMMAND_MASTER_ENABLE;
1343fd17b28cSdyoung 	csr |= priv->p_csr & PCI_COMMAND_MASTER_ENABLE;
13440e748e63Sdrochner 	pci_conf_write(priv->p_pc, priv->p_tag, PCI_COMMAND_STATUS_REG, csr);
13450e748e63Sdrochner 	return true;
13460e748e63Sdrochner }
13470e748e63Sdrochner 
13484c1d81b2Sjmcneill static void
13494c1d81b2Sjmcneill pci_child_deregister(device_t dv)
13504c1d81b2Sjmcneill {
13514c1d81b2Sjmcneill 	struct pci_child_power *priv = device_pmf_bus_private(dv);
13524c1d81b2Sjmcneill 
13534c1d81b2Sjmcneill 	free(priv, M_DEVBUF);
13544c1d81b2Sjmcneill }
13554c1d81b2Sjmcneill 
135631786995Sriastradh static void
13574c1d81b2Sjmcneill pci_child_register(device_t child)
13584c1d81b2Sjmcneill {
13594c1d81b2Sjmcneill 	device_t self = device_parent(child);
13604c1d81b2Sjmcneill 	struct pci_softc *sc = device_private(self);
13614c1d81b2Sjmcneill 	struct pci_child_power *priv;
13624c1d81b2Sjmcneill 	int device, function, off;
13634c1d81b2Sjmcneill 	pcireg_t reg;
13644c1d81b2Sjmcneill 
13654c1d81b2Sjmcneill 	priv = malloc(sizeof(*priv), M_DEVBUF, M_WAITOK);
13664c1d81b2Sjmcneill 
13674c1d81b2Sjmcneill 	device = device_locator(child, PCICF_DEV);
13684c1d81b2Sjmcneill 	function = device_locator(child, PCICF_FUNCTION);
13694c1d81b2Sjmcneill 
13704c1d81b2Sjmcneill 	priv->p_pc = sc->sc_pc;
13714c1d81b2Sjmcneill 	priv->p_tag = pci_make_tag(priv->p_pc, sc->sc_bus, device,
13724c1d81b2Sjmcneill 	    function);
13734c1d81b2Sjmcneill 	priv->p_class = pci_conf_read(priv->p_pc, priv->p_tag, PCI_CLASS_REG);
1374fd17b28cSdyoung 	priv->p_csr = pci_conf_read(priv->p_pc, priv->p_tag,
1375fd17b28cSdyoung 	    PCI_COMMAND_STATUS_REG);
13764c1d81b2Sjmcneill 
13774c1d81b2Sjmcneill 	if (pci_get_capability(priv->p_pc, priv->p_tag,
13784c1d81b2Sjmcneill 			       PCI_CAP_PWRMGMT, &off, &reg)) {
13794c1d81b2Sjmcneill 		priv->p_has_pm = true;
13804c1d81b2Sjmcneill 		priv->p_pm_offset = off;
13814c1d81b2Sjmcneill 		priv->p_pm_cap = reg;
138294d88f2cSmsaitoh 		pci_pme_check_and_clear(child, priv->p_pc, priv->p_tag, off);
13834c1d81b2Sjmcneill 	} else {
13844c1d81b2Sjmcneill 		priv->p_has_pm = false;
13854c1d81b2Sjmcneill 		priv->p_pm_offset = -1;
13864c1d81b2Sjmcneill 	}
13874c1d81b2Sjmcneill 
13884c1d81b2Sjmcneill 	device_pmf_bus_register(child, priv, pci_child_suspend,
13890e748e63Sdrochner 	    pci_child_resume, pci_child_shutdown, pci_child_deregister);
13904c1d81b2Sjmcneill }
13913076984fSjmcneill 
13923076984fSjmcneill MODULE(MODULE_CLASS_DRIVER, pci, NULL);
13933076984fSjmcneill 
13943076984fSjmcneill static int
13953076984fSjmcneill pci_modcmd(modcmd_t cmd, void *priv)
13963076984fSjmcneill {
13973076984fSjmcneill 	if (cmd == MODULE_CMD_INIT || cmd == MODULE_CMD_FINI)
13983076984fSjmcneill 		return 0;
13993076984fSjmcneill 	return ENOTTY;
14003076984fSjmcneill }
1401