Merge branch 'remotes/lorenzo/pci/aardvark'
[platform/kernel/linux-rpi.git] / drivers / pci / controller / pci-aardvark.c
index 3a05f6c..a938af4 100644 (file)
        (PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn))    | \
         PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where))
 
-#define PIO_TIMEOUT_MS                 1
+#define PIO_RETRY_CNT                  500
+#define PIO_RETRY_DELAY                        2 /* 2 us*/
 
 #define LINK_WAIT_MAX_RETRIES          10
 #define LINK_WAIT_USLEEP_MIN           90000
 #define LINK_WAIT_USLEEP_MAX           100000
+#define RETRAIN_WAIT_MAX_RETRIES       10
+#define RETRAIN_WAIT_USLEEP_US         2000
 
 #define MSI_IRQ_NUM                    32
 
@@ -240,6 +243,17 @@ static int advk_pcie_wait_for_link(struct advk_pcie *pcie)
        return -ETIMEDOUT;
 }
 
+static void advk_pcie_wait_for_retrain(struct advk_pcie *pcie)
+{
+       size_t retries;
+
+       for (retries = 0; retries < RETRAIN_WAIT_MAX_RETRIES; ++retries) {
+               if (!advk_pcie_link_up(pcie))
+                       break;
+               udelay(RETRAIN_WAIT_USLEEP_US);
+       }
+}
+
 static void advk_pcie_setup_hw(struct advk_pcie *pcie)
 {
        u32 reg;
@@ -325,6 +339,14 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
        reg |= PIO_CTRL_ADDR_WIN_DISABLE;
        advk_writel(pcie, reg, PIO_CTRL);
 
+       /*
+        * PERST# signal could have been asserted by pinctrl subsystem before
+        * probe() callback has been called, making the endpoint going into
+        * fundamental reset. As required by PCI Express spec a delay for at
+        * least 100ms after such a reset before link training is needed.
+        */
+       msleep(PCI_PM_D3COLD_WAIT);
+
        /* Start link training */
        reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG);
        reg |= PCIE_CORE_LINK_TRAINING;
@@ -384,17 +406,16 @@ static void advk_pcie_check_pio_status(struct advk_pcie *pcie)
 static int advk_pcie_wait_pio(struct advk_pcie *pcie)
 {
        struct device *dev = &pcie->pdev->dev;
-       unsigned long timeout;
+       int i;
 
-       timeout = jiffies + msecs_to_jiffies(PIO_TIMEOUT_MS);
-
-       while (time_before(jiffies, timeout)) {
+       for (i = 0; i < PIO_RETRY_CNT; i++) {
                u32 start, isr;
 
                start = advk_readl(pcie, PIO_START);
                isr = advk_readl(pcie, PIO_ISR);
                if (!start && isr)
                        return 0;
+               udelay(PIO_RETRY_DELAY);
        }
 
        dev_err(dev, "config read/write timed out\n");
@@ -416,7 +437,7 @@ advk_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
 
        case PCI_EXP_RTCTL: {
                u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG);
-               *value = (val & PCIE_MSG_PM_PME_MASK) ? PCI_EXP_RTCTL_PMEIE : 0;
+               *value = (val & PCIE_MSG_PM_PME_MASK) ? 0 : PCI_EXP_RTCTL_PMEIE;
                return PCI_BRIDGE_EMUL_HANDLED;
        }
 
@@ -427,11 +448,20 @@ advk_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
                return PCI_BRIDGE_EMUL_HANDLED;
        }
 
+       case PCI_EXP_LNKCTL: {
+               /* u32 contains both PCI_EXP_LNKCTL and PCI_EXP_LNKSTA */
+               u32 val = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg) &
+                       ~(PCI_EXP_LNKSTA_LT << 16);
+               if (!advk_pcie_link_up(pcie))
+                       val |= (PCI_EXP_LNKSTA_LT << 16);
+               *value = val;
+               return PCI_BRIDGE_EMUL_HANDLED;
+       }
+
        case PCI_CAP_LIST_ID:
        case PCI_EXP_DEVCAP:
        case PCI_EXP_DEVCTL:
        case PCI_EXP_LNKCAP:
-       case PCI_EXP_LNKCTL:
                *value = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg);
                return PCI_BRIDGE_EMUL_HANDLED;
        default:
@@ -448,14 +478,24 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
 
        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)
+                       advk_pcie_wait_for_retrain(pcie);
                break;
 
-       case PCI_EXP_RTCTL:
-               new = (new & PCI_EXP_RTCTL_PMEIE) << 3;
-               advk_writel(pcie, new, PCIE_ISR0_MASK_REG);
+       case PCI_EXP_RTCTL: {
+               /* Only mask/unmask PME interrupt */
+               u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG) &
+                       ~PCIE_MSG_PM_PME_MASK;
+               if ((new & PCI_EXP_RTCTL_PMEIE) == 0)
+                       val |= PCIE_MSG_PM_PME_MASK;
+               advk_writel(pcie, val, PCIE_ISR0_MASK_REG);
                break;
+       }
 
        case PCI_EXP_RTSTA:
                new = (new & PCI_EXP_RTSTA_PME) >> 9;
@@ -480,18 +520,20 @@ static void advk_sw_pci_bridge_init(struct advk_pcie *pcie)
 {
        struct pci_bridge_emul *bridge = &pcie->bridge;
 
-       bridge->conf.vendor = advk_readl(pcie, PCIE_CORE_DEV_ID_REG) & 0xffff;
-       bridge->conf.device = advk_readl(pcie, PCIE_CORE_DEV_ID_REG) >> 16;
+       bridge->conf.vendor =
+               cpu_to_le16(advk_readl(pcie, PCIE_CORE_DEV_ID_REG) & 0xffff);
+       bridge->conf.device =
+               cpu_to_le16(advk_readl(pcie, PCIE_CORE_DEV_ID_REG) >> 16);
        bridge->conf.class_revision =
-               advk_readl(pcie, PCIE_CORE_DEV_REV_REG) & 0xff;
+               cpu_to_le32(advk_readl(pcie, PCIE_CORE_DEV_REV_REG) & 0xff);
 
        /* Support 32 bits I/O addressing */
        bridge->conf.iobase = PCI_IO_RANGE_TYPE_32;
        bridge->conf.iolimit = PCI_IO_RANGE_TYPE_32;
 
        /* Support 64 bits memory pref */
-       bridge->conf.pref_mem_base = PCI_PREF_RANGE_TYPE_64;
-       bridge->conf.pref_mem_limit = PCI_PREF_RANGE_TYPE_64;
+       bridge->conf.pref_mem_base = cpu_to_le16(PCI_PREF_RANGE_TYPE_64);
+       bridge->conf.pref_mem_limit = cpu_to_le16(PCI_PREF_RANGE_TYPE_64);
 
        /* Support interrupt A for MSI feature */
        bridge->conf.intpin = PCIE_CORE_INT_A_ASSERT_ENABLE;