summaryrefslogtreecommitdiff
path: root/arch/mips/vr4181/common/irq.c
blob: 2cdf77c5cb3ea04b8926fdc705ff490369c05a42 (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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/*
 * Copyright (C) 2001 MontaVista Software Inc.
 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
 * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org)
 *
 * linux/arch/mips/vr4181/common/irq.c
 *	Completely re-written to use the new irq.c
 *
 * Credits to Bradley D. LaRonde and Michael Klar for writing the original
 * irq.c file which was derived from the common irq.c file.
 *
 * 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.
 */
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/random.h>

#include <asm/irq.h>
#include <asm/mipsregs.h>
#include <asm/gdb-stub.h>

#include <asm/vr4181/vr4181.h>

/*
 * Strategy:
 *
 * We essentially have three irq controllers, CPU, system, and gpio.
 *
 * CPU irq controller is taken care by arch/mips/kernel/irq_cpu.c and
 * CONFIG_IRQ_CPU config option.
 *
 * We here provide sys_irq and gpio_irq controller code.
 */

static int sys_irq_base;
static int gpio_irq_base;

/* ---------------------- sys irq ------------------------ */
static void
sys_irq_enable(unsigned int irq)
{
	irq -= sys_irq_base;
	if (irq < 16) {
		*VR4181_MSYSINT1REG |= (u16)(1 << irq);
	} else {
		irq -= 16;
		*VR4181_MSYSINT2REG |= (u16)(1 << irq);
	}
}

static void
sys_irq_disable(unsigned int irq)
{
	irq -= sys_irq_base;
	if (irq < 16) {
		*VR4181_MSYSINT1REG &= ~((u16)(1 << irq));
	} else {
		irq -= 16;
		*VR4181_MSYSINT2REG &= ~((u16)(1 << irq));
	}

}

static unsigned int
sys_irq_startup(unsigned int irq)
{
	sys_irq_enable(irq);
	return 0;
}

#define sys_irq_shutdown	sys_irq_disable
#define sys_irq_ack		sys_irq_disable

static void
sys_irq_end(unsigned int irq)
{
	if(!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
		sys_irq_enable(irq);
}

static hw_irq_controller sys_irq_controller = {
	"vr4181_sys_irq",
	sys_irq_startup,
	sys_irq_shutdown,
	sys_irq_enable,
	sys_irq_disable,
	sys_irq_ack,
	sys_irq_end,
	NULL			/* no affinity stuff for UP */
};

/* ---------------------- gpio irq ------------------------ */
/* gpio irq lines use reverse logic */
static void
gpio_irq_enable(unsigned int irq)
{
	irq -= gpio_irq_base;
	*VR4181_GPINTMSK &= ~((u16)(1 << irq));
}

static void
gpio_irq_disable(unsigned int irq)
{
	irq -= gpio_irq_base;
	*VR4181_GPINTMSK |= (u16)(1 << irq);
}

static unsigned int
gpio_irq_startup(unsigned int irq)
{
	gpio_irq_enable(irq);

	irq -= gpio_irq_base;
	*VR4181_GPINTEN |= (u16)(1 << irq );

	return 0;
}

static void
gpio_irq_shutdown(unsigned int irq)
{
	gpio_irq_disable(irq);

	irq -= gpio_irq_base;
	*VR4181_GPINTEN &= ~((u16)(1 << irq ));
}

static void
gpio_irq_ack(unsigned int irq)
{
	u16 irqtype;
	u16 irqshift;

	gpio_irq_disable(irq);

	/* we clear interrupt if it is edge triggered */
	irq -= gpio_irq_base;
	if (irq < 8) {
		irqtype = *VR4181_GPINTTYPL;
		irqshift = 2 << (irq*2);
	} else {
		irqtype = *VR4181_GPINTTYPH;
		irqshift = 2 << ((irq-8)*2);
	}
	if ( ! (irqtype & irqshift) ) {
		*VR4181_GPINTSTAT = (u16) (1 << irq);
	}
}

static void
gpio_irq_end(unsigned int irq)
{
	if(!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
		gpio_irq_enable(irq);
}

static hw_irq_controller gpio_irq_controller = {
	"vr4181_gpio_irq",
	gpio_irq_startup,
	gpio_irq_shutdown,
	gpio_irq_enable,
	gpio_irq_disable,
	gpio_irq_ack,
	gpio_irq_end,
	NULL			/* no affinity stuff for UP */
};

/* ---------------------  IRQ init stuff ---------------------- */

extern asmlinkage void vr4181_handle_irq(void);
extern void breakpoint(void);
extern int setup_irq(unsigned int irq, struct irqaction *irqaction);
extern void mips_cpu_irq_init(u32 irq_base);

static struct irqaction cascade =
	{ no_action, SA_INTERRUPT, CPU_MASK_NONE, "cascade", NULL, NULL };
static struct irqaction reserved =
	{ no_action, SA_INTERRUPT, CPU_MASK_NONE, "cascade", NULL, NULL };

void __init arch_init_irq(void)
{
	int i;

	set_except_vector(0, vr4181_handle_irq);

	/* init CPU irqs */
	mips_cpu_irq_init(VR4181_CPU_IRQ_BASE);

	/* init sys irqs */
	sys_irq_base = VR4181_SYS_IRQ_BASE;
	for (i=sys_irq_base; i < sys_irq_base + VR4181_NUM_SYS_IRQ; i++) {
		irq_desc[i].status = IRQ_DISABLED;
		irq_desc[i].action = NULL;
		irq_desc[i].depth = 1;
		irq_desc[i].handler = &sys_irq_controller;
	}

	/* init gpio irqs */
	gpio_irq_base = VR4181_GPIO_IRQ_BASE;
	for (i=gpio_irq_base; i < gpio_irq_base + VR4181_NUM_GPIO_IRQ; i++) {
		irq_desc[i].status = IRQ_DISABLED;
		irq_desc[i].action = NULL;
		irq_desc[i].depth = 1;
		irq_desc[i].handler = &gpio_irq_controller;
	}

	/* Default all ICU IRQs to off ... */
	*VR4181_MSYSINT1REG = 0;
	*VR4181_MSYSINT2REG = 0;

	/* We initialize the level 2 ICU registers to all bits disabled. */
	*VR4181_MPIUINTREG = 0;
	*VR4181_MAIUINTREG = 0;
	*VR4181_MKIUINTREG = 0;

	/* disable all GPIO intrs */
	*VR4181_GPINTMSK = 0xffff;

	/* vector handler.  What these do is register the IRQ as non-sharable */
	setup_irq(VR4181_IRQ_INT0, &cascade);
	setup_irq(VR4181_IRQ_GIU, &cascade);

	/*
	 * RTC interrupts are interesting.  They have two destinations.
	 * One is at sys irq controller, and the other is at CPU IP3 and IP4.
	 * RTC timer is used as system timer.
	 * We enable them here, but timer routine will register later
	 * with CPU IP3/IP4.
	 */
	setup_irq(VR4181_IRQ_RTCL1, &reserved);
	setup_irq(VR4181_IRQ_RTCL2, &reserved);
}