xref: /openbmc/linux/arch/mips/ar7/irq.c (revision ead5d1f4d877e92c051e1a1ade623d0d30e71619)
1fd534e9bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27ca5dc14SFlorian Fainelli /*
37ca5dc14SFlorian Fainelli  * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org>
47ca5dc14SFlorian Fainelli  * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org>
57ca5dc14SFlorian Fainelli  */
67ca5dc14SFlorian Fainelli 
77ca5dc14SFlorian Fainelli #include <linux/interrupt.h>
87ca5dc14SFlorian Fainelli #include <linux/io.h>
9ca4d3e67SDavid Howells #include <linux/irq.h>
107ca5dc14SFlorian Fainelli 
117ca5dc14SFlorian Fainelli #include <asm/irq_cpu.h>
127ca5dc14SFlorian Fainelli #include <asm/mipsregs.h>
137ca5dc14SFlorian Fainelli #include <asm/mach-ar7/ar7.h>
147ca5dc14SFlorian Fainelli 
157ca5dc14SFlorian Fainelli #define EXCEPT_OFFSET	0x80
167ca5dc14SFlorian Fainelli #define PACE_OFFSET	0xA0
177ca5dc14SFlorian Fainelli #define CHNLS_OFFSET	0x200
187ca5dc14SFlorian Fainelli 
197ca5dc14SFlorian Fainelli #define REG_OFFSET(irq, reg)	((irq) / 32 * 0x4 + reg * 0x10)
207ca5dc14SFlorian Fainelli #define SEC_REG_OFFSET(reg)	(EXCEPT_OFFSET + reg * 0x8)
217ca5dc14SFlorian Fainelli #define SEC_SR_OFFSET		(SEC_REG_OFFSET(0))	/* 0x80 */
227ca5dc14SFlorian Fainelli #define CR_OFFSET(irq)		(REG_OFFSET(irq, 1))	/* 0x10 */
237ca5dc14SFlorian Fainelli #define SEC_CR_OFFSET		(SEC_REG_OFFSET(1))	/* 0x88 */
247ca5dc14SFlorian Fainelli #define ESR_OFFSET(irq)		(REG_OFFSET(irq, 2))	/* 0x20 */
257ca5dc14SFlorian Fainelli #define SEC_ESR_OFFSET		(SEC_REG_OFFSET(2))	/* 0x90 */
267ca5dc14SFlorian Fainelli #define ECR_OFFSET(irq)		(REG_OFFSET(irq, 3))	/* 0x30 */
277ca5dc14SFlorian Fainelli #define SEC_ECR_OFFSET		(SEC_REG_OFFSET(3))	/* 0x98 */
287ca5dc14SFlorian Fainelli #define PIR_OFFSET		(0x40)
297ca5dc14SFlorian Fainelli #define MSR_OFFSET		(0x44)
307ca5dc14SFlorian Fainelli #define PM_OFFSET(irq)		(REG_OFFSET(irq, 5))	/* 0x50 */
317ca5dc14SFlorian Fainelli #define TM_OFFSET(irq)		(REG_OFFSET(irq, 6))	/* 0x60 */
327ca5dc14SFlorian Fainelli 
337ca5dc14SFlorian Fainelli #define REG(addr) ((u32 *)(KSEG1ADDR(AR7_REGS_IRQ) + addr))
347ca5dc14SFlorian Fainelli 
357ca5dc14SFlorian Fainelli #define CHNL_OFFSET(chnl) (CHNLS_OFFSET + (chnl * 4))
367ca5dc14SFlorian Fainelli 
377ca5dc14SFlorian Fainelli static int ar7_irq_base;
387ca5dc14SFlorian Fainelli 
ar7_unmask_irq(struct irq_data * d)3941d735e8SThomas Gleixner static void ar7_unmask_irq(struct irq_data *d)
407ca5dc14SFlorian Fainelli {
4141d735e8SThomas Gleixner 	writel(1 << ((d->irq - ar7_irq_base) % 32),
4241d735e8SThomas Gleixner 	       REG(ESR_OFFSET(d->irq - ar7_irq_base)));
437ca5dc14SFlorian Fainelli }
447ca5dc14SFlorian Fainelli 
ar7_mask_irq(struct irq_data * d)4541d735e8SThomas Gleixner static void ar7_mask_irq(struct irq_data *d)
467ca5dc14SFlorian Fainelli {
4741d735e8SThomas Gleixner 	writel(1 << ((d->irq - ar7_irq_base) % 32),
4841d735e8SThomas Gleixner 	       REG(ECR_OFFSET(d->irq - ar7_irq_base)));
497ca5dc14SFlorian Fainelli }
507ca5dc14SFlorian Fainelli 
ar7_ack_irq(struct irq_data * d)5141d735e8SThomas Gleixner static void ar7_ack_irq(struct irq_data *d)
527ca5dc14SFlorian Fainelli {
5341d735e8SThomas Gleixner 	writel(1 << ((d->irq - ar7_irq_base) % 32),
5441d735e8SThomas Gleixner 	       REG(CR_OFFSET(d->irq - ar7_irq_base)));
557ca5dc14SFlorian Fainelli }
567ca5dc14SFlorian Fainelli 
ar7_unmask_sec_irq(struct irq_data * d)5741d735e8SThomas Gleixner static void ar7_unmask_sec_irq(struct irq_data *d)
587ca5dc14SFlorian Fainelli {
5941d735e8SThomas Gleixner 	writel(1 << (d->irq - ar7_irq_base - 40), REG(SEC_ESR_OFFSET));
607ca5dc14SFlorian Fainelli }
617ca5dc14SFlorian Fainelli 
ar7_mask_sec_irq(struct irq_data * d)6241d735e8SThomas Gleixner static void ar7_mask_sec_irq(struct irq_data *d)
637ca5dc14SFlorian Fainelli {
6441d735e8SThomas Gleixner 	writel(1 << (d->irq - ar7_irq_base - 40), REG(SEC_ECR_OFFSET));
657ca5dc14SFlorian Fainelli }
667ca5dc14SFlorian Fainelli 
ar7_ack_sec_irq(struct irq_data * d)6741d735e8SThomas Gleixner static void ar7_ack_sec_irq(struct irq_data *d)
687ca5dc14SFlorian Fainelli {
6941d735e8SThomas Gleixner 	writel(1 << (d->irq - ar7_irq_base - 40), REG(SEC_CR_OFFSET));
707ca5dc14SFlorian Fainelli }
717ca5dc14SFlorian Fainelli 
727ca5dc14SFlorian Fainelli static struct irq_chip ar7_irq_type = {
737ca5dc14SFlorian Fainelli 	.name = "AR7",
7441d735e8SThomas Gleixner 	.irq_unmask = ar7_unmask_irq,
7541d735e8SThomas Gleixner 	.irq_mask = ar7_mask_irq,
7641d735e8SThomas Gleixner 	.irq_ack = ar7_ack_irq
777ca5dc14SFlorian Fainelli };
787ca5dc14SFlorian Fainelli 
797ca5dc14SFlorian Fainelli static struct irq_chip ar7_sec_irq_type = {
807ca5dc14SFlorian Fainelli 	.name = "AR7",
8141d735e8SThomas Gleixner 	.irq_unmask = ar7_unmask_sec_irq,
8241d735e8SThomas Gleixner 	.irq_mask = ar7_mask_sec_irq,
8341d735e8SThomas Gleixner 	.irq_ack = ar7_ack_sec_irq,
847ca5dc14SFlorian Fainelli };
857ca5dc14SFlorian Fainelli 
ar7_irq_init(int base)867ca5dc14SFlorian Fainelli static void __init ar7_irq_init(int base)
877ca5dc14SFlorian Fainelli {
887ca5dc14SFlorian Fainelli 	int i;
897ca5dc14SFlorian Fainelli 	/*
907ca5dc14SFlorian Fainelli 	 * Disable interrupts and clear pending
917ca5dc14SFlorian Fainelli 	 */
927ca5dc14SFlorian Fainelli 	writel(0xffffffff, REG(ECR_OFFSET(0)));
937ca5dc14SFlorian Fainelli 	writel(0xff, REG(ECR_OFFSET(32)));
947ca5dc14SFlorian Fainelli 	writel(0xffffffff, REG(SEC_ECR_OFFSET));
957ca5dc14SFlorian Fainelli 	writel(0xffffffff, REG(CR_OFFSET(0)));
967ca5dc14SFlorian Fainelli 	writel(0xff, REG(CR_OFFSET(32)));
977ca5dc14SFlorian Fainelli 	writel(0xffffffff, REG(SEC_CR_OFFSET));
987ca5dc14SFlorian Fainelli 
997ca5dc14SFlorian Fainelli 	ar7_irq_base = base;
1007ca5dc14SFlorian Fainelli 
1017ca5dc14SFlorian Fainelli 	for (i = 0; i < 40; i++) {
1027ca5dc14SFlorian Fainelli 		writel(i, REG(CHNL_OFFSET(i)));
1037ca5dc14SFlorian Fainelli 		/* Primary IRQ's */
104e4ec7989SThomas Gleixner 		irq_set_chip_and_handler(base + i, &ar7_irq_type,
1057ca5dc14SFlorian Fainelli 					 handle_level_irq);
1067ca5dc14SFlorian Fainelli 		/* Secondary IRQ's */
1077ca5dc14SFlorian Fainelli 		if (i < 32)
108e4ec7989SThomas Gleixner 			irq_set_chip_and_handler(base + i + 40,
1097ca5dc14SFlorian Fainelli 						 &ar7_sec_irq_type,
1107ca5dc14SFlorian Fainelli 						 handle_level_irq);
1117ca5dc14SFlorian Fainelli 	}
1127ca5dc14SFlorian Fainelli 
113*ac8fd122Safzal mohammed 	if (request_irq(2, no_action, IRQF_NO_THREAD, "AR7 cascade interrupt",
114*ac8fd122Safzal mohammed 			NULL))
115*ac8fd122Safzal mohammed 		pr_err("Failed to request irq 2 (AR7 cascade interrupt)\n");
116*ac8fd122Safzal mohammed 	if (request_irq(ar7_irq_base, no_action, IRQF_NO_THREAD,
117*ac8fd122Safzal mohammed 			"AR7 cascade interrupt", NULL)) {
118*ac8fd122Safzal mohammed 		pr_err("Failed to request irq %d (AR7 cascade interrupt)\n",
119*ac8fd122Safzal mohammed 		       ar7_irq_base);
120*ac8fd122Safzal mohammed 	}
1217ca5dc14SFlorian Fainelli 	set_c0_status(IE_IRQ0);
1227ca5dc14SFlorian Fainelli }
1237ca5dc14SFlorian Fainelli 
arch_init_irq(void)1247ca5dc14SFlorian Fainelli void __init arch_init_irq(void)
1257ca5dc14SFlorian Fainelli {
1267ca5dc14SFlorian Fainelli 	mips_cpu_irq_init();
1277ca5dc14SFlorian Fainelli 	ar7_irq_init(8);
1287ca5dc14SFlorian Fainelli }
1297ca5dc14SFlorian Fainelli 
ar7_cascade(void)1307ca5dc14SFlorian Fainelli static void ar7_cascade(void)
1317ca5dc14SFlorian Fainelli {
1327ca5dc14SFlorian Fainelli 	u32 status;
1337ca5dc14SFlorian Fainelli 	int i, irq;
1347ca5dc14SFlorian Fainelli 
1357ca5dc14SFlorian Fainelli 	/* Primary IRQ's */
1367ca5dc14SFlorian Fainelli 	irq = readl(REG(PIR_OFFSET)) & 0x3f;
1377ca5dc14SFlorian Fainelli 	if (irq) {
1387ca5dc14SFlorian Fainelli 		do_IRQ(ar7_irq_base + irq);
1397ca5dc14SFlorian Fainelli 		return;
1407ca5dc14SFlorian Fainelli 	}
1417ca5dc14SFlorian Fainelli 
1427ca5dc14SFlorian Fainelli 	/* Secondary IRQ's are cascaded through primary '0' */
1437ca5dc14SFlorian Fainelli 	writel(1, REG(CR_OFFSET(irq)));
1447ca5dc14SFlorian Fainelli 	status = readl(REG(SEC_SR_OFFSET));
1457ca5dc14SFlorian Fainelli 	for (i = 0; i < 32; i++) {
1467ca5dc14SFlorian Fainelli 		if (status & 1) {
1477ca5dc14SFlorian Fainelli 			do_IRQ(ar7_irq_base + i + 40);
1487ca5dc14SFlorian Fainelli 			return;
1497ca5dc14SFlorian Fainelli 		}
1507ca5dc14SFlorian Fainelli 		status >>= 1;
1517ca5dc14SFlorian Fainelli 	}
1527ca5dc14SFlorian Fainelli 
1537ca5dc14SFlorian Fainelli 	spurious_interrupt();
1547ca5dc14SFlorian Fainelli }
1557ca5dc14SFlorian Fainelli 
plat_irq_dispatch(void)1567ca5dc14SFlorian Fainelli asmlinkage void plat_irq_dispatch(void)
1577ca5dc14SFlorian Fainelli {
1587ca5dc14SFlorian Fainelli 	unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
1597ca5dc14SFlorian Fainelli 	if (pending & STATUSF_IP7)		/* cpu timer */
1607ca5dc14SFlorian Fainelli 		do_IRQ(7);
1617ca5dc14SFlorian Fainelli 	else if (pending & STATUSF_IP2)		/* int0 hardware line */
1627ca5dc14SFlorian Fainelli 		ar7_cascade();
1637ca5dc14SFlorian Fainelli 	else
1647ca5dc14SFlorian Fainelli 		spurious_interrupt();
1657ca5dc14SFlorian Fainelli }
166