aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/pending-5.10/850-0018-PCI-aardvark-Use-separate-INTA-interrupt-for-emulate.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic/pending-5.10/850-0018-PCI-aardvark-Use-separate-INTA-interrupt-for-emulate.patch')
-rw-r--r--target/linux/generic/pending-5.10/850-0018-PCI-aardvark-Use-separate-INTA-interrupt-for-emulate.patch166
1 files changed, 166 insertions, 0 deletions
diff --git a/target/linux/generic/pending-5.10/850-0018-PCI-aardvark-Use-separate-INTA-interrupt-for-emulate.patch b/target/linux/generic/pending-5.10/850-0018-PCI-aardvark-Use-separate-INTA-interrupt-for-emulate.patch
new file mode 100644
index 0000000000..46fb548d92
--- /dev/null
+++ b/target/linux/generic/pending-5.10/850-0018-PCI-aardvark-Use-separate-INTA-interrupt-for-emulate.patch
@@ -0,0 +1,166 @@
+From db305233136f5aa2444a8287a279384e8458c458 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Thu, 1 Apr 2021 20:12:48 +0200
+Subject: [PATCH] PCI: aardvark: Use separate INTA interrupt for emulated root
+ bridge
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Emulated root bridge currently provides only one Legacy INTA interrupt
+which is used for reporting PCIe PME and ERR events and handled by kernel
+PCIe PME and AER drivers.
+
+Aardvark HW reports these PME and ERR events separately, so there is no
+need to mix real INTA interrupt and emulated INTA interrupt for PCIe PME
+and AER drivers.
+
+Register a new advk-RP (as in Root Port) irq chip and a new irq domain
+for emulated root bridge and use this new separate irq domain for
+providing INTA interrupt from emulated root bridge for PME and ERR events.
+
+The real INTA interrupt from real devices is now separate.
+
+A custom map_irq callback function on PCI host bridge structure is used to
+allocate IRQ mapping for emulated root bridge from new irq domain. Original
+callback of_irq_parse_and_map_pci() is used for all other devices as before.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Signed-off-by: Marek Behún <kabel@kernel.org>
+---
+ drivers/pci/controller/pci-aardvark.c | 69 ++++++++++++++++++++++++++-
+ 1 file changed, 67 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
+index 85a632537b70..b6e723c5725c 100644
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -280,6 +280,7 @@ struct advk_pcie {
+ } 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;
+@@ -1441,6 +1442,44 @@ 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;
+@@ -1463,7 +1502,7 @@ static void advk_pcie_handle_pme(struct advk_pcie *pcie)
+ if (!(le16_to_cpu(pcie->bridge.pcie_conf.rootctl) & PCI_EXP_RTCTL_PMEIE))
+ return;
+
+- virq = irq_find_mapping(pcie->irq_domain, 0);
++ virq = irq_find_mapping(pcie->rp_irq_domain, 0);
+ if (generic_handle_irq(virq) == -EINVAL)
+ dev_err_ratelimited(&pcie->pdev->dev, "unhandled PME IRQ\n");
+ }
+@@ -1520,7 +1559,7 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie)
+ * Aardvark HW returns zero for PCI_ERR_ROOT_AER_IRQ, so use
+ * PCIe interrupt 0
+ */
+- virq = irq_find_mapping(pcie->irq_domain, 0);
++ virq = irq_find_mapping(pcie->rp_irq_domain, 0);
+ if (generic_handle_irq(virq) == -EINVAL)
+ dev_err_ratelimited(&pcie->pdev->dev, "unhandled ERR IRQ\n");
+ }
+@@ -1566,6 +1605,21 @@ static void advk_pcie_irq_handler(struct irq_desc *desc)
+ chained_irq_exit(chip, desc);
+ }
+
++static int advk_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
++{
++ struct advk_pcie *pcie = dev->bus->sysdata;
++
++ /*
++ * 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)
+ {
+ phy_power_off(pcie->phy);
+@@ -1769,14 +1823,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;
+@@ -1828,6 +1892,7 @@ static int advk_pcie_remove(struct platform_device *pdev)
+ 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);
+
+--
+2.34.1
+