Merge branch 'pci/host/hv'
authorBjorn Helgaas <bhelgaas@google.com>
Thu, 13 Jan 2022 15:57:47 +0000 (09:57 -0600)
committerBjorn Helgaas <bhelgaas@google.com>
Thu, 13 Jan 2022 15:57:47 +0000 (09:57 -0600)
- Add hv-internal interfaces to encapsulate arch IRQ dependencies (Sunil
  Muthuswamy)

- Add arm64 Hyper-V vPCI support (Sunil Muthuswamy)

* pci/host/hv:
  PCI: hv: Add arm64 Hyper-V vPCI support
  PCI: hv: Make the code arch neutral by adding arch specific interfaces

30 files changed:
Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml
Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
MAINTAINERS
arch/x86/pci/acpi.c
drivers/char/agp/amd64-agp.c
drivers/char/agp/sis-agp.c
drivers/char/agp/via-agp.c
drivers/pci/controller/dwc/pci-imx6.c
drivers/pci/controller/dwc/pci-layerscape.c
drivers/pci/controller/dwc/pcie-designware.c
drivers/pci/controller/dwc/pcie-qcom-ep.c
drivers/pci/controller/pci-aardvark.c
drivers/pci/controller/pcie-apple.c
drivers/pci/controller/pcie-brcmstb.c
drivers/pci/endpoint/pci-epc-core.c
drivers/pci/hotplug/TODO
drivers/pci/hotplug/ibmphp_core.c
drivers/pci/hotplug/pciehp.h
drivers/pci/hotplug/pciehp_core.c
drivers/pci/hotplug/pciehp_hpc.c
drivers/pci/p2pdma.c
drivers/pci/pci-bridge-emul.c
drivers/pci/pci.c
drivers/pci/pcie/aspm.c
drivers/pci/probe.c
drivers/pci/quirks.c
drivers/pci/setup-res.c
drivers/pci/slot.c
drivers/pci/switch/switchtec.c
include/linux/pci.h

index 1fe1027..3e3c892 100644 (file)
@@ -143,11 +143,15 @@ examples:
                     #address-cells = <3>;
                     #size-cells = <2>;
                     #interrupt-cells = <1>;
-                    interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
+                    interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>,
                                  <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
                     interrupt-names = "pcie", "msi";
                     interrupt-map-mask = <0x0 0x0 0x0 0x7>;
-                    interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
+                    interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH
+                                     0 0 0 2 &gicv2 GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH
+                                     0 0 0 3 &gicv2 GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH
+                                     0 0 0 4 &gicv2 GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
+
                     msi-parent = <&pcie0>;
                     msi-controller;
                     ranges = <0x02000000 0x0 0xf8000000 0x6 0x00000000 0x0 0x04000000>;
@@ -155,5 +159,24 @@ examples:
                                  <0x42000000 0x1 0x80000000 0x3 0x00000000 0x0 0x80000000>;
                     brcm,enable-ssc;
                     brcm,scb-sizes =  <0x0000000080000000 0x0000000080000000>;
+
+                    /* PCIe bridge, Root Port */
+                    pci@0,0 {
+                            #address-cells = <3>;
+                            #size-cells = <2>;
+                            reg = <0x0 0x0 0x0 0x0 0x0>;
+                            compatible = "pciclass,0604";
+                            device_type = "pci";
+                            vpcie3v3-supply = <&vreg7>;
+                            ranges;
+
+                            /* PCIe endpoint */
+                            pci-ep@0,0 {
+                                    assigned-addresses =
+                                        <0x82010000 0x0 0xf8000000 0x6 0x00000000 0x0 0x2000>;
+                                    reg = <0x0 0x0 0x0 0x0 0x0>;
+                                    compatible = "pci14e4,1688";
+                            };
+                    };
             };
     };
index acea1cd..643a633 100644 (file)
@@ -127,6 +127,12 @@ properties:
     enum: [1, 2, 3, 4]
     default: 1
 
+  phys:
+    maxItems: 1
+
+  phy-names:
+    const: pcie-phy
+
   reset-gpio:
     description: Should specify the GPIO for controlling the PCI bus device
       reset signal. It's not polarity aware and defaults to active-low reset
index 7a2345c..ea59e32 100644 (file)
@@ -14717,6 +14717,19 @@ L:     linux-pci@vger.kernel.org
 S:     Supported
 F:     Documentation/PCI/pci-error-recovery.rst
 
+PCI PEER-TO-PEER DMA (P2PDMA)
+M:     Bjorn Helgaas <bhelgaas@google.com>
+M:     Logan Gunthorpe <logang@deltatee.com>
+L:     linux-pci@vger.kernel.org
+S:     Supported
+Q:     https://patchwork.kernel.org/project/linux-pci/list/
+B:     https://bugzilla.kernel.org
+C:     irc://irc.oftc.net/linux-pci
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git
+F:     Documentation/driver-api/pci/p2pdma.rst
+F:     drivers/pci/p2pdma.c
+F:     include/linux/pci-p2pdma.h
+
 PCI MSI DRIVER FOR ALTERA MSI IP
 M:     Joyce Ooi <joyce.ooi@intel.com>
 L:     linux-pci@vger.kernel.org
index 9486560..052f1d7 100644 (file)
@@ -20,7 +20,7 @@ struct pci_root_info {
 };
 
 static bool pci_use_crs = true;
-static bool pci_ignore_seg = false;
+static bool pci_ignore_seg;
 
 static int __init set_use_crs(const struct dmi_system_id *id)
 {
index b40edae..dc78a4f 100644 (file)
@@ -588,20 +588,11 @@ static void agp_amd64_remove(struct pci_dev *pdev)
        agp_bridges_found--;
 }
 
-#ifdef CONFIG_PM
+#define agp_amd64_suspend NULL
 
-static int agp_amd64_suspend(struct pci_dev *pdev, pm_message_t state)
+static int __maybe_unused agp_amd64_resume(struct device *dev)
 {
-       pci_save_state(pdev);
-       pci_set_power_state(pdev, pci_choose_state(pdev, state));
-
-       return 0;
-}
-
-static int agp_amd64_resume(struct pci_dev *pdev)
-{
-       pci_set_power_state(pdev, PCI_D0);
-       pci_restore_state(pdev);
+       struct pci_dev *pdev = to_pci_dev(dev);
 
        if (pdev->vendor == PCI_VENDOR_ID_NVIDIA)
                nforce3_agp_init(pdev);
@@ -609,8 +600,6 @@ static int agp_amd64_resume(struct pci_dev *pdev)
        return amd_8151_configure();
 }
 
