Merge tag 'io_uring-5.15-2021-09-11' of git://git.kernel.dk/linux-block
[linux-2.6-microblaze.git] / drivers / pci / controller / pcie-rcar-host.c
index 765cf2b..8f31318 100644 (file)
 
 #include <linux/bitops.h>
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/iopoll.h>
 #include <linux/msi.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
@@ -41,6 +43,21 @@ struct rcar_msi {
        int irq2;
 };
 
+#ifdef CONFIG_ARM
+/*
+ * Here we keep a static copy of the remapped PCIe controller address.
+ * This is only used on aarch32 systems, all of which have one single
+ * PCIe controller, to provide quick access to the PCIe controller in
+ * the L1 link state fixup function, called from the ARM fault handler.
+ */
+static void __iomem *pcie_base;
+/*
+ * Static copy of bus clock pointer, so we can check whether the clock
+ * is enabled or not.
+ */
+static struct clk *pcie_bus_clk;
+#endif
+
 /* Structure representing the PCIe interface */
 struct rcar_pcie_host {
        struct rcar_pcie        pcie;
@@ -486,12 +503,10 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data)
 
        while (reg) {
                unsigned int index = find_first_bit(&reg, 32);
-               unsigned int msi_irq;
+               int ret;
 
-               msi_irq = irq_find_mapping(msi->domain->parent, index);
-               if (msi_irq) {
-                       generic_handle_irq(msi_irq);
-               } else {
+               ret = generic_handle_domain_irq(msi->domain->parent, index);
+               if (ret) {
                        /* Unknown MSI, just clear it */
                        dev_dbg(dev, "unexpected MSI\n");
                        rcar_pci_write_reg(pcie, BIT(index), PCIEMSIFR);
@@ -776,6 +791,12 @@ static int rcar_pcie_get_resources(struct rcar_pcie_host *host)
        }
        host->msi.irq2 = i;
 
+#ifdef CONFIG_ARM
+       /* Cache static copy for L1 link state fixup hook on aarch32 */
+       pcie_base = pcie->base;
+       pcie_bus_clk = host->bus_clk;
+#endif
+
        return 0;
 
 err_irq2:
@@ -1031,4 +1052,67 @@ static struct platform_driver rcar_pcie_driver = {
        },
        .probe = rcar_pcie_probe,
 };
+
+#ifdef CONFIG_ARM
+static DEFINE_SPINLOCK(pmsr_lock);
+static int rcar_pcie_aarch32_abort_handler(unsigned long addr,
+               unsigned int fsr, struct pt_regs *regs)
+{
+       unsigned long flags;
+       u32 pmsr, val;
+       int ret = 0;
+
+       spin_lock_irqsave(&pmsr_lock, flags);
+
+       if (!pcie_base || !__clk_is_enabled(pcie_bus_clk)) {
+               ret = 1;
+               goto unlock_exit;
+       }
+
+       pmsr = readl(pcie_base + PMSR);
+
+       /*
+        * Test if the PCIe controller received PM_ENTER_L1 DLLP and
+        * the PCIe controller is not in L1 link state. If true, apply
+        * fix, which will put the controller into L1 link state, from
+        * which it can return to L0s/L0 on its own.
+        */
+       if ((pmsr & PMEL1RX) && ((pmsr & PMSTATE) != PMSTATE_L1)) {
+               writel(L1IATN, pcie_base + PMCTLR);
+               ret = readl_poll_timeout_atomic(pcie_base + PMSR, val,
+                                               val & L1FAEG, 10, 1000);
+               WARN(ret, "Timeout waiting for L1 link state, ret=%d\n", ret);
+               writel(L1FAEG | PMEL1RX, pcie_base + PMSR);
+       }
+
+unlock_exit:
+       spin_unlock_irqrestore(&pmsr_lock, flags);
+       return ret;
+}
+
+static const struct of_device_id rcar_pcie_abort_handler_of_match[] __initconst = {
+       { .compatible = "renesas,pcie-r8a7779" },
+       { .compatible = "renesas,pcie-r8a7790" },
+       { .compatible = "renesas,pcie-r8a7791" },
+       { .compatible = "renesas,pcie-rcar-gen2" },
+       {},
+};
+
+static int __init rcar_pcie_init(void)
+{
+       if (of_find_matching_node(NULL, rcar_pcie_abort_handler_of_match)) {
+#ifdef CONFIG_ARM_LPAE
+               hook_fault_code(17, rcar_pcie_aarch32_abort_handler, SIGBUS, 0,
+                               "asynchronous external abort");
+#else
+               hook_fault_code(22, rcar_pcie_aarch32_abort_handler, SIGBUS, 0,
+                               "imprecise external abort");
+#endif
+       }
+
+       return platform_driver_register(&rcar_pcie_driver);
+}
+device_initcall(rcar_pcie_init);
+#else
 builtin_platform_driver(rcar_pcie_driver);
+#endif