1 /* 2 * Platform information definitions. 3 * 4 * Copied from arch/ppc/syslib/cpm2_pic.c with minor subsequent updates 5 * to make in work in arch/powerpc/. Original (c) belongs to Dan Malek. 6 * 7 * Author: Vitaly Bordug <vbordug@ru.mvista.com> 8 * 9 * 1999-2001 (c) Dan Malek <dan@embeddedalley.com> 10 * 2006 (c) MontaVista Software, Inc. 11 * 12 * This file is licensed under the terms of the GNU General Public License 13 * version 2. This program is licensed "as is" without any warranty of any 14 * kind, whether express or implied. 15 */ 16 17 /* The CPM2 internal interrupt controller. It is usually 18 * the only interrupt controller. 19 * There are two 32-bit registers (high/low) for up to 64 20 * possible interrupts. 21 * 22 * Now, the fun starts.....Interrupt Numbers DO NOT MAP 23 * in a simple arithmetic fashion to mask or pending registers. 24 * That is, interrupt 4 does not map to bit position 4. 25 * We create two tables, indexed by vector number, to indicate 26 * which register to use and which bit in the register to use. 27 */ 28 29 #include <linux/stddef.h> 30 #include <linux/init.h> 31 #include <linux/sched.h> 32 #include <linux/signal.h> 33 #include <linux/irq.h> 34 35 #include <asm/immap_cpm2.h> 36 #include <asm/mpc8260.h> 37 #include <asm/io.h> 38 #include <asm/prom.h> 39 40 #include "cpm2_pic.h" 41 42 static struct device_node *cpm2_pic_node; 43 static struct irq_host *cpm2_pic_host; 44 #define NR_MASK_WORDS ((NR_IRQS + 31) / 32) 45 static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; 46 47 static const u_char irq_to_siureg[] = { 48 1, 1, 1, 1, 1, 1, 1, 1, 49 1, 1, 1, 1, 1, 1, 1, 1, 50 0, 0, 0, 0, 0, 0, 0, 0, 51 0, 0, 0, 0, 0, 0, 0, 0, 52 1, 1, 1, 1, 1, 1, 1, 1, 53 1, 1, 1, 1, 1, 1, 1, 1, 54 0, 0, 0, 0, 0, 0, 0, 0, 55 0, 0, 0, 0, 0, 0, 0, 0 56 }; 57 58 /* bit numbers do not match the docs, these are precomputed so the bit for 59 * a given irq is (1 << irq_to_siubit[irq]) */ 60 static const u_char irq_to_siubit[] = { 61 0, 15, 14, 13, 12, 11, 10, 9, 62 8, 7, 6, 5, 4, 3, 2, 1, 63 2, 1, 0, 14, 13, 12, 11, 10, 64 9, 8, 7, 6, 5, 4, 3, 0, 65 31, 30, 29, 28, 27, 26, 25, 24, 66 23, 22, 21, 20, 19, 18, 17, 16, 67 16, 17, 18, 19, 20, 21, 22, 23, 68 24, 25, 26, 27, 28, 29, 30, 31, 69 }; 70 71 static void cpm2_mask_irq(unsigned int irq_nr) 72 { 73 int bit, word; 74 volatile uint *simr; 75 76 irq_nr -= CPM_IRQ_OFFSET; 77 78 bit = irq_to_siubit[irq_nr]; 79 word = irq_to_siureg[irq_nr]; 80 81 simr = &(cpm2_intctl->ic_simrh); 82 ppc_cached_irq_mask[word] &= ~(1 << bit); 83 simr[word] = ppc_cached_irq_mask[word]; 84 } 85 86 static void cpm2_unmask_irq(unsigned int irq_nr) 87 { 88 int bit, word; 89 volatile uint *simr; 90 91 irq_nr -= CPM_IRQ_OFFSET; 92 93 bit = irq_to_siubit[irq_nr]; 94 word = irq_to_siureg[irq_nr]; 95 96 simr = &(cpm2_intctl->ic_simrh); 97 ppc_cached_irq_mask[word] |= 1 << bit; 98 simr[word] = ppc_cached_irq_mask[word]; 99 } 100 101 static void cpm2_mask_and_ack(unsigned int irq_nr) 102 { 103 int bit, word; 104 volatile uint *simr, *sipnr; 105 106 irq_nr -= CPM_IRQ_OFFSET; 107 108 bit = irq_to_siubit[irq_nr]; 109 word = irq_to_siureg[irq_nr]; 110 111 simr = &(cpm2_intctl->ic_simrh); 112 sipnr = &(cpm2_intctl->ic_sipnrh); 113 ppc_cached_irq_mask[word] &= ~(1 << bit); 114 simr[word] = ppc_cached_irq_mask[word]; 115 sipnr[word] = 1 << bit; 116 } 117 118 static void cpm2_end_irq(unsigned int irq_nr) 119 { 120 int bit, word; 121 volatile uint *simr; 122 123 if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)) 124 && irq_desc[irq_nr].action) { 125 126 irq_nr -= CPM_IRQ_OFFSET; 127 bit = irq_to_siubit[irq_nr]; 128 word = irq_to_siureg[irq_nr]; 129 130 simr = &(cpm2_intctl->ic_simrh); 131 ppc_cached_irq_mask[word] |= 1 << bit; 132 simr[word] = ppc_cached_irq_mask[word]; 133 /* 134 * Work around large numbers of spurious IRQs on PowerPC 82xx 135 * systems. 136 */ 137 mb(); 138 } 139 } 140 141 static struct irq_chip cpm2_pic = { 142 .typename = " CPM2 SIU ", 143 .enable = cpm2_unmask_irq, 144 .disable = cpm2_mask_irq, 145 .unmask = cpm2_unmask_irq, 146 .mask_ack = cpm2_mask_and_ack, 147 .end = cpm2_end_irq, 148 }; 149 150 unsigned int cpm2_get_irq(void) 151 { 152 int irq; 153 unsigned long bits; 154 155 /* For CPM2, read the SIVEC register and shift the bits down 156 * to get the irq number. */ 157 bits = cpm2_intctl->ic_sivec; 158 irq = bits >> 26; 159 160 if (irq == 0) 161 return(-1); 162 return irq+CPM_IRQ_OFFSET; 163 } 164 165 static int cpm2_pic_host_match(struct irq_host *h, struct device_node *node) 166 { 167 return cpm2_pic_node == NULL || cpm2_pic_node == node; 168 } 169 170 static int cpm2_pic_host_map(struct irq_host *h, unsigned int virq, 171 irq_hw_number_t hw) 172 { 173 pr_debug("cpm2_pic_host_map(%d, 0x%lx)\n", virq, hw); 174 175 get_irq_desc(virq)->status |= IRQ_LEVEL; 176 set_irq_chip_and_handler(virq, &cpm2_pic, handle_level_irq); 177 return 0; 178 } 179 180 static void cpm2_host_unmap(struct irq_host *h, unsigned int virq) 181 { 182 /* Make sure irq is masked in hardware */ 183 cpm2_mask_irq(virq); 184 185 /* remove chip and handler */ 186 set_irq_chip_and_handler(virq, NULL, NULL); 187 } 188 189 static int cpm2_pic_host_xlate(struct irq_host *h, struct device_node *ct, 190 u32 *intspec, unsigned int intsize, 191 irq_hw_number_t *out_hwirq, unsigned int *out_flags) 192 { 193 static const unsigned char map_cpm2_senses[4] = { 194 IRQ_TYPE_LEVEL_LOW, 195 IRQ_TYPE_LEVEL_HIGH, 196 IRQ_TYPE_EDGE_FALLING, 197 IRQ_TYPE_EDGE_RISING, 198 }; 199 200 *out_hwirq = intspec[0]; 201 if (intsize > 1 && intspec[1] < 4) 202 *out_flags = map_cpm2_senses[intspec[1]]; 203 else 204 *out_flags = IRQ_TYPE_NONE; 205 206 return 0; 207 } 208 209 static struct irq_host_ops cpm2_pic_host_ops = { 210 .match = cpm2_pic_host_match, 211 .map = cpm2_pic_host_map, 212 .unmap = cpm2_host_unmap, 213 .xlate = cpm2_pic_host_xlate, 214 }; 215 216 void cpm2_pic_init(struct device_node *node) 217 { 218 int i; 219 220 /* Clear the CPM IRQ controller, in case it has any bits set 221 * from the bootloader 222 */ 223 224 /* Mask out everything */ 225 226 cpm2_intctl->ic_simrh = 0x00000000; 227 cpm2_intctl->ic_simrl = 0x00000000; 228 229 wmb(); 230 231 /* Ack everything */ 232 cpm2_intctl->ic_sipnrh = 0xffffffff; 233 cpm2_intctl->ic_sipnrl = 0xffffffff; 234 wmb(); 235 236 /* Dummy read of the vector */ 237 i = cpm2_intctl->ic_sivec; 238 rmb(); 239 240 /* Initialize the default interrupt mapping priorities, 241 * in case the boot rom changed something on us. 242 */ 243 cpm2_intctl->ic_sicr = 0; 244 cpm2_intctl->ic_scprrh = 0x05309770; 245 cpm2_intctl->ic_scprrl = 0x05309770; 246 247 /* create a legacy host */ 248 if (node) 249 cpm2_pic_node = of_node_get(node); 250 251 cpm2_pic_host = irq_alloc_host(IRQ_HOST_MAP_LINEAR, 64, &cpm2_pic_host_ops, 64); 252 if (cpm2_pic_host == NULL) { 253 printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n"); 254 return; 255 } 256 } 257