irq.c 18.5 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
/* $Id: irq.c,v 1.114 2002/01/11 08:45:38 davem Exp $
 * irq.c: UltraSparc IRQ handling/init/registry.
 *
 * Copyright (C) 1997  David S. Miller  (davem@caip.rutgers.edu)
 * Copyright (C) 1998  Eddie C. Dost    (ecd@skynet.be)
 * Copyright (C) 1998  Jakub Jelinek    (jj@ultra.linux.cz)
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
24
#include <linux/bootmem.h>
25
#include <linux/irq.h>
L
Linus Torvalds 已提交
26 27 28 29 30 31

#include <asm/ptrace.h>
#include <asm/processor.h>
#include <asm/atomic.h>
#include <asm/system.h>
#include <asm/irq.h>
32
#include <asm/io.h>
L
Linus Torvalds 已提交
33 34 35 36
#include <asm/sbus.h>
#include <asm/iommu.h>
#include <asm/upa.h>
#include <asm/oplib.h>
37
#include <asm/prom.h>
L
Linus Torvalds 已提交
38 39 40 41 42 43
#include <asm/timer.h>
#include <asm/smp.h>
#include <asm/starfire.h>
#include <asm/uaccess.h>
#include <asm/cache.h>
#include <asm/cpudata.h>
44
#include <asm/auxio.h>
45
#include <asm/head.h>
L
Linus Torvalds 已提交
46 47 48 49 50 51 52 53 54 55 56 57

/* UPA nodes send interrupt packet to UltraSparc with first data reg
 * value low 5 (7 on Starfire) bits holding the IRQ identifier being
 * delivered.  We must translate this into a non-vector IRQ so we can
 * set the softint on this cpu.
 *
 * To make processing these packets efficient and race free we use
 * an array of irq buckets below.  The interrupt vector handler in
 * entry.S feeds incoming packets into per-cpu pil-indexed lists.
 * The IVEC handler does not need to act atomically, the PIL dispatch
 * code uses CAS to get an atomic snapshot of the list and clear it
 * at the same time.
58 59 60
 *
 * If you make changes to ino_bucket, please update hand coded assembler
 * of the vectored interrupt trap handler(s) in entry.S and sun4v_ivec.S
L
Linus Torvalds 已提交
61
 */
62 63 64 65 66 67
struct ino_bucket {
	/* Next handler in per-CPU IRQ worklist.  We know that
	 * bucket pointers have the high 32-bits clear, so to
	 * save space we only store the bits we need.
	 */
/*0x00*/unsigned int irq_chain;
L
Linus Torvalds 已提交
68

69 70 71 72 73
	/* Virtual interrupt number assigned to this INO.  */
/*0x04*/unsigned int virt_irq;
};

#define NUM_IVECS	(IMAP_INR + 1)
L
Linus Torvalds 已提交
74 75
struct ino_bucket ivector_table[NUM_IVECS] __attribute__ ((aligned (SMP_CACHE_BYTES)));

76 77 78 79 80
#define __irq_ino(irq) \
        (((struct ino_bucket *)(unsigned long)(irq)) - &ivector_table[0])
#define __bucket(irq) ((struct ino_bucket *)(unsigned long)(irq))
#define __irq(bucket) ((unsigned int)(unsigned long)(bucket))

L
Linus Torvalds 已提交
81 82 83 84 85 86 87
/* This has to be in the main kernel image, it cannot be
 * turned into per-cpu data.  The reason is that the main
 * kernel image is locked into the TLB and this structure
 * is accessed from the vectored interrupt trap handler.  If
 * access to this structure takes a TLB miss it could cause
 * the 5-level sparc v9 trap stack to overflow.
 */
88
#define irq_work(__cpu)	&(trap_block[(__cpu)].irq_worklist)
L
Linus Torvalds 已提交
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
static unsigned int virt_to_real_irq_table[NR_IRQS];
static unsigned char virt_irq_cur = 1;

static unsigned char virt_irq_alloc(unsigned int real_irq)
{
	unsigned char ent;

	BUILD_BUG_ON(NR_IRQS >= 256);

	ent = virt_irq_cur;
	if (ent >= NR_IRQS) {
		printk(KERN_ERR "IRQ: Out of virtual IRQs.\n");
		return 0;
	}

	virt_irq_cur = ent + 1;
	virt_to_real_irq_table[ent] = real_irq;

	return ent;
}

