irq.c 10.1 KB
Newer Older
L
Linus Torvalds 已提交
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
/* 
 * Code to handle x86 style IRQs plus some generic interrupt stuff.
 *
 * Copyright (C) 1992 Linus Torvalds
 * Copyright (C) 1994, 1995, 1996, 1997, 1998 Ralf Baechle
 * Copyright (C) 1999 SuSE GmbH (Philipp Rumpf, prumpf@tux.org)
 * Copyright (C) 1999-2000 Grant Grundler
 * Copyright (c) 2005 Matthew Wilcox
 *
 *    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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/types.h>
J
James Bottomley 已提交
32
#include <asm/io.h>
L
Linus Torvalds 已提交
33

34 35
#include <asm/smp.h>

L
Linus Torvalds 已提交
36 37
#undef PARISC_IRQ_CR16_COUNTS

38 39
extern irqreturn_t timer_interrupt(int, void *);
extern irqreturn_t ipi_interrupt(int, void *);
L
Linus Torvalds 已提交
40 41 42 43 44 45 46 47

#define EIEM_MASK(irq)       (1UL<<(CPU_IRQ_MAX - irq))

/* Bits in EIEM correlate with cpu_irq_action[].
** Numbered *Big Endian*! (ie bit 0 is MSB)
*/
static volatile unsigned long cpu_eiem = 0;

J
James Bottomley 已提交
48
/*
G
Grant Grundler 已提交
49
** local ACK bitmap ... habitually set to 1, but reset to zero
J
James Bottomley 已提交
50 51 52 53 54
** between ->ack() and ->end() of the interrupt to prevent
** re-interruption of a processing interrupt.
*/
static DEFINE_PER_CPU(unsigned long, local_ack_eiem) = ~0UL;

55
static void cpu_mask_irq(struct irq_data *d)
L
Linus Torvalds 已提交
56
{
57
	unsigned long eirr_bit = EIEM_MASK(d->irq);
L
Linus Torvalds 已提交
58 59

	cpu_eiem &= ~eirr_bit;
60 61 62 63
	/* Do nothing on the other CPUs.  If they get this interrupt,
	 * The & cpu_eiem in the do_cpu_irq_mask() ensures they won't
	 * handle it, and the set_eiem() at the bottom will ensure it
	 * then gets disabled */
L
Linus Torvalds 已提交
64 65
}

66
static void __cpu_unmask_irq(unsigned int irq)
L
Linus Torvalds 已提交
67 68 69 70
{
	unsigned long eirr_bit = EIEM_MASK(irq);

	cpu_eiem |= eirr_bit;
71 72 73 74 75

	/* This is just a simple NOP IPI.  But what it does is cause
	 * all the other CPUs to do a set_eiem(cpu_eiem) at the end
	 * of the interrupt handler */
	smp_send_all_nop();
L
Linus Torvalds 已提交
76 77
}

78 79 80 81 82 83
static void cpu_unmask_irq(struct irq_data *d)
{
	__cpu_unmask_irq(d->irq);
}

void cpu_ack_irq(struct irq_data *d)
J
James Bottomley 已提交
84
{
85
	unsigned long mask = EIEM_MASK(d->irq);
J
James Bottomley 已提交
86 87 88
	int cpu = smp_processor_id();

	/* Clear in EIEM so we can no longer process */
G
Grant Grundler 已提交
89
	per_cpu(local_ack_eiem, cpu) &= ~mask;
J
James Bottomley 已提交
90 91

	/* disable the interrupt */
G
Grant Grundler 已提交
92 93
	set_eiem(cpu_eiem & per_cpu(local_ack_eiem, cpu));

J
James Bottomley 已提交
94 95 96 97
	/* and now ack it */
	mtctl(mask, 23);
}

98
void cpu_eoi_irq(struct irq_data *d)
J
James Bottomley 已提交
99
{
100
	unsigned long mask = EIEM_MASK(d->irq);
J
James Bottomley 已提交
101 102 103
	int cpu = smp_processor_id();

	/* set it in the eiems---it's no longer in process */
G
Grant Grundler 已提交
104
	per_cpu(local_ack_eiem, cpu) |= mask;
J
James Bottomley 已提交
105 106

	/* enable the interrupt */
G
Grant Grundler 已提交
107
	set_eiem(cpu_eiem & per_cpu(local_ack_eiem, cpu));
J
James Bottomley 已提交
108 109
}