-#endif /* CONFIG_PM */
-
 static const struct pci_device_id agp_amd64_pci_table[] = {
        {
        .class          = (PCI_CLASS_BRIDGE_HOST << 8),
@@ -738,15 +727,14 @@ static const struct pci_device_id agp_amd64_pci_promisc_table[] = {
        { }
 };
 
+static SIMPLE_DEV_PM_OPS(agp_amd64_pm_ops, agp_amd64_suspend, agp_amd64_resume);
+
 static struct pci_driver agp_amd64_pci_driver = {
        .name           = "agpgart-amd64",
        .id_table       = agp_amd64_pci_table,
        .probe          = agp_amd64_probe,
        .remove         = agp_amd64_remove,
-#ifdef CONFIG_PM
-       .suspend        = agp_amd64_suspend,
-       .resume         = agp_amd64_resume,
-#endif
+       .driver.pm  = &agp_amd64_pm_ops,
 };
 
 
index 14909fc..f8a02f4 100644 (file)
@@ -217,26 +217,14 @@ static void agp_sis_remove(struct pci_dev *pdev)
        agp_put_bridge(bridge);
 }
 
-#ifdef CONFIG_PM
+#define agp_sis_suspend NULL
 
-static int agp_sis_suspend(struct pci_dev *pdev, pm_message_t state)
+static int __maybe_unused agp_sis_resume(
+       __attribute__((unused)) struct device *dev)
 {
-       pci_save_state(pdev);
-       pci_set_power_state(pdev, pci_choose_state(pdev, state));
-
-       return 0;
-}
-
-static int agp_sis_resume(struct pci_dev *pdev)
-{
-       pci_set_power_state(pdev, PCI_D0);
-       pci_restore_state(pdev);
-
        return sis_driver.configure();
 }
 
-#endif /* CONFIG_PM */
-
 static const struct pci_device_id agp_sis_pci_table[] = {
        {
                .class          = (PCI_CLASS_BRIDGE_HOST << 8),
@@ -419,15 +407,14 @@ static const struct pci_device_id agp_sis_pci_table[] = {
 
 MODULE_DEVICE_TABLE(pci, agp_sis_pci_table);
 
+static SIMPLE_DEV_PM_OPS(agp_sis_pm_ops, agp_sis_suspend, agp_sis_resume);
+
 static struct pci_driver agp_sis_pci_driver = {
        .name           = "agpgart-sis",
        .id_table       = agp_sis_pci_table,
        .probe          = agp_sis_probe,
        .remove         = agp_sis_remove,
-#ifdef CONFIG_PM
-       .suspend        = agp_sis_suspend,
-       .resume         = agp_sis_resume,
-#endif
+       .driver.pm      = &agp_sis_pm_ops,
 };
 
 static int __init agp_sis_init(void)
index 87a92a0..a460ae3 100644 (file)
@@ -492,22 +492,11 @@ static void agp_via_remove(struct pci_dev *pdev)
        agp_put_bridge(bridge);
 }
 
-#ifdef CONFIG_PM
+#define agp_via_suspend NULL
 
-static int agp_via_suspend(struct pci_dev *pdev, pm_message_t state)
+static int __maybe_unused agp_via_resume(struct device *dev)
 {
-       pci_save_state (pdev);
-       pci_set_power_state (pdev, PCI_D3hot);
-
-       return 0;
-}
-
-static int agp_via_resume(struct pci_dev *pdev)
-{
-       struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
-
-       pci_set_power_state (pdev, PCI_D0);
-       pci_restore_state(pdev);
+       struct agp_bridge_data *bridge = dev_get_drvdata(dev);
 
        if (bridge->driver == &via_agp3_driver)
                return via_configure_agp3();
@@ -517,8 +506,6 @@ static int agp_via_resume(struct pci_dev *pdev)
        return 0;
 }
 
-#endif /* CONFIG_PM */
-
 /* must be the same order as name table above */
 static const struct pci_device_id agp_via_pci_table[] = {
 #define ID(x) \
@@ -567,16 +554,14 @@ static const struct pci_device_id agp_via_pci_table[] = {
 
 MODULE_DEVICE_TABLE(pci, agp_via_pci_table);
 
+static SIMPLE_DEV_PM_OPS(agp_via_pm_ops, agp_via_suspend, agp_via_resume);
 
 static struct pci_driver agp_via_pci_driver = {
        .name           = "agpgart-via",
        .id_table       = agp_via_pci_table,
        .probe          = agp_via_probe,
        .remove         = agp_via_remove,
-#ifdef CONFIG_PM
-       .suspend        = agp_via_suspend,
-       .resume         = agp_via_resume,
-#endif
+       .driver.pm      = &agp_via_pm_ops,
 };
 
 
index 26f49f7..6974bd5 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/types.h>
 #include <linux/interrupt.h>
 #include <linux/reset.h>
+#include <linux/phy/phy.h>
 #include <linux/pm_domain.h>
 #include <linux/pm_runtime.h>
 
@@ -49,6 +50,7 @@ enum imx6_pcie_variants {
        IMX6QP,
        IMX7D,
        IMX8MQ,
+       IMX8MM,
 };
 
 #define IMX6_PCIE_FLAG_IMX6_PHY                        BIT(0)
@@ -88,6 +90,7 @@ struct imx6_pcie {
        struct device           *pd_pcie;
        /* power domain for pcie phy */
        struct device           *pd_pcie_phy;
+       struct phy              *phy;
        const struct imx6_pcie_drvdata *drvdata;
 };
 
@@ -372,6 +375,8 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
        case IMX7D:
        case IMX8MQ:
                reset_control_assert(imx6_pcie->pciephy_reset);
+               fallthrough;
+       case IMX8MM:
                reset_control_assert(imx6_pcie->apps_reset);
                break;
        case IMX6SX:
@@ -407,7 +412,8 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
 
 static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie)
 {
-       WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ);
+       WARN_ON(imx6_pcie->drvdata->variant != IMX8MQ &&
+               imx6_pcie->drvdata->variant != IMX8MM);
        return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14;
 }
 
@@ -446,6 +452,11 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
                break;
        case IMX7D:
                break;
+       case IMX8MM:
+               ret = clk_prepare_enable(imx6_pcie->pcie_aux);
+               if (ret)
+                       dev_err(dev, "unable to enable pcie_aux clock\n");
+               break;
        case IMX8MQ:
                ret = clk_prepare_enable(imx6_pcie->pcie_aux);
                if (ret) {
@@ -522,6 +533,14 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
                goto err_ref_clk;
        }
 
+       switch (imx6_pcie->drvdata->variant) {
+       case IMX8MM:
+               if (phy_power_on(imx6_pcie->phy))
+                       dev_err(dev, "unable to power on PHY\n");
+               break;
+       default:
+               break;
+       }
        /* allow the clocks to stabilize */
        usleep_range(200, 500);
 
@@ -538,6 +557,10 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
        case IMX8MQ:
                reset_control_deassert(imx6_pcie->pciephy_reset);
                break;
+       case IMX8MM:
+               if (phy_init(imx6_pcie->phy))
+                       dev_err(dev, "waiting for phy ready timeout!\n");
+               break;
        case IMX7D:
                reset_control_deassert(imx6_pcie->pciephy_reset);
 
@@ -614,6 +637,12 @@ static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie)
 static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
 {
        switch (imx6_pcie->drvdata->variant) {
+       case IMX8MM:
+               /*
+                * The PHY initialization had been done in the PHY
+                * driver, break here directly.
+                */
+               break;
        case IMX8MQ:
                /*
                 * TODO: Currently this code assumes external
@@ -753,6 +782,7 @@ static void imx6_pcie_ltssm_enable(struct device *dev)
                break;
        case IMX7D:
        case IMX8MQ:
+       case IMX8MM:
                reset_control_deassert(imx6_pcie->apps_reset);
                break;
        }
@@ -871,6 +901,7 @@ static void imx6_pcie_ltssm_disable(struct device *dev)
                                   IMX6Q_GPR12_PCIE_CTL_2, 0);
                break;
        case IMX7D:
+       case IMX8MM:
                reset_control_assert(imx6_pcie->apps_reset);
                break;
        default:
@@ -930,6 +961,7 @@ static void imx6_pcie_clk_disable(struct imx6_pcie *imx6_pcie)
                                   IMX7D_GPR12_PCIE_PHY_REFCLK_SEL);
                break;
        case IMX8MQ:
+       case IMX8MM:
                clk_disable_unprepare(imx6_pcie->pcie_aux);
                break;
        default:
@@ -945,8 +977,16 @@ static int imx6_pcie_suspend_noirq(struct device *dev)
                return 0;
 
        imx6_pcie_pm_turnoff(imx6_pcie);
-       imx6_pcie_clk_disable(imx6_pcie);
        imx6_pcie_ltssm_disable(dev);
+       imx6_pcie_clk_disable(imx6_pcie);
+       switch (imx6_pcie->drvdata->variant) {
+       case IMX8MM:
+               if (phy_power_off(imx6_pcie->phy))
+                       dev_err(dev, "unable to power off PHY\n");
+               break;
+       default:
+               break;
+       }
 
        return 0;
 }
@@ -1043,11 +1083,6 @@ static int imx6_pcie_probe(struct platform_device *pdev)
        }
 
        /* Fetch clocks */
-       imx6_pcie->pcie_phy = devm_clk_get(dev, "pcie_phy");
-       if (IS_ERR(imx6_pcie->pcie_phy))
-               return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_phy),
-                                    "pcie_phy clock source missing or invalid\n");
-
        imx6_pcie->pcie_bus = devm_clk_get(dev, "pcie_bus");
        if (IS_ERR(imx6_pcie->pcie_bus))
                return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_bus),
@@ -1090,9 +1125,34 @@ static int imx6_pcie_probe(struct platform_device *pdev)
                        return PTR_ERR(imx6_pcie->apps_reset);
                }
                break;
+       case IMX8MM:
+               imx6_pcie->pcie_aux = devm_clk_get(dev, "pcie_aux");
+               if (IS_ERR(imx6_pcie->pcie_aux))
+                       return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_aux),
+                                            "pcie_aux clock source missing or invalid\n");
+               imx6_pcie->apps_reset = devm_reset_control_get_exclusive(dev,
+                                                                        "apps");
+               if (IS_ERR(imx6_pcie->apps_reset))
+                       return dev_err_probe(dev, PTR_ERR(imx6_pcie->apps_reset),
+                                            "failed to get pcie apps reset control\n");
+
+               imx6_pcie->phy = devm_phy_get(dev, "pcie-phy");
+               if (IS_ERR(imx6_pcie->phy))
+                       return dev_err_probe(dev, PTR_ERR(imx6_pcie->phy),
+                                            "failed to get pcie phy\n");
+
+               break;
        default:
                break;
        }
+       /* Don't fetch the pcie_phy clock, if it has abstract PHY driver */
+       if (imx6_pcie->phy == NULL) {
+               imx6_pcie->pcie_phy = devm_clk_get(dev, "pcie_phy");
+               if (IS_ERR(imx6_pcie->pcie_phy))
+                       return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_phy),
+                                            "pcie_phy clock source missing or invalid\n");
+       }
+
 
        /* Grab turnoff reset */
        imx6_pcie->turnoff_reset = devm_reset_control_get_optional_exclusive(dev, "turnoff");
@@ -1202,6 +1262,10 @@ static const struct imx6_pcie_drvdata drvdata[] = {
        [IMX8MQ] = {
                .variant = IMX8MQ,
        },
+       [IMX8MM] = {
+               .variant = IMX8MM,
+               .flags = IMX6_PCIE_FLAG_SUPPORTS_SUSPEND,
+       },
 };
 
 static const struct of_device_id imx6_pcie_of_match[] = {
@@ -1209,7 +1273,8 @@ static const struct of_device_id imx6_pcie_of_match[] = {
        { .compatible = "fsl,imx6sx-pcie", .data = &drvdata[IMX6SX], },
        { .compatible = "fsl,imx6qp-pcie", .data = &drvdata[IMX6QP], },
        { .compatible = "fsl,imx7d-pcie",  .data = &drvdata[IMX7D],  },
-       { .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], } ,
+       { .compatible = "fsl,imx8mq-pcie", .data = &drvdata[IMX8MQ], },
+       { .compatible = "fsl,imx8mm-pcie", .data = &drvdata[IMX8MM], },
        {},
 };
 
index 5b9c625..6a4f061 100644 (file)
@@ -3,6 +3,7 @@
  * PCIe host controller driver for Freescale Layerscape SoCs
  *
  * Copyright (C) 2014 Freescale Semiconductor.
+ * Copyright 2021 NXP
  *
  * Author: Minghuan Lian <Minghuan.Lian@freescale.com>
  */
 
 #include "pcie-designware.h"
 
-/* PEX1/2 Misc Ports Status Register */
-#define SCFG_PEXMSCPORTSR(pex_idx)     (0x94 + (pex_idx) * 4)
-#define LTSSM_STATE_SHIFT      20
-#define LTSSM_STATE_MASK       0x3f
-#define LTSSM_PCIE_L0          0x11 /* L0 state */
-
 /* PEX Internal Configuration Registers */
 #define PCIE_STRFMR1           0x71c /* Symbol Timer & Filter Mask Register1 */
 #define PCIE_ABSERR            0x8d0 /* Bridge Slave Error Response Register */
 
 #define PCIE_IATU_NUM          6
 
-struct ls_pcie_drvdata {
-       u32 lut_offset;
-       u32 ltssm_shift;
-       u32 lut_dbg;
-       const struct dw_pcie_host_ops *ops;
-       const struct dw_pcie_ops *dw_pcie_ops;
-};
-
 struct ls_pcie {
        struct dw_pcie *pci;
-       void __iomem *lut;
-       struct regmap *scfg;
-       const struct ls_pcie_drvdata *drvdata;
-       int index;
 };
 
 #define to_ls_pcie(x)  dev_get_drvdata((x)->dev)
@@ -83,38 +66,6 @@ static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie)
        iowrite32(val, pci->dbi_base + PCIE_STRFMR1);
 }
 
