brcm2708: organize kernel patches
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-4.19 / 950-0529-PCI-brcmstb-Add-MSI-capability.patch
diff --git a/target/linux/brcm2708/patches-4.19/950-0529-PCI-brcmstb-Add-MSI-capability.patch b/target/linux/brcm2708/patches-4.19/950-0529-PCI-brcmstb-Add-MSI-capability.patch
deleted file mode 100644 (file)
index b8b61aa..0000000
+++ /dev/null
@@ -1,543 +0,0 @@
-From cd3af4fa73ab25353f0865ebe8e0d2af1fd2a50b Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.org>
-Date: Tue, 19 Feb 2019 22:06:59 +0000
-Subject: [PATCH 529/806] PCI: brcmstb: Add MSI capability
-
-This commit adds MSI to the Broadcom STB PCIe host controller. It does
-not add MSIX since that functionality is not in the HW.  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.
-
-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 <jim2101024@gmail.com>
----
- drivers/pci/controller/pcie-brcmstb.c | 374 ++++++++++++++++++++++++--
- 1 file changed, 353 insertions(+), 21 deletions(-)
-
---- a/drivers/pci/controller/pcie-brcmstb.c
-+++ b/drivers/pci/controller/pcie-brcmstb.c
-@@ -1,6 +1,7 @@
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright (C) 2009 - 2017 Broadcom */
-+#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>
-@@ -47,6 +50,9 @@
- #define PCIE_MISC_RC_BAR2_CONFIG_LO                   0x4034
- #define PCIE_MISC_RC_BAR2_CONFIG_HI                   0x4038
- #define PCIE_MISC_RC_BAR3_CONFIG_LO                   0x403c
-+#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_PCIE_CTRL                           0x4064
- #define PCIE_MISC_PCIE_STATUS                         0x4068
- #define PCIE_MISC_REVISION                            0x406c
-@@ -55,6 +61,7 @@
- #define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI                0x4084
- #define PCIE_MISC_HARD_PCIE_HARD_DEBUG                        0x4204
- #define PCIE_INTR2_CPU_BASE                           0x4300
-+#define PCIE_MSI_INTR2_BASE                           0x4500
- /*
-  * Broadcom Settop Box PCIe Register Field shift and mask info. The
-@@ -115,6 +122,8 @@
- #define BRCM_NUM_PCIE_OUT_WINS                0x4
- #define BRCM_MAX_SCB                  0x4
-+#define BRCM_INT_PCI_MSI_NR           32
-+#define BRCM_PCIE_HW_REV_33           0x0303
- #define BRCM_MSI_TARGET_ADDR_LT_4GB   0x0fffffffcULL
- #define BRCM_MSI_TARGET_ADDR_GT_4GB   0xffffffffcULL
-@@ -203,6 +212,33 @@ struct brcm_window {
-       dma_addr_t size;
- };
-+struct brcm_msi {
-+      struct device           *dev;
-+      void __iomem            *base;
-+      struct device_node      *dn;
-+      struct irq_domain       *msi_domain;
-+      struct irq_domain       *inner_domain;
-+      struct mutex            lock; /* guards the alloc/free operations */
-+      u64                     target_addr;
-+      int                     irq;
-+
-+      /* intr_base is the base pointer for interrupt status/set/clr regs */
-+      void __iomem            *intr_base;
-+
-+      /* intr_legacy_mask indicates how many bits are MSI interrupts */
-+      u32                     intr_legacy_mask;
-+
-+      /*
-+       * intr_legacy_offset indicates bit position of MSI_01. It is
-+       * to map the register bit position to a hwirq that starts at 0.
-+       */
-+      u32                     intr_legacy_offset;
-+
-+      /* used indicates which MSI interrupts have been alloc'd */
-+      unsigned long           used;
-+      unsigned int            rev;
-+};
-+
- /* Internal PCIe Host Controller Information.*/
- struct brcm_pcie {
-       struct device           *dev;
-@@ -217,7 +253,10 @@ struct brcm_pcie {
-       int                     num_out_wins;
-       bool                    ssc;
-       int                     gen;
-+      u64                     msi_target_addr;
-       struct brcm_window      out_wins[BRCM_NUM_PCIE_OUT_WINS];
-+      struct brcm_msi         *msi;
-+      bool                    msi_internal;
-       unsigned int            rev;
-       const int               *reg_offsets;
-       const int               *reg_field_info;
-@@ -225,9 +264,9 @@ struct brcm_pcie {
- };
- struct pcie_cfg_data {
--      const int *reg_field_info;
--      const int *offsets;
--      const enum pcie_type type;
-+      const int               *reg_field_info;
-+      const int               *offsets;
-+      const enum pcie_type    type;
- };
- static const int pcie_reg_field_info[] = {
-@@ -828,6 +867,267 @@ static void brcm_pcie_set_outbound_win(s
-       }
- }
-+static struct irq_chip brcm_msi_irq_chip = {
-+      .name = "Brcm_MSI",
-+      .irq_mask = pci_msi_mask_irq,
-+      .irq_unmask = pci_msi_unmask_irq,
-+};
-+
-+static struct msi_domain_info brcm_msi_domain_info = {
-+      .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
-+                 MSI_FLAG_PCI_MSIX),
-+      .chip   = &brcm_msi_irq_chip,
-+};
-+
-+static void brcm_pcie_msi_isr(struct irq_desc *desc)
-+{
-+      struct irq_chip *chip = irq_desc_get_chip(desc);
-+      struct brcm_msi *msi;
-+      unsigned long status, virq;
-+      u32 mask, bit, hwirq;
-+      struct device *dev;
-+
-+      chained_irq_enter(chip, desc);
-+      msi = irq_desc_get_handler_data(desc);
-+      mask = msi->intr_legacy_mask;
-+      dev = msi->dev;
-+
-+      while ((status = bcm_readl(msi->intr_base + STATUS) & mask)) {
-+              for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR) {
-+                      /* clear the interrupt */
-+                      bcm_writel(1 << bit, msi->intr_base + CLR);
-+
-+                      /* Account for legacy interrupt offset */
-+                      hwirq = bit - msi->intr_legacy_offset;
-+
-+                      virq = irq_find_mapping(msi->inner_domain, hwirq);
-+                      if (virq) {
-+                              if (msi->used & (1 << hwirq))
-+                                      generic_handle_irq(virq);
-+                              else
-+                                      dev_info(dev, "unhandled MSI %d\n",
-+                                               hwirq);
-+                      } else {
-+                              /* Unknown MSI, just clear it */
-+                              dev_dbg(dev, "unexpected MSI\n");
-+                      }
-+              }
-+      }
-+      chained_irq_exit(chip, desc);
-+}
-+
-+static void brcm_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
-+{
-+      struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
-+      u32 temp;
-+
-+      msg->address_lo = lower_32_bits(msi->target_addr);
-+      msg->address_hi = upper_32_bits(msi->target_addr);
-+      temp = bcm_readl(msi->base + PCIE_MISC_MSI_DATA_CONFIG);
-+      msg->data = ((temp >> 16) & (temp & 0xffff)) | data->hwirq;
-+}
-+
-+static int brcm_msi_set_affinity(struct irq_data *irq_data,
-+                               const struct cpumask *mask, bool force)
-+{
-+      return -EINVAL;
-+}
-+
-+static struct irq_chip brcm_msi_bottom_irq_chip = {
-+      .name                   = "Brcm_MSI",
-+      .irq_compose_msi_msg    = brcm_compose_msi_msg,
-+      .irq_set_affinity       = brcm_msi_set_affinity,
-+};
-+
-+static int brcm_msi_alloc(struct brcm_msi *msi)
-+{
-+      int bit, hwirq;
-+
-+      mutex_lock(&msi->lock);
-+      bit = ~msi->used ? ffz(msi->used) : -1;
-+
-+      if (bit >= 0 && bit < BRCM_INT_PCI_MSI_NR) {
-+              msi->used |= (1 << bit);
-+              hwirq = bit - msi->intr_legacy_offset;
-+      } else {
-+              hwirq = -ENOSPC;
-+      }
-+
-+      mutex_unlock(&msi->lock);
-+      return hwirq;
-+}
-+
-+static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq)
-+{
-+      mutex_lock(&msi->lock);
-+      msi->used &= ~(1 << (hwirq + msi->intr_legacy_offset));
-+      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_simple_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 void brcm_msi_set_regs(struct brcm_msi *msi)
-+{
-+      u32 data_val, msi_lo, msi_hi;
-+
-+      if (msi->rev >= BRCM_PCIE_HW_REV_33) {
-+              /*
-+               * ffe0 -- least sig 5 bits are 0 indicating 32 msgs
-+               * 6540 -- this is our arbitrary unique data value
-+               */
-+              data_val = 0xffe06540;
-+      } else {
-+              /*
-+               * fff8 -- least sig 3 bits are 0 indicating 8 msgs
-+               * 6540 -- this is our arbitrary unique data value
-+               */
-+              data_val = 0xfff86540;
-+      }
-+
-+      /*
-+       * Make sure we are not masking MSIs.  Note that MSIs can be masked,
-+       * but that occurs on the PCIe EP device
-+       */
-+      bcm_writel(0xffffffff & msi->intr_legacy_mask,
-+                 msi->intr_base + MASK_CLR);
-+
-+      msi_lo = lower_32_bits(msi->target_addr);
-+      msi_hi = upper_32_bits(msi->target_addr);
-+      /*
-+       * The 0 bit of PCIE_MISC_MSI_BAR_CONFIG_LO is repurposed to MSI
-+       * enable, which we set to 1.
-+       */
-+      bcm_writel(msi_lo | 1, msi->base + PCIE_MISC_MSI_BAR_CONFIG_LO);
-+      bcm_writel(msi_hi, msi->base + PCIE_MISC_MSI_BAR_CONFIG_HI);
-+      bcm_writel(data_val, msi->base + PCIE_MISC_MSI_DATA_CONFIG);
-+}
-+
-+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->dn);
-+      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 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 intr\n");
-+              return -ENODEV;
-+      }
-+
-+      msi = devm_kzalloc(dev, sizeof(struct brcm_msi), GFP_KERNEL);
-+      if (!msi)
-+              return -ENOMEM;
-+
-+      msi->dev = dev;
-+      msi->base = pcie->base;
-+      msi->rev =  pcie->rev;
-+      msi->dn = pcie->dn;
-+      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);
-+
-+      if (msi->rev >= BRCM_PCIE_HW_REV_33) {
-+              msi->intr_base = msi->base + PCIE_MSI_INTR2_BASE;
-+              /*
-+               * This version of PCIe hw has only 32 intr bits
-+               * starting at bit position 0.
-+               */
-+              msi->intr_legacy_mask = 0xffffffff;
-+              msi->intr_legacy_offset = 0x0;
-+              msi->used = 0x0;
-+
-+      } else {
-+              msi->intr_base = msi->base + PCIE_INTR2_CPU_BASE;
-+              /*
-+               * This version of PCIe hw has only 8 intr bits starting
-+               * at bit position 24.
-+               */
-+              msi->intr_legacy_mask = 0xff000000;
-+              msi->intr_legacy_offset = 24;
-+              msi->used = 0x00ffffff;
-+      }
-+
-+      brcm_msi_set_regs(msi);
-+      pcie->msi = msi;
-+
-+      return 0;
-+}
-+
- /* Configuration space read/write support */
- static int cfg_index(int busnr, int devfn, int reg)
- {
-@@ -1072,6 +1372,7 @@ static int brcm_pcie_setup(struct brcm_p
-       u16 nlw, cls, lnksta;
-       bool ssc_good = false;
-       struct device *dev = pcie->dev;
-+      u64 msi_target_addr;
-       /* Reset the bridge */
-       brcm_pcie_bridge_sw_init_set(pcie, 1);
-@@ -1116,27 +1417,24 @@ static int brcm_pcie_setup(struct brcm_p
-        * The PCIe host controller by design must set the inbound
-        * viewport to be a contiguous arrangement of all of the
-        * system's memory.  In addition, its size mut be a power of
--       * two.  To further complicate matters, the viewport must
--       * start on a pcie-address that is aligned on a multiple of its
--       * size.  If a portion of the viewport does not represent
--       * system memory -- e.g. 3GB of memory requires a 4GB viewport
--       * -- we can map the outbound memory in or after 3GB and even
--       * though the viewport will overlap the outbound memory the
--       * controller will know to send outbound memory downstream and
--       * everything else upstream.
-+       * two.  Further, the MSI target address must NOT be placed
-+       * inside this region, as the decoding logic will consider its
-+       * address to be inbound memory traffic.  To further
-+       * complicate matters, the viewport must start on a
-+       * pcie-address that is aligned on a multiple of its size.
-+       * If a portion of the viewport does not represent system
-+       * memory -- e.g. 3GB of memory requires a 4GB viewport --
-+       * we can map the outbound memory in or after 3GB and even
-+       * though the viewport will overlap the outbound memory
-+       * the controller will know to send outbound memory downstream
-+       * and everything else upstream.
-        */
-       rc_bar2_size = roundup_pow_of_two_64(total_mem_size);
--      /*
--       * Set simple configuration based on memory sizes
--       * only.  We always start the viewport at address 0.
--       */
--      rc_bar2_offset = 0;
--
-       if (dma_ranges) {
-               /*
-                * The best-case scenario is to place the inbound
--               * region in the first 4GB of pci-space, as some
-+               * region in the first 4GB of pcie-space, as some
-                * legacy devices can only address 32bits.
-                * We would also like to put the MSI under 4GB
-                * as well, since some devices require a 32bit
-@@ -1145,6 +1443,14 @@ static int brcm_pcie_setup(struct brcm_p
-               if (total_mem_size <= 0xc0000000ULL &&
-                   rc_bar2_size <= 0x100000000ULL) {
-                       rc_bar2_offset = 0;
-+                      /* If the viewport is less then 4GB we can fit
-+                       * the MSI target address under 4GB. Otherwise
-+                       * put it right below 64GB.
-+                       */
-+                      msi_target_addr =
-+                              (rc_bar2_size == 0x100000000ULL)
-+                              ? BRCM_MSI_TARGET_ADDR_GT_4GB
-+                              : BRCM_MSI_TARGET_ADDR_LT_4GB;
-               } else {
-                       /*
-                        * The system memory is 4GB or larger so we
-@@ -1154,8 +1460,12 @@ static int brcm_pcie_setup(struct brcm_p
-                        * start it at the 1x multiple of its size
-                        */
-                       rc_bar2_offset = rc_bar2_size;
--              }
-+                      /* Since we are starting the viewport at 4GB or
-+                       * higher, put the MSI target address below 4GB
-+                       */
-+                      msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB;
-+              }
-       } else {
-               /*
-                * Set simple configuration based on memory sizes
-@@ -1163,7 +1473,12 @@ static int brcm_pcie_setup(struct brcm_p
-                * and set the MSI target address accordingly.
-                */
-               rc_bar2_offset = 0;
-+
-+              msi_target_addr = (rc_bar2_size >= 0x100000000ULL)
-+                      ? BRCM_MSI_TARGET_ADDR_GT_4GB
-+                      : BRCM_MSI_TARGET_ADDR_LT_4GB;
-       }
-+      pcie->msi_target_addr = msi_target_addr;
-       tmp = lower_32_bits(rc_bar2_offset);
-       tmp = INSERT_FIELD(tmp, PCIE_MISC_RC_BAR2_CONFIG_LO, SIZE,
-@@ -1333,6 +1648,9 @@ static int brcm_pcie_resume(struct devic
-       if (ret)
-               return ret;
-+      if (pcie->msi && pcie->msi_internal)
-+              brcm_msi_set_regs(pcie->msi);
-+
-       pcie->suspended = false;
-       return 0;
-@@ -1340,6 +1658,7 @@ static int brcm_pcie_resume(struct devic
- static void _brcm_pcie_remove(struct brcm_pcie *pcie)
- {
-+      brcm_msi_remove(pcie);
-       turn_off(pcie);
-       clk_disable_unprepare(pcie->clk);
-       clk_put(pcie->clk);
-@@ -1368,7 +1687,7 @@ MODULE_DEVICE_TABLE(of, brcm_pcie_match)
- static int brcm_pcie_probe(struct platform_device *pdev)
- {
--      struct device_node *dn = pdev->dev.of_node;
-+      struct device_node *dn = pdev->dev.of_node, *msi_dn;
-       const struct of_device_id *of_id;
-       const struct pcie_cfg_data *data;
-       int ret;
-@@ -1448,6 +1767,20 @@ static int brcm_pcie_probe(struct platfo
-       if (ret)
-               goto fail;
-+      msi_dn = of_parse_phandle(pcie->dn, "msi-parent", 0);
-+      /* Use the internal MSI if no msi-parent property */
-+      if (!msi_dn)
-+              msi_dn = pcie->dn;
-+
-+      if (pci_msi_enabled() && msi_dn == pcie->dn) {
-+              ret = brcm_pcie_enable_msi(pcie);
-+              if (ret)
-+                      dev_err(pcie->dev,
-+                              "probe of internal MSI failed: %d)", ret);
-+              else
-+                      pcie->msi_internal = true;
-+      }
-+
-       list_splice_init(&pcie->resources, &bridge->windows);
-       bridge->dev.parent = &pdev->dev;
-       bridge->busnr = 0;
-@@ -1470,7 +1803,6 @@ static int brcm_pcie_probe(struct platfo
-       pcie->root_bus = bridge->bus;
-       return 0;
--
- fail:
-       _brcm_pcie_remove(pcie);
-       return ret;