#if 0 /* Currently unused. */
static unsigned char real_to_virt_irq(unsigned int real_irq)
{
	struct ino_bucket *bucket = __bucket(real_irq);

	return bucket->virt_irq;
}
#endif

static unsigned int virt_to_real_irq(unsigned char virt_irq)
{
	return virt_to_real_irq_table[virt_irq];
}

L
Linus Torvalds 已提交
125
/*
126
 * /proc/interrupts printing:
L
Linus Torvalds 已提交
127 128 129 130
 */

int show_interrupts(struct seq_file *p, void *v)
{
131 132
	int i = *(loff_t *) v, j;
	struct irqaction * action;
L
Linus Torvalds 已提交
133 134
	unsigned long flags;

135 136 137 138 139 140 141 142 143 144 145 146 147
	if (i == 0) {
		seq_printf(p, "           ");
		for_each_online_cpu(j)
			seq_printf(p, "CPU%d       ",j);
		seq_putc(p, '\n');
	}

	if (i < NR_IRQS) {
		spin_lock_irqsave(&irq_desc[i].lock, flags);
		action = irq_desc[i].action;
		if (!action)
			goto skip;
		seq_printf(p, "%3d: ",i);
L
Linus Torvalds 已提交
148 149 150
#ifndef CONFIG_SMP
		seq_printf(p, "%10u ", kstat_irqs(i));
#else
151 152
		for_each_online_cpu(j)
			seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
L
Linus Torvalds 已提交
153
#endif
154
		seq_printf(p, " %9s", irq_desc[i].chip->typename);
155 156 157
		seq_printf(p, "  %s", action->name);

		for (action=action->next; action; action = action->next)
158
			seq_printf(p, ", %s", action->name);
159

L
Linus Torvalds 已提交
160
		seq_putc(p, '\n');
161 162
skip:
		spin_unlock_irqrestore(&irq_desc[i].lock, flags);
L
Linus Torvalds 已提交
163 164 165 166
	}
	return 0;
}

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
extern unsigned long real_hard_smp_processor_id(void);

static unsigned int sun4u_compute_tid(unsigned long imap, unsigned long cpuid)
{
	unsigned int tid;

	if (this_is_starfire) {
		tid = starfire_translate(imap, cpuid);
		tid <<= IMAP_TID_SHIFT;
		tid &= IMAP_TID_UPA;
	} else {
		if (tlb_type == cheetah || tlb_type == cheetah_plus) {
			unsigned long ver;

			__asm__ ("rdpr %%ver, %0" : "=r" (ver));
			if ((ver >> 32UL) == __JALAPENO_ID ||
			    (ver >> 32UL) == __SERRANO_ID) {
				tid = cpuid << IMAP_TID_SHIFT;
				tid &= IMAP_TID_JBUS;
			} else {
				unsigned int a = cpuid & 0x1f;
				unsigned int n = (cpuid >> 5) & 0x1f;

				tid = ((a << IMAP_AID_SHIFT) |
				       (n << IMAP_NID_SHIFT));
				tid &= (IMAP_AID_SAFARI |
					IMAP_NID_SAFARI);;
			}
		} else {
			tid = cpuid << IMAP_TID_SHIFT;
			tid &= IMAP_TID_UPA;
		}
	}

	return tid;
}

204 205 206
struct irq_handler_data {
	unsigned long	iclr;
	unsigned long	imap;
207

208 209 210 211
	void		(*pre_handler)(unsigned int, void *, void *);
	void		*pre_handler_arg1;
	void		*pre_handler_arg2;
};
L
Linus Torvalds 已提交
212

213
static inline struct ino_bucket *virt_irq_to_bucket(unsigned int virt_irq)
L
Linus Torvalds 已提交
214
{
215
	unsigned int real_irq = virt_to_real_irq(virt_irq);
216
	struct ino_bucket *bucket = NULL;
L
Linus Torvalds 已提交
217

218 219
	if (likely(real_irq))
		bucket = __bucket(real_irq);
220

221
	return bucket;
L
Linus Torvalds 已提交
222 223
}