-static int ls1021_pcie_link_up(struct dw_pcie *pci)
-{
-       u32 state;
-       struct ls_pcie *pcie = to_ls_pcie(pci);
-
-       if (!pcie->scfg)
-               return 0;
-
-       regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state);
-       state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK;
-
-       if (state < LTSSM_PCIE_L0)
-               return 0;
-
-       return 1;
-}
-
-static int ls_pcie_link_up(struct dw_pcie *pci)
-{
-       struct ls_pcie *pcie = to_ls_pcie(pci);
-       u32 state;
-
-       state = (ioread32(pcie->lut + pcie->drvdata->lut_dbg) >>
-                pcie->drvdata->ltssm_shift) &
-                LTSSM_STATE_MASK;
-
-       if (state < LTSSM_PCIE_L0)
-               return 0;
-
-       return 1;
-}
-
 /* Forward error response of outbound non-posted requests */
 static void ls_pcie_fix_error_response(struct ls_pcie *pcie)
 {
@@ -139,96 +90,20 @@ static int ls_pcie_host_init(struct pcie_port *pp)
        return 0;
 }
 
-static int ls1021_pcie_host_init(struct pcie_port *pp)
-{
-       struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
-       struct ls_pcie *pcie = to_ls_pcie(pci);
-       struct device *dev = pci->dev;
-       u32 index[2];
-       int ret;
-
-       pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node,
-                                                    "fsl,pcie-scfg");
-       if (IS_ERR(pcie->scfg)) {
-               ret = PTR_ERR(pcie->scfg);
-               dev_err(dev, "No syscfg phandle specified\n");
-               pcie->scfg = NULL;
-               return ret;
-       }
-
-       if (of_property_read_u32_array(dev->of_node,
-                                      "fsl,pcie-scfg", index, 2)) {
-               pcie->scfg = NULL;
-               return -EINVAL;
-       }
-       pcie->index = index[1];
-
-       return ls_pcie_host_init(pp);
-}
-
-static const struct dw_pcie_host_ops ls1021_pcie_host_ops = {
-       .host_init = ls1021_pcie_host_init,
-};
-
 static const struct dw_pcie_host_ops ls_pcie_host_ops = {
        .host_init = ls_pcie_host_init,
 };
 
-static const struct dw_pcie_ops dw_ls1021_pcie_ops = {
-       .link_up = ls1021_pcie_link_up,
-};
-
-static const struct dw_pcie_ops dw_ls_pcie_ops = {
-       .link_up = ls_pcie_link_up,
-};
-
-static const struct ls_pcie_drvdata ls1021_drvdata = {
-       .ops = &ls1021_pcie_host_ops,
-       .dw_pcie_ops = &dw_ls1021_pcie_ops,
-};
-
-static const struct ls_pcie_drvdata ls1043_drvdata = {
-       .lut_offset = 0x10000,
-       .ltssm_shift = 24,
-       .lut_dbg = 0x7fc,
-       .ops = &ls_pcie_host_ops,
-       .dw_pcie_ops = &dw_ls_pcie_ops,
-};
-
-static const struct ls_pcie_drvdata ls1046_drvdata = {
-       .lut_offset = 0x80000,
-       .ltssm_shift = 24,
-       .lut_dbg = 0x407fc,
-       .ops = &ls_pcie_host_ops,
-       .dw_pcie_ops = &dw_ls_pcie_ops,
-};
-
-static const struct ls_pcie_drvdata ls2080_drvdata = {
-       .lut_offset = 0x80000,
-       .ltssm_shift = 0,
-       .lut_dbg = 0x7fc,
-       .ops = &ls_pcie_host_ops,
-       .dw_pcie_ops = &dw_ls_pcie_ops,
-};
-
-static const struct ls_pcie_drvdata ls2088_drvdata = {
-       .lut_offset = 0x80000,
-       .ltssm_shift = 0,
-       .lut_dbg = 0x407fc,
-       .ops = &ls_pcie_host_ops,
-       .dw_pcie_ops = &dw_ls_pcie_ops,
-};
-
 static const struct of_device_id ls_pcie_of_match[] = {
-       { .compatible = "fsl,ls1012a-pcie", .data = &ls1046_drvdata },
-       { .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata },
-       { .compatible = "fsl,ls1028a-pcie", .data = &ls2088_drvdata },
-       { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata },
-       { .compatible = "fsl,ls1046a-pcie", .data = &ls1046_drvdata },
-       { .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata },
-       { .compatible = "fsl,ls2085a-pcie", .data = &ls2080_drvdata },
-       { .compatible = "fsl,ls2088a-pcie", .data = &ls2088_drvdata },
-       { .compatible = "fsl,ls1088a-pcie", .data = &ls2088_drvdata },
+       { .compatible = "fsl,ls1012a-pcie", },
+       { .compatible = "fsl,ls1021a-pcie", },
+       { .compatible = "fsl,ls1028a-pcie", },
+       { .compatible = "fsl,ls1043a-pcie", },
+       { .compatible = "fsl,ls1046a-pcie", },
+       { .compatible = "fsl,ls2080a-pcie", },
+       { .compatible = "fsl,ls2085a-pcie", },
+       { .compatible = "fsl,ls2088a-pcie", },
+       { .compatible = "fsl,ls1088a-pcie", },
        { },
 };
 
@@ -247,11 +122,8 @@ static int ls_pcie_probe(struct platform_device *pdev)
        if (!pci)
                return -ENOMEM;
 
-       pcie->drvdata = of_device_get_match_data(dev);
-
        pci->dev = dev;
-       pci->ops = pcie->drvdata->dw_pcie_ops;
-       pci->pp.ops = pcie->drvdata->ops;
+       pci->pp.ops = &ls_pcie_host_ops;
 
        pcie->pci = pci;
 
@@ -260,8 +132,6 @@ static int ls_pcie_probe(struct platform_device *pdev)
        if (IS_ERR(pci->dbi_base))
                return PTR_ERR(pci->dbi_base);
 
-       pcie->lut = pci->dbi_base + pcie->drvdata->lut_offset;
-
        if (!ls_pcie_is_bridge(pcie))
                return -ENODEV;
 
index 850b453..d92c8a2 100644 (file)
@@ -672,10 +672,11 @@ void dw_pcie_iatu_detect(struct dw_pcie *pci)
                if (!pci->atu_base) {
                        struct resource *res =
                                platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu");
-                       if (res)
+                       if (res) {
                                pci->atu_size = resource_size(res);
-                       pci->atu_base = devm_ioremap_resource(dev, res);
-                       if (IS_ERR(pci->atu_base))
+                               pci->atu_base = devm_ioremap_resource(dev, res);
+                       }
+                       if (!pci->atu_base || IS_ERR(pci->atu_base))
                                pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET;
                }
 
index 7b17da2..cdabd51 100644 (file)
@@ -552,10 +552,8 @@ static int qcom_pcie_ep_enable_irq_resources(struct platform_device *pdev,
        int irq, ret;
 
        irq = platform_get_irq_byname(pdev, "global");
-       if (irq < 0) {
-               dev_err(&pdev->dev, "Failed to get Global IRQ\n");
+       if (irq < 0)
                return irq;
-       }
 
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        qcom_pcie_ep_global_irq_thread,
index c5300d4..b654d06 100644 (file)
 #define PCIE_MSI_ADDR_HIGH_REG                 (CONTROL_BASE_ADDR + 0x54)
 #define PCIE_MSI_STATUS_REG                    (CONTROL_BASE_ADDR + 0x58)
 #define PCIE_MSI_MASK_REG                      (CONTROL_BASE_ADDR + 0x5C)
+#define     PCIE_MSI_ALL_MASK                  GENMASK(31, 0)
 #define PCIE_MSI_PAYLOAD_REG                   (CONTROL_BASE_ADDR + 0x9C)
 #define     PCIE_MSI_DATA_MASK                 GENMASK(15, 0)
 
@@ -571,6 +572,7 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
        advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG);
 
        /* Clear all interrupts */
+       advk_writel(pcie, PCIE_MSI_ALL_MASK, PCIE_MSI_STATUS_REG);
        advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_REG);
        advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG);
        advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
@@ -583,7 +585,7 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
        advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG);
 
        /* Unmask all MSIs */
-       advk_writel(pcie, 0, PCIE_MSI_MASK_REG);
+       advk_writel(pcie, ~(u32)PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG);
 
        /* Enable summary interrupt for GIC SPI source */
        reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK);
@@ -884,8 +886,13 @@ advk_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
        case PCI_CAP_LIST_ID:
        case PCI_EXP_DEVCAP:
        case PCI_EXP_DEVCTL:
+       case PCI_EXP_DEVCAP2:
+       case PCI_EXP_DEVCTL2:
+       case PCI_EXP_LNKCAP2:
+       case PCI_EXP_LNKCTL2:
                *value = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg);
                return PCI_BRIDGE_EMUL_HANDLED;
+
        default:
                return PCI_BRIDGE_EMUL_NOT_HANDLED;
        }
@@ -899,10 +906,6 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
        struct advk_pcie *pcie = bridge->data;
 
        switch (reg) {
-       case PCI_EXP_DEVCTL:
-               advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg);
-               break;
-
        case PCI_EXP_LNKCTL:
                advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg);
                if (new & PCI_EXP_LNKCTL_RL)
@@ -924,6 +927,12 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
                advk_writel(pcie, new, PCIE_ISR0_REG);
                break;
 
+       case PCI_EXP_DEVCTL:
+       case PCI_EXP_DEVCTL2:
+       case PCI_EXP_LNKCTL2:
+               advk_writel(pcie, new, PCIE_CORE_PCIEXP_CAP + reg);
+               break;
+
        default:
                break;
        }
@@ -1392,7 +1401,7 @@ static void advk_pcie_handle_msi(struct advk_pcie *pcie)
 
        msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG);
        msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG);
