irq_32.c 8.6 KB
Newer Older
A
Adrian Bunk 已提交
1
/*
S
Sam Ravnborg 已提交
2 3 4 5
 * Interrupt request handling routines. On the
 * Sparc the IRQs are basically 'cast in stone'
 * and you are supposed to probe the prom's device
 * node trees to find out who's got which IRQ.
L
Linus Torvalds 已提交
6 7 8 9 10 11 12 13 14 15
 *
 *  Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
 *  Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
 *  Copyright (C) 1995,2002 Pete A. Zaitcev (zaitcev@yahoo.com)
 *  Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
 *  Copyright (C) 1998-2000 Anton Blanchard (anton@samba.org)
 */

#include <linux/kernel_stat.h>
#include <linux/seq_file.h>
16
#include <linux/export.h>
L
Linus Torvalds 已提交
17

18
#include <asm/cacheflush.h>
S
Sam Ravnborg 已提交
19
#include <asm/cpudata.h>
L
Linus Torvalds 已提交
20
#include <asm/pcic.h>
21
#include <asm/leon.h>
L
Linus Torvalds 已提交
22

23
#include "kernel.h"
24 25
#include "irq.h"

26
/* platform specific irq setup */
27
struct sparc_config sparc_config;
28

D
David Howells 已提交
29
unsigned long arch_local_irq_save(void)
L
Linus Torvalds 已提交
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
{
	unsigned long retval;
	unsigned long tmp;

	__asm__ __volatile__(
		"rd	%%psr, %0\n\t"
		"or	%0, %2, %1\n\t"
		"wr	%1, 0, %%psr\n\t"
		"nop; nop; nop\n"
		: "=&r" (retval), "=r" (tmp)
		: "i" (PSR_PIL)
		: "memory");

	return retval;
}
D
David Howells 已提交
45
EXPORT_SYMBOL(arch_local_irq_save);
L
Linus Torvalds 已提交
46

D
David Howells 已提交
47
void arch_local_irq_enable(void)
L
Linus Torvalds 已提交
48 49 50 51 52 53 54 55 56 57 58 59
{
	unsigned long tmp;

	__asm__ __volatile__(
		"rd	%%psr, %0\n\t"
		"andn	%0, %1, %0\n\t"
		"wr	%0, 0, %%psr\n\t"
		"nop; nop; nop\n"
		: "=&r" (tmp)
		: "i" (PSR_PIL)
		: "memory");
}
D
David Howells 已提交
60
EXPORT_SYMBOL(arch_local_irq_enable);
L
Linus Torvalds 已提交
61

D
David Howells 已提交
62
void arch_local_irq_restore(unsigned long old_psr)
L
Linus Torvalds 已提交
63 64 65 66 67 68 69 70 71 72 73 74 75
{
	unsigned long tmp;

	__asm__ __volatile__(
		"rd	%%psr, %0\n\t"
		"and	%2, %1, %2\n\t"
		"andn	%0, %1, %0\n\t"
		"wr	%0, %2, %%psr\n\t"
		"nop; nop; nop\n"
		: "=&r" (tmp)
		: "i" (PSR_PIL), "r" (old_psr)
		: "memory");
}
D
David Howells 已提交
76
EXPORT_SYMBOL(arch_local_irq_restore);
L
Linus Torvalds 已提交
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94

/*
 * Dave Redman (djhr@tadpole.co.uk)
 *
 * IRQ numbers.. These are no longer restricted to 15..
 *
 * this is done to enable SBUS cards and onboard IO to be masked
 * correctly. using the interrupt level isn't good enough.
 *
 * For example:
 *   A device interrupting at sbus level6 and the Floppy both come in
 *   at IRQ11, but enabling and disabling them requires writing to
 *   different bits in the SLAVIO/SEC.
 *
 * As a result of these changes sun4m machines could now support
 * directed CPU interrupts using the existing enable/disable irq code
 * with tweaks.
 *
S
Sam Ravnborg 已提交
95 96 97 98 99 100 101 102 103 104 105 106
 * Sun4d complicates things even further.  IRQ numbers are arbitrary
 * 32-bit values in that case.  Since this is similar to sparc64,
 * we adopt a virtual IRQ numbering scheme as is done there.
 * Virutal interrupt numbers are allocated by build_irq().  So NR_IRQS
 * just becomes a limit of how many interrupt sources we can handle in
 * a single system.  Even fully loaded SS2000 machines top off at
 * about 32 interrupt sources or so, therefore a NR_IRQS value of 64
 * is more than enough.
  *
 * We keep a map of per-PIL enable interrupts.  These get wired
 * up via the irq_chip->startup() method which gets invoked by
 * the generic IRQ layer during request_irq().
L
Linus Torvalds 已提交
107 108 109
 */