224 225
#ifdef CONFIG_SMP
static int irq_choose_cpu(unsigned int virt_irq)
226
{
227
	cpumask_t mask = irq_desc[virt_irq].affinity;
228
	int cpuid;
229

230 231 232 233
	if (cpus_equal(mask, CPU_MASK_ALL)) {
		static int irq_rover;
		static DEFINE_SPINLOCK(irq_rover_lock);
		unsigned long flags;
L
Linus Torvalds 已提交
234

235 236 237
		/* Round-robin distribution... */
	do_round_robin:
		spin_lock_irqsave(&irq_rover_lock, flags);
238

239 240 241 242 243 244 245 246 247
		while (!cpu_online(irq_rover)) {
			if (++irq_rover >= NR_CPUS)
				irq_rover = 0;
		}
		cpuid = irq_rover;
		do {
			if (++irq_rover >= NR_CPUS)
				irq_rover = 0;
		} while (!cpu_online(irq_rover));
L
Linus Torvalds 已提交
248

249 250 251
		spin_unlock_irqrestore(&irq_rover_lock, flags);
	} else {
		cpumask_t tmp;
252

253
		cpus_and(tmp, cpu_online_map, mask);
254

255 256
		if (cpus_empty(tmp))
			goto do_round_robin;
257

258
		cpuid = first_cpu(tmp);
L
Linus Torvalds 已提交
259
	}
260

261 262 263 264 265 266
	return cpuid;
}
#else
static int irq_choose_cpu(unsigned int virt_irq)
{
	return real_hard_smp_processor_id();
L
Linus Torvalds 已提交
267
}
268
#endif
L
Linus Torvalds 已提交
269

270
static void sun4u_irq_enable(unsigned int virt_irq)
271
{
272 273
	irq_desc_t *desc = irq_desc + virt_irq;
	struct irq_handler_data *data = desc->handler_data;
274

275 276 277
	if (likely(data)) {
		unsigned long cpuid, imap;
		unsigned int tid;
278

279 280
		cpuid = irq_choose_cpu(virt_irq);
		imap = data->imap;
281

282
		tid = sun4u_compute_tid(imap, cpuid);
283

284
		upa_writel(tid | IMAP_VALID, imap);
285 286 287
	}
}

288
static void sun4u_irq_disable(unsigned int virt_irq)
L
Linus Torvalds 已提交
289
{
290 291
	irq_desc_t *desc = irq_desc + virt_irq;
	struct irq_handler_data *data = desc->handler_data;
L
Linus Torvalds 已提交
292

293 294 295
	if (likely(data)) {
		unsigned long imap = data->imap;
		u32 tmp = upa_readl(imap);
L
Linus Torvalds 已提交
296

297 298
		tmp &= ~IMAP_VALID;
		upa_writel(tmp, imap);
299 300 301
	}
}

302
static void sun4u_irq_end(unsigned int virt_irq)
303
{
304 305
	irq_desc_t *desc = irq_desc + virt_irq;
	struct irq_handler_data *data = desc->handler_data;
306

307 308
	if (likely(data))
		upa_writel(ICLR_IDLE, data->iclr);
309 310
}

311
static void sun4v_irq_enable(unsigned int virt_irq)
312
{
313 314
	struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
	unsigned int ino = bucket - &ivector_table[0];
315

316 317 318
	if (likely(bucket)) {
		unsigned long cpuid;
		int err;
319

320
		cpuid = irq_choose_cpu(virt_irq);
321

322 323 324 325 326 327 328 329
		err = sun4v_intr_settarget(ino, cpuid);
		if (err != HV_EOK)
			printk("sun4v_intr_settarget(%x,%lu): err(%d)\n",
			       ino, cpuid, err);
		err = sun4v_intr_setenabled(ino, HV_INTR_ENABLED);
		if (err != HV_EOK)
			printk("sun4v_intr_setenabled(%x): err(%d)\n",
			       ino, err);
330 331 332
	}
}

