Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / arch / h8300 / kernel / ints.c
1 /*
2  * linux/arch/h8300/platform/h8300h/ints.c
3  *
4  * Yoshinori Sato <ysato@users.sourceforge.jp>
5  *
6  * Based on linux/arch/$(ARCH)/platform/$(PLATFORM)/ints.c
7  *
8  * This file is subject to the terms and conditions of the GNU General Public
9  * License.  See the file COPYING in the main directory of this archive
10  * for more details.
11  *
12  * Copyright 1996 Roman Zippel
13  * Copyright 1999 D. Jeff Dionne <jeff@rt-control.com>
14  */
15
16 #include <linux/module.h>
17 #include <linux/types.h>
18 #include <linux/kernel.h>
19 #include <linux/sched.h>
20 #include <linux/kernel_stat.h>
21 #include <linux/seq_file.h>
22 #include <linux/init.h>
23 #include <linux/random.h>
24 #include <linux/bootmem.h>
25 #include <linux/hardirq.h>
26 #include <linux/vs_context.h>
27
28 #include <asm/system.h>
29 #include <asm/irq.h>
30 #include <asm/traps.h>
31 #include <asm/io.h>
32 #include <asm/setup.h>
33 #include <asm/errno.h>
34
35 /*
36  * This structure has only 4 elements for speed reasons
37  */
38 typedef struct irq_handler {
39         irqreturn_t (*handler)(int, void *, struct pt_regs *);
40         int         flags;
41         int         count;
42         void        *dev_id;
43         const char  *devname;
44 } irq_handler_t;
45
46 static irq_handler_t *irq_list[NR_IRQS];
47 static int use_kmalloc;
48
49 extern unsigned long *interrupt_redirect_table;
50 extern const int h8300_saved_vectors[];
51 extern const unsigned long h8300_trap_table[];
52 int h8300_enable_irq_pin(unsigned int irq);
53 void h8300_disable_irq_pin(unsigned int irq);
54
55 #define CPU_VECTOR ((unsigned long *)0x000000)
56 #define ADDR_MASK (0xffffff)
57
58 #if defined(CONFIG_RAMKERNEL)
59 static unsigned long __init *get_vector_address(void)
60 {
61         unsigned long *rom_vector = CPU_VECTOR;
62         unsigned long base,tmp;
63         int vec_no;
64
65         base = rom_vector[EXT_IRQ0] & ADDR_MASK;
66         
67         /* check romvector format */
68         for (vec_no = EXT_IRQ1; vec_no <= EXT_IRQ0+EXT_IRQS; vec_no++) {
69                 if ((base+(vec_no - EXT_IRQ0)*4) != (rom_vector[vec_no] & ADDR_MASK))
70                         return NULL;
71         }
72
73         /* ramvector base address */
74         base -= EXT_IRQ0*4;
75
76         /* writerble check */
77         tmp = ~(*(volatile unsigned long *)base);
78         (*(volatile unsigned long *)base) = tmp;
79         if ((*(volatile unsigned long *)base) != tmp)
80                 return NULL;
81         return (unsigned long *)base;
82 }
83 #endif
84
85 void __init init_IRQ(void)
86 {
87 #if defined(CONFIG_RAMKERNEL)
88         int i;
89         unsigned long *ramvec,*ramvec_p;
90         const unsigned long *trap_entry;
91         const int *saved_vector;
92
93         ramvec = get_vector_address();
94         if (ramvec == NULL)
95                 panic("interrupt vector serup failed.");
96         else
97                 printk(KERN_INFO "virtual vector at 0x%08lx\n",(unsigned long)ramvec);
98
99         /* create redirect table */
100         ramvec_p = ramvec;
101         trap_entry = h8300_trap_table;
102         saved_vector = h8300_saved_vectors;
103         for ( i = 0; i < NR_IRQS; i++) {
104                 if (i == *saved_vector) {
105                         ramvec_p++;
106                         saved_vector++;
107                 } else {
108                         if ( i < NR_TRAPS ) {
109                                 if (*trap_entry)
110                                         *ramvec_p = VECTOR(*trap_entry);
111                                 ramvec_p++;
112                                 trap_entry++;
113                         } else
114                                 *ramvec_p++ = REDIRECT(interrupt_entry);
115                 }
116         }
117         interrupt_redirect_table = ramvec;
118 #ifdef DUMP_VECTOR
119         ramvec_p = ramvec;
120         for (i = 0; i < NR_IRQS; i++) {
121                 if ((i % 8) == 0)
122                         printk(KERN_DEBUG "\n%p: ",ramvec_p);
123                 printk(KERN_DEBUG "%p ",*ramvec_p);
124                 ramvec_p++;
125         }
126         printk(KERN_DEBUG "\n");
127 #endif
128 #endif
129 }
130
131 int request_irq(unsigned int irq, 
132                 irqreturn_t (*handler)(int, void *, struct pt_regs *),
133                 unsigned long flags, const char *devname, void *dev_id)
134 {
135         irq_handler_t *irq_handle;
136         if (irq < 0 || irq >= NR_IRQS) {
137                 printk(KERN_ERR "Incorrect IRQ %d from %s\n", irq, devname);
138                 return -EINVAL;
139         }
140
141         if (irq_list[irq] || (h8300_enable_irq_pin(irq) == -EBUSY))
142                 return -EBUSY;
143
144         if (use_kmalloc)
145                 irq_handle = (irq_handler_t *)kmalloc(sizeof(irq_handler_t), GFP_ATOMIC);
146         else {
147                 /* use bootmem allocater */
148                 irq_handle = (irq_handler_t *)alloc_bootmem(sizeof(irq_handler_t));
149                 irq_handle = (irq_handler_t *)((unsigned long)irq_handle | 0x80000000);
150         }
151
152         if (irq_handle == NULL)
153                 return -ENOMEM;
154
155         irq_handle->handler = handler;
156         irq_handle->flags   = flags;
157         irq_handle->count   = 0;
158         irq_handle->dev_id  = dev_id;
159         irq_handle->devname = devname;
160         irq_list[irq] = irq_handle;
161
162         if (irq_handle->flags & IRQF_SAMPLE_RANDOM)
163                 rand_initialize_irq(irq);
164
165         enable_irq(irq);
166         return 0;
167 }
168
169 EXPORT_SYMBOL(request_irq);
170
171 void free_irq(unsigned int irq, void *dev_id)
172 {
173         if (irq >= NR_IRQS)
174                 return;
175
176         if (!irq_list[irq] || irq_list[irq]->dev_id != dev_id)
177                 printk(KERN_WARNING "Removing probably wrong IRQ %d from %s\n",
178                        irq, irq_list[irq]->devname);
179         disable_irq(irq);
180         h8300_disable_irq_pin(irq);
181         if (((unsigned long)irq_list[irq] & 0x80000000) == 0) {
182                 kfree(irq_list[irq]);
183                 irq_list[irq] = NULL;
184         }
185 }
186
187 EXPORT_SYMBOL(free_irq);
188
189 /*
190  * Do we need these probe functions on the m68k?
191  */
192 unsigned long probe_irq_on (void)
193 {
194         return 0;
195 }
196
197 EXPORT_SYMBOL(probe_irq_on);
198
199 int probe_irq_off (unsigned long irqs)
200 {
201         return 0;
202 }
203
204 EXPORT_SYMBOL(probe_irq_off);
205
206 void enable_irq(unsigned int irq)
207 {
208         if (irq >= EXT_IRQ0 && irq <= (EXT_IRQ0 + EXT_IRQS))
209                 IER_REGS |= 1 << (irq - EXT_IRQ0);
210 }
211
212 void disable_irq(unsigned int irq)
213 {
214         if (irq >= EXT_IRQ0 && irq <= (EXT_IRQ0 + EXT_IRQS))
215                 IER_REGS &= ~(1 << (irq - EXT_IRQ0));
216 }
217
218 asmlinkage void process_int(int irq, struct pt_regs *fp)
219 {
220         struct vx_info_save vxis;
221
222         irq_enter();
223         __enter_vx_admin(&vxis);
224         h8300_clear_isr(irq);
225         if (irq >= NR_TRAPS && irq < NR_IRQS) {
226                 if (irq_list[irq]) {
227                         irq_list[irq]->handler(irq, irq_list[irq]->dev_id, fp);
228                         irq_list[irq]->count++;
229                         if (irq_list[irq]->flags & IRQF_SAMPLE_RANDOM)
230                                 add_interrupt_randomness(irq);
231                 }
232         } else {
233                 BUG();
234         }
235         __leave_vx_admin(&vxis);
236         irq_exit();
237 }
238
239 int show_interrupts(struct seq_file *p, void *v)
240 {
241         int i = *(loff_t *) v;
242
243         if ((i < NR_IRQS) && (irq_list[i]!=NULL)) {
244                 seq_printf(p, "%3d: %10u ",i,irq_list[i]->count);
245                 seq_printf(p, "%s\n", irq_list[i]->devname);
246         }
247
248         return 0;
249 }
250
251 void init_irq_proc(void)
252 {
253 }
254
255 static int __init enable_kmalloc(void)
256 {
257         use_kmalloc = 1;
258         return 0;
259 }
260 core_initcall(enable_kmalloc);