* (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: src/sys/i386/pci/pci_cfgreg.c,v 1.124.2.2.6.1 2009/04/15 03:14:26 kensmith Exp $
+ * $FreeBSD: src/sys/i386/pci/pci_cfgreg.c,v 1.124.2.3 2009/05/04 21:04:29 jhb
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
+#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/thread2.h>
};
static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU];
-static uint32_t pciebar;
+static uint64_t pcie_base;
+static int pcie_minbus, pcie_maxbus;
+static uint32_t pcie_badslots;
static int cfgmech;
static int devmax;
#if defined(__DragonFly__)
#else
static struct mtx pcicfg_mtx;
#endif
+static int mcfg_enable = 0;
+TUNABLE_INT("hw.pci.mcfg", &mcfg_enable);
+
+static uint32_t pci_docfgregread(int bus, int slot, int func, int reg, int bytes);
static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
static int pcireg_cfgopen(void);
-static int pciereg_cfgopen(void);
-static int pciereg_cfgread(int bus, int slot, int func, int reg,
- int bytes);
-static void pciereg_cfgwrite(int bus, int slot, int func, int reg,
- int data, int bytes);
+static int pciereg_cfgread(int bus, unsigned slot, unsigned func,
+ unsigned reg, unsigned bytes);
+static void pciereg_cfgwrite(int bus, unsigned slot, unsigned func,
+ unsigned reg, int data, unsigned bytes);
+
+
/*
* Some BIOS writers seem to want to ignore the spec and put
pci_cfgregopen(void)
{
static int opened = 0;
+ uint64_t pciebar;
u_int16_t vid, did;
u_int16_t v;
if (opened)
- return(1);
+ return (1);
- if (pcireg_cfgopen() == 0)
- return(0);
+ if (cfgmech == CFGMECH_NONE && pcireg_cfgopen() == 0)
+ return (0);
v = pcibios_get_version();
if (v > 0)
if (v >= 0x0210)
pci_pir_open();
+ if (cfgmech == CFGMECH_PCIE)
+ return (1);
+
/*
* Grope around in the PCI config space to see if this is a
* chipset that is capable of doing memory-mapped config cycles.
/* Check for supported chipsets */
vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
- if (vid == 0x8086) {
- if (did == 0x3590 || did == 0x3592) {
+ switch (vid) {
+ case 0x8086:
+ switch (did) {
+ case 0x3590:
+ case 0x3592:
/* Intel 7520 or 7320 */
pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
- pciereg_cfgopen();
- } else if (did == 0x2580 || did == 0x2584) {
- /* Intel 915 or 925 */
+ pcie_cfgregopen(pciebar, 0, 255);
+ break;
+ case 0x2580:
+ case 0x2584:
+ case 0x2590:
+ /* Intel 915, 925, or 915GM */
pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
- pciereg_cfgopen();
+ pcie_cfgregopen(pciebar, 0, 255);
+ break;
}
}
return(1);
}
+static uint32_t
+pci_docfgregread(int bus, int slot, int func, int reg, int bytes)
+{
+
+ if (cfgmech == CFGMECH_PCIE &&
+ (bus >= pcie_minbus && bus <= pcie_maxbus) &&
+ (bus != 0 || !(1 << slot & pcie_badslots)))
+ return (pciereg_cfgread(bus, slot, func, reg, bytes));
+ else
+ return (pcireg_cfgread(bus, slot, func, reg, bytes));
+}
+
/*
* Read configuration space register
*/
* the code uses 255 as an invalid IRQ.
*/
if (reg == PCIR_INTLINE && bytes == 1) {
- line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
+ line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1);
return (pci_i386_map_intline(line));
}
- return (pcireg_cfgread(bus, slot, func, reg, bytes));
+ return (pci_docfgregread(bus, slot, func, reg, bytes));
}
/*
pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
{
- pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
+ if (cfgmech == CFGMECH_PCIE &&
+ (bus >= pcie_minbus && bus <= pcie_maxbus) &&
+ (bus != 0 || !(1 << slot & pcie_badslots)))
+ pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
+ else
+ pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
}
/*
&& (unsigned) bytes <= 4
&& (reg & (bytes - 1)) == 0) {
switch (cfgmech) {
+ case CFGMECH_PCIE:
case CFGMECH_1:
outl(CONF1_ADDR_PORT, (1 << 31)
| (bus << 16) | (slot << 11)
pci_cfgdisable(void)
{
switch (cfgmech) {
+ case CFGMECH_PCIE:
case CFGMECH_1:
/*
* Do nothing for the config mechanism 1 case.
int data = -1;
int port;
- if (cfgmech == CFGMECH_PCIE) {
- data = pciereg_cfgread(bus, slot, func, reg, bytes);
- return (data);
- }
-
mtx_lock_spin(&pcicfg_mtx);
port = pci_cfgenable(bus, slot, func, reg, bytes);
if (port != 0) {
{
int port;
- if (cfgmech == CFGMECH_PCIE) {
- pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
- return;
- }
-
mtx_lock_spin(&pcicfg_mtx);
port = pci_cfgenable(bus, slot, func, reg, bytes);
if (port != 0) {
return (cfgmech);
}
-static int
-pciereg_cfgopen(void)
+int
+pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
{
#ifdef PCIE_CFG_MECH
struct pcie_cfg_list *pcielist;
struct pcpu *pc;
#endif
vm_offset_t va;
- int i;
+ uint32_t val1, val2;
+ int i, slot;
+
+ if (!mcfg_enable)
+ return (0);
+
+ if (minbus != 0)
+ return (0);
+
+ if (base >= 0x100000000) {
+ if (bootverbose)
+ kprintf(
+ "PCI: Memory Mapped PCI configuration area base 0x%jx too high\n",
+ (uintmax_t)base);
+ return (0);
+ }
if (bootverbose)
- kprintf("Setting up PCIe mappings for BAR 0x%x\n", pciebar);
+ kprintf("PCIe: Memory Mapped configuration base @ 0x%jx\n",
+ (uintmax_t)base);
#ifdef SMP
SLIST_FOREACH(pc, &cpuhead, pc_allcpu)
}
}
-
+ pcie_base = base;
+ pcie_minbus = minbus;
+ pcie_maxbus = maxbus;
cfgmech = CFGMECH_PCIE;
devmax = 32;
+
+ /*
+ * On some AMD systems, some of the devices on bus 0 are
+ * inaccessible using memory-mapped PCI config access. Walk
+ * bus 0 looking for such devices. For these devices, we will
+ * fall back to using type 1 config access instead.
+ */
+ if (pci_cfgregopen() != 0) {
+ for (slot = 0; slot < 32; slot++) {
+ val1 = pcireg_cfgread(0, slot, 0, 0, 4);
+ if (val1 == 0xffffffff)
+ continue;
+
+ val2 = pciereg_cfgread(0, slot, 0, 0, 4);
+ if (val2 != val1)
+ pcie_badslots |= (1 << slot);
+ }
+ }
+
return (1);
#else /* !PCIE_CFG_MECH */
return (0);
}
static int
-pciereg_cfgread(int bus, int slot, int func, int reg, int bytes)
+pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg,
+ unsigned bytes)
{
struct pcie_cfg_elem *elem;
volatile vm_offset_t va;
vm_paddr_t pa, papage;
- int data;
+ int data = -1;
+
+ if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
+ func > PCI_FUNCMAX || reg >= 0x1000 || bytes > 4 || bytes == 3)
+ return (-1);
crit_enter();
- pa = PCIE_PADDR(pciebar, reg, bus, slot, func);
+ pa = PCIE_PADDR(pcie_base, reg, bus, slot, func);
papage = pa & ~PAGE_MASK;
elem = pciereg_findelem(papage);
va = elem->vapage | (pa & PAGE_MASK);
case 1:
data = *(volatile uint8_t *)(va);
break;
- default:
- panic("pciereg_cfgread: invalid width");
}
crit_exit();
}
static void
-pciereg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
+pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data, unsigned bytes)
{
struct pcie_cfg_elem *elem;
volatile vm_offset_t va;
vm_paddr_t pa, papage;
crit_enter();
- pa = PCIE_PADDR(pciebar, reg, bus, slot, func);
+ pa = PCIE_PADDR(pcie_base, reg, bus, slot, func);
papage = pa & ~PAGE_MASK;
elem = pciereg_findelem(papage);
va = elem->vapage | (pa & PAGE_MASK);
case 1:
*(volatile uint8_t *)(va) = data;
break;
- default:
- panic("pciereg_cfgwrite: invalid width");
}
crit_exit();
#define CONF2_ENABLE_CHK 0x0e
#define CONF2_ENABLE_RES 0x0e
+int pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus);
int pci_cfgregopen(void);
u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes);
void pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes);
* (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: src/sys/i386/pci/pci_cfgreg.c,v 1.124.2.2.6.1 2009/04/15 03:14:26 kensmith Exp $
+ * $FreeBSD: src/sys/i386/pci/pci_cfgreg.c,v 1.124.2.3 2009/05/04 21:04:29 jhb
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
+#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/thread2.h>
};
static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU];
-static uint32_t pciebar;
+static uint64_t pcie_base;
+static int pcie_minbus, pcie_maxbus;
+static uint32_t pcie_badslots;
static int cfgmech;
static int devmax;
#if defined(__DragonFly__)
#else
static struct mtx pcicfg_mtx;
#endif
+static int mcfg_enable = 0;
+TUNABLE_INT("hw.pci.mcfg", &mcfg_enable);
+
+static uint32_t pci_docfgregread(int bus, int slot, int func, int reg, int bytes);
static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
static int pcireg_cfgopen(void);
-static int pciereg_cfgopen(void);
-static int pciereg_cfgread(int bus, int slot, int func, int reg,
- int bytes);
-static void pciereg_cfgwrite(int bus, int slot, int func, int reg,
- int data, int bytes);
+static int pciereg_cfgread(int bus, unsigned slot, unsigned func,
+ unsigned reg, unsigned bytes);
+static void pciereg_cfgwrite(int bus, unsigned slot, unsigned func,
+ unsigned reg, int data, unsigned bytes);
+
+
/*
* Some BIOS writers seem to want to ignore the spec and put
if (v >= 0x0210)
pci_pir_open();
+ if (cfgmech == CFGMECH_PCIE)
+ return (1);
+
/*
* Grope around in the PCI config space to see if this is a
* chipset that is capable of doing memory-mapped config cycles.
*/
/* Check for supported chipsets */
- vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
- did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
- if (vid == 0x8086) {
- if (did == 0x3590 || did == 0x3592) {
- /* Intel 7520 or 7320 */
- pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
- pciereg_cfgopen();
- } else if (did == 0x2580 || did == 0x2584) {
- /* Intel 915 or 925 */
- pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
- pciereg_cfgopen();
- }
- }
+ vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
+ did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
+ switch (vid) {
+ case 0x8086:
+ switch (did) {
+ case 0x3590:
+ case 0x3592:
+ /* Intel 7520 or 7320 */
+ pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
+ pcie_cfgregopen(pciebar, 0, 255);
+ break;
+ case 0x2580:
+ case 0x2584:
+ case 0x2590:
+ /* Intel 915, 925, or 915GM */
+ pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
+ pcie_cfgregopen(pciebar, 0, 255);
+ break;
+ }
+ }
+
#else
opened = 1;
#endif
return(1);
}
+static uint32_t
+pci_docfgregread(int bus, int slot, int func, int reg, int bytes)
+{
+
+ if (cfgmech == CFGMECH_PCIE &&
+ (bus >= pcie_minbus && bus <= pcie_maxbus) &&
+ (bus != 0 || !(1 << slot & pcie_badslots)))
+ return (pciereg_cfgread(bus, slot, func, reg, bytes));
+ else
+ return (pcireg_cfgread(bus, slot, func, reg, bytes));
+}
+
/*
* Read configuration space register
*/
* the code uses 255 as an invalid IRQ.
*/
if (reg == PCIR_INTLINE && bytes == 1) {
- line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
+ line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1);
return (pci_i386_map_intline(line));
}
- return (pcireg_cfgread(bus, slot, func, reg, bytes));
+ return (pci_docfgregread(bus, slot, func, reg, bytes));
}
/*
pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
{
- pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
+ if (cfgmech == CFGMECH_PCIE &&
+ (bus >= pcie_minbus && bus <= pcie_maxbus) &&
+ (bus != 0 || !(1 << slot & pcie_badslots)))
+ pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
+ else
+ pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
}
/*
&& (unsigned) bytes <= 4
&& (reg & (bytes - 1)) == 0) {
switch (cfgmech) {
+ case CFGMECH_PCIE:
case CFGMECH_1:
outl(CONF1_ADDR_PORT, (1 << 31)
| (bus << 16) | (slot << 11)
pci_cfgdisable(void)
{
switch (cfgmech) {
+ case CFGMECH_PCIE:
case CFGMECH_1:
/*
* Do nothing for the config mechanism 1 case.
int data = -1;
int port;
- if (cfgmech == CFGMECH_PCIE) {
- data = pciereg_cfgread(bus, slot, func, reg, bytes);
- return (data);
- }
-
mtx_lock_spin(&pcicfg_mtx);
port = pci_cfgenable(bus, slot, func, reg, bytes);
if (port != 0) {
{
int port;
- if (cfgmech == CFGMECH_PCIE) {
- pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
- return;
- }
-
mtx_lock_spin(&pcicfg_mtx);
port = pci_cfgenable(bus, slot, func, reg, bytes);
if (port != 0) {
return (cfgmech);
}
-static int
-pciereg_cfgopen(void)
+int
+pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
{
#ifdef PCIE_CFG_MECH
struct pcie_cfg_list *pcielist;
struct pcpu *pc;
#endif
vm_offset_t va;
- int i;
+ uint32_t val1, val2;
+ int i, slot;
+
+ if (!mcfg_enable)
+ return (0);
+
+ if (minbus != 0)
+ return (0);
+
+ if (base >= 0x100000000) {
+ if (bootverbose)
+ kprintf(
+ "PCI: Memory Mapped PCI configuration area base 0x%jx too high\n",
+ (uintmax_t)base);
+ return (0);
+ }
if (bootverbose)
- kprintf("Setting up PCIe mappings for BAR 0x%x\n", pciebar);
+ kprintf("PCIe: Memory Mapped configuration base @ 0x%jx\n",
+ (uintmax_t)base);
#ifdef SMP
SLIST_FOREACH(pc, &cpuhead, pc_allcpu)
}
}
-
+ pcie_base = base;
+ pcie_minbus = minbus;
+ pcie_maxbus = maxbus;
cfgmech = CFGMECH_PCIE;
devmax = 32;
+
+ /*
+ * On some AMD systems, some of the devices on bus 0 are
+ * inaccessible using memory-mapped PCI config access. Walk
+ * bus 0 looking for such devices. For these devices, we will
+ * fall back to using type 1 config access instead.
+ */
+ if (pci_cfgregopen() != 0) {
+ for (slot = 0; slot < 32; slot++) {
+ val1 = pcireg_cfgread(0, slot, 0, 0, 4);
+ if (val1 == 0xffffffff)
+ continue;
+
+ val2 = pciereg_cfgread(0, slot, 0, 0, 4);
+ if (val2 != val1)
+ pcie_badslots |= (1 << slot);
+ }
+ }
+
return (1);
#else /* !PCIE_CFG_MECH */
return (0);
}
static int
-pciereg_cfgread(int bus, int slot, int func, int reg, int bytes)
+pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg,
+ unsigned bytes)
{
struct pcie_cfg_elem *elem;
volatile vm_offset_t va;
vm_paddr_t pa, papage;
- int data;
+ int data = -1;
+
+ if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
+ func > PCI_FUNCMAX || reg >= 0x1000 || bytes > 4 || bytes == 3)
+ return (-1);
crit_enter();
- pa = PCIE_PADDR(pciebar, reg, bus, slot, func);
+ pa = PCIE_PADDR(pcie_base, reg, bus, slot, func);
papage = pa & ~PAGE_MASK;
elem = pciereg_findelem(papage);
va = elem->vapage | (pa & PAGE_MASK);
case 1:
data = *(volatile uint8_t *)(va);
break;
- default:
- panic("pciereg_cfgread: invalid width");
}
crit_exit();
}
static void
-pciereg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
+pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data, unsigned bytes)
{
struct pcie_cfg_elem *elem;
volatile vm_offset_t va;
vm_paddr_t pa, papage;
crit_enter();
- pa = PCIE_PADDR(pciebar, reg, bus, slot, func);
+ pa = PCIE_PADDR(pcie_base, reg, bus, slot, func);
papage = pa & ~PAGE_MASK;
elem = pciereg_findelem(papage);
va = elem->vapage | (pa & PAGE_MASK);
case 1:
*(volatile uint8_t *)(va) = data;
break;
- default:
- panic("pciereg_cfgwrite: invalid width");
}
crit_exit();
#define CONF2_ENABLE_CHK 0x0e
#define CONF2_ENABLE_RES 0x0e
+int pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus);
int pci_cfgregopen(void);
u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes);
void pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes);
#include "acglobal.h"
#include "pci_if.h"
+#include <bus/pci/pci_cfgreg.h>
#include <bus/pci/pcivar.h>
#include <bus/pci/pci_private.h>
char *buf, size_t buflen);
static int acpi_child_pnpinfo_str_method(device_t acdev, device_t child,
char *buf, size_t buflen);
+static void acpi_enable_pcie(void);
static device_method_t acpi_methods[] = {
/* Device interface */
goto out;
}
+ /* Handle MCFG table if present. */
+ acpi_enable_pcie();
+
/* Install the default address space handlers. */
status = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_SYSTEM_MEMORY, ACPI_DEFAULT_HANDLER, NULL, NULL);
}
/*
+ * Look for a MCFG table. If it is present, use the settings for
+ * domain (segment) 0 to setup PCI config space access via the memory
+ * map.
+ */
+static void
+acpi_enable_pcie(void)
+{
+ ACPI_TABLE_HEADER *hdr;
+ ACPI_MCFG_ALLOCATION *alloc, *end;
+ ACPI_STATUS status;
+
+ status = AcpiGetTable(ACPI_SIG_MCFG, 1, &hdr);
+ if (ACPI_FAILURE(status))
+ return;
+
+ end = (ACPI_MCFG_ALLOCATION *)((char *)hdr + hdr->Length);
+ alloc = (ACPI_MCFG_ALLOCATION *)((ACPI_TABLE_MCFG *)hdr + 1);
+ while (alloc < end) {
+ if (alloc->PciSegment == 0) {
+ pcie_cfgregopen(alloc->Address, alloc->StartBusNumber,
+ alloc->EndBusNumber);
+ return;
+ }
+ alloc++;
+ }
+}
+
+/*
* Scan all of the ACPI namespace and attach child devices.
*
* We should only expect to find devices in the \_PR, \_TZ, \_SI, and