diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2022-03-22 17:16:22 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2022-03-22 17:16:22 -0500 |
commit | 7ec9ff94f5f7416f501d1c0177c82e785974f9ec (patch) | |
tree | d161a9093d13ed2b4c13d144cf607c93ec5cb948 /drivers | |
parent | 816f8917046de14e78a5715b72d462e10123a45c (diff) | |
parent | 92f4ffecc4170ce29e67a1f8d51c168c3de95fb2 (diff) | |
download | linux-stable-7ec9ff94f5f7416f501d1c0177c82e785974f9ec.tar.gz linux-stable-7ec9ff94f5f7416f501d1c0177c82e785974f9ec.tar.bz2 linux-stable-7ec9ff94f5f7416f501d1c0177c82e785974f9ec.zip |
Merge branch 'remotes/lorenzo/pci/aardvark'
- Use PCI_INTERRUPT_* definitions from PCI core instead of custom ones
(Pali Rohár)
- Derive MSI number from bit(s) set in PCIE_MSI_STATUS_REG, not from
PCIE_MSI_PAYLOAD_REG (Pali Rohár)
- Align multi-MSI vectors to power of two (Pali Rohár)
- Rewrite IRQ code to use chained IRQ handler (Pali Rohár)
- Check return value of generic_handle_domain_irq() and warn about spurious
interrupts (Pali Rohár)
- Make MSI irq_chip structures static to driver (Marek Behún)
- Make msi_domain_info structure static to driver (Marek Behún)
- Use dev_fwnode() instead of of_node_to_fwnode(dev->of_node) (Marek Behún)
- Refactor unmasking of summary MSI interrupt (Pali Rohár)
- Add support for masking MSI interrupts and leave them masked at setup
(Pali Rohár)
- Set MSI doorbell address to address of struct advk_pcie (Pali Rohár)
- Enable MSI-X support (Pali Rohár)
- Add support for ERR interrupt on emulated bridge (Pali Rohár)
- Fix read of PCI_EXP_RTSTA_PME bit on emulated bridge (Pali Rohár)
- Optimize writing PCI_EXP_RTCTL_PMEIE and PCI_EXP_RTSTA_PME on emulated
bridge (Pali Rohár)
- Add support for PME interrupts (Pali Rohár)
- Fix support for PME requester on emulated bridge (Pali Rohár)
- Use separate INTA interrupt for emulated Root Port so PME and AER
interrupt is not shared with downstream devices (Pali Rohár)
- Remove irq_mask_ack() callback for INTx interrupts (Pali Rohár)
- Don't mask legacy INTx interrupts when mapping (Pali Rohár)
- Drop unnecessary "__maybe_unused" from advk_pcie_disable_phy() (Marek
Behún)
- Update comment about why we check for link being up before issuing a
config request (Marek Behún)
* remotes/lorenzo/pci/aardvark:
PCI: aardvark: Update comment about link going down after link-up
PCI: aardvark: Drop __maybe_unused from advk_pcie_disable_phy()
PCI: aardvark: Don't mask irq when mapping
PCI: aardvark: Remove irq_mask_ack() callback for INTx interrupts
PCI: aardvark: Use separate INTA interrupt for emulated root bridge
PCI: aardvark: Fix support for PME requester on emulated bridge
PCI: aardvark: Add support for PME interrupts
PCI: aardvark: Optimize writing PCI_EXP_RTCTL_PMEIE and PCI_EXP_RTSTA_PME on emulated bridge
PCI: aardvark: Fix reading PCI_EXP_RTSTA_PME bit on emulated bridge
PCI: aardvark: Add support for ERR interrupt on emulated bridge
PCI: aardvark: Enable MSI-X support
PCI: aardvark: Fix setting MSI address
PCI: aardvark: Add support for masking MSI interrupts
PCI: aardvark: Refactor unmasking summary MSI interrupt
PCI: aardvark: Use dev_fwnode() instead of of_node_to_fwnode(dev->of_node)
PCI: aardvark: Make msi_domain_info structure a static driver structure
PCI: aardvark: Make MSI irq_chip structures static driver structures
PCI: aardvark: Check return value of generic_handle_domain_irq() when processing INTx IRQ
PCI: aardvark: Rewrite IRQ code to chained IRQ handler
PCI: aardvark: Fix support for MSI interrupts
PCI: aardvark: Fix reading MSI interrupt number
PCI: aardvark: Replace custom PCIE_CORE_INT_* macros with PCI_INTERRUPT_*
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/controller/pci-aardvark.c | 390 |
1 files changed, 274 insertions, 116 deletions
diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 2561326e9181..1594994aefd7 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -38,10 +38,6 @@ #define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6) #define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK BIT(7) #define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV BIT(8) -#define PCIE_CORE_INT_A_ASSERT_ENABLE 1 -#define PCIE_CORE_INT_B_ASSERT_ENABLE 2 -#define PCIE_CORE_INT_C_ASSERT_ENABLE 3 -#define PCIE_CORE_INT_D_ASSERT_ENABLE 4 /* PIO registers base address and register offsets */ #define PIO_BASE_ADDR 0x4000 #define PIO_CTRL (PIO_BASE_ADDR + 0x0) @@ -102,6 +98,10 @@ #define PCIE_MSG_PM_PME_MASK BIT(7) #define PCIE_ISR0_MASK_REG (CONTROL_BASE_ADDR + 0x44) #define PCIE_ISR0_MSI_INT_PENDING BIT(24) +#define PCIE_ISR0_CORR_ERR BIT(11) +#define PCIE_ISR0_NFAT_ERR BIT(12) +#define PCIE_ISR0_FAT_ERR BIT(13) +#define PCIE_ISR0_ERR_MASK GENMASK(13, 11) #define PCIE_ISR0_INTX_ASSERT(val) BIT(16 + (val)) #define PCIE_ISR0_INTX_DEASSERT(val) BIT(20 + (val)) #define PCIE_ISR0_ALL_MASK GENMASK(31, 0) @@ -272,17 +272,16 @@ struct advk_pcie { u32 actions; } wins[OB_WIN_COUNT]; u8 wins_count; + int irq; + struct irq_domain *rp_irq_domain; struct irq_domain *irq_domain; struct irq_chip irq_chip; raw_spinlock_t irq_lock; struct irq_domain *msi_domain; struct irq_domain *msi_inner_domain; - struct irq_chip msi_bottom_irq_chip; - struct irq_chip msi_irq_chip; - struct msi_domain_info msi_domain_info; + raw_spinlock_t msi_irq_lock; DECLARE_BITMAP(msi_used, MSI_IRQ_NUM); struct mutex msi_used_lock; - u16 msi_msg; int link_gen; struct pci_bridge_emul bridge; struct gpio_desc *reset_gpio; @@ -477,6 +476,7 @@ static void advk_pcie_disable_ob_win(struct advk_pcie *pcie, u8 win_num) static void advk_pcie_setup_hw(struct advk_pcie *pcie) { + phys_addr_t msi_addr; u32 reg; int i; @@ -565,6 +565,11 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) reg |= LANE_COUNT_1; advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); + /* Set MSI address */ + msi_addr = virt_to_phys(pcie); + advk_writel(pcie, lower_32_bits(msi_addr), PCIE_MSI_ADDR_LOW_REG); + advk_writel(pcie, upper_32_bits(msi_addr), PCIE_MSI_ADDR_HIGH_REG); + /* Enable MSI */ reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG); reg |= PCIE_CORE_CTRL2_MSI_ENABLE; @@ -576,15 +581,20 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG); advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG); - /* Disable All ISR0/1 Sources */ - reg = PCIE_ISR0_ALL_MASK; + /* Disable All ISR0/1 and MSI Sources */ + 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_MSI_ALL_MASK, PCIE_MSI_MASK_REG); + + /* Unmask summary MSI interrupt */ + reg = advk_readl(pcie, PCIE_ISR0_MASK_REG); reg &= ~PCIE_ISR0_MSI_INT_PENDING; advk_writel(pcie, reg, PCIE_ISR0_MASK_REG); - advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG); - - /* Unmask all MSIs */ - advk_writel(pcie, ~(u32)PCIE_MSI_ALL_MASK, PCIE_MSI_MASK_REG); + /* Unmask PME interrupt for processing of PME requester */ + reg = advk_readl(pcie, PCIE_ISR0_MASK_REG); + reg &= ~PCIE_MSG_PM_PME_MASK; + advk_writel(pcie, reg, PCIE_ISR0_MASK_REG); /* Enable summary interrupt for GIC SPI source */ reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK); @@ -778,11 +788,15 @@ advk_pci_bridge_emul_base_conf_read(struct pci_bridge_emul *bridge, case PCI_INTERRUPT_LINE: { /* * From the whole 32bit register we support reading from HW only - * one bit: PCI_BRIDGE_CTL_BUS_RESET. + * two bits: PCI_BRIDGE_CTL_BUS_RESET and PCI_BRIDGE_CTL_SERR. * Other bits are retrieved only from emulated config buffer. */ __le32 *cfgspace = (__le32 *)&bridge->conf; u32 val = le32_to_cpu(cfgspace[PCI_INTERRUPT_LINE / 4]); + if (advk_readl(pcie, PCIE_ISR0_MASK_REG) & PCIE_ISR0_ERR_MASK) + val &= ~(PCI_BRIDGE_CTL_SERR << 16); + else + val |= PCI_BRIDGE_CTL_SERR << 16; if (advk_readl(pcie, PCIE_CORE_CTRL1_REG) & HOT_RESET_GEN) val |= PCI_BRIDGE_CTL_BUS_RESET << 16; else @@ -808,6 +822,19 @@ advk_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, break; case PCI_INTERRUPT_LINE: + /* + * According to Figure 6-3: Pseudo Logic Diagram for Error + * Message Controls in PCIe base specification, SERR# Enable bit + * in Bridge Control register enable receiving of ERR_* messages + */ + if (mask & (PCI_BRIDGE_CTL_SERR << 16)) { + u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG); + if (new & (PCI_BRIDGE_CTL_SERR << 16)) + val &= ~PCIE_ISR0_ERR_MASK; + else + val |= PCIE_ISR0_ERR_MASK; + advk_writel(pcie, val, PCIE_ISR0_MASK_REG); + } if (mask & (PCI_BRIDGE_CTL_BUS_RESET << 16)) { u32 val = advk_readl(pcie, PCIE_CORE_CTRL1_REG); if (new & (PCI_BRIDGE_CTL_BUS_RESET << 16)) @@ -835,20 +862,11 @@ advk_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, *value = PCI_EXP_SLTSTA_PDS << 16; return PCI_BRIDGE_EMUL_HANDLED; - case PCI_EXP_RTCTL: { - u32 val = advk_readl(pcie, PCIE_ISR0_MASK_REG); - *value = (val & PCIE_MSG_PM_PME_MASK) ? 0 : PCI_EXP_RTCTL_PMEIE; - *value |= le16_to_cpu(bridge->pcie_conf.rootctl) & PCI_EXP_RTCTL_CRSSVE; - *value |= PCI_EXP_RTCAP_CRSVIS << 16; - return PCI_BRIDGE_EMUL_HANDLED; - } - - case PCI_EXP_RTSTA: { - u32 isr0 = advk_readl(pcie, PCIE_ISR0_REG); - u32 msglog = advk_readl(pcie, PCIE_MSG_LOG_REG); - *value = (isr0 & PCIE_MSG_PM_PME_MASK) << 16 | (msglog >> 16); - return PCI_BRIDGE_EMUL_HANDLED; - } + /* + * PCI_EXP_RTCTL and PCI_EXP_RTSTA are also supported, but do not need + * to be handled here, because their values are stored in emulated + * config space buffer, and we read them from there when needed. + */ case PCI_EXP_LNKCAP: { u32 val = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + reg); @@ -903,19 +921,18 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, break; 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); + u16 rootctl = le16_to_cpu(bridge->pcie_conf.rootctl); + /* Only emulation of PMEIE and CRSSVE bits is provided */ + rootctl &= PCI_EXP_RTCTL_PMEIE | PCI_EXP_RTCTL_CRSSVE; + bridge->pcie_conf.rootctl = cpu_to_le16(rootctl); break; } - case PCI_EXP_RTSTA: - new = (new & PCI_EXP_RTSTA_PME) >> 9; - advk_writel(pcie, new, PCIE_ISR0_REG); - break; + /* + * PCI_EXP_RTSTA is also supported, but does not need to be handled + * here, because its value is stored in emulated config space buffer, + * and we write it there when needed. + */ case PCI_EXP_DEVCTL: case PCI_EXP_DEVCTL2: @@ -959,7 +976,7 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) 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; + bridge->conf.intpin = PCI_INTERRUPT_INTA; /* Aardvark HW provides PCIe Capability structure in version 2 */ bridge->pcie_conf.cap = cpu_to_le16(2); @@ -981,8 +998,12 @@ static bool advk_pcie_valid_device(struct advk_pcie *pcie, struct pci_bus *bus, return false; /* - * If the link goes down after we check for link-up, nothing bad - * happens but the config access times out. + * If the link goes down after we check for link-up, we have a problem: + * if a PIO request is executed while link-down, the whole controller + * gets stuck in a non-functional state, and even after link comes up + * again, PIO requests won't work anymore, and a reset of the whole PCIe + * controller is needed. Therefore we need to prevent sending PIO + * requests while the link is down. */ if (!pci_is_root_bus(bus) && !advk_pcie_link_up(pcie)) return false; @@ -1180,11 +1201,11 @@ static void advk_msi_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) { struct advk_pcie *pcie = irq_data_get_irq_chip_data(data); - phys_addr_t msi_msg = virt_to_phys(&pcie->msi_msg); + phys_addr_t msi_addr = virt_to_phys(pcie); - msg->address_lo = lower_32_bits(msi_msg); - msg->address_hi = upper_32_bits(msi_msg); - msg->data = data->irq; + msg->address_lo = lower_32_bits(msi_addr); + msg->address_hi = upper_32_bits(msi_addr); + msg->data = data->hwirq; } static int advk_msi_set_affinity(struct irq_data *irq_data, @@ -1193,6 +1214,54 @@ static int advk_msi_set_affinity(struct irq_data *irq_data, return -EINVAL; } +static void advk_msi_irq_mask(struct irq_data *d) +{ + struct advk_pcie *pcie = d->domain->host_data; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + unsigned long flags; + u32 mask; + + raw_spin_lock_irqsave(&pcie->msi_irq_lock, flags); + mask = advk_readl(pcie, PCIE_MSI_MASK_REG); + mask |= BIT(hwirq); + advk_writel(pcie, mask, PCIE_MSI_MASK_REG); + raw_spin_unlock_irqrestore(&pcie->msi_irq_lock, flags); +} + +static void advk_msi_irq_unmask(struct irq_data *d) +{ + struct advk_pcie *pcie = d->domain->host_data; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + unsigned long flags; + u32 mask; + + raw_spin_lock_irqsave(&pcie->msi_irq_lock, flags); + mask = advk_readl(pcie, PCIE_MSI_MASK_REG); + mask &= ~BIT(hwirq); + advk_writel(pcie, mask, PCIE_MSI_MASK_REG); + raw_spin_unlock_irqrestore(&pcie->msi_irq_lock, flags); +} + +static void advk_msi_top_irq_mask(struct irq_data *d) +{ + pci_msi_mask_irq(d); + irq_chip_mask_parent(d); +} + +static void advk_msi_top_irq_unmask(struct irq_data *d) +{ + pci_msi_unmask_irq(d); + irq_chip_unmask_parent(d); +} + +static struct irq_chip advk_msi_bottom_irq_chip = { + .name = "MSI", + .irq_compose_msi_msg = advk_msi_irq_compose_msi_msg, + .irq_set_affinity = advk_msi_set_affinity, + .irq_mask = advk_msi_irq_mask, + .irq_unmask = advk_msi_irq_unmask, +}; + static int advk_msi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *args) @@ -1201,19 +1270,15 @@ static int advk_msi_irq_domain_alloc(struct irq_domain *domain, int hwirq, i; mutex_lock(&pcie->msi_used_lock); - hwirq = bitmap_find_next_zero_area(pcie->msi_used, MSI_IRQ_NUM, - 0, nr_irqs, 0); - if (hwirq >= MSI_IRQ_NUM) { - mutex_unlock(&pcie->msi_used_lock); - return -ENOSPC; - } - - bitmap_set(pcie->msi_used, hwirq, nr_irqs); + hwirq = bitmap_find_free_region(pcie->msi_used, MSI_IRQ_NUM, + order_base_2(nr_irqs)); mutex_unlock(&pcie->msi_used_lock); + if (hwirq < 0) + return -ENOSPC; for (i = 0; i < nr_irqs; i++) irq_domain_set_info(domain, virq + i, hwirq + i, - &pcie->msi_bottom_irq_chip, + &advk_msi_bottom_irq_chip, domain->host_data, handle_simple_irq, NULL, NULL); @@ -1227,7 +1292,7 @@ static void advk_msi_irq_domain_free(struct irq_domain *domain, struct advk_pcie *pcie = domain->host_data; mutex_lock(&pcie->msi_used_lock); - bitmap_clear(pcie->msi_used, d->hwirq, nr_irqs); + bitmap_release_region(pcie->msi_used, d->hwirq, order_base_2(nr_irqs)); mutex_unlock(&pcie->msi_used_lock); } @@ -1269,7 +1334,6 @@ static int advk_pcie_irq_map(struct irq_domain *h, { struct advk_pcie *pcie = h->host_data; - advk_pcie_irq_mask(irq_get_irq_data(virq)); irq_set_status_flags(virq, IRQ_LEVEL); irq_set_chip_and_handler(virq, &pcie->irq_chip, handle_level_irq); @@ -1283,37 +1347,25 @@ static const struct irq_domain_ops advk_pcie_irq_domain_ops = { .xlate = irq_domain_xlate_onecell, }; +static struct irq_chip advk_msi_irq_chip = { + .name = "advk-MSI", + .irq_mask = advk_msi_top_irq_mask, + .irq_unmask = advk_msi_top_irq_unmask, +}; + +static struct msi_domain_info advk_msi_domain_info = { + .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX, + .chip = &advk_msi_irq_chip, +}; + static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie) { struct device *dev = &pcie->pdev->dev; - struct device_node *node = dev->of_node; - struct irq_chip *bottom_ic, *msi_ic; - struct msi_domain_info *msi_di; - phys_addr_t msi_msg_phys; + raw_spin_lock_init(&pcie->msi_irq_lock); mutex_init(&pcie->msi_used_lock); - bottom_ic = &pcie->msi_bottom_irq_chip; - - bottom_ic->name = "MSI"; - bottom_ic->irq_compose_msi_msg = advk_msi_irq_compose_msi_msg; - bottom_ic->irq_set_affinity = advk_msi_set_affinity; - - msi_ic = &pcie->msi_irq_chip; - msi_ic->name = "advk-MSI"; - - msi_di = &pcie->msi_domain_info; - msi_di->flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_MULTI_PCI_MSI; - msi_di->chip = msi_ic; - - msi_msg_phys = virt_to_phys(&pcie->msi_msg); - - advk_writel(pcie, lower_32_bits(msi_msg_phys), - PCIE_MSI_ADDR_LOW_REG); - advk_writel(pcie, upper_32_bits(msi_msg_phys), - PCIE_MSI_ADDR_HIGH_REG); - pcie->msi_inner_domain = irq_domain_add_linear(NULL, MSI_IRQ_NUM, &advk_msi_domain_ops, pcie); @@ -1321,8 +1373,9 @@ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie) return -ENOMEM; pcie->msi_domain = - pci_msi_create_irq_domain(of_node_to_fwnode(node), - msi_di, pcie->msi_inner_domain); + pci_msi_create_irq_domain(dev_fwnode(dev), + &advk_msi_domain_info, + pcie->msi_inner_domain); if (!pcie->msi_domain) { irq_domain_remove(pcie->msi_inner_domain); return -ENOMEM; @@ -1363,7 +1416,6 @@ static int advk_pcie_init_irq_domain(struct advk_pcie *pcie) } irq_chip->irq_mask = advk_pcie_irq_mask; - irq_chip->irq_mask_ack = advk_pcie_irq_mask; irq_chip->irq_unmask = advk_pcie_irq_unmask; pcie->irq_domain = @@ -1385,10 +1437,73 @@ static void advk_pcie_remove_irq_domain(struct advk_pcie *pcie) irq_domain_remove(pcie->irq_domain); } +static struct irq_chip advk_rp_irq_chip = { + .name = "advk-RP", +}; + +static int advk_pcie_rp_irq_map(struct irq_domain *h, + unsigned int virq, irq_hw_number_t hwirq) +{ + struct advk_pcie *pcie = h->host_data; + + irq_set_chip_and_handler(virq, &advk_rp_irq_chip, handle_simple_irq); + irq_set_chip_data(virq, pcie); + + return 0; +} + +static const struct irq_domain_ops advk_pcie_rp_irq_domain_ops = { + .map = advk_pcie_rp_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int advk_pcie_init_rp_irq_domain(struct advk_pcie *pcie) +{ + pcie->rp_irq_domain = irq_domain_add_linear(NULL, 1, + &advk_pcie_rp_irq_domain_ops, + pcie); + if (!pcie->rp_irq_domain) { + dev_err(&pcie->pdev->dev, "Failed to add Root Port IRQ domain\n"); + return -ENOMEM; + } + + return 0; +} + +static void advk_pcie_remove_rp_irq_domain(struct advk_pcie *pcie) +{ + irq_domain_remove(pcie->rp_irq_domain); +} + +static void advk_pcie_handle_pme(struct advk_pcie *pcie) +{ + u32 requester = advk_readl(pcie, PCIE_MSG_LOG_REG) >> 16; + + advk_writel(pcie, PCIE_MSG_PM_PME_MASK, PCIE_ISR0_REG); + + /* + * PCIE_MSG_LOG_REG contains the last inbound message, so store + * the requester ID only when PME was not asserted yet. + * Also do not trigger PME interrupt when PME is still asserted. + */ + if (!(le32_to_cpu(pcie->bridge.pcie_conf.rootsta) & PCI_EXP_RTSTA_PME)) { + pcie->bridge.pcie_conf.rootsta = cpu_to_le32(requester | PCI_EXP_RTSTA_PME); + + /* + * Trigger PME interrupt only if PMEIE bit in Root Control is set. + * Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ, so use PCIe interrupt 0. + */ + if (!(le16_to_cpu(pcie->bridge.pcie_conf.rootctl) & PCI_EXP_RTCTL_PMEIE)) + return; + + if (generic_handle_domain_irq(pcie->rp_irq_domain, 0) == -EINVAL) + dev_err_ratelimited(&pcie->pdev->dev, "unhandled PME IRQ\n"); + } +} + static void advk_pcie_handle_msi(struct advk_pcie *pcie) { u32 msi_val, msi_mask, msi_status, msi_idx; - u16 msi_data; msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG); msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG); @@ -1398,13 +1513,9 @@ static void advk_pcie_handle_msi(struct advk_pcie *pcie) if (!(BIT(msi_idx) & msi_status)) continue; - /* - * msi_idx contains bits [4:0] of the msi_data and msi_data - * contains 16bit MSI interrupt number - */ advk_writel(pcie, BIT(msi_idx), PCIE_MSI_STATUS_REG); - msi_data = advk_readl(pcie, PCIE_MSI_PAYLOAD_REG) & PCIE_MSI_DATA_MASK; - generic_handle_irq(msi_data); + if (generic_handle_domain_irq(pcie->msi_inner_domain, msi_idx) == -EINVAL) + dev_err_ratelimited(&pcie->pdev->dev, "unexpected MSI 0x%02x\n", msi_idx); } advk_writel(pcie, PCIE_ISR0_MSI_INT_PENDING, @@ -1425,6 +1536,22 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie) isr1_mask = advk_readl(pcie, PCIE_ISR1_MASK_REG); isr1_status = isr1_val & ((~isr1_mask) & PCIE_ISR1_ALL_MASK); + /* Process PME interrupt as the first one to do not miss PME requester id */ + if (isr0_status & PCIE_MSG_PM_PME_MASK) + advk_pcie_handle_pme(pcie); + + /* Process ERR interrupt */ + if (isr0_status & PCIE_ISR0_ERR_MASK) { + advk_writel(pcie, PCIE_ISR0_ERR_MASK, PCIE_ISR0_REG); + + /* + * Aardvark HW returns zero for PCI_ERR_ROOT_AER_IRQ, so use + * PCIe interrupt 0 + */ + if (generic_handle_domain_irq(pcie->rp_irq_domain, 0) == -EINVAL) + dev_err_ratelimited(&pcie->pdev->dev, "unhandled ERR IRQ\n"); + } + /* Process MSI interrupts */ if (isr0_status & PCIE_ISR0_MSI_INT_PENDING) advk_pcie_handle_msi(pcie); @@ -1437,28 +1564,50 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie) advk_writel(pcie, PCIE_ISR1_INTX_ASSERT(i), PCIE_ISR1_REG); - generic_handle_domain_irq(pcie->irq_domain, i); + if (generic_handle_domain_irq(pcie->irq_domain, i) == -EINVAL) + dev_err_ratelimited(&pcie->pdev->dev, "unexpected INT%c IRQ\n", + (char)i + 'A'); } } -static irqreturn_t advk_pcie_irq_handler(int irq, void *arg) +static void advk_pcie_irq_handler(struct irq_desc *desc) { - struct advk_pcie *pcie = arg; - u32 status; + struct advk_pcie *pcie = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + u32 val, mask, status; + + chained_irq_enter(chip, desc); + + val = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG); + mask = advk_readl(pcie, HOST_CTRL_INT_MASK_REG); + status = val & ((~mask) & PCIE_IRQ_ALL_MASK); + + if (status & PCIE_IRQ_CORE_INT) { + advk_pcie_handle_int(pcie); - status = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG); - if (!(status & PCIE_IRQ_CORE_INT)) - return IRQ_NONE; + /* Clear interrupt */ + advk_writel(pcie, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG); + } - advk_pcie_handle_int(pcie); + chained_irq_exit(chip, desc); +} - /* Clear interrupt */ - advk_writel(pcie, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG); +static int advk_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct advk_pcie *pcie = dev->bus->sysdata; - return IRQ_HANDLED; + /* + * Emulated root bridge has its own emulated irq chip and irq domain. + * Argument pin is the INTx pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) and + * hwirq for irq_create_mapping() is indexed from zero. + */ + if (pci_is_root_bus(dev->bus)) + return irq_create_mapping(pcie->rp_irq_domain, pin - 1); + else + return of_irq_parse_and_map_pci(dev, slot, pin); } -static void __maybe_unused advk_pcie_disable_phy(struct advk_pcie *pcie) +static void advk_pcie_disable_phy(struct advk_pcie *pcie) { phy_power_off(pcie->phy); phy_exit(pcie->phy); @@ -1522,7 +1671,7 @@ static int advk_pcie_probe(struct platform_device *pdev) struct advk_pcie *pcie; struct pci_host_bridge *bridge; struct resource_entry *entry; - int ret, irq; + int ret; bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct advk_pcie)); if (!bridge) @@ -1608,17 +1757,9 @@ static int advk_pcie_probe(struct platform_device *pdev) if (IS_ERR(pcie->base)) return PTR_ERR(pcie->base); - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = devm_request_irq(dev, irq, advk_pcie_irq_handler, - IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie", - pcie); - if (ret) { - dev_err(dev, "Failed to register interrupt\n"); - return ret; - } + pcie->irq = platform_get_irq(pdev, 0); + if (pcie->irq < 0) + return pcie->irq; pcie->reset_gpio = devm_gpiod_get_from_of_node(dev, dev->of_node, "reset-gpios", 0, @@ -1667,11 +1808,24 @@ static int advk_pcie_probe(struct platform_device *pdev) return ret; } + ret = advk_pcie_init_rp_irq_domain(pcie); + if (ret) { + dev_err(dev, "Failed to initialize irq\n"); + advk_pcie_remove_msi_irq_domain(pcie); + advk_pcie_remove_irq_domain(pcie); + return ret; + } + + irq_set_chained_handler_and_data(pcie->irq, advk_pcie_irq_handler, pcie); + bridge->sysdata = pcie; bridge->ops = &advk_pcie_ops; + bridge->map_irq = advk_pcie_map_irq; ret = pci_host_probe(bridge); if (ret < 0) { + irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); + advk_pcie_remove_rp_irq_domain(pcie); advk_pcie_remove_msi_irq_domain(pcie); advk_pcie_remove_irq_domain(pcie); return ret; @@ -1719,7 +1873,11 @@ static int advk_pcie_remove(struct platform_device *pdev) advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG); advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG); + /* Remove IRQ handler */ + irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); + /* Remove IRQ domains */ + advk_pcie_remove_rp_irq_domain(pcie); advk_pcie_remove_msi_irq_domain(pcie); advk_pcie_remove_irq_domain(pcie); |