1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org> 4 * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org> 5 */ 6 7 #include <linux/interrupt.h> 8 #include <linux/io.h> 9 #include <linux/irq.h> 10 11 #include <asm/irq_cpu.h> 12 #include <asm/mipsregs.h> 13 #include <asm/mach-ar7/ar7.h> 14 15 #define EXCEPT_OFFSET 0x80 16 #define PACE_OFFSET 0xA0 17 #define CHNLS_OFFSET 0x200 18 19 #define REG_OFFSET(irq, reg) ((irq) / 32 * 0x4 + reg * 0x10) 20 #define SEC_REG_OFFSET(reg) (EXCEPT_OFFSET + reg * 0x8) 21 #define SEC_SR_OFFSET (SEC_REG_OFFSET(0)) /* 0x80 */ 22 #define CR_OFFSET(irq) (REG_OFFSET(irq, 1)) /* 0x10 */ 23 #define SEC_CR_OFFSET (SEC_REG_OFFSET(1)) /* 0x88 */ 24 #define ESR_OFFSET(irq) (REG_OFFSET(irq, 2)) /* 0x20 */ 25 #define SEC_ESR_OFFSET (SEC_REG_OFFSET(2)) /* 0x90 */ 26 #define ECR_OFFSET(irq) (REG_OFFSET(irq, 3)) /* 0x30 */ 27 #define SEC_ECR_OFFSET (SEC_REG_OFFSET(3)) /* 0x98 */ 28 #define PIR_OFFSET (0x40) 29 #define MSR_OFFSET (0x44) 30 #define PM_OFFSET(irq) (REG_OFFSET(irq, 5)) /* 0x50 */ 31 #define TM_OFFSET(irq) (REG_OFFSET(irq, 6)) /* 0x60 */ 32 33 #define REG(addr) ((u32 *)(KSEG1ADDR(AR7_REGS_IRQ) + addr)) 34 35 #define CHNL_OFFSET(chnl) (CHNLS_OFFSET + (chnl * 4)) 36 37 static int ar7_irq_base; 38 39 static void ar7_unmask_irq(struct irq_data *d) 40 { 41 writel(1 << ((d->irq - ar7_irq_base) % 32), 42 REG(ESR_OFFSET(d->irq - ar7_irq_base))); 43 } 44 45 static void ar7_mask_irq(struct irq_data *d) 46 { 47 writel(1 << ((d->irq - ar7_irq_base) % 32), 48 REG(ECR_OFFSET(d->irq - ar7_irq_base))); 49 } 50 51 static void ar7_ack_irq(struct irq_data *d) 52 { 53 writel(1 << ((d->irq - ar7_irq_base) % 32), 54 REG(CR_OFFSET(d->irq - ar7_irq_base))); 55 } 56 57 static void ar7_unmask_sec_irq(struct irq_data *d) 58 { 59 writel(1 << (d->irq - ar7_irq_base - 40), REG(SEC_ESR_OFFSET)); 60 } 61 62 static void ar7_mask_sec_irq(struct irq_data *d) 63 { 64 writel(1 << (d->irq - ar7_irq_base - 40), REG(SEC_ECR_OFFSET)); 65 } 66 67 static void ar7_ack_sec_irq(struct irq_data *d) 68 { 69 writel(1 << (d->irq - ar7_irq_base - 40), REG(SEC_CR_OFFSET)); 70 } 71 72 static struct irq_chip ar7_irq_type = { 73 .name = "AR7", 74 .irq_unmask = ar7_unmask_irq, 75 .irq_mask = ar7_mask_irq, 76 .irq_ack = ar7_ack_irq 77 }; 78 79 static struct irq_chip ar7_sec_irq_type = { 80 .name = "AR7", 81 .irq_unmask = ar7_unmask_sec_irq, 82 .irq_mask = ar7_mask_sec_irq, 83 .irq_ack = ar7_ack_sec_irq, 84 }; 85 86 static struct irqaction ar7_cascade_action = { 87 .handler = no_action, 88 .name = "AR7 cascade interrupt", 89 .flags = IRQF_NO_THREAD, 90 }; 91 92 static void __init ar7_irq_init(int base) 93 { 94 int i; 95 /* 96 * Disable interrupts and clear pending 97 */ 98 writel(0xffffffff, REG(ECR_OFFSET(0))); 99 writel(0xff, REG(ECR_OFFSET(32))); 100 writel(0xffffffff, REG(SEC_ECR_OFFSET)); 101 writel(0xffffffff, REG(CR_OFFSET(0))); 102 writel(0xff, REG(CR_OFFSET(32))); 103 writel(0xffffffff, REG(SEC_CR_OFFSET)); 104 105 ar7_irq_base = base; 106 107 for (i = 0; i < 40; i++) { 108 writel(i, REG(CHNL_OFFSET(i))); 109 /* Primary IRQ's */ 110 irq_set_chip_and_handler(base + i, &ar7_irq_type, 111 handle_level_irq); 112 /* Secondary IRQ's */ 113 if (i < 32) 114 irq_set_chip_and_handler(base + i + 40, 115 &ar7_sec_irq_type, 116 handle_level_irq); 117 } 118 119 setup_irq(2, &ar7_cascade_action); 120 setup_irq(ar7_irq_base, &ar7_cascade_action); 121 set_c0_status(IE_IRQ0); 122 } 123 124 void __init arch_init_irq(void) 125 { 126 mips_cpu_irq_init(); 127 ar7_irq_init(8); 128 } 129 130 static void ar7_cascade(void) 131 { 132 u32 status; 133 int i, irq; 134 135 /* Primary IRQ's */ 136 irq = readl(REG(PIR_OFFSET)) & 0x3f; 137 if (irq) { 138 do_IRQ(ar7_irq_base + irq); 139 return; 140 } 141 142 /* Secondary IRQ's are cascaded through primary '0' */ 143 writel(1, REG(CR_OFFSET(irq))); 144 status = readl(REG(SEC_SR_OFFSET)); 145 for (i = 0; i < 32; i++) { 146 if (status & 1) { 147 do_IRQ(ar7_irq_base + i + 40); 148 return; 149 } 150 status >>= 1; 151 } 152 153 spurious_interrupt(); 154 } 155 156 asmlinkage void plat_irq_dispatch(void) 157 { 158 unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM; 159 if (pending & STATUSF_IP7) /* cpu timer */ 160 do_IRQ(7); 161 else if (pending & STATUSF_IP2) /* int0 hardware line */ 162 ar7_cascade(); 163 else 164 spurious_interrupt(); 165 } 166