-       msi_status = msi_val & ~msi_mask;
+       msi_status = msi_val & ((~msi_mask) & PCIE_MSI_ALL_MASK);
 
        for (msi_idx = 0; msi_idx < MSI_IRQ_NUM; msi_idx++) {
                if (!(BIT(msi_idx) & msi_status))
@@ -1544,8 +1553,7 @@ static int advk_pcie_probe(struct platform_device *pdev)
                 * only PIO for issuing configuration transfers which does
                 * not use PCIe window configuration.
                 */
-               if (type != IORESOURCE_MEM && type != IORESOURCE_MEM_64 &&
-                   type != IORESOURCE_IO)
+               if (type != IORESOURCE_MEM && type != IORESOURCE_IO)
                        continue;
 
                /*
@@ -1553,8 +1561,7 @@ static int advk_pcie_probe(struct platform_device *pdev)
                 * configuration is set to transparent memory access so it
                 * does not need window configuration.
                 */
-               if ((type == IORESOURCE_MEM || type == IORESOURCE_MEM_64) &&
-                   entry->offset == 0)
+               if (type == IORESOURCE_MEM && entry->offset == 0)
                        continue;
 
                /*
@@ -1686,20 +1693,64 @@ static int advk_pcie_remove(struct platform_device *pdev)
 {
        struct advk_pcie *pcie = platform_get_drvdata(pdev);
        struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
+       u32 val;
        int i;
 
+       /* Remove PCI bus with all devices */
        pci_lock_rescan_remove();
        pci_stop_root_bus(bridge->bus);
        pci_remove_root_bus(bridge->bus);
        pci_unlock_rescan_remove();
 
+       /* Disable Root Bridge I/O space, memory space and bus mastering */
+       val = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG);
+       val &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+       advk_writel(pcie, val, PCIE_CORE_CMD_STATUS_REG);
+
+       /* Disable MSI */
+       val = advk_readl(pcie, PCIE_CORE_CTRL2_REG);
+       val &= ~PCIE_CORE_CTRL2_MSI_ENABLE;
+       advk_writel(pcie, val, PCIE_CORE_CTRL2_REG);
+
+       /* Clear MSI address */
+       advk_writel(pcie, 0, PCIE_MSI_ADDR_LOW_REG);
+       advk_writel(pcie, 0, PCIE_MSI_ADDR_HIGH_REG);
+
+       /* Mask all interrupts */
+       advk_writel(pcie, PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG);
+       advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_MASK_REG);
+       advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG);
+       advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_MASK_REG);
+
+       /* Clear all interrupts */
+       advk_writel(pcie, PCIE_MSI_ALL_MASK, PCIE_MSI_STATUS_REG);
+       advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_REG);
+       advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG);
+       advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
+
+       /* Remove IRQ domains */
        advk_pcie_remove_msi_irq_domain(pcie);
        advk_pcie_remove_irq_domain(pcie);
 
+       /* Free config space for emulated root bridge */
+       pci_bridge_emul_cleanup(&pcie->bridge);
+
+       /* Assert PERST# signal which prepares PCIe card for power down */
+       if (pcie->reset_gpio)
+               gpiod_set_value_cansleep(pcie->reset_gpio, 1);
+
+       /* Disable link training */
+       val = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
+       val &= ~LINK_TRAINING_EN;
+       advk_writel(pcie, val, PCIE_CORE_CTRL0_REG);
+
        /* Disable outbound address windows mapping */
        for (i = 0; i < OB_WIN_COUNT; i++)
                advk_pcie_disable_ob_win(pcie, i);
 
+       /* Disable phy */
+       advk_pcie_disable_phy(pcie);
+
        return 0;
 }
 
index 1bf4d75..f985513 100644 (file)
@@ -42,8 +42,9 @@
 #define   CORE_FABRIC_STAT_MASK                0x001F001F
 #define CORE_LANE_CFG(port)            (0x84000 + 0x4000 * (port))
 #define   CORE_LANE_CFG_REFCLK0REQ     BIT(0)
-#define   CORE_LANE_CFG_REFCLK1                BIT(1)
+#define   CORE_LANE_CFG_REFCLK1REQ     BIT(1)
 #define   CORE_LANE_CFG_REFCLK0ACK     BIT(2)
+#define   CORE_LANE_CFG_REFCLK1ACK     BIT(3)
 #define   CORE_LANE_CFG_REFCLKEN       (BIT(9) | BIT(10))
 #define CORE_LANE_CTL(port)            (0x84004 + 0x4000 * (port))
 #define   CORE_LANE_CTL_CFGACC         BIT(15)
@@ -482,9 +483,9 @@ static int apple_pcie_setup_refclk(struct apple_pcie *pcie,
        if (res < 0)
                return res;
 
-       rmw_set(CORE_LANE_CFG_REFCLK1, pcie->base + CORE_LANE_CFG(port->idx));
+       rmw_set(CORE_LANE_CFG_REFCLK1REQ, pcie->base + CORE_LANE_CFG(port->idx));
        res = readl_relaxed_poll_timeout(pcie->base + CORE_LANE_CFG(port->idx),
-                                        stat, stat & CORE_LANE_CFG_REFCLK1,
+                                        stat, stat & CORE_LANE_CFG_REFCLK1ACK,
                                         100, 50000);
 
        if (res < 0)
@@ -553,6 +554,9 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
                return ret;
        }
 
+       rmw_clear(PORT_REFCLK_CGDIS, port->base + PORT_REFCLK);
+       rmw_clear(PORT_APPCLK_CGDIS, port->base + PORT_APPCLK);
+
        ret = apple_pcie_port_setup_irq(port);
        if (ret)
                return ret;
index 1fc7bd4..1cba6e6 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/pci.h>
 #include <linux/pci-ecam.h>
 #include <linux/printk.h>
+#include <linux/regulator/consumer.h>
 #include <linux/reset.h>
 #include <linux/sizes.h>
 #include <linux/slab.h>
 #define BRCM_INT_PCI_MSI_NR            32
 #define BRCM_INT_PCI_MSI_LEGACY_NR     8
 #define BRCM_INT_PCI_MSI_SHIFT         0
+#define BRCM_INT_PCI_MSI_MASK          GENMASK(BRCM_INT_PCI_MSI_NR - 1, 0)
+#define BRCM_INT_PCI_MSI_LEGACY_MASK   GENMASK(31, \
+                                               32 - BRCM_INT_PCI_MSI_LEGACY_NR)
 
 /* MSI target addresses */
 #define BRCM_MSI_TARGET_ADDR_LT_4GB    0x0fffffffcULL
@@ -191,6 +195,8 @@ static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie,
 static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val);
 static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val);
 static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val);