333
static void sun4v_irq_disable(unsigned int virt_irq)
L
Linus Torvalds 已提交
334
{
335 336
	struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
	unsigned int ino = bucket - &ivector_table[0];
L
Linus Torvalds 已提交
337

338 339
	if (likely(bucket)) {
		int err;
L
Linus Torvalds 已提交
340

341 342 343 344
		err = sun4v_intr_setenabled(ino, HV_INTR_DISABLED);
		if (err != HV_EOK)
			printk("sun4v_intr_setenabled(%x): "
			       "err(%d)\n", ino, err);
L
Linus Torvalds 已提交
345
	}
346
}
L
Linus Torvalds 已提交
347

348 349 350 351
static void sun4v_irq_end(unsigned int virt_irq)
{
	struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
	unsigned int ino = bucket - &ivector_table[0];
L
Linus Torvalds 已提交
352

353 354
	if (likely(bucket)) {
		int err;
L
Linus Torvalds 已提交
355

356 357 358 359
		err = sun4v_intr_setstate(ino, HV_INTR_STATE_IDLE);
		if (err != HV_EOK)
			printk("sun4v_intr_setstate(%x): "
			       "err(%d)\n", ino, err);
L
Linus Torvalds 已提交
360 361 362
	}
}

363
static void run_pre_handler(unsigned int virt_irq)
L
Linus Torvalds 已提交
364
{
365 366 367
	struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
	irq_desc_t *desc = irq_desc + virt_irq;
	struct irq_handler_data *data = desc->handler_data;
L
Linus Torvalds 已提交
368

369 370 371 372
	if (likely(data->pre_handler)) {
		data->pre_handler(__irq_ino(__irq(bucket)),
				  data->pre_handler_arg1,
				  data->pre_handler_arg2);
L
Linus Torvalds 已提交
373
	}
374 375
}

376 377 378 379 380 381
static struct hw_interrupt_type sun4u_irq = {
	.typename	= "sun4u",
	.enable		= sun4u_irq_enable,
	.disable	= sun4u_irq_disable,
	.end		= sun4u_irq_end,
};
382

383 384 385 386 387 388 389
static struct hw_interrupt_type sun4u_irq_ack = {
	.typename	= "sun4u+ack",
	.enable		= sun4u_irq_enable,
	.disable	= sun4u_irq_disable,
	.ack		= run_pre_handler,
	.end		= sun4u_irq_end,
};
390

391 392 393 394 395 396
static struct hw_interrupt_type sun4v_irq = {
	.typename	= "sun4v",
	.enable		= sun4v_irq_enable,
	.disable	= sun4v_irq_disable,
	.end		= sun4v_irq_end,
};
L
Linus Torvalds 已提交
397

398 399 400 401 402 403 404
static struct hw_interrupt_type sun4v_irq_ack = {
	.typename	= "sun4v+ack",
	.enable		= sun4v_irq_enable,
	.disable	= sun4v_irq_disable,
	.ack		= run_pre_handler,
	.end		= sun4v_irq_end,
};
L
Linus Torvalds 已提交
405

406 407 408 409 410 411
void irq_install_pre_handler(int virt_irq,
			     void (*func)(unsigned int, void *, void *),
			     void *arg1, void *arg2)
{
	irq_desc_t *desc = irq_desc + virt_irq;
	struct irq_handler_data *data = desc->handler_data;
412

413 414 415
	data->pre_handler = func;
	data->pre_handler_arg1 = arg1;
	data->pre_handler_arg2 = arg2;
L
Linus Torvalds 已提交
416

417 418
	desc->chip = (desc->chip == &sun4u_irq ?
		      &sun4u_irq_ack : &sun4v_irq_ack);
419
}
L
Linus Torvalds 已提交
420

421 422 423 424 425 426
unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap)
{
	struct ino_bucket *bucket;
	struct irq_handler_data *data;
	irq_desc_t *desc;
	int ino;
L
Linus Torvalds 已提交
427

428
	BUG_ON(tlb_type == hypervisor);
429

430 431 432 433
	ino = (upa_readl(imap) & (IMAP_IGN | IMAP_INO)) + inofixup;
	bucket = &ivector_table[ino];
	if (!bucket->virt_irq) {
		bucket->virt_irq = virt_irq_alloc(__irq(bucket));
434
		irq_desc[bucket->virt_irq].chip = &sun4u_irq;
435
	}
L
Linus Torvalds 已提交
436

437 438 439
	desc = irq_desc + bucket->virt_irq;
	if (unlikely(desc->handler_data))
		goto out;
440

441 442 443 444
	data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
	if (unlikely(!data)) {
		prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
		prom_halt();
L
Linus Torvalds 已提交
445
	}
446
	desc->handler_data = data;
L
Linus Torvalds 已提交
447

448 449
	data->imap  = imap;
	data->iclr  = iclr;
L
Linus Torvalds 已提交
450

451 452 453
out:
	return bucket->virt_irq;
}
L
Linus Torvalds 已提交
454

