MIPS: JZ4740: support >32 interrupts
On newer Ingenic SoCs the interrupt controller supports more than 32 interrupts, which it does by duplicating the registers at intervals of 0x20 bytes within its address space. Add support for an arbitrary number of interrupts using multiple generic chips, and provide the number of chips to register from the interrupt controller probe function. Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: Lars-Peter Clausen <lars@metafoo.de> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jason Cooper <jason@lakedaemon.net> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Cc: Brian Norris <computersforpeace@gmail.com> Patchwork: https://patchwork.linux-mips.org/patch/10141/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
Родитель
fe778ece8e
Коммит
943d69c6c2
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
struct ingenic_intc_data {
|
struct ingenic_intc_data {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
unsigned num_chips;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define JZ_REG_INTC_STATUS 0x00
|
#define JZ_REG_INTC_STATUS 0x00
|
||||||
|
@ -41,16 +42,22 @@ struct ingenic_intc_data {
|
||||||
#define JZ_REG_INTC_SET_MASK 0x08
|
#define JZ_REG_INTC_SET_MASK 0x08
|
||||||
#define JZ_REG_INTC_CLEAR_MASK 0x0c
|
#define JZ_REG_INTC_CLEAR_MASK 0x0c
|
||||||
#define JZ_REG_INTC_PENDING 0x10
|
#define JZ_REG_INTC_PENDING 0x10
|
||||||
|
#define CHIP_SIZE 0x20
|
||||||
|
|
||||||
static irqreturn_t jz4740_cascade(int irq, void *data)
|
static irqreturn_t jz4740_cascade(int irq, void *data)
|
||||||
{
|
{
|
||||||
struct ingenic_intc_data *intc = irq_get_handler_data(irq);
|
struct ingenic_intc_data *intc = irq_get_handler_data(irq);
|
||||||
uint32_t irq_reg;
|
uint32_t irq_reg;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
irq_reg = readl(intc->base + JZ_REG_INTC_PENDING);
|
for (i = 0; i < intc->num_chips; i++) {
|
||||||
|
irq_reg = readl(intc->base + (i * CHIP_SIZE) +
|
||||||
|
JZ_REG_INTC_PENDING);
|
||||||
|
if (!irq_reg)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (irq_reg)
|
generic_handle_irq(__fls(irq_reg) + (i * 32) + JZ4740_IRQ_BASE);
|
||||||
generic_handle_irq(__fls(irq_reg) + JZ4740_IRQ_BASE);
|
}
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -80,14 +87,15 @@ static struct irqaction jz4740_cascade_action = {
|
||||||
.name = "JZ4740 cascade interrupt",
|
.name = "JZ4740 cascade interrupt",
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init jz4740_intc_of_init(struct device_node *node,
|
static int __init ingenic_intc_of_init(struct device_node *node,
|
||||||
struct device_node *parent)
|
unsigned num_chips)
|
||||||
{
|
{
|
||||||
struct ingenic_intc_data *intc;
|
struct ingenic_intc_data *intc;
|
||||||
struct irq_chip_generic *gc;
|
struct irq_chip_generic *gc;
|
||||||
struct irq_chip_type *ct;
|
struct irq_chip_type *ct;
|
||||||
struct irq_domain *domain;
|
struct irq_domain *domain;
|
||||||
int parent_irq, err = 0;
|
int parent_irq, err = 0;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
intc = kzalloc(sizeof(*intc), GFP_KERNEL);
|
intc = kzalloc(sizeof(*intc), GFP_KERNEL);
|
||||||
if (!intc) {
|
if (!intc) {
|
||||||
|
@ -105,27 +113,34 @@ static int __init jz4740_intc_of_init(struct device_node *node,
|
||||||
if (err)
|
if (err)
|
||||||
goto out_unmap_irq;
|
goto out_unmap_irq;
|
||||||
|
|
||||||
|
intc->num_chips = num_chips;
|
||||||
intc->base = ioremap(JZ4740_INTC_BASE_ADDR, 0x14);
|
intc->base = ioremap(JZ4740_INTC_BASE_ADDR, 0x14);
|
||||||
|
|
||||||
/* Mask all irqs */
|
for (i = 0; i < num_chips; i++) {
|
||||||
writel(0xffffffff, intc->base + JZ_REG_INTC_SET_MASK);
|
/* Mask all irqs */
|
||||||
|
writel(0xffffffff, intc->base + (i * CHIP_SIZE) +
|
||||||
|
JZ_REG_INTC_SET_MASK);
|
||||||
|
|
||||||
gc = irq_alloc_generic_chip("INTC", 1, JZ4740_IRQ_BASE, intc->base,
|
gc = irq_alloc_generic_chip("INTC", 1,
|
||||||
handle_level_irq);
|
JZ4740_IRQ_BASE + (i * 32),
|
||||||
|
intc->base + (i * CHIP_SIZE),
|
||||||
|
handle_level_irq);
|
||||||
|
|
||||||
gc->wake_enabled = IRQ_MSK(32);
|
gc->wake_enabled = IRQ_MSK(32);
|
||||||
|
|
||||||
ct = gc->chip_types;
|
ct = gc->chip_types;
|
||||||
ct->regs.enable = JZ_REG_INTC_CLEAR_MASK;
|
ct->regs.enable = JZ_REG_INTC_CLEAR_MASK;
|
||||||
ct->regs.disable = JZ_REG_INTC_SET_MASK;
|
ct->regs.disable = JZ_REG_INTC_SET_MASK;
|
||||||
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
|
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
|
||||||
ct->chip.irq_mask = irq_gc_mask_disable_reg;
|
ct->chip.irq_mask = irq_gc_mask_disable_reg;
|
||||||
ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
|
ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
|
||||||
ct->chip.irq_set_wake = irq_gc_set_wake;
|
ct->chip.irq_set_wake = irq_gc_set_wake;
|
||||||
ct->chip.irq_suspend = jz4740_irq_suspend;
|
ct->chip.irq_suspend = jz4740_irq_suspend;
|
||||||
ct->chip.irq_resume = jz4740_irq_resume;
|
ct->chip.irq_resume = jz4740_irq_resume;
|
||||||
|
|
||||||
irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0, IRQ_NOPROBE | IRQ_LEVEL);
|
irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0,
|
||||||
|
IRQ_NOPROBE | IRQ_LEVEL);
|
||||||
|
}
|
||||||
|
|
||||||
domain = irq_domain_add_legacy(node, num_chips * 32, JZ4740_IRQ_BASE, 0,
|
domain = irq_domain_add_legacy(node, num_chips * 32, JZ4740_IRQ_BASE, 0,
|
||||||
&irq_domain_simple_ops, NULL);
|
&irq_domain_simple_ops, NULL);
|
||||||
|
@ -142,4 +157,10 @@ out_free:
|
||||||
out_err:
|
out_err:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
IRQCHIP_DECLARE(jz4740_intc, "ingenic,jz4740-intc", jz4740_intc_of_init);
|
|
||||||
|
static int __init intc_1chip_of_init(struct device_node *node,
|
||||||
|
struct device_node *parent)
|
||||||
|
{
|
||||||
|
return ingenic_intc_of_init(node, 1);
|
||||||
|
}
|
||||||
|
IRQCHIP_DECLARE(jz4740_intc, "ingenic,jz4740-intc", intc_1chip_of_init);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче