2018-12-15 08:38:45 +03:00
|
|
|
/* $NetBSD: pci.c,v 1.154 2018/12/15 05:38:45 msaitoh Exp $ */
|
1996-02-28 04:44:41 +03:00
|
|
|
|
|
|
|
/*
|
1998-05-31 10:05:28 +04:00
|
|
|
* Copyright (c) 1995, 1996, 1997, 1998
|
1997-04-11 03:12:16 +04:00
|
|
|
* Christopher G. Demetriou. All rights reserved.
|
1998-08-15 07:02:31 +04:00
|
|
|
* Copyright (c) 1994 Charles M. Hannum. All rights reserved.
|
1996-02-28 04:44:41 +03:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
1998-08-15 07:02:31 +04:00
|
|
|
* This product includes software developed by Charles M. Hannum.
|
1996-02-28 04:44:41 +03:00
|
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PCI bus autoconfiguration.
|
|
|
|
*/
|
|
|
|
|
2001-11-13 10:48:40 +03:00
|
|
|
#include <sys/cdefs.h>
|
2018-12-15 08:38:45 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: pci.c,v 1.154 2018/12/15 05:38:45 msaitoh Exp $");
|
2001-11-13 10:48:40 +03:00
|
|
|
|
2015-08-25 02:55:04 +03:00
|
|
|
#ifdef _KERNEL_OPT
|
2000-03-22 03:47:26 +03:00
|
|
|
#include "opt_pci.h"
|
2015-08-25 02:55:04 +03:00
|
|
|
#endif
|
2000-03-22 03:47:26 +03:00
|
|
|
|
1996-02-28 04:44:41 +03:00
|
|
|
#include <sys/param.h>
|
2007-12-09 23:27:42 +03:00
|
|
|
#include <sys/malloc.h>
|
1996-02-28 04:44:41 +03:00
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/device.h>
|
2011-08-29 18:47:07 +04:00
|
|
|
#include <sys/module.h>
|
1996-02-28 04:44:41 +03:00
|
|
|
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
|
|
#include <dev/pci/pcivar.h>
|
1998-03-28 05:24:04 +03:00
|
|
|
#include <dev/pci/pcidevs.h>
|
2017-04-05 07:04:54 +03:00
|
|
|
#include <dev/pci/ppbvar.h>
|
1996-02-28 04:44:41 +03:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
#include <net/if.h>
|
|
|
|
|
2003-03-19 20:56:58 +03:00
|
|
|
#include "locators.h"
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
static bool pci_child_register(device_t);
|
|
|
|
|
2000-03-22 03:47:26 +03:00
|
|
|
#ifdef PCI_CONFIG_DUMP
|
|
|
|
int pci_config_dump = 1;
|
|
|
|
#else
|
|
|
|
int pci_config_dump = 0;
|
|
|
|
#endif
|
|
|
|
|
2005-02-04 05:10:35 +03:00
|
|
|
int pciprint(void *, const char *);
|
1996-02-28 04:44:41 +03:00
|
|
|
|
2004-07-29 20:51:01 +04:00
|
|
|
#ifdef PCI_MACHDEP_ENUMERATE_BUS
|
|
|
|
#define pci_enumerate_bus PCI_MACHDEP_ENUMERATE_BUS
|
|
|
|
#endif
|
|
|
|
|
1996-11-24 00:58:16 +03:00
|
|
|
/*
|
1998-06-09 22:48:41 +04:00
|
|
|
* Important note about PCI-ISA bridges:
|
|
|
|
*
|
|
|
|
* Callbacks are used to configure these devices so that ISA/EISA bridges
|
|
|
|
* can attach their child busses after PCI configuration is done.
|
1996-11-24 00:58:16 +03:00
|
|
|
*
|
|
|
|
* This works because:
|
|
|
|
* (1) there can be at most one ISA/EISA bridge per PCI bus, and
|
|
|
|
* (2) any ISA/EISA bridges must be attached to primary PCI
|
|
|
|
* busses (i.e. bus zero).
|
|
|
|
*
|
|
|
|
* That boils down to: there can only be one of these outstanding
|
|
|
|
* at a time, it is cleared when configuring PCI bus 0 before any
|
|
|
|
* subdevices have been found, and it is run after all subdevices
|
|
|
|
* of PCI bus 0 have been found.
|
|
|
|
*
|
|
|
|
* This is needed because there are some (legacy) PCI devices which
|
|
|
|
* can show up as ISA/EISA devices as well (the prime example of which
|
|
|
|
* are VGA controllers). If you attach ISA from a PCI-ISA/EISA bridge,
|
|
|
|
* and the bridge is seen before the video board is, the board can show
|
|
|
|
* up as an ISA device, and that can (bogusly) complicate the PCI device's
|
|
|
|
* attach code, or make the PCI device not be properly attached at all.
|
1998-06-09 22:48:41 +04:00
|
|
|
*
|
|
|
|
* We use the generic config_defer() facility to achieve this.
|
1996-11-24 00:58:16 +03:00
|
|
|
*/
|
|
|
|
|
2008-04-09 21:01:53 +04:00
|
|
|
int
|
2008-03-21 10:47:43 +03:00
|
|
|
pcirescan(device_t self, const char *ifattr, const int *locators)
|
2005-06-28 04:28:41 +04:00
|
|
|
{
|
2008-03-21 10:47:43 +03:00
|
|
|
struct pci_softc *sc = device_private(self);
|
2005-06-28 04:28:41 +04:00
|
|
|
|
|
|
|
KASSERT(ifattr && !strcmp(ifattr, "pci"));
|
|
|
|
KASSERT(locators);
|
|
|
|
|
2008-03-21 10:47:43 +03:00
|
|
|
pci_enumerate_bus(sc, locators, NULL, NULL);
|
2010-05-25 00:29:40 +04:00
|
|
|
|
2008-03-21 10:47:43 +03:00
|
|
|
return 0;
|
2005-06-28 04:28:41 +04:00
|
|
|
}
|
|
|
|
|
2008-04-09 21:01:53 +04:00
|
|
|
int
|
2008-03-23 19:40:12 +03:00
|
|
|
pcimatch(device_t parent, cfdata_t cf, void *aux)
|
1996-02-28 04:44:41 +03:00
|
|
|
{
|
|
|
|
struct pcibus_attach_args *pba = aux;
|
|
|
|
|
|
|
|
/* Check the locators */
|
2004-09-13 16:22:52 +04:00
|
|
|
if (cf->cf_loc[PCIBUSCF_BUS] != PCIBUSCF_BUS_DEFAULT &&
|
|
|
|
cf->cf_loc[PCIBUSCF_BUS] != pba->pba_bus)
|
2009-07-05 01:01:10 +04:00
|
|
|
return 0;
|
1996-02-28 04:44:41 +03:00
|
|
|
|
|
|
|
/* sanity */
|
|
|
|
if (pba->pba_bus < 0 || pba->pba_bus > 255)
|
2009-07-05 01:01:10 +04:00
|
|
|
return 0;
|
1996-02-28 04:44:41 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX check other (hardware?) indicators
|
|
|
|
*/
|
|
|
|
|
2009-07-05 01:01:10 +04:00
|
|
|
return 1;
|
1996-02-28 04:44:41 +03:00
|
|
|
}
|
|
|
|
|
2008-04-09 21:01:53 +04:00
|
|
|
void
|
2008-03-21 10:47:43 +03:00
|
|
|
pciattach(device_t parent, device_t self, void *aux)
|
1998-04-17 22:40:31 +04:00
|
|
|
{
|
|
|
|
struct pcibus_attach_args *pba = aux;
|
2008-03-21 10:47:43 +03:00
|
|
|
struct pci_softc *sc = device_private(self);
|
1999-11-04 04:03:34 +03:00
|
|
|
int io_enabled, mem_enabled, mrl_enabled, mrm_enabled, mwi_enabled;
|
|
|
|
const char *sep = "";
|
2005-08-26 15:01:42 +04:00
|
|
|
static const int wildcard[PCICF_NLOCS] = {
|
|
|
|
PCICF_DEV_DEFAULT, PCICF_FUNCTION_DEFAULT
|
|
|
|
};
|
1998-04-17 22:40:31 +04:00
|
|
|
|
2008-03-23 19:40:12 +03:00
|
|
|
sc->sc_dev = self;
|
|
|
|
|
1998-04-17 22:40:31 +04:00
|
|
|
pci_attach_hook(parent, self, pba);
|
2003-04-29 05:07:30 +04:00
|
|
|
|
|
|
|
aprint_naive("\n");
|
|
|
|
aprint_normal("\n");
|
1998-04-17 22:40:31 +04:00
|
|
|
|
2011-05-17 21:34:47 +04:00
|
|
|
io_enabled = (pba->pba_flags & PCI_FLAGS_IO_OKAY);
|
|
|
|
mem_enabled = (pba->pba_flags & PCI_FLAGS_MEM_OKAY);
|
1999-11-04 04:03:34 +03:00
|
|
|
mrl_enabled = (pba->pba_flags & PCI_FLAGS_MRL_OKAY);
|
|
|
|
mrm_enabled = (pba->pba_flags & PCI_FLAGS_MRM_OKAY);
|
|
|
|
mwi_enabled = (pba->pba_flags & PCI_FLAGS_MWI_OKAY);
|
1998-04-17 22:40:31 +04:00
|
|
|
|
|
|
|
if (io_enabled == 0 && mem_enabled == 0) {
|
2008-03-21 10:47:43 +03:00
|
|
|
aprint_error_dev(self, "no spaces enabled!\n");
|
2007-12-09 23:27:42 +03:00
|
|
|
goto fail;
|
1998-04-17 22:40:31 +04:00
|
|
|
}
|
|
|
|
|
2003-04-29 05:07:30 +04:00
|
|
|
#define PRINT(str) \
|
|
|
|
do { \
|
2007-12-01 19:46:18 +03:00
|
|
|
aprint_verbose("%s%s", sep, str); \
|
2003-04-29 05:07:30 +04:00
|
|
|
sep = ", "; \
|
|
|
|
} while (/*CONSTCOND*/0)
|
1999-11-04 04:03:34 +03:00
|
|
|
|
2008-03-23 19:40:12 +03:00
|
|
|
aprint_verbose_dev(self, "");
|
1999-11-04 04:03:34 +03:00
|
|
|
|
1998-04-17 22:40:31 +04:00
|
|
|
if (io_enabled)
|
1999-11-04 04:03:34 +03:00
|
|
|
PRINT("i/o space");
|
|
|
|
if (mem_enabled)
|
|
|
|
PRINT("memory space");
|
2007-12-01 19:46:18 +03:00
|
|
|
aprint_verbose(" enabled");
|
1999-11-04 04:03:34 +03:00
|
|
|
|
|
|
|
if (mrl_enabled || mrm_enabled || mwi_enabled) {
|
|
|
|
if (mrl_enabled)
|
|
|
|
PRINT("rd/line");
|
|
|
|
if (mrm_enabled)
|
|
|
|
PRINT("rd/mult");
|
|
|
|
if (mwi_enabled)
|
|
|
|
PRINT("wr/inv");
|
2007-12-01 19:46:18 +03:00
|
|
|
aprint_verbose(" ok");
|
1998-04-17 22:40:31 +04:00
|
|
|
}
|
1999-11-04 04:03:34 +03:00
|
|
|
|
2007-12-01 19:46:18 +03:00
|
|
|
aprint_verbose("\n");
|
1998-04-17 22:40:31 +04:00
|
|
|
|
1999-11-04 04:03:34 +03:00
|
|
|
#undef PRINT
|
|
|
|
|
1998-04-17 22:40:31 +04:00
|
|
|
sc->sc_iot = pba->pba_iot;
|
|
|
|
sc->sc_memt = pba->pba_memt;
|
|
|
|
sc->sc_dmat = pba->pba_dmat;
|
2003-06-16 03:08:53 +04:00
|
|
|
sc->sc_dmat64 = pba->pba_dmat64;
|
1998-04-17 22:40:31 +04:00
|
|
|
sc->sc_pc = pba->pba_pc;
|
|
|
|
sc->sc_bus = pba->pba_bus;
|
2002-05-16 05:01:28 +04:00
|
|
|
sc->sc_bridgetag = pba->pba_bridgetag;
|
1998-04-17 22:40:31 +04:00
|
|
|
sc->sc_maxndevs = pci_bus_maxdevs(pba->pba_pc, pba->pba_bus);
|
|
|
|
sc->sc_intrswiz = pba->pba_intrswiz;
|
|
|
|
sc->sc_intrtag = pba->pba_intrtag;
|
|
|
|
sc->sc_flags = pba->pba_flags;
|
2006-09-26 03:09:42 +04:00
|
|
|
|
2008-03-23 19:40:12 +03:00
|
|
|
device_pmf_driver_set_child_register(sc->sc_dev, pci_child_register);
|
2006-09-26 03:09:42 +04:00
|
|
|
|
2008-03-23 19:40:12 +03:00
|
|
|
pcirescan(sc->sc_dev, "pci", wildcard);
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
fail:
|
|
|
|
if (!pmf_device_register(self, NULL, NULL))
|
|
|
|
aprint_error_dev(self, "couldn't establish power handler\n");
|
|
|
|
}
|
|
|
|
|
2008-04-09 21:01:53 +04:00
|
|
|
int
|
2008-03-21 10:47:43 +03:00
|
|
|
pcidetach(device_t self, int flags)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
2007-12-17 00:28:30 +03:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
if ((rc = config_detach_children(self, flags)) != 0)
|
|
|
|
return rc;
|
2007-12-09 23:27:42 +03:00
|
|
|
pmf_device_deregister(self);
|
|
|
|
return 0;
|
2004-08-18 03:20:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2005-06-28 04:28:41 +04:00
|
|
|
pciprint(void *aux, const char *pnp)
|
1996-02-28 04:44:41 +03:00
|
|
|
{
|
2000-03-30 16:41:09 +04:00
|
|
|
struct pci_attach_args *pa = aux;
|
1996-02-28 04:44:41 +03:00
|
|
|
char devinfo[256];
|
1998-05-31 10:05:28 +04:00
|
|
|
const struct pci_quirkdata *qd;
|
1996-02-28 04:44:41 +03:00
|
|
|
|
|
|
|
if (pnp) {
|
2004-04-24 01:13:05 +04:00
|
|
|
pci_devinfo(pa->pa_id, pa->pa_class, 1, devinfo, sizeof(devinfo));
|
2003-01-01 03:10:15 +03:00
|
|
|
aprint_normal("%s at %s", devinfo, pnp);
|
1996-02-28 04:44:41 +03:00
|
|
|
}
|
2003-01-01 03:10:15 +03:00
|
|
|
aprint_normal(" dev %d function %d", pa->pa_device, pa->pa_function);
|
2000-03-22 03:47:26 +03:00
|
|
|
if (pci_config_dump) {
|
|
|
|
printf(": ");
|
|
|
|
pci_conf_print(pa->pa_pc, pa->pa_tag, NULL);
|
|
|
|
if (!pnp)
|
2004-04-24 01:13:05 +04:00
|
|
|
pci_devinfo(pa->pa_id, pa->pa_class, 1, devinfo, sizeof(devinfo));
|
2000-03-22 03:47:26 +03:00
|
|
|
printf("%s at %s", devinfo, pnp ? pnp : "?");
|
|
|
|
printf(" dev %d function %d (", pa->pa_device, pa->pa_function);
|
1998-05-31 10:05:28 +04:00
|
|
|
#ifdef __i386__
|
2000-03-22 03:47:26 +03:00
|
|
|
printf("tag %#lx, intrtag %#lx, intrswiz %#lx, intrpin %#lx",
|
|
|
|
*(long *)&pa->pa_tag, *(long *)&pa->pa_intrtag,
|
|
|
|
(long)pa->pa_intrswiz, (long)pa->pa_intrpin);
|
1998-05-31 10:05:28 +04:00
|
|
|
#else
|
2001-07-11 12:36:46 +04:00
|
|
|
printf("intrswiz %#lx, intrpin %#lx",
|
|
|
|
(long)pa->pa_intrswiz, (long)pa->pa_intrpin);
|
1998-05-18 21:28:07 +04:00
|
|
|
#endif
|
2000-03-22 03:47:26 +03:00
|
|
|
printf(", i/o %s, mem %s,",
|
2011-05-17 21:34:47 +04:00
|
|
|
pa->pa_flags & PCI_FLAGS_IO_OKAY ? "on" : "off",
|
|
|
|
pa->pa_flags & PCI_FLAGS_MEM_OKAY ? "on" : "off");
|
2000-03-22 03:47:26 +03:00
|
|
|
qd = pci_lookup_quirkdata(PCI_VENDOR(pa->pa_id),
|
|
|
|
PCI_PRODUCT(pa->pa_id));
|
|
|
|
if (qd == NULL) {
|
|
|
|
printf(" no quirks");
|
|
|
|
} else {
|
2008-12-17 01:35:21 +03:00
|
|
|
snprintb(devinfo, sizeof (devinfo),
|
2003-08-18 09:39:07 +04:00
|
|
|
"\002\001multifn\002singlefn\003skipfunc0"
|
|
|
|
"\004skipfunc1\005skipfunc2\006skipfunc3"
|
|
|
|
"\007skipfunc4\010skipfunc5\011skipfunc6"
|
2008-12-17 01:35:21 +03:00
|
|
|
"\012skipfunc7", qd->quirks);
|
2000-03-22 03:47:26 +03:00
|
|
|
printf(" quirks %s", devinfo);
|
|
|
|
}
|
|
|
|
printf(")");
|
1998-05-31 10:05:28 +04:00
|
|
|
}
|
2009-07-05 01:01:10 +04:00
|
|
|
return UNCONF;
|
1996-02-28 04:44:41 +03:00
|
|
|
}
|
|
|
|
|
2002-05-15 22:13:00 +04:00
|
|
|
int
|
|
|
|
pci_probe_device(struct pci_softc *sc, pcitag_t tag,
|
Neither pci_dma64_available(), pci_probe_device(), pci_mapreg_map(9),
pci_find_rom(), pci_intr_map(9), pci_enumerate_bus(), nor the match
predicate passed to pciide_compat_intr_establish() should ever modify
their pci_attach_args argument, so make their pci_attach_args arguments
const and deal with the fallout throughout the kernel.
For the most part, these changes add a 'const' where there was no
'const' before, however, some drivers and MD code used to modify
pci_attach_args. Now those drivers either copy their pci_attach_args
and modify the copy, or refrain from modifying pci_attach_args:
Xen: according to Manuel Bouyer, writing to pci_attach_args in
pci_intr_map() was a leftover from Xen 2. Probably a bug. I
stopped writing it. I have not tested this change.
siside(4): sis_hostbr_match() needlessly wrote to pci_attach_args.
Probably a bug. I use a temporary variable. I have not tested this
change.
slide(4): sl82c105_chip_map() overwrote the caller's pci_attach_args.
Probably a bug. Use a local pci_attach_args. I have not tested
this change.
viaide(4): via_sata_chip_map() and via_sata_chip_map_new() overwrote the
caller's pci_attach_args. Probably a bug. Make a local copy of the
caller's pci_attach_args and modify the copy. I have not tested
this change.
While I'm here, make pci_mapreg_submap() static.
With these changes in place, I have tested the compilation of these
kernels:
alpha GENERIC
amd64 GENERIC XEN3_DOM0
arc GENERIC
atari HADES MILAN-PCIIDE
bebox GENERIC
cats GENERIC
cobalt GENERIC
evbarm-eb NSLU2
evbarm-el ADI_BRH ARMADILLO9 CP3100 GEMINI GEMINI_MASTER GEMINI_SLAVE GUMSTIX
HDL_G IMX31LITE INTEGRATOR IQ31244 IQ80310 IQ80321 IXDP425 IXM1200
KUROBOX_PRO LUBBOCK MARVELL_NAS NAPPI SHEEVAPLUG SMDK2800 TEAMASA_NPWR
TEAMASA_NPWR_FC TS7200 TWINTAIL ZAO425
evbmips-el AP30 DBAU1500 DBAU1550 MALTA MERAKI MTX-1 OMSAL400 RB153 WGT624V3
evbmips64-el XLSATX
evbppc EV64260 MPC8536DS MPC8548CDS OPENBLOCKS200 OPENBLOCKS266
OPENBLOCKS266_OPT P2020RDB PMPPC RB800 WALNUT
hp700 GENERIC
i386 ALL XEN3_DOM0 XEN3_DOMU
ibmnws GENERIC
macppc GENERIC
mvmeppc GENERIC
netwinder GENERIC
ofppc GENERIC
prep GENERIC
sandpoint GENERIC
sgimips GENERIC32_IP2x
sparc GENERIC_SUN4U KRUPS
sparc64 GENERIC
As of Sun Apr 3 15:26:26 CDT 2011, I could not compile these kernels
with or without my patches in place:
### evbmips-el GDIUM
nbmake: nbmake: don't know how to make /home/dyoung/pristine-nbsd/src/sys/arch/mips/mips/softintr.c. Stop
### evbarm-el MPCSA_GENERIC
src/sys/arch/evbarm/conf/MPCSA_GENERIC:318: ds1672rtc*: unknown device `ds1672rtc'
### ia64 GENERIC
/tmp/genassym.28085/assym.c: In function 'f111':
/tmp/genassym.28085/assym.c:67: error: invalid application of 'sizeof' to incomplete type 'struct pcb'
/tmp/genassym.28085/assym.c:76: error: dereferencing pointer to incomplete type
### sgimips GENERIC32_IP3x
crmfb.o: In function `crmfb_attach':
crmfb.c:(.text+0x2304): undefined reference to `ddc_read_edid'
crmfb.c:(.text+0x2304): relocation truncated to fit: R_MIPS_26 against `ddc_read_edid'
crmfb.c:(.text+0x234c): undefined reference to `edid_parse'
crmfb.c:(.text+0x234c): relocation truncated to fit: R_MIPS_26 against `edid_parse'
crmfb.c:(.text+0x2354): undefined reference to `edid_print'
crmfb.c:(.text+0x2354): relocation truncated to fit: R_MIPS_26 against `edid_print'
2011-04-05 00:37:49 +04:00
|
|
|
int (*match)(const struct pci_attach_args *),
|
|
|
|
struct pci_attach_args *pap)
|
2002-05-15 22:13:00 +04:00
|
|
|
{
|
|
|
|
pci_chipset_tag_t pc = sc->sc_pc;
|
|
|
|
struct pci_attach_args pa;
|
2014-09-05 09:29:16 +04:00
|
|
|
pcireg_t id, /* csr, */ pciclass, intr, bhlcr, bar, endbar;
|
2015-04-27 10:03:57 +03:00
|
|
|
#ifdef __HAVE_PCI_MSI_MSIX
|
|
|
|
pcireg_t cap;
|
|
|
|
int off;
|
|
|
|
#endif
|
2011-02-10 15:37:58 +03:00
|
|
|
int ret, pin, bus, device, function, i, width;
|
2005-08-25 22:35:38 +04:00
|
|
|
int locs[PCICF_NLOCS];
|
2002-05-15 22:13:00 +04:00
|
|
|
|
|
|
|
pci_decompose_tag(pc, tag, &bus, &device, &function);
|
|
|
|
|
2004-08-18 03:20:10 +04:00
|
|
|
/* a driver already attached? */
|
2008-05-06 00:14:30 +04:00
|
|
|
if (sc->PCI_SC_DEVICESC(device, function).c_dev != NULL && !match)
|
2009-07-05 01:01:10 +04:00
|
|
|
return 0;
|
2004-08-18 03:20:10 +04:00
|
|
|
|
2003-08-15 11:17:21 +04:00
|
|
|
bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
|
|
|
|
if (PCI_HDRTYPE_TYPE(bhlcr) > 2)
|
2009-07-05 01:01:10 +04:00
|
|
|
return 0;
|
2003-08-15 11:17:21 +04:00
|
|
|
|
2002-05-15 22:13:00 +04:00
|
|
|
id = pci_conf_read(pc, tag, PCI_ID_REG);
|
2013-09-15 13:19:52 +04:00
|
|
|
/* csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); */
|
2014-09-05 09:29:16 +04:00
|
|
|
pciclass = pci_conf_read(pc, tag, PCI_CLASS_REG);
|
2002-05-15 22:13:00 +04:00
|
|
|
|
|
|
|
/* Invalid vendor ID value? */
|
|
|
|
if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
|
2009-07-05 01:01:10 +04:00
|
|
|
return 0;
|
2002-05-15 22:13:00 +04:00
|
|
|
/* XXX Not invalid, but we've done this ~forever. */
|
|
|
|
if (PCI_VENDOR(id) == 0)
|
2009-07-05 01:01:10 +04:00
|
|
|
return 0;
|
2002-05-15 22:13:00 +04:00
|
|
|
|
2011-02-10 15:37:58 +03:00
|
|
|
/* Collect memory range info */
|
|
|
|
memset(sc->PCI_SC_DEVICESC(device, function).c_range, 0,
|
|
|
|
sizeof(sc->PCI_SC_DEVICESC(device, function).c_range));
|
|
|
|
i = 0;
|
2011-03-03 00:57:40 +03:00
|
|
|
switch (PCI_HDRTYPE_TYPE(bhlcr)) {
|
2011-04-06 03:10:52 +04:00
|
|
|
case PCI_HDRTYPE_PPB:
|
|
|
|
endbar = PCI_MAPREG_PPB_END;
|
|
|
|
break;
|
|
|
|
case PCI_HDRTYPE_PCB:
|
|
|
|
endbar = PCI_MAPREG_PCB_END;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
endbar = PCI_MAPREG_END;
|
|
|
|
break;
|
2011-03-03 00:57:40 +03:00
|
|
|
}
|
|
|
|
for (bar = PCI_MAPREG_START; bar < endbar; bar += width) {
|
2011-02-10 15:37:58 +03:00
|
|
|
struct pci_range *r;
|
2011-02-27 21:10:25 +03:00
|
|
|
pcireg_t type;
|
2011-02-10 15:37:58 +03:00
|
|
|
|
|
|
|
width = 4;
|
2011-02-27 21:10:25 +03:00
|
|
|
if (pci_mapreg_probe(pc, tag, bar, &type) == 0)
|
|
|
|
continue;
|
|
|
|
|
2011-02-10 15:37:58 +03:00
|
|
|
if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_MEM) {
|
|
|
|
if (PCI_MAPREG_MEM_TYPE(type) ==
|
|
|
|
PCI_MAPREG_MEM_TYPE_64BIT)
|
|
|
|
width = 8;
|
|
|
|
|
|
|
|
r = &sc->PCI_SC_DEVICESC(device, function).c_range[i++];
|
|
|
|
if (pci_mapreg_info(pc, tag, bar, type,
|
|
|
|
&r->r_offset, &r->r_size, &r->r_flags) != 0)
|
|
|
|
break;
|
2011-02-24 06:37:02 +03:00
|
|
|
if ((PCI_VENDOR(id) == PCI_VENDOR_ATI) && (bar == 0x10)
|
2011-04-06 03:05:06 +04:00
|
|
|
&& (r->r_size == 0x1000000)) {
|
2011-02-24 06:37:02 +03:00
|
|
|
struct pci_range *nr;
|
|
|
|
/*
|
|
|
|
* this has to be a mach64
|
|
|
|
* split things up so each half-aperture can
|
|
|
|
* be mapped PREFETCHABLE except the last page
|
|
|
|
* which may contain registers
|
|
|
|
*/
|
|
|
|
r->r_size = 0x7ff000;
|
|
|
|
r->r_flags = BUS_SPACE_MAP_LINEAR |
|
|
|
|
BUS_SPACE_MAP_PREFETCHABLE;
|
|
|
|
nr = &sc->PCI_SC_DEVICESC(device,
|
|
|
|
function).c_range[i++];
|
|
|
|
nr->r_offset = r->r_offset + 0x800000;
|
|
|
|
nr->r_size = 0x7ff000;
|
|
|
|
nr->r_flags = BUS_SPACE_MAP_LINEAR |
|
|
|
|
BUS_SPACE_MAP_PREFETCHABLE;
|
2016-01-23 20:09:51 +03:00
|
|
|
} else if ((PCI_VENDOR(id) == PCI_VENDOR_SILMOTION) &&
|
|
|
|
(PCI_PRODUCT(id) == PCI_PRODUCT_SILMOTION_SM502) &&
|
|
|
|
(bar == 0x10)) {
|
|
|
|
r->r_flags = BUS_SPACE_MAP_LINEAR |
|
|
|
|
BUS_SPACE_MAP_PREFETCHABLE;
|
2011-02-24 06:37:02 +03:00
|
|
|
}
|
2011-02-10 15:37:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-05-15 22:13:00 +04:00
|
|
|
pa.pa_iot = sc->sc_iot;
|
|
|
|
pa.pa_memt = sc->sc_memt;
|
|
|
|
pa.pa_dmat = sc->sc_dmat;
|
2003-06-16 03:08:53 +04:00
|
|
|
pa.pa_dmat64 = sc->sc_dmat64;
|
2002-05-15 22:13:00 +04:00
|
|
|
pa.pa_pc = pc;
|
2002-05-16 05:03:05 +04:00
|
|
|
pa.pa_bus = bus;
|
2002-05-15 22:13:00 +04:00
|
|
|
pa.pa_device = device;
|
|
|
|
pa.pa_function = function;
|
|
|
|
pa.pa_tag = tag;
|
|
|
|
pa.pa_id = id;
|
2014-09-05 09:29:16 +04:00
|
|
|
pa.pa_class = pciclass;
|
2002-05-15 22:13:00 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up memory, I/O enable, and PCI command flags
|
|
|
|
* as appropriate.
|
|
|
|
*/
|
|
|
|
pa.pa_flags = sc->sc_flags;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the cache line size is not configured, then
|
|
|
|
* clear the MRL/MRM/MWI command-ok flags.
|
|
|
|
*/
|
2011-04-06 03:10:52 +04:00
|
|
|
if (PCI_CACHELINE(bhlcr) == 0) {
|
2002-05-15 22:13:00 +04:00
|
|
|
pa.pa_flags &= ~(PCI_FLAGS_MRL_OKAY|
|
|
|
|
PCI_FLAGS_MRM_OKAY|PCI_FLAGS_MWI_OKAY);
|
2011-04-06 03:10:52 +04:00
|
|
|
}
|
2002-05-15 22:13:00 +04:00
|
|
|
|
2002-05-18 22:14:11 +04:00
|
|
|
if (sc->sc_bridgetag == NULL) {
|
2002-05-15 22:13:00 +04:00
|
|
|
pa.pa_intrswiz = 0;
|
|
|
|
pa.pa_intrtag = tag;
|
|
|
|
} else {
|
|
|
|
pa.pa_intrswiz = sc->sc_intrswiz + device;
|
|
|
|
pa.pa_intrtag = sc->sc_intrtag;
|
|
|
|
}
|
2003-08-15 11:17:21 +04:00
|
|
|
|
|
|
|
intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
|
|
|
|
|
2002-05-15 22:13:00 +04:00
|
|
|
pin = PCI_INTERRUPT_PIN(intr);
|
2002-05-19 01:40:41 +04:00
|
|
|
pa.pa_rawintrpin = pin;
|
2002-05-15 22:13:00 +04:00
|
|
|
if (pin == PCI_INTERRUPT_PIN_NONE) {
|
|
|
|
/* no interrupt */
|
|
|
|
pa.pa_intrpin = 0;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* swizzle it based on the number of busses we're
|
|
|
|
* behind and our device number.
|
|
|
|
*/
|
|
|
|
pa.pa_intrpin = /* XXX */
|
|
|
|
((pin + pa.pa_intrswiz - 1) % 4) + 1;
|
|
|
|
}
|
|
|
|
pa.pa_intrline = PCI_INTERRUPT_LINE(intr);
|
|
|
|
|
2015-04-27 10:03:57 +03:00
|
|
|
#ifdef __HAVE_PCI_MSI_MSIX
|
|
|
|
if (pci_get_ht_capability(pc, tag, PCI_HT_CAP_MSIMAP, &off, &cap)) {
|
|
|
|
/*
|
|
|
|
* XXX Should we enable MSI mapping ourselves on
|
|
|
|
* systems that have it disabled?
|
|
|
|
*/
|
|
|
|
if (cap & PCI_HT_MSI_ENABLED) {
|
|
|
|
uint64_t addr;
|
|
|
|
if ((cap & PCI_HT_MSI_FIXED) == 0) {
|
|
|
|
addr = pci_conf_read(pc, tag,
|
|
|
|
off + PCI_HT_MSI_ADDR_LO);
|
|
|
|
addr |= (uint64_t)pci_conf_read(pc, tag,
|
|
|
|
off + PCI_HT_MSI_ADDR_HI) << 32;
|
|
|
|
} else
|
|
|
|
addr = PCI_HT_MSI_FIXED_ADDR;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX This will fail to enable MSI on systems
|
|
|
|
* that don't use the canonical address.
|
|
|
|
*/
|
|
|
|
if (addr == PCI_HT_MSI_FIXED_ADDR) {
|
|
|
|
pa.pa_flags |= PCI_FLAGS_MSI_OKAY;
|
|
|
|
pa.pa_flags |= PCI_FLAGS_MSIX_OKAY;
|
2015-11-02 12:29:08 +03:00
|
|
|
} else
|
|
|
|
aprint_verbose_dev(sc->sc_dev,
|
|
|
|
"HyperTransport MSI mapping is not supported yet. Disable MSI/MSI-X.\n");
|
2015-04-27 10:03:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2002-05-15 22:13:00 +04:00
|
|
|
if (match != NULL) {
|
|
|
|
ret = (*match)(&pa);
|
|
|
|
if (ret != 0 && pap != NULL)
|
|
|
|
*pap = pa;
|
|
|
|
} else {
|
2008-05-06 00:14:30 +04:00
|
|
|
struct pci_child *c;
|
2005-08-25 22:35:38 +04:00
|
|
|
locs[PCICF_DEV] = device;
|
|
|
|
locs[PCICF_FUNCTION] = function;
|
2004-08-18 03:20:10 +04:00
|
|
|
|
2008-05-06 00:14:30 +04:00
|
|
|
c = &sc->PCI_SC_DEVICESC(device, function);
|
|
|
|
pci_conf_capture(pc, tag, &c->c_conf);
|
|
|
|
if (pci_get_powerstate(pc, tag, &c->c_powerstate) == 0)
|
|
|
|
c->c_psok = true;
|
|
|
|
else
|
|
|
|
c->c_psok = false;
|
2009-08-20 01:36:47 +04:00
|
|
|
|
|
|
|
c->c_dev = config_found_sm_loc(sc->sc_dev, "pci", locs, &pa,
|
|
|
|
pciprint, config_stdsubmatch);
|
|
|
|
|
|
|
|
ret = (c->c_dev != NULL);
|
2002-05-15 22:13:00 +04:00
|
|
|
}
|
|
|
|
|
2009-07-05 01:01:10 +04:00
|
|
|
return ret;
|
2002-05-15 22:13:00 +04:00
|
|
|
}
|
|
|
|
|
2008-04-09 21:01:53 +04:00
|
|
|
void
|
2008-03-21 10:47:43 +03:00
|
|
|
pcidevdetached(device_t self, device_t child)
|
2004-08-18 03:20:10 +04:00
|
|
|
{
|
2008-05-06 00:14:30 +04:00
|
|
|
struct pci_softc *sc = device_private(self);
|
2004-08-18 03:20:10 +04:00
|
|
|
int d, f;
|
2008-05-06 00:14:30 +04:00
|
|
|
pcitag_t tag;
|
|
|
|
struct pci_child *c;
|
2004-08-18 03:20:10 +04:00
|
|
|
|
2008-03-21 10:47:43 +03:00
|
|
|
d = device_locator(child, PCICF_DEV);
|
|
|
|
f = device_locator(child, PCICF_FUNCTION);
|
2004-08-18 03:20:10 +04:00
|
|
|
|
2008-05-06 00:14:30 +04:00
|
|
|
c = &sc->PCI_SC_DEVICESC(d, f);
|
|
|
|
|
|
|
|
KASSERT(c->c_dev == child);
|
2004-08-18 03:20:10 +04:00
|
|
|
|
2008-05-06 00:14:30 +04:00
|
|
|
tag = pci_make_tag(sc->sc_pc, sc->sc_bus, d, f);
|
|
|
|
if (c->c_psok)
|
|
|
|
pci_set_powerstate(sc->sc_pc, tag, c->c_powerstate);
|
|
|
|
pci_conf_restore(sc->sc_pc, tag, &c->c_conf);
|
|
|
|
c->c_dev = NULL;
|
2004-08-18 03:20:10 +04:00
|
|
|
}
|
|
|
|
|
During shutdown, detach devices in an orderly fashion.
Call the detach routine for every device in the device tree, starting
with the leaves and moving toward the root, expecting that each
(pseudo-)device driver will use the opportunity to gracefully commit
outstandings transactions to the underlying (pseudo-)device and to
relinquish control of the hardware to the system BIOS.
Detaching devices is not suitable for every shutdown: in an emergency,
or if the system state is inconsistent, we should resort to a fast,
simple shutdown that uses only the pmf(9) shutdown hooks and the
(deprecated) shutdownhooks. For now, if the flag RB_NOSYNC is set in
boothowto, opt for the fast, simple shutdown.
Add a device flag, DVF_DETACH_SHUTDOWN, that indicates by its presence
that it is safe to detach a device during shutdown. Introduce macros
CFATTACH_DECL3() and CFATTACH_DECL3_NEW() for creating autoconf
attachments with default device flags. Add DVF_DETACH_SHUTDOWN
to configuration attachments for atabus(4), atw(4) at cardbus(4),
cardbus(4), cardslot(4), com(4) at isa(4), elanpar(4), elanpex(4),
elansc(4), gpio(4), npx(4) at isa(4), nsphyter(4), pci(4), pcib(4),
pcmcia(4), ppb(4), sip(4), wd(4), and wdc(4) at isa(4).
Add a device-detachment "reason" flag, DETACH_SHUTDOWN, that tells the
autoconf code and a device driver that the reason for detachment is
system shutdown.
Add a sysctl, kern.detachall, that tells the system to try to detach
every device at shutdown, regardless of any device's DVF_DETACH_SHUTDOWN
flag. The default for kern.detachall is 0. SET IT TO 1, PLEASE, TO
HELP TEST AND DEBUG DEVICE DETACHMENT AT SHUTDOWN.
This is a work in progress. In future work, I aim to treat
pseudo-devices more thoroughly, and to gracefully tear down a stack of
(pseudo-)disk drivers and filesystems, including cgd(4), vnd(4), and
raid(4) instances at shutdown.
Also commit some changes that are not easily untangled from the rest:
(1) begin to simplify device_t locking: rename struct pmf_private to
device_lock, and incorporate device_lock into struct device.
(2) #include <sys/device.h> in sys/pmf.h in order to get some
definitions that it needs. Stop unnecessarily #including <sys/device.h>
in sys/arch/x86/include/pic.h to keep the amd64, xen, and i386 releases
building.
2009-04-02 04:09:32 +04:00
|
|
|
CFATTACH_DECL3_NEW(pci, sizeof(struct pci_softc),
|
|
|
|
pcimatch, pciattach, pcidetach, NULL, pcirescan, pcidevdetached,
|
|
|
|
DVF_DETACH_SHUTDOWN);
|
2007-12-09 23:27:42 +03:00
|
|
|
|
1998-11-07 19:47:22 +03:00
|
|
|
int
|
2005-06-28 04:28:41 +04:00
|
|
|
pci_get_capability(pci_chipset_tag_t pc, pcitag_t tag, int capid,
|
|
|
|
int *offset, pcireg_t *value)
|
1998-11-07 19:47:22 +03:00
|
|
|
{
|
|
|
|
pcireg_t reg;
|
|
|
|
unsigned int ofs;
|
|
|
|
|
|
|
|
reg = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
|
|
|
|
if (!(reg & PCI_STATUS_CAPLIST_SUPPORT))
|
2009-07-05 01:01:10 +04:00
|
|
|
return 0;
|
1998-11-07 19:47:22 +03:00
|
|
|
|
2000-05-10 12:39:58 +04:00
|
|
|
/* Determine the Capability List Pointer register to start with. */
|
2000-05-08 14:52:38 +04:00
|
|
|
reg = pci_conf_read(pc, tag, PCI_BHLC_REG);
|
|
|
|
switch (PCI_HDRTYPE_TYPE(reg)) {
|
|
|
|
case 0: /* standard device header */
|
2007-10-16 22:25:53 +04:00
|
|
|
case 1: /* PCI-PCI bridge header */
|
2000-05-08 14:52:38 +04:00
|
|
|
ofs = PCI_CAPLISTPTR_REG;
|
|
|
|
break;
|
|
|
|
case 2: /* PCI-CardBus Bridge header */
|
|
|
|
ofs = PCI_CARDBUS_CAPLISTPTR_REG;
|
|
|
|
break;
|
|
|
|
default:
|
2009-07-05 01:01:10 +04:00
|
|
|
return 0;
|
2000-05-08 14:52:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
ofs = PCI_CAPLIST_PTR(pci_conf_read(pc, tag, ofs));
|
1998-11-07 19:47:22 +03:00
|
|
|
while (ofs != 0) {
|
2008-09-19 18:37:13 +04:00
|
|
|
if ((ofs & 3) || (ofs < 0x40)) {
|
|
|
|
int bus, device, function;
|
|
|
|
|
|
|
|
pci_decompose_tag(pc, tag, &bus, &device, &function);
|
|
|
|
|
|
|
|
printf("Skipping broken PCI header on %d:%d:%d\n",
|
|
|
|
bus, device, function);
|
|
|
|
break;
|
|
|
|
}
|
1998-11-07 19:47:22 +03:00
|
|
|
reg = pci_conf_read(pc, tag, ofs);
|
|
|
|
if (PCI_CAPLIST_CAP(reg) == capid) {
|
|
|
|
if (offset)
|
|
|
|
*offset = ofs;
|
|
|
|
if (value)
|
|
|
|
*value = reg;
|
2009-07-05 01:01:10 +04:00
|
|
|
return 1;
|
1998-11-07 19:47:22 +03:00
|
|
|
}
|
|
|
|
ofs = PCI_CAPLIST_NEXT(reg);
|
|
|
|
}
|
|
|
|
|
2009-07-05 01:01:10 +04:00
|
|
|
return 0;
|
1998-11-07 19:47:22 +03:00
|
|
|
}
|
2001-09-10 14:04:49 +04:00
|
|
|
|
2015-04-27 10:03:57 +03:00
|
|
|
int
|
|
|
|
pci_get_ht_capability(pci_chipset_tag_t pc, pcitag_t tag, int capid,
|
|
|
|
int *offset, pcireg_t *value)
|
|
|
|
{
|
|
|
|
pcireg_t reg;
|
|
|
|
unsigned int ofs;
|
|
|
|
|
|
|
|
if (pci_get_capability(pc, tag, PCI_CAP_LDT, &ofs, NULL) == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while (ofs != 0) {
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if ((ofs & 3) || (ofs < 0x40))
|
|
|
|
panic("pci_get_ht_capability");
|
|
|
|
#endif
|
|
|
|
reg = pci_conf_read(pc, tag, ofs);
|
|
|
|
if (PCI_HT_CAP(reg) == capid) {
|
|
|
|
if (offset)
|
|
|
|
*offset = ofs;
|
|
|
|
if (value)
|
|
|
|
*value = reg;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
ofs = PCI_CAPLIST_NEXT(reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-13 07:39:33 +03:00
|
|
|
/*
|
|
|
|
* return number of the devices's MSI vectors
|
|
|
|
* return 0 if the device does not support MSI
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
pci_msi_count(pci_chipset_tag_t pc, pcitag_t tag)
|
|
|
|
{
|
|
|
|
pcireg_t reg;
|
|
|
|
uint32_t mmc;
|
|
|
|
int count, offset;
|
|
|
|
|
|
|
|
if (pci_get_capability(pc, tag, PCI_CAP_MSI, &offset, NULL) == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
reg = pci_conf_read(pc, tag, offset + PCI_MSI_CTL);
|
|
|
|
mmc = PCI_MSI_CTL_MMC(reg);
|
|
|
|
count = 1 << mmc;
|
|
|
|
if (count > PCI_MSI_MAX_VECTORS) {
|
|
|
|
aprint_error("detect an illegal device! The device use reserved MMC values.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* return number of the devices's MSI-X vectors
|
|
|
|
* return 0 if the device does not support MSI-X
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
pci_msix_count(pci_chipset_tag_t pc, pcitag_t tag)
|
|
|
|
{
|
|
|
|
pcireg_t reg;
|
|
|
|
int offset;
|
|
|
|
|
|
|
|
if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &offset, NULL) == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
reg = pci_conf_read(pc, tag, offset + PCI_MSIX_CTL);
|
|
|
|
|
|
|
|
return PCI_MSIX_CTL_TBLSIZE(reg);
|
|
|
|
}
|
|
|
|
|
2015-10-02 08:22:49 +03:00
|
|
|
int
|
|
|
|
pci_get_ext_capability(pci_chipset_tag_t pc, pcitag_t tag, int capid,
|
|
|
|
int *offset, pcireg_t *value)
|
|
|
|
{
|
|
|
|
pcireg_t reg;
|
|
|
|
unsigned int ofs;
|
|
|
|
|
|
|
|
/* Only supported for PCI-express devices */
|
|
|
|
if (!pci_get_capability(pc, tag, PCI_CAP_PCIEXPRESS, NULL, NULL))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ofs = PCI_EXTCAPLIST_BASE;
|
|
|
|
reg = pci_conf_read(pc, tag, ofs);
|
|
|
|
if (reg == 0xffffffff || reg == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if ((ofs & 3) || ofs < PCI_EXTCAPLIST_BASE)
|
|
|
|
panic("%s: invalid offset %u", __func__, ofs);
|
|
|
|
#endif
|
|
|
|
if (PCI_EXTCAPLIST_CAP(reg) == capid) {
|
|
|
|
if (offset != NULL)
|
|
|
|
*offset = ofs;
|
|
|
|
if (value != NULL)
|
|
|
|
*value = reg;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
ofs = PCI_EXTCAPLIST_NEXT(reg);
|
|
|
|
if (ofs == 0)
|
|
|
|
break;
|
|
|
|
reg = pci_conf_read(pc, tag, ofs);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-09-10 14:04:49 +04:00
|
|
|
int
|
|
|
|
pci_find_device(struct pci_attach_args *pa,
|
Neither pci_dma64_available(), pci_probe_device(), pci_mapreg_map(9),
pci_find_rom(), pci_intr_map(9), pci_enumerate_bus(), nor the match
predicate passed to pciide_compat_intr_establish() should ever modify
their pci_attach_args argument, so make their pci_attach_args arguments
const and deal with the fallout throughout the kernel.
For the most part, these changes add a 'const' where there was no
'const' before, however, some drivers and MD code used to modify
pci_attach_args. Now those drivers either copy their pci_attach_args
and modify the copy, or refrain from modifying pci_attach_args:
Xen: according to Manuel Bouyer, writing to pci_attach_args in
pci_intr_map() was a leftover from Xen 2. Probably a bug. I
stopped writing it. I have not tested this change.
siside(4): sis_hostbr_match() needlessly wrote to pci_attach_args.
Probably a bug. I use a temporary variable. I have not tested this
change.
slide(4): sl82c105_chip_map() overwrote the caller's pci_attach_args.
Probably a bug. Use a local pci_attach_args. I have not tested
this change.
viaide(4): via_sata_chip_map() and via_sata_chip_map_new() overwrote the
caller's pci_attach_args. Probably a bug. Make a local copy of the
caller's pci_attach_args and modify the copy. I have not tested
this change.
While I'm here, make pci_mapreg_submap() static.
With these changes in place, I have tested the compilation of these
kernels:
alpha GENERIC
amd64 GENERIC XEN3_DOM0
arc GENERIC
atari HADES MILAN-PCIIDE
bebox GENERIC
cats GENERIC
cobalt GENERIC
evbarm-eb NSLU2
evbarm-el ADI_BRH ARMADILLO9 CP3100 GEMINI GEMINI_MASTER GEMINI_SLAVE GUMSTIX
HDL_G IMX31LITE INTEGRATOR IQ31244 IQ80310 IQ80321 IXDP425 IXM1200
KUROBOX_PRO LUBBOCK MARVELL_NAS NAPPI SHEEVAPLUG SMDK2800 TEAMASA_NPWR
TEAMASA_NPWR_FC TS7200 TWINTAIL ZAO425
evbmips-el AP30 DBAU1500 DBAU1550 MALTA MERAKI MTX-1 OMSAL400 RB153 WGT624V3
evbmips64-el XLSATX
evbppc EV64260 MPC8536DS MPC8548CDS OPENBLOCKS200 OPENBLOCKS266
OPENBLOCKS266_OPT P2020RDB PMPPC RB800 WALNUT
hp700 GENERIC
i386 ALL XEN3_DOM0 XEN3_DOMU
ibmnws GENERIC
macppc GENERIC
mvmeppc GENERIC
netwinder GENERIC
ofppc GENERIC
prep GENERIC
sandpoint GENERIC
sgimips GENERIC32_IP2x
sparc GENERIC_SUN4U KRUPS
sparc64 GENERIC
As of Sun Apr 3 15:26:26 CDT 2011, I could not compile these kernels
with or without my patches in place:
### evbmips-el GDIUM
nbmake: nbmake: don't know how to make /home/dyoung/pristine-nbsd/src/sys/arch/mips/mips/softintr.c. Stop
### evbarm-el MPCSA_GENERIC
src/sys/arch/evbarm/conf/MPCSA_GENERIC:318: ds1672rtc*: unknown device `ds1672rtc'
### ia64 GENERIC
/tmp/genassym.28085/assym.c: In function 'f111':
/tmp/genassym.28085/assym.c:67: error: invalid application of 'sizeof' to incomplete type 'struct pcb'
/tmp/genassym.28085/assym.c:76: error: dereferencing pointer to incomplete type
### sgimips GENERIC32_IP3x
crmfb.o: In function `crmfb_attach':
crmfb.c:(.text+0x2304): undefined reference to `ddc_read_edid'
crmfb.c:(.text+0x2304): relocation truncated to fit: R_MIPS_26 against `ddc_read_edid'
crmfb.c:(.text+0x234c): undefined reference to `edid_parse'
crmfb.c:(.text+0x234c): relocation truncated to fit: R_MIPS_26 against `edid_parse'
crmfb.c:(.text+0x2354): undefined reference to `edid_print'
crmfb.c:(.text+0x2354): relocation truncated to fit: R_MIPS_26 against `edid_print'
2011-04-05 00:37:49 +04:00
|
|
|
int (*match)(const struct pci_attach_args *))
|
2001-09-10 14:04:49 +04:00
|
|
|
{
|
|
|
|
extern struct cfdriver pci_cd;
|
2008-03-21 10:47:43 +03:00
|
|
|
device_t pcidev;
|
2002-05-15 22:13:00 +04:00
|
|
|
int i;
|
2004-08-18 03:20:10 +04:00
|
|
|
static const int wildcard[2] = {
|
|
|
|
PCICF_DEV_DEFAULT,
|
|
|
|
PCICF_FUNCTION_DEFAULT
|
|
|
|
};
|
2001-09-10 14:04:49 +04:00
|
|
|
|
|
|
|
for (i = 0; i < pci_cd.cd_ndevs; i++) {
|
2008-06-13 02:44:47 +04:00
|
|
|
pcidev = device_lookup(&pci_cd, i);
|
2002-05-15 22:13:00 +04:00
|
|
|
if (pcidev != NULL &&
|
2008-03-23 19:40:12 +03:00
|
|
|
pci_enumerate_bus(device_private(pcidev), wildcard,
|
2002-05-15 22:13:00 +04:00
|
|
|
match, pa) != 0)
|
2009-07-05 01:01:10 +04:00
|
|
|
return 1;
|
2001-09-10 14:04:49 +04:00
|
|
|
}
|
2009-07-05 01:01:10 +04:00
|
|
|
return 0;
|
2002-05-15 22:13:00 +04:00
|
|
|
}
|
|
|
|
|
2004-07-29 20:51:01 +04:00
|
|
|
#ifndef PCI_MACHDEP_ENUMERATE_BUS
|
2002-05-15 22:13:00 +04:00
|
|
|
/*
|
|
|
|
* Generic PCI bus enumeration routine. Used unless machine-dependent
|
|
|
|
* code needs to provide something else.
|
|
|
|
*/
|
|
|
|
int
|
2004-08-18 03:20:10 +04:00
|
|
|
pci_enumerate_bus(struct pci_softc *sc, const int *locators,
|
Neither pci_dma64_available(), pci_probe_device(), pci_mapreg_map(9),
pci_find_rom(), pci_intr_map(9), pci_enumerate_bus(), nor the match
predicate passed to pciide_compat_intr_establish() should ever modify
their pci_attach_args argument, so make their pci_attach_args arguments
const and deal with the fallout throughout the kernel.
For the most part, these changes add a 'const' where there was no
'const' before, however, some drivers and MD code used to modify
pci_attach_args. Now those drivers either copy their pci_attach_args
and modify the copy, or refrain from modifying pci_attach_args:
Xen: according to Manuel Bouyer, writing to pci_attach_args in
pci_intr_map() was a leftover from Xen 2. Probably a bug. I
stopped writing it. I have not tested this change.
siside(4): sis_hostbr_match() needlessly wrote to pci_attach_args.
Probably a bug. I use a temporary variable. I have not tested this
change.
slide(4): sl82c105_chip_map() overwrote the caller's pci_attach_args.
Probably a bug. Use a local pci_attach_args. I have not tested
this change.
viaide(4): via_sata_chip_map() and via_sata_chip_map_new() overwrote the
caller's pci_attach_args. Probably a bug. Make a local copy of the
caller's pci_attach_args and modify the copy. I have not tested
this change.
While I'm here, make pci_mapreg_submap() static.
With these changes in place, I have tested the compilation of these
kernels:
alpha GENERIC
amd64 GENERIC XEN3_DOM0
arc GENERIC
atari HADES MILAN-PCIIDE
bebox GENERIC
cats GENERIC
cobalt GENERIC
evbarm-eb NSLU2
evbarm-el ADI_BRH ARMADILLO9 CP3100 GEMINI GEMINI_MASTER GEMINI_SLAVE GUMSTIX
HDL_G IMX31LITE INTEGRATOR IQ31244 IQ80310 IQ80321 IXDP425 IXM1200
KUROBOX_PRO LUBBOCK MARVELL_NAS NAPPI SHEEVAPLUG SMDK2800 TEAMASA_NPWR
TEAMASA_NPWR_FC TS7200 TWINTAIL ZAO425
evbmips-el AP30 DBAU1500 DBAU1550 MALTA MERAKI MTX-1 OMSAL400 RB153 WGT624V3
evbmips64-el XLSATX
evbppc EV64260 MPC8536DS MPC8548CDS OPENBLOCKS200 OPENBLOCKS266
OPENBLOCKS266_OPT P2020RDB PMPPC RB800 WALNUT
hp700 GENERIC
i386 ALL XEN3_DOM0 XEN3_DOMU
ibmnws GENERIC
macppc GENERIC
mvmeppc GENERIC
netwinder GENERIC
ofppc GENERIC
prep GENERIC
sandpoint GENERIC
sgimips GENERIC32_IP2x
sparc GENERIC_SUN4U KRUPS
sparc64 GENERIC
As of Sun Apr 3 15:26:26 CDT 2011, I could not compile these kernels
with or without my patches in place:
### evbmips-el GDIUM
nbmake: nbmake: don't know how to make /home/dyoung/pristine-nbsd/src/sys/arch/mips/mips/softintr.c. Stop
### evbarm-el MPCSA_GENERIC
src/sys/arch/evbarm/conf/MPCSA_GENERIC:318: ds1672rtc*: unknown device `ds1672rtc'
### ia64 GENERIC
/tmp/genassym.28085/assym.c: In function 'f111':
/tmp/genassym.28085/assym.c:67: error: invalid application of 'sizeof' to incomplete type 'struct pcb'
/tmp/genassym.28085/assym.c:76: error: dereferencing pointer to incomplete type
### sgimips GENERIC32_IP3x
crmfb.o: In function `crmfb_attach':
crmfb.c:(.text+0x2304): undefined reference to `ddc_read_edid'
crmfb.c:(.text+0x2304): relocation truncated to fit: R_MIPS_26 against `ddc_read_edid'
crmfb.c:(.text+0x234c): undefined reference to `edid_parse'
crmfb.c:(.text+0x234c): relocation truncated to fit: R_MIPS_26 against `edid_parse'
crmfb.c:(.text+0x2354): undefined reference to `edid_print'
crmfb.c:(.text+0x2354): relocation truncated to fit: R_MIPS_26 against `edid_print'
2011-04-05 00:37:49 +04:00
|
|
|
int (*match)(const struct pci_attach_args *), struct pci_attach_args *pap)
|
2002-05-15 22:13:00 +04:00
|
|
|
{
|
|
|
|
pci_chipset_tag_t pc = sc->sc_pc;
|
|
|
|
int device, function, nfunctions, ret;
|
|
|
|
const struct pci_quirkdata *qd;
|
|
|
|
pcireg_t id, bhlcr;
|
|
|
|
pcitag_t tag;
|
2011-08-25 00:27:35 +04:00
|
|
|
uint8_t devs[32];
|
|
|
|
int i, n;
|
|
|
|
|
2017-04-05 07:04:54 +03:00
|
|
|
device_t bridgedev;
|
|
|
|
bool arien = false;
|
|
|
|
|
|
|
|
/* Check PCIe ARI */
|
|
|
|
bridgedev = device_parent(sc->sc_dev);
|
|
|
|
if (device_is_a(bridgedev, "ppb")) {
|
|
|
|
struct ppb_softc *ppbsc = device_private(bridgedev);
|
|
|
|
pci_chipset_tag_t ppbpc = ppbsc->sc_pc;
|
|
|
|
pcitag_t ppbtag = ppbsc->sc_tag;
|
|
|
|
pcireg_t pciecap, reg;
|
|
|
|
|
|
|
|
if (pci_get_capability(ppbpc, ppbtag, PCI_CAP_PCIEXPRESS,
|
|
|
|
&pciecap, NULL) != 0) {
|
|
|
|
reg = pci_conf_read(ppbpc, ppbtag, pciecap
|
|
|
|
+ PCIE_DCSR2);
|
|
|
|
if ((reg & PCIE_DCSR2_ARI_FWD) != 0)
|
|
|
|
arien = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-25 00:27:35 +04:00
|
|
|
n = pci_bus_devorder(sc->sc_pc, sc->sc_bus, devs, __arraycount(devs));
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
device = devs[i];
|
2002-05-15 22:13:00 +04:00
|
|
|
|
2004-08-18 03:20:10 +04:00
|
|
|
if ((locators[PCICF_DEV] != PCICF_DEV_DEFAULT) &&
|
|
|
|
(locators[PCICF_DEV] != device))
|
|
|
|
continue;
|
|
|
|
|
2002-05-15 22:13:00 +04:00
|
|
|
tag = pci_make_tag(pc, sc->sc_bus, device, 0);
|
2003-08-15 11:17:21 +04:00
|
|
|
|
|
|
|
bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
|
|
|
|
if (PCI_HDRTYPE_TYPE(bhlcr) > 2)
|
|
|
|
continue;
|
|
|
|
|
2002-05-15 22:13:00 +04:00
|
|
|
id = pci_conf_read(pc, tag, PCI_ID_REG);
|
|
|
|
|
|
|
|
/* Invalid vendor ID value? */
|
|
|
|
if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
|
|
|
|
continue;
|
|
|
|
/* XXX Not invalid, but we've done this ~forever. */
|
|
|
|
if (PCI_VENDOR(id) == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
qd = pci_lookup_quirkdata(PCI_VENDOR(id), PCI_PRODUCT(id));
|
|
|
|
|
2003-08-15 11:17:21 +04:00
|
|
|
if (qd != NULL &&
|
|
|
|
(qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0)
|
2002-05-15 22:13:00 +04:00
|
|
|
nfunctions = 8;
|
2003-08-15 11:17:21 +04:00
|
|
|
else if (qd != NULL &&
|
|
|
|
(qd->quirks & PCI_QUIRK_MONOFUNCTION) != 0)
|
2002-05-15 22:13:00 +04:00
|
|
|
nfunctions = 1;
|
2017-04-05 07:04:54 +03:00
|
|
|
else if (arien)
|
|
|
|
nfunctions = 8; /* Scan all if ARI is enabled */
|
2003-08-15 11:17:21 +04:00
|
|
|
else
|
|
|
|
nfunctions = PCI_HDRTYPE_MULTIFN(bhlcr) ? 8 : 1;
|
2002-05-15 22:13:00 +04:00
|
|
|
|
2012-10-20 10:04:01 +04:00
|
|
|
#ifdef __PCI_DEV_FUNCORDER
|
|
|
|
char funcs[8];
|
|
|
|
int j;
|
|
|
|
for (j = 0; j < nfunctions; j++) {
|
|
|
|
funcs[j] = j;
|
|
|
|
}
|
|
|
|
if (j < __arraycount(funcs))
|
|
|
|
funcs[j] = -1;
|
|
|
|
if (nfunctions > 1) {
|
|
|
|
pci_dev_funcorder(sc->sc_pc, sc->sc_bus, device,
|
|
|
|
nfunctions, funcs);
|
|
|
|
}
|
|
|
|
for (j = 0;
|
|
|
|
j < 8 && (function = funcs[j]) < 8 && function >= 0;
|
|
|
|
j++) {
|
|
|
|
#else
|
2002-05-15 22:13:00 +04:00
|
|
|
for (function = 0; function < nfunctions; function++) {
|
2012-10-20 10:04:01 +04:00
|
|
|
#endif
|
2004-08-18 03:20:10 +04:00
|
|
|
if ((locators[PCICF_FUNCTION] != PCICF_FUNCTION_DEFAULT)
|
|
|
|
&& (locators[PCICF_FUNCTION] != function))
|
|
|
|
continue;
|
|
|
|
|
2003-08-15 11:17:21 +04:00
|
|
|
if (qd != NULL &&
|
|
|
|
(qd->quirks & PCI_QUIRK_SKIP_FUNC(function)) != 0)
|
|
|
|
continue;
|
2002-05-15 22:13:00 +04:00
|
|
|
tag = pci_make_tag(pc, sc->sc_bus, device, function);
|
|
|
|
ret = pci_probe_device(sc, tag, match, pap);
|
|
|
|
if (match != NULL && ret != 0)
|
2009-07-05 01:01:10 +04:00
|
|
|
return ret;
|
2002-05-15 22:13:00 +04:00
|
|
|
}
|
|
|
|
}
|
2009-07-05 01:01:10 +04:00
|
|
|
return 0;
|
2001-09-10 14:04:49 +04:00
|
|
|
}
|
2004-07-29 20:51:01 +04:00
|
|
|
#endif /* PCI_MACHDEP_ENUMERATE_BUS */
|
2002-06-18 17:18:37 +04:00
|
|
|
|
2003-03-26 00:56:20 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Vital Product Data (PCI 2.2)
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
pci_vpd_read(pci_chipset_tag_t pc, pcitag_t tag, int offset, int count,
|
|
|
|
pcireg_t *data)
|
|
|
|
{
|
|
|
|
uint32_t reg;
|
|
|
|
int ofs, i, j;
|
|
|
|
|
|
|
|
KASSERT(data != NULL);
|
|
|
|
KASSERT((offset + count) < 0x7fff);
|
|
|
|
|
|
|
|
if (pci_get_capability(pc, tag, PCI_CAP_VPD, &ofs, ®) == 0)
|
2009-07-05 01:01:10 +04:00
|
|
|
return 1;
|
2003-03-26 00:56:20 +03:00
|
|
|
|
|
|
|
for (i = 0; i < count; offset += sizeof(*data), i++) {
|
|
|
|
reg &= 0x0000ffff;
|
|
|
|
reg &= ~PCI_VPD_OPFLAG;
|
|
|
|
reg |= PCI_VPD_ADDRESS(offset);
|
|
|
|
pci_conf_write(pc, tag, ofs, reg);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PCI 2.2 does not specify how long we should poll
|
|
|
|
* for completion nor whether the operation can fail.
|
|
|
|
*/
|
|
|
|
j = 0;
|
|
|
|
do {
|
|
|
|
if (j++ == 20)
|
2009-07-05 01:01:10 +04:00
|
|
|
return 1;
|
2003-03-26 00:56:20 +03:00
|
|
|
delay(4);
|
|
|
|
reg = pci_conf_read(pc, tag, ofs);
|
|
|
|
} while ((reg & PCI_VPD_OPFLAG) == 0);
|
|
|
|
data[i] = pci_conf_read(pc, tag, PCI_VPD_DATAREG(ofs));
|
|
|
|
}
|
|
|
|
|
2009-07-05 01:01:10 +04:00
|
|
|
return 0;
|
2003-03-26 00:56:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
pci_vpd_write(pci_chipset_tag_t pc, pcitag_t tag, int offset, int count,
|
|
|
|
pcireg_t *data)
|
|
|
|
{
|
|
|
|
pcireg_t reg;
|
|
|
|
int ofs, i, j;
|
|
|
|
|
|
|
|
KASSERT(data != NULL);
|
|
|
|
KASSERT((offset + count) < 0x7fff);
|
|
|
|
|
|
|
|
if (pci_get_capability(pc, tag, PCI_CAP_VPD, &ofs, ®) == 0)
|
2009-07-05 01:01:10 +04:00
|
|
|
return 1;
|
2003-03-26 00:56:20 +03:00
|
|
|
|
|
|
|
for (i = 0; i < count; offset += sizeof(*data), i++) {
|
|
|
|
pci_conf_write(pc, tag, PCI_VPD_DATAREG(ofs), data[i]);
|
|
|
|
|
|
|
|
reg &= 0x0000ffff;
|
2003-05-03 22:02:37 +04:00
|
|
|
reg |= PCI_VPD_OPFLAG;
|
2003-03-26 00:56:20 +03:00
|
|
|
reg |= PCI_VPD_ADDRESS(offset);
|
|
|
|
pci_conf_write(pc, tag, ofs, reg);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PCI 2.2 does not specify how long we should poll
|
|
|
|
* for completion nor whether the operation can fail.
|
|
|
|
*/
|
|
|
|
j = 0;
|
|
|
|
do {
|
|
|
|
if (j++ == 20)
|
2009-07-05 01:01:10 +04:00
|
|
|
return 1;
|
2003-03-26 00:56:20 +03:00
|
|
|
delay(1);
|
|
|
|
reg = pci_conf_read(pc, tag, ofs);
|
2003-05-03 22:02:37 +04:00
|
|
|
} while (reg & PCI_VPD_OPFLAG);
|
2003-03-26 00:56:20 +03:00
|
|
|
}
|
|
|
|
|
2009-07-05 01:01:10 +04:00
|
|
|
return 0;
|
2003-03-26 00:56:20 +03:00
|
|
|
}
|
2003-06-16 03:08:53 +04:00
|
|
|
|
|
|
|
int
|
Neither pci_dma64_available(), pci_probe_device(), pci_mapreg_map(9),
pci_find_rom(), pci_intr_map(9), pci_enumerate_bus(), nor the match
predicate passed to pciide_compat_intr_establish() should ever modify
their pci_attach_args argument, so make their pci_attach_args arguments
const and deal with the fallout throughout the kernel.
For the most part, these changes add a 'const' where there was no
'const' before, however, some drivers and MD code used to modify
pci_attach_args. Now those drivers either copy their pci_attach_args
and modify the copy, or refrain from modifying pci_attach_args:
Xen: according to Manuel Bouyer, writing to pci_attach_args in
pci_intr_map() was a leftover from Xen 2. Probably a bug. I
stopped writing it. I have not tested this change.
siside(4): sis_hostbr_match() needlessly wrote to pci_attach_args.
Probably a bug. I use a temporary variable. I have not tested this
change.
slide(4): sl82c105_chip_map() overwrote the caller's pci_attach_args.
Probably a bug. Use a local pci_attach_args. I have not tested
this change.
viaide(4): via_sata_chip_map() and via_sata_chip_map_new() overwrote the
caller's pci_attach_args. Probably a bug. Make a local copy of the
caller's pci_attach_args and modify the copy. I have not tested
this change.
While I'm here, make pci_mapreg_submap() static.
With these changes in place, I have tested the compilation of these
kernels:
alpha GENERIC
amd64 GENERIC XEN3_DOM0
arc GENERIC
atari HADES MILAN-PCIIDE
bebox GENERIC
cats GENERIC
cobalt GENERIC
evbarm-eb NSLU2
evbarm-el ADI_BRH ARMADILLO9 CP3100 GEMINI GEMINI_MASTER GEMINI_SLAVE GUMSTIX
HDL_G IMX31LITE INTEGRATOR IQ31244 IQ80310 IQ80321 IXDP425 IXM1200
KUROBOX_PRO LUBBOCK MARVELL_NAS NAPPI SHEEVAPLUG SMDK2800 TEAMASA_NPWR
TEAMASA_NPWR_FC TS7200 TWINTAIL ZAO425
evbmips-el AP30 DBAU1500 DBAU1550 MALTA MERAKI MTX-1 OMSAL400 RB153 WGT624V3
evbmips64-el XLSATX
evbppc EV64260 MPC8536DS MPC8548CDS OPENBLOCKS200 OPENBLOCKS266
OPENBLOCKS266_OPT P2020RDB PMPPC RB800 WALNUT
hp700 GENERIC
i386 ALL XEN3_DOM0 XEN3_DOMU
ibmnws GENERIC
macppc GENERIC
mvmeppc GENERIC
netwinder GENERIC
ofppc GENERIC
prep GENERIC
sandpoint GENERIC
sgimips GENERIC32_IP2x
sparc GENERIC_SUN4U KRUPS
sparc64 GENERIC
As of Sun Apr 3 15:26:26 CDT 2011, I could not compile these kernels
with or without my patches in place:
### evbmips-el GDIUM
nbmake: nbmake: don't know how to make /home/dyoung/pristine-nbsd/src/sys/arch/mips/mips/softintr.c. Stop
### evbarm-el MPCSA_GENERIC
src/sys/arch/evbarm/conf/MPCSA_GENERIC:318: ds1672rtc*: unknown device `ds1672rtc'
### ia64 GENERIC
/tmp/genassym.28085/assym.c: In function 'f111':
/tmp/genassym.28085/assym.c:67: error: invalid application of 'sizeof' to incomplete type 'struct pcb'
/tmp/genassym.28085/assym.c:76: error: dereferencing pointer to incomplete type
### sgimips GENERIC32_IP3x
crmfb.o: In function `crmfb_attach':
crmfb.c:(.text+0x2304): undefined reference to `ddc_read_edid'
crmfb.c:(.text+0x2304): relocation truncated to fit: R_MIPS_26 against `ddc_read_edid'
crmfb.c:(.text+0x234c): undefined reference to `edid_parse'
crmfb.c:(.text+0x234c): relocation truncated to fit: R_MIPS_26 against `edid_parse'
crmfb.c:(.text+0x2354): undefined reference to `edid_print'
crmfb.c:(.text+0x2354): relocation truncated to fit: R_MIPS_26 against `edid_print'
2011-04-05 00:37:49 +04:00
|
|
|
pci_dma64_available(const struct pci_attach_args *pa)
|
2005-02-27 03:26:58 +03:00
|
|
|
{
|
2003-06-16 03:08:53 +04:00
|
|
|
#ifdef _PCI_HAVE_DMA64
|
2008-11-16 20:31:03 +03:00
|
|
|
if (BUS_DMA_TAG_VALID(pa->pa_dmat64))
|
2003-06-16 03:08:53 +04:00
|
|
|
return 1;
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
2005-01-27 00:49:00 +03:00
|
|
|
|
|
|
|
void
|
|
|
|
pci_conf_capture(pci_chipset_tag_t pc, pcitag_t tag,
|
|
|
|
struct pci_conf_state *pcs)
|
|
|
|
{
|
|
|
|
int off;
|
|
|
|
|
|
|
|
for (off = 0; off < 16; off++)
|
|
|
|
pcs->reg[off] = pci_conf_read(pc, tag, (off * 4));
|
|
|
|
|
2018-12-01 04:23:24 +03:00
|
|
|
/* For PCI-X */
|
|
|
|
if (pci_get_capability(pc, tag, PCI_CAP_PCIX, &off, NULL) != 0)
|
|
|
|
pcs->x_csr = pci_conf_read(pc, tag, off + PCIX_CMD);
|
|
|
|
|
|
|
|
/* For PCIe */
|
|
|
|
if (pci_get_capability(pc, tag, PCI_CAP_PCIEXPRESS, &off, NULL) != 0) {
|
|
|
|
pcireg_t xcap = pci_conf_read(pc, tag, off + PCIE_XCAP);
|
|
|
|
unsigned int devtype;
|
|
|
|
|
|
|
|
devtype = PCIE_XCAP_TYPE(xcap);
|
|
|
|
pcs->e_dcr = (uint16_t)pci_conf_read(pc, tag, off + PCIE_DCSR);
|
|
|
|
|
|
|
|
if (PCIE_HAS_LINKREGS(devtype))
|
|
|
|
pcs->e_lcr = (uint16_t)pci_conf_read(pc, tag,
|
|
|
|
off + PCIE_LCSR);
|
|
|
|
|
|
|
|
if ((xcap & PCIE_XCAP_SI) != 0)
|
|
|
|
pcs->e_slcr = (uint16_t)pci_conf_read(pc, tag,
|
|
|
|
off + PCIE_SLCSR);
|
|
|
|
|
|
|
|
if (PCIE_HAS_ROOTREGS(devtype))
|
|
|
|
pcs->e_rcr = (uint16_t)pci_conf_read(pc, tag,
|
|
|
|
off + PCIE_RCR);
|
|
|
|
|
|
|
|
if (__SHIFTOUT(xcap, PCIE_XCAP_VER_MASK) >= 2) {
|
|
|
|
pcs->e_dcr2 = (uint16_t)pci_conf_read(pc, tag,
|
|
|
|
off + PCIE_DCSR2);
|
|
|
|
|
|
|
|
if (PCIE_HAS_LINKREGS(devtype))
|
|
|
|
pcs->e_lcr2 = (uint16_t)pci_conf_read(pc, tag,
|
|
|
|
off + PCIE_LCSR2);
|
|
|
|
|
|
|
|
/* XXX PCIE_SLCSR2 (It's reserved by the PCIe spec) */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For MSI */
|
|
|
|
if (pci_get_capability(pc, tag, PCI_CAP_MSI, &off, NULL) != 0) {
|
|
|
|
bool bit64, pvmask;
|
|
|
|
|
|
|
|
pcs->msi_ctl = pci_conf_read(pc, tag, off + PCI_MSI_CTL);
|
|
|
|
|
|
|
|
bit64 = pcs->msi_ctl & PCI_MSI_CTL_64BIT_ADDR;
|
|
|
|
pvmask = pcs->msi_ctl & PCI_MSI_CTL_PERVEC_MASK;
|
|
|
|
|
|
|
|
/* Address */
|
|
|
|
pcs->msi_maddr = pci_conf_read(pc, tag, off + PCI_MSI_MADDR);
|
|
|
|
if (bit64)
|
|
|
|
pcs->msi_maddr64_hi = pci_conf_read(pc, tag,
|
|
|
|
off + PCI_MSI_MADDR64_HI);
|
|
|
|
|
|
|
|
/* Data */
|
|
|
|
pcs->msi_mdata = pci_conf_read(pc, tag,
|
|
|
|
off + (bit64 ? PCI_MSI_MDATA64 : PCI_MSI_MDATA));
|
|
|
|
|
|
|
|
/* Per-vector masking */
|
|
|
|
if (pvmask)
|
|
|
|
pcs->msi_mask = pci_conf_read(pc, tag,
|
|
|
|
off + (bit64 ? PCI_MSI_MASK64 : PCI_MSI_MASK));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For MSI-X */
|
|
|
|
if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL) != 0)
|
|
|
|
pcs->msix_ctl = pci_conf_read(pc, tag, off + PCI_MSIX_CTL);
|
2005-01-27 00:49:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pci_conf_restore(pci_chipset_tag_t pc, pcitag_t tag,
|
|
|
|
struct pci_conf_state *pcs)
|
|
|
|
{
|
|
|
|
int off;
|
2007-12-09 23:27:42 +03:00
|
|
|
pcireg_t val;
|
2005-01-27 00:49:00 +03:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
for (off = 15; off >= 0; off--) {
|
|
|
|
val = pci_conf_read(pc, tag, (off * 4));
|
|
|
|
if (val != pcs->reg[off])
|
|
|
|
pci_conf_write(pc, tag, (off * 4), pcs->reg[off]);
|
|
|
|
}
|
2005-01-27 00:49:00 +03:00
|
|
|
|
2018-12-01 04:23:24 +03:00
|
|
|
/* For PCI-X */
|
|
|
|
if (pci_get_capability(pc, tag, PCI_CAP_PCIX, &off, NULL) != 0)
|
|
|
|
pci_conf_write(pc, tag, off + PCIX_CMD, pcs->x_csr);
|
|
|
|
|
|
|
|
/* For PCIe */
|
|
|
|
if (pci_get_capability(pc, tag, PCI_CAP_PCIEXPRESS, &off, NULL) != 0) {
|
|
|
|
pcireg_t xcap = pci_conf_read(pc, tag, off + PCIE_XCAP);
|
|
|
|
unsigned int devtype;
|
|
|
|
|
|
|
|
devtype = PCIE_XCAP_TYPE(xcap);
|
|
|
|
pci_conf_write(pc, tag, off + PCIE_DCSR, pcs->e_dcr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PCIe capability is variable sized. To not to write the next
|
|
|
|
* area, check the existence of each register.
|
|
|
|
*/
|
|
|
|
if (PCIE_HAS_LINKREGS(devtype))
|
|
|
|
pci_conf_write(pc, tag, off + PCIE_LCSR, pcs->e_lcr);
|
|
|
|
|
|
|
|
if ((xcap & PCIE_XCAP_SI) != 0)
|
|
|
|
pci_conf_write(pc, tag, off + PCIE_SLCSR, pcs->e_slcr);
|
|
|
|
|
|
|
|
if (PCIE_HAS_ROOTREGS(devtype))
|
|
|
|
pci_conf_write(pc, tag, off + PCIE_RCR, pcs->e_rcr);
|
|
|
|
|
|
|
|
if (__SHIFTOUT(xcap, PCIE_XCAP_VER_MASK) >= 2) {
|
|
|
|
pci_conf_write(pc, tag, off + PCIE_DCSR2, pcs->e_dcr2);
|
|
|
|
|
|
|
|
if (PCIE_HAS_LINKREGS(devtype))
|
|
|
|
pci_conf_write(pc, tag, off + PCIE_LCSR2,
|
|
|
|
pcs->e_lcr2);
|
|
|
|
|
|
|
|
/* XXX PCIE_SLCSR2 (It's reserved by the PCIe spec) */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For MSI */
|
|
|
|
if (pci_get_capability(pc, tag, PCI_CAP_MSI, &off, NULL) != 0) {
|
|
|
|
pcireg_t reg;
|
|
|
|
bool bit64, pvmask;
|
|
|
|
|
|
|
|
/* First, drop Enable bit in case it's already set. */
|
|
|
|
reg = pci_conf_read(pc, tag, off + PCI_MSI_CTL);
|
|
|
|
pci_conf_write(pc, tag, off + PCI_MSI_CTL,
|
|
|
|
reg & ~PCI_MSI_CTL_MSI_ENABLE);
|
|
|
|
|
|
|
|
bit64 = pcs->msi_ctl & PCI_MSI_CTL_64BIT_ADDR;
|
|
|
|
pvmask = pcs->msi_ctl & PCI_MSI_CTL_PERVEC_MASK;
|
|
|
|
|
|
|
|
/* Address */
|
|
|
|
pci_conf_write(pc, tag, off + PCI_MSI_MADDR, pcs->msi_maddr);
|
|
|
|
|
|
|
|
if (bit64)
|
|
|
|
pci_conf_write(pc, tag,
|
|
|
|
off + PCI_MSI_MADDR64_HI, pcs->msi_maddr64_hi);
|
|
|
|
|
|
|
|
/* Data */
|
|
|
|
pci_conf_write(pc, tag,
|
|
|
|
off + (bit64 ? PCI_MSI_MDATA64 : PCI_MSI_MDATA),
|
|
|
|
pcs->msi_mdata);
|
|
|
|
|
|
|
|
/* Per-vector masking */
|
|
|
|
if (pvmask)
|
|
|
|
pci_conf_write(pc, tag,
|
|
|
|
off + (bit64 ? PCI_MSI_MASK64 : PCI_MSI_MASK),
|
|
|
|
pcs->msi_mask);
|
|
|
|
|
|
|
|
/* Write CTRL register in the end */
|
|
|
|
pci_conf_write(pc, tag, off + PCI_MSI_CTL, pcs->msi_ctl);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For MSI-X */
|
|
|
|
if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL) != 0)
|
|
|
|
pci_conf_write(pc, tag, off + PCI_MSIX_CTL, pcs->msix_ctl);
|
2005-01-27 00:49:00 +03:00
|
|
|
}
|
2005-06-28 04:28:41 +04:00
|
|
|
|
2006-06-18 03:34:26 +04:00
|
|
|
/*
|
|
|
|
* Power Management Capability (Rev 2.2)
|
|
|
|
*/
|
2007-12-09 23:27:42 +03:00
|
|
|
static int
|
|
|
|
pci_get_powerstate_int(pci_chipset_tag_t pc, pcitag_t tag , pcireg_t *state,
|
|
|
|
int offset)
|
2006-06-18 03:34:26 +04:00
|
|
|
{
|
2007-12-09 23:27:42 +03:00
|
|
|
pcireg_t value, now;
|
2006-06-18 03:34:26 +04:00
|
|
|
|
|
|
|
value = pci_conf_read(pc, tag, offset + PCI_PMCSR);
|
|
|
|
now = value & PCI_PMCSR_STATE_MASK;
|
|
|
|
switch (now) {
|
|
|
|
case PCI_PMCSR_STATE_D0:
|
|
|
|
case PCI_PMCSR_STATE_D1:
|
|
|
|
case PCI_PMCSR_STATE_D2:
|
|
|
|
case PCI_PMCSR_STATE_D3:
|
|
|
|
*state = now;
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-12-09 23:27:42 +03:00
|
|
|
pci_get_powerstate(pci_chipset_tag_t pc, pcitag_t tag , pcireg_t *state)
|
2006-06-18 03:34:26 +04:00
|
|
|
{
|
|
|
|
int offset;
|
2007-12-09 23:27:42 +03:00
|
|
|
pcireg_t value;
|
2006-06-18 03:34:26 +04:00
|
|
|
|
|
|
|
if (!pci_get_capability(pc, tag, PCI_CAP_PWRMGMT, &offset, &value))
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
return pci_get_powerstate_int(pc, tag, state, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pci_set_powerstate_int(pci_chipset_tag_t pc, pcitag_t tag, pcireg_t state,
|
|
|
|
int offset, pcireg_t cap_reg)
|
|
|
|
{
|
|
|
|
pcireg_t value, cap, now;
|
|
|
|
|
|
|
|
cap = cap_reg >> PCI_PMCR_SHIFT;
|
2006-06-18 03:34:26 +04:00
|
|
|
value = pci_conf_read(pc, tag, offset + PCI_PMCSR);
|
|
|
|
now = value & PCI_PMCSR_STATE_MASK;
|
|
|
|
value &= ~PCI_PMCSR_STATE_MASK;
|
|
|
|
|
|
|
|
if (now == state)
|
|
|
|
return 0;
|
|
|
|
switch (state) {
|
|
|
|
case PCI_PMCSR_STATE_D0:
|
|
|
|
break;
|
|
|
|
case PCI_PMCSR_STATE_D1:
|
2007-12-09 23:27:42 +03:00
|
|
|
if (now == PCI_PMCSR_STATE_D2 || now == PCI_PMCSR_STATE_D3) {
|
|
|
|
printf("invalid transition from %d to D1\n", (int)now);
|
2006-06-18 03:34:26 +04:00
|
|
|
return EINVAL;
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
|
|
|
if (!(cap & PCI_PMCR_D1SUPP)) {
|
|
|
|
printf("D1 not supported\n");
|
2006-06-18 03:34:26 +04:00
|
|
|
return EOPNOTSUPP;
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
2006-06-18 03:34:26 +04:00
|
|
|
break;
|
|
|
|
case PCI_PMCSR_STATE_D2:
|
2007-12-09 23:27:42 +03:00
|
|
|
if (now == PCI_PMCSR_STATE_D3) {
|
|
|
|
printf("invalid transition from %d to D2\n", (int)now);
|
2006-06-18 03:34:26 +04:00
|
|
|
return EINVAL;
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
|
|
|
if (!(cap & PCI_PMCR_D2SUPP)) {
|
|
|
|
printf("D2 not supported\n");
|
2006-06-18 03:34:26 +04:00
|
|
|
return EOPNOTSUPP;
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
2006-06-18 03:34:26 +04:00
|
|
|
break;
|
|
|
|
case PCI_PMCSR_STATE_D3:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2008-02-23 03:27:53 +03:00
|
|
|
value |= state;
|
2006-06-18 03:34:26 +04:00
|
|
|
pci_conf_write(pc, tag, offset + PCI_PMCSR, value);
|
2008-02-22 01:02:22 +03:00
|
|
|
/* delay according to pcipm1.2, ch. 5.6.1 */
|
2008-02-23 03:27:53 +03:00
|
|
|
if (state == PCI_PMCSR_STATE_D3 || now == PCI_PMCSR_STATE_D3)
|
2008-01-29 01:48:43 +03:00
|
|
|
DELAY(10000);
|
2008-02-23 03:27:53 +03:00
|
|
|
else if (state == PCI_PMCSR_STATE_D2 || now == PCI_PMCSR_STATE_D2)
|
2008-01-29 01:48:43 +03:00
|
|
|
DELAY(200);
|
|
|
|
|
2006-06-18 03:34:26 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
int
|
|
|
|
pci_set_powerstate(pci_chipset_tag_t pc, pcitag_t tag, pcireg_t state)
|
|
|
|
{
|
|
|
|
int offset;
|
|
|
|
pcireg_t value;
|
|
|
|
|
|
|
|
if (!pci_get_capability(pc, tag, PCI_CAP_PWRMGMT, &offset, &value)) {
|
|
|
|
printf("pci_set_powerstate not supported\n");
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pci_set_powerstate_int(pc, tag, state, offset, value);
|
|
|
|
}
|
|
|
|
|
2006-06-18 03:34:26 +04:00
|
|
|
int
|
2008-03-21 10:47:43 +03:00
|
|
|
pci_activate(pci_chipset_tag_t pc, pcitag_t tag, device_t dev,
|
|
|
|
int (*wakefun)(pci_chipset_tag_t, pcitag_t, device_t, pcireg_t))
|
2006-06-18 03:34:26 +04:00
|
|
|
{
|
|
|
|
pcireg_t pmode;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if ((error = pci_get_powerstate(pc, tag, &pmode)))
|
|
|
|
return error;
|
|
|
|
|
|
|
|
switch (pmode) {
|
|
|
|
case PCI_PMCSR_STATE_D0:
|
|
|
|
break;
|
|
|
|
case PCI_PMCSR_STATE_D3:
|
|
|
|
if (wakefun == NULL) {
|
|
|
|
/*
|
|
|
|
* The card has lost all configuration data in
|
|
|
|
* this state, so punt.
|
|
|
|
*/
|
2008-03-21 10:47:43 +03:00
|
|
|
aprint_error_dev(dev,
|
|
|
|
"unable to wake up from power state D3\n");
|
2006-06-18 03:34:26 +04:00
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
/*FALLTHROUGH*/
|
|
|
|
default:
|
|
|
|
if (wakefun) {
|
2008-03-21 10:47:43 +03:00
|
|
|
error = (*wakefun)(pc, tag, dev, pmode);
|
2006-06-18 03:34:26 +04:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
}
|
2008-03-21 10:47:43 +03:00
|
|
|
aprint_normal_dev(dev, "waking up from power state D%d\n",
|
|
|
|
pmode);
|
2006-06-18 03:34:26 +04:00
|
|
|
if ((error = pci_set_powerstate(pc, tag, PCI_PMCSR_STATE_D0)))
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2006-11-16 04:32:37 +03:00
|
|
|
pci_activate_null(pci_chipset_tag_t pc, pcitag_t tag,
|
2008-03-21 10:47:43 +03:00
|
|
|
device_t dev, pcireg_t state)
|
2006-06-18 03:34:26 +04:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
struct pci_child_power {
|
|
|
|
struct pci_conf_state p_pciconf;
|
|
|
|
pci_chipset_tag_t p_pc;
|
|
|
|
pcitag_t p_tag;
|
|
|
|
bool p_has_pm;
|
|
|
|
int p_pm_offset;
|
|
|
|
pcireg_t p_pm_cap;
|
|
|
|
pcireg_t p_class;
|
2011-02-01 22:37:37 +03:00
|
|
|
pcireg_t p_csr;
|
2007-12-09 23:27:42 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static bool
|
2010-02-25 01:37:54 +03:00
|
|
|
pci_child_suspend(device_t dv, const pmf_qual_t *qual)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
|
|
|
struct pci_child_power *priv = device_pmf_bus_private(dv);
|
2008-02-22 01:02:22 +03:00
|
|
|
pcireg_t ocsr, csr;
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
pci_conf_capture(priv->p_pc, priv->p_tag, &priv->p_pciconf);
|
|
|
|
|
2008-02-22 01:02:22 +03:00
|
|
|
if (!priv->p_has_pm)
|
|
|
|
return true; /* ??? hopefully handled by ACPI */
|
|
|
|
if (PCI_CLASS(priv->p_class) == PCI_CLASS_DISPLAY)
|
|
|
|
return true; /* XXX */
|
|
|
|
|
|
|
|
/* disable decoding and busmastering, see pcipm1.2 ch. 8.2.1 */
|
|
|
|
ocsr = pci_conf_read(priv->p_pc, priv->p_tag, PCI_COMMAND_STATUS_REG);
|
|
|
|
csr = ocsr & ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE
|
|
|
|
| PCI_COMMAND_MASTER_ENABLE);
|
|
|
|
pci_conf_write(priv->p_pc, priv->p_tag, PCI_COMMAND_STATUS_REG, csr);
|
|
|
|
if (pci_set_powerstate_int(priv->p_pc, priv->p_tag,
|
2007-12-09 23:27:42 +03:00
|
|
|
PCI_PMCSR_STATE_D3, priv->p_pm_offset, priv->p_pm_cap)) {
|
2008-02-22 01:02:22 +03:00
|
|
|
pci_conf_write(priv->p_pc, priv->p_tag,
|
|
|
|
PCI_COMMAND_STATUS_REG, ocsr);
|
2007-12-09 23:27:42 +03:00
|
|
|
aprint_error_dev(dv, "unsupported state, continuing.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-12-15 08:38:45 +03:00
|
|
|
static void
|
|
|
|
pci_pme_check_and_clear(device_t dv, pci_chipset_tag_t pc, pcitag_t tag,
|
|
|
|
int off)
|
|
|
|
{
|
|
|
|
pcireg_t pmcsr;
|
|
|
|
|
|
|
|
pmcsr = pci_conf_read(pc, tag, off + PCI_PMCSR);
|
|
|
|
|
|
|
|
if (pmcsr & PCI_PMCSR_PME_STS) {
|
|
|
|
/* Clear W1C bit */
|
|
|
|
pmcsr |= PCI_PMCSR_PME_STS;
|
|
|
|
pci_conf_write(pc, tag, off + PCI_PMCSR, pmcsr);
|
|
|
|
aprint_verbose_dev(dv, "Clear PME# now\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
static bool
|
2010-02-25 01:37:54 +03:00
|
|
|
pci_child_resume(device_t dv, const pmf_qual_t *qual)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
|
|
|
struct pci_child_power *priv = device_pmf_bus_private(dv);
|
|
|
|
|
2018-12-15 08:38:45 +03:00
|
|
|
if (priv->p_has_pm) {
|
|
|
|
if (pci_set_powerstate_int(priv->p_pc, priv->p_tag,
|
|
|
|
PCI_PMCSR_STATE_D0, priv->p_pm_offset, priv->p_pm_cap)) {
|
|
|
|
aprint_error_dev(dv,
|
|
|
|
"unsupported state, continuing.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
pci_pme_check_and_clear(dv, priv->p_pc, priv->p_tag,
|
|
|
|
priv->p_pm_offset);
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pci_conf_restore(priv->p_pc, priv->p_tag, &priv->p_pciconf);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-02-28 17:25:12 +03:00
|
|
|
static bool
|
|
|
|
pci_child_shutdown(device_t dv, int how)
|
|
|
|
{
|
|
|
|
struct pci_child_power *priv = device_pmf_bus_private(dv);
|
|
|
|
pcireg_t csr;
|
|
|
|
|
2011-02-01 22:37:37 +03:00
|
|
|
/* restore original bus-mastering state */
|
2008-02-28 17:25:12 +03:00
|
|
|
csr = pci_conf_read(priv->p_pc, priv->p_tag, PCI_COMMAND_STATUS_REG);
|
|
|
|
csr &= ~PCI_COMMAND_MASTER_ENABLE;
|
2011-02-01 22:37:37 +03:00
|
|
|
csr |= priv->p_csr & PCI_COMMAND_MASTER_ENABLE;
|
2008-02-28 17:25:12 +03:00
|
|
|
pci_conf_write(priv->p_pc, priv->p_tag, PCI_COMMAND_STATUS_REG, csr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
static void
|
|
|
|
pci_child_deregister(device_t dv)
|
|
|
|
{
|
|
|
|
struct pci_child_power *priv = device_pmf_bus_private(dv);
|
|
|
|
|
|
|
|
free(priv, M_DEVBUF);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
pci_child_register(device_t child)
|
|
|
|
{
|
|
|
|
device_t self = device_parent(child);
|
|
|
|
struct pci_softc *sc = device_private(self);
|
|
|
|
struct pci_child_power *priv;
|
|
|
|
int device, function, off;
|
|
|
|
pcireg_t reg;
|
|
|
|
|
|
|
|
priv = malloc(sizeof(*priv), M_DEVBUF, M_WAITOK);
|
|
|
|
|
|
|
|
device = device_locator(child, PCICF_DEV);
|
|
|
|
function = device_locator(child, PCICF_FUNCTION);
|
|
|
|
|
|
|
|
priv->p_pc = sc->sc_pc;
|
|
|
|
priv->p_tag = pci_make_tag(priv->p_pc, sc->sc_bus, device,
|
|
|
|
function);
|
|
|
|
priv->p_class = pci_conf_read(priv->p_pc, priv->p_tag, PCI_CLASS_REG);
|
2011-02-01 22:37:37 +03:00
|
|
|
priv->p_csr = pci_conf_read(priv->p_pc, priv->p_tag,
|
|
|
|
PCI_COMMAND_STATUS_REG);
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
if (pci_get_capability(priv->p_pc, priv->p_tag,
|
|
|
|
PCI_CAP_PWRMGMT, &off, ®)) {
|
|
|
|
priv->p_has_pm = true;
|
|
|
|
priv->p_pm_offset = off;
|
|
|
|
priv->p_pm_cap = reg;
|
2018-12-15 08:38:45 +03:00
|
|
|
pci_pme_check_and_clear(child, priv->p_pc, priv->p_tag, off);
|
2007-12-09 23:27:42 +03:00
|
|
|
} else {
|
|
|
|
priv->p_has_pm = false;
|
|
|
|
priv->p_pm_offset = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
device_pmf_bus_register(child, priv, pci_child_suspend,
|
2008-02-28 17:25:12 +03:00
|
|
|
pci_child_resume, pci_child_shutdown, pci_child_deregister);
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2011-08-29 18:47:07 +04:00
|
|
|
|
|
|
|
MODULE(MODULE_CLASS_DRIVER, pci, NULL);
|
|
|
|
|
|
|
|
static int
|
|
|
|
pci_modcmd(modcmd_t cmd, void *priv)
|
|
|
|
{
|
|
|
|
if (cmd == MODULE_CMD_INIT || cmd == MODULE_CMD_FINI)
|
|
|
|
return 0;
|
|
|
|
return ENOTTY;
|
|
|
|
}
|