1 /* 2 * Copyright (C) 2000 YAEGASHI Takeshi 3 * Hitachi HD64461 companion chip support 4 */ 5 6 #include <linux/sched.h> 7 #include <linux/module.h> 8 #include <linux/kernel.h> 9 #include <linux/param.h> 10 #include <linux/interrupt.h> 11 #include <linux/init.h> 12 #include <linux/irq.h> 13 #include <asm/io.h> 14 #include <asm/irq.h> 15 #include <asm/hd64461.h> 16 17 /* This belongs in cpu specific */ 18 #define INTC_ICR1 0xA4140010UL 19 20 static void disable_hd64461_irq(unsigned int irq) 21 { 22 unsigned short nimr; 23 unsigned short mask = 1 << (irq - HD64461_IRQBASE); 24 25 nimr = inw(HD64461_NIMR); 26 nimr |= mask; 27 outw(nimr, HD64461_NIMR); 28 } 29 30 static void enable_hd64461_irq(unsigned int irq) 31 { 32 unsigned short nimr; 33 unsigned short mask = 1 << (irq - HD64461_IRQBASE); 34 35 nimr = inw(HD64461_NIMR); 36 nimr &= ~mask; 37 outw(nimr, HD64461_NIMR); 38 } 39 40 static void mask_and_ack_hd64461(unsigned int irq) 41 { 42 disable_hd64461_irq(irq); 43 #ifdef CONFIG_HD64461_ENABLER 44 if (irq == HD64461_IRQBASE + 13) 45 outb(0x00, HD64461_PCC1CSCR); 46 #endif 47 } 48 49 static void end_hd64461_irq(unsigned int irq) 50 { 51 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) 52 enable_hd64461_irq(irq); 53 } 54 55 static unsigned int startup_hd64461_irq(unsigned int irq) 56 { 57 enable_hd64461_irq(irq); 58 return 0; 59 } 60 61 static void shutdown_hd64461_irq(unsigned int irq) 62 { 63 disable_hd64461_irq(irq); 64 } 65 66 static struct hw_interrupt_type hd64461_irq_type = { 67 .typename = "HD64461-IRQ", 68 .startup = startup_hd64461_irq, 69 .shutdown = shutdown_hd64461_irq, 70 .enable = enable_hd64461_irq, 71 .disable = disable_hd64461_irq, 72 .ack = mask_and_ack_hd64461, 73 .end = end_hd64461_irq, 74 }; 75 76 static irqreturn_t hd64461_interrupt(int irq, void *dev_id) 77 { 78 printk(KERN_INFO 79 "HD64461: spurious interrupt, nirr: 0x%x nimr: 0x%x\n", 80 inw(HD64461_NIRR), inw(HD64461_NIMR)); 81 82 return IRQ_NONE; 83 } 84 85 static struct { 86 int (*func) (int, void *); 87 void *dev; 88 } hd64461_demux[HD64461_IRQ_NUM]; 89 90 void hd64461_register_irq_demux(int irq, 91 int (*demux) (int irq, void *dev), void *dev) 92 { 93 hd64461_demux[irq - HD64461_IRQBASE].func = demux; 94 hd64461_demux[irq - HD64461_IRQBASE].dev = dev; 95 } 96 97 EXPORT_SYMBOL(hd64461_register_irq_demux); 98 99 void hd64461_unregister_irq_demux(int irq) 100 { 101 hd64461_demux[irq - HD64461_IRQBASE].func = 0; 102 } 103 104 EXPORT_SYMBOL(hd64461_unregister_irq_demux); 105 106 int hd64461_irq_demux(int irq) 107 { 108 if (irq == CONFIG_HD64461_IRQ) { 109 unsigned short bit; 110 unsigned short nirr = inw(HD64461_NIRR); 111 unsigned short nimr = inw(HD64461_NIMR); 112 int i; 113 114 nirr &= ~nimr; 115 for (bit = 1, i = 0; i < 16; bit <<= 1, i++) 116 if (nirr & bit) 117 break; 118 if (i == 16) 119 irq = CONFIG_HD64461_IRQ; 120 else { 121 irq = HD64461_IRQBASE + i; 122 if (hd64461_demux[i].func != 0) { 123 irq = hd64461_demux[i].func(irq, hd64461_demux[i].dev); 124 } 125 } 126 } 127 return irq; 128 } 129 130 static struct irqaction irq0 = { 131 .handler = hd64461_interrupt, 132 .flags = IRQF_DISABLED, 133 .mask = CPU_MASK_NONE, 134 .name = "HD64461", 135 }; 136 137 int __init setup_hd64461(void) 138 { 139 int i; 140 141 if (!MACH_HD64461) 142 return 0; 143 144 printk(KERN_INFO 145 "HD64461 configured at 0x%x on irq %d(mapped into %d to %d)\n", 146 CONFIG_HD64461_IOBASE, CONFIG_HD64461_IRQ, HD64461_IRQBASE, 147 HD64461_IRQBASE + 15); 148 149 #if defined(CONFIG_CPU_SUBTYPE_SH7709) /* Should be at processor specific part.. */ 150 outw(0x2240, INTC_ICR1); 151 #endif 152 outw(0xffff, HD64461_NIMR); 153 154 /* IRQ 80 -> 95 belongs to HD64461 */ 155 for (i = HD64461_IRQBASE; i < HD64461_IRQBASE + 16; i++) { 156 irq_desc[i].chip = &hd64461_irq_type; 157 } 158 159 setup_irq(CONFIG_HD64461_IRQ, &irq0); 160 161 #ifdef CONFIG_HD64461_ENABLER 162 printk(KERN_INFO "HD64461: enabling PCMCIA devices\n"); 163 outb(0x4c, HD64461_PCC1CSCIER); 164 outb(0x00, HD64461_PCC1CSCR); 165 #endif 166 167 return 0; 168 } 169 170 module_init(setup_hd64461); 171