Merge branch 'pci/imx6'
authorBjorn Helgaas <bhelgaas@google.com>
Wed, 2 Jan 2019 21:31:15 +0000 (15:31 -0600)
committerBjorn Helgaas <bhelgaas@google.com>
Wed, 2 Jan 2019 21:31:15 +0000 (15:31 -0600)
  - Enable MSI for imx6 downstream components (Richard Zhu)

* pci/imx6:
  PCI: imx: Enable MSI from downstream components

1  2 
drivers/pci/controller/dwc/pci-imx6.c

@@@ -27,8 -27,6 +27,8 @@@
  #include <linux/types.h>
  #include <linux/interrupt.h>
  #include <linux/reset.h>
 +#include <linux/pm_domain.h>
 +#include <linux/pm_runtime.h>
  
  #include "pcie-designware.h"
  
@@@ -61,11 -59,6 +61,11 @@@ struct imx6_pcie 
        u32                     tx_swing_low;
        int                     link_gen;
        struct regulator        *vpcie;
 +
 +      /* power domain for pcie */
 +      struct device           *pd_pcie;
 +      /* power domain for pcie phy */
 +      struct device           *pd_pcie_phy;
  };
  
  /* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */
@@@ -74,6 -67,7 +74,7 @@@
  #define PHY_PLL_LOCK_WAIT_USLEEP_MAX  200
  
  /* PCIe Root Complex registers (memory-mapped) */
+ #define PCIE_RC_IMX6_MSI_CAP                  0x50
  #define PCIE_RC_LCR                           0x7c
  #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1      0x1
  #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2      0x2
@@@ -299,43 -293,6 +300,43 @@@ static int imx6q_pcie_abort_handler(uns
        return 1;
  }
  
 +static int imx6_pcie_attach_pd(struct device *dev)
 +{
 +      struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
 +      struct device_link *link;
 +
 +      /* Do nothing when in a single power domain */
 +      if (dev->pm_domain)
 +              return 0;
 +
 +      imx6_pcie->pd_pcie = dev_pm_domain_attach_by_name(dev, "pcie");
 +      if (IS_ERR(imx6_pcie->pd_pcie))
 +              return PTR_ERR(imx6_pcie->pd_pcie);
 +      link = device_link_add(dev, imx6_pcie->pd_pcie,
 +                      DL_FLAG_STATELESS |
 +                      DL_FLAG_PM_RUNTIME |
 +                      DL_FLAG_RPM_ACTIVE);
 +      if (!link) {
 +              dev_err(dev, "Failed to add device_link to pcie pd.\n");
 +              return -EINVAL;
 +      }
 +
 +      imx6_pcie->pd_pcie_phy = dev_pm_domain_attach_by_name(dev, "pcie_phy");
 +      if (IS_ERR(imx6_pcie->pd_pcie_phy))
 +              return PTR_ERR(imx6_pcie->pd_pcie_phy);
 +
 +      device_link_add(dev, imx6_pcie->pd_pcie_phy,
 +                      DL_FLAG_STATELESS |
 +                      DL_FLAG_PM_RUNTIME |
 +                      DL_FLAG_RPM_ACTIVE);
 +      if (IS_ERR(link)) {
 +              dev_err(dev, "Failed to add device_link to pcie_phy pd: %ld\n", PTR_ERR(link));
 +              return PTR_ERR(link);
 +      }
 +
 +      return 0;
 +}
 +
  static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
  {
        struct device *dev = imx6_pcie->pci->dev;
@@@ -817,28 -774,8 +818,28 @@@ static void imx6_pcie_ltssm_disable(str
  
  static void imx6_pcie_pm_turnoff(struct imx6_pcie *imx6_pcie)
  {
 -      reset_control_assert(imx6_pcie->turnoff_reset);
 -      reset_control_deassert(imx6_pcie->turnoff_reset);
 +      struct device *dev = imx6_pcie->pci->dev;
 +
 +      /* Some variants have a turnoff reset in DT */
 +      if (imx6_pcie->turnoff_reset) {
 +              reset_control_assert(imx6_pcie->turnoff_reset);
 +              reset_control_deassert(imx6_pcie->turnoff_reset);
 +              goto pm_turnoff_sleep;
 +      }
 +
 +      /* Others poke directly at IOMUXC registers */
 +      switch (imx6_pcie->variant) {
 +      case IMX6SX:
 +              regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
 +                              IMX6SX_GPR12_PCIE_PM_TURN_OFF,
 +                              IMX6SX_GPR12_PCIE_PM_TURN_OFF);
 +              regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
 +                              IMX6SX_GPR12_PCIE_PM_TURN_OFF, 0);
 +              break;
 +      default:
 +              dev_err(dev, "PME_Turn_Off not implemented\n");
 +              return;
 +      }
  
        /*
         * Components with an upstream port must respond to
         * The standard recommends a 1-10ms timeout after which to
         * proceed anyway as if acks were received.
         */
 +pm_turnoff_sleep:
        usleep_range(1000, 10000);
  }
  
