1/* $Id: irq_ipr.c,v 1.1.1.1 2008/10/15 03:26:18 james26_jang Exp $ 2 * 3 * linux/arch/sh/kernel/irq_ipr.c 4 * 5 * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi 6 * Copyright (C) 2000 Kazumoto Kojima 7 * 8 * Interrupt handling for IPR-based IRQ. 9 * 10 * Supported system: 11 * On-chip supporting modules (TMU, RTC, etc.). 12 * On-chip supporting modules for SH7709/SH7709A/SH7729. 13 * Hitachi SolutionEngine external I/O: 14 * MS7709SE01, MS7709ASE01, and MS7750SE01 15 * 16 */ 17 18#include <linux/config.h> 19#include <linux/init.h> 20#include <linux/irq.h> 21 22#include <asm/system.h> 23#include <asm/io.h> 24#include <asm/machvec.h> 25 26struct ipr_data { 27 unsigned int addr; /* Address of Interrupt Priority Register */ 28 int shift; /* Shifts of the 16-bit data */ 29 int priority; /* The priority */ 30}; 31static struct ipr_data ipr_data[NR_IRQS]; 32 33static void enable_ipr_irq(unsigned int irq); 34static void disable_ipr_irq(unsigned int irq); 35 36/* shutdown is same as "disable" */ 37#define shutdown_ipr_irq disable_ipr_irq 38 39static void mask_and_ack_ipr(unsigned int); 40static void end_ipr_irq(unsigned int irq); 41 42static unsigned int startup_ipr_irq(unsigned int irq) 43{ 44 enable_ipr_irq(irq); 45 return 0; /* never anything pending */ 46} 47 48static struct hw_interrupt_type ipr_irq_type = { 49 "IPR-IRQ", 50 startup_ipr_irq, 51 shutdown_ipr_irq, 52 enable_ipr_irq, 53 disable_ipr_irq, 54 mask_and_ack_ipr, 55 end_ipr_irq 56}; 57 58static void disable_ipr_irq(unsigned int irq) 59{ 60 unsigned long val, flags; 61 unsigned int addr = ipr_data[irq].addr; 62 unsigned short mask = 0xffff ^ (0x0f << ipr_data[irq].shift); 63 64 /* Set the priority in IPR to 0 */ 65 save_and_cli(flags); 66 val = ctrl_inw(addr); 67 val &= mask; 68 ctrl_outw(val, addr); 69 restore_flags(flags); 70} 71 72static void enable_ipr_irq(unsigned int irq) 73{ 74 unsigned long val, flags; 75 unsigned int addr = ipr_data[irq].addr; 76 int priority = ipr_data[irq].priority; 77 unsigned short value = (priority << ipr_data[irq].shift); 78 79 /* Set priority in IPR back to original value */ 80 save_and_cli(flags); 81 val = ctrl_inw(addr); 82 val |= value; 83 ctrl_outw(val, addr); 84 restore_flags(flags); 85} 86 87static void mask_and_ack_ipr(unsigned int irq) 88{ 89 disable_ipr_irq(irq); 90 91#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) 92 /* This is needed when we use edge triggered setting */ 93 if (IRQ0_IRQ <= irq && irq <= IRQ5_IRQ) { 94 /* Clear external interrupt request */ 95 int a = ctrl_inb(INTC_IRR0); 96 a &= ~(1 << (irq - IRQ0_IRQ)); 97 ctrl_outb(a, INTC_IRR0); 98 } 99#endif 100} 101 102static void end_ipr_irq(unsigned int irq) 103{ 104 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) 105 enable_ipr_irq(irq); 106} 107 108void make_ipr_irq(unsigned int irq, unsigned int addr, int pos, int priority) 109{ 110 disable_irq_nosync(irq); 111 ipr_data[irq].addr = addr; 112 ipr_data[irq].shift = pos*4; /* POSition (0-3) x 4 means shift */ 113 ipr_data[irq].priority = priority; 114 115 irq_desc[irq].handler = &ipr_irq_type; 116 disable_ipr_irq(irq); 117} 118 119#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) 120static unsigned char pint_map[256]; 121static unsigned long portcr_mask = 0; 122 123static void enable_pint_irq(unsigned int irq); 124static void disable_pint_irq(unsigned int irq); 125 126/* shutdown is same as "disable" */ 127#define shutdown_pint_irq disable_pint_irq 128 129static void mask_and_ack_pint(unsigned int); 130static void end_pint_irq(unsigned int irq); 131 132static unsigned int startup_pint_irq(unsigned int irq) 133{ 134 enable_pint_irq(irq); 135 return 0; /* never anything pending */ 136} 137 138static struct hw_interrupt_type pint_irq_type = { 139 "PINT-IRQ", 140 startup_pint_irq, 141 shutdown_pint_irq, 142 enable_pint_irq, 143 disable_pint_irq, 144 mask_and_ack_pint, 145 end_pint_irq 146}; 147 148static void disable_pint_irq(unsigned int irq) 149{ 150 unsigned long val, flags; 151 152 save_and_cli(flags); 153 val = ctrl_inw(INTC_INTER); 154 val &= ~(1 << (irq - PINT_IRQ_BASE)); 155 ctrl_outw(val, INTC_INTER); /* disable PINTn */ 156 portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2); 157 restore_flags(flags); 158} 159 160static void enable_pint_irq(unsigned int irq) 161{ 162 unsigned long val, flags; 163 164 save_and_cli(flags); 165 val = ctrl_inw(INTC_INTER); 166 val |= 1 << (irq - PINT_IRQ_BASE); 167 ctrl_outw(val, INTC_INTER); /* enable PINTn */ 168 portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2; 169 restore_flags(flags); 170} 171 172static void mask_and_ack_pint(unsigned int irq) 173{ 174 disable_pint_irq(irq); 175} 176 177static void end_pint_irq(unsigned int irq) 178{ 179 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) 180 enable_pint_irq(irq); 181} 182 183void make_pint_irq(unsigned int irq) 184{ 185 disable_irq_nosync(irq); 186 irq_desc[irq].handler = &pint_irq_type; 187 disable_pint_irq(irq); 188} 189#endif 190 191void __init init_IRQ(void) 192{ 193#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) 194 int i; 195#endif 196 197 make_ipr_irq(TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY); 198 make_ipr_irq(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY); 199 200#ifdef SCI_ERI_IRQ 201 make_ipr_irq(SCI_ERI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); 202 make_ipr_irq(SCI_RXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); 203 make_ipr_irq(SCI_TXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); 204#endif 205 206#ifdef SCIF1_ERI_IRQ 207 make_ipr_irq(SCIF1_ERI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); 208 make_ipr_irq(SCIF1_RXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); 209 make_ipr_irq(SCIF1_BRI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); 210 make_ipr_irq(SCIF1_TXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); 211#endif 212 213#ifdef SCIF_ERI_IRQ 214 make_ipr_irq(SCIF_ERI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); 215 make_ipr_irq(SCIF_RXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); 216 make_ipr_irq(SCIF_BRI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); 217 make_ipr_irq(SCIF_TXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); 218#endif 219 220#ifdef IRDA_ERI_IRQ 221 make_ipr_irq(IRDA_ERI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); 222 make_ipr_irq(IRDA_RXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); 223 make_ipr_irq(IRDA_BRI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); 224 make_ipr_irq(IRDA_TXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); 225#endif 226 227#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) 228 /* 229 * Initialize the Interrupt Controller (INTC) 230 * registers to their power on values 231 */ 232 233 /* 234 * Enable external irq (INTC IRQ mode). 235 * You should set corresponding bits of PFC to "00" 236 * to enable these interrupts. 237 */ 238 make_ipr_irq(IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, IRQ0_PRIORITY); 239 make_ipr_irq(IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, IRQ1_PRIORITY); 240 make_ipr_irq(IRQ2_IRQ, IRQ2_IPR_ADDR, IRQ2_IPR_POS, IRQ2_PRIORITY); 241 make_ipr_irq(IRQ3_IRQ, IRQ3_IPR_ADDR, IRQ3_IPR_POS, IRQ3_PRIORITY); 242 make_ipr_irq(IRQ4_IRQ, IRQ4_IPR_ADDR, IRQ4_IPR_POS, IRQ4_PRIORITY); 243 make_ipr_irq(IRQ5_IRQ, IRQ5_IPR_ADDR, IRQ5_IPR_POS, IRQ5_PRIORITY); 244 make_ipr_irq(PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY); 245 make_ipr_irq(PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY); 246 enable_ipr_irq(PINT0_IRQ); 247 enable_ipr_irq(PINT8_IRQ); 248 249 for(i = 0; i < 16; i++) 250 make_pint_irq(PINT_IRQ_BASE + i); 251 for(i = 0; i < 256; i++) 252 { 253 if(i & 1) pint_map[i] = 0; 254 else if(i & 2) pint_map[i] = 1; 255 else if(i & 4) pint_map[i] = 2; 256 else if(i & 8) pint_map[i] = 3; 257 else if(i & 0x10) pint_map[i] = 4; 258 else if(i & 0x20) pint_map[i] = 5; 259 else if(i & 0x40) pint_map[i] = 6; 260 else if(i & 0x80) pint_map[i] = 7; 261 } 262#endif /* CONFIG_CPU_SUBTYPE_SH7707 || CONFIG_CPU_SUBTYPE_SH7709 */ 263 264 /* Perform the machine specific initialisation */ 265 if (sh_mv.mv_init_irq != NULL) { 266 sh_mv.mv_init_irq(); 267 } 268} 269#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) 270int ipr_irq_demux(int irq) 271{ 272 unsigned long creg, dreg, d, sav; 273 274 if(irq == PINT0_IRQ) 275 { 276#if defined(CONFIG_CPU_SUBTYPE_SH7707) 277 creg = PORT_PACR; 278 dreg = PORT_PADR; 279#else 280 creg = PORT_PCCR; 281 dreg = PORT_PCDR; 282#endif 283 sav = ctrl_inw(creg); 284 ctrl_outw(sav | portcr_mask, creg); 285 d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) & ctrl_inw(INTC_INTER) & 0xff; 286 ctrl_outw(sav, creg); 287 if(d == 0) return irq; 288 return PINT_IRQ_BASE + pint_map[d]; 289 } 290 else if(irq == PINT8_IRQ) 291 { 292#if defined(CONFIG_CPU_SUBTYPE_SH7707) 293 creg = PORT_PBCR; 294 dreg = PORT_PBDR; 295#else 296 creg = PORT_PFCR; 297 dreg = PORT_PFDR; 298#endif 299 sav = ctrl_inw(creg); 300 ctrl_outw(sav | (portcr_mask >> 16), creg); 301 d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) & (ctrl_inw(INTC_INTER) >> 8) & 0xff; 302 ctrl_outw(sav, creg); 303 if(d == 0) return irq; 304 return PINT_IRQ_BASE + 8 + pint_map[d]; 305 } 306 return irq; 307} 308#endif 309