455
unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino)
L
Linus Torvalds 已提交
456
{
457
	struct ino_bucket *bucket;
458 459 460
	struct irq_handler_data *data;
	unsigned long sysino;
	irq_desc_t *desc;
461

462
	BUG_ON(tlb_type != hypervisor);
L
Linus Torvalds 已提交
463

464 465 466 467
	sysino = sun4v_devino_to_sysino(devhandle, devino);
	bucket = &ivector_table[sysino];
	if (!bucket->virt_irq) {
		bucket->virt_irq = virt_irq_alloc(__irq(bucket));
468
		irq_desc[bucket->virt_irq].chip = &sun4v_irq;
L
Linus Torvalds 已提交
469 470
	}

471 472
	desc = irq_desc + bucket->virt_irq;
	if (unlikely(desc->handler_data))
L
Linus Torvalds 已提交
473 474
		goto out;

475 476 477 478 479 480
	data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
	if (unlikely(!data)) {
		prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
		prom_halt();
	}
	desc->handler_data = data;
L
Linus Torvalds 已提交
481

482 483 484 485 486 487
	/* Catch accidental accesses to these things.  IMAP/ICLR handling
	 * is done by hypervisor calls on sun4v platforms, not by direct
	 * register accesses.
	 */
	data->imap = ~0UL;
	data->iclr = ~0UL;
L
Linus Torvalds 已提交
488

489 490 491
out:
	return bucket->virt_irq;
}
L
Linus Torvalds 已提交
492

493 494 495 496 497
void hw_resend_irq(struct hw_interrupt_type *handler, unsigned int virt_irq)
{
	struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
	unsigned long pstate;
	unsigned int *ent;
498

499 500 501 502 503 504 505 506 507
	__asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
	__asm__ __volatile__("wrpr %0, %1, %%pstate"
			     : : "r" (pstate), "i" (PSTATE_IE));
	ent = irq_work(smp_processor_id());
	bucket->irq_chain = *ent;
	*ent = __irq(bucket);
	set_softint(1 << PIL_DEVICE_IRQ);
	__asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate));
}
508

509 510 511 512
void ack_bad_irq(unsigned int virt_irq)
{
	struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq);
	unsigned int ino = 0xdeadbeef;
513

514 515
	if (bucket)
		ino = bucket - &ivector_table[0];
516

517 518
	printk(KERN_CRIT "Unexpected IRQ from ino[%x] virt_irq[%u]\n",
	       ino, virt_irq);
L
Linus Torvalds 已提交
519 520
}

521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
#ifndef CONFIG_SMP
extern irqreturn_t timer_interrupt(int, void *, struct pt_regs *);

void timer_irq(int irq, struct pt_regs *regs)
{
	unsigned long clr_mask = 1 << irq;
	unsigned long tick_mask = tick_ops->softint_mask;

	if (get_softint() & tick_mask) {
		irq = 0;
		clr_mask = tick_mask;
	}
	clear_softint(clr_mask);

	irq_enter();
536

537
	kstat_this_cpu.irqs[0]++;
538
	timer_interrupt(irq, NULL, regs);
539

540 541 542 543
	irq_exit();
}
#endif

L
Linus Torvalds 已提交
544 545
void handler_irq(int irq, struct pt_regs *regs)
{
546
	struct ino_bucket *bucket;
L
Linus Torvalds 已提交
547 548 549 550 551 552

	clear_softint(1 << irq);

	irq_enter();

	/* Sliiiick... */
553 554 555
	bucket = __bucket(xchg32(irq_work(smp_processor_id()), 0));
	while (bucket) {
		struct ino_bucket *next = __bucket(bucket->irq_chain);
L
Linus Torvalds 已提交
556

557 558
		bucket->irq_chain = 0;
		__do_IRQ(bucket->virt_irq, regs);
559

560
		bucket = next;
L
Linus Torvalds 已提交
561
	}
562

L
Linus Torvalds 已提交
563 564 565
	irq_exit();
}