+static int brcm_pcie_linkup(struct brcm_pcie *pcie);
+static int brcm_pcie_add_bus(struct pci_bus *bus);
 
 enum {
        RGR1_SW_INIT_1,
@@ -257,6 +263,14 @@ static const struct pcie_cfg_data bcm2711_cfg = {
        .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
 };
 
+struct subdev_regulators {
+       unsigned int num_supplies;
+       struct regulator_bulk_data supplies[];
+};
+
+static int pci_subdev_regulators_add_bus(struct pci_bus *bus);
+static void pci_subdev_regulators_remove_bus(struct pci_bus *bus);
+
 struct brcm_msi {
        struct device           *dev;
        void __iomem            *base;
@@ -266,8 +280,7 @@ struct brcm_msi {
        struct mutex            lock; /* guards the alloc/free operations */
        u64                     target_addr;
        int                     irq;
-       /* used indicates which MSI interrupts have been alloc'd */
-       unsigned long           used;
+       DECLARE_BITMAP(used, BRCM_INT_PCI_MSI_NR);
        bool                    legacy;
        /* Some chips have MSIs in bits [31..24] of a shared register. */
        int                     legacy_shift;
@@ -295,6 +308,9 @@ struct brcm_pcie {
        u32                     hw_rev;
        void                    (*perst_set)(struct brcm_pcie *pcie, u32 val);
        void                    (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
+       bool                    refusal_mode;
+       struct subdev_regulators *sr;
+       bool                    ep_wakeup_capable;
 };
 
 /*
@@ -406,6 +422,99 @@ static int brcm_pcie_set_ssc(struct brcm_pcie *pcie)
        return ssc && pll ? 0 : -EIO;
 }
 
+static void *alloc_subdev_regulators(struct device *dev)
+{
+       static const char * const supplies[] = {
+               "vpcie3v3",
+               "vpcie3v3aux",
+               "vpcie12v",
+       };
+       const size_t size = sizeof(struct subdev_regulators)
+               + sizeof(struct regulator_bulk_data) * ARRAY_SIZE(supplies);
+       struct subdev_regulators *sr;
+       int i;
+
+       sr = devm_kzalloc(dev, size, GFP_KERNEL);
+       if (sr) {
+               sr->num_supplies = ARRAY_SIZE(supplies);
+               for (i = 0; i < ARRAY_SIZE(supplies); i++)
+                       sr->supplies[i].supply = supplies[i];
+       }
+
+       return sr;
+}
+
+static int pci_subdev_regulators_add_bus(struct pci_bus *bus)
+{
+       struct device *dev = &bus->dev;
+       struct subdev_regulators *sr;
+       int ret;
+
+       if (!dev->of_node || !bus->parent || !pci_is_root_bus(bus->parent))
+               return 0;
+
+       if (dev->driver_data)
+               dev_err(dev, "dev.driver_data unexpectedly non-NULL\n");
+
+       sr = alloc_subdev_regulators(dev);
+       if (!sr)
+               return -ENOMEM;
+
+       dev->driver_data = sr;
+       ret = regulator_bulk_get(dev, sr->num_supplies, sr->supplies);
+       if (ret)
+               return ret;
+
+       ret = regulator_bulk_enable(sr->num_supplies, sr->supplies);
+       if (ret) {
+               dev_err(dev, "failed to enable regulators for downstream device\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int brcm_pcie_add_bus(struct pci_bus *bus)
+{
+       struct device *dev = &bus->dev;
+       struct brcm_pcie *pcie = (struct brcm_pcie *) bus->sysdata;
+       int ret;
+
+       if (!dev->of_node || !bus->parent || !pci_is_root_bus(bus->parent))
+               return 0;
+
+       ret = pci_subdev_regulators_add_bus(bus);
+       if (ret)
+               return ret;
+
+       /* Grab the regulators for suspend/resume */
+       pcie->sr = bus->dev.driver_data;
+
+       /*
+        * If we have failed linkup there is no point to return an error as
+        * currently it will cause a WARNING() from pci_alloc_child_bus().
+        * We return 0 and turn on the "refusal_mode" so that any further
+        * accesses to the pci_dev just get 0xffffffff
+        */
+       if (brcm_pcie_linkup(pcie) != 0)
+               pcie->refusal_mode = true;
+
+       return 0;
+}
+
+static void pci_subdev_regulators_remove_bus(struct pci_bus *bus)
+{
+       struct device *dev = &bus->dev;
+       struct subdev_regulators *sr = dev->driver_data;
+
+       if (!sr || !bus->parent || !pci_is_root_bus(bus->parent))
+               return;
+
+       if (regulator_bulk_disable(sr->num_supplies, sr->supplies))
+               dev_err(dev, "failed to disable regulators for downstream device\n");
+       dev->driver_data = NULL;
+}
+
 /* Limits operation to a specific generation (1, 2, or 3) */
 static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen)
 {
@@ -534,7 +643,7 @@ static int brcm_msi_alloc(struct brcm_msi *msi)
        int hwirq;
 
        mutex_lock(&msi->lock);
-       hwirq = bitmap_find_free_region(&msi->used, msi->nr, 0);
+       hwirq = bitmap_find_free_region(msi->used, msi->nr, 0);
        mutex_unlock(&msi->lock);
 
        return hwirq;
@@ -543,7 +652,7 @@ static int brcm_msi_alloc(struct brcm_msi *msi)
 static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq)
 {
        mutex_lock(&msi->lock);
-       bitmap_release_region(&msi->used, hwirq, 0);
+       bitmap_release_region(msi->used, hwirq, 0);
        mutex_unlock(&msi->lock);
 }
 
@@ -619,7 +728,8 @@ static void brcm_msi_remove(struct brcm_pcie *pcie)
 
 static void brcm_msi_set_regs(struct brcm_msi *msi)
 {
-       u32 val = __GENMASK(31, msi->legacy_shift);
+       u32 val = msi->legacy ? BRCM_INT_PCI_MSI_LEGACY_MASK :
+                               BRCM_INT_PCI_MSI_MASK;
 
        writel(val, msi->intr_base + MSI_INT_MASK_CLR);
        writel(val, msi->intr_base + MSI_INT_CLR);
@@ -661,6 +771,12 @@ static int brcm_pcie_enable_msi(struct brcm_pcie *pcie)
        msi->irq = irq;
        msi->legacy = pcie->hw_rev < BRCM_PCIE_HW_REV_33;
 
+       /*
+        * Sanity check to make sure that the 'used' bitmap in struct brcm_msi
+        * is large enough.
+        */
+       BUILD_BUG_ON(BRCM_INT_PCI_MSI_LEGACY_NR > BRCM_INT_PCI_MSI_NR);
+
        if (msi->legacy) {
                msi->intr_base = msi->base + PCIE_INTR2_CPU_BASE;
                msi->nr = BRCM_INT_PCI_MSI_LEGACY_NR;
@@ -711,6 +827,18 @@ static void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn,
        /* Accesses to the RC go right to the RC registers if slot==0 */
        if (pci_is_root_bus(bus))
                return PCI_SLOT(devfn) ? NULL : base + where;
+       if (pcie->refusal_mode) {
+               /*
+                * At this point we do not have link.  There will be a CPU
+                * abort -- a quirk with this controller --if Linux tries
+                * to read any config-space registers besides those
+                * targeting the host bridge.  To prevent this we hijack
+                * the address to point to a safe access that will return
+                * 0xffffffff.
+                */
+               writel(0xffffffff, base + PCIE_MISC_RC_BAR2_CONFIG_HI);
+               return base + PCIE_MISC_RC_BAR2_CONFIG_HI + (where & 0x3);
+       }
 
        /* For devices, write to the config space index register */
        idx = PCIE_ECAM_OFFSET(bus->number, devfn, 0);
@@ -722,6 +850,8 @@ static struct pci_ops brcm_pcie_ops = {
        .map_bus = brcm_pcie_map_conf,
        .read = pci_generic_config_read,
        .write = pci_generic_config_write,
+       .add_bus = brcm_pcie_add_bus,
+       .remove_bus = pci_subdev_regulators_remove_bus,
 };
 
 static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val)
@@ -863,16 +993,9 @@ static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie,
 
 static int brcm_pcie_setup(struct brcm_pcie *pcie)
 {
-       struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
        u64 rc_bar2_offset, rc_bar2_size;
        void __iomem *base = pcie->base;
-       struct device *dev = pcie->dev;
-       struct resource_entry *entry;
-       bool ssc_good = false;
-       struct resource *res;
-       int num_out_wins = 0;
-       u16 nlw, cls, lnksta;
-       int i, ret, memc;
+       int ret, memc;
        u32 tmp, burst, aspm_support;
 
        /* Reset the bridge */
@@ -957,6 +1080,40 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
        if (pcie->gen)
                brcm_pcie_set_gen(pcie, pcie->gen);
 
+       /* Don't advertise L0s capability if 'aspm-no-l0s' */
+       aspm_support = PCIE_LINK_STATE_L1;
+       if (!of_property_read_bool(pcie->np, "aspm-no-l0s"))
+               aspm_support |= PCIE_LINK_STATE_L0S;
+       tmp = readl(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
+       u32p_replace_bits(&tmp, aspm_support,
+               PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK);
+       writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
+
+       /*
+        * For config space accesses on the RC, show the right class for
+        * a PCIe-PCIe bridge (the default setting is to be EP mode).
+        */
+       tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3);
+       u32p_replace_bits(&tmp, 0x060400,
+                         PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK);
+       writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3);
+
+       return 0;
+}
+
+static int brcm_pcie_linkup(struct brcm_pcie *pcie)
+{
+       struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
+       struct device *dev = pcie->dev;
+       void __iomem *base = pcie->base;
+       struct resource_entry *entry;
+       struct resource *res;
+       int num_out_wins = 0;
+       u16 nlw, cls, lnksta;
+       bool ssc_good = false;
+       u32 tmp;
+       int ret, i;
+
        /* Unassert the fundamental reset */
        pcie->perst_set(pcie, 0);
 
@@ -994,24 +1151,6 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
                num_out_wins++;
        }
 
-       /* Don't advertise L0s capability if 'aspm-no-l0s' */
-       aspm_support = PCIE_LINK_STATE_L1;
-       if (!of_property_read_bool(pcie->np, "aspm-no-l0s"))
-               aspm_support |= PCIE_LINK_STATE_L0S;
-       tmp = readl(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
-       u32p_replace_bits(&tmp, aspm_support,
-               PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK);
-       writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
-
-       /*
-        * For config space accesses on the RC, show the right class for
-        * a PCIe-PCIe bridge (the default setting is to be EP mode).
-        */
-       tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3);
-       u32p_replace_bits(&tmp, 0x060400,
-                         PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK);
-       writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3);
-
        if (pcie->ssc) {
                ret = brcm_pcie_set_ssc(pcie);
                if (ret == 0)
@@ -1140,17 +1279,60 @@ static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
        pcie->bridge_sw_init_set(pcie, 1);
 }
 
+static int pci_dev_may_wakeup(struct pci_dev *dev, void *data)
+{
+       bool *ret = data;
+
+       if (device_may_wakeup(&dev->dev)) {
+               *ret = true;
+               dev_info(&dev->dev, "disable cancelled for wake-up device\n");
+       }
+       return (int) *ret;
+}
+
 static int brcm_pcie_suspend(struct device *dev)
 {
        struct brcm_pcie *pcie = dev_get_drvdata(dev);
+       struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
        int ret;
 
        brcm_pcie_turn_off(pcie);
-       ret = brcm_phy_stop(pcie);
-       reset_control_rearm(pcie->rescal);
+       /*
+        * If brcm_phy_stop() returns an error, just dev_err(). If we
+        * return the error it will cause the suspend to fail and this is a
+        * forgivable offense that will probably be erased on resume.
+        */
+       if (brcm_phy_stop(pcie))
+               dev_err(dev, "Could not stop phy for suspend\n");
+
+       ret = reset_control_rearm(pcie->rescal);
+       if (ret) {
+               dev_err(dev, "Could not rearm rescal reset\n");
+               return ret;
+       }
+
+       if (pcie->sr) {
+               /*
+                * Now turn off the regulators, but if at least one
+                * downstream device is enabled as a wake-up source, do not
+                * turn off regulators.
+                */
+               pcie->ep_wakeup_capable = false;
+               pci_walk_bus(bridge->bus, pci_dev_may_wakeup,
+                            &pcie->ep_wakeup_capable);
+               if (!pcie->ep_wakeup_capable) {
+                       ret = regulator_bulk_disable(pcie->sr->num_supplies,
+                                                    pcie->sr->supplies);
+                       if (ret) {
+                               dev_err(dev, "Could not turn off regulators\n");
+                               reset_control_reset(pcie->rescal);
+                               return ret;
+                       }
+               }
+       }
        clk_disable_unprepare(pcie->clk);
 
-       return ret;
+       return 0;
 }
 
 static int brcm_pcie_resume(struct device *dev)
@@ -1161,11 +1343,32 @@ static int brcm_pcie_resume(struct device *dev)
        int ret;
 
        base = pcie->base;
-       clk_prepare_enable(pcie->clk);
+       ret = clk_prepare_enable(pcie->clk);
+       if (ret)
+               return ret;
+
+       if (pcie->sr) {
+               if (pcie->ep_wakeup_capable) {
+                       /*
+                        * We are resuming from a suspend.  In the suspend we
+                        * did not disable the power supplies, so there is
+                        * no need to enable them (and falsely increase their
+                        * usage count).
+                        */
+                       pcie->ep_wakeup_capable = false;
+               } else {
+                       ret = regulator_bulk_enable(pcie->sr->num_supplies,
+                                                   pcie->sr->supplies);
+                       if (ret) {
+                               dev_err(dev, "Could not turn on regulators\n");
+                               goto err_disable_clk;
+                       }
+               }
+       }
 
        ret = reset_control_reset(pcie->rescal);
        if (ret)
-               goto err_disable_clk;
+               goto err_regulator;
 
        ret = brcm_phy_start(pcie);
        if (ret)
@@ -1186,6 +1389,10 @@ static int brcm_pcie_resume(struct device *dev)
        if (ret)
                goto err_reset;
 
+       ret = brcm_pcie_linkup(pcie);
+       if (ret)
+               goto err_reset;
+
        if (pcie->msi)
                brcm_msi_set_regs(pcie->msi);
 
@@ -1193,6 +1400,9 @@ static int brcm_pcie_resume(struct device *dev)
 
 err_reset:
        reset_control_rearm(pcie->rescal);
+err_regulator:
+       if (pcie->sr)
+               regulator_bulk_disable(pcie->sr->num_supplies, pcie->sr->supplies);
 err_disable_clk:
        clk_disable_unprepare(pcie->clk);
        return ret;
@@ -1202,8 +1412,10 @@ static void __brcm_pcie_remove(struct brcm_pcie *pcie)
 {
        brcm_msi_remove(pcie);
        brcm_pcie_turn_off(pcie);
-       brcm_phy_stop(pcie);
-       reset_control_rearm(pcie->rescal);
+       if (brcm_phy_stop(pcie))
+               dev_err(pcie->dev, "Could not stop phy\n");
+       if (reset_control_rearm(pcie->rescal))
+               dev_err(pcie->dev, "Could not rearm rescal reset\n");
        clk_disable_unprepare(pcie->clk);
 }
 
@@ -1320,7 +1532,17 @@ static int brcm_pcie_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, pcie);
 
