2 * Copyright (C) 2003 PMC-Sierra Inc.
3 * Author: Manish Lachwani (lachwani@pmc-sierra.com)
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
10 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
11 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
13 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
14 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
15 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
16 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
17 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
18 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
19 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 675 Mass Ave, Cambridge, MA 02139, USA.
25 * Second level Interrupt handlers for the PMC-Sierra Titan/Yosemite board
28 #include <linux/errno.h>
29 #include <linux/init.h>
30 #include <linux/kernel_stat.h>
31 #include <linux/module.h>
32 #include <linux/signal.h>
33 #include <linux/sched.h>
34 #include <linux/types.h>
35 #include <linux/interrupt.h>
36 #include <linux/ioport.h>
37 #include <linux/timex.h>
38 #include <linux/slab.h>
39 #include <linux/random.h>
40 #include <asm/bitops.h>
41 #include <asm/bootinfo.h>
44 #include <asm/mipsregs.h>
45 #include <asm/system.h>
47 /* Hypertransport specific */
48 #define IRQ_STATUS_REG_CPU0 0xbb001b30 /* INT# 3 status register on CPU 0*/
49 #define IRQ_STATUS_REG_CPU1 0xbb002b30 /* INT# 3 status register on CPU 1*/
50 #define IRQ_ACK_BITS 0x00000000 /* Ack bits */
51 #define IRQ_CLEAR_REG_CPU0 0xbb002b3c /* IRQ clear register on CPU 0*/
52 #define IRQ_CLEAR_REG_CPU0 0xbb002b3c /* IRQ clear register on CPU 1*/
54 #define HYPERTRANSPORT_EOI 0xbb0006E0 /* End of Interrupt */
55 #define HYPERTRANSPORT_INTA 0x78 /* INTA# */
56 #define HYPERTRANSPORT_INTB 0x79 /* INTB# */
57 #define HYPERTRANSPORT_INTC 0x7a /* INTC# */
58 #define HYPERTRANSPORT_INTD 0x7b /* INTD# */
60 #define read_32bit_cp0_set1_register(source) \
62 __asm__ __volatile__( \
65 "cfc0\t%0,"STR(source)"\n\t" \
70 #define write_32bit_cp0_set1_register(register,value) \
71 __asm__ __volatile__( \
72 "ctc0\t%0,"STR(register)"\n\t" \
76 static spinlock_t irq_lock = SPIN_LOCK_UNLOCKED;
78 /* Function for careful CP0 interrupt mask access */
79 static inline void modify_cp0_intmask(unsigned clr_mask_in, unsigned set_mask_in)
85 /* do the low 8 bits first */
86 clr_mask = 0xff & clr_mask_in;
87 set_mask = 0xff & set_mask_in;
88 status = read_c0_status();
89 status &= ~((clr_mask & 0xFF) << 8);
90 status |= (set_mask & 0xFF) << 8 | 0x0000FF00;
91 write_c0_status(status);
93 /* do the high 8 bits */
94 clr_mask = 0xff & (clr_mask_in >> 8);
95 set_mask = 0xff & (set_mask_in >> 8);
96 status = read_32bit_cp0_set1_register(CP0_S1_INTCONTROL);
97 status &= ~((clr_mask & 0xFF) << 8);
98 status |= (set_mask & 0xFF) << 8;
99 write_32bit_cp0_set1_register(CP0_S1_INTCONTROL, status);
102 static inline void mask_irq(unsigned int irq)
104 modify_cp0_intmask(irq, 0);
107 static inline void unmask_irq(unsigned int irq)
109 modify_cp0_intmask(0, irq);
112 static void enable_rm9000_irq(unsigned int irq)
116 spin_lock_irqsave(&irq_lock, flags);
117 unmask_irq(1 << (irq-1));
118 spin_unlock_irqrestore(&irq_lock, flags);
121 static unsigned int startup_rm9000_irq(unsigned int irq)
123 enable_rm9000_irq(irq);
125 return 0; /* never anything pending */
128 static void disable_rm9000_irq(unsigned int irq)
132 spin_lock_irqsave(&irq_lock, flags);
133 mask_irq(1 << (irq-1));
134 spin_unlock_irqrestore(&irq_lock, flags);
137 #define shutdown_rm9000_irq disable_rm9000_irq
139 static void mask_and_ack_rm9000_irq(unsigned int irq)
141 mask_irq(1 << (irq-1));
144 static void end_rm9000_irq(unsigned int irq)
146 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
147 unmask_irq(1 << (irq-1));
150 static struct hw_interrupt_type rm9000_hpcdma_irq_type = {
156 mask_and_ack_rm9000_irq,
161 extern asmlinkage void titan_handle_int(void);
162 extern void jaguar_mailbox_irq(struct pt_regs *);
165 * Handle hypertransport & SMP interrupts. The interrupt lines are scarce. For interprocessor
166 * interrupts, the best thing to do is to use the INTMSG register. We use the same external
167 * interrupt line, i.e. INTB3 and monitor another status bit
169 asmlinkage void ll_ht_smp_irq_handler(int irq, struct pt_regs *regs)
172 status = *(volatile uint32_t *)(IRQ_STATUS_REG_CPU0);
174 /* Ack all the bits that correspond to the interrupt sources */
176 *(volatile uint32_t *)(IRQ_STATUS_REG_CPU0) = IRQ_ACK_BITS;
178 status = *(volatile uint32_t *)(IRQ_STATUS_REG_CPU1);
180 *(volatile uint32_t *)(IRQ_STATUS_REG_CPU1) = IRQ_ACK_BITS;
184 /* This is an SMP IPI sent from one core to another */
185 jaguar_mailbox_irq(regs);
190 #ifdef CONFIG_HT_LEVEL_TRIGGER
192 * Level Trigger Mode only. Send the HT EOI message back to the source.
196 *(volatile uint32_t *)(HYPERTRANSPORT_EOI) = HYPERTRANSPORT_INTA;
199 *(volatile uint32_t *)(HYPERTRANSPORT_EOI) = HYPERTRANSPORT_INTB;
202 *(volatile uint32_t *)(HYPERTRANSPORT_EOI) = HYPERTRANSPORT_INTC;
205 *(volatile uint32_t *)(HYPERTRANSPORT_EOI) = HYPERTRANSPORT_INTD;
209 *(volatile uint32_t *)(HYPERTRANSPORT_EOI) = 0x20;
210 *(volatile uint32_t *)(IRQ_CLEAR_REG) = IRQ_ACK_BITS;
213 *(volatile uint32_t *)(HYPERTRANSPORT_EOI) = HYPERTRANSPORT_INTA;
214 *(volatile uint32_t *)(HYPERTRANSPORT_EOI) = HYPERTRANSPORT_INTB;
215 *(volatile uint32_t *)(HYPERTRANSPORT_EOI) = HYPERTRANSPORT_INTC;
216 *(volatile uint32_t *)(HYPERTRANSPORT_EOI) = HYPERTRANSPORT_INTD;
219 #endif /* CONFIG_HT_LEVEL_TRIGGER */
228 * Initialize the next level interrupt handler
230 void __init init_IRQ(void)
234 clear_c0_status(ST0_IM | ST0_BEV);
237 set_except_vector(0, titan_handle_int);
240 for (i = 0; i < 13; i++) {
241 irq_desc[i].status = IRQ_DISABLED;
242 irq_desc[i].action = 0;
243 irq_desc[i].depth = 1;
244 irq_desc[i].handler = &rm9000_hpcdma_irq_type;