Skip to content

Commit 40ca1bf

Browse files
jamesequinlanLorenzo Pieralisi
authored andcommitted
PCI: brcmstb: Add MSI support
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 <[email protected]> Co-developed-by: Nicolas Saenz Julienne <[email protected]> Signed-off-by: Nicolas Saenz Julienne <[email protected]> Signed-off-by: Lorenzo Pieralisi <[email protected]> Reviewed-by: Marc Zyngier <[email protected]> Reviewed-by: Andrew Murray <[email protected]>
1 parent c045213 commit 40ca1bf

File tree

2 files changed

+262
-1
lines changed

2 files changed

+262
-1
lines changed

drivers/pci/controller/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ config PCIE_BRCMSTB
257257
tristate "Broadcom Brcmstb PCIe host controller"
258258
depends on ARCH_BCM2835 || COMPILE_TEST
259259
depends on OF
260+
depends on PCI_MSI_IRQ_DOMAIN
260261
help
261262
Say Y here to enable PCIe host controller support for
262263
Broadcom STB based SoCs, like the Raspberry Pi 4.

drivers/pci/controller/pcie-brcmstb.c

Lines changed: 261 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,21 @@
22
/* Copyright (C) 2009 - 2019 Broadcom */
33

44
#include <linux/bitfield.h>
5+
#include <linux/bitops.h>
56
#include <linux/clk.h>
67
#include <linux/compiler.h>
78
#include <linux/delay.h>
89
#include <linux/init.h>
910
#include <linux/interrupt.h>
1011
#include <linux/io.h>
1112
#include <linux/ioport.h>
13+
#include <linux/irqchip/chained_irq.h>
1214
#include <linux/irqdomain.h>
1315
#include <linux/kernel.h>
1416
#include <linux/list.h>
1517
#include <linux/log2.h>
1618
#include <linux/module.h>
19+
#include <linux/msi.h>
1720
#include <linux/of_address.h>
1821
#include <linux/of_irq.h>
1922
#include <linux/of_pci.h>
@@ -67,6 +70,12 @@
6770
#define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c
6871
#define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f
6972

73+
#define PCIE_MISC_MSI_BAR_CONFIG_LO 0x4044
74+
#define PCIE_MISC_MSI_BAR_CONFIG_HI 0x4048
75+
76+
#define PCIE_MISC_MSI_DATA_CONFIG 0x404c
77+
#define PCIE_MISC_MSI_DATA_CONFIG_VAL 0xffe06540
78+
7079
#define PCIE_MISC_PCIE_CTRL 0x4064
7180
#define PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK 0x1
7281

@@ -114,6 +123,11 @@
114123

115124
/* PCIe parameters */
116125
#define BRCM_NUM_PCIE_OUT_WINS 0x4
126+
#define BRCM_INT_PCI_MSI_NR 32
127+
128+
/* MSI target adresses */
129+
#define BRCM_MSI_TARGET_ADDR_LT_4GB 0x0fffffffcULL
130+
#define BRCM_MSI_TARGET_ADDR_GT_4GB 0xffffffffcULL
117131

118132
/* MDIO registers */
119133
#define MDIO_PORT0 0x0
@@ -135,6 +149,19 @@
135149
#define SSC_STATUS_SSC_MASK 0x400
136150
#define SSC_STATUS_PLL_LOCK_MASK 0x800
137151

