| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * arch/v850/kernel/v850e_intc.c -- V850E interrupt controller (INTC) | 
|  | 3 | * | 
|  | 4 | *  Copyright (C) 2001,02,03  NEC Electronics Corporation | 
|  | 5 | *  Copyright (C) 2001,02,03  Miles Bader <miles@gnu.org> | 
|  | 6 | * | 
|  | 7 | * This file is subject to the terms and conditions of the GNU General | 
|  | 8 | * Public License.  See the file COPYING in the main directory of this | 
|  | 9 | * archive for more details. | 
|  | 10 | * | 
|  | 11 | * Written by Miles Bader <miles@gnu.org> | 
|  | 12 | */ | 
|  | 13 |  | 
|  | 14 | #include <linux/kernel.h> | 
|  | 15 | #include <linux/init.h> | 
|  | 16 | #include <linux/irq.h> | 
|  | 17 |  | 
|  | 18 | #include <asm/v850e_intc.h> | 
|  | 19 |  | 
|  | 20 | static void irq_nop (unsigned irq) { } | 
|  | 21 |  | 
|  | 22 | static unsigned v850e_intc_irq_startup (unsigned irq) | 
|  | 23 | { | 
|  | 24 | v850e_intc_clear_pending_irq (irq); | 
|  | 25 | v850e_intc_enable_irq (irq); | 
|  | 26 | return 0; | 
|  | 27 | } | 
|  | 28 |  | 
|  | 29 | static void v850e_intc_end_irq (unsigned irq) | 
|  | 30 | { | 
|  | 31 | unsigned long psw, temp; | 
|  | 32 |  | 
|  | 33 | /* Clear the highest-level bit in the In-service priority register | 
|  | 34 | (ISPR), to allow this interrupt (or another of the same or | 
|  | 35 | lesser priority) to happen again. | 
|  | 36 |  | 
|  | 37 | The `reti' instruction normally does this automatically when the | 
|  | 38 | PSW bits EP and NP are zero, but we can't always rely on reti | 
|  | 39 | being used consistently to return after an interrupt (another | 
|  | 40 | process can be scheduled, for instance, which can delay the | 
|  | 41 | associated reti for a long time, or this process may be being | 
|  | 42 | single-stepped, which uses the `dbret' instruction to return | 
|  | 43 | from the kernel). | 
|  | 44 |  | 
|  | 45 | We also set the PSW EP bit, which prevents reti from also | 
|  | 46 | trying to modify the ISPR itself.  */ | 
|  | 47 |  | 
|  | 48 | /* Get PSW and disable interrupts.  */ | 
|  | 49 | asm volatile ("stsr psw, %0; di" : "=r" (psw)); | 
|  | 50 | /* We don't want to do anything for NMIs (they don't use the ISPR).  */ | 
|  | 51 | if (! (psw & 0xC0)) { | 
|  | 52 | /* Transition to `trap' state, so that an eventual real | 
|  | 53 | reti instruction won't modify the ISPR.  */ | 
|  | 54 | psw |= 0x40; | 
|  | 55 | /* Fake an interrupt return, which automatically clears the | 
|  | 56 | appropriate bit in the ISPR.  */ | 
|  | 57 | asm volatile ("mov hilo(1f), %0;" | 
|  | 58 | "ldsr %0, eipc; ldsr %1, eipsw;" | 
|  | 59 | "reti;" | 
|  | 60 | "1:" | 
|  | 61 | : "=&r" (temp) : "r" (psw)); | 
|  | 62 | } | 
|  | 63 | } | 
|  | 64 |  | 
|  | 65 | /* Initialize HW_IRQ_TYPES for INTC-controlled irqs described in array | 
|  | 66 | INITS (which is terminated by an entry with the name field == 0).  */ | 
|  | 67 | void __init v850e_intc_init_irq_types (struct v850e_intc_irq_init *inits, | 
|  | 68 | struct hw_interrupt_type *hw_irq_types) | 
|  | 69 | { | 
|  | 70 | struct v850e_intc_irq_init *init; | 
|  | 71 | for (init = inits; init->name; init++) { | 
|  | 72 | unsigned i; | 
|  | 73 | struct hw_interrupt_type *hwit = hw_irq_types++; | 
|  | 74 |  | 
|  | 75 | hwit->typename = init->name; | 
|  | 76 |  | 
|  | 77 | hwit->startup  = v850e_intc_irq_startup; | 
|  | 78 | hwit->shutdown = v850e_intc_disable_irq; | 
|  | 79 | hwit->enable   = v850e_intc_enable_irq; | 
|  | 80 | hwit->disable  = v850e_intc_disable_irq; | 
|  | 81 | hwit->ack      = irq_nop; | 
|  | 82 | hwit->end      = v850e_intc_end_irq; | 
|  | 83 |  | 
|  | 84 | /* Initialize kernel IRQ infrastructure for this interrupt.  */ | 
|  | 85 | init_irq_handlers(init->base, init->num, init->interval, hwit); | 
|  | 86 |  | 
|  | 87 | /* Set the interrupt priorities.  */ | 
|  | 88 | for (i = 0; i < init->num; i++) { | 
|  | 89 | unsigned irq = init->base + i * init->interval; | 
|  | 90 |  | 
|  | 91 | /* If the interrupt is currently enabled (all | 
|  | 92 | interrupts are initially disabled), then | 
|  | 93 | assume whoever enabled it has set things up | 
|  | 94 | properly, and avoid messing with it.  */ | 
|  | 95 | if (! v850e_intc_irq_enabled (irq)) | 
|  | 96 | /* This write also (1) disables the | 
|  | 97 | interrupt, and (2) clears any pending | 
|  | 98 | interrupts.  */ | 
|  | 99 | V850E_INTC_IC (irq) | 
|  | 100 | = (V850E_INTC_IC_PR (init->priority) | 
|  | 101 | | V850E_INTC_IC_MK); | 
|  | 102 | } | 
|  | 103 | } | 
|  | 104 | } |