S
Sam Ravnborg 已提交
110 111 112 113
/* Table of allocated irqs. Unused entries has irq == 0 */
static struct irq_bucket irq_table[NR_IRQS];
/* Protect access to irq_table */
static DEFINE_SPINLOCK(irq_table_lock);
L
Linus Torvalds 已提交
114

S
Sam Ravnborg 已提交
115 116 117 118
/* Map between the irq identifier used in hw to the irq_bucket. */
struct irq_bucket *irq_map[SUN4D_MAX_IRQ];
/* Protect access to irq_map */
static DEFINE_SPINLOCK(irq_map_lock);
L
Linus Torvalds 已提交
119

S
Sam Ravnborg 已提交
120 121
/* Allocate a new irq from the irq_table */
unsigned int irq_alloc(unsigned int real_irq, unsigned int pil)
L
Linus Torvalds 已提交
122 123
{
	unsigned long flags;
S
Sam Ravnborg 已提交
124 125 126 127 128 129 130
	unsigned int i;

	spin_lock_irqsave(&irq_table_lock, flags);
	for (i = 1; i < NR_IRQS; i++) {
		if (irq_table[i].real_irq == real_irq && irq_table[i].pil == pil)
			goto found;
	}
L
Linus Torvalds 已提交
131

S
Sam Ravnborg 已提交
132 133 134 135
	for (i = 1; i < NR_IRQS; i++) {
		if (!irq_table[i].irq)
			break;
	}
S
Sam Ravnborg 已提交
136

L
Linus Torvalds 已提交
137
	if (i < NR_IRQS) {
S
Sam Ravnborg 已提交
138 139 140 141 142 143
		irq_table[i].real_irq = real_irq;
		irq_table[i].irq = i;
		irq_table[i].pil = pil;
	} else {
		printk(KERN_ERR "IRQ: Out of virtual IRQs.\n");
		i = 0;
L
Linus Torvalds 已提交
144
	}
S
Sam Ravnborg 已提交
145 146 147 148
found:
	spin_unlock_irqrestore(&irq_table_lock, flags);

	return i;
L
Linus Torvalds 已提交
149 150
}

S
Sam Ravnborg 已提交
151 152 153 154 155
/* Based on a single pil handler_irq may need to call several
 * interrupt handlers. Use irq_map as entry to irq_table,
 * and let each entry in irq_table point to the next entry.
 */
void irq_link(unsigned int irq)
L
Linus Torvalds 已提交
156
{
S
Sam Ravnborg 已提交
157
	struct irq_bucket *p;
S
Sam Ravnborg 已提交
158
	unsigned long flags;
S
Sam Ravnborg 已提交
159
	unsigned int pil;
S
Sam Ravnborg 已提交
160

S
Sam Ravnborg 已提交
161
	BUG_ON(irq >= NR_IRQS);
L
Linus Torvalds 已提交
162

S
Sam Ravnborg 已提交
163
	spin_lock_irqsave(&irq_map_lock, flags);
L
Linus Torvalds 已提交
164

S
Sam Ravnborg 已提交
165 166 167 168 169
	p = &irq_table[irq];
	pil = p->pil;
	BUG_ON(pil > SUN4D_MAX_IRQ);
	p->next = irq_map[pil];
	irq_map[pil] = p;
L
Linus Torvalds 已提交
170

S
Sam Ravnborg 已提交
171 172
	spin_unlock_irqrestore(&irq_map_lock, flags);
}
L
Linus Torvalds 已提交
173

