[Midnightbsd-cvs] src [10087] trunk/sys/dev/pci: sync pci
laffer1 at midnightbsd.org
laffer1 at midnightbsd.org
Sun May 27 19:28:52 EDT 2018
Revision: 10087
http://svnweb.midnightbsd.org/src/?rev=10087
Author: laffer1
Date: 2018-05-27 19:28:52 -0400 (Sun, 27 May 2018)
Log Message:
-----------
sync pci
Modified Paths:
--------------
trunk/sys/dev/pci/eisa_pci.c
trunk/sys/dev/pci/fixup_pci.c
trunk/sys/dev/pci/hostb_pci.c
trunk/sys/dev/pci/ignore_pci.c
trunk/sys/dev/pci/isa_pci.c
trunk/sys/dev/pci/pci.c
trunk/sys/dev/pci/pci_if.m
trunk/sys/dev/pci/pci_pci.c
trunk/sys/dev/pci/pci_private.h
trunk/sys/dev/pci/pci_subr.c
trunk/sys/dev/pci/pci_user.c
trunk/sys/dev/pci/pcib_if.m
trunk/sys/dev/pci/pcib_private.h
trunk/sys/dev/pci/pcireg.h
trunk/sys/dev/pci/pcivar.h
trunk/sys/dev/pci/vga_pci.c
Added Paths:
-----------
trunk/sys/dev/pci/pcib_support.c
Property Changed:
----------------
trunk/sys/dev/pci/pci_if.m
trunk/sys/dev/pci/pcib_if.m
Modified: trunk/sys/dev/pci/eisa_pci.c
===================================================================
--- trunk/sys/dev/pci/eisa_pci.c 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/eisa_pci.c 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier
* Copyright (c) 2000 Michael Smith <msmith at freebsd.org>
@@ -29,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/pci/eisa_pci.c 227843 2011-11-22 21:28:20Z marius $");
/*
* PCI:EISA bridge support
Modified: trunk/sys/dev/pci/fixup_pci.c
===================================================================
--- trunk/sys/dev/pci/fixup_pci.c 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/fixup_pci.c 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier
* Copyright (c) 2000 Michael Smith <msmith at freebsd.org>
@@ -29,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/pci/fixup_pci.c 254207 2013-08-11 06:57:57Z rpaulo $");
#include <sys/param.h>
#include <sys/kernel.h>
@@ -92,13 +93,13 @@
pmccfg = pci_read_config(dev, 0x50, 2);
#if defined(SMP)
if (pmccfg & 0x8000) {
- printf("Correcting Natoma config for SMP\n");
+ device_printf(dev, "correcting Natoma config for SMP\n");
pmccfg &= ~0x8000;
pci_write_config(dev, 0x50, pmccfg, 2);
}
#else
if ((pmccfg & 0x8000) == 0) {
- printf("Correcting Natoma config for non-SMP\n");
+ device_printf(dev, "correcting Natoma config for non-SMP\n");
pmccfg |= 0x8000;
pci_write_config(dev, 0x50, pmccfg, 2);
}
@@ -132,7 +133,8 @@
pci_get_function(dev) == 0) {
val = pci_read_config(dev, 0x6c, 4);
if (val & 0x000e0000) {
- printf("Correcting nForce2 C1 CPU disconnect hangs\n");
+ device_printf(dev,
+ "correcting nForce2 C1 CPU disconnect hangs\n");
val &= ~0x000e0000;
pci_write_config(dev, 0x6c, val, 4);
}
Modified: trunk/sys/dev/pci/hostb_pci.c
===================================================================
--- trunk/sys/dev/pci/hostb_pci.c 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/hostb_pci.c 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*
* Copyright (c) 1997, Stefan Esser <se at freebsd.org>
* All rights reserved.
@@ -25,7 +26,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/pci/hostb_pci.c 232472 2012-03-03 18:08:57Z jhb $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -197,6 +198,14 @@
}
static int
+pci_hostb_find_cap(device_t dev, device_t child, int capability,
+ int *capreg)
+{
+
+ return (pci_find_cap(dev, capability, capreg));
+}
+
+static int
pci_hostb_find_extcap(device_t dev, device_t child, int capability,
int *capreg)
{
@@ -204,6 +213,14 @@
return (pci_find_extcap(dev, capability, capreg));
}
+static int
+pci_hostb_find_htcap(device_t dev, device_t child, int capability,
+ int *capreg)
+{
+
+ return (pci_find_htcap(dev, capability, capreg));
+}
+
static device_method_t pci_hostb_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, pci_hostb_probe),
@@ -233,7 +250,9 @@
DEVMETHOD(pci_get_powerstate, pci_hostb_get_powerstate),
DEVMETHOD(pci_set_powerstate, pci_hostb_set_powerstate),
DEVMETHOD(pci_assign_interrupt, pci_hostb_assign_interrupt),
+ DEVMETHOD(pci_find_cap, pci_hostb_find_cap),
DEVMETHOD(pci_find_extcap, pci_hostb_find_extcap),
+ DEVMETHOD(pci_find_htcap, pci_hostb_find_htcap),
{ 0, 0 }
};
Modified: trunk/sys/dev/pci/ignore_pci.c
===================================================================
--- trunk/sys/dev/pci/ignore_pci.c 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/ignore_pci.c 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 2000 Michael Smith <msmith at freebsd.org>
* Copyright (c) 2000 BSDi
@@ -26,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/pci/ignore_pci.c 129876 2004-05-30 17:57:46Z phk $");
/*
* 'Ignore' driver - eats devices that show up errnoeously on PCI
Modified: trunk/sys/dev/pci/isa_pci.c
===================================================================
--- trunk/sys/dev/pci/isa_pci.c 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/isa_pci.c 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier
* Copyright (c) 2000 Michael Smith <msmith at freebsd.org>
@@ -29,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/pci/isa_pci.c 228496 2011-12-14 12:34:02Z jhb $");
/*
* PCI:ISA bridge support
Modified: trunk/sys/dev/pci/pci.c
===================================================================
--- trunk/sys/dev/pci/pci.c 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/pci.c 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 1997, Stefan Esser <se at freebsd.org>
* Copyright (c) 2000, Michael Smith <msmith at freebsd.org>
@@ -27,7 +28,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/pci/pci.c 330938 2018-03-14 19:04:40Z jhb $");
#include "opt_bus.h"
@@ -35,6 +36,7 @@
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/module.h>
+#include <sys/limits.h>
#include <sys/linker.h>
#include <sys/fcntl.h>
#include <sys/conf.h>
@@ -70,25 +72,11 @@
#include "pcib_if.h"
#include "pci_if.h"
-/*
- * XXX: Due to a limitation of the bus_dma_tag_create() API, we cannot
- * specify a 4GB boundary on 32-bit targets. Usually this does not
- * matter as it is ok to use a boundary of 0 on these systems.
- * However, in the case of PAE, DMA addresses can cross a 4GB
- * boundary, so as a workaround use a 2GB boundary.
- */
-#if (BUS_SPACE_MAXADDR > 0xFFFFFFFF)
-#ifdef PAE
-#define PCI_DMA_BOUNDARY 0x80000000
-#else
-#define PCI_DMA_BOUNDARY 0x100000000
-#endif
-#endif
-
#define PCIR_IS_BIOS(cfg, reg) \
(((cfg)->hdrtype == PCIM_HDRTYPE_NORMAL && reg == PCIR_BIOS) || \
((cfg)->hdrtype == PCIM_HDRTYPE_BRIDGE && reg == PCIR_BIOS_1))
+static int pci_has_quirk(uint32_t devid, int quirk);
static pci_addr_t pci_mapbase(uint64_t mapreg);
static const char *pci_maptype(uint64_t mapreg);
static int pci_mapsize(uint64_t testval);
@@ -106,11 +94,13 @@
struct resource_list *rl, int force, int prefetch);
static int pci_probe(device_t dev);
static int pci_attach(device_t dev);
+#ifdef PCI_RES_BUS
+static int pci_detach(device_t dev);
+#endif
static void pci_load_vendor_data(void);
static int pci_describe_parse_line(char **ptr, int *vendor,
int *device, char **desc);
static char *pci_describe_device(device_t dev);
-static bus_dma_tag_t pci_get_dma_tag(device_t bus, device_t dev);
static int pci_modevent(module_t mod, int what, void *arg);
static void pci_hdrtypedata(device_t pcib, int b, int s, int f,
pcicfgregs *cfg);
@@ -122,24 +112,26 @@
int reg, uint32_t data);
#endif
static void pci_read_vpd(device_t pcib, pcicfgregs *cfg);
-static void pci_disable_msi(device_t dev);
-static void pci_enable_msi(device_t dev, uint64_t address,
- uint16_t data);
-static void pci_enable_msix(device_t dev, u_int index,
- uint64_t address, uint32_t data);
static void pci_mask_msix(device_t dev, u_int index);
static void pci_unmask_msix(device_t dev, u_int index);
static int pci_msi_blacklisted(void);
+static int pci_msix_blacklisted(void);
static void pci_resume_msi(device_t dev);
static void pci_resume_msix(device_t dev);
static int pci_remap_intr_method(device_t bus, device_t dev,
u_int irq);
+static uint16_t pci_get_rid_method(device_t dev, device_t child);
+
static device_method_t pci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, pci_probe),
DEVMETHOD(device_attach, pci_attach),
+#ifdef PCI_RES_BUS
+ DEVMETHOD(device_detach, pci_detach),
+#else
DEVMETHOD(device_detach, bus_generic_detach),
+#endif
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, pci_suspend),
DEVMETHOD(device_resume, pci_resume),
@@ -160,9 +152,11 @@
DEVMETHOD(bus_delete_resource, pci_delete_resource),
DEVMETHOD(bus_alloc_resource, pci_alloc_resource),
DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource),
- DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
+ DEVMETHOD(bus_release_resource, pci_release_resource),
DEVMETHOD(bus_activate_resource, pci_activate_resource),
DEVMETHOD(bus_deactivate_resource, pci_deactivate_resource),
+ DEVMETHOD(bus_child_deleted, pci_child_deleted),
+ DEVMETHOD(bus_child_detached, pci_child_detached),
DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method),
DEVMETHOD(bus_child_location_str, pci_child_location_str_method),
DEVMETHOD(bus_remap_intr, pci_remap_intr_method),
@@ -179,13 +173,22 @@
DEVMETHOD(pci_get_powerstate, pci_get_powerstate_method),
DEVMETHOD(pci_set_powerstate, pci_set_powerstate_method),
DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method),
+ DEVMETHOD(pci_find_cap, pci_find_cap_method),
DEVMETHOD(pci_find_extcap, pci_find_extcap_method),
+ DEVMETHOD(pci_find_htcap, pci_find_htcap_method),
DEVMETHOD(pci_alloc_msi, pci_alloc_msi_method),
DEVMETHOD(pci_alloc_msix, pci_alloc_msix_method),
+ DEVMETHOD(pci_enable_msi, pci_enable_msi_method),
+ DEVMETHOD(pci_enable_msix, pci_enable_msix_method),
+ DEVMETHOD(pci_disable_msi, pci_disable_msi_method),
DEVMETHOD(pci_remap_msix, pci_remap_msix_method),
DEVMETHOD(pci_release_msi, pci_release_msi_method),
DEVMETHOD(pci_msi_count, pci_msi_count_method),
DEVMETHOD(pci_msix_count, pci_msix_count_method),
+ DEVMETHOD(pci_msix_pba_bar, pci_msix_pba_bar_method),
+ DEVMETHOD(pci_msix_table_bar, pci_msix_table_bar_method),
+ DEVMETHOD(pci_get_rid, pci_get_rid_method),
+ DEVMETHOD(pci_child_added, pci_child_added_method),
DEVMETHOD_END
};
@@ -193,7 +196,7 @@
DEFINE_CLASS_0(pci, pci_driver, pci_methods, sizeof(struct pci_softc));
static devclass_t pci_devclass;
-DRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0);
+DRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, NULL);
MODULE_VERSION(pci, 1);
static char *pci_vendordata;
@@ -203,15 +206,17 @@
uint32_t devid; /* Vendor/device of the card */
int type;
#define PCI_QUIRK_MAP_REG 1 /* PCI map register in weird place */
-#define PCI_QUIRK_DISABLE_MSI 2 /* MSI/MSI-X doesn't work */
+#define PCI_QUIRK_DISABLE_MSI 2 /* Neither MSI nor MSI-X work */
#define PCI_QUIRK_ENABLE_MSI_VM 3 /* Older chipset in VM where MSI works */
#define PCI_QUIRK_UNMAP_REG 4 /* Ignore PCI map register */
+#define PCI_QUIRK_DISABLE_MSIX 5 /* MSI-X doesn't work */
+#define PCI_QUIRK_MSI_INTX_BUG 6 /* PCIM_CMD_INTxDIS disables MSI */
int arg1;
int arg2;
};
static const struct pci_quirk pci_quirks[] = {
- /* The Intel 82371AB and 82443MX has a map register at offset 0x90. */
+ /* The Intel 82371AB and 82443MX have a map register at offset 0x90. */
{ 0x71138086, PCI_QUIRK_MAP_REG, 0x90, 0 },
{ 0x719b8086, PCI_QUIRK_MAP_REG, 0x90, 0 },
/* As does the Serverworks OSB4 (the SMBus mapping register) */
@@ -246,8 +251,8 @@
* MSI-X allocation doesn't work properly for devices passed through
* by VMware up to at least ESXi 5.1.
*/
- { 0x079015ad, PCI_QUIRK_DISABLE_MSI, 0, 0 }, /* PCI/PCI-X */
- { 0x07a015ad, PCI_QUIRK_DISABLE_MSI, 0, 0 }, /* PCIe */
+ { 0x079015ad, PCI_QUIRK_DISABLE_MSIX, 0, 0 }, /* PCI/PCI-X */
+ { 0x07a015ad, PCI_QUIRK_DISABLE_MSIX, 0, 0 }, /* PCIe */
/*
* Some virtualization environments emulate an older chipset
@@ -265,6 +270,28 @@
*/
{ 0x43851002, PCI_QUIRK_UNMAP_REG, 0x14, 0 },
+ /*
+ * Atheros AR8161/AR8162/E2200/E2400/E2500 Ethernet controllers have
+ * a bug that MSI interrupt does not assert if PCIM_CMD_INTxDIS bit
+ * of the command register is set.
+ */
+ { 0x10911969, PCI_QUIRK_MSI_INTX_BUG, 0, 0 },
+ { 0xE0911969, PCI_QUIRK_MSI_INTX_BUG, 0, 0 },
+ { 0xE0A11969, PCI_QUIRK_MSI_INTX_BUG, 0, 0 },
+ { 0xE0B11969, PCI_QUIRK_MSI_INTX_BUG, 0, 0 },
+ { 0x10901969, PCI_QUIRK_MSI_INTX_BUG, 0, 0 },
+
+ /*
+ * Broadcom BCM5714(S)/BCM5715(S)/BCM5780(S) Ethernet MACs don't
+ * issue MSI interrupts with PCIM_CMD_INTxDIS set either.
+ */
+ { 0x166814e4, PCI_QUIRK_MSI_INTX_BUG, 0, 0 }, /* BCM5714 */
+ { 0x166914e4, PCI_QUIRK_MSI_INTX_BUG, 0, 0 }, /* BCM5714S */
+ { 0x166a14e4, PCI_QUIRK_MSI_INTX_BUG, 0, 0 }, /* BCM5780 */
+ { 0x166b14e4, PCI_QUIRK_MSI_INTX_BUG, 0, 0 }, /* BCM5780S */
+ { 0x167814e4, PCI_QUIRK_MSI_INTX_BUG, 0, 0 }, /* BCM5715 */
+ { 0x167914e4, PCI_QUIRK_MSI_INTX_BUG, 0, 0 }, /* BCM5715S */
+
{ 0 }
};
@@ -289,6 +316,12 @@
enable these bits correctly. We'd like to do this all the time, but there\n\
are some peripherals that this causes problems with.");
+static int pci_do_realloc_bars = 0;
+TUNABLE_INT("hw.pci.realloc_bars", &pci_do_realloc_bars);
+SYSCTL_INT(_hw_pci, OID_AUTO, realloc_bars, CTLFLAG_RW,
+ &pci_do_realloc_bars, 0,
+ "Attempt to allocate a new range for any BARs whose original firmware-assigned ranges fail to allocate during the initial device scan.");
+
static int pci_do_power_nodriver = 0;
TUNABLE_INT("hw.pci.do_power_nodriver", &pci_do_power_nodriver);
SYSCTL_INT(_hw_pci, OID_AUTO, do_power_nodriver, CTLFLAG_RW,
@@ -320,10 +353,15 @@
SYSCTL_INT(_hw_pci, OID_AUTO, enable_msix, CTLFLAG_RW, &pci_do_msix, 1,
"Enable support for MSI-X interrupts");
+static int pci_msix_rewrite_table = 0;
+SYSCTL_INT(_hw_pci, OID_AUTO, msix_rewrite_table, CTLFLAG_RWTUN,
+ &pci_msix_rewrite_table, 0,
+ "Rewrite entire MSI-X table when updating MSI-X entries");
+
static int pci_honor_msi_blacklist = 1;
TUNABLE_INT("hw.pci.honor_msi_blacklist", &pci_honor_msi_blacklist);
SYSCTL_INT(_hw_pci, OID_AUTO, honor_msi_blacklist, CTLFLAG_RD,
- &pci_honor_msi_blacklist, 1, "Honor chipset blacklist for MSI");
+ &pci_honor_msi_blacklist, 1, "Honor chipset blacklist for MSI/MSI-X");
#if defined(__i386__) || defined(__amd64__)
static int pci_usb_takeover = 1;
@@ -336,6 +374,35 @@
Disable this if you depend on BIOS emulation of USB devices, that is\n\
you use USB devices (like keyboard or mouse) but do not load USB drivers");
+static int pci_clear_bars;
+TUNABLE_INT("hw.pci.clear_bars", &pci_clear_bars);
+SYSCTL_INT(_hw_pci, OID_AUTO, clear_bars, CTLFLAG_RDTUN, &pci_clear_bars, 0,
+ "Ignore firmware-assigned resources for BARs.");
+
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+static int pci_clear_buses;
+TUNABLE_INT("hw.pci.clear_buses", &pci_clear_buses);
+SYSCTL_INT(_hw_pci, OID_AUTO, clear_buses, CTLFLAG_RDTUN, &pci_clear_buses, 0,
+ "Ignore firmware-assigned bus numbers.");
+#endif
+
+static int pci_enable_ari = 1;
+TUNABLE_INT("hw.pci.enable_ari", &pci_enable_ari);
+SYSCTL_INT(_hw_pci, OID_AUTO, enable_ari, CTLFLAG_RDTUN, &pci_enable_ari,
+ 0, "Enable support for PCIe Alternative RID Interpretation");
+
+static int
+pci_has_quirk(uint32_t devid, int quirk)
+{
+ const struct pci_quirk *q;
+
+ for (q = &pci_quirks[0]; q->devid; q++) {
+ if (q->devid == devid && q->type == quirk)
+ return (1);
+ }
+ return (0);
+}
+
/* Find a device_t by bus/slot/function in domain 0 */
device_t
@@ -529,6 +596,8 @@
case PCIM_HDRTYPE_NORMAL:
cfg->subvendor = REG(PCIR_SUBVEND_0, 2);
cfg->subdevice = REG(PCIR_SUBDEV_0, 2);
+ cfg->mingnt = REG(PCIR_MINGNT, 1);
+ cfg->maxlat = REG(PCIR_MAXLAT, 1);
cfg->nummaps = PCI_MAXMAPS_0;
break;
case PCIM_HDRTYPE_BRIDGE:
@@ -558,8 +627,6 @@
if (REG(PCIR_DEVVENDOR, 4) != 0xfffffffful) {
devlist_entry = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
- if (devlist_entry == NULL)
- return (NULL);
cfg = &devlist_entry->cfg;
@@ -581,9 +648,6 @@
cfg->intpin = REG(PCIR_INTPIN, 1);
cfg->intline = REG(PCIR_INTLINE, 1);
- cfg->mingnt = REG(PCIR_MINGNT, 1);
- cfg->maxlat = REG(PCIR_MAXLAT, 1);
-
cfg->mfdev = (cfg->hdrtype & PCIM_MFDEV) != 0;
cfg->hdrtype &= ~PCIM_MFDEV;
STAILQ_INIT(&cfg->maps);
@@ -744,6 +808,7 @@
if ((cfg->hdrtype & PCIM_HDRTYPE) ==
PCIM_HDRTYPE_BRIDGE)
pcix_chipset = 1;
+ cfg->pcix.pcix_location = ptr;
break;
case PCIY_EXPRESS: /* PCI-express */
/*
@@ -751,6 +816,9 @@
* at least one PCI-express device.
*/
pcie_chipset = 1;
+ cfg->pcie.pcie_location = ptr;
+ val = REG(ptr + PCIER_FLAGS, 2);
+ cfg->pcie.pcie_type = val & PCIEM_FLAGS_TYPE;
break;
default:
break;
@@ -907,10 +975,9 @@
remain |= byte2 << 8;
if (remain > (0x7f*4 - vrs.off)) {
state = -1;
- printf(
- "pci%d:%d:%d:%d: invalid VPD data, remain %#x\n",
- cfg->domain, cfg->bus, cfg->slot,
- cfg->func, remain);
+ pci_printf(cfg,
+ "invalid VPD data, remain %#x\n",
+ remain);
}
name = byte & 0x7f;
} else {
@@ -974,7 +1041,7 @@
state = -2;
break;
}
- dflen = byte2;
+ cfg->vpd.vpd_ros[off].len = dflen = byte2;
if (dflen == 0 &&
strncmp(cfg->vpd.vpd_ros[off].keyword, "RV",
2) == 0) {
@@ -982,10 +1049,8 @@
* if this happens, we can't trust the rest
* of the VPD.
*/
- printf(
- "pci%d:%d:%d:%d: bad keyword length: %d\n",
- cfg->domain, cfg->bus, cfg->slot,
- cfg->func, dflen);
+ pci_printf(cfg, "bad keyword length: %d\n",
+ dflen);
cksumvalid = 0;
state = -1;
break;
@@ -1018,10 +1083,8 @@
cksumvalid = 1;
else {
if (bootverbose)
- printf(
- "pci%d:%d:%d:%d: bad VPD cksum, remain %hhu\n",
- cfg->domain, cfg->bus,
- cfg->slot, cfg->func,
+ pci_printf(cfg,
+ "bad VPD cksum, remain %hhu\n",
vrs.cksum);
cksumvalid = 0;
state = -1;
@@ -1099,9 +1162,7 @@
break;
default:
- printf("pci%d:%d:%d:%d: invalid state: %d\n",
- cfg->domain, cfg->bus, cfg->slot, cfg->func,
- state);
+ pci_printf(cfg, "invalid state: %d\n", state);
state = -1;
break;
}
@@ -1118,8 +1179,7 @@
}
if (state < -1) {
/* I/O error, clean up */
- printf("pci%d:%d:%d:%d: failed to read VPD data.\n",
- cfg->domain, cfg->bus, cfg->slot, cfg->func);
+ pci_printf(cfg, "failed to read VPD data.\n");
if (cfg->vpd.vpd_ident != NULL) {
free(cfg->vpd.vpd_ident, M_DEVBUF);
cfg->vpd.vpd_ident = NULL;
@@ -1175,13 +1235,67 @@
return (ENXIO);
}
+struct pcicfg_vpd *
+pci_fetch_vpd_list(device_t dev)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ pcicfgregs *cfg = &dinfo->cfg;
+
+ if (!cfg->vpd.vpd_cached && cfg->vpd.vpd_reg != 0)
+ pci_read_vpd(device_get_parent(device_get_parent(dev)), cfg);
+ return (&cfg->vpd);
+}
+
/*
- * Find the requested extended capability and return the offset in
- * configuration space via the pointer provided. The function returns
- * 0 on success and error code otherwise.
+ * Find the requested HyperTransport capability and return the offset
+ * in configuration space via the pointer provided. The function
+ * returns 0 on success and an error code otherwise.
*/
int
-pci_find_extcap_method(device_t dev, device_t child, int capability,
+pci_find_htcap_method(device_t dev, device_t child, int capability, int *capreg)
+{
+ int ptr, error;
+ uint16_t val;
+
+ error = pci_find_cap(child, PCIY_HT, &ptr);
+ if (error)
+ return (error);
+
+ /*
+ * Traverse the capabilities list checking each HT capability
+ * to see if it matches the requested HT capability.
+ */
+ while (ptr != 0) {
+ val = pci_read_config(child, ptr + PCIR_HT_COMMAND, 2);
+ if (capability == PCIM_HTCAP_SLAVE ||
+ capability == PCIM_HTCAP_HOST)
+ val &= 0xe000;
+ else
+ val &= PCIM_HTCMD_CAP_MASK;
+ if (val == capability) {
+ if (capreg != NULL)
+ *capreg = ptr;
+ return (0);
+ }
+
+ /* Skip to the next HT capability. */
+ while (ptr != 0) {
+ ptr = pci_read_config(child, ptr + PCICAP_NEXTPTR, 1);
+ if (pci_read_config(child, ptr + PCICAP_ID, 1) ==
+ PCIY_HT)
+ break;
+ }
+ }
+ return (ENOENT);
+}
+
+/*
+ * Find the requested capability and return the offset in
+ * configuration space via the pointer provided. The function returns
+ * 0 on success and an error code otherwise.
+ */
+int
+pci_find_cap_method(device_t dev, device_t child, int capability,
int *capreg)
{
struct pci_devinfo *dinfo = device_get_ivars(child);
@@ -1229,10 +1343,47 @@
}
/*
+ * Find the requested extended capability and return the offset in
+ * configuration space via the pointer provided. The function returns
+ * 0 on success and an error code otherwise.
+ */
+int
+pci_find_extcap_method(device_t dev, device_t child, int capability,
+ int *capreg)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ pcicfgregs *cfg = &dinfo->cfg;
+ uint32_t ecap;
+ uint16_t ptr;
+
+ /* Only supported for PCI-express devices. */
+ if (cfg->pcie.pcie_location == 0)
+ return (ENXIO);
+
+ ptr = PCIR_EXTCAP;
+ ecap = pci_read_config(child, ptr, 4);
+ if (ecap == 0xffffffff || ecap == 0)
+ return (ENOENT);
+ for (;;) {
+ if (PCI_EXTCAP_ID(ecap) == capability) {
+ if (capreg != NULL)
+ *capreg = ptr;
+ return (0);
+ }
+ ptr = PCI_EXTCAP_NEXTPTR(ecap);
+ if (ptr == 0)
+ break;
+ ecap = pci_read_config(child, ptr, 4);
+ }
+
+ return (ENOENT);
+}
+
+/*
* Support for MSI-X message interrupts.
*/
-void
-pci_enable_msix(device_t dev, u_int index, uint64_t address, uint32_t data)
+static void
+pci_write_msix_entry(device_t dev, u_int index, uint64_t address, uint32_t data)
{
struct pci_devinfo *dinfo = device_get_ivars(dev);
struct pcicfg_msix *msix = &dinfo->cfg.msix;
@@ -1243,9 +1394,34 @@
bus_write_4(msix->msix_table_res, offset, address & 0xffffffff);
bus_write_4(msix->msix_table_res, offset + 4, address >> 32);
bus_write_4(msix->msix_table_res, offset + 8, data);
+}
+void
+pci_enable_msix_method(device_t dev, device_t child, u_int index,
+ uint64_t address, uint32_t data)
+{
+
+ if (pci_msix_rewrite_table) {
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ struct pcicfg_msix *msix = &dinfo->cfg.msix;
+
+ /*
+ * Some VM hosts require MSIX to be disabled in the
+ * control register before updating the MSIX table
+ * entries are allowed. It is not enough to only
+ * disable MSIX while updating a single entry. MSIX
+ * must be disabled while updating all entries in the
+ * table.
+ */
+ pci_write_config(child,
+ msix->msix_location + PCIR_MSIX_CTRL,
+ msix->msix_ctrl & ~PCIM_MSIXCTRL_MSIX_ENABLE, 2);
+ pci_resume_msix(child);
+ } else
+ pci_write_msix_entry(child, index, address, data);
+
/* Enable MSI -> HT mapping. */
- pci_ht_map_msi(dev, address);
+ pci_ht_map_msi(child, address);
}
void
@@ -1318,7 +1494,8 @@
if (mte->mte_vector == 0 || mte->mte_handlers == 0)
continue;
mv = &msix->msix_vectors[mte->mte_vector - 1];
- pci_enable_msix(dev, i, mv->mv_address, mv->mv_data);
+ pci_write_msix_entry(dev, i, mv->mv_address,
+ mv->mv_data);
pci_unmask_msix(dev, i);
}
}
@@ -1352,8 +1529,8 @@
if (cfg->msi.msi_alloc != 0 || cfg->msix.msix_alloc != 0)
return (ENXIO);
- /* If MSI is blacklisted for this system, fail. */
- if (pci_msi_blacklisted())
+ /* If MSI-X is blacklisted for this system, fail. */
+ if (pci_msix_blacklisted())
return (ENXIO);
/* MSI-X capability present? */
@@ -1496,7 +1673,7 @@
* 3. Call the three vectors allocated by pci_alloc_msix() A, B, and
* C. After the call to pci_alloc_msix(), the device will be setup to
* have an MSI-X table of ABC--- (where - means no vector assigned).
- * If the driver ten passes a vector array of { 1, 0, 1, 2, 0, 2 },
+ * If the driver then passes a vector array of { 1, 0, 1, 2, 0, 2 },
* then the MSI-X table will look like A-AB-B, and the 'C' vector will
* be freed back to the system. This device will also have valid
* SYS_RES_IRQ rids of 1, 3, 4, and 6.
@@ -1601,7 +1778,7 @@
for (i = 0; i < count; i++) {
if (vectors[i] == 0)
continue;
- irq = msix->msix_vectors[vectors[i]].mv_irq;
+ irq = msix->msix_vectors[vectors[i] - 1].mv_irq;
resource_list_add(&dinfo->resources, SYS_RES_IRQ, i + 1, irq,
irq, 1);
}
@@ -1615,7 +1792,7 @@
printf("---");
else
printf("%d",
- msix->msix_vectors[vectors[i]].mv_irq);
+ msix->msix_vectors[vectors[i] - 1].mv_irq);
}
printf("\n");
}
@@ -1687,6 +1864,28 @@
return (0);
}
+int
+pci_msix_pba_bar_method(device_t dev, device_t child)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ struct pcicfg_msix *msix = &dinfo->cfg.msix;
+
+ if (pci_do_msix && msix->msix_location != 0)
+ return (msix->msix_pba_bar);
+ return (-1);
+}
+
+int
+pci_msix_table_bar_method(device_t dev, device_t child)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(child);
+ struct pcicfg_msix *msix = &dinfo->cfg.msix;
+
+ if (pci_do_msix && msix->msix_location != 0)
+ return (msix->msix_table_bar);
+ return (-1);
+}
+
/*
* HyperTransport MSI mapping control
*/
@@ -1716,12 +1915,30 @@
}
int
+pci_get_max_payload(device_t dev)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ int cap;
+ uint16_t val;
+
+ cap = dinfo->cfg.pcie.pcie_location;
+ if (cap == 0)
+ return (0);
+ val = pci_read_config(dev, cap + PCIER_DEVICE_CTL, 2);
+ val &= PCIEM_CTL_MAX_PAYLOAD;
+ val >>= 5;
+ return (1 << (val + 7));
+}
+
+int
pci_get_max_read_req(device_t dev)
{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
int cap;
uint16_t val;
- if (pci_find_cap(dev, PCIY_EXPRESS, &cap) != 0)
+ cap = dinfo->cfg.pcie.pcie_location;
+ if (cap == 0)
return (0);
val = pci_read_config(dev, cap + PCIER_DEVICE_CTL, 2);
val &= PCIEM_CTL_MAX_READ_REQUEST;
@@ -1732,10 +1949,12 @@
int
pci_set_max_read_req(device_t dev, int size)
{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
int cap;
uint16_t val;
- if (pci_find_cap(dev, PCIY_EXPRESS, &cap) != 0)
+ cap = dinfo->cfg.pcie.pcie_location;
+ if (cap == 0)
return (0);
if (size < 128)
size = 128;
@@ -1749,49 +1968,107 @@
return (size);
}
+uint32_t
+pcie_read_config(device_t dev, int reg, int width)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ int cap;
+
+ cap = dinfo->cfg.pcie.pcie_location;
+ if (cap == 0) {
+ if (width == 2)
+ return (0xffff);
+ return (0xffffffff);
+ }
+
+ return (pci_read_config(dev, cap + reg, width));
+}
+
+void
+pcie_write_config(device_t dev, int reg, uint32_t value, int width)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ int cap;
+
+ cap = dinfo->cfg.pcie.pcie_location;
+ if (cap == 0)
+ return;
+ pci_write_config(dev, cap + reg, value, width);
+}
+
/*
+ * Adjusts a PCI-e capability register by clearing the bits in mask
+ * and setting the bits in (value & mask). Bits not set in mask are
+ * not adjusted.
+ *
+ * Returns the old value on success or all ones on failure.
+ */
+uint32_t
+pcie_adjust_config(device_t dev, int reg, uint32_t mask, uint32_t value,
+ int width)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ uint32_t old, new;
+ int cap;
+
+ cap = dinfo->cfg.pcie.pcie_location;
+ if (cap == 0) {
+ if (width == 2)
+ return (0xffff);
+ return (0xffffffff);
+ }
+
+ old = pci_read_config(dev, cap + reg, width);
+ new = old & ~mask;
+ new |= (value & mask);
+ pci_write_config(dev, cap + reg, new, width);
+ return (old);
+}
+
+/*
* Support for MSI message signalled interrupts.
*/
void
-pci_enable_msi(device_t dev, uint64_t address, uint16_t data)
+pci_enable_msi_method(device_t dev, device_t child, uint64_t address,
+ uint16_t data)
{
- struct pci_devinfo *dinfo = device_get_ivars(dev);
+ struct pci_devinfo *dinfo = device_get_ivars(child);
struct pcicfg_msi *msi = &dinfo->cfg.msi;
/* Write data and address values. */
- pci_write_config(dev, msi->msi_location + PCIR_MSI_ADDR,
+ pci_write_config(child, msi->msi_location + PCIR_MSI_ADDR,
address & 0xffffffff, 4);
if (msi->msi_ctrl & PCIM_MSICTRL_64BIT) {
- pci_write_config(dev, msi->msi_location + PCIR_MSI_ADDR_HIGH,
+ pci_write_config(child, msi->msi_location + PCIR_MSI_ADDR_HIGH,
address >> 32, 4);
- pci_write_config(dev, msi->msi_location + PCIR_MSI_DATA_64BIT,
+ pci_write_config(child, msi->msi_location + PCIR_MSI_DATA_64BIT,
data, 2);
} else
- pci_write_config(dev, msi->msi_location + PCIR_MSI_DATA, data,
+ pci_write_config(child, msi->msi_location + PCIR_MSI_DATA, data,
2);
/* Enable MSI in the control register. */
msi->msi_ctrl |= PCIM_MSICTRL_MSI_ENABLE;
- pci_write_config(dev, msi->msi_location + PCIR_MSI_CTRL, msi->msi_ctrl,
- 2);
+ pci_write_config(child, msi->msi_location + PCIR_MSI_CTRL,
+ msi->msi_ctrl, 2);
/* Enable MSI -> HT mapping. */
- pci_ht_map_msi(dev, address);
+ pci_ht_map_msi(child, address);
}
void
-pci_disable_msi(device_t dev)
+pci_disable_msi_method(device_t dev, device_t child)
{
- struct pci_devinfo *dinfo = device_get_ivars(dev);
+ struct pci_devinfo *dinfo = device_get_ivars(child);
struct pcicfg_msi *msi = &dinfo->cfg.msi;
/* Disable MSI -> HT mapping. */
- pci_ht_map_msi(dev, 0);
+ pci_ht_map_msi(child, 0);
/* Disable MSI in the control register. */
msi->msi_ctrl &= ~PCIM_MSICTRL_MSI_ENABLE;
- pci_write_config(dev, msi->msi_location + PCIR_MSI_CTRL, msi->msi_ctrl,
- 2);
+ pci_write_config(child, msi->msi_location + PCIR_MSI_CTRL,
+ msi->msi_ctrl, 2);
}
/*
@@ -1905,38 +2182,15 @@
int
pci_msi_device_blacklisted(device_t dev)
{
- const struct pci_quirk *q;
if (!pci_honor_msi_blacklist)
return (0);
- for (q = &pci_quirks[0]; q->devid; q++) {
- if (q->devid == pci_get_devid(dev) &&
- q->type == PCI_QUIRK_DISABLE_MSI)
- return (1);
- }
- return (0);
+ return (pci_has_quirk(pci_get_devid(dev), PCI_QUIRK_DISABLE_MSI));
}
/*
- * Returns true if a specified chipset supports MSI when it is
- * emulated hardware in a virtual machine.
- */
-static int
-pci_msi_vm_chipset(device_t dev)
-{
- const struct pci_quirk *q;
-
- for (q = &pci_quirks[0]; q->devid; q++) {
- if (q->devid == pci_get_devid(dev) &&
- q->type == PCI_QUIRK_ENABLE_MSI_VM)
- return (1);
- }
- return (0);
-}
-
-/*
- * Determine if MSI is blacklisted globally on this sytem. Currently,
+ * Determine if MSI is blacklisted globally on this system. Currently,
* we just check for blacklisted chipsets as represented by the
* host-PCI bridge at device 0:0:0. In the future, it may become
* necessary to check other system attributes, such as the kenv values
@@ -1953,9 +2207,14 @@
/* Blacklist all non-PCI-express and non-PCI-X chipsets. */
if (!(pcie_chipset || pcix_chipset)) {
if (vm_guest != VM_GUEST_NO) {
+ /*
+ * Whitelist older chipsets in virtual
+ * machines known to support MSI.
+ */
dev = pci_find_bsf(0, 0, 0);
if (dev != NULL)
- return (pci_msi_vm_chipset(dev) == 0);
+ return (!pci_has_quirk(pci_get_devid(dev),
+ PCI_QUIRK_ENABLE_MSI_VM));
}
return (1);
}
@@ -1967,6 +2226,45 @@
}
/*
+ * Returns true if the specified device is blacklisted because MSI-X
+ * doesn't work. Note that this assumes that if MSI doesn't work,
+ * MSI-X doesn't either.
+ */
+int
+pci_msix_device_blacklisted(device_t dev)
+{
+
+ if (!pci_honor_msi_blacklist)
+ return (0);
+
+ if (pci_has_quirk(pci_get_devid(dev), PCI_QUIRK_DISABLE_MSIX))
+ return (1);
+
+ return (pci_msi_device_blacklisted(dev));
+}
+
+/*
+ * Determine if MSI-X is blacklisted globally on this system. If MSI
+ * is blacklisted, assume that MSI-X is as well. Check for additional
+ * chipsets where MSI works but MSI-X does not.
+ */
+static int
+pci_msix_blacklisted(void)
+{
+ device_t dev;
+
+ if (!pci_honor_msi_blacklist)
+ return (0);
+
+ dev = pci_find_bsf(0, 0, 0);
+ if (dev != NULL && pci_has_quirk(pci_get_devid(dev),
+ PCI_QUIRK_DISABLE_MSIX))
+ return (1);
+
+ return (pci_msi_blacklisted());
+}
+
+/*
* Attempt to allocate *count MSI messages. The actual number allocated is
* returned in *count. After this function returns, each message will be
* available to the driver as SYS_RES_IRQ resources starting at a rid 1.
@@ -2199,7 +2497,7 @@
struct pci_devinfo *dinfo = device_get_ivars(child);
pcicfgregs *cfg = &dinfo->cfg;
uint16_t status;
- int result, oldstate, highest, delay;
+ int oldstate, highest, delay;
if (cfg->pp.pp_cap == 0)
return (EOPNOTSUPP);
@@ -2234,7 +2532,6 @@
delay = 0;
status = PCI_READ_CONFIG(dev, child, cfg->pp.pp_status, 2)
& ~PCIM_PSTAT_DMASK;
- result = 0;
switch (state) {
case PCI_POWERSTATE_D0:
status |= PCIM_PSTAT_D0;
@@ -2622,7 +2919,7 @@
struct pci_map *pm;
pci_addr_t base, map, testval;
pci_addr_t start, end, count;
- int barlen, basezero, maprange, mapsize, type;
+ int barlen, basezero, flags, maprange, mapsize, type;
uint16_t cmd;
struct resource *res;
@@ -2728,7 +3025,10 @@
}
count = (pci_addr_t)1 << mapsize;
- if (basezero || base == pci_mapbase(testval)) {
+ flags = RF_ALIGNMENT_LOG2(mapsize);
+ if (prefetch)
+ flags |= RF_PREFETCHABLE;
+ if (basezero || base == pci_mapbase(testval) || pci_clear_bars) {
start = 0; /* Let the parent decide. */
end = ~0ul;
} else {
@@ -2744,14 +3044,35 @@
* pci_alloc_resource().
*/
res = resource_list_reserve(rl, bus, dev, type, ®, start, end, count,
- prefetch ? RF_PREFETCHABLE : 0);
+ flags);
+ if (pci_do_realloc_bars && res == NULL && (start != 0 || end != ~0ul)) {
+ /*
+ * If the allocation fails, try to allocate a resource for
+ * this BAR using any available range. The firmware felt
+ * it was important enough to assign a resource, so don't
+ * disable decoding if we can help it.
+ */
+ resource_list_delete(rl, type, reg);
+ resource_list_add(rl, type, reg, 0, ~0ul, count);
+ res = resource_list_reserve(rl, bus, dev, type, ®, 0, ~0ul,
+ count, flags);
+ }
if (res == NULL) {
/*
* If the allocation fails, delete the resource list entry
- * to force pci_alloc_resource() to allocate resources
- * from the parent.
+ * and disable decoding for this device.
+ *
+ * If the driver requests this resource in the future,
+ * pci_reserve_map() will try to allocate a fresh
+ * resource range.
*/
resource_list_delete(rl, type, reg);
+ pci_disable_io(dev, type);
+ if (bootverbose)
+ device_printf(bus,
+ "pci%d:%d:%d:%d bar %#x failed to allocate\n",
+ pci_get_domain(dev), pci_get_bus(dev),
+ pci_get_slot(dev), pci_get_function(dev), reg);
} else {
start = rman_get_start(res);
pci_write_bar(dev, pm, start);
@@ -2770,7 +3091,6 @@
pci_ata_maps(device_t bus, device_t dev, struct resource_list *rl, int force,
uint32_t prefetchmask)
{
- struct resource *r;
int rid, type, progif;
#if 0
/* if this device supports PCI native addressing use it */
@@ -2793,11 +3113,11 @@
} else {
rid = PCIR_BAR(0);
resource_list_add(rl, type, rid, 0x1f0, 0x1f7, 8);
- r = resource_list_reserve(rl, bus, dev, type, &rid, 0x1f0,
+ (void)resource_list_reserve(rl, bus, dev, type, &rid, 0x1f0,
0x1f7, 8, 0);
rid = PCIR_BAR(1);
resource_list_add(rl, type, rid, 0x3f6, 0x3f6, 1);
- r = resource_list_reserve(rl, bus, dev, type, &rid, 0x3f6,
+ (void)resource_list_reserve(rl, bus, dev, type, &rid, 0x3f6,
0x3f6, 1, 0);
}
if (progif & PCIP_STORAGE_IDE_MODESEC) {
@@ -2808,11 +3128,11 @@
} else {
rid = PCIR_BAR(2);
resource_list_add(rl, type, rid, 0x170, 0x177, 8);
- r = resource_list_reserve(rl, bus, dev, type, &rid, 0x170,
+ (void)resource_list_reserve(rl, bus, dev, type, &rid, 0x170,
0x177, 8, 0);
rid = PCIR_BAR(3);
resource_list_add(rl, type, rid, 0x376, 0x376, 1);
- r = resource_list_reserve(rl, bus, dev, type, &rid, 0x376,
+ (void)resource_list_reserve(rl, bus, dev, type, &rid, 0x376,
0x376, 1, 0);
}
pci_add_map(bus, dev, PCIR_BAR(4), rl, force,
@@ -3048,6 +3368,164 @@
bus_release_resource(self, SYS_RES_MEMORY, rid, res);
}
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+static void
+pci_reserve_secbus(device_t bus, device_t dev, pcicfgregs *cfg,
+ struct resource_list *rl)
+{
+ struct resource *res;
+ char *cp;
+ u_long start, end, count;
+ int rid, sec_bus, sec_reg, sub_bus, sub_reg, sup_bus;
+
+ switch (cfg->hdrtype & PCIM_HDRTYPE) {
+ case PCIM_HDRTYPE_BRIDGE:
+ sec_reg = PCIR_SECBUS_1;
+ sub_reg = PCIR_SUBBUS_1;
+ break;
+ case PCIM_HDRTYPE_CARDBUS:
+ sec_reg = PCIR_SECBUS_2;
+ sub_reg = PCIR_SUBBUS_2;
+ break;
+ default:
+ return;
+ }
+
+ /*
+ * If the existing bus range is valid, attempt to reserve it
+ * from our parent. If this fails for any reason, clear the
+ * secbus and subbus registers.
+ *
+ * XXX: Should we reset sub_bus to sec_bus if it is < sec_bus?
+ * This would at least preserve the existing sec_bus if it is
+ * valid.
+ */
+ sec_bus = PCI_READ_CONFIG(bus, dev, sec_reg, 1);
+ sub_bus = PCI_READ_CONFIG(bus, dev, sub_reg, 1);
+
+ /* Quirk handling. */
+ switch (pci_get_devid(dev)) {
+ case 0x12258086: /* Intel 82454KX/GX (Orion) */
+ sup_bus = pci_read_config(dev, 0x41, 1);
+ if (sup_bus != 0xff) {
+ sec_bus = sup_bus + 1;
+ sub_bus = sup_bus + 1;
+ PCI_WRITE_CONFIG(bus, dev, sec_reg, sec_bus, 1);
+ PCI_WRITE_CONFIG(bus, dev, sub_reg, sub_bus, 1);
+ }
+ break;
+
+ case 0x00dd10de:
+ /* Compaq R3000 BIOS sets wrong subordinate bus number. */
+ if ((cp = getenv("smbios.planar.maker")) == NULL)
+ break;
+ if (strncmp(cp, "Compal", 6) != 0) {
+ freeenv(cp);
+ break;
+ }
+ freeenv(cp);
+ if ((cp = getenv("smbios.planar.product")) == NULL)
+ break;
+ if (strncmp(cp, "08A0", 4) != 0) {
+ freeenv(cp);
+ break;
+ }
+ freeenv(cp);
+ if (sub_bus < 0xa) {
+ sub_bus = 0xa;
+ PCI_WRITE_CONFIG(bus, dev, sub_reg, sub_bus, 1);
+ }
+ break;
+ }
+
+ if (bootverbose)
+ printf("\tsecbus=%d, subbus=%d\n", sec_bus, sub_bus);
+ if (sec_bus > 0 && sub_bus >= sec_bus) {
+ start = sec_bus;
+ end = sub_bus;
+ count = end - start + 1;
+
+ resource_list_add(rl, PCI_RES_BUS, 0, 0ul, ~0ul, count);
+
+ /*
+ * If requested, clear secondary bus registers in
+ * bridge devices to force a complete renumbering
+ * rather than reserving the existing range. However,
+ * preserve the existing size.
+ */
+ if (pci_clear_buses)
+ goto clear;
+
+ rid = 0;
+ res = resource_list_reserve(rl, bus, dev, PCI_RES_BUS, &rid,
+ start, end, count, 0);
+ if (res != NULL)
+ return;
+
+ if (bootverbose)
+ device_printf(bus,
+ "pci%d:%d:%d:%d secbus failed to allocate\n",
+ pci_get_domain(dev), pci_get_bus(dev),
+ pci_get_slot(dev), pci_get_function(dev));
+ }
+
+clear:
+ PCI_WRITE_CONFIG(bus, dev, sec_reg, 0, 1);
+ PCI_WRITE_CONFIG(bus, dev, sub_reg, 0, 1);
+}
+
+static struct resource *
+pci_alloc_secbus(device_t dev, device_t child, int *rid, u_long start,
+ u_long end, u_long count, u_int flags)
+{
+ struct pci_devinfo *dinfo;
+ pcicfgregs *cfg;
+ struct resource_list *rl;
+ struct resource *res;
+ int sec_reg, sub_reg;
+
+ dinfo = device_get_ivars(child);
+ cfg = &dinfo->cfg;
+ rl = &dinfo->resources;
+ switch (cfg->hdrtype & PCIM_HDRTYPE) {
+ case PCIM_HDRTYPE_BRIDGE:
+ sec_reg = PCIR_SECBUS_1;
+ sub_reg = PCIR_SUBBUS_1;
+ break;
+ case PCIM_HDRTYPE_CARDBUS:
+ sec_reg = PCIR_SECBUS_2;
+ sub_reg = PCIR_SUBBUS_2;
+ break;
+ default:
+ return (NULL);
+ }
+
+ if (*rid != 0)
+ return (NULL);
+
+ if (resource_list_find(rl, PCI_RES_BUS, *rid) == NULL)
+ resource_list_add(rl, PCI_RES_BUS, *rid, start, end, count);
+ if (!resource_list_reserved(rl, PCI_RES_BUS, *rid)) {
+ res = resource_list_reserve(rl, dev, child, PCI_RES_BUS, rid,
+ start, end, count, flags & ~RF_ACTIVE);
+ if (res == NULL) {
+ resource_list_delete(rl, PCI_RES_BUS, *rid);
+ device_printf(child, "allocating %lu bus%s failed\n",
+ count, count == 1 ? "" : "es");
+ return (NULL);
+ }
+ if (bootverbose)
+ device_printf(child,
+ "Lazy allocation of %lu bus%s at %lu\n", count,
+ count == 1 ? "" : "es", rman_get_start(res));
+ PCI_WRITE_CONFIG(dev, child, sec_reg, rman_get_start(res), 1);
+ PCI_WRITE_CONFIG(dev, child, sub_reg, rman_get_end(res), 1);
+ }
+ return (resource_list_alloc(rl, dev, child, PCI_RES_BUS, rid, start,
+ end, count, flags));
+}
+#endif
+
void
pci_add_resources(device_t bus, device_t dev, int force, uint32_t prefetchmask)
{
@@ -3120,8 +3598,29 @@
else if (pci_get_progif(dev) == PCIP_SERIALBUS_USB_UHCI)
uhci_early_takeover(dev);
}
+
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+ /*
+ * Reserve resources for secondary bus ranges behind bridge
+ * devices.
+ */
+ pci_reserve_secbus(bus, dev, cfg, rl);
+#endif
}
+static struct pci_devinfo *
+pci_identify_function(device_t pcib, device_t dev, int domain, int busno,
+ int slot, int func, size_t dinfo_size)
+{
+ struct pci_devinfo *dinfo;
+
+ dinfo = pci_read_device(pcib, domain, busno, slot, func, dinfo_size);
+ if (dinfo != NULL)
+ pci_add_child(dev, dinfo);
+
+ return (dinfo);
+}
+
void
pci_add_children(device_t dev, int domain, int busno, size_t dinfo_size)
{
@@ -3131,11 +3630,29 @@
int maxslots;
int s, f, pcifunchigh;
uint8_t hdrtype;
+ int first_func;
+ /*
+ * Try to detect a device at slot 0, function 0. If it exists, try to
+ * enable ARI. We must enable ARI before detecting the rest of the
+ * functions on this bus as ARI changes the set of slots and functions
+ * that are legal on this bus.
+ */
+ dinfo = pci_identify_function(pcib, dev, domain, busno, 0, 0,
+ dinfo_size);
+ if (dinfo != NULL && pci_enable_ari)
+ PCIB_TRY_ENABLE_ARI(pcib, dinfo->cfg.dev);
+
+ /*
+ * Start looking for new devices on slot 0 at function 1 because we
+ * just identified the device at slot 0, function 0.
+ */
+ first_func = 1;
+
KASSERT(dinfo_size >= sizeof(struct pci_devinfo),
("dinfo_size too small"));
maxslots = PCIB_MAXSLOTS(pcib);
- for (s = 0; s <= maxslots; s++) {
+ for (s = 0; s <= maxslots; s++, first_func = 0) {
pcifunchigh = 0;
f = 0;
DELAY(1);
@@ -3143,14 +3660,10 @@
if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
continue;
if (hdrtype & PCIM_MFDEV)
- pcifunchigh = PCI_FUNCMAX;
- for (f = 0; f <= pcifunchigh; f++) {
- dinfo = pci_read_device(pcib, domain, busno, s, f,
+ pcifunchigh = PCIB_MAXFUNCS(pcib);
+ for (f = first_func; f <= pcifunchigh; f++)
+ pci_identify_function(pcib, dev, domain, busno, s, f,
dinfo_size);
- if (dinfo != NULL) {
- pci_add_child(dev, dinfo);
- }
- }
}
#undef REG
}
@@ -3165,8 +3678,15 @@
pci_cfg_restore(dinfo->cfg.dev, dinfo);
pci_print_verbose(dinfo);
pci_add_resources(bus, dinfo->cfg.dev, 0, 0);
+ pci_child_added(dinfo->cfg.dev);
}
+void
+pci_child_added_method(device_t dev, device_t child)
+{
+
+}
+
static int
pci_probe(device_t dev)
{
@@ -3185,10 +3705,22 @@
#ifdef PCI_DMA_BOUNDARY
int error, tag_valid;
#endif
+#ifdef PCI_RES_BUS
+ int rid;
+#endif
sc = device_get_softc(dev);
domain = pcib_get_domain(dev);
busno = pcib_get_bus(dev);
+#ifdef PCI_RES_BUS
+ rid = 0;
+ sc->sc_bus = bus_alloc_resource(dev, PCI_RES_BUS, &rid, busno, busno,
+ 1, 0);
+ if (sc->sc_bus == NULL) {
+ device_printf(dev, "failed to allocate bus number\n");
+ return (ENXIO);
+ }
+#endif
if (bootverbose)
device_printf(dev, "domain=%d, physical bus=%d\n",
domain, busno);
@@ -3233,12 +3765,26 @@
return (bus_generic_attach(dev));
}
+#ifdef PCI_RES_BUS
+static int
+pci_detach(device_t dev)
+{
+ struct pci_softc *sc;
+ int error;
+
+ error = bus_generic_detach(dev);
+ if (error)
+ return (error);
+ sc = device_get_softc(dev);
+ return (bus_release_resource(dev, PCI_RES_BUS, 0, sc->sc_bus));
+}
+#endif
+
static void
pci_set_power_children(device_t dev, device_t *devlist, int numdevs,
int state)
{
device_t child, pcib;
- struct pci_devinfo *dinfo;
int dstate, i;
/*
@@ -3251,7 +3797,6 @@
pcib = device_get_parent(dev);
for (i = 0; i < numdevs; i++) {
child = devlist[i];
- dinfo = device_get_ivars(child);
dstate = state;
if (device_is_attached(child) &&
PCIB_POWER_FOR_SLEEP(pcib, dev, &dstate) == 0)
@@ -3391,7 +3936,7 @@
pci_printf(&dinfo->cfg, "reprobing on driver added\n");
pci_cfg_restore(child, dinfo);
if (device_probe_and_attach(child) != 0)
- pci_cfg_save(child, dinfo, 1);
+ pci_child_detached(dev, child);
}
free(devlist, M_TEMP);
}
@@ -3467,16 +4012,32 @@
mv->mv_address = addr;
mv->mv_data = data;
}
- if (mte->mte_handlers == 0) {
+
+ /*
+ * The MSIX table entry must be made valid by
+ * incrementing the mte_handlers before
+ * calling pci_enable_msix() and
+ * pci_resume_msix(). Else the MSIX rewrite
+ * table quirk will not work as expected.
+ */
+ mte->mte_handlers++;
+ if (mte->mte_handlers == 1) {
pci_enable_msix(child, rid - 1, mv->mv_address,
mv->mv_data);
pci_unmask_msix(child, rid - 1);
}
- mte->mte_handlers++;
}
- /* Make sure that INTx is disabled if we are using MSI/MSIX */
- pci_set_command_bit(dev, child, PCIM_CMD_INTxDIS);
+ /*
+ * Make sure that INTx is disabled if we are using MSI/MSI-X,
+ * unless the device is affected by PCI_QUIRK_MSI_INTX_BUG,
+ * in which case we "enable" INTx so MSI/MSI-X actually works.
+ */
+ if (!pci_has_quirk(pci_get_devid(child),
+ PCI_QUIRK_MSI_INTX_BUG))
+ pci_set_command_bit(dev, child, PCIM_CMD_INTxDIS);
+ else
+ pci_clear_command_bit(dev, child, PCIM_CMD_INTxDIS);
bad:
if (error) {
(void)bus_generic_teardown_intr(dev, child, irq,
@@ -3568,6 +4129,7 @@
retval += printf(" at device %d.%d", pci_get_slot(child),
pci_get_function(child));
+ retval += bus_print_child_domain(dev, child);
retval += bus_print_child_footer(dev, child);
return (retval);
@@ -3577,99 +4139,104 @@
{
int class;
int subclass;
+ int report; /* 0 = bootverbose, 1 = always */
const char *desc;
} pci_nomatch_tab[] = {
- {PCIC_OLD, -1, "old"},
- {PCIC_OLD, PCIS_OLD_NONVGA, "non-VGA display device"},
- {PCIC_OLD, PCIS_OLD_VGA, "VGA-compatible display device"},
- {PCIC_STORAGE, -1, "mass storage"},
- {PCIC_STORAGE, PCIS_STORAGE_SCSI, "SCSI"},
- {PCIC_STORAGE, PCIS_STORAGE_IDE, "ATA"},
- {PCIC_STORAGE, PCIS_STORAGE_FLOPPY, "floppy disk"},
- {PCIC_STORAGE, PCIS_STORAGE_IPI, "IPI"},
- {PCIC_STORAGE, PCIS_STORAGE_RAID, "RAID"},
- {PCIC_STORAGE, PCIS_STORAGE_ATA_ADMA, "ATA (ADMA)"},
- {PCIC_STORAGE, PCIS_STORAGE_SATA, "SATA"},
- {PCIC_STORAGE, PCIS_STORAGE_SAS, "SAS"},
- {PCIC_STORAGE, PCIS_STORAGE_NVM, "NVM"},
- {PCIC_NETWORK, -1, "network"},
- {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, "ethernet"},
- {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, "token ring"},
- {PCIC_NETWORK, PCIS_NETWORK_FDDI, "fddi"},
- {PCIC_NETWORK, PCIS_NETWORK_ATM, "ATM"},
- {PCIC_NETWORK, PCIS_NETWORK_ISDN, "ISDN"},
- {PCIC_DISPLAY, -1, "display"},
- {PCIC_DISPLAY, PCIS_DISPLAY_VGA, "VGA"},
- {PCIC_DISPLAY, PCIS_DISPLAY_XGA, "XGA"},
- {PCIC_DISPLAY, PCIS_DISPLAY_3D, "3D"},
- {PCIC_MULTIMEDIA, -1, "multimedia"},
- {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_VIDEO, "video"},
- {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_AUDIO, "audio"},
- {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_TELE, "telephony"},
- {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_HDA, "HDA"},
- {PCIC_MEMORY, -1, "memory"},
- {PCIC_MEMORY, PCIS_MEMORY_RAM, "RAM"},
- {PCIC_MEMORY, PCIS_MEMORY_FLASH, "flash"},
- {PCIC_BRIDGE, -1, "bridge"},
- {PCIC_BRIDGE, PCIS_BRIDGE_HOST, "HOST-PCI"},
- {PCIC_BRIDGE, PCIS_BRIDGE_ISA, "PCI-ISA"},
- {PCIC_BRIDGE, PCIS_BRIDGE_EISA, "PCI-EISA"},
- {PCIC_BRIDGE, PCIS_BRIDGE_MCA, "PCI-MCA"},
- {PCIC_BRIDGE, PCIS_BRIDGE_PCI, "PCI-PCI"},
- {PCIC_BRIDGE, PCIS_BRIDGE_PCMCIA, "PCI-PCMCIA"},
- {PCIC_BRIDGE, PCIS_BRIDGE_NUBUS, "PCI-NuBus"},
- {PCIC_BRIDGE, PCIS_BRIDGE_CARDBUS, "PCI-CardBus"},
- {PCIC_BRIDGE, PCIS_BRIDGE_RACEWAY, "PCI-RACEway"},
- {PCIC_SIMPLECOMM, -1, "simple comms"},
- {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_UART, "UART"}, /* could detect 16550 */
- {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_PAR, "parallel port"},
- {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MULSER, "multiport serial"},
- {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MODEM, "generic modem"},
- {PCIC_BASEPERIPH, -1, "base peripheral"},
- {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PIC, "interrupt controller"},
- {PCIC_BASEPERIPH, PCIS_BASEPERIPH_DMA, "DMA controller"},
- {PCIC_BASEPERIPH, PCIS_BASEPERIPH_TIMER, "timer"},
- {PCIC_BASEPERIPH, PCIS_BASEPERIPH_RTC, "realtime clock"},
- {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PCIHOT, "PCI hot-plug controller"},
- {PCIC_BASEPERIPH, PCIS_BASEPERIPH_SDHC, "SD host controller"},
- {PCIC_INPUTDEV, -1, "input device"},
- {PCIC_INPUTDEV, PCIS_INPUTDEV_KEYBOARD, "keyboard"},
- {PCIC_INPUTDEV, PCIS_INPUTDEV_DIGITIZER,"digitizer"},
- {PCIC_INPUTDEV, PCIS_INPUTDEV_MOUSE, "mouse"},
- {PCIC_INPUTDEV, PCIS_INPUTDEV_SCANNER, "scanner"},
- {PCIC_INPUTDEV, PCIS_INPUTDEV_GAMEPORT, "gameport"},
- {PCIC_DOCKING, -1, "docking station"},
- {PCIC_PROCESSOR, -1, "processor"},
- {PCIC_SERIALBUS, -1, "serial bus"},
- {PCIC_SERIALBUS, PCIS_SERIALBUS_FW, "FireWire"},
- {PCIC_SERIALBUS, PCIS_SERIALBUS_ACCESS, "AccessBus"},
- {PCIC_SERIALBUS, PCIS_SERIALBUS_SSA, "SSA"},
- {PCIC_SERIALBUS, PCIS_SERIALBUS_USB, "USB"},
- {PCIC_SERIALBUS, PCIS_SERIALBUS_FC, "Fibre Channel"},
- {PCIC_SERIALBUS, PCIS_SERIALBUS_SMBUS, "SMBus"},
- {PCIC_WIRELESS, -1, "wireless controller"},
- {PCIC_WIRELESS, PCIS_WIRELESS_IRDA, "iRDA"},
- {PCIC_WIRELESS, PCIS_WIRELESS_IR, "IR"},
- {PCIC_WIRELESS, PCIS_WIRELESS_RF, "RF"},
- {PCIC_INTELLIIO, -1, "intelligent I/O controller"},
- {PCIC_INTELLIIO, PCIS_INTELLIIO_I2O, "I2O"},
- {PCIC_SATCOM, -1, "satellite communication"},
- {PCIC_SATCOM, PCIS_SATCOM_TV, "sat TV"},
- {PCIC_SATCOM, PCIS_SATCOM_AUDIO, "sat audio"},
- {PCIC_SATCOM, PCIS_SATCOM_VOICE, "sat voice"},
- {PCIC_SATCOM, PCIS_SATCOM_DATA, "sat data"},
- {PCIC_CRYPTO, -1, "encrypt/decrypt"},
- {PCIC_CRYPTO, PCIS_CRYPTO_NETCOMP, "network/computer crypto"},
- {PCIC_CRYPTO, PCIS_CRYPTO_ENTERTAIN, "entertainment crypto"},
- {PCIC_DASP, -1, "dasp"},
- {PCIC_DASP, PCIS_DASP_DPIO, "DPIO module"},
- {0, 0, NULL}
+ {PCIC_OLD, -1, 1, "old"},
+ {PCIC_OLD, PCIS_OLD_NONVGA, 1, "non-VGA display device"},
+ {PCIC_OLD, PCIS_OLD_VGA, 1, "VGA-compatible display device"},
+ {PCIC_STORAGE, -1, 1, "mass storage"},
+ {PCIC_STORAGE, PCIS_STORAGE_SCSI, 1, "SCSI"},
+ {PCIC_STORAGE, PCIS_STORAGE_IDE, 1, "ATA"},
+ {PCIC_STORAGE, PCIS_STORAGE_FLOPPY, 1, "floppy disk"},
+ {PCIC_STORAGE, PCIS_STORAGE_IPI, 1, "IPI"},
+ {PCIC_STORAGE, PCIS_STORAGE_RAID, 1, "RAID"},
+ {PCIC_STORAGE, PCIS_STORAGE_ATA_ADMA, 1, "ATA (ADMA)"},
+ {PCIC_STORAGE, PCIS_STORAGE_SATA, 1, "SATA"},
+ {PCIC_STORAGE, PCIS_STORAGE_SAS, 1, "SAS"},
+ {PCIC_STORAGE, PCIS_STORAGE_NVM, 1, "NVM"},
+ {PCIC_NETWORK, -1, 1, "network"},
+ {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, 1, "ethernet"},
+ {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, 1, "token ring"},
+ {PCIC_NETWORK, PCIS_NETWORK_FDDI, 1, "fddi"},
+ {PCIC_NETWORK, PCIS_NETWORK_ATM, 1, "ATM"},
+ {PCIC_NETWORK, PCIS_NETWORK_ISDN, 1, "ISDN"},
+ {PCIC_DISPLAY, -1, 1, "display"},
+ {PCIC_DISPLAY, PCIS_DISPLAY_VGA, 1, "VGA"},
+ {PCIC_DISPLAY, PCIS_DISPLAY_XGA, 1, "XGA"},
+ {PCIC_DISPLAY, PCIS_DISPLAY_3D, 1, "3D"},
+ {PCIC_MULTIMEDIA, -1, 1, "multimedia"},
+ {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_VIDEO, 1, "video"},
+ {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_AUDIO, 1, "audio"},
+ {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_TELE, 1, "telephony"},
+ {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_HDA, 1, "HDA"},
+ {PCIC_MEMORY, -1, 1, "memory"},
+ {PCIC_MEMORY, PCIS_MEMORY_RAM, 1, "RAM"},
+ {PCIC_MEMORY, PCIS_MEMORY_FLASH, 1, "flash"},
+ {PCIC_BRIDGE, -1, 1, "bridge"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_HOST, 1, "HOST-PCI"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_ISA, 1, "PCI-ISA"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_EISA, 1, "PCI-EISA"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_MCA, 1, "PCI-MCA"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_PCI, 1, "PCI-PCI"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_PCMCIA, 1, "PCI-PCMCIA"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_NUBUS, 1, "PCI-NuBus"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_CARDBUS, 1, "PCI-CardBus"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_RACEWAY, 1, "PCI-RACEway"},
+ {PCIC_SIMPLECOMM, -1, 1, "simple comms"},
+ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_UART, 1, "UART"}, /* could detect 16550 */
+ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_PAR, 1, "parallel port"},
+ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MULSER, 1, "multiport serial"},
+ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MODEM, 1, "generic modem"},
+ {PCIC_BASEPERIPH, -1, 0, "base peripheral"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PIC, 1, "interrupt controller"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_DMA, 1, "DMA controller"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_TIMER, 1, "timer"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_RTC, 1, "realtime clock"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PCIHOT, 1, "PCI hot-plug controller"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_SDHC, 1, "SD host controller"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_IOMMU, 1, "IOMMU"},
+ {PCIC_INPUTDEV, -1, 1, "input device"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_KEYBOARD, 1, "keyboard"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_DIGITIZER,1, "digitizer"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_MOUSE, 1, "mouse"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_SCANNER, 1, "scanner"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_GAMEPORT, 1, "gameport"},
+ {PCIC_DOCKING, -1, 1, "docking station"},
+ {PCIC_PROCESSOR, -1, 1, "processor"},
+ {PCIC_SERIALBUS, -1, 1, "serial bus"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_FW, 1, "FireWire"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_ACCESS, 1, "AccessBus"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_SSA, 1, "SSA"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_USB, 1, "USB"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_FC, 1, "Fibre Channel"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_SMBUS, 0, "SMBus"},
+ {PCIC_WIRELESS, -1, 1, "wireless controller"},
+ {PCIC_WIRELESS, PCIS_WIRELESS_IRDA, 1, "iRDA"},
+ {PCIC_WIRELESS, PCIS_WIRELESS_IR, 1, "IR"},
+ {PCIC_WIRELESS, PCIS_WIRELESS_RF, 1, "RF"},
+ {PCIC_INTELLIIO, -1, 1, "intelligent I/O controller"},
+ {PCIC_INTELLIIO, PCIS_INTELLIIO_I2O, 1, "I2O"},
+ {PCIC_SATCOM, -1, 1, "satellite communication"},
+ {PCIC_SATCOM, PCIS_SATCOM_TV, 1, "sat TV"},
+ {PCIC_SATCOM, PCIS_SATCOM_AUDIO, 1, "sat audio"},
+ {PCIC_SATCOM, PCIS_SATCOM_VOICE, 1, "sat voice"},
+ {PCIC_SATCOM, PCIS_SATCOM_DATA, 1, "sat data"},
+ {PCIC_CRYPTO, -1, 1, "encrypt/decrypt"},
+ {PCIC_CRYPTO, PCIS_CRYPTO_NETCOMP, 1, "network/computer crypto"},
+ {PCIC_CRYPTO, PCIS_CRYPTO_ENTERTAIN, 1, "entertainment crypto"},
+ {PCIC_DASP, -1, 0, "dasp"},
+ {PCIC_DASP, PCIS_DASP_DPIO, 1, "DPIO module"},
+ {PCIC_DASP, PCIS_DASP_PERFCNTRS, 1, "performance counters"},
+ {PCIC_DASP, PCIS_DASP_COMM_SYNC, 1, "communication synchronizer"},
+ {PCIC_DASP, PCIS_DASP_MGMT_CARD, 1, "signal processing management"},
+ {0, 0, 0, NULL}
};
void
pci_probe_nomatch(device_t dev, device_t child)
{
- int i;
+ int i, report;
const char *cp, *scp;
char *device;
@@ -3676,6 +4243,7 @@
/*
* Look for a listing for this device in a loaded device database.
*/
+ report = 1;
if ((device = pci_describe_device(child)) != NULL) {
device_printf(dev, "<%s>", device);
free(device, M_DEVBUF);
@@ -3690,22 +4258,60 @@
if (pci_nomatch_tab[i].class == pci_get_class(child)) {
if (pci_nomatch_tab[i].subclass == -1) {
cp = pci_nomatch_tab[i].desc;
+ report = pci_nomatch_tab[i].report;
} else if (pci_nomatch_tab[i].subclass ==
pci_get_subclass(child)) {
scp = pci_nomatch_tab[i].desc;
+ report = pci_nomatch_tab[i].report;
}
}
}
- device_printf(dev, "<%s%s%s>",
- cp ? cp : "",
- ((cp != NULL) && (scp != NULL)) ? ", " : "",
- scp ? scp : "");
+ if (report || bootverbose) {
+ device_printf(dev, "<%s%s%s>",
+ cp ? cp : "",
+ ((cp != NULL) && (scp != NULL)) ? ", " : "",
+ scp ? scp : "");
+ }
}
- printf(" at device %d.%d (no driver attached)\n",
- pci_get_slot(child), pci_get_function(child));
+ if (report || bootverbose) {
+ printf(" at device %d.%d (no driver attached)\n",
+ pci_get_slot(child), pci_get_function(child));
+ }
pci_cfg_save(child, device_get_ivars(child), 1);
}
+void
+pci_child_detached(device_t dev, device_t child)
+{
+ struct pci_devinfo *dinfo;
+ struct resource_list *rl;
+
+ dinfo = device_get_ivars(child);
+ rl = &dinfo->resources;
+
+ /*
+ * Have to deallocate IRQs before releasing any MSI messages and
+ * have to release MSI messages before deallocating any memory
+ * BARs.
+ */
+ if (resource_list_release_active(rl, dev, child, SYS_RES_IRQ) != 0)
+ pci_printf(&dinfo->cfg, "Device leaked IRQ resources\n");
+ if (dinfo->cfg.msi.msi_alloc != 0 || dinfo->cfg.msix.msix_alloc != 0) {
+ pci_printf(&dinfo->cfg, "Device leaked MSI vectors\n");
+ (void)pci_release_msi(child);
+ }
+ if (resource_list_release_active(rl, dev, child, SYS_RES_MEMORY) != 0)
+ pci_printf(&dinfo->cfg, "Device leaked memory resources\n");
+ if (resource_list_release_active(rl, dev, child, SYS_RES_IOPORT) != 0)
+ pci_printf(&dinfo->cfg, "Device leaked I/O resources\n");
+#ifdef PCI_RES_BUS
+ if (resource_list_release_active(rl, dev, child, PCI_RES_BUS) != 0)
+ pci_printf(&dinfo->cfg, "Device leaked PCI bus numbers\n");
+#endif
+
+ pci_cfg_save(child, dinfo, 1);
+}
+
/*
* Parse the PCI device database, if loaded, and return a pointer to a
* description of the device.
@@ -3902,9 +4508,17 @@
*result = cfg->cachelnsz;
break;
case PCI_IVAR_MINGNT:
+ if (cfg->hdrtype != PCIM_HDRTYPE_NORMAL) {
+ *result = -1;
+ return (EINVAL);
+ }
*result = cfg->mingnt;
break;
case PCI_IVAR_MAXLAT:
+ if (cfg->hdrtype != PCIM_HDRTYPE_NORMAL) {
+ *result = -1;
+ return (EINVAL);
+ }
*result = cfg->maxlat;
break;
case PCI_IVAR_LATTIMER:
@@ -4005,9 +4619,9 @@
{
struct pci_devinfo *dinfo = device_get_ivars(child);
struct resource_list *rl = &dinfo->resources;
- struct resource_list_entry *rle;
struct resource *res;
struct pci_map *pm;
+ uint16_t cmd;
pci_addr_t map, testval;
int mapsize;
@@ -4078,29 +4692,31 @@
* Allocate enough resource, and then write back the
* appropriate BAR for that resource.
*/
- res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, type, rid,
- start, end, count, flags & ~RF_ACTIVE);
+ resource_list_add(rl, type, *rid, start, end, count);
+ res = resource_list_reserve(rl, dev, child, type, rid, start, end,
+ count, flags & ~RF_ACTIVE);
if (res == NULL) {
+ resource_list_delete(rl, type, *rid);
device_printf(child,
"%#lx bytes of rid %#x res %d failed (%#lx, %#lx).\n",
count, *rid, type, start, end);
goto out;
}
- resource_list_add(rl, type, *rid, start, end, count);
- rle = resource_list_find(rl, type, *rid);
- if (rle == NULL)
- panic("pci_reserve_map: unexpectedly can't find resource.");
- rle->res = res;
- rle->start = rman_get_start(res);
- rle->end = rman_get_end(res);
- rle->count = count;
- rle->flags = RLE_RESERVED;
if (bootverbose)
device_printf(child,
"Lazy allocation of %#lx bytes rid %#x type %d at %#lx\n",
count, *rid, type, rman_get_start(res));
+
+ /* Disable decoding via the CMD register before updating the BAR */
+ cmd = pci_read_config(child, PCIR_COMMAND, 2);
+ pci_write_config(child, PCIR_COMMAND,
+ cmd & ~(PCI_BAR_MEM(map) ? PCIM_CMD_MEMEN : PCIM_CMD_PORTEN), 2);
+
map = rman_get_start(res);
pci_write_bar(child, pm, map);
+
+ /* Restore the original value of the CMD register */
+ pci_write_config(child, PCIR_COMMAND, cmd, 2);
out:
return (res);
}
@@ -4109,11 +4725,11 @@
pci_alloc_resource(device_t dev, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
- struct pci_devinfo *dinfo = device_get_ivars(child);
- struct resource_list *rl = &dinfo->resources;
+ struct pci_devinfo *dinfo;
+ struct resource_list *rl;
struct resource_list_entry *rle;
struct resource *res;
- pcicfgregs *cfg = &dinfo->cfg;
+ pcicfgregs *cfg;
if (device_get_parent(child) != dev)
return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child,
@@ -4122,7 +4738,15 @@
/*
* Perform lazy resource allocation
*/
+ dinfo = device_get_ivars(child);
+ rl = &dinfo->resources;
+ cfg = &dinfo->cfg;
switch (type) {
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+ case PCI_RES_BUS:
+ return (pci_alloc_secbus(dev, child, rid, start, end, count,
+ flags));
+#endif
case SYS_RES_IRQ:
/*
* Can't alloc legacy interrupt once MSI messages have
@@ -4177,6 +4801,41 @@
}
int
+pci_release_resource(device_t dev, device_t child, int type, int rid,
+ struct resource *r)
+{
+ struct pci_devinfo *dinfo;
+ struct resource_list *rl;
+ pcicfgregs *cfg;
+
+ if (device_get_parent(child) != dev)
+ return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child,
+ type, rid, r));
+
+ dinfo = device_get_ivars(child);
+ cfg = &dinfo->cfg;
+#ifdef NEW_PCIB
+ /*
+ * PCI-PCI bridge I/O window resources are not BARs. For
+ * those allocations just pass the request up the tree.
+ */
+ if (cfg->hdrtype == PCIM_HDRTYPE_BRIDGE &&
+ (type == SYS_RES_IOPORT || type == SYS_RES_MEMORY)) {
+ switch (rid) {
+ case PCIR_IOBASEL_1:
+ case PCIR_MEMBASE_1:
+ case PCIR_PMBASEL_1:
+ return (bus_generic_release_resource(dev, child, type,
+ rid, r));
+ }
+ }
+#endif
+
+ rl = &dinfo->resources;
+ return (resource_list_release(rl, dev, child, type, rid, r));
+}
+
+int
pci_activate_resource(device_t dev, device_t child, int type, int rid,
struct resource *r)
{
@@ -4226,7 +4885,7 @@
}
void
-pci_delete_child(device_t dev, device_t child)
+pci_child_deleted(device_t dev, device_t child)
{
struct resource_list_entry *rle;
struct resource_list *rl;
@@ -4235,13 +4894,14 @@
dinfo = device_get_ivars(child);
rl = &dinfo->resources;
- if (device_is_attached(child))
- device_detach(child);
-
/* Turn off access to resources we're about to free */
- pci_write_config(child, PCIR_COMMAND, pci_read_config(child,
- PCIR_COMMAND, 2) & ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN), 2);
+ if (bus_child_present(child) != 0) {
+ pci_write_config(child, PCIR_COMMAND, pci_read_config(child,
+ PCIR_COMMAND, 2) & ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN), 2);
+ pci_disable_busmaster(child);
+ }
+
/* Free all allocated resources */
STAILQ_FOREACH(rle, rl, link) {
if (rle->res) {
@@ -4261,11 +4921,20 @@
}
resource_list_free(rl);
- device_delete_child(dev, child);
pci_freecfg(dinfo);
}
+/* KBI compatability shim. */
+extern void pci_delete_child(device_t dev, device_t child);
+
void
+pci_delete_child(device_t dev, device_t child)
+{
+
+ device_delete_child (dev, child);
+}
+
+void
pci_delete_resource(device_t dev, device_t child, int type, int rid)
{
struct pci_devinfo *dinfo;
@@ -4337,8 +5006,9 @@
size_t buflen)
{
- snprintf(buf, buflen, "slot=%d function=%d", pci_get_slot(child),
- pci_get_function(child));
+ snprintf(buf, buflen, "slot=%d function=%d dbsf=pci%d:%d:%d:%d",
+ pci_get_slot(child), pci_get_function(child), pci_get_domain(child),
+ pci_get_bus(child), pci_get_slot(child), pci_get_function(child));
return (0);
}
@@ -4368,10 +5038,60 @@
cfg->intpin));
}
+static void
+pci_lookup(void *arg, const char *name, device_t *dev)
+{
+ long val;
+ char *end;
+ int domain, bus, slot, func;
+
+ if (*dev != NULL)
+ return;
+
+ /*
+ * Accept pciconf-style selectors of either pciD:B:S:F or
+ * pciB:S:F. In the latter case, the domain is assumed to
+ * be zero.
+ */
+ if (strncmp(name, "pci", 3) != 0)
+ return;
+ val = strtol(name + 3, &end, 10);
+ if (val < 0 || val > INT_MAX || *end != ':')
+ return;
+ domain = val;
+ val = strtol(end + 1, &end, 10);
+ if (val < 0 || val > INT_MAX || *end != ':')
+ return;
+ bus = val;
+ val = strtol(end + 1, &end, 10);
+ if (val < 0 || val > INT_MAX)
+ return;
+ slot = val;
+ if (*end == ':') {
+ val = strtol(end + 1, &end, 10);
+ if (val < 0 || val > INT_MAX || *end != '\0')
+ return;
+ func = val;
+ } else if (*end == '\0') {
+ func = slot;
+ slot = bus;
+ bus = domain;
+ domain = 0;
+ } else
+ return;
+
+ if (domain > PCI_DOMAINMAX || bus > PCI_BUSMAX || slot > PCI_SLOTMAX ||
+ func > PCIE_ARI_FUNCMAX || (slot != 0 && func > PCI_FUNCMAX))
+ return;
+
+ *dev = pci_find_dbsf(domain, bus, slot, func);
+}
+
static int
pci_modevent(module_t mod, int what, void *arg)
{
static struct cdev *pci_cdev;
+ static eventhandler_tag tag;
switch (what) {
case MOD_LOAD:
@@ -4380,9 +5100,13 @@
pci_cdev = make_dev(&pcicdev, 0, UID_ROOT, GID_WHEEL, 0644,
"pci");
pci_load_vendor_data();
+ tag = EVENTHANDLER_REGISTER(dev_lookup, pci_lookup, NULL,
+ 1000);
break;
case MOD_UNLOAD:
+ if (tag != NULL)
+ EVENTHANDLER_DEREGISTER(dev_lookup, tag);
destroy_dev(pci_cdev);
break;
}
@@ -4390,6 +5114,49 @@
return (0);
}
+static void
+pci_cfg_restore_pcie(device_t dev, struct pci_devinfo *dinfo)
+{
+#define WREG(n, v) pci_write_config(dev, pos + (n), (v), 2)
+ struct pcicfg_pcie *cfg;
+ int version, pos;
+
+ cfg = &dinfo->cfg.pcie;
+ pos = cfg->pcie_location;
+
+ version = cfg->pcie_flags & PCIEM_FLAGS_VERSION;
+
+ WREG(PCIER_DEVICE_CTL, cfg->pcie_device_ctl);
+
+ if (version > 1 || cfg->pcie_type == PCIEM_TYPE_ROOT_PORT ||
+ cfg->pcie_type == PCIEM_TYPE_ENDPOINT ||
+ cfg->pcie_type == PCIEM_TYPE_LEGACY_ENDPOINT)
+ WREG(PCIER_LINK_CTL, cfg->pcie_link_ctl);
+
+ if (version > 1 || (cfg->pcie_type == PCIEM_TYPE_ROOT_PORT ||
+ (cfg->pcie_type == PCIEM_TYPE_DOWNSTREAM_PORT &&
+ (cfg->pcie_flags & PCIEM_FLAGS_SLOT))))
+ WREG(PCIER_SLOT_CTL, cfg->pcie_slot_ctl);
+
+ if (version > 1 || cfg->pcie_type == PCIEM_TYPE_ROOT_PORT ||
+ cfg->pcie_type == PCIEM_TYPE_ROOT_EC)
+ WREG(PCIER_ROOT_CTL, cfg->pcie_root_ctl);
+
+ if (version > 1) {
+ WREG(PCIER_DEVICE_CTL2, cfg->pcie_device_ctl2);
+ WREG(PCIER_LINK_CTL2, cfg->pcie_link_ctl2);
+ WREG(PCIER_SLOT_CTL2, cfg->pcie_slot_ctl2);
+ }
+#undef WREG
+}
+
+static void
+pci_cfg_restore_pcix(device_t dev, struct pci_devinfo *dinfo)
+{
+ pci_write_config(dev, dinfo->cfg.pcix.pcix_location + PCIXR_COMMAND,
+ dinfo->cfg.pcix.pcix_command, 2);
+}
+
void
pci_cfg_restore(device_t dev, struct pci_devinfo *dinfo)
{
@@ -4425,6 +5192,14 @@
pci_write_config(dev, PCIR_PROGIF, dinfo->cfg.progif, 1);
pci_write_config(dev, PCIR_REVID, dinfo->cfg.revid, 1);
+ /*
+ * Restore extended capabilities for PCI-Express and PCI-X
+ */
+ if (dinfo->cfg.pcie.pcie_location != 0)
+ pci_cfg_restore_pcie(dev, dinfo);
+ if (dinfo->cfg.pcix.pcix_location != 0)
+ pci_cfg_restore_pcix(dev, dinfo);
+
/* Restore MSI and MSI-X configurations if they are present. */
if (dinfo->cfg.msi.msi_location != 0)
pci_resume_msi(dev);
@@ -4432,6 +5207,51 @@
pci_resume_msix(dev);
}
+static void
+pci_cfg_save_pcie(device_t dev, struct pci_devinfo *dinfo)
+{
+#define RREG(n) pci_read_config(dev, pos + (n), 2)
+ struct pcicfg_pcie *cfg;
+ int version, pos;
+
+ cfg = &dinfo->cfg.pcie;
+ pos = cfg->pcie_location;
+
+ cfg->pcie_flags = RREG(PCIER_FLAGS);
+
+ version = cfg->pcie_flags & PCIEM_FLAGS_VERSION;
+
+ cfg->pcie_device_ctl = RREG(PCIER_DEVICE_CTL);
+
+ if (version > 1 || cfg->pcie_type == PCIEM_TYPE_ROOT_PORT ||
+ cfg->pcie_type == PCIEM_TYPE_ENDPOINT ||
+ cfg->pcie_type == PCIEM_TYPE_LEGACY_ENDPOINT)
+ cfg->pcie_link_ctl = RREG(PCIER_LINK_CTL);
+
+ if (version > 1 || (cfg->pcie_type == PCIEM_TYPE_ROOT_PORT ||
+ (cfg->pcie_type == PCIEM_TYPE_DOWNSTREAM_PORT &&
+ (cfg->pcie_flags & PCIEM_FLAGS_SLOT))))
+ cfg->pcie_slot_ctl = RREG(PCIER_SLOT_CTL);
+
+ if (version > 1 || cfg->pcie_type == PCIEM_TYPE_ROOT_PORT ||
+ cfg->pcie_type == PCIEM_TYPE_ROOT_EC)
+ cfg->pcie_root_ctl = RREG(PCIER_ROOT_CTL);
+
+ if (version > 1) {
+ cfg->pcie_device_ctl2 = RREG(PCIER_DEVICE_CTL2);
+ cfg->pcie_link_ctl2 = RREG(PCIER_LINK_CTL2);
+ cfg->pcie_slot_ctl2 = RREG(PCIER_SLOT_CTL2);
+ }
+#undef RREG
+}
+
+static void
+pci_cfg_save_pcix(device_t dev, struct pci_devinfo *dinfo)
+{
+ dinfo->cfg.pcix.pcix_command = pci_read_config(dev,
+ dinfo->cfg.pcix.pcix_location + PCIXR_COMMAND, 2);
+}
+
void
pci_cfg_save(device_t dev, struct pci_devinfo *dinfo, int setstate)
{
@@ -4474,6 +5294,12 @@
dinfo->cfg.progif = pci_read_config(dev, PCIR_PROGIF, 1);
dinfo->cfg.revid = pci_read_config(dev, PCIR_REVID, 1);
+ if (dinfo->cfg.pcie.pcie_location != 0)
+ pci_cfg_save_pcie(dev, dinfo);
+
+ if (dinfo->cfg.pcix.pcix_location != 0)
+ pci_cfg_save_pcix(dev, dinfo);
+
/*
* don't set the state for display devices, base peripherals and
* memory devices since bad things happen when they are powered down.
@@ -4530,3 +5356,213 @@
dinfo = device_get_ivars(dev);
pci_cfg_restore(dev, dinfo);
}
+
+static uint16_t
+pci_get_rid_method(device_t dev, device_t child)
+{
+
+ return (PCIB_GET_RID(device_get_parent(dev), child));
+}
+
+/* Find the upstream port of a given PCI device in a root complex. */
+device_t
+pci_find_pcie_root_port(device_t dev)
+{
+ struct pci_devinfo *dinfo;
+ devclass_t pci_class;
+ device_t pcib, bus;
+
+ pci_class = devclass_find("pci");
+ KASSERT(device_get_devclass(device_get_parent(dev)) == pci_class,
+ ("%s: non-pci device %s", __func__, device_get_nameunit(dev)));
+
+ /*
+ * Walk the bridge hierarchy until we find a PCI-e root
+ * port or a non-PCI device.
+ */
+ for (;;) {
+ bus = device_get_parent(dev);
+ KASSERT(bus != NULL, ("%s: null parent of %s", __func__,
+ device_get_nameunit(dev)));
+
+ pcib = device_get_parent(bus);
+ KASSERT(pcib != NULL, ("%s: null bridge of %s", __func__,
+ device_get_nameunit(bus)));
+
+ /*
+ * pcib's parent must be a PCI bus for this to be a
+ * PCI-PCI bridge.
+ */
+ if (device_get_devclass(device_get_parent(pcib)) != pci_class)
+ return (NULL);
+
+ dinfo = device_get_ivars(pcib);
+ if (dinfo->cfg.pcie.pcie_location != 0 &&
+ dinfo->cfg.pcie.pcie_type == PCIEM_TYPE_ROOT_PORT)
+ return (pcib);
+
+ dev = pcib;
+ }
+}
+
+/*
+ * Wait for pending transactions to complete on a PCI-express function.
+ *
+ * The maximum delay is specified in milliseconds in max_delay. Note
+ * that this function may sleep.
+ *
+ * Returns true if the function is idle and false if the timeout is
+ * exceeded. If dev is not a PCI-express function, this returns true.
+ */
+bool
+pcie_wait_for_pending_transactions(device_t dev, u_int max_delay)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ uint16_t sta;
+ int cap;
+
+ cap = dinfo->cfg.pcie.pcie_location;
+ if (cap == 0)
+ return (true);
+
+ sta = pci_read_config(dev, cap + PCIER_DEVICE_STA, 2);
+ while (sta & PCIEM_STA_TRANSACTION_PND) {
+ if (max_delay == 0)
+ return (false);
+
+ /* Poll once every 100 milliseconds up to the timeout. */
+ if (max_delay > 100) {
+ pause_sbt("pcietp", 100 * SBT_1MS, 0, C_HARDCLOCK);
+ max_delay -= 100;
+ } else {
+ pause_sbt("pcietp", max_delay * SBT_1MS, 0,
+ C_HARDCLOCK);
+ max_delay = 0;
+ }
+ sta = pci_read_config(dev, cap + PCIER_DEVICE_STA, 2);
+ }
+
+ return (true);
+}
+
+/*
+ * Determine the maximum Completion Timeout in microseconds.
+ *
+ * For non-PCI-express functions this returns 0.
+ */
+int
+pcie_get_max_completion_timeout(device_t dev)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ int cap;
+
+ cap = dinfo->cfg.pcie.pcie_location;
+ if (cap == 0)
+ return (0);
+
+ /*
+ * Functions using the 1.x spec use the default timeout range of
+ * 50 microseconds to 50 milliseconds. Functions that do not
+ * support programmable timeouts also use this range.
+ */
+ if ((dinfo->cfg.pcie.pcie_flags & PCIEM_FLAGS_VERSION) < 2 ||
+ (pci_read_config(dev, cap + PCIER_DEVICE_CAP2, 4) &
+ PCIEM_CAP2_COMP_TIMO_RANGES) == 0)
+ return (50 * 1000);
+
+ switch (pci_read_config(dev, cap + PCIER_DEVICE_CTL2, 2) &
+ PCIEM_CTL2_COMP_TIMO_VAL) {
+ case PCIEM_CTL2_COMP_TIMO_100US:
+ return (100);
+ case PCIEM_CTL2_COMP_TIMO_10MS:
+ return (10 * 1000);
+ case PCIEM_CTL2_COMP_TIMO_55MS:
+ return (55 * 1000);
+ case PCIEM_CTL2_COMP_TIMO_210MS:
+ return (210 * 1000);
+ case PCIEM_CTL2_COMP_TIMO_900MS:
+ return (900 * 1000);
+ case PCIEM_CTL2_COMP_TIMO_3500MS:
+ return (3500 * 1000);
+ case PCIEM_CTL2_COMP_TIMO_13S:
+ return (13 * 1000 * 1000);
+ case PCIEM_CTL2_COMP_TIMO_64S:
+ return (64 * 1000 * 1000);
+ default:
+ return (50 * 1000);
+ }
+}
+
+/*
+ * Perform a Function Level Reset (FLR) on a device.
+ *
+ * This function first waits for any pending transactions to complete
+ * within the timeout specified by max_delay. If transactions are
+ * still pending, the function will return false without attempting a
+ * reset.
+ *
+ * If dev is not a PCI-express function or does not support FLR, this
+ * function returns false.
+ *
+ * Note that no registers are saved or restored. The caller is
+ * responsible for saving and restoring any registers including
+ * PCI-standard registers via pci_save_state() and
+ * pci_restore_state().
+ */
+bool
+pcie_flr(device_t dev, u_int max_delay, bool force)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ uint16_t cmd, ctl;
+ int compl_delay;
+ int cap;
+
+ cap = dinfo->cfg.pcie.pcie_location;
+ if (cap == 0)
+ return (false);
+
+ if (!(pci_read_config(dev, cap + PCIER_DEVICE_CAP, 4) & PCIEM_CAP_FLR))
+ return (false);
+
+ /*
+ * Disable busmastering to prevent generation of new
+ * transactions while waiting for the device to go idle. If
+ * the idle timeout fails, the command register is restored
+ * which will re-enable busmastering.
+ */
+ cmd = pci_read_config(dev, PCIR_COMMAND, 2);
+ pci_write_config(dev, PCIR_COMMAND, cmd & ~(PCIM_CMD_BUSMASTEREN), 2);
+ if (!pcie_wait_for_pending_transactions(dev, max_delay)) {
+ if (!force) {
+ pci_write_config(dev, PCIR_COMMAND, cmd, 2);
+ return (false);
+ }
+ pci_printf(&dinfo->cfg,
+ "Resetting with transactions pending after %d ms\n",
+ max_delay);
+
+ /*
+ * Extend the post-FLR delay to cover the maximum
+ * Completion Timeout delay of anything in flight
+ * during the FLR delay. Enforce a minimum delay of
+ * at least 10ms.
+ */
+ compl_delay = pcie_get_max_completion_timeout(dev) / 1000;
+ if (compl_delay < 10)
+ compl_delay = 10;
+ } else
+ compl_delay = 0;
+
+ /* Initiate the reset. */
+ ctl = pci_read_config(dev, cap + PCIER_DEVICE_CTL, 2);
+ pci_write_config(dev, cap + PCIER_DEVICE_CTL, ctl |
+ PCIEM_CTL_INITIATE_FLR, 2);
+
+ /* Wait for 100ms. */
+ pause_sbt("pcieflr", (100 + compl_delay) * SBT_1MS, 0, C_HARDCLOCK);
+
+ if (pci_read_config(dev, cap + PCIER_DEVICE_STA, 2) &
+ PCIEM_STA_TRANSACTION_PND)
+ pci_printf(&dinfo->cfg, "Transactions pending after FLR!\n");
+ return (true);
+}
Modified: trunk/sys/dev/pci/pci_if.m
===================================================================
--- trunk/sys/dev/pci/pci_if.m 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/pci_if.m 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
#-
# Copyright (c) 1998 Doug Rabson
# All rights reserved.
@@ -23,7 +24,7 @@
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
-# $MidnightBSD$
+# $FreeBSD: stable/10/sys/dev/pci/pci_if.m 294340 2016-01-19 21:08:31Z jhb $
#
#include <sys/bus.h>
@@ -36,6 +37,12 @@
{
return (0);
}
+
+ static int
+ null_msix_bar(device_t dev, device_t child)
+ {
+ return (-1);
+ }
};
@@ -105,6 +112,13 @@
device_t child;
};
+METHOD int find_cap {
+ device_t dev;
+ device_t child;
+ int capability;
+ int *capreg;
+};
+
METHOD int find_extcap {
device_t dev;
device_t child;
@@ -112,6 +126,13 @@
int *capreg;
};
+METHOD int find_htcap {
+ device_t dev;
+ device_t child;
+ int capability;
+ int *capreg;
+};
+
METHOD int alloc_msi {
device_t dev;
device_t child;
@@ -124,6 +145,26 @@
int *count;
};
+METHOD void enable_msi {
+ device_t dev;
+ device_t child;
+ uint64_t address;
+ uint16_t data;
+};
+
+METHOD void enable_msix {
+ device_t dev;
+ device_t child;
+ u_int index;
+ uint64_t address;
+ uint32_t data;
+};
+
+METHOD void disable_msi {
+ device_t dev;
+ device_t child;
+};
+
METHOD int remap_msix {
device_t dev;
device_t child;
@@ -145,3 +186,23 @@
device_t dev;
device_t child;
} DEFAULT null_msi_count;
+
+METHOD int msix_pba_bar {
+ device_t dev;
+ device_t child;
+} DEFAULT null_msix_bar;
+
+METHOD int msix_table_bar {
+ device_t dev;
+ device_t child;
+} DEFAULT null_msix_bar;
+
+METHOD uint16_t get_rid {
+ device_t dev;
+ device_t child;
+};
+
+METHOD void child_added {
+ device_t dev;
+ device_t child;
+};
Property changes on: trunk/sys/dev/pci/pci_if.m
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/pci/pci_pci.c
===================================================================
--- trunk/sys/dev/pci/pci_pci.c 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/pci_pci.c 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier
* Copyright (c) 2000 Michael Smith <msmith at freebsd.org>
@@ -29,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/pci/pci_pci.c 285863 2015-07-25 00:14:02Z jhb $");
/*
* PCI:PCI bridge support.
@@ -56,6 +57,14 @@
static int pcib_resume(device_t dev);
static int pcib_power_for_sleep(device_t pcib, device_t dev,
int *pstate);
+static uint16_t pcib_ari_get_rid(device_t pcib, device_t dev);
+static uint32_t pcib_read_config(device_t dev, u_int b, u_int s,
+ u_int f, u_int reg, int width);
+static void pcib_write_config(device_t dev, u_int b, u_int s,
+ u_int f, u_int reg, uint32_t val, int width);
+static int pcib_ari_maxslots(device_t dev);
+static int pcib_ari_maxfuncs(device_t dev);
+static int pcib_try_enable_ari(device_t pcib, device_t dev);
static device_method_t pcib_methods[] = {
/* Device interface */
@@ -83,7 +92,8 @@
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
/* pcib interface */
- DEVMETHOD(pcib_maxslots, pcib_maxslots),
+ DEVMETHOD(pcib_maxslots, pcib_ari_maxslots),
+ DEVMETHOD(pcib_maxfuncs, pcib_ari_maxfuncs),
DEVMETHOD(pcib_read_config, pcib_read_config),
DEVMETHOD(pcib_write_config, pcib_write_config),
DEVMETHOD(pcib_route_interrupt, pcib_route_interrupt),
@@ -93,6 +103,8 @@
DEVMETHOD(pcib_release_msix, pcib_release_msix),
DEVMETHOD(pcib_map_msi, pcib_map_msi),
DEVMETHOD(pcib_power_for_sleep, pcib_power_for_sleep),
+ DEVMETHOD(pcib_get_rid, pcib_ari_get_rid),
+ DEVMETHOD(pcib_try_enable_ari, pcib_try_enable_ari),
DEVMETHOD_END
};
@@ -100,17 +112,16 @@
static devclass_t pcib_devclass;
DEFINE_CLASS_0(pcib, pcib_driver, pcib_methods, sizeof(struct pcib_softc));
-DRIVER_MODULE(pcib, pci, pcib_driver, pcib_devclass, 0, 0);
+DRIVER_MODULE(pcib, pci, pcib_driver, pcib_devclass, NULL, NULL);
#ifdef NEW_PCIB
-/*
- * XXX Todo:
- * - properly handle the ISA enable bit. If it is set, we should change
- * the behavior of the I/O window resource and rman to not allocate the
- * blocked ranges (upper 768 bytes of each 1K in the first 64k of the
- * I/O port address space).
- */
+SYSCTL_DECL(_hw_pci);
+static int pci_clear_pcib;
+TUNABLE_INT("hw.pci.clear_pcib", &pci_clear_pcib);
+SYSCTL_INT(_hw_pci, OID_AUTO, clear_pcib, CTLFLAG_RDTUN, &pci_clear_pcib, 0,
+ "Clear firmware-assigned resources for PCI-PCI bridge I/O windows.");
+
/*
* Is a resource from a child device sub-allocated from one of our
* resource managers?
@@ -120,6 +131,10 @@
{
switch (type) {
+#ifdef PCI_RES_BUS
+ case PCI_RES_BUS:
+ return (rman_is_region_manager(r, &sc->bus.rman));
+#endif
case SYS_RES_IOPORT:
return (rman_is_region_manager(r, &sc->io.rman));
case SYS_RES_MEMORY:
@@ -189,10 +204,183 @@
}
}
+/*
+ * This is used to reject I/O port allocations that conflict with an
+ * ISA alias range.
+ */
+static int
+pcib_is_isa_range(struct pcib_softc *sc, u_long start, u_long end, u_long count)
+{
+ u_long next_alias;
+
+ if (!(sc->bridgectl & PCIB_BCR_ISA_ENABLE))
+ return (0);
+
+ /* Only check fixed ranges for overlap. */
+ if (start + count - 1 != end)
+ return (0);
+
+ /* ISA aliases are only in the lower 64KB of I/O space. */
+ if (start >= 65536)
+ return (0);
+
+ /* Check for overlap with 0x000 - 0x0ff as a special case. */
+ if (start < 0x100)
+ goto alias;
+
+ /*
+ * If the start address is an alias, the range is an alias.
+ * Otherwise, compute the start of the next alias range and
+ * check if it is before the end of the candidate range.
+ */
+ if ((start & 0x300) != 0)
+ goto alias;
+ next_alias = (start & ~0x3fful) | 0x100;
+ if (next_alias <= end)
+ goto alias;
+ return (0);
+
+alias:
+ if (bootverbose)
+ device_printf(sc->dev,
+ "I/O range %#lx-%#lx overlaps with an ISA alias\n", start,
+ end);
+ return (1);
+}
+
static void
+pcib_add_window_resources(struct pcib_window *w, struct resource **res,
+ int count)
+{
+ struct resource **newarray;
+ int error, i;
+
+ newarray = malloc(sizeof(struct resource *) * (w->count + count),
+ M_DEVBUF, M_WAITOK);
+ if (w->res != NULL)
+ bcopy(w->res, newarray, sizeof(struct resource *) * w->count);
+ bcopy(res, newarray + w->count, sizeof(struct resource *) * count);
+ free(w->res, M_DEVBUF);
+ w->res = newarray;
+ w->count += count;
+
+ for (i = 0; i < count; i++) {
+ error = rman_manage_region(&w->rman, rman_get_start(res[i]),
+ rman_get_end(res[i]));
+ if (error)
+ panic("Failed to add resource to rman");
+ }
+}
+
+typedef void (nonisa_callback)(u_long start, u_long end, void *arg);
+
+static void
+pcib_walk_nonisa_ranges(u_long start, u_long end, nonisa_callback *cb,
+ void *arg)
+{
+ u_long next_end;
+
+ /*
+ * If start is within an ISA alias range, move up to the start
+ * of the next non-alias range. As a special case, addresses
+ * in the range 0x000 - 0x0ff should also be skipped since
+ * those are used for various system I/O devices in ISA
+ * systems.
+ */
+ if (start <= 65535) {
+ if (start < 0x100 || (start & 0x300) != 0) {
+ start &= ~0x3ff;
+ start += 0x400;
+ }
+ }
+
+ /* ISA aliases are only in the lower 64KB of I/O space. */
+ while (start <= MIN(end, 65535)) {
+ next_end = MIN(start | 0xff, end);
+ cb(start, next_end, arg);
+ start += 0x400;
+ }
+
+ if (start <= end)
+ cb(start, end, arg);
+}
+
+static void
+count_ranges(u_long start, u_long end, void *arg)
+{
+ int *countp;
+
+ countp = arg;
+ (*countp)++;
+}
+
+struct alloc_state {
+ struct resource **res;
+ struct pcib_softc *sc;
+ int count, error;
+};
+
+static void
+alloc_ranges(u_long start, u_long end, void *arg)
+{
+ struct alloc_state *as;
+ struct pcib_window *w;
+ int rid;
+
+ as = arg;
+ if (as->error != 0)
+ return;
+
+ w = &as->sc->io;
+ rid = w->reg;
+ if (bootverbose)
+ device_printf(as->sc->dev,
+ "allocating non-ISA range %#lx-%#lx\n", start, end);
+ as->res[as->count] = bus_alloc_resource(as->sc->dev, SYS_RES_IOPORT,
+ &rid, start, end, end - start + 1, 0);
+ if (as->res[as->count] == NULL)
+ as->error = ENXIO;
+ else
+ as->count++;
+}
+
+static int
+pcib_alloc_nonisa_ranges(struct pcib_softc *sc, u_long start, u_long end)
+{
+ struct alloc_state as;
+ int i, new_count;
+
+ /* First, see how many ranges we need. */
+ new_count = 0;
+ pcib_walk_nonisa_ranges(start, end, count_ranges, &new_count);
+
+ /* Second, allocate the ranges. */
+ as.res = malloc(sizeof(struct resource *) * new_count, M_DEVBUF,
+ M_WAITOK);
+ as.sc = sc;
+ as.count = 0;
+ as.error = 0;
+ pcib_walk_nonisa_ranges(start, end, alloc_ranges, &as);
+ if (as.error != 0) {
+ for (i = 0; i < as.count; i++)
+ bus_release_resource(sc->dev, SYS_RES_IOPORT,
+ sc->io.reg, as.res[i]);
+ free(as.res, M_DEVBUF);
+ return (as.error);
+ }
+ KASSERT(as.count == new_count, ("%s: count mismatch", __func__));
+
+ /* Third, add the ranges to the window. */
+ pcib_add_window_resources(&sc->io, as.res, as.count);
+ free(as.res, M_DEVBUF);
+ return (0);
+}
+
+static void
pcib_alloc_window(struct pcib_softc *sc, struct pcib_window *w, int type,
int flags, pci_addr_t max_address)
{
+ struct resource *res;
char buf[64];
int error, rid;
@@ -217,9 +405,15 @@
"initial %s window has too many bits, ignoring\n", w->name);
return;
}
- rid = w->reg;
- w->res = bus_alloc_resource(sc->dev, type, &rid, w->base, w->limit,
- w->limit - w->base + 1, flags);
+ if (type == SYS_RES_IOPORT && sc->bridgectl & PCIB_BCR_ISA_ENABLE)
+ (void)pcib_alloc_nonisa_ranges(sc, w->base, w->limit);
+ else {
+ rid = w->reg;
+ res = bus_alloc_resource(sc->dev, type, &rid, w->base, w->limit,
+ w->limit - w->base + 1, flags);
+ if (res != NULL)
+ pcib_add_window_resources(w, &res, 1);
+ }
if (w->res == NULL) {
device_printf(sc->dev,
"failed to allocate initial %s window: %#jx-%#jx\n",
@@ -230,11 +424,6 @@
return;
}
pcib_activate_window(sc, type);
-
- error = rman_manage_region(&w->rman, rman_get_start(w->res),
- rman_get_end(w->res));
- if (error)
- panic("Failed to initialize rman with resource");
}
/*
@@ -249,6 +438,19 @@
dev = sc->dev;
+ if (pci_clear_pcib) {
+ pci_write_config(dev, PCIR_IOBASEL_1, 0xff, 1);
+ pci_write_config(dev, PCIR_IOBASEH_1, 0xffff, 2);
+ pci_write_config(dev, PCIR_IOLIMITL_1, 0, 1);
+ pci_write_config(dev, PCIR_IOLIMITH_1, 0, 2);
+ pci_write_config(dev, PCIR_MEMBASE_1, 0xffff, 2);
+ pci_write_config(dev, PCIR_MEMLIMIT_1, 0, 2);
+ pci_write_config(dev, PCIR_PMBASEL_1, 0xffff, 2);
+ pci_write_config(dev, PCIR_PMBASEH_1, 0xffffffff, 4);
+ pci_write_config(dev, PCIR_PMLIMITL_1, 0, 2);
+ pci_write_config(dev, PCIR_PMLIMITH_1, 0, 4);
+ }
+
/* Determine if the I/O port window is implemented. */
val = pci_read_config(dev, PCIR_IOBASEL_1, 1);
if (val == 0) {
@@ -337,6 +539,173 @@
}
}
+#ifdef PCI_RES_BUS
+/*
+ * Allocate a suitable secondary bus for this bridge if needed and
+ * initialize the resource manager for the secondary bus range. Note
+ * that the minimum count is a desired value and this may allocate a
+ * smaller range.
+ */
+void
+pcib_setup_secbus(device_t dev, struct pcib_secbus *bus, int min_count)
+{
+ char buf[64];
+ int error, rid;
+
+ switch (pci_read_config(dev, PCIR_HDRTYPE, 1) & PCIM_HDRTYPE) {
+ case PCIM_HDRTYPE_BRIDGE:
+ bus->sub_reg = PCIR_SUBBUS_1;
+ break;
+ case PCIM_HDRTYPE_CARDBUS:
+ bus->sub_reg = PCIR_SUBBUS_2;
+ break;
+ default:
+ panic("not a PCI bridge");
+ }
+ bus->dev = dev;
+ bus->rman.rm_start = 0;
+ bus->rman.rm_end = PCI_BUSMAX;
+ bus->rman.rm_type = RMAN_ARRAY;
+ snprintf(buf, sizeof(buf), "%s bus numbers", device_get_nameunit(dev));
+ bus->rman.rm_descr = strdup(buf, M_DEVBUF);
+ error = rman_init(&bus->rman);
+ if (error)
+ panic("Failed to initialize %s bus number rman",
+ device_get_nameunit(dev));
+
+ /*
+ * Allocate a bus range. This will return an existing bus range
+ * if one exists, or a new bus range if one does not.
+ */
+ rid = 0;
+ bus->res = bus_alloc_resource(dev, PCI_RES_BUS, &rid, 0ul, ~0ul,
+ min_count, 0);
+ if (bus->res == NULL) {
+ /*
+ * Fall back to just allocating a range of a single bus
+ * number.
+ */
+ bus->res = bus_alloc_resource(dev, PCI_RES_BUS, &rid, 0ul, ~0ul,
+ 1, 0);
+ } else if (rman_get_size(bus->res) < min_count)
+ /*
+ * Attempt to grow the existing range to satisfy the
+ * minimum desired count.
+ */
+ (void)bus_adjust_resource(dev, PCI_RES_BUS, bus->res,
+ rman_get_start(bus->res), rman_get_start(bus->res) +
+ min_count - 1);
+
+ /*
+ * Add the initial resource to the rman.
+ */
+ if (bus->res != NULL) {
+ error = rman_manage_region(&bus->rman, rman_get_start(bus->res),
+ rman_get_end(bus->res));
+ if (error)
+ panic("Failed to add resource to rman");
+ bus->sec = rman_get_start(bus->res);
+ bus->sub = rman_get_end(bus->res);
+ }
+}
+
+static struct resource *
+pcib_suballoc_bus(struct pcib_secbus *bus, device_t child, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct resource *res;
+
+ res = rman_reserve_resource(&bus->rman, start, end, count, flags,
+ child);
+ if (res == NULL)
+ return (NULL);
+
+ if (bootverbose)
+ device_printf(bus->dev,
+ "allocated bus range (%lu-%lu) for rid %d of %s\n",
+ rman_get_start(res), rman_get_end(res), *rid,
+ pcib_child_name(child));
+ rman_set_rid(res, *rid);
+ return (res);
+}
+
+/*
+ * Attempt to grow the secondary bus range. This is much simpler than
+ * for I/O windows as the range can only be grown by increasing
+ * subbus.
+ */
+static int
+pcib_grow_subbus(struct pcib_secbus *bus, u_long new_end)
+{
+ u_long old_end;
+ int error;
+
+ old_end = rman_get_end(bus->res);
+ KASSERT(new_end > old_end, ("attempt to shrink subbus"));
+ error = bus_adjust_resource(bus->dev, PCI_RES_BUS, bus->res,
+ rman_get_start(bus->res), new_end);
+ if (error)
+ return (error);
+ if (bootverbose)
+ device_printf(bus->dev, "grew bus range to %lu-%lu\n",
+ rman_get_start(bus->res), rman_get_end(bus->res));
+ error = rman_manage_region(&bus->rman, old_end + 1,
+ rman_get_end(bus->res));
+ if (error)
+ panic("Failed to add resource to rman");
+ bus->sub = rman_get_end(bus->res);
+ pci_write_config(bus->dev, bus->sub_reg, bus->sub, 1);
+ return (0);
+}
+
+struct resource *
+pcib_alloc_subbus(struct pcib_secbus *bus, device_t child, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct resource *res;
+ u_long start_free, end_free, new_end;
+
+ /*
+ * First, see if the request can be satisified by the existing
+ * bus range.
+ */
+ res = pcib_suballoc_bus(bus, child, rid, start, end, count, flags);
+ if (res != NULL)
+ return (res);
+
+ /*
+ * Figure out a range to grow the bus range. First, find the
+ * first bus number after the last allocated bus in the rman and
+ * enforce that as a minimum starting point for the range.
+ */
+ if (rman_last_free_region(&bus->rman, &start_free, &end_free) != 0 ||
+ end_free != bus->sub)
+ start_free = bus->sub + 1;
+ if (start_free < start)
+ start_free = start;
+ new_end = start_free + count - 1;
+
+ /*
+ * See if this new range would satisfy the request if it
+ * succeeds.
+ */
+ if (new_end > end)
+ return (NULL);
+
+ /* Finally, attempt to grow the existing resource. */
+ if (bootverbose) {
+ device_printf(bus->dev,
+ "attempting to grow bus range for %lu buses\n", count);
+ printf("\tback candidate range: %lu-%lu\n", start_free,
+ new_end);
+ }
+ if (pcib_grow_subbus(bus, new_end) == 0)
+ return (pcib_suballoc_bus(bus, child, rid, start, end, count,
+ flags));
+ return (NULL);
+}
+#endif
+
#else
/*
@@ -483,8 +852,8 @@
sc->command = pci_read_config(dev, PCIR_COMMAND, 2);
sc->pribus = pci_read_config(dev, PCIR_PRIBUS_1, 1);
- sc->secbus = pci_read_config(dev, PCIR_SECBUS_1, 1);
- sc->subbus = pci_read_config(dev, PCIR_SUBBUS_1, 1);
+ sc->bus.sec = pci_read_config(dev, PCIR_SECBUS_1, 1);
+ sc->bus.sub = pci_read_config(dev, PCIR_SUBBUS_1, 1);
sc->bridgectl = pci_read_config(dev, PCIR_BRIDGECTL_1, 2);
sc->seclat = pci_read_config(dev, PCIR_SECLAT_1, 1);
#ifndef NEW_PCIB
@@ -507,8 +876,8 @@
pci_write_config(dev, PCIR_COMMAND, sc->command, 2);
pci_write_config(dev, PCIR_PRIBUS_1, sc->pribus, 1);
- pci_write_config(dev, PCIR_SECBUS_1, sc->secbus, 1);
- pci_write_config(dev, PCIR_SUBBUS_1, sc->subbus, 1);
+ pci_write_config(dev, PCIR_SECBUS_1, sc->bus.sec, 1);
+ pci_write_config(dev, PCIR_SUBBUS_1, sc->bus.sub, 1);
pci_write_config(dev, PCIR_BRIDGECTL_1, sc->bridgectl, 2);
pci_write_config(dev, PCIR_SECLAT_1, sc->seclat, 1);
#ifdef NEW_PCIB
@@ -541,6 +910,7 @@
struct pcib_softc *sc;
struct sysctl_ctx_list *sctx;
struct sysctl_oid *soid;
+ int comma;
sc = device_get_softc(dev);
sc->dev = dev;
@@ -553,6 +923,13 @@
pcib_cfg_save(sc);
/*
+ * The primary bus register should always be the bus of the
+ * parent.
+ */
+ sc->pribus = pci_get_bus(dev);
+ pci_write_config(dev, PCIR_PRIBUS_1, sc->pribus, 1);
+
+ /*
* Setup sysctl reporting nodes
*/
sctx = device_get_sysctl_ctx(dev);
@@ -562,14 +939,15 @@
SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "pribus",
CTLFLAG_RD, &sc->pribus, 0, "Primary bus number");
SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "secbus",
- CTLFLAG_RD, &sc->secbus, 0, "Secondary bus number");
+ CTLFLAG_RD, &sc->bus.sec, 0, "Secondary bus number");
SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "subbus",
- CTLFLAG_RD, &sc->subbus, 0, "Subordinate bus number");
+ CTLFLAG_RD, &sc->bus.sub, 0, "Subordinate bus number");
/*
* Quirk handling.
*/
switch (pci_get_devid(dev)) {
+#if !(defined(NEW_PCIB) && defined(PCI_RES_BUS))
case 0x12258086: /* Intel 82454KX/GX (Orion) */
{
uint8_t supbus;
@@ -576,11 +954,12 @@
supbus = pci_read_config(dev, 0x41, 1);
if (supbus != 0xff) {
- sc->secbus = supbus + 1;
- sc->subbus = supbus + 1;
+ sc->bus.sec = supbus + 1;
+ sc->bus.sub = supbus + 1;
}
break;
}
+#endif
/*
* The i82380FB mobile docking controller is a PCI-PCI bridge,
@@ -594,6 +973,7 @@
sc->flags |= PCIB_SUBTRACTIVE;
break;
+#if !(defined(NEW_PCIB) && defined(PCI_RES_BUS))
/* Compaq R3000 BIOS sets wrong subordinate bus number. */
case 0x00dd10de:
{
@@ -613,17 +993,21 @@
break;
}
freeenv(cp);
- if (sc->subbus < 0xa) {
+ if (sc->bus.sub < 0xa) {
pci_write_config(dev, PCIR_SUBBUS_1, 0xa, 1);
- sc->subbus = pci_read_config(dev, PCIR_SUBBUS_1, 1);
+ sc->bus.sub = pci_read_config(dev, PCIR_SUBBUS_1, 1);
}
break;
}
+#endif
}
if (pci_msi_device_blacklisted(dev))
sc->flags |= PCIB_DISABLE_MSI;
+ if (pci_msix_device_blacklisted(dev))
+ sc->flags |= PCIB_DISABLE_MSIX;
+
/*
* Intel 815, 845 and other chipsets say they are PCI-PCI bridges,
* but have a ProgIF of 0x80. The 82801 family (AA, AB, BAM/CAM,
@@ -637,12 +1021,15 @@
sc->flags |= PCIB_SUBTRACTIVE;
#ifdef NEW_PCIB
+#ifdef PCI_RES_BUS
+ pcib_setup_secbus(dev, &sc->bus, 1);
+#endif
pcib_probe_windows(sc);
#endif
if (bootverbose) {
device_printf(dev, " domain %d\n", sc->domain);
- device_printf(dev, " secondary bus %d\n", sc->secbus);
- device_printf(dev, " subordinate bus %d\n", sc->subbus);
+ device_printf(dev, " secondary bus %d\n", sc->bus.sec);
+ device_printf(dev, " subordinate bus %d\n", sc->bus.sub);
#ifdef NEW_PCIB
if (pcib_is_window_open(&sc->io))
device_printf(dev, " I/O decode 0x%jx-0x%jx\n",
@@ -664,27 +1051,25 @@
device_printf(dev, " prefetched decode 0x%jx-0x%jx\n",
(uintmax_t)sc->pmembase, (uintmax_t)sc->pmemlimit);
#endif
- else
- device_printf(dev, " no prefetched decode\n");
- if (sc->flags & PCIB_SUBTRACTIVE)
- device_printf(dev, " Subtractively decoded bridge.\n");
+ if (sc->bridgectl & (PCIB_BCR_ISA_ENABLE | PCIB_BCR_VGA_ENABLE) ||
+ sc->flags & PCIB_SUBTRACTIVE) {
+ device_printf(dev, " special decode ");
+ comma = 0;
+ if (sc->bridgectl & PCIB_BCR_ISA_ENABLE) {
+ printf("ISA");
+ comma = 1;
+ }
+ if (sc->bridgectl & PCIB_BCR_VGA_ENABLE) {
+ printf("%sVGA", comma ? ", " : "");
+ comma = 1;
+ }
+ if (sc->flags & PCIB_SUBTRACTIVE)
+ printf("%ssubtractive", comma ? ", " : "");
+ printf("\n");
+ }
}
/*
- * XXX If the secondary bus number is zero, we should assign a bus number
- * since the BIOS hasn't, then initialise the bridge. A simple
- * bus_alloc_resource with the a couple of busses seems like the right
- * approach, but we don't know what busses the BIOS might have already
- * assigned to other bridges on this bus that probe later than we do.
- *
- * If the subordinate bus number is less than the secondary bus number,
- * we should pick a better value. One sensible alternative would be to
- * pick 255; the only tradeoff here is that configuration transactions
- * would be more widely routed than absolutely necessary. We could
- * then do a walk of the tree later and fix it.
- */
-
- /*
* Always enable busmastering on bridges so that transactions
* initiated on the secondary bus are passed through to the
* primary bus.
@@ -700,8 +1085,8 @@
pcib_attach_common(dev);
sc = device_get_softc(dev);
- if (sc->secbus != 0) {
- child = device_add_child(dev, "pci", sc->secbus);
+ if (sc->bus.sec != 0) {
+ child = device_add_child(dev, "pci", sc->bus.sec);
if (child != NULL)
return(bus_generic_attach(dev));
}
@@ -713,30 +1098,15 @@
int
pcib_suspend(device_t dev)
{
- device_t pcib;
- int dstate, error;
pcib_cfg_save(device_get_softc(dev));
- error = bus_generic_suspend(dev);
- if (error == 0 && pci_do_power_suspend) {
- dstate = PCI_POWERSTATE_D3;
- pcib = device_get_parent(device_get_parent(dev));
- if (PCIB_POWER_FOR_SLEEP(pcib, dev, &dstate) == 0)
- pci_set_powerstate(dev, dstate);
- }
- return (error);
+ return (bus_generic_suspend(dev));
}
int
pcib_resume(device_t dev)
{
- device_t pcib;
- if (pci_do_power_resume) {
- pcib = device_get_parent(device_get_parent(dev));
- if (PCIB_POWER_FOR_SLEEP(pcib, dev, NULL) == 0)
- pci_set_powerstate(dev, PCI_POWERSTATE_D0);
- }
pcib_cfg_restore(device_get_softc(dev));
return (bus_generic_resume(dev));
}
@@ -751,7 +1121,7 @@
*result = sc->domain;
return(0);
case PCIB_IVAR_BUS:
- *result = sc->secbus;
+ *result = sc->bus.sec;
return(0);
}
return(ENOENT);
@@ -760,14 +1130,12 @@
int
pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
{
- struct pcib_softc *sc = device_get_softc(dev);
switch (which) {
case PCIB_IVAR_DOMAIN:
return(EINVAL);
case PCIB_IVAR_BUS:
- sc->secbus = value;
- return(0);
+ return(EINVAL);
}
return(ENOENT);
}
@@ -814,9 +1182,177 @@
return (res);
}
+/* Allocate a fresh resource range for an unconfigured window. */
+static int
+pcib_alloc_new_window(struct pcib_softc *sc, struct pcib_window *w, int type,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct resource *res;
+ u_long base, limit, wmask;
+ int rid;
+
+ /*
+ * If this is an I/O window on a bridge with ISA enable set
+ * and the start address is below 64k, then try to allocate an
+ * initial window of 0x1000 bytes long starting at address
+ * 0xf000 and walking down. Note that if the original request
+ * was larger than the non-aliased range size of 0x100 our
+ * caller would have raised the start address up to 64k
+ * already.
+ */
+ if (type == SYS_RES_IOPORT && sc->bridgectl & PCIB_BCR_ISA_ENABLE &&
+ start < 65536) {
+ for (base = 0xf000; (long)base >= 0; base -= 0x1000) {
+ limit = base + 0xfff;
+
+ /*
+ * Skip ranges that wouldn't work for the
+ * original request. Note that the actual
+ * window that overlaps are the non-alias
+ * ranges within [base, limit], so this isn't
+ * quite a simple comparison.
+ */
+ if (start + count > limit - 0x400)
+ continue;
+ if (base == 0) {
+ /*
+ * The first open region for the window at
+ * 0 is 0x400-0x4ff.
+ */
+ if (end - count + 1 < 0x400)
+ continue;
+ } else {
+ if (end - count + 1 < base)
+ continue;
+ }
+
+ if (pcib_alloc_nonisa_ranges(sc, base, limit) == 0) {
+ w->base = base;
+ w->limit = limit;
+ return (0);
+ }
+ }
+ return (ENOSPC);
+ }
+
+ wmask = (1ul << w->step) - 1;
+ if (RF_ALIGNMENT(flags) < w->step) {
+ flags &= ~RF_ALIGNMENT_MASK;
+ flags |= RF_ALIGNMENT_LOG2(w->step);
+ }
+ start &= ~wmask;
+ end |= wmask;
+ count = roundup2(count, 1ul << w->step);
+ rid = w->reg;
+ res = bus_alloc_resource(sc->dev, type, &rid, start, end, count,
+ flags & ~RF_ACTIVE);
+ if (res == NULL)
+ return (ENOSPC);
+ pcib_add_window_resources(w, &res, 1);
+ pcib_activate_window(sc, type);
+ w->base = rman_get_start(res);
+ w->limit = rman_get_end(res);
+ return (0);
+}
+
+/* Try to expand an existing window to the requested base and limit. */
+static int
+pcib_expand_window(struct pcib_softc *sc, struct pcib_window *w, int type,
+ u_long base, u_long limit)
+{
+ struct resource *res;
+ int error, i, force_64k_base;
+
+ KASSERT(base <= w->base && limit >= w->limit,
+ ("attempting to shrink window"));
+
+ /*
+ * XXX: pcib_grow_window() doesn't try to do this anyway and
+ * the error handling for all the edge cases would be tedious.
+ */
+ KASSERT(limit == w->limit || base == w->base,
+ ("attempting to grow both ends of a window"));
+
+ /*
+ * Yet more special handling for requests to expand an I/O
+ * window behind an ISA-enabled bridge. Since I/O windows
+ * have to grow in 0x1000 increments and the end of the 0xffff
+ * range is an alias, growing a window below 64k will always
+ * result in allocating new resources and never adjusting an
+ * existing resource.
+ */
+ if (type == SYS_RES_IOPORT && sc->bridgectl & PCIB_BCR_ISA_ENABLE &&
+ (limit <= 65535 || (base <= 65535 && base != w->base))) {
+ KASSERT(limit == w->limit || limit <= 65535,
+ ("attempting to grow both ends across 64k ISA alias"));
+
+ if (base != w->base)
+ error = pcib_alloc_nonisa_ranges(sc, base, w->base - 1);
+ else
+ error = pcib_alloc_nonisa_ranges(sc, w->limit + 1,
+ limit);
+ if (error == 0) {
+ w->base = base;
+ w->limit = limit;
+ }
+ return (error);
+ }
+
+ /*
+ * Find the existing resource to adjust. Usually there is only one,
+ * but for an ISA-enabled bridge we might be growing the I/O window
+ * above 64k and need to find the existing resource that maps all
+ * of the area above 64k.
+ */
+ for (i = 0; i < w->count; i++) {
+ if (rman_get_end(w->res[i]) == w->limit)
+ break;
+ }
+ KASSERT(i != w->count, ("did not find existing resource"));
+ res = w->res[i];
+
+ /*
+ * Usually the resource we found should match the window's
+ * existing range. The one exception is the ISA-enabled case
+ * mentioned above in which case the resource should start at
+ * 64k.
+ */
+ if (type == SYS_RES_IOPORT && sc->bridgectl & PCIB_BCR_ISA_ENABLE &&
+ w->base <= 65535) {
+ KASSERT(rman_get_start(res) == 65536,
+ ("existing resource mismatch"));
+ force_64k_base = 1;
+ } else {
+ KASSERT(w->base == rman_get_start(res),
+ ("existing resource mismatch"));
+ force_64k_base = 0;
+ }
+
+ error = bus_adjust_resource(sc->dev, type, res, force_64k_base ?
+ rman_get_start(res) : base, limit);
+ if (error)
+ return (error);
+
+ /* Add the newly allocated region to the resource manager. */
+ if (w->base != base) {
+ error = rman_manage_region(&w->rman, base, w->base - 1);
+ w->base = base;
+ } else {
+ error = rman_manage_region(&w->rman, w->limit + 1, limit);
+ w->limit = limit;
+ }
+ if (error) {
+ if (bootverbose)
+ device_printf(sc->dev,
+ "failed to expand %s resource manager\n", w->name);
+ (void)bus_adjust_resource(sc->dev, type, res, force_64k_base ?
+ rman_get_start(res) : w->base, w->limit);
+ }
+ return (error);
+}
+
/*
* Attempt to grow a window to make room for a given resource request.
- * The 'step' parameter is log_2 of the desired I/O window's alignment.
*/
static int
pcib_grow_window(struct pcib_softc *sc, struct pcib_window *w, int type,
@@ -823,14 +1359,20 @@
u_long start, u_long end, u_long count, u_int flags)
{
u_long align, start_free, end_free, front, back, wmask;
- int error, rid;
+ int error;
/*
* Clamp the desired resource range to the maximum address
* this window supports. Reject impossible requests.
+ *
+ * For I/O port requests behind a bridge with the ISA enable
+ * bit set, force large allocations to start above 64k.
*/
if (!w->valid)
return (EINVAL);
+ if (sc->bridgectl & PCIB_BCR_ISA_ENABLE && count > 0x100 &&
+ start < 65536)
+ start = 65536;
if (end > w->rman.rm_end)
end = w->rman.rm_end;
if (start + count - 1 > end || start + count < start)
@@ -842,40 +1384,19 @@
* aligned space for this resource.
*/
if (w->res == NULL) {
- if (RF_ALIGNMENT(flags) < w->step) {
- flags &= ~RF_ALIGNMENT_MASK;
- flags |= RF_ALIGNMENT_LOG2(w->step);
- }
- start &= ~wmask;
- end |= wmask;
- count = roundup2(count, 1ul << w->step);
- rid = w->reg;
- w->res = bus_alloc_resource(sc->dev, type, &rid, start, end,
- count, flags & ~RF_ACTIVE);
- if (w->res == NULL) {
+ error = pcib_alloc_new_window(sc, w, type, start, end, count,
+ flags);
+ if (error) {
if (bootverbose)
device_printf(sc->dev,
"failed to allocate initial %s window (%#lx-%#lx,%#lx)\n",
w->name, start, end, count);
- return (ENXIO);
+ return (error);
}
if (bootverbose)
device_printf(sc->dev,
- "allocated initial %s window of %#lx-%#lx\n",
- w->name, rman_get_start(w->res),
- rman_get_end(w->res));
- error = rman_manage_region(&w->rman, rman_get_start(w->res),
- rman_get_end(w->res));
- if (error) {
- if (bootverbose)
- device_printf(sc->dev,
- "failed to add initial %s window to rman\n",
- w->name);
- bus_release_resource(sc->dev, type, w->reg, w->res);
- w->res = NULL;
- return (error);
- }
- pcib_activate_window(sc, type);
+ "allocated initial %s window of %#jx-%#jx\n",
+ w->name, (uintmax_t)w->base, (uintmax_t)w->limit);
goto updatewin;
}
@@ -889,6 +1410,11 @@
* edge of the window, grow from the inner edge of the free
* region. Otherwise grow from the window boundary.
*
+ * Growing an I/O window below 64k for a bridge with the ISA
+ * enable bit doesn't require any special magic as the step
+ * size of an I/O window (1k) always includes multiple
+ * non-alias ranges when it is grown in either direction.
+ *
* XXX: Special case: if w->res is completely empty and the
* request size is larger than w->res, we should find the
* optimal aligned buffer containing w->res and allocate that.
@@ -898,10 +1424,10 @@
"attempting to grow %s window for (%#lx-%#lx,%#lx)\n",
w->name, start, end, count);
align = 1ul << RF_ALIGNMENT(flags);
- if (start < rman_get_start(w->res)) {
+ if (start < w->base) {
if (rman_first_free_region(&w->rman, &start_free, &end_free) !=
- 0 || start_free != rman_get_start(w->res))
- end_free = rman_get_start(w->res);
+ 0 || start_free != w->base)
+ end_free = w->base;
if (end_free > end)
end_free = end + 1;
@@ -922,15 +1448,15 @@
printf("\tfront candidate range: %#lx-%#lx\n",
front, end_free);
front &= ~wmask;
- front = rman_get_start(w->res) - front;
+ front = w->base - front;
} else
front = 0;
} else
front = 0;
- if (end > rman_get_end(w->res)) {
+ if (end > w->limit) {
if (rman_last_free_region(&w->rman, &start_free, &end_free) !=
- 0 || end_free != rman_get_end(w->res))
- start_free = rman_get_end(w->res) + 1;
+ 0 || end_free != w->limit)
+ start_free = w->limit + 1;
if (start_free < start)
start_free = start;
@@ -950,7 +1476,7 @@
printf("\tback candidate range: %#lx-%#lx\n",
start_free, back);
back |= wmask;
- back -= rman_get_end(w->res);
+ back -= w->limit;
} else
back = 0;
} else
@@ -963,16 +1489,14 @@
error = ENOSPC;
while (front != 0 || back != 0) {
if (front != 0 && (front <= back || back == 0)) {
- error = bus_adjust_resource(sc->dev, type, w->res,
- rman_get_start(w->res) - front,
- rman_get_end(w->res));
+ error = pcib_expand_window(sc, w, type, w->base - front,
+ w->limit);
if (error == 0)
break;
front = 0;
} else {
- error = bus_adjust_resource(sc->dev, type, w->res,
- rman_get_start(w->res),
- rman_get_end(w->res) + back);
+ error = pcib_expand_window(sc, w, type, w->base,
+ w->limit + back);
if (error == 0)
break;
back = 0;
@@ -982,32 +1506,11 @@
if (error)
return (error);
if (bootverbose)
- device_printf(sc->dev, "grew %s window to %#lx-%#lx\n",
- w->name, rman_get_start(w->res), rman_get_end(w->res));
+ device_printf(sc->dev, "grew %s window to %#jx-%#jx\n",
+ w->name, (uintmax_t)w->base, (uintmax_t)w->limit);
- /* Add the newly allocated region to the resource manager. */
- if (w->base != rman_get_start(w->res)) {
- KASSERT(w->limit == rman_get_end(w->res), ("both ends moved"));
- error = rman_manage_region(&w->rman, rman_get_start(w->res),
- w->base - 1);
- } else {
- KASSERT(w->limit != rman_get_end(w->res),
- ("neither end moved"));
- error = rman_manage_region(&w->rman, w->limit + 1,
- rman_get_end(w->res));
- }
- if (error) {
- if (bootverbose)
- device_printf(sc->dev,
- "failed to expand %s resource manager\n", w->name);
- bus_adjust_resource(sc->dev, type, w->res, w->base, w->limit);
- return (error);
- }
-
updatewin:
- /* Save the new window. */
- w->base = rman_get_start(w->res);
- w->limit = rman_get_end(w->res);
+ /* Write the new window. */
KASSERT((w->base & wmask) == 0, ("start address is not aligned"));
KASSERT((w->limit & wmask) == wmask, ("end address is not aligned"));
pcib_write_windows(sc, w->mask);
@@ -1042,7 +1545,14 @@
}
switch (type) {
+#ifdef PCI_RES_BUS
+ case PCI_RES_BUS:
+ return (pcib_alloc_subbus(&sc->bus, child, rid, start, end,
+ count, flags));
+#endif
case SYS_RES_IOPORT:
+ if (pcib_is_isa_range(sc, start, end, count))
+ return (NULL);
r = pcib_suballoc_resource(sc, &sc->io, child, type, rid, start,
end, count, flags);
if (r != NULL || (sc->flags & PCIB_SUBTRACTIVE) != 0)
@@ -1289,27 +1799,94 @@
#endif
/*
+ * If ARI is enabled on this downstream port, translate the function number
+ * to the non-ARI slot/function. The downstream port will convert it back in
+ * hardware. If ARI is not enabled slot and func are not modified.
+ */
+static __inline void
+pcib_xlate_ari(device_t pcib, int bus, int *slot, int *func)
+{
+ struct pcib_softc *sc;
+ int ari_func;
+
+ sc = device_get_softc(pcib);
+ ari_func = *func;
+
+ if (sc->flags & PCIB_ENABLE_ARI) {
+ KASSERT(*slot == 0,
+ ("Non-zero slot number with ARI enabled!"));
+ *slot = PCIE_ARI_SLOT(ari_func);
+ *func = PCIE_ARI_FUNC(ari_func);
+ }
+}
+
+
+static void
+pcib_enable_ari(struct pcib_softc *sc, uint32_t pcie_pos)
+{
+ uint32_t ctl2;
+
+ ctl2 = pci_read_config(sc->dev, pcie_pos + PCIER_DEVICE_CTL2, 4);
+ ctl2 |= PCIEM_CTL2_ARI;
+ pci_write_config(sc->dev, pcie_pos + PCIER_DEVICE_CTL2, ctl2, 4);
+
+ sc->flags |= PCIB_ENABLE_ARI;
+}
+
+/*
* PCIB interface.
*/
int
pcib_maxslots(device_t dev)
{
- return(PCI_SLOTMAX);
+ return (PCI_SLOTMAX);
}
+static int
+pcib_ari_maxslots(device_t dev)
+{
+ struct pcib_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->flags & PCIB_ENABLE_ARI)
+ return (PCIE_ARI_SLOTMAX);
+ else
+ return (PCI_SLOTMAX);
+}
+
+static int
+pcib_ari_maxfuncs(device_t dev)
+{
+ struct pcib_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->flags & PCIB_ENABLE_ARI)
+ return (PCIE_ARI_FUNCMAX);
+ else
+ return (PCI_FUNCMAX);
+}
+
/*
* Since we are a child of a PCI bus, its parent must support the pcib interface.
*/
-uint32_t
+static uint32_t
pcib_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width)
{
- return(PCIB_READ_CONFIG(device_get_parent(device_get_parent(dev)), b, s, f, reg, width));
+
+ pcib_xlate_ari(dev, b, &s, &f);
+ return(PCIB_READ_CONFIG(device_get_parent(device_get_parent(dev)), b, s,
+ f, reg, width));
}
-void
+static void
pcib_write_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, uint32_t val, int width)
{
- PCIB_WRITE_CONFIG(device_get_parent(device_get_parent(dev)), b, s, f, reg, val, width);
+
+ pcib_xlate_ari(dev, b, &s, &f);
+ PCIB_WRITE_CONFIG(device_get_parent(device_get_parent(dev)), b, s, f,
+ reg, val, width);
}
/*
@@ -1379,7 +1956,7 @@
struct pcib_softc *sc = device_get_softc(pcib);
device_t bus;
- if (sc->flags & PCIB_DISABLE_MSI)
+ if (sc->flags & PCIB_DISABLE_MSIX)
return (ENXIO);
bus = device_get_parent(pcib);
return (PCIB_ALLOC_MSIX(device_get_parent(bus), dev, irq));
@@ -1421,3 +1998,83 @@
bus = device_get_parent(pcib);
return (PCIB_POWER_FOR_SLEEP(bus, dev, pstate));
}
+
+static uint16_t
+pcib_ari_get_rid(device_t pcib, device_t dev)
+{
+ struct pcib_softc *sc;
+ uint8_t bus, slot, func;
+
+ sc = device_get_softc(pcib);
+
+ if (sc->flags & PCIB_ENABLE_ARI) {
+ bus = pci_get_bus(dev);
+ func = pci_get_function(dev);
+
+ return (PCI_ARI_RID(bus, func));
+ } else {
+ bus = pci_get_bus(dev);
+ slot = pci_get_slot(dev);
+ func = pci_get_function(dev);
+
+ return (PCI_RID(bus, slot, func));
+ }
+}
+
+/*
+ * Check that the downstream port (pcib) and the endpoint device (dev) both
+ * support ARI. If so, enable it and return 0, otherwise return an error.
+ */
+static int
+pcib_try_enable_ari(device_t pcib, device_t dev)
+{
+ struct pcib_softc *sc;
+ int error;
+ uint32_t cap2;
+ int ari_cap_off;
+ uint32_t ari_ver;
+ uint32_t pcie_pos;
+
+ sc = device_get_softc(pcib);
+
+ /*
+ * ARI is controlled in a register in the PCIe capability structure.
+ * If the downstream port does not have the PCIe capability structure
+ * then it does not support ARI.
+ */
+ error = pci_find_cap(pcib, PCIY_EXPRESS, &pcie_pos);
+ if (error != 0)
+ return (ENODEV);
+
+ /* Check that the PCIe port advertises ARI support. */
+ cap2 = pci_read_config(pcib, pcie_pos + PCIER_DEVICE_CAP2, 4);
+ if (!(cap2 & PCIEM_CAP2_ARI))
+ return (ENODEV);
+
+ /*
+ * Check that the endpoint device advertises ARI support via the ARI
+ * extended capability structure.
+ */
+ error = pci_find_extcap(dev, PCIZ_ARI, &ari_cap_off);
+ if (error != 0)
+ return (ENODEV);
+
+ /*
+ * Finally, check that the endpoint device supports the same version
+ * of ARI that we do.
+ */
+ ari_ver = pci_read_config(dev, ari_cap_off, 4);
+ if (PCI_EXTCAP_VER(ari_ver) != PCIB_SUPPORTED_ARI_VER) {
+ if (bootverbose)
+ device_printf(pcib,
+ "Unsupported version of ARI (%d) detected\n",
+ PCI_EXTCAP_VER(ari_ver));
+
+ return (ENXIO);
+ }
+
+ pcib_enable_ari(sc, pcie_pos);
+
+ return (0);
+}
+
Modified: trunk/sys/dev/pci/pci_private.h
===================================================================
--- trunk/sys/dev/pci/pci_private.h 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/pci_private.h 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 1997, Stefan Esser <se at freebsd.org>
* Copyright (c) 2000, Michael Smith <msmith at freebsd.org>
@@ -25,7 +26,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/pci/pci_private.h 330938 2018-03-14 19:04:40Z jhb $
*
*/
@@ -40,6 +41,9 @@
struct pci_softc {
bus_dma_tag_t sc_dma_tag;
+#ifdef PCI_RES_BUS
+ struct resource *sc_bus;
+#endif
};
extern int pci_do_power_resume;
@@ -51,7 +55,6 @@
void pci_add_resources(device_t bus, device_t dev, int force,
uint32_t prefetchmask);
int pci_attach_common(device_t dev);
-void pci_delete_child(device_t dev, device_t child);
void pci_driver_added(device_t dev, driver_t *driver);
int pci_print_child(device_t dev, device_t child);
void pci_probe_nomatch(device_t dev, device_t child);
@@ -79,18 +82,31 @@
int pci_disable_busmaster_method(device_t dev, device_t child);
int pci_enable_io_method(device_t dev, device_t child, int space);
int pci_disable_io_method(device_t dev, device_t child, int space);
+int pci_find_cap_method(device_t dev, device_t child,
+ int capability, int *capreg);
int pci_find_extcap_method(device_t dev, device_t child,
int capability, int *capreg);
+int pci_find_htcap_method(device_t dev, device_t child,
+ int capability, int *capreg);
int pci_alloc_msi_method(device_t dev, device_t child, int *count);
int pci_alloc_msix_method(device_t dev, device_t child, int *count);
+void pci_enable_msi_method(device_t dev, device_t child,
+ uint64_t address, uint16_t data);
+void pci_enable_msix_method(device_t dev, device_t child,
+ u_int index, uint64_t address, uint32_t data);
+void pci_disable_msi_method(device_t dev, device_t child);
int pci_remap_msix_method(device_t dev, device_t child,
int count, const u_int *vectors);
int pci_release_msi_method(device_t dev, device_t child);
int pci_msi_count_method(device_t dev, device_t child);
int pci_msix_count_method(device_t dev, device_t child);
+int pci_msix_pba_bar_method(device_t dev, device_t child);
+int pci_msix_table_bar_method(device_t dev, device_t child);
struct resource *pci_alloc_resource(device_t dev, device_t child,
int type, int *rid, u_long start, u_long end, u_long count,
u_int flags);
+int pci_release_resource(device_t dev, device_t child, int type,
+ int rid, struct resource *r);
int pci_activate_resource(device_t dev, device_t child, int type,
int rid, struct resource *r);
int pci_deactivate_resource(device_t dev, device_t child, int type,
@@ -102,6 +118,8 @@
size_t size);
void pci_print_verbose(struct pci_devinfo *dinfo);
int pci_freecfg(struct pci_devinfo *dinfo);
+void pci_child_deleted(device_t dev, device_t child);
+void pci_child_detached(device_t dev, device_t child);
int pci_child_location_str_method(device_t cbdev, device_t child,
char *buf, size_t buflen);
int pci_child_pnpinfo_str_method(device_t cbdev, device_t child,
@@ -109,6 +127,8 @@
int pci_assign_interrupt_method(device_t dev, device_t child);
int pci_resume(device_t dev);
int pci_suspend(device_t dev);
+bus_dma_tag_t pci_get_dma_tag(device_t bus, device_t dev);
+void pci_child_added_method(device_t dev, device_t child);
/** Restore the config register state. The state must be previously
* saved with pci_cfg_save. However, the pci bus driver takes care of
Modified: trunk/sys/dev/pci/pci_subr.c
===================================================================
--- trunk/sys/dev/pci/pci_subr.c 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/pci_subr.c 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,5 +1,6 @@
+/* $MidnightBSD$ */
/*-
- * Copyright (c) 2011 Advanced Computing Technologies LLC
+ * Copyright (c) 2011 Hudson River Trading LLC
* Written by: John H. Baldwin <jhb at FreeBSD.org>
* All rights reserved.
*
@@ -26,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/pci/pci_subr.c 283927 2015-06-02 19:20:39Z jhb $");
/*
* Support APIs for Host to PCI bridge drivers and drivers that
@@ -35,6 +36,7 @@
#include <sys/param.h>
#include <sys/bus.h>
+#include <sys/malloc.h>
#include <sys/rman.h>
#include <sys/systm.h>
@@ -282,4 +284,102 @@
}
return (ERANGE);
}
+
+#ifdef PCI_RES_BUS
+struct pci_domain {
+ int pd_domain;
+ struct rman pd_bus_rman;
+ TAILQ_ENTRY(pci_domain) pd_link;
+};
+
+static TAILQ_HEAD(, pci_domain) domains = TAILQ_HEAD_INITIALIZER(domains);
+
+/*
+ * Each PCI domain maintains its own resource manager for PCI bus
+ * numbers in that domain. Domain objects are created on first use.
+ * Host to PCI bridge drivers and PCI-PCI bridge drivers should
+ * allocate their bus ranges from their domain.
+ */
+static struct pci_domain *
+pci_find_domain(int domain)
+{
+ struct pci_domain *d;
+ char buf[64];
+ int error;
+
+ TAILQ_FOREACH(d, &domains, pd_link) {
+ if (d->pd_domain == domain)
+ return (d);
+ }
+
+ snprintf(buf, sizeof(buf), "PCI domain %d bus numbers", domain);
+ d = malloc(sizeof(*d) + strlen(buf) + 1, M_DEVBUF, M_WAITOK | M_ZERO);
+ d->pd_domain = domain;
+ d->pd_bus_rman.rm_start = 0;
+ d->pd_bus_rman.rm_end = PCI_BUSMAX;
+ d->pd_bus_rman.rm_type = RMAN_ARRAY;
+ strcpy((char *)(d + 1), buf);
+ d->pd_bus_rman.rm_descr = (char *)(d + 1);
+ error = rman_init(&d->pd_bus_rman);
+ if (error == 0)
+ error = rman_manage_region(&d->pd_bus_rman, 0, PCI_BUSMAX);
+ if (error)
+ panic("Failed to initialize PCI domain %d rman", domain);
+ TAILQ_INSERT_TAIL(&domains, d, pd_link);
+ return (d);
+}
+
+struct resource *
+pci_domain_alloc_bus(int domain, device_t dev, int *rid, u_long start,
+ u_long end, u_long count, u_int flags)
+{
+ struct pci_domain *d;
+ struct resource *res;
+
+ if (domain < 0 || domain > PCI_DOMAINMAX)
+ return (NULL);
+ d = pci_find_domain(domain);
+ res = rman_reserve_resource(&d->pd_bus_rman, start, end, count, flags,
+ dev);
+ if (res == NULL)
+ return (NULL);
+
+ rman_set_rid(res, *rid);
+ return (res);
+}
+
+int
+pci_domain_adjust_bus(int domain, device_t dev, struct resource *r,
+ u_long start, u_long end)
+{
+#ifdef INVARIANTS
+ struct pci_domain *d;
+#endif
+
+ if (domain < 0 || domain > PCI_DOMAINMAX)
+ return (EINVAL);
+#ifdef INVARIANTS
+ d = pci_find_domain(domain);
+ KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource"));
+#endif
+ return (rman_adjust_resource(r, start, end));
+}
+
+int
+pci_domain_release_bus(int domain, device_t dev, int rid, struct resource *r)
+{
+#ifdef INVARIANTS
+ struct pci_domain *d;
+#endif
+
+ if (domain < 0 || domain > PCI_DOMAINMAX)
+ return (EINVAL);
+#ifdef INVARIANTS
+ d = pci_find_domain(domain);
+ KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource"));
+#endif
+ return (rman_release_resource(r));
+}
+#endif /* PCI_RES_BUS */
+
#endif /* NEW_PCIB */
Modified: trunk/sys/dev/pci/pci_user.c
===================================================================
--- trunk/sys/dev/pci/pci_user.c 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/pci_user.c 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 1997, Stefan Esser <se at freebsd.org>
* All rights reserved.
@@ -25,7 +26,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/pci/pci_user.c 306469 2016-09-30 01:13:57Z jhb $");
#include "opt_bus.h" /* XXX trim includes */
#include "opt_compat.h"
@@ -225,6 +226,51 @@
u_int32_t pi_data; /* data to write or result of read */
};
+#ifdef COMPAT_FREEBSD32
+struct pci_conf_old32 {
+ struct pcisel_old pc_sel; /* bus+slot+function */
+ uint8_t pc_hdr; /* PCI header type */
+ uint16_t pc_subvendor; /* card vendor ID */
+ uint16_t pc_subdevice; /* card device ID, assigned by
+ card vendor */
+ uint16_t pc_vendor; /* chip vendor ID */
+ uint16_t pc_device; /* chip device ID, assigned by
+ chip vendor */
+ uint8_t pc_class; /* chip PCI class */
+ uint8_t pc_subclass; /* chip PCI subclass */
+ uint8_t pc_progif; /* chip PCI programming interface */
+ uint8_t pc_revid; /* chip revision ID */
+ char pd_name[PCI_MAXNAMELEN + 1]; /* device name */
+ uint32_t pd_unit; /* device unit number (u_long) */
+};
+
+struct pci_match_conf_old32 {
+ struct pcisel_old pc_sel; /* bus+slot+function */
+ char pd_name[PCI_MAXNAMELEN + 1]; /* device name */
+ uint32_t pd_unit; /* Unit number (u_long) */
+ uint16_t pc_vendor; /* PCI Vendor ID */
+ uint16_t pc_device; /* PCI Device ID */
+ uint8_t pc_class; /* PCI class */
+ pci_getconf_flags_old flags; /* Matching expression */
+};
+
+struct pci_conf_io32 {
+ uint32_t pat_buf_len; /* pattern buffer length */
+ uint32_t num_patterns; /* number of patterns */
+ uint32_t patterns; /* pattern buffer
+ (struct pci_match_conf_old32 *) */
+ uint32_t match_buf_len; /* match buffer length */
+ uint32_t num_matches; /* number of matches returned */
+ uint32_t matches; /* match buffer
+ (struct pci_conf_old32 *) */
+ uint32_t offset; /* offset into device list */
+ uint32_t generation; /* device list generation */
+ pci_getconf_status status; /* request status */
+};
+
+#define PCIOCGETCONF_OLD32 _IOWR('p', 1, struct pci_conf_io32)
+#endif /* COMPAT_FREEBSD32 */
+
#define PCIOCGETCONF_OLD _IOWR('p', 1, struct pci_conf_io)
#define PCIOCREAD_OLD _IOWR('p', 2, struct pci_io_old)
#define PCIOCWRITE_OLD _IOWR('p', 3, struct pci_io_old)
@@ -295,9 +341,156 @@
return(1);
}
-#endif
+#ifdef COMPAT_FREEBSD32
+static int
+pci_conf_match_old32(struct pci_match_conf_old32 *matches, int num_matches,
+ struct pci_conf *match_buf)
+{
+ int i;
+ if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
+ return(1);
+
+ for (i = 0; i < num_matches; i++) {
+ if (match_buf->pc_sel.pc_domain != 0)
+ continue;
+
+ /*
+ * I'm not sure why someone would do this...but...
+ */
+ if (matches[i].flags == PCI_GETCONF_NO_MATCH_OLD)
+ continue;
+
+ /*
+ * Look at each of the match flags. If it's set, do the
+ * comparison. If the comparison fails, we don't have a
+ * match, go on to the next item if there is one.
+ */
+ if (((matches[i].flags & PCI_GETCONF_MATCH_BUS_OLD) != 0) &&
+ (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
+ continue;
+
+ if (((matches[i].flags & PCI_GETCONF_MATCH_DEV_OLD) != 0) &&
+ (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
+ continue;
+
+ if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC_OLD) != 0) &&
+ (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
+ continue;
+
+ if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR_OLD) != 0) &&
+ (match_buf->pc_vendor != matches[i].pc_vendor))
+ continue;
+
+ if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE_OLD) != 0) &&
+ (match_buf->pc_device != matches[i].pc_device))
+ continue;
+
+ if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS_OLD) != 0) &&
+ (match_buf->pc_class != matches[i].pc_class))
+ continue;
+
+ if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT_OLD) != 0) &&
+ ((u_int32_t)match_buf->pd_unit != matches[i].pd_unit))
+ continue;
+
+ if (((matches[i].flags & PCI_GETCONF_MATCH_NAME_OLD) != 0) &&
+ (strncmp(matches[i].pd_name, match_buf->pd_name,
+ sizeof(match_buf->pd_name)) != 0))
+ continue;
+
+ return (0);
+ }
+
+ return (1);
+}
+#endif /* COMPAT_FREEBSD32 */
+#endif /* PRE7_COMPAT */
+
static int
+pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio)
+{
+ struct pci_vpd_element vpd_element, *vpd_user;
+ struct pcicfg_vpd *vpd;
+ size_t len;
+ int error, i;
+
+ vpd = pci_fetch_vpd_list(dev);
+ if (vpd->vpd_reg == 0 || vpd->vpd_ident == NULL)
+ return (ENXIO);
+
+ /*
+ * Calculate the amount of space needed in the data buffer. An
+ * identifier element is always present followed by the read-only
+ * and read-write keywords.
+ */
+ len = sizeof(struct pci_vpd_element) + strlen(vpd->vpd_ident);
+ for (i = 0; i < vpd->vpd_rocnt; i++)
+ len += sizeof(struct pci_vpd_element) + vpd->vpd_ros[i].len;
+ for (i = 0; i < vpd->vpd_wcnt; i++)
+ len += sizeof(struct pci_vpd_element) + vpd->vpd_w[i].len;
+
+ if (lvio->plvi_len == 0) {
+ lvio->plvi_len = len;
+ return (0);
+ }
+ if (lvio->plvi_len < len) {
+ lvio->plvi_len = len;
+ return (ENOMEM);
+ }
+
+ /*
+ * Copyout the identifier string followed by each keyword and
+ * value.
+ */
+ vpd_user = lvio->plvi_data;
+ vpd_element.pve_keyword[0] = '\0';
+ vpd_element.pve_keyword[1] = '\0';
+ vpd_element.pve_flags = PVE_FLAG_IDENT;
+ vpd_element.pve_datalen = strlen(vpd->vpd_ident);
+ error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
+ if (error)
+ return (error);
+ error = copyout(vpd->vpd_ident, vpd_user->pve_data,
+ strlen(vpd->vpd_ident));
+ if (error)
+ return (error);
+ vpd_user = PVE_NEXT(vpd_user);
+ vpd_element.pve_flags = 0;
+ for (i = 0; i < vpd->vpd_rocnt; i++) {
+ vpd_element.pve_keyword[0] = vpd->vpd_ros[i].keyword[0];
+ vpd_element.pve_keyword[1] = vpd->vpd_ros[i].keyword[1];
+ vpd_element.pve_datalen = vpd->vpd_ros[i].len;
+ error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
+ if (error)
+ return (error);
+ error = copyout(vpd->vpd_ros[i].value, vpd_user->pve_data,
+ vpd->vpd_ros[i].len);
+ if (error)
+ return (error);
+ vpd_user = PVE_NEXT(vpd_user);
+ }
+ vpd_element.pve_flags = PVE_FLAG_RW;
+ for (i = 0; i < vpd->vpd_wcnt; i++) {
+ vpd_element.pve_keyword[0] = vpd->vpd_w[i].keyword[0];
+ vpd_element.pve_keyword[1] = vpd->vpd_w[i].keyword[1];
+ vpd_element.pve_datalen = vpd->vpd_w[i].len;
+ error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
+ if (error)
+ return (error);
+ error = copyout(vpd->vpd_w[i].value, vpd_user->pve_data,
+ vpd->vpd_w[i].len);
+ if (error)
+ return (error);
+ vpd_user = PVE_NEXT(vpd_user);
+ }
+ KASSERT((char *)vpd_user - (char *)lvio->plvi_data == len,
+ ("length mismatch"));
+ lvio->plvi_len = len;
+ return (0);
+}
+
+static int
pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
{
device_t pcidev, brdev;
@@ -304,39 +497,79 @@
void *confdata;
const char *name;
struct devlist *devlist_head;
- struct pci_conf_io *cio;
+ struct pci_conf_io *cio = NULL;
struct pci_devinfo *dinfo;
struct pci_io *io;
struct pci_bar_io *bio;
+ struct pci_list_vpd_io *lvio;
struct pci_match_conf *pattern_buf;
struct pci_map *pm;
size_t confsz, iolen, pbufsz;
int error, ionum, i, num_patterns;
#ifdef PRE7_COMPAT
+#ifdef COMPAT_FREEBSD32
+ struct pci_conf_io32 *cio32 = NULL;
+ struct pci_conf_old32 conf_old32;
+ struct pci_match_conf_old32 *pattern_buf_old32 = NULL;
+#endif
struct pci_conf_old conf_old;
struct pci_io iodata;
struct pci_io_old *io_old;
- struct pci_match_conf_old *pattern_buf_old;
+ struct pci_match_conf_old *pattern_buf_old = NULL;
io_old = NULL;
- pattern_buf_old = NULL;
+#endif
- if (!(flag & FWRITE) && cmd != PCIOCGETBAR &&
- cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD)
- return EPERM;
-#else
- if (!(flag & FWRITE) && cmd != PCIOCGETBAR && cmd != PCIOCGETCONF)
- return EPERM;
+ if (!(flag & FWRITE)) {
+ switch (cmd) {
+#ifdef PRE7_COMPAT
+#ifdef COMPAT_FREEBSD32
+ case PCIOCGETCONF_OLD32:
#endif
+ case PCIOCGETCONF_OLD:
+#endif
+ case PCIOCGETCONF:
+ case PCIOCGETBAR:
+ case PCIOCLISTVPD:
+ break;
+ default:
+ return (EPERM);
+ }
+ }
- switch(cmd) {
+ switch (cmd) {
#ifdef PRE7_COMPAT
+#ifdef COMPAT_FREEBSD32
+ case PCIOCGETCONF_OLD32:
+ cio32 = (struct pci_conf_io32 *)data;
+ cio = malloc(sizeof(struct pci_conf_io), M_TEMP, M_WAITOK);
+ cio->pat_buf_len = cio32->pat_buf_len;
+ cio->num_patterns = cio32->num_patterns;
+ cio->patterns = (void *)(uintptr_t)cio32->patterns;
+ cio->match_buf_len = cio32->match_buf_len;
+ cio->num_matches = cio32->num_matches;
+ cio->matches = (void *)(uintptr_t)cio32->matches;
+ cio->offset = cio32->offset;
+ cio->generation = cio32->generation;
+ cio->status = cio32->status;
+ cio32->num_matches = 0;
+ break;
+#endif
case PCIOCGETCONF_OLD:
- /* FALLTHROUGH */
#endif
case PCIOCGETCONF:
cio = (struct pci_conf_io *)data;
+ }
+ switch (cmd) {
+#ifdef PRE7_COMPAT
+#ifdef COMPAT_FREEBSD32
+ case PCIOCGETCONF_OLD32:
+#endif
+ case PCIOCGETCONF_OLD:
+#endif
+ case PCIOCGETCONF:
+
pattern_buf = NULL;
num_patterns = 0;
dinfo = NULL;
@@ -353,7 +586,7 @@
&& (cio->generation != pci_generation)){
cio->status = PCI_GETCONF_LIST_CHANGED;
error = 0;
- break;
+ goto getconfexit;
}
/*
@@ -363,7 +596,7 @@
if (cio->offset >= pci_numdevs) {
cio->status = PCI_GETCONF_LAST_DEVICE;
error = 0;
- break;
+ goto getconfexit;
}
/* get the head of the device queue */
@@ -376,6 +609,11 @@
* didn't specify a multiple of that size.
*/
#ifdef PRE7_COMPAT
+#ifdef COMPAT_FREEBSD32
+ if (cmd == PCIOCGETCONF_OLD32)
+ confsz = sizeof(struct pci_conf_old32);
+ else
+#endif
if (cmd == PCIOCGETCONF_OLD)
confsz = sizeof(struct pci_conf_old);
else
@@ -410,6 +648,11 @@
* updated their kernel but not their userland.
*/
#ifdef PRE7_COMPAT
+#ifdef COMPAT_FREEBSD32
+ if (cmd == PCIOCGETCONF_OLD32)
+ pbufsz = sizeof(struct pci_match_conf_old32);
+ else
+#endif
if (cmd == PCIOCGETCONF_OLD)
pbufsz = sizeof(struct pci_match_conf_old);
else
@@ -419,7 +662,7 @@
/* The user made a mistake, return an error. */
cio->status = PCI_GETCONF_ERROR;
error = EINVAL;
- break;
+ goto getconfexit;
}
/*
@@ -426,6 +669,14 @@
* Allocate a buffer to hold the patterns.
*/
#ifdef PRE7_COMPAT
+#ifdef COMPAT_FREEBSD32
+ if (cmd == PCIOCGETCONF_OLD32) {
+ pattern_buf_old32 = malloc(cio->pat_buf_len,
+ M_TEMP, M_WAITOK);
+ error = copyin(cio->patterns,
+ pattern_buf_old32, cio->pat_buf_len);
+ } else
+#endif /* COMPAT_FREEBSD32 */
if (cmd == PCIOCGETCONF_OLD) {
pattern_buf_old = malloc(cio->pat_buf_len,
M_TEMP, M_WAITOK);
@@ -432,7 +683,7 @@
error = copyin(cio->patterns,
pattern_buf_old, cio->pat_buf_len);
} else
-#endif
+#endif /* PRE7_COMPAT */
{
pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
M_WAITOK);
@@ -451,7 +702,7 @@
*/
cio->status = PCI_GETCONF_ERROR;
error = EINVAL;
- break;
+ goto getconfexit;
}
/*
@@ -458,10 +709,9 @@
* Go through the list of devices and copy out the devices
* that match the user's criteria.
*/
- for (cio->num_matches = 0, error = 0, i = 0,
- dinfo = STAILQ_FIRST(devlist_head);
- (dinfo != NULL) && (cio->num_matches < ionum)
- && (error == 0) && (i < pci_numdevs) && (dinfo != NULL);
+ for (cio->num_matches = 0, i = 0,
+ dinfo = STAILQ_FIRST(devlist_head);
+ dinfo != NULL;
dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
if (i < cio->offset)
@@ -483,7 +733,14 @@
}
#ifdef PRE7_COMPAT
- if ((cmd == PCIOCGETCONF_OLD &&
+ if (
+#ifdef COMPAT_FREEBSD32
+ (cmd == PCIOCGETCONF_OLD32 &&
+ (pattern_buf_old32 == NULL ||
+ pci_conf_match_old32(pattern_buf_old32,
+ num_patterns, &dinfo->conf) == 0)) ||
+#endif
+ (cmd == PCIOCGETCONF_OLD &&
(pattern_buf_old == NULL ||
pci_conf_match_old(pattern_buf_old, num_patterns,
&dinfo->conf) == 0)) ||
@@ -508,6 +765,40 @@
break;
#ifdef PRE7_COMPAT
+#ifdef COMPAT_FREEBSD32
+ if (cmd == PCIOCGETCONF_OLD32) {
+ conf_old32.pc_sel.pc_bus =
+ dinfo->conf.pc_sel.pc_bus;
+ conf_old32.pc_sel.pc_dev =
+ dinfo->conf.pc_sel.pc_dev;
+ conf_old32.pc_sel.pc_func =
+ dinfo->conf.pc_sel.pc_func;
+ conf_old32.pc_hdr = dinfo->conf.pc_hdr;
+ conf_old32.pc_subvendor =
+ dinfo->conf.pc_subvendor;
+ conf_old32.pc_subdevice =
+ dinfo->conf.pc_subdevice;
+ conf_old32.pc_vendor =
+ dinfo->conf.pc_vendor;
+ conf_old32.pc_device =
+ dinfo->conf.pc_device;
+ conf_old32.pc_class =
+ dinfo->conf.pc_class;
+ conf_old32.pc_subclass =
+ dinfo->conf.pc_subclass;
+ conf_old32.pc_progif =
+ dinfo->conf.pc_progif;
+ conf_old32.pc_revid =
+ dinfo->conf.pc_revid;
+ strncpy(conf_old32.pd_name,
+ dinfo->conf.pd_name,
+ sizeof(conf_old32.pd_name));
+ conf_old32.pd_name[PCI_MAXNAMELEN] = 0;
+ conf_old32.pd_unit =
+ (uint32_t)dinfo->conf.pd_unit;
+ confdata = &conf_old32;
+ } else
+#endif /* COMPAT_FREEBSD32 */
if (cmd == PCIOCGETCONF_OLD) {
conf_old.pc_sel.pc_bus =
dinfo->conf.pc_sel.pc_bus;
@@ -540,13 +831,14 @@
dinfo->conf.pd_unit;
confdata = &conf_old;
} else
-#endif
+#endif /* PRE7_COMPAT */
confdata = &dinfo->conf;
- /* Only if we can copy it out do we count it. */
- if (!(error = copyout(confdata,
+ error = copyout(confdata,
(caddr_t)cio->matches +
- confsz * cio->num_matches, confsz)))
- cio->num_matches++;
+ confsz * cio->num_matches, confsz);
+ if (error)
+ break;
+ cio->num_matches++;
}
}
@@ -574,12 +866,23 @@
cio->status = PCI_GETCONF_MORE_DEVS;
getconfexit:
- if (pattern_buf != NULL)
- free(pattern_buf, M_TEMP);
#ifdef PRE7_COMPAT
+#ifdef COMPAT_FREEBSD32
+ if (cmd == PCIOCGETCONF_OLD32) {
+ cio32->status = cio->status;
+ cio32->generation = cio->generation;
+ cio32->offset = cio->offset;
+ cio32->num_matches = cio->num_matches;
+ free(cio, M_TEMP);
+ }
+ if (pattern_buf_old32 != NULL)
+ free(pattern_buf_old32, M_TEMP);
+#endif
if (pattern_buf_old != NULL)
free(pattern_buf_old, M_TEMP);
#endif
+ if (pattern_buf != NULL)
+ free(pattern_buf, M_TEMP);
break;
@@ -704,6 +1007,22 @@
else
error = ENODEV;
break;
+ case PCIOCLISTVPD:
+ lvio = (struct pci_list_vpd_io *)data;
+
+ /*
+ * Assume that the user-level bus number is
+ * in fact the physical PCI bus number.
+ */
+ pcidev = pci_find_dbsf(lvio->plvi_sel.pc_domain,
+ lvio->plvi_sel.pc_bus, lvio->plvi_sel.pc_dev,
+ lvio->plvi_sel.pc_func);
+ if (pcidev == NULL) {
+ error = ENODEV;
+ break;
+ }
+ error = pci_list_vpd(pcidev, lvio);
+ break;
default:
error = ENOTTY;
break;
Modified: trunk/sys/dev/pci/pcib_if.m
===================================================================
--- trunk/sys/dev/pci/pcib_if.m 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/pcib_if.m 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
#-
# Copyright (c) 2000 Doug Rabson
# All rights reserved.
@@ -23,11 +24,13 @@
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
-# $MidnightBSD$
+# $FreeBSD: stable/10/sys/dev/pci/pcib_if.m 279472 2015-03-01 04:28:30Z rstone $
#
#include <sys/bus.h>
+#include <sys/rman.h>
#include <dev/pci/pcivar.h>
+#include <dev/pci/pcib_private.h>
INTERFACE pcib;
@@ -47,6 +50,14 @@
};
#
+#
+# Return the number of functions on the attached PCI bus.
+#
+METHOD int maxfuncs {
+ device_t dev;
+} DEFAULT pcib_maxfuncs;
+
+#
# Read configuration space on the PCI bus. The bus, slot and func
# arguments determine the device which is being read and the reg
# argument is a byte offset into configuration space for that
@@ -154,3 +165,21 @@
device_t dev;
int *pstate;
};
+
+#
+# Return the PCI Routing Identifier (RID) for the device.
+#
+METHOD uint16_t get_rid {
+ device_t pcib;
+ device_t dev;
+} DEFAULT pcib_get_rid;
+
+#
+# Enable Alternative RID Interpretation if both the downstream port (pcib)
+# and the endpoint device (dev) both support it.
+#
+METHOD int try_enable_ari {
+ device_t pcib;
+ device_t dev;
+};
+
Property changes on: trunk/sys/dev/pci/pcib_if.m
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/pci/pcib_private.h
===================================================================
--- trunk/sys/dev/pci/pcib_private.h 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/pcib_private.h 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 1994,1995 Stefan Esser, Wolfgang StanglMeier
* Copyright (c) 2000 Michael Smith <msmith at freebsd.org>
@@ -27,7 +28,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/pci/pcib_private.h 285863 2015-07-25 00:14:02Z jhb $
*/
#ifndef __PCIB_PRIVATE_H__
@@ -73,7 +74,8 @@
pci_addr_t base; /* base address */
pci_addr_t limit; /* topmost address */
struct rman rman;
- struct resource *res;
+ struct resource **res;
+ int count; /* size of 'res' array */
int reg; /* resource id from parent */
int valid;
int mask; /* WIN_* bitmask of this window */
@@ -82,6 +84,18 @@
};
#endif
+struct pcib_secbus {
+ u_int sec;
+ u_int sub;
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+ device_t dev;
+ struct rman rman;
+ struct resource *res;
+ const char *name;
+ int sub_reg;
+#endif
+};
+
/*
* Bridge-specific data.
*/
@@ -91,11 +105,12 @@
uint32_t flags; /* flags */
#define PCIB_SUBTRACTIVE 0x1
#define PCIB_DISABLE_MSI 0x2
+#define PCIB_DISABLE_MSIX 0x4
+#define PCIB_ENABLE_ARI 0x8
uint16_t command; /* command register */
u_int domain; /* domain number */
u_int pribus; /* primary bus number */
- u_int secbus; /* secondary bus number */
- u_int subbus; /* subordinate bus number */
+ struct pcib_secbus bus; /* secondary bus numbers */
#ifdef NEW_PCIB
struct pcib_window io; /* I/O port window */
struct pcib_window mem; /* memory window */
@@ -113,15 +128,30 @@
uint8_t seclat; /* secondary bus latency timer */
};
+#define PCIB_SUPPORTED_ARI_VER 1
+
typedef uint32_t pci_read_config_fn(int b, int s, int f, int reg, int width);
-#ifdef NEW_PCIB
-const char *pcib_child_name(device_t child);
-#endif
int host_pcib_get_busno(pci_read_config_fn read_config, int bus,
int slot, int func, uint8_t *busnum);
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+struct resource *pci_domain_alloc_bus(int domain, device_t dev, int *rid,
+ u_long start, u_long end, u_long count, u_int flags);
+int pci_domain_adjust_bus(int domain, device_t dev,
+ struct resource *r, u_long start, u_long end);
+int pci_domain_release_bus(int domain, device_t dev, int rid,
+ struct resource *r);
+struct resource *pcib_alloc_subbus(struct pcib_secbus *bus, device_t child,
+ int *rid, u_long start, u_long end, u_long count,
+ u_int flags);
+void pcib_setup_secbus(device_t dev, struct pcib_secbus *bus,
+ int min_count);
+#endif
int pcib_attach(device_t dev);
void pcib_attach_common(device_t dev);
+#ifdef NEW_PCIB
+const char *pcib_child_name(device_t child);
+#endif
int pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result);
int pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value);
struct resource *pcib_alloc_resource(device_t dev, device_t child, int type, int *rid,
@@ -133,8 +163,7 @@
struct resource *r);
#endif
int pcib_maxslots(device_t dev);
-uint32_t pcib_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width);
-void pcib_write_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, uint32_t val, int width);
+int pcib_maxfuncs(device_t dev);
int pcib_route_interrupt(device_t pcib, device_t dev, int pin);
int pcib_alloc_msi(device_t pcib, device_t dev, int count, int maxcount, int *irqs);
int pcib_release_msi(device_t pcib, device_t dev, int count, int *irqs);
@@ -141,5 +170,6 @@
int pcib_alloc_msix(device_t pcib, device_t dev, int *irq);
int pcib_release_msix(device_t pcib, device_t dev, int irq);
int pcib_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr, uint32_t *data);
+uint16_t pcib_get_rid(device_t pcib, device_t dev);
#endif
Added: trunk/sys/dev/pci/pcib_support.c
===================================================================
--- trunk/sys/dev/pci/pcib_support.c (rev 0)
+++ trunk/sys/dev/pci/pcib_support.c 2018-05-27 23:28:52 UTC (rev 10087)
@@ -0,0 +1,69 @@
+/* $MidnightBSD$ */
+/*
+ * Copyright (c) 2014 Sandvine Inc. All rights reserved.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/sys/dev/pci/pcib_support.c 279470 2015-03-01 04:22:06Z rstone $");
+
+/*
+ * Support functions for the PCI:PCI bridge driver. This has to be in a
+ * separate file because kernel configurations end up referencing the functions
+ * here even when pci support is compiled out of the kernel.
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcib_private.h>
+
+#include "pcib_if.h"
+
+int
+pcib_maxfuncs(device_t dev)
+{
+ return (PCI_FUNCMAX);
+}
+
+uint16_t
+pcib_get_rid(device_t pcib, device_t dev)
+{
+ uint8_t bus, slot, func;
+
+ bus = pci_get_bus(dev);
+ slot = pci_get_slot(dev);
+ func = pci_get_function(dev);
+
+ return (PCI_RID(bus, slot, func));
+}
+
Property changes on: trunk/sys/dev/pci/pcib_support.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/sys/dev/pci/pcireg.h
===================================================================
--- trunk/sys/dev/pci/pcireg.h 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/pcireg.h 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 1997, Stefan Esser <se at freebsd.org>
* All rights reserved.
@@ -23,7 +24,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $FreeBSD: stable/9/sys/dev/pci/pcireg.h 257497 2013-11-01 07:03:44Z kib $
+ * $FreeBSD: stable/10/sys/dev/pci/pcireg.h 306520 2016-09-30 18:47:34Z jhb $
*
*/
@@ -48,6 +49,29 @@
#define PCIE_REGMAX 4095 /* highest supported config register addr. */
#define PCI_MAXHDRTYPE 2
+#define PCIE_ARI_SLOTMAX 0
+#define PCIE_ARI_FUNCMAX 255
+
+#define PCI_RID_BUS_SHIFT 8
+#define PCI_RID_SLOT_SHIFT 3
+#define PCI_RID_FUNC_SHIFT 0
+
+#define PCI_RID(bus, slot, func) \
+ ((((bus) & PCI_BUSMAX) << PCI_RID_BUS_SHIFT) | \
+ (((slot) & PCI_SLOTMAX) << PCI_RID_SLOT_SHIFT) | \
+ (((func) & PCI_FUNCMAX) << PCI_RID_FUNC_SHIFT))
+
+#define PCI_ARI_RID(bus, func) \
+ ((((bus) & PCI_BUSMAX) << PCI_RID_BUS_SHIFT) | \
+ (((func) & PCIE_ARI_FUNCMAX) << PCI_RID_FUNC_SHIFT))
+
+#define PCI_RID2BUS(rid) (((rid) >> PCI_RID_BUS_SHIFT) & PCI_BUSMAX)
+#define PCI_RID2SLOT(rid) (((rid) >> PCI_RID_SLOT_SHIFT) & PCI_SLOTMAX)
+#define PCI_RID2FUNC(rid) (((rid) >> PCI_RID_FUNC_SHIFT) & PCI_FUNCMAX)
+
+#define PCIE_ARI_SLOT(func) (((func) >> PCI_RID_SLOT_SHIFT) & PCI_SLOTMAX)
+#define PCIE_ARI_FUNC(func) (((func) >> PCI_RID_FUNC_SHIFT) & PCI_FUNCMAX)
+
/* PCI config header registers for all devices */
#define PCIR_DEVVENDOR 0x00
@@ -132,7 +156,7 @@
/* Extended Capability Identification Numbers */
#define PCIZ_AER 0x0001 /* Advanced Error Reporting */
-#define PCIZ_VC 0x0002 /* Virtual Channel */
+#define PCIZ_VC 0x0002 /* Virtual Channel if MFVC Ext Cap not set */
#define PCIZ_SERNUM 0x0003 /* Device Serial Number */
#define PCIZ_PWRBDGT 0x0004 /* Power Budgeting */
#define PCIZ_RCLINK_DCL 0x0005 /* Root Complex Link Declaration */
@@ -139,18 +163,28 @@
#define PCIZ_RCLINK_CTL 0x0006 /* Root Complex Internal Link Control */
#define PCIZ_RCEC_ASSOC 0x0007 /* Root Complex Event Collector Association */
#define PCIZ_MFVC 0x0008 /* Multi-Function Virtual Channel */
+#define PCIZ_VC2 0x0009 /* Virtual Channel if MFVC Ext Cap set */
#define PCIZ_RCRB 0x000a /* RCRB Header */
#define PCIZ_VENDOR 0x000b /* Vendor Unique */
+#define PCIZ_CAC 0x000c /* Configuration Access Correction -- obsolete */
#define PCIZ_ACS 0x000d /* Access Control Services */
#define PCIZ_ARI 0x000e /* Alternative Routing-ID Interpretation */
#define PCIZ_ATS 0x000f /* Address Translation Services */
#define PCIZ_SRIOV 0x0010 /* Single Root IO Virtualization */
+#define PCIZ_MRIOV 0x0011 /* Multiple Root IO Virtualization */
#define PCIZ_MULTICAST 0x0012 /* Multicast */
+#define PCIZ_PAGE_REQ 0x0013 /* Page Request */
+#define PCIZ_AMD 0x0014 /* Reserved for AMD */
#define PCIZ_RESIZE_BAR 0x0015 /* Resizable BAR */
#define PCIZ_DPA 0x0016 /* Dynamic Power Allocation */
#define PCIZ_TPH_REQ 0x0017 /* TPH Requester */
#define PCIZ_LTR 0x0018 /* Latency Tolerance Reporting */
#define PCIZ_SEC_PCIE 0x0019 /* Secondary PCI Express */
+#define PCIZ_PMUX 0x001a /* Protocol Multiplexing */
+#define PCIZ_PASID 0x001b /* Process Address Space ID */
+#define PCIZ_LN_REQ 0x001c /* LN Requester */
+#define PCIZ_DPC 0x001d /* Downstream Porto Containment */
+#define PCIZ_L1PM 0x001e /* L1 PM Substates */
/* config registers for header type 0 devices */
@@ -183,7 +217,7 @@
#define PCIM_CIS_ASI_ROM 7
#define PCIM_CIS_ADDR_MASK 0x0ffffff8
#define PCIM_CIS_ROM_MASK 0xf0000000
-#define PCIM_CIS_CONFIG_MASK 0xff
+#define PCIM_CIS_CONFIG_MASK 0xff
#define PCIR_SUBVEND_0 0x2c
#define PCIR_SUBDEV_0 0x2e
#define PCIR_BIOS 0x30
@@ -227,6 +261,11 @@
#define PCIR_BIOS_1 0x38
#define PCIR_BRIDGECTL_1 0x3e
+#define PCI_PPBMEMBASE(h,l) ((((uint64_t)(h) << 32) + ((l)<<16)) & ~0xfffff)
+#define PCI_PPBMEMLIMIT(h,l) ((((uint64_t)(h) << 32) + ((l)<<16)) | 0xfffff)
+#define PCI_PPBIOBASE(h,l) ((((h)<<16) + ((l)<<8)) & ~0xfff)
+#define PCI_PPBIOLIMIT(h,l) ((((h)<<16) + ((l)<<8)) | 0xfff)
+
/* config registers for header type 2 (CardBus) devices */
#define PCIR_MAX_BAR_2 0
@@ -246,6 +285,9 @@
#define PCIR_IOLIMIT0_2 0x30
#define PCIR_IOBASE1_2 0x34
#define PCIR_IOLIMIT1_2 0x38
+#define PCIM_CBBIO_16 0x0
+#define PCIM_CBBIO_32 0x1
+#define PCIM_CBBIO_MASK 0x3
#define PCIR_BRIDGECTL_2 0x3e
@@ -254,6 +296,11 @@
#define PCIR_PCCARDIF_2 0x44
+#define PCI_CBBMEMBASE(l) ((l) & ~0xfffff)
+#define PCI_CBBMEMLIMIT(l) ((l) | 0xfffff)
+#define PCI_CBBIOBASE(l) ((l) & ~0x3)
+#define PCI_CBBIOLIMIT(l) ((l) | 0x3)
+
/* PCI device class, subclass and programming interface definitions */
#define PCIC_OLD 0x00
@@ -351,6 +398,7 @@
#define PCIS_BASEPERIPH_RTC 0x03
#define PCIS_BASEPERIPH_PCIHOT 0x04
#define PCIS_BASEPERIPH_SDHC 0x05
+#define PCIS_BASEPERIPH_IOMMU 0x06
#define PCIS_BASEPERIPH_OTHER 0x80
#define PCIC_INPUTDEV 0x09
@@ -440,6 +488,17 @@
#define PCIB_BCR_DISCARD_TIMER_STATUS 0x0400
#define PCIB_BCR_DISCARD_TIMER_SERREN 0x0800
+#define CBB_BCR_PERR_ENABLE 0x0001
+#define CBB_BCR_SERR_ENABLE 0x0002
+#define CBB_BCR_ISA_ENABLE 0x0004
+#define CBB_BCR_VGA_ENABLE 0x0008
+#define CBB_BCR_MASTER_ABORT_MODE 0x0020
+#define CBB_BCR_CARDBUS_RESET 0x0040
+#define CBB_BCR_IREQ_INT_ENABLE 0x0080
+#define CBB_BCR_PREFETCH_0_ENABLE 0x0100
+#define CBB_BCR_PREFETCH_1_ENABLE 0x0200
+#define CBB_BCR_WRITE_POSTING_ENABLE 0x0400
+
/* PCI power manangement */
#define PCIR_POWER_CAP 0x2
#define PCIM_PCAP_SPEC 0x0007
@@ -764,9 +823,25 @@
#define PCIEM_ROOT_STA_PME_STATUS 0x00010000
#define PCIEM_ROOT_STA_PME_PEND 0x00020000
#define PCIER_DEVICE_CAP2 0x24
+#define PCIEM_CAP2_COMP_TIMO_RANGES 0x0000000f
+#define PCIEM_CAP2_COMP_TIMO_RANGE_A 0x00000001
+#define PCIEM_CAP2_COMP_TIMO_RANGE_B 0x00000002
+#define PCIEM_CAP2_COMP_TIMO_RANGE_C 0x00000004
+#define PCIEM_CAP2_COMP_TIMO_RANGE_D 0x00000008
+#define PCIEM_CAP2_COMP_TIMO_DISABLE 0x00000010
+#define PCIEM_CAP2_ARI 0x00000020
#define PCIER_DEVICE_CTL2 0x28
-#define PCIEM_CTL2_COMP_TIMEOUT_VAL 0x000f
-#define PCIEM_CTL2_COMP_TIMEOUT_DIS 0x0010
+#define PCIEM_CTL2_COMP_TIMO_VAL 0x000f
+#define PCIEM_CTL2_COMP_TIMO_50MS 0x0000
+#define PCIEM_CTL2_COMP_TIMO_100US 0x0001
+#define PCIEM_CTL2_COMP_TIMO_10MS 0x0002
+#define PCIEM_CTL2_COMP_TIMO_55MS 0x0005
+#define PCIEM_CTL2_COMP_TIMO_210MS 0x0006
+#define PCIEM_CTL2_COMP_TIMO_900MS 0x0009
+#define PCIEM_CTL2_COMP_TIMO_3500MS 0x000a
+#define PCIEM_CTL2_COMP_TIMO_13S 0x000d
+#define PCIEM_CTL2_COMP_TIMO_64S 0x000e
+#define PCIEM_CTL2_COMP_TIMO_DISABLE 0x0010
#define PCIEM_CTL2_ARI 0x0020
#define PCIEM_CTL2_ATOMIC_REQ_ENABLE 0x0040
#define PCIEM_CTL2_ATOMIC_EGR_BLOCK 0x0080
@@ -787,117 +862,6 @@
#define PCIER_SLOT_CTL2 0x38
#define PCIER_SLOT_STA2 0x3a
-/* Old compatibility definitions for PCI Express registers */
-#define PCIR_EXPRESS_FLAGS PCIER_FLAGS
-#define PCIM_EXP_FLAGS_VERSION PCIEM_FLAGS_VERSION
-#define PCIM_EXP_FLAGS_TYPE PCIEM_FLAGS_TYPE
-#define PCIM_EXP_TYPE_ENDPOINT PCIEM_TYPE_ENDPOINT
-#define PCIM_EXP_TYPE_LEGACY_ENDPOINT PCIEM_TYPE_LEGACY_ENDPOINT
-#define PCIM_EXP_TYPE_ROOT_PORT PCIEM_TYPE_ROOT_PORT
-#define PCIM_EXP_TYPE_UPSTREAM_PORT PCIEM_TYPE_UPSTREAM_PORT
-#define PCIM_EXP_TYPE_DOWNSTREAM_PORT PCIEM_TYPE_DOWNSTREAM_PORT
-#define PCIM_EXP_TYPE_PCI_BRIDGE PCIEM_TYPE_PCI_BRIDGE
-#define PCIM_EXP_TYPE_PCIE_BRIDGE PCIEM_TYPE_PCIE_BRIDGE
-#define PCIM_EXP_TYPE_ROOT_INT_EP PCIEM_TYPE_ROOT_INT_EP
-#define PCIM_EXP_TYPE_ROOT_EC PCIEM_TYPE_ROOT_EC
-#define PCIM_EXP_FLAGS_SLOT PCIEM_FLAGS_SLOT
-#define PCIM_EXP_FLAGS_IRQ PCIEM_FLAGS_IRQ
-#define PCIR_EXPRESS_DEVICE_CAP PCIER_DEVICE_CAP
-#define PCIM_EXP_CAP_MAX_PAYLOAD PCIEM_CAP_MAX_PAYLOAD
-#define PCIM_EXP_CAP_PHANTHOM_FUNCS PCIEM_CAP_PHANTHOM_FUNCS
-#define PCIM_EXP_CAP_EXT_TAG_FIELD PCIEM_CAP_EXT_TAG_FIELD
-#define PCIM_EXP_CAP_L0S_LATENCY PCIEM_CAP_L0S_LATENCY
-#define PCIM_EXP_CAP_L1_LATENCY PCIEM_CAP_L1_LATENCY
-#define PCIM_EXP_CAP_ROLE_ERR_RPT PCIEM_CAP_ROLE_ERR_RPT
-#define PCIM_EXP_CAP_SLOT_PWR_LIM_VAL PCIEM_CAP_SLOT_PWR_LIM_VAL
-#define PCIM_EXP_CAP_SLOT_PWR_LIM_SCALE PCIEM_CAP_SLOT_PWR_LIM_SCALE
-#define PCIM_EXP_CAP_FLR PCIEM_CAP_FLR
-#define PCIR_EXPRESS_DEVICE_CTL PCIER_DEVICE_CTL
-#define PCIM_EXP_CTL_COR_ENABLE PCIEM_CTL_COR_ENABLE
-#define PCIM_EXP_CTL_NFER_ENABLE PCIEM_CTL_NFER_ENABLE
-#define PCIM_EXP_CTL_FER_ENABLE PCIEM_CTL_FER_ENABLE
-#define PCIM_EXP_CTL_URR_ENABLE PCIEM_CTL_URR_ENABLE
-#define PCIM_EXP_CTL_RELAXED_ORD_ENABLE PCIEM_CTL_RELAXED_ORD_ENABLE
-#define PCIM_EXP_CTL_MAX_PAYLOAD PCIEM_CTL_MAX_PAYLOAD
-#define PCIM_EXP_CTL_EXT_TAG_FIELD PCIEM_CTL_EXT_TAG_FIELD
-#define PCIM_EXP_CTL_PHANTHOM_FUNCS PCIEM_CTL_PHANTHOM_FUNCS
-#define PCIM_EXP_CTL_AUX_POWER_PM PCIEM_CTL_AUX_POWER_PM
-#define PCIM_EXP_CTL_NOSNOOP_ENABLE PCIEM_CTL_NOSNOOP_ENABLE
-#define PCIM_EXP_CTL_MAX_READ_REQUEST PCIEM_CTL_MAX_READ_REQUEST
-#define PCIM_EXP_CTL_BRDG_CFG_RETRY PCIEM_CTL_BRDG_CFG_RETRY
-#define PCIM_EXP_CTL_INITIATE_FLR PCIEM_CTL_INITIATE_FLR
-#define PCIR_EXPRESS_DEVICE_STA PCIER_DEVICE_STA
-#define PCIM_EXP_STA_CORRECTABLE_ERROR PCIEM_STA_CORRECTABLE_ERROR
-#define PCIM_EXP_STA_NON_FATAL_ERROR PCIEM_STA_NON_FATAL_ERROR
-#define PCIM_EXP_STA_FATAL_ERROR PCIEM_STA_FATAL_ERROR
-#define PCIM_EXP_STA_UNSUPPORTED_REQ PCIEM_STA_UNSUPPORTED_REQ
-#define PCIM_EXP_STA_AUX_POWER PCIEM_STA_AUX_POWER
-#define PCIM_EXP_STA_TRANSACTION_PND PCIEM_STA_TRANSACTION_PND
-#define PCIR_EXPRESS_LINK_CAP PCIER_LINK_CAP
-#define PCIM_LINK_CAP_MAX_SPEED PCIEM_LINK_CAP_MAX_SPEED
-#define PCIM_LINK_CAP_MAX_WIDTH PCIEM_LINK_CAP_MAX_WIDTH
-#define PCIM_LINK_CAP_ASPM PCIEM_LINK_CAP_ASPM
-#define PCIM_LINK_CAP_L0S_EXIT PCIEM_LINK_CAP_L0S_EXIT
-#define PCIM_LINK_CAP_L1_EXIT PCIEM_LINK_CAP_L1_EXIT
-#define PCIM_LINK_CAP_CLOCK_PM PCIEM_LINK_CAP_CLOCK_PM
-#define PCIM_LINK_CAP_SURPRISE_DOWN PCIEM_LINK_CAP_SURPRISE_DOWN
-#define PCIM_LINK_CAP_DL_ACTIVE PCIEM_LINK_CAP_DL_ACTIVE
-#define PCIM_LINK_CAP_LINK_BW_NOTIFY PCIEM_LINK_CAP_LINK_BW_NOTIFY
-#define PCIM_LINK_CAP_ASPM_COMPLIANCE PCIEM_LINK_CAP_ASPM_COMPLIANCE
-#define PCIM_LINK_CAP_PORT PCIEM_LINK_CAP_PORT
-#define PCIR_EXPRESS_LINK_CTL PCIER_LINK_CTL
-#define PCIM_EXP_LINK_CTL_ASPMC_DIS PCIEM_LINK_CTL_ASPMC_DIS
-#define PCIM_EXP_LINK_CTL_ASPMC_L0S PCIEM_LINK_CTL_ASPMC_L0S
-#define PCIM_EXP_LINK_CTL_ASPMC_L1 PCIEM_LINK_CTL_ASPMC_L1
-#define PCIM_EXP_LINK_CTL_ASPMC PCIEM_LINK_CTL_ASPMC
-#define PCIM_EXP_LINK_CTL_RCB PCIEM_LINK_CTL_RCB
-#define PCIM_EXP_LINK_CTL_LINK_DIS PCIEM_LINK_CTL_LINK_DIS
-#define PCIM_EXP_LINK_CTL_RETRAIN_LINK PCIEM_LINK_CTL_RETRAIN_LINK
-#define PCIM_EXP_LINK_CTL_COMMON_CLOCK PCIEM_LINK_CTL_COMMON_CLOCK
-#define PCIM_EXP_LINK_CTL_EXTENDED_SYNC PCIEM_LINK_CTL_EXTENDED_SYNC
-#define PCIM_EXP_LINK_CTL_ECPM PCIEM_LINK_CTL_ECPM
-#define PCIM_EXP_LINK_CTL_HAWD PCIEM_LINK_CTL_HAWD
-#define PCIM_EXP_LINK_CTL_LBMIE PCIEM_LINK_CTL_LBMIE
-#define PCIM_EXP_LINK_CTL_LABIE PCIEM_LINK_CTL_LABIE
-#define PCIR_EXPRESS_LINK_STA PCIER_LINK_STA
-#define PCIM_LINK_STA_SPEED PCIEM_LINK_STA_SPEED
-#define PCIM_LINK_STA_WIDTH PCIEM_LINK_STA_WIDTH
-#define PCIM_LINK_STA_TRAINING_ERROR PCIEM_LINK_STA_TRAINING_ERROR
-#define PCIM_LINK_STA_TRAINING PCIEM_LINK_STA_TRAINING
-#define PCIM_LINK_STA_SLOT_CLOCK PCIEM_LINK_STA_SLOT_CLOCK
-#define PCIM_LINK_STA_DL_ACTIVE PCIEM_LINK_STA_DL_ACTIVE
-#define PCIM_LINK_STA_LINK_BW_MGMT PCIEM_LINK_STA_LINK_BW_MGMT
-#define PCIM_LINK_STA_LINK_AUTO_BW PCIEM_LINK_STA_LINK_AUTO_BW
-#define PCIR_EXPRESS_SLOT_CAP PCIER_SLOT_CAP
-#define PCIR_EXPRESS_SLOT_CTL PCIER_SLOT_CTL
-#define PCIR_EXPRESS_SLOT_STA PCIER_SLOT_STA
-#define PCIR_EXPRESS_ROOT_CTL PCIER_ROOT_CTL
-#define PCIR_EXPRESS_ROOT_CAP PCIER_ROOT_CAP
-#define PCIR_EXPRESS_ROOT_STA PCIER_ROOT_STA
-#define PCIR_EXPRESS_DEVICE_CAP2 PCIER_DEVICE_CAP2
-#define PCIR_EXPRESS_DEVICE_CTL2 PCIER_DEVICE_CTL2
-#define PCIM_EXP_CTL2_COMP_TIMEOUT_VAL PCIEM_CTL2_COMP_TIMEOUT_VAL
-#define PCIM_EXP_CTL2_COMP_TIMEOUT_DIS PCIEM_CTL2_COMP_TIMEOUT_DIS
-#define PCIM_EXP_CTL2_ARI PCIEM_CTL2_ARI
-#define PCIM_EXP_CTL2_ATOMIC_REQ_ENABLE PCIEM_CTL2_ATOMIC_REQ_ENABLE
-#define PCIM_EXP_CTL2_ATOMIC_EGR_BLOCK PCIEM_CTL2_ATOMIC_EGR_BLOCK
-#define PCIM_EXP_CTL2_ID_ORDERED_REQ_EN PCIEM_CTL2_ID_ORDERED_REQ_EN
-#define PCIM_EXP_CTL2_ID_ORDERED_CMP_EN PCIEM_CTL2_ID_ORDERED_CMP_EN
-#define PCIM_EXP_CTL2_LTR_ENABLE PCIEM_CTL2_LTR_ENABLE
-#define PCIM_EXP_CTL2_OBFF PCIEM_CTL2_OBFF
-#define PCIM_EXP_OBFF_DISABLE PCIEM_OBFF_DISABLE
-#define PCIM_EXP_OBFF_MSGA_ENABLE PCIEM_OBFF_MSGA_ENABLE
-#define PCIM_EXP_OBFF_MSGB_ENABLE PCIEM_OBFF_MSGB_ENABLE
-#define PCIM_EXP_OBFF_WAKE_ENABLE PCIEM_OBFF_WAKE_ENABLE
-#define PCIM_EXP_CTL2_END2END_TLP PCIEM_CTL2_END2END_TLP
-#define PCIR_EXPRESS_DEVICE_STA2 PCIER_DEVICE_STA2
-#define PCIR_EXPRESS_LINK_CAP2 PCIER_LINK_CAP2
-#define PCIR_EXPRESS_LINK_CTL2 PCIER_LINK_CTL2
-#define PCIR_EXPRESS_LINK_STA2 PCIER_LINK_STA2
-#define PCIR_EXPRESS_SLOT_CAP2 PCIER_SLOT_CAP2
-#define PCIR_EXPRESS_SLOT_CTL2 PCIER_SLOT_CTL2
-#define PCIR_EXPRESS_SLOT_STA2 PCIER_SLOT_STA2
-
/* MSI-X definitions */
#define PCIR_MSIX_CTRL 0x2
#define PCIM_MSIXCTRL_MSIX_ENABLE 0x8000
@@ -995,3 +959,4 @@
/* Serial Number definitions */
#define PCIR_SERIAL_LOW 0x04
#define PCIR_SERIAL_HIGH 0x08
+
Modified: trunk/sys/dev/pci/pcivar.h
===================================================================
--- trunk/sys/dev/pci/pcivar.h 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/pcivar.h 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 1997, Stefan Esser <se at freebsd.org>
* All rights reserved.
@@ -23,7 +24,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/pci/pcivar.h 306520 2016-09-30 18:47:34Z jhb $
*
*/
@@ -57,6 +58,7 @@
struct vpd_readonly {
char keyword[2];
char *value;
+ int len;
};
struct vpd_write {
@@ -123,6 +125,25 @@
uint64_t ht_msiaddr; /* MSI mapping base address */
};
+/* Interesting values for PCI-express */
+struct pcicfg_pcie {
+ uint8_t pcie_location; /* Offset of PCI-e capability registers. */
+ uint8_t pcie_type; /* Device type. */
+ uint16_t pcie_flags; /* Device capabilities register. */
+ uint16_t pcie_device_ctl; /* Device control register. */
+ uint16_t pcie_link_ctl; /* Link control register. */
+ uint16_t pcie_slot_ctl; /* Slot control register. */
+ uint16_t pcie_root_ctl; /* Root control register. */
+ uint16_t pcie_device_ctl2; /* Second device control register. */
+ uint16_t pcie_link_ctl2; /* Second link control register. */
+ uint16_t pcie_slot_ctl2; /* Second slot control register. */
+};
+
+struct pcicfg_pcix {
+ uint16_t pcix_command;
+ uint8_t pcix_location; /* Offset of PCI-X capability registers. */
+};
+
/* config header information common to all header types */
typedef struct pcicfg {
struct device *dev; /* device which owns this */
@@ -164,15 +185,12 @@
struct pcicfg_msi msi; /* PCI MSI */
struct pcicfg_msix msix; /* PCI MSI-X */
struct pcicfg_ht ht; /* HyperTransport */
+ struct pcicfg_pcie pcie; /* PCI Express */
+ struct pcicfg_pcix pcix; /* PCI-X */
} pcicfgregs;
/* additional type 1 device config header information (PCI to PCI bridge) */
-#define PCI_PPBMEMBASE(h,l) ((((pci_addr_t)(h) << 32) + ((l)<<16)) & ~0xfffff)
-#define PCI_PPBMEMLIMIT(h,l) ((((pci_addr_t)(h) << 32) + ((l)<<16)) | 0xfffff)
-#define PCI_PPBIOBASE(h,l) ((((h)<<16) + ((l)<<8)) & ~0xfff)
-#define PCI_PPBIOLIMIT(h,l) ((((h)<<16) + ((l)<<8)) | 0xfff)
-
typedef struct {
pci_addr_t pmembase; /* base address of prefetchable memory */
pci_addr_t pmemlimit; /* topmost address of prefetchable memory */
@@ -350,9 +368,9 @@
}
static __inline int
-pci_get_vpd_readonly(device_t dev, const char *kw, const char **identptr)
+pci_get_vpd_readonly(device_t dev, const char *kw, const char **vptr)
{
- return(PCI_GET_VPD_READONLY(device_get_parent(dev), dev, kw, identptr));
+ return(PCI_GET_VPD_READONLY(device_get_parent(dev), dev, kw, vptr));
}
/*
@@ -409,7 +427,7 @@
static __inline int
pci_find_cap(device_t dev, int capability, int *capreg)
{
- return (PCI_FIND_EXTCAP(device_get_parent(dev), dev, capability, capreg));
+ return (PCI_FIND_CAP(device_get_parent(dev), dev, capability, capreg));
}
static __inline int
@@ -419,6 +437,12 @@
}
static __inline int
+pci_find_htcap(device_t dev, int capability, int *capreg)
+{
+ return (PCI_FIND_HTCAP(device_get_parent(dev), dev, capability, capreg));
+}
+
+static __inline int
pci_alloc_msi(device_t dev, int *count)
{
return (PCI_ALLOC_MSI(device_get_parent(dev), dev, count));
@@ -430,6 +454,24 @@
return (PCI_ALLOC_MSIX(device_get_parent(dev), dev, count));
}
+static __inline void
+pci_enable_msi(device_t dev, uint64_t address, uint16_t data)
+{
+ PCI_ENABLE_MSI(device_get_parent(dev), dev, address, data);
+}
+
+static __inline void
+pci_enable_msix(device_t dev, u_int index, uint64_t address, uint32_t data)
+{
+ PCI_ENABLE_MSIX(device_get_parent(dev), dev, index, address, data);
+}
+
+static __inline void
+pci_disable_msi(device_t dev)
+{
+ PCI_DISABLE_MSI(device_get_parent(dev), dev);
+}
+
static __inline int
pci_remap_msix(device_t dev, int count, const u_int *vectors)
{
@@ -454,6 +496,31 @@
return (PCI_MSIX_COUNT(device_get_parent(dev), dev));
}
+static __inline int
+pci_msix_pba_bar(device_t dev)
+{
+ return (PCI_MSIX_PBA_BAR(device_get_parent(dev), dev));
+}
+
+static __inline int
+pci_msix_table_bar(device_t dev)
+{
+ return (PCI_MSIX_TABLE_BAR(device_get_parent(dev), dev));
+}
+
+static __inline uint16_t
+pci_get_rid(device_t dev)
+{
+ return (PCI_GET_RID(device_get_parent(dev), dev));
+}
+
+static __inline void
+pci_child_added(device_t dev)
+{
+
+ return (PCI_CHILD_ADDED(device_get_parent(dev), dev));
+}
+
device_t pci_find_bsf(uint8_t, uint8_t, uint8_t);
device_t pci_find_dbsf(uint32_t, uint8_t, uint8_t, uint8_t);
device_t pci_find_device(uint16_t, uint16_t);
@@ -463,14 +530,32 @@
int pci_pending_msix(device_t dev, u_int index);
int pci_msi_device_blacklisted(device_t dev);
+int pci_msix_device_blacklisted(device_t dev);
void pci_ht_map_msi(device_t dev, uint64_t addr);
+device_t pci_find_pcie_root_port(device_t dev);
+int pci_get_max_payload(device_t dev);
int pci_get_max_read_req(device_t dev);
void pci_restore_state(device_t dev);
void pci_save_state(device_t dev);
int pci_set_max_read_req(device_t dev, int size);
+uint32_t pcie_read_config(device_t dev, int reg, int width);
+void pcie_write_config(device_t dev, int reg, uint32_t value, int width);
+uint32_t pcie_adjust_config(device_t dev, int reg, uint32_t mask,
+ uint32_t value, int width);
+bool pcie_flr(device_t dev, u_int max_delay, bool force);
+int pcie_get_max_completion_timeout(device_t dev);
+bool pcie_wait_for_pending_transactions(device_t dev, u_int max_delay);
+#ifdef BUS_SPACE_MAXADDR
+#if (BUS_SPACE_MAXADDR > 0xFFFFFFFF)
+#define PCI_DMA_BOUNDARY 0x100000000
+#else
+#define PCI_DMA_BOUNDARY 0
+#endif
+#endif
+
#endif /* _SYS_BUS_H_ */
/*
@@ -488,5 +573,13 @@
struct pci_map *pci_find_bar(device_t dev, int reg);
int pci_bar_enabled(device_t dev, struct pci_map *pm);
+struct pcicfg_vpd *pci_fetch_vpd_list(device_t dev);
+#define VGA_PCI_BIOS_SHADOW_ADDR 0xC0000
+#define VGA_PCI_BIOS_SHADOW_SIZE 131072
+
+int vga_pci_is_boot_display(device_t dev);
+void * vga_pci_map_bios(device_t dev, size_t *size);
+void vga_pci_unmap_bios(device_t dev, void *bios);
+
#endif /* _PCIVAR_H_ */
Modified: trunk/sys/dev/pci/vga_pci.c
===================================================================
--- trunk/sys/dev/pci/vga_pci.c 2018-05-27 23:27:48 UTC (rev 10086)
+++ trunk/sys/dev/pci/vga_pci.c 2018-05-27 23:28:52 UTC (rev 10087)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 2005 John Baldwin <jhb at FreeBSD.org>
* All rights reserved.
@@ -10,9 +11,6 @@
* 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. Neither the name of the author nor the names of any co-contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -28,7 +26,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/pci/vga_pci.c 284503 2015-06-17 07:41:53Z hselasky $");
/*
* Simple driver for PCI VGA display devices. Drivers such as agp(4) and
@@ -46,6 +44,11 @@
#include <sys/sysctl.h>
#include <sys/systm.h>
+#if defined(__amd64__) || defined(__i386__) || defined(__ia64__)
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#endif
+
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
@@ -56,22 +59,153 @@
struct vga_pci_softc {
device_t vga_msi_child; /* Child driver using MSI. */
- struct vga_resource vga_res[PCIR_MAX_BAR_0 + 1];
+ struct vga_resource vga_bars[PCIR_MAX_BAR_0 + 1];
+ struct vga_resource vga_bios;
};
SYSCTL_DECL(_hw_pci);
+static struct vga_resource *lookup_res(struct vga_pci_softc *sc, int rid);
+static struct resource *vga_pci_alloc_resource(device_t dev, device_t child,
+ int type, int *rid, u_long start, u_long end, u_long count, u_int flags);
+static int vga_pci_release_resource(device_t dev, device_t child, int type,
+ int rid, struct resource *r);
+
int vga_pci_default_unit = -1;
TUNABLE_INT("hw.pci.default_vgapci_unit", &vga_pci_default_unit);
SYSCTL_INT(_hw_pci, OID_AUTO, default_vgapci_unit, CTLFLAG_RDTUN,
&vga_pci_default_unit, -1, "Default VGA-compatible display");
+int
+vga_pci_is_boot_display(device_t dev)
+{
+ int unit;
+ device_t pcib;
+ uint16_t config;
+
+ /* Check that the given device is a video card */
+ if ((pci_get_class(dev) != PCIC_DISPLAY &&
+ (pci_get_class(dev) != PCIC_OLD ||
+ pci_get_subclass(dev) != PCIS_OLD_VGA)))
+ return (0);
+
+ unit = device_get_unit(dev);
+
+ if (vga_pci_default_unit >= 0) {
+ /*
+ * The boot display device was determined by a previous
+ * call to this function, or the user forced it using
+ * the hw.pci.default_vgapci_unit tunable.
+ */
+ return (vga_pci_default_unit == unit);
+ }
+
+ /*
+ * The primary video card used as a boot display must have the
+ * "I/O" and "Memory Address Space Decoding" bits set in its
+ * Command register.
+ *
+ * Furthermore, if the card is attached to a bridge, instead of
+ * the root PCI bus, the bridge must have the "VGA Enable" bit
+ * set in its Control register.
+ */
+
+ pcib = device_get_parent(device_get_parent(dev));
+ if (device_get_devclass(device_get_parent(pcib)) ==
+ devclass_find("pci")) {
+ /*
+ * The parent bridge is a PCI-to-PCI bridge: check the
+ * value of the "VGA Enable" bit.
+ */
+ config = pci_read_config(pcib, PCIR_BRIDGECTL_1, 2);
+ if ((config & PCIB_BCR_VGA_ENABLE) == 0)
+ return (0);
+ }
+
+ config = pci_read_config(dev, PCIR_COMMAND, 2);
+ if ((config & (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN)) == 0)
+ return (0);
+
+ /*
+ * Disable interrupts until a chipset driver is loaded for
+ * this PCI device. Else unhandled display adapter interrupts
+ * might freeze the CPU.
+ */
+ pci_write_config(dev, PCIR_COMMAND, config | PCIM_CMD_INTxDIS, 2);
+
+ /* This video card is the boot display: record its unit number. */
+ vga_pci_default_unit = unit;
+ device_set_flags(dev, 1);
+
+ return (1);
+}
+
+void *
+vga_pci_map_bios(device_t dev, size_t *size)
+{
+ int rid;
+ struct resource *res;
+
+#if defined(__amd64__) || defined(__i386__) || defined(__ia64__)
+ if (vga_pci_is_boot_display(dev)) {
+ /*
+ * On x86, the System BIOS copy the default display
+ * device's Video BIOS at a fixed location in system
+ * memory (0xC0000, 128 kBytes long) at boot time.
+ *
+ * We use this copy for the default boot device, because
+ * the original ROM may not be valid after boot.
+ */
+
+ *size = VGA_PCI_BIOS_SHADOW_SIZE;
+ return (pmap_mapbios(VGA_PCI_BIOS_SHADOW_ADDR, *size));
+ }
+#endif
+
+ rid = PCIR_BIOS;
+ res = vga_pci_alloc_resource(dev, NULL, SYS_RES_MEMORY, &rid, 0ul,
+ ~0ul, 1, RF_ACTIVE);
+ if (res == NULL) {
+ return (NULL);
+ }
+
+ *size = rman_get_size(res);
+ return (rman_get_virtual(res));
+}
+
+void
+vga_pci_unmap_bios(device_t dev, void *bios)
+{
+ struct vga_resource *vr;
+
+ if (bios == NULL) {
+ return;
+ }
+
+#if defined(__amd64__) || defined(__i386__) || defined(__ia64__)
+ if (vga_pci_is_boot_display(dev)) {
+ /* We mapped the BIOS shadow copy located at 0xC0000. */
+ pmap_unmapdev((vm_offset_t)bios, VGA_PCI_BIOS_SHADOW_SIZE);
+
+ return;
+ }
+#endif
+
+ /*
+ * Look up the PCIR_BIOS resource in our softc. It should match
+ * the address we returned previously.
+ */
+ vr = lookup_res(device_get_softc(dev), PCIR_BIOS);
+ KASSERT(vr->vr_res != NULL, ("vga_pci_unmap_bios: bios not mapped"));
+ KASSERT(rman_get_virtual(vr->vr_res) == bios,
+ ("vga_pci_unmap_bios: mismatch"));
+ vga_pci_release_resource(dev, NULL, SYS_RES_MEMORY, PCIR_BIOS,
+ vr->vr_res);
+}
+
static int
vga_pci_probe(device_t dev)
{
- device_t bdev;
- int unit;
- uint16_t bctl;
switch (pci_get_class(dev)) {
case PCIC_DISPLAY:
@@ -85,13 +219,7 @@
}
/* Probe default display. */
- unit = device_get_unit(dev);
- bdev = device_get_parent(device_get_parent(dev));
- bctl = pci_read_config(bdev, PCIR_BRIDGECTL_1, 2);
- if (vga_pci_default_unit < 0 && (bctl & PCIB_BCR_VGA_ENABLE) != 0)
- vga_pci_default_unit = unit;
- if (vga_pci_default_unit == unit)
- device_set_flags(dev, 1);
+ vga_pci_is_boot_display(dev);
device_set_desc(dev, "VGA-compatible display");
return (BUS_PROBE_GENERIC);
@@ -107,6 +235,10 @@
device_add_child(dev, "drm", -1);
device_add_child(dev, "drmn", -1);
bus_generic_attach(dev);
+
+ if (vga_pci_is_boot_display(dev))
+ device_printf(dev, "Boot video device\n");
+
return (0);
}
@@ -156,12 +288,24 @@
return (BUS_TEARDOWN_INTR(device_get_parent(dev), dev, irq, cookie));
}
+static struct vga_resource *
+lookup_res(struct vga_pci_softc *sc, int rid)
+{
+ int bar;
+
+ if (rid == PCIR_BIOS)
+ return (&sc->vga_bios);
+ bar = PCI_RID2BAR(rid);
+ if (bar >= 0 && bar <= PCIR_MAX_BAR_0)
+ return (&sc->vga_bars[bar]);
+ return (NULL);
+}
+
static struct resource *
vga_pci_alloc_resource(device_t dev, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
- struct vga_pci_softc *sc;
- int bar;
+ struct vga_resource *vr;
switch (type) {
case SYS_RES_MEMORY:
@@ -170,16 +314,15 @@
* For BARs, we cache the resource so that we only allocate it
* from the PCI bus once.
*/
- bar = PCI_RID2BAR(*rid);
- if (bar < 0 || bar > PCIR_MAX_BAR_0)
+ vr = lookup_res(device_get_softc(dev), *rid);
+ if (vr == NULL)
return (NULL);
- sc = device_get_softc(dev);
- if (sc->vga_res[bar].vr_res == NULL)
- sc->vga_res[bar].vr_res = bus_alloc_resource(dev, type,
- rid, start, end, count, flags);
- if (sc->vga_res[bar].vr_res != NULL)
- sc->vga_res[bar].vr_refs++;
- return (sc->vga_res[bar].vr_res);
+ if (vr->vr_res == NULL)
+ vr->vr_res = bus_alloc_resource(dev, type, rid, start,
+ end, count, flags);
+ if (vr->vr_res != NULL)
+ vr->vr_refs++;
+ return (vr->vr_res);
}
return (bus_alloc_resource(dev, type, rid, start, end, count, flags));
}
@@ -188,8 +331,8 @@
vga_pci_release_resource(device_t dev, device_t child, int type, int rid,
struct resource *r)
{
- struct vga_pci_softc *sc;
- int bar, error;
+ struct vga_resource *vr;
+ int error;
switch (type) {
case SYS_RES_MEMORY:
@@ -198,24 +341,22 @@
* For BARs, we release the resource from the PCI bus
* when the last child reference goes away.
*/
- bar = PCI_RID2BAR(rid);
- if (bar < 0 || bar > PCIR_MAX_BAR_0)
+ vr = lookup_res(device_get_softc(dev), rid);
+ if (vr == NULL)
return (EINVAL);
- sc = device_get_softc(dev);
- if (sc->vga_res[bar].vr_res == NULL)
+ if (vr->vr_res == NULL)
return (EINVAL);
- KASSERT(sc->vga_res[bar].vr_res == r,
- ("vga_pci resource mismatch"));
- if (sc->vga_res[bar].vr_refs > 1) {
- sc->vga_res[bar].vr_refs--;
+ KASSERT(vr->vr_res == r, ("vga_pci resource mismatch"));
+ if (vr->vr_refs > 1) {
+ vr->vr_refs--;
return (0);
}
- KASSERT(sc->vga_res[bar].vr_refs > 0,
+ KASSERT(vr->vr_refs > 0,
("vga_pci resource reference count underflow"));
error = bus_release_resource(dev, type, rid, r);
if (error == 0) {
- sc->vga_res[bar].vr_res = NULL;
- sc->vga_res[bar].vr_refs = 0;
+ vr->vr_res = NULL;
+ vr->vr_refs = 0;
}
return (error);
}
@@ -315,6 +456,14 @@
}
static int
+vga_pci_find_cap(device_t dev, device_t child, int capability,
+ int *capreg)
+{
+
+ return (pci_find_cap(dev, capability, capreg));
+}
+
+static int
vga_pci_find_extcap(device_t dev, device_t child, int capability,
int *capreg)
{
@@ -323,6 +472,14 @@
}
static int
+vga_pci_find_htcap(device_t dev, device_t child, int capability,
+ int *capreg)
+{
+
+ return (pci_find_htcap(dev, capability, capreg));
+}
+
+static int
vga_pci_alloc_msi(device_t dev, device_t child, int *count)
{
struct vga_pci_softc *sc;
@@ -431,7 +588,9 @@
DEVMETHOD(pci_get_powerstate, vga_pci_get_powerstate),
DEVMETHOD(pci_set_powerstate, vga_pci_set_powerstate),
DEVMETHOD(pci_assign_interrupt, vga_pci_assign_interrupt),
+ DEVMETHOD(pci_find_cap, vga_pci_find_cap),
DEVMETHOD(pci_find_extcap, vga_pci_find_extcap),
+ DEVMETHOD(pci_find_htcap, vga_pci_find_htcap),
DEVMETHOD(pci_alloc_msi, vga_pci_alloc_msi),
DEVMETHOD(pci_alloc_msix, vga_pci_alloc_msix),
DEVMETHOD(pci_remap_msix, vga_pci_remap_msix),
More information about the Midnightbsd-cvs
mailing list