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