S
Sam Ravnborg 已提交
174 175 176 177
void irq_unlink(unsigned int irq)
{
	struct irq_bucket *p, **pnext;
	unsigned long flags;
L
Linus Torvalds 已提交
178

S
Sam Ravnborg 已提交
179
	BUG_ON(irq >= NR_IRQS);
L
Linus Torvalds 已提交
180

S
Sam Ravnborg 已提交
181
	spin_lock_irqsave(&irq_map_lock, flags);
L
Linus Torvalds 已提交
182

S
Sam Ravnborg 已提交
183 184 185 186 187 188
	p = &irq_table[irq];
	BUG_ON(p->pil > SUN4D_MAX_IRQ);
	pnext = &irq_map[p->pil];
	while (*pnext != p)
		pnext = &(*pnext)->next;
	*pnext = p->next;
L
Linus Torvalds 已提交
189

S
Sam Ravnborg 已提交
190
	spin_unlock_irqrestore(&irq_map_lock, flags);
L
Linus Torvalds 已提交
191 192
}

193

S
Sam Ravnborg 已提交
194 195
/* /proc/interrupts printing */
int arch_show_interrupts(struct seq_file *p, int prec)
L
Linus Torvalds 已提交
196
{
S
Sam Ravnborg 已提交
197
	int j;
S
Sam Ravnborg 已提交
198

199 200 201 202 203 204 205 206 207 208
#ifdef CONFIG_SMP
	seq_printf(p, "RES: ");
	for_each_online_cpu(j)
		seq_printf(p, "%10u ", cpu_data(j).irq_resched_count);
	seq_printf(p, "     IPI rescheduling interrupts\n");
	seq_printf(p, "CAL: ");
	for_each_online_cpu(j)
		seq_printf(p, "%10u ", cpu_data(j).irq_call_count);
	seq_printf(p, "     IPI function call interrupts\n");
#endif
S
Sam Ravnborg 已提交
209 210 211 212 213
	seq_printf(p, "NMI: ");
	for_each_online_cpu(j)
		seq_printf(p, "%10u ", cpu_data(j).counter);
	seq_printf(p, "     Non-maskable interrupts\n");
	return 0;
L
Linus Torvalds 已提交
214 215
}

S
Sam Ravnborg 已提交
216
void handler_irq(unsigned int pil, struct pt_regs *regs)
L
Linus Torvalds 已提交
217
{
A
Al Viro 已提交
218
	struct pt_regs *old_regs;
S
Sam Ravnborg 已提交
219
	struct irq_bucket *p;
L
Linus Torvalds 已提交
220

S
Sam Ravnborg 已提交
221
	BUG_ON(pil > 15);
A
Al Viro 已提交
222
	old_regs = set_irq_regs(regs);
L
Linus Torvalds 已提交
223
	irq_enter();
S
Sam Ravnborg 已提交
224 225 226 227 228 229 230 231

	p = irq_map[pil];
	while (p) {
		struct irq_bucket *next = p->next;

		generic_handle_irq(p->irq);
		p = next;
	}
L
Linus Torvalds 已提交
232
	irq_exit();
A
Al Viro 已提交
233
	set_irq_regs(old_regs);
L
Linus Torvalds 已提交
234 235
}

236
#if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_BLK_DEV_FD_MODULE)
S
Sam Ravnborg 已提交
237
static unsigned int floppy_irq;
L
Linus Torvalds 已提交
238

