diff mbox

[v3] PCI: Max Payload Size BIOS workaround

Message ID 1307339353.2874.134.camel@pasglop (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Benjamin Herrenschmidt June 6, 2011, 5:49 a.m. UTC
On Mon, 2011-06-06 at 10:08 +0530, Pratyush Anand wrote:
> I do not know, how will it be implemented as a generic for all the
> architecture.
> I have implemented it for SPEAr( Its an ARM based SOC). ARM architecture
> provides a postinit hook. I have implemented these modifications using
> this hook.
> This will also not work for hotplug case.
> Since patch is still to be sent to main line for review, I am posting relevant
> part of code here:

And here's my ppc variant :-) A bit different as you can see, the whole
thing gets only triggered if the platform populates the "pcie_settings"
pointer in our arch-specific "pci_controller" structure (one per host
bridge). This is typically static, and contains the capabilities
(payload and max read req. size) of the host controller.

Here too I don't handle hotplug (yet) as currently my only hotplug
capable machines do that in IBM proprietary hypervisor but I will
eventually have to.

Cheers,
Ben.

commit 4d38c68f402ebc7810e21347885904c1b621b9c8
Author: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Date:   Tue Mar 29 09:35:06 2011 +1100

    powerpc/pci: Add generic mechanism to configure PCIe device settings
    
    Settings such as MaxPayload and MaxReadRequestSize must be configured
    properly for devices to work. Linux generic PCIe code doesn't really
    touch them unless asked to by drivers (which is generally bogus btw),
    so we add a mechanism in arch/powerpc, under control of a new flag,
    that allows the platform to specify some "max" values for those settings.
    
    Currently we support those two:
    
      - MaxPayload : We set all devices to a value which is the min of the
    platform value and the capabilities of all devices under the PHB
    
      - MaxReadRqSize : We set all devices to the platform value (since there
    is no capability involved here)
    
    Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>



--
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 mbox

Patch

diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h
index b90dbf8..7ad3526 100644
--- a/arch/powerpc/include/asm/pci-bridge.h
+++ b/arch/powerpc/include/asm/pci-bridge.h
@@ -61,6 +61,11 @@  static inline int ppc_pci_has_flag(int flag)
 }
 #endif
 
+/* A bunch of settings to apply to all devices on PCIe */
+struct pcie_settings {
+	int max_payload;
+	int max_read_size;
+};
 
 /*
  * Structure of a PCI controller (host bridge)
@@ -135,6 +140,9 @@  struct pci_controller {
 	resource_size_t dma_window_base_cur;
 	resource_size_t dma_window_size;
 
+	/* Optional pointer to PCIe settings to apply */
+	const struct pcie_settings *pcie_settings;
+
 #ifdef CONFIG_PPC64
 	unsigned long buid;
 
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index 893af2a..3c00752 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -1685,6 +1685,55 @@  int early_find_capability(struct pci_controller *hose, int bus, int devfn,
 	return pci_bus_find_capability(fake_pci_bus(hose, bus), devfn, cap);
 }
 
+static int __devinit __fixup_pcie_scan_caps(struct pci_dev *dev, void *data)
+{
+	struct pcie_settings *set = data;
+	int pcie_cap, max_payload;
+	u32 devcap;
+
+	pcie_cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	if (pcie_cap == 0)
+		return 0;
+
+	pci_read_config_dword(dev, pcie_cap + PCI_EXP_DEVCAP, &devcap);
+	max_payload = devcap & PCI_EXP_DEVCAP_PAYLOAD;
+	if (max_payload < set->max_payload)
+		set->max_payload = max_payload;
+	return 0;
+}
+
+static int __devinit __fixup_pcie_settings(struct pci_dev *dev, void *data)
+{
+	struct pcie_settings *set = data;
+	int pcie_cap;
+	u16 devctl;
+
+	pcie_cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	if (pcie_cap == 0)
+		return 0;
+
+	pci_read_config_word(dev, pcie_cap + PCI_EXP_DEVCTL, &devctl);
+	devctl &= ~(PCI_EXP_DEVCTL_PAYLOAD | PCI_EXP_DEVCTL_READRQ);
+	devctl |= (set->max_read_size << 12) | (set->max_payload << 5);
+	pci_write_config_word(dev, pcie_cap + PCI_EXP_DEVCTL, devctl);
+	return 0;
+}
+
+static void __devinit fixup_pcie_settings(struct pci_controller *hose)
+{
+	struct pcie_settings set = *hose->pcie_settings;
+
+	pci_walk_bus(hose->bus, __fixup_pcie_scan_caps, &set);
+	pr_info("pci %04x: Capabilities for all devices:\n",
+		hose->global_number);
+	pr_info("pci %04x:   Max payload      : %d bytes\n",
+		hose->global_number, 1 << (set.max_payload + 7));
+	pr_info("pci %04x:   Max read rq size : %d bytes\n",
+		hose->global_number, 1 << (set.max_read_size + 7));
+	pci_walk_bus(hose->bus, __fixup_pcie_settings, &set);
+}
+
+
 /**
  * pci_scan_phb - Given a pci_controller, setup and scan the PCI bus
  * @hose: Pointer to the PCI host controller instance structure
@@ -1727,4 +1776,8 @@  void __devinit pcibios_scan_phb(struct pci_controller *hose)
 
 	if (mode == PCI_PROBE_NORMAL)
 		hose->last_busno = bus->subordinate = pci_scan_child_bus(bus);
+
+	/* Fixup max payload sizes etc... */
+	if (hose->pcie_settings)
+		fixup_pcie_settings(hose);
 }