aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/irq-bcm2835.c102
-rw-r--r--drivers/irqchip/irq-bcm2836.c28
-rw-r--r--drivers/irqchip/irq-brcmstb-l2.c17
3 files changed, 141 insertions, 6 deletions
diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c
index 1e384c870350..cef18106c229 100644
--- a/drivers/irqchip/irq-bcm2835.c
+++ b/drivers/irqchip/irq-bcm2835.c
@@ -40,12 +40,16 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/irqchip.h>
+#include <linux/irqchip/irq-bcm2836.h>
#include <linux/irqdomain.h>
#include <asm/exception.h>
+#ifndef CONFIG_ARM64
+#include <asm/mach/irq.h>
+#endif
/* Put the bank and irq (32 bits) into the hwirq */
-#define MAKE_HWIRQ(b, n) ((b << 5) | (n))
+#define MAKE_HWIRQ(b, n) (((b) << 5) | (n))
#define HWIRQ_BANK(i) (i >> 5)
#define HWIRQ_BIT(i) BIT(i & 0x1f)
@@ -60,11 +64,17 @@
#define BANK0_VALID_MASK (BANK0_HWIRQ_MASK | BANK1_HWIRQ | BANK2_HWIRQ \
| SHORTCUT1_MASK | SHORTCUT2_MASK)
+#undef ARM_LOCAL_GPU_INT_ROUTING
+#define ARM_LOCAL_GPU_INT_ROUTING 0x0c
+
#define REG_FIQ_CONTROL 0x0c
#define FIQ_CONTROL_ENABLE BIT(7)
+#define REG_FIQ_ENABLE FIQ_CONTROL_ENABLE
+#define REG_FIQ_DISABLE 0
#define NR_BANKS 3
#define IRQS_PER_BANK 32
+#define NUMBER_IRQS MAKE_HWIRQ(NR_BANKS, 0)
static const int reg_pending[] __initconst = { 0x00, 0x04, 0x08 };
static const int reg_enable[] __initconst = { 0x18, 0x10, 0x14 };
@@ -82,6 +92,7 @@ struct armctrl_ic {
void __iomem *enable[NR_BANKS];
void __iomem *disable[NR_BANKS];
struct irq_domain *domain;
+ void __iomem *local_base;
};
static struct armctrl_ic intc __read_mostly;
@@ -89,22 +100,77 @@ static void __exception_irq_entry bcm2835_handle_irq(
struct pt_regs *regs);
static void bcm2836_chained_handle_irq(struct irq_desc *desc);
+static inline unsigned int hwirq_to_fiq(unsigned long hwirq)
+{
+ hwirq -= NUMBER_IRQS;
+ /*
+ * The hwirq numbering used in this driver is:
+ * BASE (0-7) GPU1 (32-63) GPU2 (64-95).
+ * This differ from the one used in the FIQ register:
+ * GPU1 (0-31) GPU2 (32-63) BASE (64-71)
+ */
+ if (hwirq >= 32)
+ return hwirq - 32;
+
+ return hwirq + 64;
+}
+
static void armctrl_mask_irq(struct irq_data *d)
{
- writel_relaxed(HWIRQ_BIT(d->hwirq), intc.disable[HWIRQ_BANK(d->hwirq)]);
+ if (d->hwirq >= NUMBER_IRQS)
+ writel_relaxed(REG_FIQ_DISABLE, intc.base + REG_FIQ_CONTROL);
+ else
+ writel_relaxed(HWIRQ_BIT(d->hwirq),
+ intc.disable[HWIRQ_BANK(d->hwirq)]);
}
static void armctrl_unmask_irq(struct irq_data *d)
{
- writel_relaxed(HWIRQ_BIT(d->hwirq), intc.enable[HWIRQ_BANK(d->hwirq)]);
+ if (d->hwirq >= NUMBER_IRQS) {
+ if (num_online_cpus() > 1) {
+ unsigned int data;
+
+ if (!intc.local_base) {
+ pr_err("FIQ is disabled due to missing arm_local_intc\n");
+ return;
+ }
+
+ data = readl_relaxed(intc.local_base +
+ ARM_LOCAL_GPU_INT_ROUTING);
+
+ data &= ~0xc;
+ data |= (1 << 2);
+ writel_relaxed(data,
+ intc.local_base +
+ ARM_LOCAL_GPU_INT_ROUTING);
+ }
+
+ writel_relaxed(REG_FIQ_ENABLE | hwirq_to_fiq(d->hwirq),
+ intc.base + REG_FIQ_CONTROL);
+ } else {
+ writel_relaxed(HWIRQ_BIT(d->hwirq),
+ intc.enable[HWIRQ_BANK(d->hwirq)]);
+ }
+}
+
+#ifdef CONFIG_ARM64
+
+static void armctrl_ack_irq(struct irq_data *d)
+{
+ bcm2836_arm_irqchip_spin_gpu_irq();
}
+#endif
+
static struct irq_chip armctrl_chip = {
.name = "ARMCTRL-level",
.irq_mask = armctrl_mask_irq,
.irq_unmask = armctrl_unmask_irq,
.flags = IRQCHIP_MASK_ON_SUSPEND |
IRQCHIP_SKIP_SET_WAKE,
+#ifdef CONFIG_ARM64
+ .irq_ack = armctrl_ack_irq
+#endif
};
static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr,
@@ -137,13 +203,14 @@ static int __init armctrl_of_init(struct device_node *node,
bool is_2836)
{
void __iomem *base;
- int irq, b, i;
+ int irq = 0, last_irq, b, i;
u32 reg;
base = of_iomap(node, 0);
if (!base)
panic("%pOF: unable to map IC registers\n", node);
+ intc.base = base;
intc.domain = irq_domain_create_linear(of_fwnode_handle(node), MAKE_HWIRQ(NR_BANKS, 0),
&armctrl_ops, NULL);
if (!intc.domain)
@@ -176,6 +243,8 @@ static int __init armctrl_of_init(struct device_node *node,
pr_err(FW_BUG "Bootloader left fiq enabled\n");
}
+ last_irq = irq;
+
if (is_2836) {
int parent_irq = irq_of_parse_and_map(node, 0);
@@ -188,6 +257,27 @@ static int __init armctrl_of_init(struct device_node *node,
set_handle_irq(bcm2835_handle_irq);
}
+ if (is_2836) {
+ extern void __iomem * __attribute__((weak)) arm_local_intc;
+ intc.local_base = arm_local_intc;
+ if (!intc.local_base)
+ pr_err("Failed to get local intc base. FIQ is disabled for cpus > 1\n");
+ }
+
+ /* Make a duplicate irq range which is used to enable FIQ */
+ for (b = 0; b < NR_BANKS; b++) {
+ for (i = 0; i < bank_irqs[b]; i++) {
+ irq = irq_create_mapping(intc.domain,
+ MAKE_HWIRQ(b, i) + NUMBER_IRQS);
+ BUG_ON(irq <= 0);
+ irq_set_chip(irq, &armctrl_chip);
+ irq_set_probe(irq);
+ }
+ }
+#ifndef CONFIG_ARM64
+ init_FIQ(irq - last_irq);
+#endif
+
return 0;
}
@@ -255,7 +345,8 @@ static void bcm2836_chained_handle_irq(struct irq_desc *desc)
{
u32 hwirq;
- while ((hwirq = get_next_armctrl_hwirq()) != ~0)
+ hwirq = get_next_armctrl_hwirq();
+ if (hwirq != ~0)
generic_handle_domain_irq(intc.domain, hwirq);
}
@@ -263,3 +354,4 @@ IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic",
bcm2835_armctrl_of_init);
IRQCHIP_DECLARE(bcm2836_armctrl_ic, "brcm,bcm2836-armctrl-ic",
bcm2836_armctrl_of_init);
+ \ No newline at end of file
diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c
index fafd1f71348e..85f16f308bcd 100644
--- a/drivers/irqchip/irq-bcm2836.c
+++ b/drivers/irqchip/irq-bcm2836.c
@@ -22,6 +22,9 @@ struct bcm2836_arm_irqchip_intc {
static struct bcm2836_arm_irqchip_intc intc __read_mostly;
+void __iomem *arm_local_intc;
+EXPORT_SYMBOL_GPL(arm_local_intc);
+
static void bcm2836_arm_irqchip_mask_per_cpu_irq(unsigned int reg_offset,
unsigned int bit,
int cpu)
@@ -86,6 +89,27 @@ static void bcm2836_arm_irqchip_unmask_gpu_irq(struct irq_data *d)
{
}
+#ifdef CONFIG_ARM64
+
+void bcm2836_arm_irqchip_spin_gpu_irq(void)
+{
+ u32 i;
+ void __iomem *gpurouting = (intc.base + LOCAL_GPU_ROUTING);
+ u32 routing_val = readl(gpurouting);
+
+ for (i = 1; i <= 3; i++) {
+ u32 new_routing_val = (routing_val + i) & 3;
+
+ if (cpu_active(new_routing_val)) {
+ writel(new_routing_val, gpurouting);
+ return;
+ }
+ }
+}
+EXPORT_SYMBOL(bcm2836_arm_irqchip_spin_gpu_irq);
+
+#endif
+
static struct irq_chip bcm2836_arm_irqchip_gpu = {
.name = "bcm2836-gpu",
.irq_mask = bcm2836_arm_irqchip_mask_gpu_irq,
@@ -131,7 +155,7 @@ static int bcm2836_map(struct irq_domain *d, unsigned int irq,
irq_set_percpu_devid(irq);
irq_domain_set_info(d, irq, hw, chip, d->host_data,
handle_percpu_devid_irq, NULL, NULL);
- irq_set_status_flags(irq, IRQ_NOAUTOEN);
+ irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_TYPE_LEVEL_LOW);
return 0;
}
@@ -323,6 +347,8 @@ static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node,
panic("%pOF: unable to map local interrupt registers\n", node);
}
+ arm_local_intc = intc.base;
+
bcm2835_init_local_timer_frequency();
intc.domain = irq_domain_create_linear(of_fwnode_handle(node), LAST_IRQ + 1,
diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c
index 1bec5b2cd3f0..8b3a6838183f 100644
--- a/drivers/irqchip/irq-brcmstb-l2.c
+++ b/drivers/irqchip/irq-brcmstb-l2.c
@@ -51,6 +51,16 @@ static const struct brcmstb_intc_init_params l2_lvl_intc_init = {
.cpu_mask_clear = 0x0C
};
+/* Register offsets in the 2711 L2 level interrupt controller */
+static const struct brcmstb_intc_init_params l2_2711_lvl_intc_init = {
+ .handler = handle_level_irq,
+ .cpu_status = 0x00,
+ .cpu_clear = 0x08,
+ .cpu_mask_status = 0x0c,
+ .cpu_mask_set = 0x10,
+ .cpu_mask_clear = 0x14
+};
+
/* L2 intc private data structure */
struct brcmstb_l2_intc_data {
struct irq_domain *domain;
@@ -269,11 +279,18 @@ static int __init brcmstb_l2_lvl_intc_of_init(struct device_node *np,
return brcmstb_l2_intc_of_init(np, parent, &l2_lvl_intc_init);
}
+static int __init brcmstb_l2_2711_lvl_intc_of_init(struct device_node *np,
+ struct device_node *parent)
+{
+ return brcmstb_l2_intc_of_init(np, parent, &l2_2711_lvl_intc_init);
+}
+
IRQCHIP_PLATFORM_DRIVER_BEGIN(brcmstb_l2)
IRQCHIP_MATCH("brcm,l2-intc", brcmstb_l2_edge_intc_of_init)
IRQCHIP_MATCH("brcm,hif-spi-l2-intc", brcmstb_l2_edge_intc_of_init)
IRQCHIP_MATCH("brcm,upg-aux-aon-l2-intc", brcmstb_l2_edge_intc_of_init)
IRQCHIP_MATCH("brcm,bcm7271-l2-intc", brcmstb_l2_lvl_intc_of_init)
+IRQCHIP_MATCH("brcm,bcm2711-l2-intc", brcmstb_l2_2711_lvl_intc_of_init)
IRQCHIP_PLATFORM_DRIVER_END(brcmstb_l2)
MODULE_DESCRIPTION("Broadcom STB generic L2 interrupt controller");
MODULE_LICENSE("GPL v2");