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, ®) == 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, ®) == 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, ®)) {
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