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 * arch/sh64/kernel/irq_intc.c 7 * 8 * Copyright (C) 2000, 2001 Paolo Alberelli 9 * Copyright (C) 2003 Paul Mundt 10 * 11 * Interrupt Controller support for SH5 INTC. 12 * Per-interrupt selective. IRLM=0 (Fixed priority) is not 13 * supported being useless without a cascaded interrupt 14 * controller. 15 * 16 */ 17 18#include <linux/init.h> 19#include <linux/irq.h> 20#include <linux/kernel.h> 21#include <linux/stddef.h> 22#include <linux/bitops.h> /* this includes also <asm/registers.h */ 23 /* which is required to remap register */ 24 /* names used into __asm__ blocks... */ 25 26#include <asm/hardware.h> 27#include <asm/platform.h> 28#include <asm/page.h> 29#include <asm/io.h> 30#include <asm/irq.h> 31 32/* 33 * Maybe the generic Peripheral block could move to a more 34 * generic include file. INTC Block will be defined here 35 * and only here to make INTC self-contained in a single 36 * file. 37 */ 38#define INTC_BLOCK_OFFSET 0x01000000 39 40/* Base */ 41#define INTC_BASE PHYS_PERIPHERAL_BLOCK + \ 42 INTC_BLOCK_OFFSET 43 44/* Address */ 45#define INTC_ICR_SET (intc_virt + 0x0) 46#define INTC_ICR_CLEAR (intc_virt + 0x8) 47#define INTC_INTPRI_0 (intc_virt + 0x10) 48#define INTC_INTSRC_0 (intc_virt + 0x50) 49#define INTC_INTSRC_1 (intc_virt + 0x58) 50#define INTC_INTREQ_0 (intc_virt + 0x60) 51#define INTC_INTREQ_1 (intc_virt + 0x68) 52#define INTC_INTENB_0 (intc_virt + 0x70) 53#define INTC_INTENB_1 (intc_virt + 0x78) 54#define INTC_INTDSB_0 (intc_virt + 0x80) 55#define INTC_INTDSB_1 (intc_virt + 0x88) 56 57#define INTC_ICR_IRLM 0x1 58#define INTC_INTPRI_PREGS 8 /* 8 Priority Registers */ 59#define INTC_INTPRI_PPREG 8 /* 8 Priorities per Register */ 60 61 62/* 63 * Mapper between the vector ordinal and the IRQ number 64 * passed to kernel/device drivers. 65 */ 66int intc_evt_to_irq[(0xE20/0x20)+1] = { 67 -1, -1, -1, -1, -1, -1, -1, -1, /* 0x000 - 0x0E0 */ 68 -1, -1, -1, -1, -1, -1, -1, -1, /* 0x100 - 0x1E0 */ 69 0, 0, 0, 0, 0, 1, 0, 0, /* 0x200 - 0x2E0 */ 70 2, 0, 0, 3, 0, 0, 0, -1, /* 0x300 - 0x3E0 */ 71 32, 33, 34, 35, 36, 37, 38, -1, /* 0x400 - 0x4E0 */ 72 -1, -1, -1, 63, -1, -1, -1, -1, /* 0x500 - 0x5E0 */ 73 -1, -1, 18, 19, 20, 21, 22, -1, /* 0x600 - 0x6E0 */ 74 39, 40, 41, 42, -1, -1, -1, -1, /* 0x700 - 0x7E0 */ 75 4, 5, 6, 7, -1, -1, -1, -1, /* 0x800 - 0x8E0 */ 76 -1, -1, -1, -1, -1, -1, -1, -1, /* 0x900 - 0x9E0 */ 77 12, 13, 14, 15, 16, 17, -1, -1, /* 0xA00 - 0xAE0 */ 78 -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB00 - 0xBE0 */ 79 -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC00 - 0xCE0 */ 80 -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD00 - 0xDE0 */ 81 -1, -1 /* 0xE00 - 0xE20 */ 82}; 83 84/* 85 * Opposite mapper. 86 */ 87static int IRQ_to_vectorN[NR_INTC_IRQS] = { 88 0x12, 0x15, 0x18, 0x1B, 0x40, 0x41, 0x42, 0x43, /* 0- 7 */ 89 -1, -1, -1, -1, 0x50, 0x51, 0x52, 0x53, /* 8-15 */ 90 0x54, 0x55, 0x32, 0x33, 0x34, 0x35, 0x36, -1, /* 16-23 */ 91 -1, -1, -1, -1, -1, -1, -1, -1, /* 24-31 */ 92 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x38, /* 32-39 */ 93 0x39, 0x3A, 0x3B, -1, -1, -1, -1, -1, /* 40-47 */ 94 -1, -1, -1, -1, -1, -1, -1, -1, /* 48-55 */ 95 -1, -1, -1, -1, -1, -1, -1, 0x2B, /* 56-63 */ 96 97}; 98 99static unsigned long intc_virt; 100 101static unsigned int startup_intc_irq(unsigned int irq); 102static void shutdown_intc_irq(unsigned int irq); 103static void enable_intc_irq(unsigned int irq); 104static void disable_intc_irq(unsigned int irq); 105static void mask_and_ack_intc(unsigned int); 106static void end_intc_irq(unsigned int irq); 107 108static struct hw_interrupt_type intc_irq_type = { 109 .typename = "INTC", 110 .startup = startup_intc_irq, 111 .shutdown = shutdown_intc_irq, 112 .enable = enable_intc_irq, 113 .disable = disable_intc_irq, 114 .ack = mask_and_ack_intc, 115 .end = end_intc_irq 116}; 117 118static int irlm; /* IRL mode */ 119 120static unsigned int startup_intc_irq(unsigned int irq) 121{ 122 enable_intc_irq(irq); 123 return 0; /* never anything pending */ 124} 125 126static void shutdown_intc_irq(unsigned int irq) 127{ 128 disable_intc_irq(irq); 129} 130 131static void enable_intc_irq(unsigned int irq) 132{ 133 unsigned long reg; 134 unsigned long bitmask; 135 136 if ((irq <= IRQ_IRL3) && (irlm == NO_PRIORITY)) 137 printk("Trying to use straight IRL0-3 with an encoding platform.\n"); 138 139 if (irq < 32) { 140 reg = INTC_INTENB_0; 141 bitmask = 1 << irq; 142 } else { 143 reg = INTC_INTENB_1; 144 bitmask = 1 << (irq - 32); 145 } 146 147 ctrl_outl(bitmask, reg); 148} 149 150static void disable_intc_irq(unsigned int irq) 151{ 152 unsigned long reg; 153 unsigned long bitmask; 154 155 if (irq < 32) { 156 reg = INTC_INTDSB_0; 157 bitmask = 1 << irq; 158 } else { 159 reg = INTC_INTDSB_1; 160 bitmask = 1 << (irq - 32); 161 } 162 163 ctrl_outl(bitmask, reg); 164} 165 166static void mask_and_ack_intc(unsigned int irq) 167{ 168 disable_intc_irq(irq); 169} 170 171static void end_intc_irq(unsigned int irq) 172{ 173 enable_intc_irq(irq); 174} 175 176/* For future use, if we ever support IRLM=0) */ 177void make_intc_irq(unsigned int irq) 178{ 179 disable_irq_nosync(irq); 180 irq_desc[irq].chip = &intc_irq_type; 181 disable_intc_irq(irq); 182} 183 184#if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL) 185int intc_irq_describe(char* p, int irq) 186{ 187 if (irq < NR_INTC_IRQS) 188 return sprintf(p, "(0x%3x)", IRQ_to_vectorN[irq]*0x20); 189 else 190 return 0; 191} 192#endif 193 194void __init init_IRQ(void) 195{ 196 unsigned long long __dummy0, __dummy1=~0x00000000100000f0; 197 unsigned long reg; 198 unsigned long data; 199 int i; 200 201 intc_virt = onchip_remap(INTC_BASE, 1024, "INTC"); 202 if (!intc_virt) { 203 panic("Unable to remap INTC\n"); 204 } 205 206 207 /* Set default: per-line enable/disable, priority driven ack/eoi */ 208 for (i = 0; i < NR_INTC_IRQS; i++) { 209 if (platform_int_priority[i] != NO_PRIORITY) { 210 irq_desc[i].chip = &intc_irq_type; 211 } 212 } 213 214 215 /* Disable all interrupts and set all priorities to 0 to avoid trouble */ 216 ctrl_outl(-1, INTC_INTDSB_0); 217 ctrl_outl(-1, INTC_INTDSB_1); 218 219 for (reg = INTC_INTPRI_0, i = 0; i < INTC_INTPRI_PREGS; i++, reg += 8) 220 ctrl_outl( NO_PRIORITY, reg); 221 222 223 /* Set IRLM */ 224 /* If all the priorities are set to 'no priority', then 225 * assume we are using encoded mode. 226 */ 227 irlm = platform_int_priority[IRQ_IRL0] + platform_int_priority[IRQ_IRL1] + \ 228 platform_int_priority[IRQ_IRL2] + platform_int_priority[IRQ_IRL3]; 229 230 if (irlm == NO_PRIORITY) { 231 /* IRLM = 0 */ 232 reg = INTC_ICR_CLEAR; 233 i = IRQ_INTA; 234 printk("Trying to use encoded IRL0-3. IRLs unsupported.\n"); 235 } else { 236 /* IRLM = 1 */ 237 reg = INTC_ICR_SET; 238 i = IRQ_IRL0; 239 } 240 ctrl_outl(INTC_ICR_IRLM, reg); 241 242 /* Set interrupt priorities according to platform description */ 243 for (data = 0, reg = INTC_INTPRI_0; i < NR_INTC_IRQS; i++) { 244 data |= platform_int_priority[i] << ((i % INTC_INTPRI_PPREG) * 4); 245 if ((i % INTC_INTPRI_PPREG) == (INTC_INTPRI_PPREG - 1)) { 246 /* Upon the 7th, set Priority Register */ 247 ctrl_outl(data, reg); 248 data = 0; 249 reg += 8; 250 } 251 } 252 253#ifdef CONFIG_SH_CAYMAN 254 { 255 extern void init_cayman_irq(void); 256 257 init_cayman_irq(); 258 } 259#endif 260 261 /* 262 * And now let interrupts come in. 263 * sti() is not enough, we need to 264 * lower priority, too. 265 */ 266 __asm__ __volatile__("getcon " __SR ", %0\n\t" 267 "and %0, %1, %0\n\t" 268 "putcon %0, " __SR "\n\t" 269 : "=&r" (__dummy0) 270 : "r" (__dummy1)); 271} 272