152+
struct brcm_msi {
153+
struct device *dev;
154+
void __iomem *base;
155+
struct device_node *np;
156+
struct irq_domain *msi_domain;
157+
struct irq_domain *inner_domain;
158+
struct mutex lock; /* guards the alloc/free operations */
159+
u64 target_addr;
160+
int irq;
161+
/* used indicates which MSI interrupts have been alloc'd */
162+
unsigned long used;
163+
};
164+
138165
/* Internal PCIe Host Controller Information.*/
139166
struct brcm_pcie {
140167
struct device *dev;
@@ -144,6 +171,8 @@ struct brcm_pcie {
144171
struct device_node *np;
145172
bool ssc;
146173
int gen;
174+
u64 msi_target_addr;
175+
struct brcm_msi *msi;
147176
};
148177

149178
/*
@@ -309,6 +338,215 @@ static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie,
309338
writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
310339
}
311340

341+
static struct irq_chip brcm_msi_irq_chip = {
342+
.name = "BRCM STB PCIe MSI",
343+
.irq_ack = irq_chip_ack_parent,
344+
.irq_mask = pci_msi_mask_irq,
345+
.irq_unmask = pci_msi_unmask_irq,
346+
};
347+
348+
static struct msi_domain_info brcm_msi_domain_info = {
349+
/* Multi MSI is supported by the controller, but not by this driver */
350+
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
351+
.chip = &brcm_msi_irq_chip,
352+
};
353+
354+
static void brcm_pcie_msi_isr(struct irq_desc *desc)
355+
{
356+
struct irq_chip *chip = irq_desc_get_chip(desc);
357+
unsigned long status, virq;
358+
struct brcm_msi *msi;
359+
struct device *dev;
360+
u32 bit;
361+
362+
chained_irq_enter(chip, desc);
363+
msi = irq_desc_get_handler_data(desc);
364+
dev = msi->dev;
365+
366+
status = readl(msi->base + PCIE_MSI_INTR2_STATUS);
367+
for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR) {
368+
virq = irq_find_mapping(msi->inner_domain, bit);
369+
if (virq)
370+
generic_handle_irq(virq);
371+
else
372+
dev_dbg(dev, "unexpected MSI\n");
373+
}
374+
375+
chained_irq_exit(chip, desc);
376+
}
377+
378+
static void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
379+
{
380+
struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
381+
382+
msg->address_lo = lower_32_bits(msi->target_addr);
383+
msg->address_hi = upper_32_bits(msi->target_addr);
384+
msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL) | data->hwirq;
385+
}
386+
387+
static int brcm_msi_set_affinity(struct irq_data *irq_data,
388+
const struct cpumask *mask, bool force)
389+
{
390+
return -EINVAL;
391+
}
392+
393+
static void brcm_msi_ack_irq(struct irq_data *data)
394+
{
395+
struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
396+
397+
writel(1 << data->hwirq, msi->base + PCIE_MSI_INTR2_CLR);
398+
}
399+
400+
401+
static struct irq_chip brcm_msi_bottom_irq_chip = {
402+
.name = "BRCM STB MSI",
403+
.irq_compose_msi_msg = brcm_msi_compose_msi_msg,
404+
.irq_set_affinity = brcm_msi_set_affinity,
405+
.irq_ack = brcm_msi_ack_irq,
406+
};
407+
408+
static int brcm_msi_alloc(struct brcm_msi *msi)
409+
{
410+
int hwirq;
411+
412+
mutex_lock(&msi->lock);
413+
hwirq = bitmap_find_free_region(&msi->used, BRCM_INT_PCI_MSI_NR, 0);
414+
mutex_unlock(&msi->lock);
415+
416+
return hwirq;
417+
}
418+
419+
static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq)
420+
{
421+
mutex_lock(&msi->lock);
422+
bitmap_release_region(&msi->used, hwirq, 0);
423+
mutex_unlock(&msi->lock);
424+
}
425+
426+
static int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
427+
unsigned int nr_irqs, void *args)
428+
{
429+
struct brcm_msi *msi = domain->host_data;
430+
int hwirq;
431+
432+
hwirq = brcm_msi_alloc(msi);
433+
434+
if (hwirq < 0)
435+
return hwirq;
436+
437+
irq_domain_set_info(domain, virq, (irq_hw_number_t)hwirq,
438+
&brcm_msi_bottom_irq_chip, domain->host_data,
439+
handle_edge_irq, NULL, NULL);
440+
return 0;
441+
}
442+
443+
static void brcm_irq_domain_free(struct irq_domain *domain,
444+
unsigned int virq, unsigned int nr_irqs)
445+
{
446+
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
447+
struct brcm_msi *msi = irq_data_get_irq_chip_data(d);
448+
449+
brcm_msi_free(msi, d->hwirq);
450+
}
451+
452+
static const struct irq_domain_ops msi_domain_ops = {
453+
.alloc = brcm_irq_domain_alloc,
454+
.free = brcm_irq_domain_free,
455+
};
456+
457+
static int brcm_allocate_domains(struct brcm_msi *msi)
458+
{
459+
struct fwnode_handle *fwnode = of_node_to_fwnode(msi->np);
460+
struct device *dev = msi->dev;
461+
462+
msi->inner_domain = irq_domain_add_linear(NULL, BRCM_INT_PCI_MSI_NR,
463+
&msi_domain_ops, msi);
464+
if (!msi->inner_domain) {
465+
dev_err(dev, "failed to create IRQ domain\n");
466+
return -ENOMEM;
467+
}
468+
469+
msi->msi_domain = pci_msi_create_irq_domain(fwnode,
470+
&brcm_msi_domain_info,
471+
msi->inner_domain);
472+
if (!msi->msi_domain) {
473+
dev_err(dev, "failed to create MSI domain\n");
474+
irq_domain_remove(msi->inner_domain);
475+
return -ENOMEM;
476+
}
477+
478+
return 0;
479+
}
480+
481+
static void brcm_free_domains(struct brcm_msi *msi)
482+
{
483+
irq_domain_remove(msi->msi_domain);
484+
irq_domain_remove(msi->inner_domain);
485+
}
486+
487+
static void brcm_msi_remove(struct brcm_pcie *pcie)
488+
{
489+
struct brcm_msi *msi = pcie->msi;
490+
491+
if (!msi)
492+
return;
493+
irq_set_chained_handler(msi->irq, NULL);
494+
irq_set_handler_data(msi->irq, NULL);
495+
brcm_free_domains(msi);
496+
}
497+
498+
static void brcm_msi_set_regs(struct brcm_msi *msi)
499+
{
500+
writel(0xffffffff, msi->base + PCIE_MSI_INTR2_MASK_CLR);
501+
502+
/*
503+
* The 0 bit of PCIE_MISC_MSI_BAR_CONFIG_LO is repurposed to MSI
504+
* enable, which we set to 1.
505+
*/
506+
writel(lower_32_bits(msi->target_addr) | 0x1,
507+
msi->base + PCIE_MISC_MSI_BAR_CONFIG_LO);
508+
writel(upper_32_bits(msi->target_addr),
509+
msi->base + PCIE_MISC_MSI_BAR_CONFIG_HI);
510+
511+
writel(PCIE_MISC_MSI_DATA_CONFIG_VAL,
512+
msi->base + PCIE_MISC_MSI_DATA_CONFIG);
513+
}
514+
515+
static int brcm_pcie_enable_msi(struct brcm_pcie *pcie)
516+
{
517+
struct brcm_msi *msi;
518+
int irq, ret;
519+
struct device *dev = pcie->dev;
520+
521+
irq = irq_of_parse_and_map(dev->of_node, 1);
522+
if (irq <= 0) {
523+
dev_err(dev, "cannot map MSI interrupt\n");
524+
return -ENODEV;
525+
}
526+
527+
msi = devm_kzalloc(dev, sizeof(struct brcm_msi), GFP_KERNEL);
528+
if (!msi)
529+
return -ENOMEM;
530+
531+
mutex_init(&msi->lock);
532+
msi->dev = dev;
533+
msi->base = pcie->base;
534+
msi->np = pcie->np;
535+
msi->target_addr = pcie->msi_target_addr;
536+
msi->irq = irq;
537+
538+
ret = brcm_allocate_domains(msi);
539+
if (ret)
540+
return ret;
541+
542+
irq_set_chained_handler_and_data(msi->irq, brcm_pcie_msi_isr, msi);
543+
544+
brcm_msi_set_regs(msi);
545+
pcie->msi = msi;
546+
547+
return 0;
548+
}
549+
312550
/* The controller is capable of serving in both RC and EP roles */
313551
static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
314552
{
@@ -497,6 +735,18 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
497735
PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK);
498736
writel(tmp, base + PCIE_MISC_MISC_CTRL);
499737

