diff options
Diffstat (limited to 'drivers/irqchip/irq-sun4i.c')
| -rw-r--r-- | drivers/irqchip/irq-sun4i.c | 42 | 
1 files changed, 27 insertions, 15 deletions
diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c index a5438d88924..6fcef4a95a1 100644 --- a/drivers/irqchip/irq-sun4i.c +++ b/drivers/irqchip/irq-sun4i.c @@ -36,18 +36,16 @@  static void __iomem *sun4i_irq_base;  static struct irq_domain *sun4i_irq_domain; -static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs); +static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs);  static void sun4i_irq_ack(struct irq_data *irqd)  {  	unsigned int irq = irqd_to_hwirq(irqd); -	unsigned int irq_off = irq % 32; -	int reg = irq / 32; -	u32 val; -	val = readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); -	writel(val | (1 << irq_off), -	       sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); +	if (irq != 0) +		return; /* Only IRQ 0 / the ENMI needs to be acked */ + +	writel(BIT(0), sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0));  }  static void sun4i_irq_mask(struct irq_data *irqd) @@ -76,16 +74,16 @@ static void sun4i_irq_unmask(struct irq_data *irqd)  static struct irq_chip sun4i_irq_chip = {  	.name		= "sun4i_irq", -	.irq_ack	= sun4i_irq_ack, +	.irq_eoi	= sun4i_irq_ack,  	.irq_mask	= sun4i_irq_mask,  	.irq_unmask	= sun4i_irq_unmask, +	.flags		= IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED,  };  static int sun4i_irq_map(struct irq_domain *d, unsigned int virq,  			 irq_hw_number_t hw)  { -	irq_set_chip_and_handler(virq, &sun4i_irq_chip, -				 handle_level_irq); +	irq_set_chip_and_handler(virq, &sun4i_irq_chip, handle_fasteoi_irq);  	set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);  	return 0; @@ -109,7 +107,7 @@ static int __init sun4i_of_init(struct device_node *node,  	writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(1));  	writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(2)); -	/* Mask all the interrupts */ +	/* Unmask all the interrupts, ENABLE_REG(x) is used for masking */  	writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(0));  	writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(1));  	writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(2)); @@ -134,16 +132,30 @@ static int __init sun4i_of_init(struct device_node *node,  	return 0;  } -IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-ic", sun4i_of_init); +IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-a10-ic", sun4i_of_init); -static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs) +static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs)  {  	u32 irq, hwirq; +	/* +	 * hwirq == 0 can mean one of 3 things: +	 * 1) no more irqs pending +	 * 2) irq 0 pending +	 * 3) spurious irq +	 * So if we immediately get a reading of 0, check the irq-pending reg +	 * to differentiate between 2 and 3. We only do this once to avoid +	 * the extra check in the common case of 1 hapening after having +	 * read the vector-reg once. +	 */  	hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; -	while (hwirq != 0) { +	if (hwirq == 0 && +		  !(readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)) & BIT(0))) +		return; + +	do {  		irq = irq_find_mapping(sun4i_irq_domain, hwirq);  		handle_IRQ(irq, regs);  		hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; -	} +	} while (hwirq != 0);  }  | 
