/* * linux/arch/h8300/platform/h8300h/ints.c * * Yoshinori Sato * * Based on linux/arch/$(ARCH)/platform/$(PLATFORM)/ints.c * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. * * Copyright 1996 Roman Zippel * Copyright 1999 D. Jeff Dionne */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * This structure has only 4 elements for speed reasons */ typedef struct irq_handler { irqreturn_t (*handler)(int, void *, struct pt_regs *); int flags; int count; void *dev_id; const char *devname; } irq_handler_t; static irq_handler_t *irq_list[NR_IRQS]; static int use_kmalloc; extern unsigned long *interrupt_redirect_table; extern const int h8300_saved_vectors[]; extern const unsigned long h8300_trap_table[]; int h8300_enable_irq_pin(unsigned int irq); void h8300_disable_irq_pin(unsigned int irq); #define CPU_VECTOR ((unsigned long *)0x000000) #define ADDR_MASK (0xffffff) #if defined(CONFIG_RAMKERNEL) static unsigned long __init *get_vector_address(void) { unsigned long *rom_vector = CPU_VECTOR; unsigned long base,tmp; int vec_no; base = rom_vector[EXT_IRQ0] & ADDR_MASK; /* check romvector format */ for (vec_no = EXT_IRQ1; vec_no <= EXT_IRQ0+EXT_IRQS; vec_no++) { if ((base+(vec_no - EXT_IRQ0)*4) != (rom_vector[vec_no] & ADDR_MASK)) return NULL; } /* ramvector base address */ base -= EXT_IRQ0*4; /* writerble check */ tmp = ~(*(volatile unsigned long *)base); (*(volatile unsigned long *)base) = tmp; if ((*(volatile unsigned long *)base) != tmp) return NULL; return (unsigned long *)base; } #endif void __init init_IRQ(void) { #if defined(CONFIG_RAMKERNEL) int i; unsigned long *ramvec,*ramvec_p; const unsigned long *trap_entry; const int *saved_vector; ramvec = get_vector_address(); if (ramvec == NULL) panic("interrupt vector serup failed."); else printk(KERN_INFO "virtual vector at 0x%08lx\n",(unsigned long)ramvec); /* create redirect table */ ramvec_p = ramvec; trap_entry = h8300_trap_table; saved_vector = h8300_saved_vectors; for ( i = 0; i < NR_IRQS; i++) { if (i == *saved_vector) { ramvec_p++; saved_vector++; } else { if ( i < NR_TRAPS ) { if (*trap_entry) *ramvec_p = VECTOR(*trap_entry); ramvec_p++; trap_entry++; } else *ramvec_p++ = REDIRECT(interrupt_entry); } } interrupt_redirect_table = ramvec; #ifdef DUMP_VECTOR ramvec_p = ramvec; for (i = 0; i < NR_IRQS; i++) { if ((i % 8) == 0) printk(KERN_DEBUG "\n%p: ",ramvec_p); printk(KERN_DEBUG "%p ",*ramvec_p); ramvec_p++; } printk(KERN_DEBUG "\n"); #endif #endif } int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id) { irq_handler_t *irq_handle; if (irq < 0 || irq >= NR_IRQS) { printk(KERN_ERR "Incorrect IRQ %d from %s\n", irq, devname); return -EINVAL; } if (irq_list[irq] || (h8300_enable_irq_pin(irq) == -EBUSY)) return -EBUSY; if (use_kmalloc) irq_handle = (irq_handler_t *)kmalloc(sizeof(irq_handler_t), GFP_ATOMIC); else { /* use bootmem allocater */ irq_handle = (irq_handler_t *)alloc_bootmem(sizeof(irq_handler_t)); irq_handle = (irq_handler_t *)((unsigned long)irq_handle | 0x80000000); } if (irq_handle == NULL) return -ENOMEM; irq_handle->handler = handler; irq_handle->flags = flags; irq_handle->count = 0; irq_handle->dev_id = dev_id; irq_handle->devname = devname; irq_list[irq] = irq_handle; if (irq_handle->flags & SA_SAMPLE_RANDOM) rand_initialize_irq(irq); enable_irq(irq); return 0; } EXPORT_SYMBOL(request_irq); void free_irq(unsigned int irq, void *dev_id) { if (irq >= NR_IRQS) return; if (!irq_list[irq] || irq_list[irq]->dev_id != dev_id) printk(KERN_WARNING "Removing probably wrong IRQ %d from %s\n", irq, irq_list[irq]->devname); disable_irq(irq); h8300_disable_irq_pin(irq); if (((unsigned long)irq_list[irq] & 0x80000000) == 0) { kfree(irq_list[irq]); irq_list[irq] = NULL; } } EXPORT_SYMBOL(free_irq); /* * Do we need these probe functions on the m68k? */ unsigned long probe_irq_on (void) { return 0; } EXPORT_SYMBOL(probe_irq_on); int probe_irq_off (unsigned long irqs) { return 0; } EXPORT_SYMBOL(probe_irq_off); void enable_irq(unsigned int irq) { if (irq >= EXT_IRQ0 && irq <= (EXT_IRQ0 + EXT_IRQS)) IER_REGS |= 1 << (irq - EXT_IRQ0); } void disable_irq(unsigned int irq) { if (irq >= EXT_IRQ0 && irq <= (EXT_IRQ0 + EXT_IRQS)) IER_REGS &= ~(1 << (irq - EXT_IRQ0)); } asmlinkage void process_int(int irq, struct pt_regs *fp) { irq_enter(); h8300_clear_isr(irq); if (irq >= NR_TRAPS && irq < NR_IRQS) { if (irq_list[irq]) { irq_list[irq]->handler(irq, irq_list[irq]->dev_id, fp); irq_list[irq]->count++; if (irq_list[irq]->flags & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); } } else { BUG(); } irq_exit(); } int show_interrupts(struct seq_file *p, void *v) { int i = *(loff_t *) v; if ((i < NR_IRQS) && (irq_list[i]!=NULL)) { seq_printf(p, "%3d: %10u ",i,irq_list[i]->count); seq_printf(p, "%s\n", irq_list[i]->devname); } return 0; } void init_irq_proc(void) { } static int __init enable_kmalloc(void) { use_kmalloc = 1; return 0; } core_initcall(enable_kmalloc);