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