summaryrefslogtreecommitdiff
path: root/arch/arm/mach-ixp4xx/common.c
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@linaro.org>2018-12-29 16:30:27 +0300
committerLinus Walleij <linus.walleij@linaro.org>2019-04-19 21:37:34 +0300
commit98ac0cc270b717c49a49787fe7c42041123290bb (patch)
tree555cfeaa75f141a9b2a9d84f4a4508cce97235d1 /arch/arm/mach-ixp4xx/common.c
parent64534e060dda5546e6e8b38b8a9422e84a7df84d (diff)
downloadlinux-98ac0cc270b717c49a49787fe7c42041123290bb.tar.xz
ARM: ixp4xx: Convert to MULTI_IRQ_HANDLER
This rewrites the IXP4xx to use MULTI_IRQ_HANDLER and create an irqdomain for the irqchip in the platform. We convert the timer to request the interrupt like any other driver in the process. We bump all IRQs to 16+offset to avoid using IRQ 0 and set NR_IRQS to 512 (the default for most systems). This conveniently fits with the first 16 IRQs being pre-allocated when using SPARSE_IRQ. This is a prerequisite for SPARSE_IRQ and DT boot. Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'arch/arm/mach-ixp4xx/common.c')
-rw-r--r--arch/arm/mach-ixp4xx/common.c109
1 files changed, 80 insertions, 29 deletions
diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c
index 846e033c56fa..58a1b851425e 100644
--- a/arch/arm/mach-ixp4xx/common.c
+++ b/arch/arm/mach-ixp4xx/common.c
@@ -31,12 +31,14 @@
#include <linux/cpu.h>
#include <linux/pci.h>
#include <linux/sched_clock.h>
+#include <linux/bitops.h>
#include <mach/udc.h>
#include <mach/hardware.h>
#include <mach/io.h>
#include <linux/uaccess.h>
#include <asm/pgtable.h>
#include <asm/page.h>
+#include <asm/exception.h>
#include <asm/irq.h>
#include <asm/system_misc.h>
#include <asm/mach/map.h>
@@ -54,6 +56,7 @@
(IXP4XX_OST_RELOAD_MASK + 1) * HZ) * \
(IXP4XX_OST_RELOAD_MASK + 1)
+static struct irq_domain *ixp4xx_irqdomain;
static void __init ixp4xx_clocksource_init(void);
static void __init ixp4xx_clockevent_init(void);
static struct clock_event_device clockevent_ixp4xx;
@@ -166,16 +169,17 @@ static int ixp4xx_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
static int ixp4xx_set_irq_type(struct irq_data *d, unsigned int type)
{
- int line = irq2gpio[d->irq];
+ int line = irq2gpio[d->hwirq];
u32 int_style;
enum ixp4xx_irq_type irq_type;
volatile u32 *int_reg;
/*
* Only for GPIO IRQs
+ * all other IRQs are simply active low
*/
if (line < 0)
- return -EINVAL;
+ return 0;
switch (type){
case IRQ_TYPE_EDGE_BOTH:
@@ -203,9 +207,9 @@ static int ixp4xx_set_irq_type(struct irq_data *d, unsigned int type)
}
if (irq_type == IXP4XX_IRQ_EDGE)
- ixp4xx_irq_edge |= (1 << d->irq);
+ ixp4xx_irq_edge |= (1 << d->hwirq);
else
- ixp4xx_irq_edge &= ~(1 << d->irq);
+ ixp4xx_irq_edge &= ~(1 << d->hwirq);
if (line >= 8) { /* pins 8-15 */
line -= 8;
@@ -224,22 +228,22 @@ static int ixp4xx_set_irq_type(struct irq_data *d, unsigned int type)
*int_reg |= (int_style << (line * IXP4XX_GPIO_STYLE_SIZE));
/* Configure the line as an input */
- gpio_line_config(irq2gpio[d->irq], IXP4XX_GPIO_IN);
+ gpio_line_config(irq2gpio[d->hwirq], IXP4XX_GPIO_IN);
return 0;
}
static void ixp4xx_irq_mask(struct irq_data *d)
{
- if ((cpu_is_ixp46x() || cpu_is_ixp43x()) && d->irq >= 32)
- *IXP4XX_ICMR2 &= ~(1 << (d->irq - 32));
+ if ((cpu_is_ixp46x() || cpu_is_ixp43x()) && d->hwirq >= 32)
+ *IXP4XX_ICMR2 &= ~(1 << (d->hwirq - 32));
else
- *IXP4XX_ICMR &= ~(1 << d->irq);
+ *IXP4XX_ICMR &= ~(1 << d->hwirq);
}
static void ixp4xx_irq_ack(struct irq_data *d)
{
- int line = (d->irq < 32) ? irq2gpio[d->irq] : -1;
+ int line = (d->hwirq < 32) ? irq2gpio[d->hwirq] : -1;
if (line >= 0)
*IXP4XX_GPIO_GPISR = (1 << line);
@@ -251,13 +255,13 @@ static void ixp4xx_irq_ack(struct irq_data *d)
*/
static void ixp4xx_irq_unmask(struct irq_data *d)
{
- if (!(ixp4xx_irq_edge & (1 << d->irq)))
+ if (!(ixp4xx_irq_edge & (1 << d->hwirq)))
ixp4xx_irq_ack(d);
- if ((cpu_is_ixp46x() || cpu_is_ixp43x()) && d->irq >= 32)
- *IXP4XX_ICMR2 |= (1 << (d->irq - 32));
+ if ((cpu_is_ixp46x() || cpu_is_ixp43x()) && d->hwirq >= 32)
+ *IXP4XX_ICMR2 |= (1 << (d->hwirq - 32));
else
- *IXP4XX_ICMR |= (1 << d->irq);
+ *IXP4XX_ICMR |= (1 << d->hwirq);
}
static struct irq_chip ixp4xx_irq_chip = {
@@ -268,9 +272,50 @@ static struct irq_chip ixp4xx_irq_chip = {
.irq_set_type = ixp4xx_set_irq_type,
};
+asmlinkage void __exception_irq_entry ixp4xx_handle_irq(struct pt_regs *regs)
+{
+ unsigned long status;
+ int i;
+
+ status = *IXP4XX_ICIP;
+
+ for_each_set_bit(i, &status, 32)
+ handle_domain_irq(ixp4xx_irqdomain, i, regs);
+
+ /*
+ * IXP465/IXP435 has an upper IRQ status register
+ */
+ if ((cpu_is_ixp46x() || cpu_is_ixp43x())) {
+ status = *IXP4XX_ICIP2;
+ for_each_set_bit(i, &status, 32)
+ handle_domain_irq(ixp4xx_irqdomain, i + 32, regs);
+ }
+}
+
+static int ixp4xx_irqdomain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_data(irq, &ixp4xx_irq_chip);
+ irq_set_chip_and_handler(irq, &ixp4xx_irq_chip, handle_level_irq);
+ irq_set_probe(irq);
+
+ return 0;
+}
+
+static void ixp4xx_irqdomain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops ixp4xx_irqdomain_ops = {
+ .map = ixp4xx_irqdomain_map,
+ .unmap = ixp4xx_irqdomain_unmap,
+};
+
void __init ixp4xx_init_irq(void)
{
- int i = 0;
+ int nr_irqs;
/*
* ixp4xx does not implement the XScale PWRMODE register
@@ -290,14 +335,21 @@ void __init ixp4xx_init_irq(void)
/* Disable upper 32 interrupts */
*IXP4XX_ICMR2 = 0x00;
+
+ nr_irqs = 64;
+ } else {
+ nr_irqs = 32;
}
- /* Default to all level triggered */
- for(i = 0; i < NR_IRQS; i++) {
- irq_set_chip_and_handler(i, &ixp4xx_irq_chip,
- handle_level_irq);
- irq_clear_status_flags(i, IRQ_NOREQUEST);
+ ixp4xx_irqdomain = irq_domain_add_simple(NULL, nr_irqs, IRQ_IXP4XX_BASE,
+ &ixp4xx_irqdomain_ops,
+ NULL);
+ if (!ixp4xx_irqdomain) {
+ pr_crit("can not add primary irqdomain\n");
+ return;
}
+
+ set_handle_irq(ixp4xx_handle_irq);
}
@@ -319,13 +371,6 @@ static irqreturn_t ixp4xx_timer_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static struct irqaction ixp4xx_timer_irq = {
- .name = "timer1",
- .flags = IRQF_TIMER | IRQF_IRQPOLL,
- .handler = ixp4xx_timer_interrupt,
- .dev_id = &clockevent_ixp4xx,
-};
-
void __init ixp4xx_timer_init(void)
{
/* Reset/disable counter */
@@ -337,9 +382,6 @@ void __init ixp4xx_timer_init(void)
/* Reset time-stamp counter */
*IXP4XX_OSTS = 0;
- /* Connect the interrupt handler and enable the interrupt */
- setup_irq(IRQ_IXP4XX_TIMER1, &ixp4xx_timer_irq);
-
ixp4xx_clocksource_init();
ixp4xx_clockevent_init();
}
@@ -574,7 +616,16 @@ static struct clock_event_device clockevent_ixp4xx = {
static void __init ixp4xx_clockevent_init(void)
{
+ int ret;
+
clockevent_ixp4xx.cpumask = cpumask_of(0);
+ clockevent_ixp4xx.irq = IRQ_IXP4XX_TIMER1;
+ ret = request_irq(IRQ_IXP4XX_TIMER1, ixp4xx_timer_interrupt,
+ IRQF_TIMER, "IXP4XX-TIMER1", &clockevent_ixp4xx);
+ if (ret) {
+ pr_crit("no timer IRQ\n");
+ return;
+ }
clockevents_config_and_register(&clockevent_ixp4xx, IXP4XX_TIMER_FREQ,
0xf, 0xfffffffe);
}