S
Sam Ravnborg 已提交
239
int sparc_floppy_request_irq(unsigned int irq, irq_handler_t irq_handler)
L
Linus Torvalds 已提交
240 241
{
	unsigned int cpu_irq;
S
Sam Ravnborg 已提交
242 243
	int err;

L
Linus Torvalds 已提交
244

S
Sam Ravnborg 已提交
245 246 247
	err = request_irq(irq, irq_handler, 0, "floppy", NULL);
	if (err)
		return -1;
L
Linus Torvalds 已提交
248

S
Sam Ravnborg 已提交
249 250
	/* Save for later use in floppy interrupt handler */
	floppy_irq = irq;
L
Linus Torvalds 已提交
251

S
Sam Ravnborg 已提交
252
	cpu_irq = (irq & (NR_IRQS - 1));
L
Linus Torvalds 已提交
253 254 255 256 257

	/* Dork with trap table if we get this far. */
#define INSTANTIATE(table) \
	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_one = SPARC_RD_PSR_L0; \
	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two = \
S
Sam Ravnborg 已提交
258
		SPARC_BRANCH((unsigned long) floppy_hardint, \
L
Linus Torvalds 已提交
259 260 261 262 263
			     (unsigned long) &table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two);\
	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_three = SPARC_RD_WIM_L3; \
	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_four = SPARC_NOP;

	INSTANTIATE(sparc_ttable)
S
Sam Ravnborg 已提交
264 265 266 267 268 269 270 271 272 273 274 275

#if defined CONFIG_SMP
	if (sparc_cpu_model != sparc_leon) {
		struct tt_entry *trap_table;

		trap_table = &trapbase_cpu1;
		INSTANTIATE(trap_table)
		trap_table = &trapbase_cpu2;
		INSTANTIATE(trap_table)
		trap_table = &trapbase_cpu3;
		INSTANTIATE(trap_table)
	}
L
Linus Torvalds 已提交
276 277 278 279 280 281 282 283
#endif
#undef INSTANTIATE
	/*
	 * XXX Correct thing whould be to flush only I- and D-cache lines
	 * which contain the handler in question. But as of time of the
	 * writing we have no CPU-neutral interface to fine-grained flushes.
	 */
	flush_cache_all();
S
Sam Ravnborg 已提交
284
	return 0;
L
Linus Torvalds 已提交
285
}
S
Sam Ravnborg 已提交
286
EXPORT_SYMBOL(sparc_floppy_request_irq);
L
Linus Torvalds 已提交
287

S
Sam Ravnborg 已提交
288 289
/*
 * These variables are used to access state from the assembler
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
 * interrupt handler, floppy_hardint, so we cannot put these in
 * the floppy driver image because that would not work in the
 * modular case.
 */
volatile unsigned char *fdc_status;
EXPORT_SYMBOL(fdc_status);

char *pdma_vaddr;
EXPORT_SYMBOL(pdma_vaddr);

unsigned long pdma_size;
EXPORT_SYMBOL(pdma_size);

volatile int doing_pdma;
EXPORT_SYMBOL(doing_pdma);

char *pdma_base;
EXPORT_SYMBOL(pdma_base);

unsigned long pdma_areasize;
EXPORT_SYMBOL(pdma_areasize);

S
Sam Ravnborg 已提交
312 313 314 315 316
/* Use the generic irq support to call floppy_interrupt
 * which was setup using request_irq() in sparc_floppy_request_irq().
 * We only have one floppy interrupt so we do not need to check
 * for additional handlers being wired up by irq_link()
 */
317 318 319 320 321 322
void sparc_floppy_irq(int irq, void *dev_id, struct pt_regs *regs)
{
	struct pt_regs *old_regs;

	old_regs = set_irq_regs(regs);
	irq_enter();
S
Sam Ravnborg 已提交
323
	generic_handle_irq(floppy_irq);
324 325 326 327 328
	irq_exit();
	set_irq_regs(old_regs);
}
#endif

L
Linus Torvalds 已提交
329 330 331 332 333 334 335 336 337 338
/* djhr
 * This could probably be made indirect too and assigned in the CPU
 * bits of the code. That would be much nicer I think and would also
 * fit in with the idea of being able to tune your kernel for your machine
 * by removing unrequired machine and device support.
 *
 */

void __init init_IRQ(void)
{
S
Sam Ravnborg 已提交
339
	switch (sparc_cpu_model) {
L
Linus Torvalds 已提交
340 341
	case sun4m:
		pcic_probe();
342
		if (pcic_present())
L
Linus Torvalds 已提交
343
			sun4m_pci_init_IRQ();
344 345
		else
			sun4m_init_IRQ();
L
Linus Torvalds 已提交
346
		break;
S
Sam Ravnborg 已提交
347

L
Linus Torvalds 已提交
348 349 350 351
	case sun4d:
		sun4d_init_IRQ();
		break;

352 353 354 355
	case sparc_leon:
		leon_init_IRQ();
		break;

L
Linus Torvalds 已提交
356
	default:
S
Simon Arlott 已提交
357
		prom_printf("Cannot initialize IRQs on this Sun machine...");
L
Linus Torvalds 已提交
358 359 360 361
		break;
	}
}