1 /* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> 7 * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr> 8 */ 9 10 #include <linux/kernel.h> 11 #include <linux/init.h> 12 #include <linux/interrupt.h> 13 #include <linux/module.h> 14 #include <linux/irq.h> 15 #include <asm/irq_cpu.h> 16 #include <asm/mipsregs.h> 17 #include <bcm63xx_cpu.h> 18 #include <bcm63xx_regs.h> 19 #include <bcm63xx_io.h> 20 #include <bcm63xx_irq.h> 21 22 /* 23 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not 24 * prioritize any interrupt relatively to another. the static counter 25 * will resume the loop where it ended the last time we left this 26 * function. 27 */ 28 static void bcm63xx_irq_dispatch_internal(void) 29 { 30 u32 pending; 31 static int i; 32 33 pending = bcm_perf_readl(PERF_IRQMASK_REG) & 34 bcm_perf_readl(PERF_IRQSTAT_REG); 35 36 if (!pending) 37 return ; 38 39 while (1) { 40 int to_call = i; 41 42 i = (i + 1) & 0x1f; 43 if (pending & (1 << to_call)) { 44 do_IRQ(to_call + IRQ_INTERNAL_BASE); 45 break; 46 } 47 } 48 } 49 50 asmlinkage void plat_irq_dispatch(void) 51 { 52 u32 cause; 53 54 do { 55 cause = read_c0_cause() & read_c0_status() & ST0_IM; 56 57 if (!cause) 58 break; 59 60 if (cause & CAUSEF_IP7) 61 do_IRQ(7); 62 if (cause & CAUSEF_IP2) 63 bcm63xx_irq_dispatch_internal(); 64 if (cause & CAUSEF_IP3) 65 do_IRQ(IRQ_EXT_0); 66 if (cause & CAUSEF_IP4) 67 do_IRQ(IRQ_EXT_1); 68 if (cause & CAUSEF_IP5) 69 do_IRQ(IRQ_EXT_2); 70 if (cause & CAUSEF_IP6) 71 do_IRQ(IRQ_EXT_3); 72 } while (1); 73 } 74 75 /* 76 * internal IRQs operations: only mask/unmask on PERF irq mask 77 * register. 78 */ 79 static inline void bcm63xx_internal_irq_mask(unsigned int irq) 80 { 81 u32 mask; 82 83 irq -= IRQ_INTERNAL_BASE; 84 mask = bcm_perf_readl(PERF_IRQMASK_REG); 85 mask &= ~(1 << irq); 86 bcm_perf_writel(mask, PERF_IRQMASK_REG); 87 } 88 89 static void bcm63xx_internal_irq_unmask(unsigned int irq) 90 { 91 u32 mask; 92 93 irq -= IRQ_INTERNAL_BASE; 94 mask = bcm_perf_readl(PERF_IRQMASK_REG); 95 mask |= (1 << irq); 96 bcm_perf_writel(mask, PERF_IRQMASK_REG); 97 } 98 99 static unsigned int bcm63xx_internal_irq_startup(unsigned int irq) 100 { 101 bcm63xx_internal_irq_unmask(irq); 102 return 0; 103 } 104 105 /* 106 * external IRQs operations: mask/unmask and clear on PERF external 107 * irq control register. 108 */ 109 static void bcm63xx_external_irq_mask(unsigned int irq) 110 { 111 u32 reg; 112 113 irq -= IRQ_EXT_BASE; 114 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG); 115 reg &= ~EXTIRQ_CFG_MASK(irq); 116 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG); 117 } 118 119 static void bcm63xx_external_irq_unmask(unsigned int irq) 120 { 121 u32 reg; 122 123 irq -= IRQ_EXT_BASE; 124 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG); 125 reg |= EXTIRQ_CFG_MASK(irq); 126 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG); 127 } 128 129 static void bcm63xx_external_irq_clear(unsigned int irq) 130 { 131 u32 reg; 132 133 irq -= IRQ_EXT_BASE; 134 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG); 135 reg |= EXTIRQ_CFG_CLEAR(irq); 136 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG); 137 } 138 139 static unsigned int bcm63xx_external_irq_startup(unsigned int irq) 140 { 141 set_c0_status(0x100 << (irq - IRQ_MIPS_BASE)); 142 irq_enable_hazard(); 143 bcm63xx_external_irq_unmask(irq); 144 return 0; 145 } 146 147 static void bcm63xx_external_irq_shutdown(unsigned int irq) 148 { 149 bcm63xx_external_irq_mask(irq); 150 clear_c0_status(0x100 << (irq - IRQ_MIPS_BASE)); 151 irq_disable_hazard(); 152 } 153 154 static int bcm63xx_external_irq_set_type(unsigned int irq, 155 unsigned int flow_type) 156 { 157 u32 reg; 158 struct irq_desc *desc = irq_desc + irq; 159 160 irq -= IRQ_EXT_BASE; 161 162 flow_type &= IRQ_TYPE_SENSE_MASK; 163 164 if (flow_type == IRQ_TYPE_NONE) 165 flow_type = IRQ_TYPE_LEVEL_LOW; 166 167 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG); 168 switch (flow_type) { 169 case IRQ_TYPE_EDGE_BOTH: 170 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq); 171 reg |= EXTIRQ_CFG_BOTHEDGE(irq); 172 break; 173 174 case IRQ_TYPE_EDGE_RISING: 175 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq); 176 reg |= EXTIRQ_CFG_SENSE(irq); 177 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq); 178 break; 179 180 case IRQ_TYPE_EDGE_FALLING: 181 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq); 182 reg &= ~EXTIRQ_CFG_SENSE(irq); 183 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq); 184 break; 185 186 case IRQ_TYPE_LEVEL_HIGH: 187 reg |= EXTIRQ_CFG_LEVELSENSE(irq); 188 reg |= EXTIRQ_CFG_SENSE(irq); 189 break; 190 191 case IRQ_TYPE_LEVEL_LOW: 192 reg |= EXTIRQ_CFG_LEVELSENSE(irq); 193 reg &= ~EXTIRQ_CFG_SENSE(irq); 194 break; 195 196 default: 197 printk(KERN_ERR "bogus flow type combination given !\n"); 198 return -EINVAL; 199 } 200 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG); 201 202 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) { 203 desc->status |= IRQ_LEVEL; 204 desc->handle_irq = handle_level_irq; 205 } else { 206 desc->handle_irq = handle_edge_irq; 207 } 208 209 return 0; 210 } 211 212 static struct irq_chip bcm63xx_internal_irq_chip = { 213 .name = "bcm63xx_ipic", 214 .startup = bcm63xx_internal_irq_startup, 215 .shutdown = bcm63xx_internal_irq_mask, 216 217 .mask = bcm63xx_internal_irq_mask, 218 .mask_ack = bcm63xx_internal_irq_mask, 219 .unmask = bcm63xx_internal_irq_unmask, 220 }; 221 222 static struct irq_chip bcm63xx_external_irq_chip = { 223 .name = "bcm63xx_epic", 224 .startup = bcm63xx_external_irq_startup, 225 .shutdown = bcm63xx_external_irq_shutdown, 226 227 .ack = bcm63xx_external_irq_clear, 228 229 .mask = bcm63xx_external_irq_mask, 230 .unmask = bcm63xx_external_irq_unmask, 231 232 .set_type = bcm63xx_external_irq_set_type, 233 }; 234 235 static struct irqaction cpu_ip2_cascade_action = { 236 .handler = no_action, 237 .name = "cascade_ip2", 238 }; 239 240 void __init arch_init_irq(void) 241 { 242 int i; 243 244 mips_cpu_irq_init(); 245 for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i) 246 set_irq_chip_and_handler(i, &bcm63xx_internal_irq_chip, 247 handle_level_irq); 248 249 for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i) 250 set_irq_chip_and_handler(i, &bcm63xx_external_irq_chip, 251 handle_edge_irq); 252 253 setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action); 254 } 255