aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.4/950-0448-PCI-brcmstb-Add-MSI-support.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0448-PCI-brcmstb-Add-MSI-support.patch')
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0448-PCI-brcmstb-Add-MSI-support.patch383
1 files changed, 0 insertions, 383 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0448-PCI-brcmstb-Add-MSI-support.patch b/target/linux/bcm27xx/patches-5.4/950-0448-PCI-brcmstb-Add-MSI-support.patch
deleted file mode 100644
index a27259bd19..0000000000
--- a/target/linux/bcm27xx/patches-5.4/950-0448-PCI-brcmstb-Add-MSI-support.patch
+++ /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;