738+
/*
739+
* We ideally want the MSI target address to be located in the 32bit
740+
* addressable memory area. Some devices might depend on it. This is
741+
* possible either when the inbound window is located above the lower
742+
* 4GB or when the inbound area is smaller than 4GB (taking into
743+
* account the rounding-up we're forced to perform).
744+
*/
745+
if (rc_bar2_offset >= SZ_4G || (rc_bar2_size + rc_bar2_offset) < SZ_4G)
746+
pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB;
747+
else
748+
pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB;
749+
500750
/* disable the PCIe->GISB memory window (RC_BAR1) */
501751
tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO);
502752
tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK;
@@ -646,6 +896,7 @@ static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
646896

647897
static void __brcm_pcie_remove(struct brcm_pcie *pcie)
648898
{
899+
brcm_msi_remove(pcie);
649900
brcm_pcie_turn_off(pcie);
650901
clk_disable_unprepare(pcie->clk);
651902
clk_put(pcie->clk);
@@ -664,7 +915,7 @@ static int brcm_pcie_remove(struct platform_device *pdev)
664915

665916
static int brcm_pcie_probe(struct platform_device *pdev)
666917
{
667-
struct device_node *np = pdev->dev.of_node;
918+
struct device_node *np = pdev->dev.of_node, *msi_np;
668919
struct pci_host_bridge *bridge;
669920
struct brcm_pcie *pcie;
670921
struct pci_bus *child;
@@ -708,6 +959,15 @@ static int brcm_pcie_probe(struct platform_device *pdev)
708959
if (ret)
709960
goto fail;
710961

962+
msi_np = of_parse_phandle(pcie->np, "msi-parent", 0);
963+
if (pci_msi_enabled() && msi_np == pcie->np) {
964+
ret = brcm_pcie_enable_msi(pcie);
965+
if (ret) {
966+
dev_err(pcie->dev, "probe of internal MSI failed");
967+
goto fail;
968+
}
969+
}
970+
711971
bridge->dev.parent = &pdev->dev;
712972
bridge->busnr = 0;
713973
bridge->ops = &brcm_pcie_ops;

0 commit comments

Comments
 (0)