From: Lucas Kannebley Tavares <luca...@linux.vnet.ibm.com>

On pseries machines the detection for max_bus_speed should be done
through an OpenFirmware property. This patch adds a function to perform
this detection and a hook to perform dynamic adding of the function only for
pseries. This is done by overwriting the weak
pcibios_root_bridge_prepare function which is called by pci_create_root_bus().

Signed-off-by: Lucas Kannebley Tavares <lucaskt at linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/machdep.h       |  2 ++
 arch/powerpc/kernel/pci-common.c         |  8 +++++
 arch/powerpc/platforms/pseries/pci.c     | 51 ++++++++++++++++++++++++++++++++
 arch/powerpc/platforms/pseries/pseries.h |  4 +++
 arch/powerpc/platforms/pseries/setup.c   |  2 ++
 5 files changed, 67 insertions(+)

diff --git a/arch/powerpc/include/asm/machdep.h 
b/arch/powerpc/include/asm/machdep.h
index 3d6b410..8f558bf 100644
--- a/arch/powerpc/include/asm/machdep.h
+++ b/arch/powerpc/include/asm/machdep.h
@@ -107,6 +107,8 @@ struct machdep_calls {
        void            (*pcibios_fixup)(void);
        int             (*pci_probe_mode)(struct pci_bus *);
        void            (*pci_irq_fixup)(struct pci_dev *dev);
+       int             (*pcibios_root_bridge_prepare)(struct pci_host_bridge
+                               *bridge);

        /* To setup PHBs when using automatic OF platform driver for PCI */
        int             (*pci_setup_phb)(struct pci_controller *host);
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index fa12ae4..80986cf 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -844,6 +844,14 @@ int pci_proc_domain(struct pci_bus *bus)
        return 1;
 }

+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+       if (ppc_md.pcibios_root_bridge_prepare)
+               return ppc_md.pcibios_root_bridge_prepare(bridge);
+
+       return 0;
+}
+
 /* This header fixup will do the resource fixup for all devices as they are
  * probed, but not for bridge ranges
  */
diff --git a/arch/powerpc/platforms/pseries/pci.c 
b/arch/powerpc/platforms/pseries/pci.c
index 0b580f4..7f9c956 100644
--- a/arch/powerpc/platforms/pseries/pci.c
+++ b/arch/powerpc/platforms/pseries/pci.c
@@ -108,3 +108,54 @@ static void fixup_winbond_82c105(struct pci_dev* dev)
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105,
                         fixup_winbond_82c105);
+
+int pseries_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+       struct device_node *dn, *pdn;
+       struct pci_bus *bus;
+       const uint32_t *pcie_link_speed_stats;
+
+       bus = bridge->bus;
+
+       dn = pcibios_get_phb_of_node(bus);
+       if (!dn)
+               return 0;
+
+       for (pdn = dn; pdn != NULL; pdn = pdn->parent) {
+               pcie_link_speed_stats = (const uint32_t *) of_get_property(dn,
+                       "ibm,pcie-link-speed-stats", NULL);
+               if (pcie_link_speed_stats)
+                       break;
+       }
+
+       if (!pcie_link_speed_stats) {
+               pr_err("no ibm,pcie-link-speed-stats property\n");
+               return 0;
+       }
+
+       switch (pcie_link_speed_stats[0]) {
+       case 0x01:
+               bus->max_bus_speed = PCIE_SPEED_2_5GT;
+               break;
+       case 0x02:
+               bus->max_bus_speed = PCIE_SPEED_5_0GT;
+               break;
+       default:
+               bus->max_bus_speed = PCI_SPEED_UNKNOWN;
+               break;
+       }
+
+       switch (pcie_link_speed_stats[1]) {
+       case 0x01:
+               bus->cur_bus_speed = PCIE_SPEED_2_5GT;
+               break;
+       case 0x02:
+               bus->cur_bus_speed = PCIE_SPEED_5_0GT;
+               break;
+       default:
+               bus->cur_bus_speed = PCI_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
diff --git a/arch/powerpc/platforms/pseries/pseries.h 
b/arch/powerpc/platforms/pseries/pseries.h
index 9a3dda0..b79393d 100644
--- a/arch/powerpc/platforms/pseries/pseries.h
+++ b/arch/powerpc/platforms/pseries/pseries.h
@@ -60,4 +60,8 @@ extern int dlpar_detach_node(struct device_node *);
 /* Snooze Delay, pseries_idle */
 DECLARE_PER_CPU(long, smt_snooze_delay);

+/* PCI root bridge prepare function override for pseries */
+struct pci_host_bridge;
+int pseries_root_bridge_prepare(struct pci_host_bridge *bridge);
+
 #endif /* _PSERIES_PSERIES_H */
diff --git a/arch/powerpc/platforms/pseries/setup.c 
b/arch/powerpc/platforms/pseries/setup.c
index 8bcc9ca..bf34cc9 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -466,6 +466,8 @@ static void __init pSeries_setup_arch(void)
        else
                ppc_md.enable_pmcs = power4_enable_pmcs;

+       ppc_md.pcibios_root_bridge_prepare = pseries_root_bridge_prepare;
+
        if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
                long rc;
                if ((rc = pSeries_enable_reloc_on_exc()) != H_SUCCESS) {
-- 
1.8.1.4

Reply via email to