J
James Bottomley 已提交
110
#ifdef CONFIG_SMP
111
int cpu_check_affinity(struct irq_data *d, const struct cpumask *dest)
J
James Bottomley 已提交
112 113 114 115
{
	int cpu_dest;

	/* timer and ipi have to always be received on all CPUs */
116
	if (irqd_is_per_cpu(d))
J
James Bottomley 已提交
117 118 119 120 121
		return -EINVAL;

	/* whatever mask they set, we just allow one CPU */
	cpu_dest = first_cpu(*dest);

122
	return cpu_dest;
J
James Bottomley 已提交
123 124
}

125 126
static int cpu_set_affinity_irq(struct irq_data *d, const struct cpumask *dest,
				bool force)
J
James Bottomley 已提交
127
{
128 129
	int cpu_dest;

130
	cpu_dest = cpu_check_affinity(d, dest);
131
	if (cpu_dest < 0)
132
		return -1;
J
James Bottomley 已提交
133

134
	cpumask_copy(d->affinity, dest);
135 136

	return 0;
J
James Bottomley 已提交
137 138 139
}
#endif

140
static struct irq_chip cpu_interrupt_type = {
141 142 143 144 145
	.name			= "CPU",
	.irq_mask		= cpu_mask_irq,
	.irq_unmask		= cpu_unmask_irq,
	.irq_ack		= cpu_ack_irq,
	.irq_eoi		= cpu_eoi_irq,
J
James Bottomley 已提交
146
#ifdef CONFIG_SMP
147
	.irq_set_affinity	= cpu_set_affinity_irq,
J
James Bottomley 已提交
148
#endif
149 150 151
	/* XXX: Needs to be written.  We managed without it so far, but
	 * we really ought to write it.
	 */
152
	.irq_retrigger	= NULL,
L
Linus Torvalds 已提交
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
};

int show_interrupts(struct seq_file *p, void *v)
{
	int i = *(loff_t *) v, j;
	unsigned long flags;

	if (i == 0) {
		seq_puts(p, "    ");
		for_each_online_cpu(j)
			seq_printf(p, "       CPU%d", j);

#ifdef PARISC_IRQ_CR16_COUNTS
		seq_printf(p, " [min/avg/max] (CPU cycle counts)");
#endif
		seq_putc(p, '\n');
	}

	if (i < NR_IRQS) {
172
		struct irq_desc *desc = irq_to_desc(i);
L
Linus Torvalds 已提交
173 174
		struct irqaction *action;

175 176
		raw_spin_lock_irqsave(&desc->lock, flags);
		action = desc->action;
L
Linus Torvalds 已提交
177 178 179 180 181
		if (!action)
			goto skip;
		seq_printf(p, "%3d: ", i);
#ifdef CONFIG_SMP
		for_each_online_cpu(j)
182
			seq_printf(p, "%10u ", kstat_irqs_cpu(i, j));
L
Linus Torvalds 已提交
183 184 185 186
#else
		seq_printf(p, "%10u ", kstat_irqs(i));
#endif

187
		seq_printf(p, " %14s", irq_desc_get_chip(desc)->name);
L
Linus Torvalds 已提交
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
#ifndef PARISC_IRQ_CR16_COUNTS
		seq_printf(p, "  %s", action->name);

		while ((action = action->next))
			seq_printf(p, ", %s", action->name);
#else
		for ( ;action; action = action->next) {
			unsigned int k, avg, min, max;

			min = max = action->cr16_hist[0];

			for (avg = k = 0; k < PARISC_CR16_HIST_SIZE; k++) {
				int hist = action->cr16_hist[k];

				if (hist) {
					avg += hist;
				} else
					break;

				if (hist > max) max = hist;
				if (hist < min) min = hist;
			}

			avg /= k;
			seq_printf(p, " %s[%d/%d/%d]", action->name,
					min,avg,max);
		}
#endif

		seq_putc(p, '\n');
 skip:
219
		raw_spin_unlock_irqrestore(&desc->lock, flags);
L
Linus Torvalds 已提交
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
	}

	return 0;
}



/*
** The following form a "set": Virtual IRQ, Transaction Address, Trans Data.
** Respectively, these map to IRQ region+EIRR, Processor HPA, EIRR bit.
**
** To use txn_XXX() interfaces, get a Virtual IRQ first.
** Then use that to get the Transaction address and data.
*/