-       return pci_host_probe(bridge);
+       ret = pci_host_probe(bridge);
+       if (!ret && !brcm_pcie_link_up(pcie))
+               ret = -ENODEV;
+
+       if (ret) {
+               brcm_pcie_remove(pdev);
+               return ret;
+       }
+
+       return 0;
+
 fail:
        __brcm_pcie_remove(pcie);
        return ret;
@@ -1329,8 +1551,8 @@ fail:
 MODULE_DEVICE_TABLE(of, brcm_pcie_match);
 
 static const struct dev_pm_ops brcm_pcie_pm_ops = {
-       .suspend = brcm_pcie_suspend,
-       .resume = brcm_pcie_resume,
+       .suspend_noirq = brcm_pcie_suspend,
+       .resume_noirq = brcm_pcie_resume,
 };
 
 static struct platform_driver brcm_pcie_driver = {
index 3862155..3bc9273 100644 (file)
@@ -334,7 +334,7 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, u8 interrupts)
        u8 encode_int;
 
        if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
-           interrupts > 32)
+           interrupts < 1 || interrupts > 32)
                return -EINVAL;
 
        if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
index cc6194a..88f217c 100644 (file)
@@ -30,11 +30,6 @@ ibmphp:
   or ibmphp should store a pointer to its bus in struct slot.  Probably the
   former.
 
-* The functions get_max_adapter_speed() and get_bus_name() are commented out.
-  Can they be deleted?  There are also forward declarations at the top of
-  ibmphp_core.c as well as pointers in ibmphp_hotplug_slot_ops, likewise
-  commented out.
-
 * ibmphp_init_devno() takes a struct slot **, it could instead take a
   struct slot *.
 
index 1712425..197997e 100644 (file)
@@ -50,14 +50,6 @@ static int irqs[16];    /* PIC mode IRQs we're using so far (in case MPS
 
 static int init_flag;
 
-/*
-static int get_max_adapter_speed_1 (struct hotplug_slot *, u8 *, u8);
-
-static inline int get_max_adapter_speed (struct hotplug_slot *hs, u8 *value)
-{
-       return get_max_adapter_speed_1 (hs, value, 1);
-}
-*/
 static inline int get_cur_bus_info(struct slot **sl)
 {
        int rc = 1;
@@ -401,69 +393,6 @@ static int get_max_bus_speed(struct slot *slot)
        return rc;
 }
 
-/*
-static int get_max_adapter_speed_1(struct hotplug_slot *hotplug_slot, u8 *value, u8 flag)
-{
-       int rc = -ENODEV;
-       struct slot *pslot;
-       struct slot myslot;
-
-       debug("get_max_adapter_speed_1 - Entry hotplug_slot[%lx] pvalue[%lx]\n",
-                                               (ulong)hotplug_slot, (ulong) value);
-
-       if (flag)
-               ibmphp_lock_operations();
-
-       if (hotplug_slot && value) {
-               pslot = hotplug_slot->private;
-               if (pslot) {
-                       memcpy(&myslot, pslot, sizeof(struct slot));
-                       rc = ibmphp_hpc_readslot(pslot, READ_SLOTSTATUS,
-                                               &(myslot.status));
-
-                       if (!(SLOT_LATCH (myslot.status)) &&
-                                       (SLOT_PRESENT (myslot.status))) {
-                               rc = ibmphp_hpc_readslot(pslot,
-                                               READ_EXTSLOTSTATUS,
-                                               &(myslot.ext_status));
-                               if (!rc)
-                                       *value = SLOT_SPEED(myslot.ext_status);
-                       } else
-                               *value = MAX_ADAPTER_NONE;
-               }
-       }
-
-       if (flag)
-               ibmphp_unlock_operations();
-
-       debug("get_max_adapter_speed_1 - Exit rc[%d] value[%x]\n", rc, *value);
-       return rc;
-}
-
-static int get_bus_name(struct hotplug_slot *hotplug_slot, char *value)
-{
-       int rc = -ENODEV;
-       struct slot *pslot = NULL;
-
-       debug("get_bus_name - Entry hotplug_slot[%lx]\n", (ulong)hotplug_slot);
-
-       ibmphp_lock_operations();
-
-       if (hotplug_slot) {
-               pslot = hotplug_slot->private;
-               if (pslot) {
-                       rc = 0;
-                       snprintf(value, 100, "Bus %x", pslot->bus);
-               }
-       } else
-               rc = -ENODEV;
-
-       ibmphp_unlock_operations();
-       debug("get_bus_name - Exit rc[%d] value[%x]\n", rc, *value);
-       return rc;
-}
-*/
-
 /****************************************************************************
  * This routine will initialize the ops data structure used in the validate
  * function. It will also power off empty slots that are powered on since BIOS
@@ -1231,9 +1160,6 @@ const struct hotplug_slot_ops ibmphp_hotplug_slot_ops = {
        .get_attention_status =         get_attention_status,
        .get_latch_status =             get_latch_status,
        .get_adapter_status =           get_adapter_present,
-/*     .get_max_adapter_speed =        get_max_adapter_speed,
-       .get_bus_name_status =          get_bus_name,
-*/
 };
 
 static void ibmphp_unload(void)
index 918dccb..e0a614a 100644 (file)
@@ -75,6 +75,8 @@ extern int pciehp_poll_time;
  * @reset_lock: prevents access to the Data Link Layer Link Active bit in the
  *     Link Status register and to the Presence Detect State bit in the Slot
  *     Status register during a slot reset which may cause them to flap
+ * @depth: Number of additional hotplug ports in the path to the root bus,
+ *     used as lock subclass for @reset_lock
  * @ist_running: flag to keep user request waiting while IRQ thread is running
  * @request_result: result of last user request submitted to the IRQ thread
  * @requester: wait queue to wake up on completion of user request,
