Merge branch 'remotes/lorenzo/pci/bridge-emul'
[linux-2.6-block.git] / drivers / pci / controller / pci-aardvark.c
index 62fc55f2ed40dc78072ccf6bea8a300fad9c5a57..a3987e717258325f8f256363c374f31909b4b5a1 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);
@@ -883,8 +885,13 @@ advk_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
 
        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;
        }
@@ -898,10 +905,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)
@@ -923,6 +926,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;
        }
@@ -1394,7 +1403,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))
@@ -1546,8 +1555,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;
 
                /*
@@ -1555,8 +1563,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;
 
                /*
@@ -1688,20 +1695,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;
 }