235
int cpu_claim_irq(unsigned int irq, struct irq_chip *type, void *data)
L
Linus Torvalds 已提交
236
{
237
	if (irq_has_action(irq))
L
Linus Torvalds 已提交
238
		return -EBUSY;
T
Thomas Gleixner 已提交
239
	if (irq_get_chip(irq) != &cpu_interrupt_type)
L
Linus Torvalds 已提交
240 241
		return -EBUSY;

242
	/* for iosapic interrupts */
L
Linus Torvalds 已提交
243
	if (type) {
T
Thomas Gleixner 已提交
244 245
		irq_set_chip_and_handler(irq, type, handle_percpu_irq);
		irq_set_chip_data(irq, data);
246
		__cpu_unmask_irq(irq);
L
Linus Torvalds 已提交
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
	}
	return 0;
}

int txn_claim_irq(int irq)
{
	return cpu_claim_irq(irq, NULL, NULL) ? -1 : irq;
}

/*
 * The bits_wide parameter accommodates the limitations of the HW/SW which
 * use these bits:
 * Legacy PA I/O (GSC/NIO): 5 bits (architected EIM register)
 * V-class (EPIC):          6 bits
 * N/L/A-class (iosapic):   8 bits
 * PCI 2.2 MSI:            16 bits
 * Some PCI devices:       32 bits (Symbios SCSI/ATM/HyperFabric)
 *
 * On the service provider side:
 * o PA 1.1 (and PA2.0 narrow mode)     5-bits (width of EIR register)
 * o PA 2.0 wide mode                   6-bits (per processor)
 * o IA64                               8-bits (0-256 total)
 *
 * So a Legacy PA I/O device on a PA 2.0 box can't use all the bits supported
 * by the processor...and the N/L-class I/O subsystem supports more bits than
 * PA2.0 has. The first case is the problem.
 */
int txn_alloc_irq(unsigned int bits_wide)
{
	int irq;

	/* never return irq 0 cause that's the interval timer */
	for (irq = CPU_IRQ_BASE + 1; irq <= CPU_IRQ_MAX; irq++) {
		if (cpu_claim_irq(irq, NULL, NULL) < 0)
			continue;
		if ((irq - CPU_IRQ_BASE) >= (1 << bits_wide))
			continue;
		return irq;
	}

	/* unlikely, but be prepared */
	return -1;
}

291

J
James Bottomley 已提交
292 293
unsigned long txn_affinity_addr(unsigned int irq, int cpu)
{
294
#ifdef CONFIG_SMP
295 296
	struct irq_data *d = irq_get_irq_data(irq);
	cpumask_copy(d->affinity, cpumask_of(cpu));
297
#endif
J
James Bottomley 已提交
298

299
	return per_cpu(cpu_data, cpu).txn_addr;
J
James Bottomley 已提交
300 301
}

302

L
Linus Torvalds 已提交
303 304 305 306 307 308 309
unsigned long txn_alloc_addr(unsigned int virt_irq)
{
	static int next_cpu = -1;

	next_cpu++; /* assign to "next" CPU we want this bugger on */

	/* validate entry */
310
	while ((next_cpu < nr_cpu_ids) &&
311 312
		(!per_cpu(cpu_data, next_cpu).txn_addr ||
		 !cpu_online(next_cpu)))
L
Linus Torvalds 已提交
313 314
		next_cpu++;

315
	if (next_cpu >= nr_cpu_ids) 
L
Linus Torvalds 已提交
316 317
		next_cpu = 0;	/* nothing else, assign monarch */

J
James Bottomley 已提交
318
	return txn_affinity_addr(virt_irq, next_cpu);
L
Linus Torvalds 已提交
319 320 321 322 323 324 325 326
}


unsigned int txn_alloc_data(unsigned int virt_irq)
{
	return virt_irq - CPU_IRQ_BASE;
}

J
James Bottomley 已提交
327 328
static inline int eirr_to_irq(unsigned long eirr)
{
K
Kyle McMartin 已提交
329
	int bit = fls_long(eirr);
J
James Bottomley 已提交
330 331 332
	return (BITS_PER_LONG - bit) + TIMER_IRQ;
}

