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(struct irq_data *d) 80 { 81 unsigned int irq = d->irq - IRQ_INTERNAL_BASE; 82 u32 mask; 83 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(struct irq_data *d) 90 { 91 unsigned int irq = d->irq - IRQ_INTERNAL_BASE; 92 u32 mask; 93 94 mask = bcm_perf_readl(PERF_IRQMASK_REG); 95 mask |= (1 << irq); 96 bcm_perf_writel(mask, PERF_IRQMASK_REG); 97 } 98 99 /* 100 * external IRQs operations: mask/unmask and clear on PERF external 101 * irq control register. 102 */ 103 static void bcm63xx_external_irq_mask(struct irq_data *d) 104 { 105 unsigned int irq = d->irq - IRQ_EXT_BASE; 106 u32 reg; 107 108 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG); 109 reg &= ~EXTIRQ_CFG_MASK(irq); 110 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG); 111 } 112 113 static void bcm63xx_external_irq_unmask(struct irq_data *d) 114 { 115 unsigned int irq = d->irq - IRQ_EXT_BASE; 116 u32 reg; 117 118 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG); 119 reg |= EXTIRQ_CFG_MASK(irq); 120 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG); 121 } 122 123 static void bcm63xx_external_irq_clear(struct irq_data *d) 124 { 125 unsigned int irq = d->irq - IRQ_EXT_BASE; 126 u32 reg; 127 128 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG); 129 reg |= EXTIRQ_CFG_CLEAR(irq); 130 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG); 131 } 132 133 static unsigned int bcm63xx_external_irq_startup(struct irq_data *d) 134 { 135 set_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE)); 136 irq_enable_hazard(); 137 bcm63xx_external_irq_unmask(d); 138 return 0; 139 } 140 141 static void bcm63xx_external_irq_shutdown(struct irq_data *d) 142 { 143 bcm63xx_external_irq_mask(d); 144 clear_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE)); 145 irq_disable_hazard(); 146 } 147 148 static int bcm63xx_external_irq_set_type(struct irq_data *d, 149 unsigned int flow_type) 150 { 151 unsigned int irq = d->irq - IRQ_EXT_BASE; 152 u32 reg; 153 154 flow_type &= IRQ_TYPE_SENSE_MASK; 155 156 if (flow_type == IRQ_TYPE_NONE) 157 flow_type = IRQ_TYPE_LEVEL_LOW; 158 159 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG); 160 switch (flow_type) { 161 case IRQ_TYPE_EDGE_BOTH: 162 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq); 163 reg |= EXTIRQ_CFG_BOTHEDGE(irq); 164 break; 165 166 case IRQ_TYPE_EDGE_RISING: 167 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq); 168 reg |= EXTIRQ_CFG_SENSE(irq); 169 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq); 170 break; 171 172 case IRQ_TYPE_EDGE_FALLING: 173 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq); 174 reg &= ~EXTIRQ_CFG_SENSE(irq); 175 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq); 176 break; 177 178 case IRQ_TYPE_LEVEL_HIGH: 179 reg |= EXTIRQ_CFG_LEVELSENSE(irq); 180 reg |= EXTIRQ_CFG_SENSE(irq); 181 break; 182 183 case IRQ_TYPE_LEVEL_LOW: 184 reg |= EXTIRQ_CFG_LEVELSENSE(irq); 185 reg &= ~EXTIRQ_CFG_SENSE(irq); 186 break; 187 188 default: 189 printk(KERN_ERR "bogus flow type combination given !\n"); 190 return -EINVAL; 191 } 192 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG); 193 194 irqd_set_trigger_type(d, flow_type); 195 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) 196 __irq_set_handler_locked(d->irq, handle_level_irq); 197 else 198 __irq_set_handler_locked(d->irq, handle_edge_irq); 199 200 return IRQ_SET_MASK_OK_NOCOPY; 201 } 202 203 static struct irq_chip bcm63xx_internal_irq_chip = { 204 .name = "bcm63xx_ipic", 205 .irq_mask = bcm63xx_internal_irq_mask, 206 .irq_unmask = bcm63xx_internal_irq_unmask, 207 }; 208 209 static struct irq_chip bcm63xx_external_irq_chip = { 210 .name = "bcm63xx_epic", 211 .irq_startup = bcm63xx_external_irq_startup, 212 .irq_shutdown = bcm63xx_external_irq_shutdown, 213 214 .irq_ack = bcm63xx_external_irq_clear, 215 216 .irq_mask = bcm63xx_external_irq_mask, 217 .irq_unmask = bcm63xx_external_irq_unmask, 218 219 .irq_set_type = bcm63xx_external_irq_set_type, 220 }; 221 222 static struct irqaction cpu_ip2_cascade_action = { 223 .handler = no_action, 224 .name = "cascade_ip2", 225 .flags = IRQF_NO_THREAD, 226 }; 227 228 void __init arch_init_irq(void) 229 { 230 int i; 231 232 mips_cpu_irq_init(); 233 for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i) 234 irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip, 235 handle_level_irq); 236 237 for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i) 238 irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip, 239 handle_edge_irq); 240 241 setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action); 242 } 243