diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2022-01-13 09:57:46 -0600 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2022-01-13 09:57:46 -0600 |
commit | 800cee8b04d18db7ea4140aa6fb294508a2d20eb (patch) | |
tree | 8d2ff95e7a66758a1fcf04eb61b7d8a680fe05db /drivers/pci | |
parent | d03f92c43f9741c06ab1e4ff9b51830705cfa176 (diff) | |
parent | 2070b2ddea89f5b604fac3d27ade5cb6d19a5706 (diff) | |
download | linux-800cee8b04d18db7ea4140aa6fb294508a2d20eb.tar.bz2 |
Merge branch 'remotes/lorenzo/pci/aardvark'
- Add bridge emulation definitions for PCIe DEVCAP2, DEVCTL2, DEVSTA2,
LNKCAP2, LNKCTL2, LNKSTA2, SLTCAP2, SLTCTL2, SLTSTA2 (Pali Rohár)
- Add aardvark support for DEVCAP2, DEVCTL2, LNKCAP2 and LNKCTL2 registers
(Pali Rohár)
- Clear all MSIs at setup to avoid spurious interrupts (Pali Rohár)
- Disable bus mastering when unbinding host controller driver (Pali Rohár)
- Mask all interrupts when unbinding host controller driver (Pali Rohár)
- Fix memory leak in host controller unbind (Pali Rohár)
- Assert PERST# when unbinding host controller driver (Pali Rohár)
- Disable link training when unbinding host controller driver (Pali Rohár)
- Disable common PHY when unbinding host controller driver (Pali Rohár)
- Fix resource type checking to check only IORESOURCE_MEM, not
IORESOURCE_MEM_64, which is a flavor of IORESOURCE_MEM (Pali Rohár)
* remotes/lorenzo/pci/aardvark:
PCI: aardvark: Fix checking for MEM resource type
PCI: aardvark: Disable common PHY when unbinding driver
PCI: aardvark: Disable link training when unbinding driver
PCI: aardvark: Assert PERST# when unbinding driver
PCI: aardvark: Fix memory leak in driver unbind
PCI: aardvark: Mask all interrupts when unbinding driver
PCI: aardvark: Disable bus mastering when unbinding driver
PCI: aardvark: Comment actions in driver remove method
PCI: aardvark: Clear all MSIs at setup
PCI: aardvark: Add support for DEVCAP2, DEVCTL2, LNKCAP2 and LNKCTL2 registers on emulated bridge
PCI: pci-bridge-emul: Add definitions for missing capabilities registers
PCI: pci-bridge-emul: Add description for class_revision field
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/controller/pci-aardvark.c | 71 | ||||
-rw-r--r-- | drivers/pci/pci-bridge-emul.c | 49 |
2 files changed, 109 insertions, 11 deletions
diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index c5300d49807a..b654d06b64df 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -116,6 +116,7 @@ #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; } diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c index db97cddfc85e..0d1177e52a43 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c @@ -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); |