@@ -106,6 +108,7 @@ struct controller {
 
        struct hotplug_slot hotplug_slot;       /* hotplug core interface */
        struct rw_semaphore reset_lock;
+       unsigned int depth;
        unsigned int ist_running;
        int request_result;
        wait_queue_head_t requester;
index f34114d..4042d87 100644 (file)
@@ -166,7 +166,7 @@ static void pciehp_check_presence(struct controller *ctrl)
 {
        int occupied;
 
-       down_read(&ctrl->reset_lock);
+       down_read_nested(&ctrl->reset_lock, ctrl->depth);
        mutex_lock(&ctrl->state_lock);
 
        occupied = pciehp_card_present_or_link_active(ctrl);
index 83a0fa1..1d3108e 100644 (file)
@@ -583,7 +583,7 @@ static void pciehp_ignore_dpc_link_change(struct controller *ctrl,
         * the corresponding link change may have been ignored above.
         * Synthesize it to ensure that it is acted on.
         */
-       down_read(&ctrl->reset_lock);
+       down_read_nested(&ctrl->reset_lock, ctrl->depth);
        if (!pciehp_check_link_active(ctrl))
                pciehp_request(ctrl, PCI_EXP_SLTSTA_DLLSC);
        up_read(&ctrl->reset_lock);
@@ -642,6 +642,8 @@ read_status:
         */
        if (ctrl->power_fault_detected)
                status &= ~PCI_EXP_SLTSTA_PFD;
+       else if (status & PCI_EXP_SLTSTA_PFD)
+               ctrl->power_fault_detected = true;
 
        events |= status;
        if (!events) {
@@ -651,7 +653,7 @@ read_status:
        }
 
        if (status) {
-               pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, events);
+               pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, status);
 
                /*
                 * In MSI mode, all event bits must be zero before the port
@@ -725,8 +727,7 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
        }
 
        /* Check Power Fault Detected */
-       if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
-               ctrl->power_fault_detected = 1;
+       if (events & PCI_EXP_SLTSTA_PFD) {
                ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
                pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
                                      PCI_EXP_SLTCTL_ATTN_IND_ON);
@@ -746,7 +747,7 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
         * Disable requests have higher priority than Presence Detect Changed
         * or Data Link Layer State Changed events.
         */
-       down_read(&ctrl->reset_lock);
+       down_read_nested(&ctrl->reset_lock, ctrl->depth);
        if (events & DISABLE_SLOT)
                pciehp_handle_disable_request(ctrl);
        else if (events & (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC))
@@ -906,7 +907,7 @@ int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, bool probe)
        if (probe)
                return 0;
 
-       down_write(&ctrl->reset_lock);
+       down_write_nested(&ctrl->reset_lock, ctrl->depth);
 
        if (!ATTN_BUTTN(ctrl)) {
                ctrl_mask |= PCI_EXP_SLTCTL_PDCE;
@@ -962,6 +963,20 @@ static inline void dbg_ctrl(struct controller *ctrl)
 
 #define FLAG(x, y)     (((x) & (y)) ? '+' : '-')
 
+static inline int pcie_hotplug_depth(struct pci_dev *dev)
+{
+       struct pci_bus *bus = dev->bus;
+       int depth = 0;
+
+       while (bus->parent) {
+               bus = bus->parent;
+               if (bus->self && bus->self->is_hotplug_bridge)
+                       depth++;
+       }
+
+       return depth;
+}
+
 struct controller *pcie_init(struct pcie_device *dev)
 {
        struct controller *ctrl;
@@ -975,6 +990,7 @@ struct controller *pcie_init(struct pcie_device *dev)
                return NULL;
 
        ctrl->pcie = dev;
+       ctrl->depth = pcie_hotplug_depth(dev->port);
        pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
 
        if (pdev->hotplug_user_indicators)
index 8d47cb7..081c391 100644 (file)
@@ -710,7 +710,7 @@ void *pci_alloc_p2pmem(struct pci_dev *pdev, size_t size)
        if (!ret)
                goto out;
 
-       if (unlikely(!percpu_ref_tryget_live(ref))) {
+       if (unlikely(!percpu_ref_tryget_live_rcu(ref))) {
                gen_pool_free(p2pdma->pool, (unsigned long) ret, size);
                ret = NULL;
                goto out;
index db97cdd..0d1177e 100644 (file)
@@ -251,6 +251,49 @@ struct pci_bridge_reg_behavior pcie_cap_regs_behavior[PCI_CAP_PCIE_SIZEOF / 4] =
                .ro = GENMASK(15, 0) | PCI_EXP_RTSTA_PENDING,
                .w1c = PCI_EXP_RTSTA_PME,
        },
+
+       [PCI_EXP_DEVCAP2 / 4] = {
+               /*
+                * Device capabilities 2 register has reserved bits [30:27].
+                * Also bits [26:24] are reserved for non-upstream ports.
+                */
+               .ro = BIT(31) | GENMASK(23, 0),
+       },
+
+       [PCI_EXP_DEVCTL2 / 4] = {
+               /*
+                * Device control 2 register is RW. Bit 11 is reserved for
+                * non-upstream ports.
+                *
+                * Device status 2 register is reserved.
+                */
+               .rw = GENMASK(15, 12) | GENMASK(10, 0),
+       },
+
+       [PCI_EXP_LNKCAP2 / 4] = {
+               /* Link capabilities 2 register has reserved bits [30:25] and 0. */
+               .ro = BIT(31) | GENMASK(24, 1),
+       },
+
+       [PCI_EXP_LNKCTL2 / 4] = {
+               /*
+                * Link control 2 register is RW.
+                *
+                * Link status 2 register has bits 5, 15 W1C;
+                * bits 10, 11 reserved and others are RO.
+                */
+               .rw = GENMASK(15, 0),
+               .w1c = (BIT(15) | BIT(5)) << 16,
+               .ro = (GENMASK(14, 12) | GENMASK(9, 6) | GENMASK(4, 0)) << 16,
+       },
+
+       [PCI_EXP_SLTCAP2 / 4] = {
+               /* Slot capabilities 2 register is reserved. */
+       },
+
+       [PCI_EXP_SLTCTL2 / 4] = {
+               /* Both Slot control 2 and Slot status 2 registers are reserved. */
+       },
 };
 
 /*
@@ -265,7 +308,11 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
 {
        BUILD_BUG_ON(sizeof(bridge->conf) != PCI_BRIDGE_CONF_END);
 
-       bridge->conf.class_revision |= cpu_to_le32(PCI_CLASS_BRIDGE_PCI << 16);
+       /*
+        * class_revision: Class is high 24 bits and revision is low 8 bit of this member,
+        * while class for PCI Bridge Normal Decode has the 24-bit value: PCI_CLASS_BRIDGE_PCI << 8
+        */
+       bridge->conf.class_revision |= cpu_to_le32((PCI_CLASS_BRIDGE_PCI << 8) << 8);
        bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE;
        bridge->conf.cache_line_size = 0x10;
        bridge->conf.status = cpu_to_le16(PCI_STATUS_CAP_LIST);
index 3d2fb39..287fa40 100644 (file)
@@ -1556,7 +1556,7 @@ static void pci_save_ltr_state(struct pci_dev *dev)
 {
        int ltr;
        struct pci_cap_saved_state *save_state;
-       u16 *cap;
+       u32 *cap;
 
        if (!pci_is_pcie(dev))
                return;
@@ -1571,25 +1571,25 @@ static void pci_save_ltr_state(struct pci_dev *dev)
                return;
        }
 
-       cap = (u16 *)&save_state->cap.data[0];
-       pci_read_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, cap++);
-       pci_read_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT, cap++);
+       /* Some broken devices only support dword access to LTR */
+       cap = &save_state->cap.data[0];
+       pci_read_config_dword(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, cap);
 }
 
 static void pci_restore_ltr_state(struct pci_dev *dev)
 {
        struct pci_cap_saved_state *save_state;
        int ltr;
-       u16 *cap;
+       u32 *cap;
 
        save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_LTR);
        ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR);
        if (!save_state || !ltr)
                return;
 
-       cap = (u16 *)&save_state->cap.data[0];
-       pci_write_config_word(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, *cap++);
-       pci_write_config_word(dev, ltr + PCI_LTR_MAX_NOSNOOP_LAT, *cap++);
+       /* Some broken devices only support dword access to LTR */
+       cap = &save_state->cap.data[0];
+       pci_write_config_dword(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, *cap);
 }
 
 /**
index 52c7468..a96b742 100644 (file)
 #define ASPM_STATE_ALL         (ASPM_STATE_L0S | ASPM_STATE_L1 |       \
                                 ASPM_STATE_L1SS)
 
-struct aspm_latency {
-       u32 l0s;                        /* L0s latency (nsec) */
-       u32 l1;                         /* L1 latency (nsec) */
-};
-
 struct pcie_link_state {
        struct pci_dev *pdev;           /* Upstream component of the Link */
        struct pci_dev *downstream;     /* Downstream component, function 0 */
@@ -65,15 +60,6 @@ struct pcie_link_state {
        u32 clkpm_enabled:1;            /* Current Clock PM state */
        u32 clkpm_default:1;            /* Default Clock PM state by BIOS */
        u32 clkpm_disable:1;            /* Clock PM disabled */
-
-       /* Exit latencies */
-       struct aspm_latency latency_up; /* Upstream direction exit latency */
-       struct aspm_latency latency_dw; /* Downstream direction exit latency */
-       /*
-        * Endpoint acceptable latencies. A pcie downstream port only
-        * has one slot under it, so at most there are 8 functions.
-        */
-       struct aspm_latency acceptable[8];
 };
 
 static int aspm_disabled, aspm_force;
@@ -105,6 +91,20 @@ static const char *policy_str[] = {
 
 #define LINK_RETRAIN_TIMEOUT HZ
 
+/*
+ * The L1 PM substate capability is only implemented in function 0 in a
+ * multi function device.
+ */
+static struct pci_dev *pci_function_0(struct pci_bus *linkbus)
+{
+       struct pci_dev *child;
+
+       list_for_each_entry(child, &linkbus->devices, bus_list)
+               if (PCI_FUNC(child->devfn) == 0)
+                       return child;
+       return NULL;
+}
+
 static int policy_to_aspm_state(struct pcie_link_state *link)
 {
        switch (aspm_policy) {
@@ -378,8 +378,10 @@ static void encode_l12_threshold(u32 threshold_us, u32 *scale, u32 *value)
 
 static void pcie_aspm_check_latency(struct pci_dev *endpoint)
 {
-       u32 latency, l1_switch_latency = 0;
-       struct aspm_latency *acceptable;
+       u32 latency, encoding, lnkcap_up, lnkcap_dw;
+       u32 l1_switch_latency = 0, latency_up_l0s;
+       u32 latency_up_l1, latency_dw_l0s, latency_dw_l1;
+       u32 acceptable_l0s, acceptable_l1;
        struct pcie_link_state *link;
 
        /* Device not in D0 doesn't need latency check */
@@ -388,17 +390,36 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
                return;
 
        link = endpoint->bus->self->link_state;
-       acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)];
+
+       /* Calculate endpoint L0s acceptable latency */
+       encoding = (endpoint->devcap & PCI_EXP_DEVCAP_L0S) >> 6;
+       acceptable_l0s = calc_l0s_acceptable(encoding);
+
+       /* Calculate endpoint L1 acceptable latency */
+       encoding = (endpoint->devcap & PCI_EXP_DEVCAP_L1) >> 9;
+       acceptable_l1 = calc_l1_acceptable(encoding);
 
        while (link) {
+               struct pci_dev *dev = pci_function_0(link->pdev->subordinate);
+
+               /* Read direction exit latencies */
+               pcie_capability_read_dword(link->pdev, PCI_EXP_LNKCAP,
+                                          &lnkcap_up);
+               pcie_capability_read_dword(dev, PCI_EXP_LNKCAP,
+                                          &lnkcap_dw);
+               latency_up_l0s = calc_l0s_latency(lnkcap_up);
+               latency_up_l1 = calc_l1_latency(lnkcap_up);
+               latency_dw_l0s = calc_l0s_latency(lnkcap_dw);
+               latency_dw_l1 = calc_l1_latency(lnkcap_dw);
+
                /* Check upstream direction L0s latency */
                if ((link->aspm_capable & ASPM_STATE_L0S_UP) &&
-                   (link->latency_up.l0s > acceptable->l0s))
+                   (latency_up_l0s > acceptable_l0s))
                        link->aspm_capable &= ~ASPM_STATE_L0S_UP;
 
                /* Check downstream direction L0s latency */
                if ((link->aspm_capable & ASPM_STATE_L0S_DW) &&
-                   (link->latency_dw.l0s > acceptable->l0s))
+                   (latency_dw_l0s > acceptable_l0s))
                        link->aspm_capable &= ~ASPM_STATE_L0S_DW;
                /*
                 * Check L1 latency.
@@ -413,9 +434,9 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
                 * L1 exit latencies advertised by a device include L1
                 * substate latencies (and hence do not do any check).
                 */
-               latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1);
+               latency = max_t(u32, latency_up_l1, latency_dw_l1);
                if ((link->aspm_capable & ASPM_STATE_L1) &&
-                   (latency + l1_switch_latency > acceptable->l1))
+                   (latency + l1_switch_latency > acceptable_l1))
                        link->aspm_capable &= ~ASPM_STATE_L1;
                l1_switch_latency += 1000;
 
@@ -423,20 +444,6 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
        }
 }
 
-/*
- * The L1 PM substate capability is only implemented in function 0 in a
- * multi function device.
- */
-static struct pci_dev *pci_function_0(struct pci_bus *linkbus)
-{
-       struct pci_dev *child;
-
-       list_for_each_entry(child, &linkbus->devices, bus_list)
-               if (PCI_FUNC(child->devfn) == 0)
-                       return child;
-       return NULL;
-}
-
 static void pci_clear_and_set_dword(struct pci_dev *pdev, int pos,
                                    u32 clear, u32 set)
 {
@@ -496,6 +503,7 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link,
        encode_l12_threshold(l1_2_threshold, &scale, &value);
        ctl1 |= t_common_mode << 8 | scale << 29 | value << 16;
 
+       /* Some broken devices only support dword access to L1 SS */
        pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, &pctl1);
        pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL2, &pctl2);
        pci_read_config_dword(child, child->l1ss + PCI_L1SS_CTL1, &cctl1);
