1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
* Ingenic XBurst platform IRQ support
*/
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/irqchip.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/timex.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <asm/io.h>
struct ingenic_intc_data {
void __iomem *base;
struct irq_domain *domain;
unsigned num_chips;
};
#define JZ_REG_INTC_STATUS 0x00
#define JZ_REG_INTC_MASK 0x04
#define JZ_REG_INTC_SET_MASK 0x08
#define JZ_REG_INTC_CLEAR_MASK 0x0c
#define JZ_REG_INTC_PENDING 0x10
#define CHIP_SIZE 0x20
static irqreturn_t intc_cascade(int irq, void *data)
{
struct ingenic_intc_data *intc = irq_get_handler_data(irq);
struct irq_domain *domain = intc->domain;
struct irq_chip_generic *gc;
uint32_t pending;
unsigned i;
for (i = 0; i < intc->num_chips; i++) {
gc = irq_get_domain_generic_chip(domain, i * 32);
pending = irq_reg_readl(gc, JZ_REG_INTC_PENDING);
if (!pending)
continue;
while (pending) {
int bit = __fls(pending);
irq = irq_linear_revmap(domain, bit + (i * 32));
generic_handle_irq(irq);
pending &= ~BIT(bit);
}
}
return IRQ_HANDLED;
}
static int __init ingenic_intc_of_init(struct device_node *node,
unsigned num_chips)
{
struct ingenic_intc_data *intc;
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
struct irq_domain *domain;
int parent_irq, err = 0;
unsigned i;
intc = kzalloc(sizeof(*intc), GFP_KERNEL);
if (!intc) {
err = -ENOMEM;
goto out_err;
}
parent_irq = irq_of_parse_and_map(node, 0);
if (!parent_irq) {
err = -EINVAL;
goto out_free;
}
err = irq_set_handler_data(parent_irq, intc);
if (err)
goto out_unmap_irq;
intc->num_chips = num_chips;
intc->base = of_iomap(node, 0);
if (!intc->base) {
err = -ENODEV;
goto out_unmap_irq;
}
domain = irq_domain_add_linear(node, num_chips * 32,
&irq_generic_chip_ops, NULL);
if (!domain) {
err = -ENOMEM;
goto out_unmap_base;
}
intc->domain = domain;
err = irq_alloc_domain_generic_chips(domain, 32, 1, "INTC",
handle_level_irq, 0,
IRQ_NOPROBE | IRQ_LEVEL, 0);
if (err)
goto out_domain_remove;
for (i = 0; i < num_chips; i++) {
gc = irq_get_domain_generic_chip(domain, i * 32);
gc->wake_enabled = IRQ_MSK(32);
gc->reg_base = intc->base + (i * CHIP_SIZE);
ct = gc->chip_types;
ct->regs.enable = JZ_REG_INTC_CLEAR_MASK;
ct->regs.disable = JZ_REG_INTC_SET_MASK;
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
ct->chip.irq_mask = 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.flags = IRQCHIP_MASK_ON_SUSPEND;
/* Mask all irqs */
irq_reg_writel(gc, IRQ_MSK(32), JZ_REG_INTC_SET_MASK);
}
if (request_irq(parent_irq, intc_cascade, IRQF_NO_SUSPEND,
"SoC intc cascade interrupt", NULL))
pr_err("Failed to register SoC intc cascade interrupt\n");
return 0;
out_domain_remove:
irq_domain_remove(domain);
out_unmap_base:
iounmap(intc->base);
out_unmap_irq:
irq_dispose_mapping(parent_irq);
out_free:
kfree(intc);
out_err:
return err;
}
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);
IRQCHIP_DECLARE(jz4725b_intc, "ingenic,jz4725b-intc", intc_1chip_of_init);
static int __init intc_2chip_of_init(struct device_node *node,
struct device_node *parent)
{
return ingenic_intc_of_init(node, 2);
}
IRQCHIP_DECLARE(jz4760_intc, "ingenic,jz4760-intc", intc_2chip_of_init);
IRQCHIP_DECLARE(jz4770_intc, "ingenic,jz4770-intc", intc_2chip_of_init);
IRQCHIP_DECLARE(jz4775_intc, "ingenic,jz4775-intc", intc_2chip_of_init);
IRQCHIP_DECLARE(jz4780_intc, "ingenic,jz4780-intc", intc_2chip_of_init);
|