566 567 568 569 570 571
struct sun5_timer {
	u64	count0;
	u64	limit0;
	u64	count1;
	u64	limit1;
};
L
Linus Torvalds 已提交
572

573
static struct sun5_timer *prom_timers;
L
Linus Torvalds 已提交
574 575 576 577
static u64 prom_limit0, prom_limit1;

static void map_prom_timers(void)
{
578 579
	struct device_node *dp;
	unsigned int *addr;
L
Linus Torvalds 已提交
580 581

	/* PROM timer node hangs out in the top level of device siblings... */
582 583 584 585 586 587 588
	dp = of_find_node_by_path("/");
	dp = dp->child;
	while (dp) {
		if (!strcmp(dp->name, "counter-timer"))
			break;
		dp = dp->sibling;
	}
L
Linus Torvalds 已提交
589 590 591 592

	/* Assume if node is not present, PROM uses different tick mechanism
	 * which we should not care about.
	 */
593
	if (!dp) {
L
Linus Torvalds 已提交
594 595 596 597 598
		prom_timers = (struct sun5_timer *) 0;
		return;
	}

	/* If PROM is really using this, it must be mapped by him. */
599 600
	addr = of_get_property(dp, "address", NULL);
	if (!addr) {
L
Linus Torvalds 已提交
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
		prom_printf("PROM does not have timer mapped, trying to continue.\n");
		prom_timers = (struct sun5_timer *) 0;
		return;
	}
	prom_timers = (struct sun5_timer *) ((unsigned long)addr[0]);
}

static void kill_prom_timer(void)
{
	if (!prom_timers)
		return;

	/* Save them away for later. */
	prom_limit0 = prom_timers->limit0;
	prom_limit1 = prom_timers->limit1;

	/* Just as in sun4c/sun4m PROM uses timer which ticks at IRQ 14.
	 * We turn both off here just to be paranoid.
	 */
	prom_timers->limit0 = 0;
	prom_timers->limit1 = 0;

	/* Wheee, eat the interrupt packet too... */
	__asm__ __volatile__(
"	mov	0x40, %%g2\n"
"	ldxa	[%%g0] %0, %%g1\n"
"	ldxa	[%%g2] %1, %%g1\n"
"	stxa	%%g0, [%%g0] %0\n"
"	membar	#Sync\n"
	: /* no outputs */
	: "i" (ASI_INTR_RECEIVE), "i" (ASI_INTR_R)
	: "g1", "g2");
}

void init_irqwork_curcpu(void)
{
	int cpu = hard_smp_processor_id();

639
	trap_block[cpu].irq_worklist = 0;
L
Linus Torvalds 已提交
640 641
}

642
static void __cpuinit register_one_mondo(unsigned long paddr, unsigned long type)
643
{
644 645 646 647 648 649 650
	unsigned long num_entries = 128;
	unsigned long status;

	status = sun4v_cpu_qconf(type, paddr, num_entries);
	if (status != HV_EOK) {
		prom_printf("SUN4V: sun4v_cpu_qconf(%lu:%lx:%lu) failed, "
			    "err %lu\n", type, paddr, num_entries, status);
651 652 653 654
		prom_halt();
	}
}

655
static void __cpuinit sun4v_register_mondo_queues(int this_cpu)
656
{
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
	struct trap_per_cpu *tb = &trap_block[this_cpu];

	register_one_mondo(tb->cpu_mondo_pa, HV_CPU_QUEUE_CPU_MONDO);
	register_one_mondo(tb->dev_mondo_pa, HV_CPU_QUEUE_DEVICE_MONDO);
	register_one_mondo(tb->resum_mondo_pa, HV_CPU_QUEUE_RES_ERROR);
	register_one_mondo(tb->nonresum_mondo_pa, HV_CPU_QUEUE_NONRES_ERROR);
}

