summaryrefslogtreecommitdiffstats
path: root/arch/mips/sibyte/bcm1480/irq_handler.S
blob: 408db88d050fbca4720eee81e9b06eed4ad78c7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
 * Copyright (C) 2000,2001,2002,2003,2004 Broadcom Corporation
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

/*
 * bcm1480_irq_handler() is the routine that is actually called when an
 * interrupt occurs.  It is installed as the exception vector handler in
 * init_IRQ() in arch/mips/sibyte/bcm1480/irq.c
 *
 * In the handle we figure out which interrupts need handling, and use that
 * to call the dispatcher, which will take care of actually calling
 * registered handlers
 *
 * Note that we take care of all raised interrupts in one go at the handler.
 * This is more BSDish than the Indy code, and also, IMHO, more sane.
 */
#include <linux/config.h>

#include <asm/addrspace.h>
#include <asm/asm.h>
#include <asm/mipsregs.h>
#include <asm/regdef.h>
#include <asm/stackframe.h>
#include <asm/sibyte/sb1250_defs.h>
#include <asm/sibyte/bcm1480_regs.h>
#include <asm/sibyte/bcm1480_int.h>

/*
 * What a pain. We have to be really careful saving the upper 32 bits of any
 * register across function calls if we don't want them trashed--since were
 * running in -o32, the calling routing never saves the full 64 bits of a
 * register across a function call.  Being the interrupt handler, we're
 * guaranteed that interrupts are disabled during this code so we don't have
 * to worry about random interrupts blasting the high 32 bits.
 */

	.text
	.set	push
	.set	noreorder
	.set	noat
	.set	mips64
	#.set	mips4
	.align	5
	NESTED(bcm1480_irq_handler, PT_SIZE, sp)
	SAVE_ALL
	CLI

#ifdef CONFIG_SIBYTE_BCM1480_PROF
	/* Set compare to count to silence count/compare timer interrupts */
	mfc0	t1, CP0_COUNT
	mtc0	t1, CP0_COMPARE /* pause to clear IP[7] bit of cause ? */
#endif
	/* Read cause */
	mfc0	s0, CP0_CAUSE

#ifdef CONFIG_SIBYTE_BCM1480_PROF
	/* Cpu performance counter interrupt is routed to IP[7] */
	andi	t1, s0, CAUSEF_IP7
	beqz	t1, 0f
	 srl	t1, s0, (CAUSEB_BD-2)	/* Shift BD bit to bit 2 */
	and	t1, t1, 0x4		/* mask to get just BD bit */
#ifdef CONFIG_MIPS64
	dmfc0	a0, CP0_EPC
	daddu	a0, a0, t1		/* a0 = EPC + (BD ? 4 :	0) */
#else
	mfc0	a0, CP0_EPC
	addu	a0, a0, t1		/* a0 = EPC + (BD ? 4 :	0) */
#endif
	jal	sbprof_cpu_intr
	 nop
	j	ret_from_irq
	 nop
0:
#endif

	/* Timer interrupt is routed to IP[4] */
	andi	t1, s0, CAUSEF_IP4
	beqz	t1, 1f
	 nop
	jal	bcm1480_timer_interrupt
	 move	a0, sp			/* Pass the registers along */
	j	ret_from_irq
	 nop				/* delay slot  */
1:

#ifdef CONFIG_SMP
	/* Mailbox interrupt is routed to IP[3] */
	andi	 t1, s0, CAUSEF_IP3
	beqz	 t1, 2f
	 nop
	jal	 bcm1480_mailbox_interrupt
	 move	 a0, sp
	j	 ret_from_irq
	 nop				/* delay slot  */
2:
#endif

#ifdef CONFIG_KGDB
	/* KGDB (uart 1) interrupt is routed to IP[6] */
	andi	 t1, s0, CAUSEF_IP6
	beqz	 t1, 3f
	 nop				/* delay slot  */
	jal	 bcm1480_kgdb_interrupt
	 move	 a0, sp
	j	 ret_from_irq
	 nop				/* delay slot  */
3:
#endif

	and	 t1, s0, CAUSEF_IP2
	beqz	 t1, 9f
	 nop

	/*
	 * Default...we've hit an IP[2] interrupt, which means we've got
	 * to check the 1480 interrupt registers to figure out what to do
	 * Need to detect which CPU we're on, now that smp_affinity is
	 * supported.
	 */
	PTR_LA	 v0, CKSEG1 + A_BCM1480_IMR_CPU0_BASE
#ifdef CONFIG_SMP
	lw	 t1, TI_CPU($28)
	sll	 t1, t1, BCM1480_IMR_REGISTER_SPACING_SHIFT
	addu	 v0, v0, t1
#endif

	/* Read IP[2] status (get both high and low halves of status) */
	ld	 s0, R_BCM1480_IMR_INTERRUPT_STATUS_BASE_H(v0)
	ld	 s1, R_BCM1480_IMR_INTERRUPT_STATUS_BASE_L(v0)

	move	 s2, zero	/* intr number  */
	li	 s3, 64

	beqz	 s0, 9f		/* No interrupts.  Return.  */
	 move	 a1, sp

	xori	 s4, s0, 1	/* if s0 (_H) == 1, it's a low intr, so...  */
	movz	 s2, s3, s4	/* start the intr number at 64, and  */
	movz	 s0, s1, s4	/* look at the low status value.  */

	dclz	 s1, s0		/* Find the next interrupt.  */
	dsubu	 a0, zero, s1
	daddiu	 a0, a0, 63
	jal	 do_IRQ
	 daddu	 a0, a0, s2

9:	j	 ret_from_irq
	 nop

	.set pop
	END(bcm1480_irq_handler)