L
Linus Torvalds 已提交
333 334 335
/* ONLY called from entry.S:intr_extint() */
void do_cpu_irq_mask(struct pt_regs *regs)
{
M
Matthew Wilcox 已提交
336
	struct pt_regs *old_regs;
L
Linus Torvalds 已提交
337
	unsigned long eirr_val;
J
James Bottomley 已提交
338
	int irq, cpu = smp_processor_id();
339
#ifdef CONFIG_SMP
340
	struct irq_desc *desc;
J
James Bottomley 已提交
341
	cpumask_t dest;
342
#endif
L
Linus Torvalds 已提交
343

M
Matthew Wilcox 已提交
344
	old_regs = set_irq_regs(regs);
J
James Bottomley 已提交
345 346
	local_irq_disable();
	irq_enter();
L
Linus Torvalds 已提交
347

G
Grant Grundler 已提交
348
	eirr_val = mfctl(23) & cpu_eiem & per_cpu(local_ack_eiem, cpu);
J
James Bottomley 已提交
349 350 351
	if (!eirr_val)
		goto set_out;
	irq = eirr_to_irq(eirr_val);
J
James Bottomley 已提交
352

J
James Bottomley 已提交
353
#ifdef CONFIG_SMP
354 355
	desc = irq_to_desc(irq);
	cpumask_copy(&dest, desc->irq_data.affinity);
356
	if (irqd_is_per_cpu(&desc->irq_data) &&
J
James Bottomley 已提交
357 358 359 360 361 362
	    !cpu_isset(smp_processor_id(), dest)) {
		int cpu = first_cpu(dest);

		printk(KERN_DEBUG "redirecting irq %d from CPU %d to %d\n",
		       irq, smp_processor_id(), cpu);
		gsc_writel(irq + CPU_IRQ_BASE,
363
			   per_cpu(cpu_data, cpu).hpa);
J
James Bottomley 已提交
364
		goto set_out;
L
Linus Torvalds 已提交
365
	}
J
James Bottomley 已提交
366
#endif
367
	generic_handle_irq(irq);
368

J
James Bottomley 已提交
369
 out:
L
Linus Torvalds 已提交
370
	irq_exit();
M
Matthew Wilcox 已提交
371
	set_irq_regs(old_regs);
J
James Bottomley 已提交
372
	return;
L
Linus Torvalds 已提交
373

J
James Bottomley 已提交
374
 set_out:
G
Grant Grundler 已提交
375
	set_eiem(cpu_eiem & per_cpu(local_ack_eiem, cpu));
J
James Bottomley 已提交
376 377
	goto out;
}
L
Linus Torvalds 已提交
378 379 380 381

static struct irqaction timer_action = {
	.handler = timer_interrupt,
	.name = "timer",
B
Bernhard Walle 已提交
382
	.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_PERCPU | IRQF_IRQPOLL,
L
Linus Torvalds 已提交
383 384 385 386 387 388
};

#ifdef CONFIG_SMP
static struct irqaction ipi_action = {
	.handler = ipi_interrupt,
	.name = "IPI",
J
James Bottomley 已提交
389
	.flags = IRQF_DISABLED | IRQF_PERCPU,
L
Linus Torvalds 已提交
390 391 392 393 394 395 396
};
#endif

static void claim_cpu_irqs(void)
{
	int i;
	for (i = CPU_IRQ_BASE; i <= CPU_IRQ_MAX; i++) {
T
Thomas Gleixner 已提交
397
		irq_set_chip_and_handler(i, &cpu_interrupt_type,
J
James Bottomley 已提交
398
					 handle_percpu_irq);
L
Linus Torvalds 已提交
399 400
	}

T
Thomas Gleixner 已提交
401
	irq_set_handler(TIMER_IRQ, handle_percpu_irq);
402
	setup_irq(TIMER_IRQ, &timer_action);
L
Linus Torvalds 已提交
403
#ifdef CONFIG_SMP
T
Thomas Gleixner 已提交
404
	irq_set_handler(IPI_IRQ, handle_percpu_irq);
405
	setup_irq(IPI_IRQ, &ipi_action);
L
Linus Torvalds 已提交
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
#endif
}

void __init init_IRQ(void)
{
	local_irq_disable();	/* PARANOID - should already be disabled */
	mtctl(~0UL, 23);	/* EIRR : clear all pending external intr */
	claim_cpu_irqs();
#ifdef CONFIG_SMP
	if (!cpu_eiem)
		cpu_eiem = EIEM_MASK(IPI_IRQ) | EIEM_MASK(TIMER_IRQ);
#else
	cpu_eiem = EIEM_MASK(TIMER_IRQ);
#endif
        set_eiem(cpu_eiem);	/* EIEM : enable all external intr */

}
423