From patchwork Wed Mar 7 22:34:53 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bjorn Helgaas X-Patchwork-Id: 145357 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 42CF3B6FA4 for ; Thu, 8 Mar 2012 09:41:12 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965300Ab2CGWko (ORCPT ); Wed, 7 Mar 2012 17:40:44 -0500 Received: from mail-ee0-f74.google.com ([74.125.83.74]:35647 "EHLO mail-ee0-f74.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965296Ab2CGWkm (ORCPT ); Wed, 7 Mar 2012 17:40:42 -0500 Received: by eekc41 with SMTP id c41so256862eek.1 for ; Wed, 07 Mar 2012 14:40:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=subject:to:from:cc:date:message-id:in-reply-to:references :user-agent:mime-version:content-type:content-transfer-encoding; bh=QYTMp88OvifhfQNJAMJHv9Grv6QyiT5jS9PhdtY7D1k=; b=ZczQoRFiheSQKkVoFLOnS+oMBAQ5Cu05p0HPPFhDsIV3Uz7QMpbt5DM8fQ8WtxW4jh r7UjnOMl2xMgTemBsy0THLQVelalsCGmTfYATxiWPTijo6Ry81lHzGq72MsI+/w1aFVU ptmY5QRS9lPSZiCjy42TQngMY5/lO/RNX9TiU3L/9eaLA59qTt6OSQPWtl5/DmgffvOI I0HRSMc/rcLKzElL4C3hd8uCk+pbNrjOHPJeO8Ap2Q0Q4CkVNvaN+bZ5649U0wno7Cfe 8vgO/kWcSmylZg9NTlzmM4p2YumhV9bqlA/bZt4+VBccIXSP/T/DcDwVIDxKOtm2Joy2 k/vg== Received: by 10.14.51.7 with SMTP id a7mr1428740eec.6.1331159694180; Wed, 07 Mar 2012 14:34:54 -0800 (PST) Received: by 10.14.51.7 with SMTP id a7mr1428722eec.6.1331159694061; Wed, 07 Mar 2012 14:34:54 -0800 (PST) Received: from hpza9.eem.corp.google.com ([74.125.121.33]) by gmr-mx.google.com with ESMTPS id y56si17200819eea.1.2012.03.07.14.34.54 (version=TLSv1/SSLv3 cipher=AES128-SHA); Wed, 07 Mar 2012 14:34:54 -0800 (PST) Received: from bhelgaas.mtv.corp.google.com (bhelgaas.mtv.corp.google.com [172.18.96.155]) by hpza9.eem.corp.google.com (Postfix) with ESMTP id ADB565C0065; Wed, 7 Mar 2012 14:34:53 -0800 (PST) Received: from bhelgaas.mtv.corp.google.com (unknown [IPv6:::1]) by bhelgaas.mtv.corp.google.com (Postfix) with ESMTP id 12D95180146; Wed, 7 Mar 2012 14:34:53 -0800 (PST) Subject: [PATCH v3 33/34] PCI: reimplement P2P bridge 1K I/O windows (Intel P64H2) To: Jesse Barnes From: Bjorn Helgaas Cc: linux-arch@vger.kernel.org, linux-pci@vger.kernel.org, sparclinux@vger.kernel.org, "David S. Miller" Date: Wed, 07 Mar 2012 15:34:53 -0700 Message-ID: <20120307223453.25669.30797.stgit@bhelgaas.mtv.corp.google.com> In-Reply-To: <20120307222436.25669.52282.stgit@bhelgaas.mtv.corp.google.com> References: <20120307222436.25669.52282.stgit@bhelgaas.mtv.corp.google.com> User-Agent: StGit/0.15 MIME-Version: 1.0 X-Gm-Message-State: ALoCoQkG7hlDiKiHV8uFQ66swIyPy2PWhP4zRPb3Mn7xZsHyLJYun4G1+I6ACvG+zDr0NTupc8TXJgXxjm0sE44NuAIV8a3nvOL40aeb0KFVpAOPXXZ1fZWGj3fS2TBN90nzA2jFdtWYHCv1tLJ0gDCW4GK+AGfYuJCa8JyO1UVSwur9RPBjEbw= Sender: sparclinux-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: sparclinux@vger.kernel.org 9d265124d051 and 15a260d53f7c added quirks for P2P bridges that support I/O windows that start/end at 1K boundaries, not just the 4K boundaries defined by the PCI spec. For details, see the IOBL_ADR register and the EN1K bit in the CNF register in the Intel 82870P2 (P64H2). These quirks complicate the code that reads P2P bridge windows (pci_read_bridge_io() and pci_cfg_fake_ranges()) because the bridge I/O resource is updated in the HEADER quirk, in pci_read_bridge_io(), in pci_setup_bridge(), and again in the FINAL quirk. This is confusing and makes it impossible to reassign the bridge windows after FINAL quirks are run. This patch adds support for 1K windows in the generic paths, so the HEADER quirk only has to enable this support. The FINAL quirk, which used to undo damage done by pci_setup_bridge(), is no longer needed. Note that the bridge window assignment code, e.g., pbus_size_io(), should pay attention to dev->io_window_1k, too, but I didn't fix that. CC: "David S. Miller" CC: sparclinux@vger.kernel.org Signed-off-by: Bjorn Helgaas --- arch/sparc/kernel/pci.c | 25 ++++++++++++++----------- drivers/pci/probe.c | 25 ++++++++++++++----------- drivers/pci/quirks.c | 39 +-------------------------------------- drivers/pci/setup-bus.c | 11 +++++++++-- include/linux/pci.h | 1 + include/linux/pci_regs.h | 3 ++- 6 files changed, 41 insertions(+), 63 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe sparclinux" 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/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index fdaf218..86610de 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -384,15 +384,23 @@ static void __devinit pci_cfg_fake_ranges(struct pci_dev *dev, struct pci_pbm_info *pbm) { struct pci_bus_region region; - struct resource *res, res2; + struct resource *res; u8 io_base_lo, io_limit_lo; u16 mem_base_lo, mem_limit_lo; - unsigned long base, limit; + unsigned long io_mask, io_granularity, base, limit; + + io_mask = PCI_IO_RANGE_MASK; + io_granularity = 0x1000; + if (dev->io_window_1k) { + /* Support 1K I/O space granularity */ + io_mask = PCI_IO_1K_RANGE_MASK; + io_granularity = 0x400; + } pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); - base = (io_base_lo & PCI_IO_RANGE_MASK) << 8; - limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8; + base = (io_base_lo & io_mask) << 8; + limit = (io_limit_lo & io_mask) << 8; if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { u16 io_base_hi, io_limit_hi; @@ -406,14 +414,9 @@ static void __devinit pci_cfg_fake_ranges(struct pci_dev *dev, res = bus->resource[0]; if (base <= limit) { res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; - res2.flags = res->flags; region.start = base; - region.end = limit + 0xfff; - pcibios_bus_to_resource(dev, &res2, ®ion); - if (!res->start) - res->start = res2.start; - if (!res->end) - res->end = res2.end; + region.end = limit + io_granularity - 1; + pcibios_bus_to_resource(dev, res, ®ion); } pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b81ec1c..e3bb7a8 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -347,15 +347,23 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) { struct pci_dev *dev = child->self; u8 io_base_lo, io_limit_lo; - unsigned long base, limit; + unsigned long io_mask, io_granularity, base, limit; struct pci_bus_region region; - struct resource *res, res2; + struct resource *res; + + io_mask = PCI_IO_RANGE_MASK; + io_granularity = 0x1000; + if (dev->io_window_1k) { + /* Support 1K I/O space granularity */ + io_mask = PCI_IO_1K_RANGE_MASK; + io_granularity = 0x400; + } res = child->resource[0]; pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); - base = (io_base_lo & PCI_IO_RANGE_MASK) << 8; - limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8; + base = (io_base_lo & io_mask) << 8; + limit = (io_limit_lo & io_mask) << 8; if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { u16 io_base_hi, io_limit_hi; @@ -367,14 +375,9 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) if (base <= limit) { res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; - res2.flags = res->flags; region.start = base; - region.end = limit + 0xfff; - pcibios_bus_to_resource(dev, &res2, ®ion); - if (!res->start) - res->start = res2.start; - if (!res->end) - res->end = res2.end; + region.end = limit + io_granularity - 1; + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index fb544d6..aa4e296 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1997,53 +1997,16 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, fixup_rev1 static void __devinit quirk_p64h2_1k_io(struct pci_dev *dev) { u16 en1k; - u8 io_base_lo, io_limit_lo; - unsigned long base, limit; - struct resource *res = dev->resource + PCI_BRIDGE_RESOURCES; pci_read_config_word(dev, 0x40, &en1k); if (en1k & 0x200) { dev_info(&dev->dev, "Enable I/O Space to 1KB granularity\n"); - - pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); - pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); - base = (io_base_lo & (PCI_IO_RANGE_MASK | 0x0c)) << 8; - limit = (io_limit_lo & (PCI_IO_RANGE_MASK | 0x0c)) << 8; - - if (base <= limit) { - res->start = base; - res->end = limit + 0x3ff; - } + dev->io_window_1k = 1; } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1460, quirk_p64h2_1k_io); -/* Fix the IOBL_ADR for 1k I/O space granularity on the Intel P64H2 - * The IOBL_ADR gets re-written to 4k boundaries in pci_setup_bridge() - * in drivers/pci/setup-bus.c - */ -static void __devinit quirk_p64h2_1k_io_fix_iobl(struct pci_dev *dev) -{ - u16 en1k, iobl_adr, iobl_adr_1k; - struct resource *res = dev->resource + PCI_BRIDGE_RESOURCES; - - pci_read_config_word(dev, 0x40, &en1k); - - if (en1k & 0x200) { - pci_read_config_word(dev, PCI_IO_BASE, &iobl_adr); - - iobl_adr_1k = iobl_adr | (res->start >> 8) | (res->end & 0xfc00); - - if (iobl_adr != iobl_adr_1k) { - dev_info(&dev->dev, "Fixing P64H2 IOBL_ADR from 0x%x to 0x%x for 1KB granularity\n", - iobl_adr,iobl_adr_1k); - pci_write_config_word(dev, PCI_IO_BASE, iobl_adr_1k); - } - } -} -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1460, quirk_p64h2_1k_io_fix_iobl); - /* Under some circumstances, AER is not linked with extended capabilities. * Force it to be linked by setting the corresponding control bit in the * config space. diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index e241f2f..b2a9f3a 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -476,16 +476,23 @@ static void pci_setup_bridge_io(struct pci_bus *bus) struct pci_dev *bridge = bus->self; struct resource *res; struct pci_bus_region region; + unsigned long io_mask; + u8 io_base_lo, io_limit_lo; u32 l, io_upper16; + io_mask = PCI_IO_RANGE_MASK; + if (bridge->io_window_1k) + io_mask = PCI_IO_1K_RANGE_MASK; + /* Set up the top and bottom of the PCI I/O segment for this bus. */ res = bus->resource[0]; pcibios_resource_to_bus(bridge, ®ion, res); if (res->flags & IORESOURCE_IO) { pci_read_config_dword(bridge, PCI_IO_BASE, &l); l &= 0xffff0000; - l |= (region.start >> 8) & 0x00f0; - l |= region.end & 0xf000; + io_base_lo = (region.start >> 8) & io_mask; + io_limit_lo = (region.end >> 8) & io_mask; + l |= ((u32) io_limit_lo << 8) | io_base_lo; /* Set up upper 16 bits of I/O base/limit. */ io_upper16 = (region.end & 0xffff0000) | (region.start >> 16); dev_info(&bridge->dev, " bridge window %pR\n", res); diff --git a/include/linux/pci.h b/include/linux/pci.h index be58a51..2db3c8c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -324,6 +324,7 @@ struct pci_dev { unsigned int is_hotplug_bridge:1; unsigned int __aer_firmware_first_valid:1; unsigned int __aer_firmware_first:1; + unsigned int io_window_1k:1; /* Intel P2P bridge 1K I/O windows */ pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 4b608f5..88c9ea5 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -125,7 +125,8 @@ #define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ #define PCI_IO_RANGE_TYPE_16 0x00 #define PCI_IO_RANGE_TYPE_32 0x01 -#define PCI_IO_RANGE_MASK (~0x0fUL) +#define PCI_IO_RANGE_MASK (~0x0fUL) /* Standard 4K I/O windows */ +#define PCI_IO_1K_RANGE_MASK (~0x03UL) /* Intel 1K I/O windows */ #define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ #define PCI_MEMORY_BASE 0x20 /* Memory range behind */ #define PCI_MEMORY_LIMIT 0x22