@@@ -857,31 -793,18 +858,31 @@@ static void imx6_pcie_clk_disable(struc
        clk_disable_unprepare(imx6_pcie->pcie_phy);
        clk_disable_unprepare(imx6_pcie->pcie_bus);
  
 -      if (imx6_pcie->variant == IMX7D) {
 +      switch (imx6_pcie->variant) {
 +      case IMX6SX:
 +              clk_disable_unprepare(imx6_pcie->pcie_inbound_axi);
 +              break;
 +      case IMX7D:
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                                   IMX7D_GPR12_PCIE_PHY_REFCLK_SEL,
                                   IMX7D_GPR12_PCIE_PHY_REFCLK_SEL);
 +              break;
 +      default:
 +              break;
        }
  }
  
 +static inline bool imx6_pcie_supports_suspend(struct imx6_pcie *imx6_pcie)
 +{
 +      return (imx6_pcie->variant == IMX7D ||
 +              imx6_pcie->variant == IMX6SX);
 +}
 +
  static int imx6_pcie_suspend_noirq(struct device *dev)
  {
        struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
  
 -      if (imx6_pcie->variant != IMX7D)
 +      if (!imx6_pcie_supports_suspend(imx6_pcie))
                return 0;
  
        imx6_pcie_pm_turnoff(imx6_pcie);
@@@ -897,7 -820,7 +898,7 @@@ static int imx6_pcie_resume_noirq(struc
        struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
        struct pcie_port *pp = &imx6_pcie->pci->pp;
  
 -      if (imx6_pcie->variant != IMX7D)
 +      if (!imx6_pcie_supports_suspend(imx6_pcie))
                return 0;
  
        imx6_pcie_assert_core_reset(imx6_pcie);
@@@ -926,6 -849,7 +927,7 @@@ static int imx6_pcie_probe(struct platf
        struct resource *dbi_base;
        struct device_node *node = dev->of_node;
        int ret;
+       u16 val;
  
        imx6_pcie = devm_kzalloc(dev, sizeof(*imx6_pcie), GFP_KERNEL);
        if (!imx6_pcie)
  
        platform_set_drvdata(pdev, imx6_pcie);
  
 +      ret = imx6_pcie_attach_pd(dev);
 +      if (ret)
 +              return ret;
 +
        ret = imx6_add_pcie_port(imx6_pcie, pdev);
        if (ret < 0)
                return ret;
  
+       if (pci_msi_enabled()) {
+               val = dw_pcie_readw_dbi(pci, PCIE_RC_IMX6_MSI_CAP +
+                                       PCI_MSI_FLAGS);
+               val |= PCI_MSI_FLAGS_ENABLE;
+               dw_pcie_writew_dbi(pci, PCIE_RC_IMX6_MSI_CAP + PCI_MSI_FLAGS,
+                                  val);
+       }
        return 0;
  }