@@ -593,8 +601,6 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
                link->aspm_enabled |= ASPM_STATE_L0S_UP;
        if (parent_lnkctl & PCI_EXP_LNKCTL_ASPM_L0S)
                link->aspm_enabled |= ASPM_STATE_L0S_DW;
-       link->latency_up.l0s = calc_l0s_latency(parent_lnkcap);
-       link->latency_dw.l0s = calc_l0s_latency(child_lnkcap);
 
        /* Setup L1 state */
        if (parent_lnkcap & child_lnkcap & PCI_EXP_LNKCAP_ASPM_L1)
@@ -602,8 +608,6 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
 
        if (parent_lnkctl & child_lnkctl & PCI_EXP_LNKCTL_ASPM_L1)
                link->aspm_enabled |= ASPM_STATE_L1;
-       link->latency_up.l1 = calc_l1_latency(parent_lnkcap);
-       link->latency_dw.l1 = calc_l1_latency(child_lnkcap);
 
        /* Setup L1 substate */
        pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CAP,
@@ -660,22 +664,10 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
 
        /* Get and check endpoint acceptable latencies */
        list_for_each_entry(child, &linkbus->devices, bus_list) {
-               u32 reg32, encoding;
-               struct aspm_latency *acceptable =
-                       &link->acceptable[PCI_FUNC(child->devfn)];
-
                if (pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT &&
                    pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END)
                        continue;
 
-               pcie_capability_read_dword(child, PCI_EXP_DEVCAP, &reg32);
-               /* Calculate endpoint L0s acceptable latency */
-               encoding = (reg32 & PCI_EXP_DEVCAP_L0S) >> 6;
-               acceptable->l0s = calc_l0s_acceptable(encoding);
-               /* Calculate endpoint L1 acceptable latency */
-               encoding = (reg32 & PCI_EXP_DEVCAP_L1) >> 9;
-               acceptable->l1 = calc_l1_acceptable(encoding);
-
                pcie_aspm_check_latency(child);
        }
 }
index 087d365..496c8b8 100644 (file)
@@ -1579,20 +1579,12 @@ void set_pcie_hotplug_bridge(struct pci_dev *pdev)
 
 static void set_pcie_thunderbolt(struct pci_dev *dev)
 {
-       int vsec = 0;
-       u32 header;
+       u16 vsec;
 
-       while ((vsec = pci_find_next_ext_capability(dev, vsec,
-                                                   PCI_EXT_CAP_ID_VNDR))) {
-               pci_read_config_dword(dev, vsec + PCI_VNDR_HEADER, &header);
-
-               /* Is the device part of a Thunderbolt controller? */
-               if (dev->vendor == PCI_VENDOR_ID_INTEL &&
-                   PCI_VNDR_HEADER_ID(header) == PCI_VSEC_ID_INTEL_TBT) {
-                       dev->is_thunderbolt = 1;
-                       return;
-               }
-       }
+       /* Is the device part of a Thunderbolt controller? */
+       vsec = pci_find_vsec_capability(dev, PCI_VENDOR_ID_INTEL, PCI_VSEC_ID_INTEL_TBT);
+       if (vsec)
+               dev->is_thunderbolt = 1;
 }
 
 static void set_pcie_untrusted(struct pci_dev *dev)
index 003950c..fa6e5bf 100644 (file)
@@ -4103,6 +4103,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9120,
                         quirk_dma_func1_alias);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9123,
                         quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c136 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9125,
+                        quirk_dma_func1_alias);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9128,
                         quirk_dma_func1_alias);
 /* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c14 */
@@ -5683,6 +5686,15 @@ SWITCHTEC_QUIRK(0x4268);  /* PAX 68XG4  */
 SWITCHTEC_QUIRK(0x4252);  /* PAX 52XG4  */
 SWITCHTEC_QUIRK(0x4236);  /* PAX 36XG4  */
 SWITCHTEC_QUIRK(0x4228);  /* PAX 28XG4  */
+SWITCHTEC_QUIRK(0x4352);  /* PFXA 52XG4 */
+SWITCHTEC_QUIRK(0x4336);  /* PFXA 36XG4 */
+SWITCHTEC_QUIRK(0x4328);  /* PFXA 28XG4 */
+SWITCHTEC_QUIRK(0x4452);  /* PSXA 52XG4 */
+SWITCHTEC_QUIRK(0x4436);  /* PSXA 36XG4 */
+SWITCHTEC_QUIRK(0x4428);  /* PSXA 28XG4 */
+SWITCHTEC_QUIRK(0x4552);  /* PAXA 52XG4 */
+SWITCHTEC_QUIRK(0x4536);  /* PAXA 36XG4 */
+SWITCHTEC_QUIRK(0x4528);  /* PAXA 28XG4 */
 
 /*
  * The PLX NTB uses devfn proxy IDs to move TLPs between NT endpoints.
@@ -5857,3 +5869,13 @@ static void nvidia_ion_ahci_fixup(struct pci_dev *pdev)
        pdev->dev_flags |= PCI_DEV_FLAGS_HAS_MSI_MASKING;
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, 0x0ab8, nvidia_ion_ahci_fixup);
+
+static void rom_bar_overlap_defect(struct pci_dev *dev)
+{
+       pci_info(dev, "working around ROM BAR overlap defect\n");
+       dev->rom_bar_overlap = 1;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1533, rom_bar_overlap_defect);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1536, rom_bar_overlap_defect);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1537, rom_bar_overlap_defect);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1538, rom_bar_overlap_defect);
index 7f1acb3..439ac5f 100644 (file)
@@ -75,12 +75,16 @@ static void pci_std_update_resource(struct pci_dev *dev, int resno)
                 * as zero when disabled, so don't update ROM BARs unless
                 * they're enabled.  See
                 * https://lore.kernel.org/r/43147B3D.1030309@vc.cvut.cz/
+                * But we must update ROM BAR for buggy devices where even a
+                * disabled ROM can conflict with other BARs.
                 */
-               if (!(res->flags & IORESOURCE_ROM_ENABLE))
+               if (!(res->flags & IORESOURCE_ROM_ENABLE) &&
+                   !dev->rom_bar_overlap)
                        return;
 
                reg = dev->rom_base_reg;
-               new |= PCI_ROM_ADDRESS_ENABLE;
+               if (res->flags & IORESOURCE_ROM_ENABLE)
+                       new |= PCI_ROM_ADDRESS_ENABLE;
        } else
                return;
 
index 751a266..a0c6719 100644 (file)
@@ -96,11 +96,12 @@ static struct attribute *pci_slot_default_attrs[] = {
        &pci_slot_attr_cur_speed.attr,
        NULL,
 };
+ATTRIBUTE_GROUPS(pci_slot_default);
 
 static struct kobj_type pci_slot_ktype = {
        .sysfs_ops = &pci_slot_sysfs_ops,
        .release = &pci_slot_release,
-       .default_attrs = pci_slot_default_attrs,
+       .default_groups = pci_slot_default_groups,
 };
 
 static char *make_slot_name(const char *name)
index 38c2b03..c36c123 100644 (file)
@@ -122,7 +122,7 @@ static void stuser_set_state(struct switchtec_user *stuser,
 {
        /* requires the mrpc_mutex to already be held when called */
 
-       const char * const state_names[] = {
+       static const char * const state_names[] = {
                [MRPC_IDLE] = "IDLE",
                [MRPC_QUEUED] = "QUEUED",
                [MRPC_RUNNING] = "RUNNING",
@@ -1779,6 +1779,15 @@ static const struct pci_device_id switchtec_pci_tbl[] = {
        SWITCHTEC_PCI_DEVICE(0x4252, SWITCHTEC_GEN4),  //PAX 52XG4
        SWITCHTEC_PCI_DEVICE(0x4236, SWITCHTEC_GEN4),  //PAX 36XG4
        SWITCHTEC_PCI_DEVICE(0x4228, SWITCHTEC_GEN4),  //PAX 28XG4
+       SWITCHTEC_PCI_DEVICE(0x4352, SWITCHTEC_GEN4),  //PFXA 52XG4
+       SWITCHTEC_PCI_DEVICE(0x4336, SWITCHTEC_GEN4),  //PFXA 36XG4
+       SWITCHTEC_PCI_DEVICE(0x4328, SWITCHTEC_GEN4),  //PFXA 28XG4
+       SWITCHTEC_PCI_DEVICE(0x4452, SWITCHTEC_GEN4),  //PSXA 52XG4
+       SWITCHTEC_PCI_DEVICE(0x4436, SWITCHTEC_GEN4),  //PSXA 36XG4
+       SWITCHTEC_PCI_DEVICE(0x4428, SWITCHTEC_GEN4),  //PSXA 28XG4
+       SWITCHTEC_PCI_DEVICE(0x4552, SWITCHTEC_GEN4),  //PAXA 52XG4
+       SWITCHTEC_PCI_DEVICE(0x4536, SWITCHTEC_GEN4),  //PAXA 36XG4
+       SWITCHTEC_PCI_DEVICE(0x4528, SWITCHTEC_GEN4),  //PAXA 28XG4
        {0}
 };
 MODULE_DEVICE_TABLE(pci, switchtec_pci_tbl);
index 18a75c8..f3cefec 100644 (file)
@@ -455,6 +455,7 @@ struct pci_dev {
        unsigned int    link_active_reporting:1;/* Device capable of reporting link active */
        unsigned int    no_vf_scan:1;           /* Don't scan for VFs after IOV enablement */
        unsigned int    no_command_memory:1;    /* No PCI_COMMAND_MEMORY */
+       unsigned int    rom_bar_overlap:1;      /* ROM BAR disable broken */
        pci_dev_flags_t dev_flags;
        atomic_t        enable_cnt;     /* pci_enable_device has been called */
 
@@ -1775,7 +1776,10 @@ static inline struct pci_dev *pci_get_class(unsigned int class,
                                            struct pci_dev *from)
 { return NULL; }
 
-#define pci_dev_present(ids)   (0)
+
+static inline int pci_dev_present(const struct pci_device_id *ids)
+{ return 0; }
+
 #define no_pci_devices()       (1)
 #define pci_dev_put(dev)       do { } while (0)