bcm27xx: update patches from RPi foundation
[openwrt/staging/luka.git] / target / linux / bcm27xx / patches-5.4 / 950-0459-PCI-brcmstb-Add-MSI-support.patch
diff --git a/target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-MSI-support.patch b/target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-MSI-support.patch
deleted file mode 100644 (file)
index a27259b..0000000
+++ /dev/null
@@ -1,383 +0,0 @@
-From 1a90ecdfae1c0cf1b242276f6f0e3d98b5877f14 Mon Sep 17 00:00:00 2001
-From: Jim Quinlan <james.quinlan@broadcom.com>
-Date: Mon, 16 Dec 2019 12:01:10 +0100
-Subject: [PATCH] PCI: brcmstb: Add MSI support
-
-commit 40ca1bf580ef24df30702032ba5e40dfdcaa200b upstream.
-
-This adds MSI support to the Broadcom STB PCIe host controller. The MSI
-controller is physically located within the PCIe block, however, there
-is no reason why the MSI controller could not be moved elsewhere in the
-future. MSIX is not supported by the HW.
-
-Since the internal Brcmstb MSI controller is intertwined with the PCIe
-controller, it is not its own platform device but rather part of the
-PCIe platform device.
-
-Signed-off-by: Jim Quinlan <james.quinlan@broadcom.com>
-Co-developed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-Reviewed-by: Marc Zyngier <maz@kernel.org>
-Reviewed-by: Andrew Murray <andrew.murray@arm.com>
----
- drivers/pci/controller/Kconfig        |   1 +
- drivers/pci/controller/pcie-brcmstb.c | 262 +++++++++++++++++++++++++-
- 2 files changed, 262 insertions(+), 1 deletion(-)
-
---- a/drivers/pci/controller/Kconfig
-+++ b/drivers/pci/controller/Kconfig
-@@ -285,6 +285,7 @@ config PCIE_BRCMSTB
-       tristate "Broadcom Brcmstb PCIe host controller"
-       depends on ARCH_BCM2835 || COMPILE_TEST
-       depends on OF
-+      depends on PCI_MSI_IRQ_DOMAIN
-       help
-         Say Y here to enable PCIe host controller support for
-         Broadcom STB based SoCs, like the Raspberry Pi 4.
---- a/drivers/pci/controller/pcie-brcmstb.c
-+++ b/drivers/pci/controller/pcie-brcmstb.c
-@@ -2,6 +2,7 @@
- /* Copyright (C) 2009 - 2019 Broadcom */
- #include <linux/bitfield.h>
-+#include <linux/bitops.h>
- #include <linux/clk.h>
- #include <linux/compiler.h>
- #include <linux/delay.h>
-@@ -9,11 +10,13 @@
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/ioport.h>
-+#include <linux/irqchip/chained_irq.h>
- #include <linux/irqdomain.h>
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/log2.h>
- #include <linux/module.h>
-+#include <linux/msi.h>
- #include <linux/of_address.h>
- #include <linux/of_irq.h>
- #include <linux/of_pci.h>
-@@ -67,6 +70,12 @@
- #define PCIE_MISC_RC_BAR3_CONFIG_LO                   0x403c
- #define  PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK                0x1f
-+#define PCIE_MISC_MSI_BAR_CONFIG_LO                   0x4044
-+#define PCIE_MISC_MSI_BAR_CONFIG_HI                   0x4048
-+
-+#define PCIE_MISC_MSI_DATA_CONFIG                     0x404c
-+#define  PCIE_MISC_MSI_DATA_CONFIG_VAL                        0xffe06540
-+
- #define PCIE_MISC_PCIE_CTRL                           0x4064
- #define  PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK    0x1
-@@ -114,6 +123,11 @@
- /* PCIe parameters */
- #define BRCM_NUM_PCIE_OUT_WINS                0x4
-+#define BRCM_INT_PCI_MSI_NR           32
-+
-+/* MSI target adresses */
-+#define BRCM_MSI_TARGET_ADDR_LT_4GB   0x0fffffffcULL
-+#define BRCM_MSI_TARGET_ADDR_GT_4GB   0xffffffffcULL
- /* MDIO registers */
- #define MDIO_PORT0                    0x0
-@@ -135,6 +149,19 @@
- #define SSC_STATUS_SSC_MASK           0x400
- #define SSC_STATUS_PLL_LOCK_MASK      0x800
-+struct brcm_msi {
-+      struct device           *dev;
-+      void __iomem            *base;
-+      struct device_node      *np;
-+      struct irq_domain       *msi_domain;
-+      struct irq_domain       *inner_domain;
-+      struct mutex            lock; /* guards the alloc/free operations */
-+      u64                     target_addr;
-+      int                     irq;
-+      /* used indicates which MSI interrupts have been alloc'd */
-+      unsigned long           used;
-+};
-+
- /* Internal PCIe Host Controller Information.*/
- struct brcm_pcie {
-       struct device           *dev;
-@@ -144,6 +171,8 @@ struct brcm_pcie {
-       struct device_node      *np;
-       bool                    ssc;
-       int                     gen;
-+      u64                     msi_target_addr;
-+      struct brcm_msi         *msi;
- };
- /*
-@@ -309,6 +338,215 @@ static void brcm_pcie_set_outbound_win(s
-       writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
- }
-+static struct irq_chip brcm_msi_irq_chip = {
-+      .name            = "BRCM STB PCIe MSI",
-+      .irq_ack         = irq_chip_ack_parent,
-+      .irq_mask        = pci_msi_mask_irq,
-+      .irq_unmask      = pci_msi_unmask_irq,
-+};
-+
-+static struct msi_domain_info brcm_msi_domain_info = {
-+      /* Multi MSI is supported by the controller, but not by this driver */
-+      .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
-+      .chip   = &brcm_msi_irq_chip,
-+};
-+
-+static void brcm_pcie_msi_isr(struct irq_desc *desc)
-+{
-+      struct irq_chip *chip = irq_desc_get_chip(desc);
-+      unsigned long status, virq;
-+      struct brcm_msi *msi;
-+      struct device *dev;
-+      u32 bit;
-+
-+      chained_irq_enter(chip, desc);
-+      msi = irq_desc_get_handler_data(desc);
-+      dev = msi->dev;
-+
-+      status = readl(msi->base + PCIE_MSI_INTR2_STATUS);
-+      for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR) {
-+              virq = irq_find_mapping(msi->inner_domain, bit);
-+              if (virq)
-+                      generic_handle_irq(virq);
-+              else
-+                      dev_dbg(dev, "unexpected MSI\n");
-+      }
-+
-+      chained_irq_exit(chip, desc);
-+}
-+
-+static void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
-+{
-+      struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
-+
-+      msg->address_lo = lower_32_bits(msi->target_addr);
-+      msg->address_hi = upper_32_bits(msi->target_addr);
-+      msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL) | data->hwirq;
-+}
-+
-+static int brcm_msi_set_affinity(struct irq_data *irq_data,
-+                               const struct cpumask *mask, bool force)
-+{
-+      return -EINVAL;
-+}
-+
-+static void brcm_msi_ack_irq(struct irq_data *data)
-+{
-+      struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
-+
-+      writel(1 << data->hwirq, msi->base + PCIE_MSI_INTR2_CLR);
-+}
-+
-+
-+static struct irq_chip brcm_msi_bottom_irq_chip = {
-+      .name                   = "BRCM STB MSI",
-+      .irq_compose_msi_msg    = brcm_msi_compose_msi_msg,
-+      .irq_set_affinity       = brcm_msi_set_affinity,
-+      .irq_ack                = brcm_msi_ack_irq,
-+};
-+
-+static int brcm_msi_alloc(struct brcm_msi *msi)
-+{
-+      int hwirq;
-+
-+      mutex_lock(&msi->lock);
-+      hwirq = bitmap_find_free_region(&msi->used, BRCM_INT_PCI_MSI_NR, 0);
-+      mutex_unlock(&msi->lock);
-+
-+      return hwirq;
-+}
-+
-+static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq)
-+{
-+      mutex_lock(&msi->lock);
-+      bitmap_release_region(&msi->used, hwirq, 0);
-+      mutex_unlock(&msi->lock);
-+}
-+
-+static int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
-+                               unsigned int nr_irqs, void *args)
-+{
-+      struct brcm_msi *msi = domain->host_data;
-+      int hwirq;
-+
-+      hwirq = brcm_msi_alloc(msi);
-+
-+      if (hwirq < 0)
-+              return hwirq;
-+
-+      irq_domain_set_info(domain, virq, (irq_hw_number_t)hwirq,
-+                          &brcm_msi_bottom_irq_chip, domain->host_data,
-+                          handle_edge_irq, NULL, NULL);
-+      return 0;
-+}
-+
-+static void brcm_irq_domain_free(struct irq_domain *domain,
-+                               unsigned int virq, unsigned int nr_irqs)
-+{
-+      struct irq_data *d = irq_domain_get_irq_data(domain, virq);
-+      struct brcm_msi *msi = irq_data_get_irq_chip_data(d);
-+
-+      brcm_msi_free(msi, d->hwirq);
-+}
-+
-+static const struct irq_domain_ops msi_domain_ops = {
-+      .alloc  = brcm_irq_domain_alloc,
-+      .free   = brcm_irq_domain_free,
-+};
-+
-+static int brcm_allocate_domains(struct brcm_msi *msi)
-+{
-+      struct fwnode_handle *fwnode = of_node_to_fwnode(msi->np);
-+      struct device *dev = msi->dev;
-+
-+      msi->inner_domain = irq_domain_add_linear(NULL, BRCM_INT_PCI_MSI_NR,
-+                                                &msi_domain_ops, msi);
-+      if (!msi->inner_domain) {
-+              dev_err(dev, "failed to create IRQ domain\n");
-+              return -ENOMEM;
-+      }
-+
-+      msi->msi_domain = pci_msi_create_irq_domain(fwnode,
-+                                                  &brcm_msi_domain_info,
-+                                                  msi->inner_domain);
-+      if (!msi->msi_domain) {
-+              dev_err(dev, "failed to create MSI domain\n");
-+              irq_domain_remove(msi->inner_domain);
-+              return -ENOMEM;
-+      }
-+
-+      return 0;
-+}
-+
-+static void brcm_free_domains(struct brcm_msi *msi)
-+{
-+      irq_domain_remove(msi->msi_domain);
-+      irq_domain_remove(msi->inner_domain);
-+}
-+
-+static void brcm_msi_remove(struct brcm_pcie *pcie)
-+{
-+      struct brcm_msi *msi = pcie->msi;
-+
-+      if (!msi)
-+              return;
-+      irq_set_chained_handler(msi->irq, NULL);
-+      irq_set_handler_data(msi->irq, NULL);
-+      brcm_free_domains(msi);
-+}
-+
-+static void brcm_msi_set_regs(struct brcm_msi *msi)
-+{
-+      writel(0xffffffff, msi->base + PCIE_MSI_INTR2_MASK_CLR);
-+
-+      /*
-+       * The 0 bit of PCIE_MISC_MSI_BAR_CONFIG_LO is repurposed to MSI
-+       * enable, which we set to 1.
-+       */
-+      writel(lower_32_bits(msi->target_addr) | 0x1,
-+             msi->base + PCIE_MISC_MSI_BAR_CONFIG_LO);
-+      writel(upper_32_bits(msi->target_addr),
-+             msi->base + PCIE_MISC_MSI_BAR_CONFIG_HI);
-+
-+      writel(PCIE_MISC_MSI_DATA_CONFIG_VAL,
-+             msi->base + PCIE_MISC_MSI_DATA_CONFIG);
-+}
-+
-+static int brcm_pcie_enable_msi(struct brcm_pcie *pcie)
-+{
-+      struct brcm_msi *msi;
-+      int irq, ret;
-+      struct device *dev = pcie->dev;
-+
-+      irq = irq_of_parse_and_map(dev->of_node, 1);
-+      if (irq <= 0) {
-+              dev_err(dev, "cannot map MSI interrupt\n");
-+              return -ENODEV;
-+      }
-+
-+      msi = devm_kzalloc(dev, sizeof(struct brcm_msi), GFP_KERNEL);
-+      if (!msi)
-+              return -ENOMEM;
-+
-+      mutex_init(&msi->lock);
-+      msi->dev = dev;
-+      msi->base = pcie->base;
-+      msi->np = pcie->np;
-+      msi->target_addr = pcie->msi_target_addr;
-+      msi->irq = irq;
-+
-+      ret = brcm_allocate_domains(msi);
-+      if (ret)
-+              return ret;
-+
-+      irq_set_chained_handler_and_data(msi->irq, brcm_pcie_msi_isr, msi);
-+
-+      brcm_msi_set_regs(msi);
-+      pcie->msi = msi;
-+
-+      return 0;
-+}
-+
- /* The controller is capable of serving in both RC and EP roles */
- static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
- {
-@@ -497,6 +735,18 @@ static int brcm_pcie_setup(struct brcm_p
-                         PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK);
-       writel(tmp, base + PCIE_MISC_MISC_CTRL);
-+      /*
-+       * We ideally want the MSI target address to be located in the 32bit
-+       * addressable memory area. Some devices might depend on it. This is
-+       * possible either when the inbound window is located above the lower
-+       * 4GB or when the inbound area is smaller than 4GB (taking into
-+       * account the rounding-up we're forced to perform).
-+       */
-+      if (rc_bar2_offset >= SZ_4G || (rc_bar2_size + rc_bar2_offset) < SZ_4G)
-+              pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB;
-+      else
-+              pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB;
-+
-       /* disable the PCIe->GISB memory window (RC_BAR1) */
-       tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO);
-       tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK;
-@@ -646,6 +896,7 @@ static void brcm_pcie_turn_off(struct br
- static void __brcm_pcie_remove(struct brcm_pcie *pcie)
- {
-+      brcm_msi_remove(pcie);
-       brcm_pcie_turn_off(pcie);
-       clk_disable_unprepare(pcie->clk);
-       clk_put(pcie->clk);
-@@ -664,7 +915,7 @@ static int brcm_pcie_remove(struct platf
- static int brcm_pcie_probe(struct platform_device *pdev)
- {
--      struct device_node *np = pdev->dev.of_node;
-+      struct device_node *np = pdev->dev.of_node, *msi_np;
-       struct pci_host_bridge *bridge;
-       struct brcm_pcie *pcie;
-       struct pci_bus *child;
-@@ -708,6 +959,15 @@ static int brcm_pcie_probe(struct platfo
-       if (ret)
-               goto fail;
-+      msi_np = of_parse_phandle(pcie->np, "msi-parent", 0);
-+      if (pci_msi_enabled() && msi_np == pcie->np) {
-+              ret = brcm_pcie_enable_msi(pcie);
-+              if (ret) {
-+                      dev_err(pcie->dev, "probe of internal MSI failed");
-+                      goto fail;
-+              }
-+      }
-+
-       bridge->dev.parent = &pdev->dev;
-       bridge->busnr = 0;
-       bridge->ops = &brcm_pcie_ops;