static void __cpuinit alloc_one_mondo(unsigned long *pa_ptr, int use_bootmem)
{
	void *page;

	if (use_bootmem)
		page = alloc_bootmem_low_pages(PAGE_SIZE);
	else
		page = (void *) get_zeroed_page(GFP_ATOMIC);

	if (!page) {
		prom_printf("SUN4V: Error, cannot allocate mondo queue.\n");
		prom_halt();
	}

	*pa_ptr = __pa(page);
}

static void __cpuinit alloc_one_kbuf(unsigned long *pa_ptr, int use_bootmem)
{
	void *page;

	if (use_bootmem)
		page = alloc_bootmem_low_pages(PAGE_SIZE);
	else
		page = (void *) get_zeroed_page(GFP_ATOMIC);
690 691 692 693 694 695 696 697 698

	if (!page) {
		prom_printf("SUN4V: Error, cannot allocate kbuf page.\n");
		prom_halt();
	}

	*pa_ptr = __pa(page);
}

699
static void __cpuinit init_cpu_send_mondo_info(struct trap_per_cpu *tb, int use_bootmem)
700 701
{
#ifdef CONFIG_SMP
702
	void *page;
703 704 705

	BUILD_BUG_ON((NR_CPUS * sizeof(u16)) > (PAGE_SIZE - 64));

706 707 708 709 710
	if (use_bootmem)
		page = alloc_bootmem_low_pages(PAGE_SIZE);
	else
		page = (void *) get_zeroed_page(GFP_ATOMIC);

711 712 713 714 715 716 717 718 719 720
	if (!page) {
		prom_printf("SUN4V: Error, cannot allocate cpu mondo page.\n");
		prom_halt();
	}

	tb->cpu_mondo_block_pa = __pa(page);
	tb->cpu_list_pa = __pa(page + 64);
#endif
}

721
/* Allocate and register the mondo and error queues for this cpu.  */
722
void __cpuinit sun4v_init_mondo_queues(int use_bootmem, int cpu, int alloc, int load)
723 724 725
{
	struct trap_per_cpu *tb = &trap_block[cpu];

726 727 728 729 730 731 732
	if (alloc) {
		alloc_one_mondo(&tb->cpu_mondo_pa, use_bootmem);
		alloc_one_mondo(&tb->dev_mondo_pa, use_bootmem);
		alloc_one_mondo(&tb->resum_mondo_pa, use_bootmem);
		alloc_one_kbuf(&tb->resum_kernel_buf_pa, use_bootmem);
		alloc_one_mondo(&tb->nonresum_mondo_pa, use_bootmem);
		alloc_one_kbuf(&tb->nonresum_kernel_buf_pa, use_bootmem);
733

734 735
		init_cpu_send_mondo_info(tb, use_bootmem);
	}
736

737 738 739 740 741 742 743 744
	if (load) {
		if (cpu != hard_smp_processor_id()) {
			prom_printf("SUN4V: init mondo on cpu %d not %d\n",
				    cpu, hard_smp_processor_id());
			prom_halt();
		}
		sun4v_register_mondo_queues(cpu);
	}
745 746
}

747 748 749 750
static struct irqaction timer_irq_action = {
	.name = "timer",
};

L
Linus Torvalds 已提交
751 752 753 754 755 756 757
/* Only invoked on boot processor. */
void __init init_IRQ(void)
{
	map_prom_timers();
	kill_prom_timer();
	memset(&ivector_table[0], 0, sizeof(ivector_table));

758
	if (tlb_type == hypervisor)
759
		sun4v_init_mondo_queues(1, hard_smp_processor_id(), 1, 1);
760

L
Linus Torvalds 已提交
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
	/* We need to clear any IRQ's pending in the soft interrupt
	 * registers, a spurious one could be left around from the
	 * PROM timer which we just disabled.
	 */
	clear_softint(get_softint());

	/* Now that ivector table is initialized, it is safe
	 * to receive IRQ vector traps.  We will normally take
	 * one or two right now, in case some device PROM used
	 * to boot us wants to speak to us.  We just ignore them.
	 */
	__asm__ __volatile__("rdpr	%%pstate, %%g1\n\t"
			     "or	%%g1, %0, %%g1\n\t"
			     "wrpr	%%g1, 0x0, %%pstate"
			     : /* No outputs */
			     : "i" (PSTATE_IE)
			     : "g1");

779
	irq_desc[0].action = &timer_irq_action;
L
Linus Torvalds 已提交
780
}