2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU General Public License version 2 as published
4 * by the Free Software Foundation.
6 * Partially based on arch/mips/ralink/irq.c
8 * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
9 * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
10 * Copyright (C) 2014 Kevin Cernekee <cernekee@gmail.com>
14 #include <linux/bitops.h>
15 #include <linux/of_platform.h>
16 #include <linux/of_address.h>
17 #include <linux/of_irq.h>
18 #include <linux/irqdomain.h>
19 #include <linux/interrupt.h>
20 #include <linux/slab.h>
21 #include <linux/spinlock.h>
23 #include <asm/bmips.h>
24 #include <asm/irq_cpu.h>
25 #include <asm/mipsregs.h>
27 /* INTC register offsets */
28 #define INTC_REG_ENABLE 0x00
29 #define INTC_REG_STATUS 0x04
32 #define IRQS_PER_WORD 32
36 void __iomem *reg[MAX_WORDS];
37 u32 enable[MAX_WORDS];
41 static void bcm3384_intc_irq_unmask(struct irq_data *d)
43 struct bcm3384_intc *priv = d->domain->host_data;
45 int idx = d->hwirq / IRQS_PER_WORD;
46 int bit = d->hwirq % IRQS_PER_WORD;
48 spin_lock_irqsave(&priv->lock, flags);
49 priv->enable[idx] |= BIT(bit);
50 __raw_writel(priv->enable[idx], priv->reg[idx] + INTC_REG_ENABLE);
51 spin_unlock_irqrestore(&priv->lock, flags);
54 static void bcm3384_intc_irq_mask(struct irq_data *d)
56 struct bcm3384_intc *priv = d->domain->host_data;
58 int idx = d->hwirq / IRQS_PER_WORD;
59 int bit = d->hwirq % IRQS_PER_WORD;
61 spin_lock_irqsave(&priv->lock, flags);
62 priv->enable[idx] &= ~BIT(bit);
63 __raw_writel(priv->enable[idx], priv->reg[idx] + INTC_REG_ENABLE);
64 spin_unlock_irqrestore(&priv->lock, flags);
67 static struct irq_chip bcm3384_intc_irq_chip = {
69 .irq_unmask = bcm3384_intc_irq_unmask,
70 .irq_mask = bcm3384_intc_irq_mask,
71 .irq_mask_ack = bcm3384_intc_irq_mask,
74 unsigned int get_c0_compare_int(void)
76 return CP0_LEGACY_COMPARE_IRQ;
79 static void bcm3384_intc_irq_handler(unsigned int irq, struct irq_desc *desc)
81 struct irq_domain *domain = irq_get_handler_data(irq);
82 struct bcm3384_intc *priv = domain->host_data;
86 for (idx = 0; idx < priv->n_words; idx++) {
87 unsigned long pending;
90 spin_lock_irqsave(&priv->lock, flags);
91 pending = __raw_readl(priv->reg[idx] + INTC_REG_STATUS) &
93 spin_unlock_irqrestore(&priv->lock, flags);
95 for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) {
96 generic_handle_irq(irq_find_mapping(domain,
97 hwirq + idx * IRQS_PER_WORD));
102 asmlinkage void plat_irq_dispatch(void)
104 unsigned long pending =
105 (read_c0_status() & read_c0_cause() & ST0_IM) >> STATUSB_IP0;
108 for_each_set_bit(bit, &pending, 8)
109 do_IRQ(MIPS_CPU_IRQ_BASE + bit);
112 static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
114 irq_set_chip_and_handler(irq, &bcm3384_intc_irq_chip, handle_level_irq);
118 static const struct irq_domain_ops irq_domain_ops = {
119 .xlate = irq_domain_xlate_onecell,
123 static int __init ioremap_one_pair(struct bcm3384_intc *priv,
124 struct device_node *node,
129 if (of_address_to_resource(node, idx, &res))
132 if (request_mem_region(res.start, resource_size(&res),
134 pr_err("Failed to request INTC register region\n");
136 priv->reg[idx] = ioremap_nocache(res.start, resource_size(&res));
138 panic("Failed to ioremap INTC register range");
140 /* start up with everything masked before we hook the parent IRQ */
141 __raw_writel(0, priv->reg[idx] + INTC_REG_ENABLE);
142 priv->enable[idx] = 0;
144 return IRQS_PER_WORD;
147 static int __init intc_of_init(struct device_node *node,
148 struct device_node *parent)
150 struct irq_domain *domain;
151 unsigned int parent_irq, n_irqs = 0;
152 struct bcm3384_intc *priv;
154 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
156 panic("Failed to allocate bcm3384_intc struct");
158 spin_lock_init(&priv->lock);
160 parent_irq = irq_of_parse_and_map(node, 0);
162 panic("Failed to get INTC IRQ");
164 n_irqs += ioremap_one_pair(priv, node, 0);
165 n_irqs += ioremap_one_pair(priv, node, 1);
168 panic("Failed to map INTC registers");
170 priv->n_words = n_irqs / IRQS_PER_WORD;
171 domain = irq_domain_add_linear(node, n_irqs, &irq_domain_ops, priv);
173 panic("Failed to add irqdomain");
175 irq_set_chained_handler(parent_irq, bcm3384_intc_irq_handler);
176 irq_set_handler_data(parent_irq, domain);
181 static struct of_device_id of_irq_ids[] __initdata = {
182 { .compatible = "mti,cpu-interrupt-controller",
183 .data = mips_cpu_intc_init },
184 { .compatible = "brcm,bcm3384-intc",
185 .data = intc_of_init },
189 void __init arch_init_irq(void)
192 of_irq_init(of_irq_ids);