From patchwork Thu Jun 11 21:23:11 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jesse Barnes X-Patchwork-Id: 29642 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n5BLP3ZL028103 for ; Thu, 11 Jun 2009 21:25:03 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751128AbZFKVYi (ORCPT ); Thu, 11 Jun 2009 17:24:38 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1750976AbZFKVYi (ORCPT ); Thu, 11 Jun 2009 17:24:38 -0400 Received: from outbound-mail-301.bluehost.com ([67.222.53.8]:35363 "HELO outbound-mail-301.bluehost.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with SMTP id S1750851AbZFKVYg (ORCPT ); Thu, 11 Jun 2009 17:24:36 -0400 Received: (qmail 16407 invoked by uid 0); 11 Jun 2009 21:23:55 -0000 Received: from unknown (HELO box514.bluehost.com) (74.220.219.114) by outboundproxy6.bluehost.com with SMTP; 11 Jun 2009 21:23:55 -0000 DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=virtuousgeek.org; h=Received:Date:From:To:Cc:Subject:Message-ID:X-Mailer:Mime-Version:Content-Type:Content-Transfer-Encoding:X-Identified-User; b=Op6D72+gUSBHspwHBb6TJ18K4Yskh5mjFZM/Wy+cRW0Pwp4LgavfOsGARZe4cyDCa7cheguq1nujaSS6U0zcoIHO91x9AsYNH1j5Ajq3wn0PxR3ZhTAZa442Xd/fw0TI; Received: from [75.111.28.251] (helo=jbarnes-g45) by box514.bluehost.com with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.69) (envelope-from ) id 1MErkZ-0007KO-0p; Thu, 11 Jun 2009 15:23:55 -0600 Date: Thu, 11 Jun 2009 14:23:11 -0700 From: Jesse Barnes To: linux-pci@vger.kernel.org, x86@kernel.org Cc: jacob.jun.pan@intel.com Subject: [PATCH] x86/PCI: Intel Moorestown platform "PCI" support Message-ID: <20090611142311.1c6b3c7b@jbarnes-g45> X-Mailer: Claws Mail 3.7.1 (GTK+ 2.16.1; x86_64-pc-linux-gnu) Mime-Version: 1.0 X-Identified-User: {10642:box514.bluehost.com:virtuous:virtuousgeek.org} {sentby:smtp auth 75.111.28.251 authed with jbarnes@virtuousgeek.org} Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org The Moorestown platform only has a few devices that actually support PCI config cycles. The rest of the devices use an in-RAM MCFG space for the purposes of device enumeration and initialization. There are a few uglies in the fake support, like BAR sizes that aren't a power of two, sizing detection, and writes to the real devices, but other than that it's pretty straightforward. Another way to think of this is not really as PCI at all, but just a table in RAM describing which devices are present, their capabilities and their offsets in MMIO space. This could have been done with a special new firmware table on this platform, but given that we do have some real PCI devices too, simply describing things in an MCFG type space was pretty simple. Signed-off-by: Jesse Barnes Signed-off-by: Jacob Pan --- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/arch/x86/pci/Makefile b/arch/x86/pci/Makefile index d49202e..7b0cc85 100644 --- a/arch/x86/pci/Makefile +++ b/arch/x86/pci/Makefile @@ -13,5 +13,5 @@ obj-$(CONFIG_X86_VISWS) += visws.o obj-$(CONFIG_X86_NUMAQ) += numaq_32.o -obj-y += common.o early.o +obj-y += common.o early.o mrst.o obj-y += amd_bus.o diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index 6dd8955..eb1586f 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c @@ -521,3 +521,13 @@ static void sb600_disable_hpet_bar(struct pci_dev *dev) } } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, 0x4385, sb600_disable_hpet_bar); +/* Intel Moorestown graphics controller has new capabilities but the status + * register does not indicate that in bit 4. + */ +static void __devinit pci_mrst_gfx_controller_cap(struct pci_dev *dev) +{ + /* force new cap flag */ + pci_write_config_byte(dev, PCI_STATUS, PCI_STATUS_CAP_LIST); +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRST_GFX, + pci_mrst_gfx_controller_cap); diff --git a/arch/x86/pci/init.c b/arch/x86/pci/init.c index 25a1f8e..1726d55 100644 --- a/arch/x86/pci/init.c +++ b/arch/x86/pci/init.c @@ -14,6 +14,10 @@ static __init int pci_arch_init(void) if (!(pci_probe & PCI_PROBE_NOEARLY)) pci_mmcfg_early_init(); +#ifdef CONFIG_PCI_MMCONFIG + pci_mmcfg_late_init(); + pci_mrst_init(); +#endif #ifdef CONFIG_PCI_OLPC if (!pci_olpc_init()) diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index fecbce6..2931756 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1019,6 +1020,10 @@ static void __init pcibios_fixup_irqs(void) u8 pin; DBG(KERN_DEBUG "PCI: IRQ fixup\n"); + if (!platform_has(X86_PLATFORM_FEATURE_BIOS)) { + DBG(KERN_DEBUG "PCI: No BIOS, skip IRQ fixup\n"); + return; + } while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { /* * If the BIOS has set an out of range IRQ number, just @@ -1214,8 +1219,26 @@ static int pirq_enable_irq(struct pci_dev *dev) { u8 pin; struct pci_dev *temp_dev; + int ioapic; pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); +#if defined(CONFIG_X86_IO_APIC) && defined(CONFIG_SFI) + /* For platforms only have IOAPIC, the PCI irq line is 1:1 mapped to + * IOAPIC RTE entries, so we just enable RTE for the device. + */ + if (platform_has(X86_PLATFORM_FEATURE_IOAPIC) && + !platform_has(X86_PLATFORM_FEATURE_8259) && + !platform_has(X86_PLATFORM_FEATURE_ACPI) && + !platform_has(X86_PLATFORM_FEATURE_BIOS) && + pin) { + ioapic = mp_sfi_find_ioapic(dev->irq); + io_apic_set_pci_routing(ioapic, + dev->irq, /* IOAPIC pin */ + dev->irq, /* IRQ line */ + 1, /* level trigger */ + 1); /* polarity active low */ + } +#endif if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) { char *msg = ""; diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 8766b0e..4ff971a 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -17,6 +17,7 @@ #include #include #include +#include /* aperture is up to 256MB but BIOS may reserve less */ #define MMCONFIG_APER_MIN (2 * 1024*1024) @@ -186,7 +187,12 @@ static const char __init *pci_mmcfg_nvidia_mcp55(void) /* * do check if amd fam10h already took over */ - if (!acpi_disabled || pci_mmcfg_config_num || mcp55_checked) +#ifdef CONFIG_ACPI + + if (!acpi_disabled) + return NULL; +#endif + if (pci_mmcfg_config_num || mcp55_checked) return NULL; mcp55_checked = true; @@ -362,6 +368,7 @@ static void __init pci_mmcfg_insert_resources(void) pci_mmcfg_resources_inserted = 1; } +#ifdef CONFIG_ACPI static acpi_status __init check_mcfg_resource(struct acpi_resource *res, void *data) { @@ -429,6 +436,7 @@ static int __init is_acpi_reserved(u64 start, u64 end, unsigned not_used) return mcfg_res.flags; } +#endif typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type); @@ -491,9 +499,10 @@ static void __init pci_mmcfg_reject_broken(int early) (unsigned int)cfg->start_bus_number, (unsigned int)cfg->end_bus_number); +#ifdef CONFIG_ACPI if (!early) valid = is_mmconf_reserved(is_acpi_reserved, addr, size, i, cfg, 0); - +#endif if (valid) continue; @@ -542,10 +551,17 @@ static void __init __pci_mmcfg_init(int early) known_bridge = 1; } - if (!known_bridge) + if (!known_bridge) { +#ifdef CONFIG_SFI + sfi_table_parse(SFI_SIG_MCFG, sfi_parse_mcfg); +#else acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg); +#endif + } +#ifndef CONFIG_SFI pci_mmcfg_reject_broken(early); +#endif if ((pci_mmcfg_config_num == 0) || (pci_mmcfg_config == NULL) || diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c index 8b2d561..ee9373a 100644 --- a/arch/x86/pci/mmconfig_32.c +++ b/arch/x86/pci/mmconfig_32.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/arch/x86/pci/mrst.c b/arch/x86/pci/mrst.c new file mode 100644 index 0000000..1d69381 --- /dev/null +++ b/arch/x86/pci/mrst.c @@ -0,0 +1,236 @@ +/* + * Moorestown PCI support + * Copyright (c) 2008 Intel Corporation + * Jesse Barnes + * + * Moorestown has an interesting PCI implementation: + * - configuration space is memory mapped (as defined by MCFG) + * - Lincroft devices also have a real, type 1 configuration space + * - Early Lincroft silicon has a type 1 access bug that will cause + * a hang if non-existent devices are accessed + * - some devices have the "fixed BAR" capability, which means + * they can't be relocated or modified; check for that during + * BAR sizing + * + * So, we use the MCFG space for all reads and writes, but also send + * Lincroft writes to type 1 space. But only read/write if the device + * actually exists, otherwise return all 1s for reads and bit bucket + * the writes. + * + * Here we assume that type 1 and MCFG based access has already been set up, + * such that raw_pci_ops is type 1 and raw_pci_ext_ops is MCFG. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +/* + * Yuck, we really need to add PCIe capability functions to the core + */ +#define PCIE_CAP_OFFSET 0x100 + +/* Fixed BAR fields */ +#define PCIE_VNDR_CAP_ID_FIXED_BAR 0x00 /* Fixed BAR (TBD) */ +#define PCI_FIXED_BAR_0_SIZE 0x04 +#define PCI_FIXED_BAR_1_SIZE 0x08 +#define PCI_FIXED_BAR_2_SIZE 0x0c +#define PCI_FIXED_BAR_3_SIZE 0x10 +#define PCI_FIXED_BAR_4_SIZE 0x14 +#define PCI_FIXED_BAR_5_SIZE 0x1c + +int fixed_bar_cap(struct pci_bus *bus, unsigned int devfn) +{ + int pos; + u32 pcie_cap = 0, cap_data; + + pos = PCIE_CAP_OFFSET; + while (pos) { + if (raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, + devfn, pos, 4, &pcie_cap)) + return 0; + + if (pcie_cap == 0xffffffff) + return 0; + + if (PCI_EXT_CAP_ID(pcie_cap) == PCI_EXT_CAP_ID_VNDR) { + raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, + devfn, pos + 4, 4, &cap_data); + if ((cap_data & 0xffff) == PCIE_VNDR_CAP_ID_FIXED_BAR) + return pos; + } + + pos = pcie_cap >> 20; + } + + return 0; +} + +static int pci_device_update_fixed(struct pci_bus *bus, unsigned int devfn, + int reg, int len, u32 val, int offset) +{ + u32 size; + unsigned int domain, busnum; + int bar = (reg - PCI_BASE_ADDRESS_0) >> 2; + + domain = pci_domain_nr(bus); + busnum = bus->number; + + if (val == ~0 && len == 4) { + unsigned long decode; + + raw_pci_ext_ops->read(domain, busnum, devfn, + offset + 8 + (bar * 4), 4, &size); + + /* Turn the size into a decode pattern for the sizing code */ + if (size) { + decode = size - 1; + decode |= decode >> 1; + decode |= decode >> 2; + decode |= decode >> 4; + decode |= decode >> 8; + decode |= decode >> 16; + decode++; + decode = ~(decode - 1); + } else { + decode = ~0; + } + + /* + * If val is all ones, the core code is trying to size the reg, + * so update the mmconfig space with the real size. + * + * Note: this assumes the fixed size we got is a power of two. + */ + return raw_pci_ext_ops->write(domain, busnum, devfn, reg, 4, + decode); + } + + /* This is some other kind of BAR write, so just do it. */ + return raw_pci_ext_ops->write(domain, busnum, devfn, reg, len, val); +} + +/** + * type1_access_ok - check whether to use type 1 + * @bus: bus number + * @devfn: device & function in question + * + * If the bus is on a Lincroft chip and it exists, or is not on a Lincroft at + * all, the we can go ahead with any reads & writes. If it's on a Lincroft, + * but doesn't exist, avoid the access altogether to keep the chip from + * hanging. + */ +static bool type1_access_ok(unsigned int bus, unsigned int devfn, int reg) +{ + /* This is a workaround for A0 LNC bug where PCI status register does + * not have new CAP bit set. can not be written by SW either. + * + * PCI header type in real LNC indicates a single function device, this + * will prevent probing other devices under the same function in PCI + * shim. Therefore, use the header type in shim instead. + */ + if (reg >= 0x100 || reg == PCI_STATUS || reg == PCI_HEADER_TYPE) + return 0; + if (bus == 0 && (devfn == PCI_DEVFN(2, 0) || devfn == PCI_DEVFN(0, 0))) + return 1; + return 0; /* langwell on others */ +} + +static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + if (type1_access_ok(bus->number, devfn, where)) + return raw_pci_ops->read(pci_domain_nr(bus), bus->number, devfn, + where, size, value); + return raw_pci_ext_ops->read(pci_domain_nr(bus), bus->number, + devfn, where, size, value); +} + +static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + int offset; + + /* On MRST, there is no PCI ROM BAR, this will cause a subsequent read + * to ROM BAR return 0 then being ignored. + */ + if (where == PCI_ROM_ADDRESS) + return 0; + /* + * Devices with fixed BARs need special handling: + * - BAR sizing code will save, write ~0, read size, restore + * - so writes to fixed BARs need special handling + * - other writes to fixed BAR devices should go through mmconfig + */ + offset = fixed_bar_cap(bus, devfn); + if (offset && + (where >= PCI_BASE_ADDRESS_0 && where <= PCI_BASE_ADDRESS_5)) { + return pci_device_update_fixed(bus, devfn, where, size, value, + offset); + } + + /* + * On Moorestown update both real & mmconfig space + * Note: assumes raw_pci_ext_ops is mmconfig if we're using Lincroft + * Note 2: early Lincroft silicon can't handle type 1 accesses to + * non-existent devices, so just eat the write in that case. + */ + if (type1_access_ok(bus->number, devfn, where)) + return raw_pci_ops->write(pci_domain_nr(bus), bus->number, + devfn, where, size, value); + return raw_pci_ext_ops->write(pci_domain_nr(bus), bus->number, devfn, + where, size, value); +} + +struct pci_ops pci_mrst_ops = { + .read = pci_read, + .write = pci_write, +}; +#ifdef CONFIG_MRST +/** + * pci_mrst_init - figure out if this platform should use pci_mrst_ops + * + * Moorestown has an interesting PCI implementation (see above). This + * function checks whether the current platform should use MRST-style PCI + * config space ops or not, and sets pci_root_ops accordingly. + */ +void pci_mrst_init(void) +{ + printk(KERN_INFO "Moorestown platform detected, using MRST PCI ops\n"); + pci_root_ops = pci_mrst_ops; +} +#else +void pci_mrst_init(void) { } +#endif + +#ifdef CONFIG_MRST +/* + * Langwell devices reside at fixed offsets, don't try to move them. + */ +static void __devinit pci_fixed_bar_fixup(struct pci_dev *dev) +{ + unsigned long offset; + u32 size; + int i; + /* Fixup the BAR sizes for fixed BAR devices and make them unmoveable */ + offset = fixed_bar_cap(dev->bus, dev->devfn); + if (!offset || PCI_DEVFN(2, 0) == dev->devfn || + PCI_DEVFN(2, 2) == dev->devfn) + return; + + for (i = 0; i < PCI_ROM_RESOURCE; i++) { + pci_read_config_dword(dev, offset + 8 + (i * 4), &size); + dev->resource[i].end = dev->resource[i].start + size - 1; + dev->resource[i].flags |= IORESOURCE_PCI_FIXED; + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_fixed_bar_fixup); +#endif + diff --git a/include/linux/pci.h b/include/linux/pci.h index 72698d8..fe4d18d 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -708,6 +708,7 @@ int pci_execute_reset_function(struct pci_dev *dev); void pci_update_resource(struct pci_dev *dev, int resno); int __must_check pci_assign_resource(struct pci_dev *dev, int i); int pci_select_bars(struct pci_dev *dev, unsigned long flags); +int fixed_bar_cap(struct pci_bus *bus, unsigned int devfn); /* ROM control related routines */ int pci_enable_rom(struct pci_dev *pdev); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 0f71812..1a6e34a 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2371,6 +2371,8 @@ #define PCI_DEVICE_ID_INTEL_82375 0x0482 #define PCI_DEVICE_ID_INTEL_82424 0x0483 #define PCI_DEVICE_ID_INTEL_82378 0x0484 +#define PCI_DEVICE_ID_INTEL_MRST_SD0 0x0807 +#define PCI_DEVICE_ID_INTEL_MRST_SD1 0x0808 #define PCI_DEVICE_ID_INTEL_I960 0x0960 #define PCI_DEVICE_ID_INTEL_I960RM 0x0962 #define PCI_DEVICE_ID_INTEL_8257X_SOL 0x1062 @@ -2539,6 +2541,7 @@ #define PCI_DEVICE_ID_INTEL_PCH_LPC_MAX 0x3b1f #define PCI_DEVICE_ID_INTEL_PCH_SMBUS 0x3b30 #define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f +#define PCI_DEVICE_ID_INTEL_MRST_GFX 0x4102 #define PCI_DEVICE_ID_INTEL_5100_16 0x65f0 #define PCI_DEVICE_ID_INTEL_5100_21 0x65f5 #define PCI_DEVICE_ID_INTEL_5100_22 0x65f6 diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 616bf8b..4b412ae 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -503,6 +503,7 @@ #define PCI_EXT_CAP_ID_PWR 4 #define PCI_EXT_CAP_ID_ARI 14 #define PCI_EXT_CAP_ID_SRIOV 16 +#define PCI_EXT_CAP_ID_VNDR 11 /* Advanced Error Reporting */ #define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */