xics.c 19.1 KB
Newer Older
1 2
/*
 * arch/powerpc/platforms/pseries/xics.c
L
Linus Torvalds 已提交
3 4 5 6 7 8 9 10
 *
 * Copyright 2000 IBM 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.
 */
11 12


L
Linus Torvalds 已提交
13 14 15 16 17 18 19 20 21 22 23
#include <linux/types.h>
#include <linux/threads.h>
#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
#include <linux/signal.h>
#include <linux/init.h>
#include <linux/gfp.h>
#include <linux/radix-tree.h>
#include <linux/cpu.h>
24

25
#include <asm/firmware.h>
L
Linus Torvalds 已提交
26 27 28 29 30 31 32
#include <asm/prom.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/smp.h>
#include <asm/rtas.h>
#include <asm/hvcall.h>
#include <asm/machdep.h>
33
#include <asm/i8259.h>
L
Linus Torvalds 已提交
34

35
#include "xics.h"
36
#include "plpar_wrappers.h"
37

L
Linus Torvalds 已提交
38 39 40 41 42 43
#define XICS_IPI		2
#define XICS_IRQ_SPURIOUS	0

/* Want a priority other than 0.  Various HW issues require this. */
#define	DEFAULT_PRIORITY	5

44
/*
L
Linus Torvalds 已提交
45
 * Mark IPIs as higher priority so we can take them inside interrupts that
46
 * arent marked IRQF_DISABLED
L
Linus Torvalds 已提交
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
 */
#define IPI_PRIORITY		4

struct xics_ipl {
	union {
		u32 word;
		u8 bytes[4];
	} xirr_poll;
	union {
		u32 word;
		u8 bytes[4];
	} xirr;
	u32 dummy;
	union {
		u32 word;
		u8 bytes[4];
	} qirr;
};

static struct xics_ipl __iomem *xics_per_cpu[NR_CPUS];

static unsigned int default_server = 0xFF;
A
Anton Blanchard 已提交
69 70
static unsigned int default_distrib_server = 0;
static unsigned int interrupt_server_size = 8;
L
Linus Torvalds 已提交
71

72 73
static struct irq_host *xics_host;

L
Linus Torvalds 已提交
74 75 76 77 78 79
/*
 * XICS only has a single IPI, so encode the messages per CPU
 */
struct xics_ipi_struct xics_ipi_message[NR_CPUS] __cacheline_aligned;

/* RTAS service tokens */
A
Anton Blanchard 已提交
80 81 82 83
static int ibm_get_xive;
static int ibm_set_xive;
static int ibm_int_on;
static int ibm_int_off;
L
Linus Torvalds 已提交
84 85


86
/* Direct HW low level accessors */
L
Linus Torvalds 已提交
87 88


89
static inline unsigned int direct_xirr_info_get(void)
L
Linus Torvalds 已提交
90
{
91 92 93
	int cpu = smp_processor_id();

	return in_be32(&xics_per_cpu[cpu]->xirr.word);
L
Linus Torvalds 已提交
94 95
}

96
static inline void direct_xirr_info_set(int value)
L
Linus Torvalds 已提交
97
{
98 99 100
	int cpu = smp_processor_id();

	out_be32(&xics_per_cpu[cpu]->xirr.word, value);
L
Linus Torvalds 已提交
101 102
}

103
static inline void direct_cppr_info(u8 value)
L
Linus Torvalds 已提交
104
{
105 106 107
	int cpu = smp_processor_id();

	out_8(&xics_per_cpu[cpu]->xirr.bytes[0], value);
L
Linus Torvalds 已提交
108 109
}

110
static inline void direct_qirr_info(int n_cpu, u8 value)
L
Linus Torvalds 已提交
111 112 113 114 115
{
	out_8(&xics_per_cpu[n_cpu]->qirr.bytes[0], value);
}


116
/* LPAR low level accessors */
L
Linus Torvalds 已提交
117 118


119
static inline unsigned int lpar_xirr_info_get(void)
L
Linus Torvalds 已提交
120 121
{
	unsigned long lpar_rc;
122
	unsigned long return_value;
L
Linus Torvalds 已提交
123 124

	lpar_rc = plpar_xirr(&return_value);
125
	if (lpar_rc != H_SUCCESS)
126
		panic(" bad return code xirr - rc = %lx \n", lpar_rc);
127
	return (unsigned int)return_value;
L
Linus Torvalds 已提交
128 129
}

130
static inline void lpar_xirr_info_set(int value)
L
Linus Torvalds 已提交
131 132 133 134 135
{
	unsigned long lpar_rc;
	unsigned long val64 = value & 0xffffffff;

	lpar_rc = plpar_eoi(val64);
136
	if (lpar_rc != H_SUCCESS)
L
Linus Torvalds 已提交
137
		panic("bad return code EOI - rc = %ld, value=%lx\n", lpar_rc,
138
		      val64);
L
Linus Torvalds 已提交
139 140
}

141
static inline void lpar_cppr_info(u8 value)
L
Linus Torvalds 已提交
142 143 144 145
{
	unsigned long lpar_rc;

	lpar_rc = plpar_cppr(value);
146
	if (lpar_rc != H_SUCCESS)
147
		panic("bad return code cppr - rc = %lx\n", lpar_rc);
L
Linus Torvalds 已提交
148 149
}

150
static inline void lpar_qirr_info(int n_cpu , u8 value)
L
Linus Torvalds 已提交
151 152 153 154
{
	unsigned long lpar_rc;

	lpar_rc = plpar_ipi(get_hard_smp_processor_id(n_cpu), value);
155
	if (lpar_rc != H_SUCCESS)
156
		panic("bad return code qirr - rc = %lx\n", lpar_rc);
L
Linus Torvalds 已提交
157 158 159
}


160
/* High level handlers and init code */
L
Linus Torvalds 已提交
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
static void xics_update_irq_servers(void)
{
	int i, j;
	struct device_node *np;
	u32 ilen;
	const u32 *ireg, *isize;
	u32 hcpuid;

	/* Find the server numbers for the boot cpu. */
	np = of_get_cpu_node(boot_cpuid, NULL);
	BUG_ON(!np);

	ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen);
	if (!ireg) {
		of_node_put(np);
		return;
	}

	i = ilen / sizeof(int);
	hcpuid = get_hard_smp_processor_id(boot_cpuid);

	/* Global interrupt distribution server is specified in the last
	 * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last
	 * entry fom this property for current boot cpu id and use it as
	 * default distribution server
	 */
	for (j = 0; j < i; j += 2) {
		if (ireg[j] == hcpuid) {
			default_server = hcpuid;
			default_distrib_server = ireg[j+1];

			isize = of_get_property(np,
					"ibm,interrupt-server#-size", NULL);
			if (isize)
				interrupt_server_size = *isize;
		}
	}

	of_node_put(np);
}
L
Linus Torvalds 已提交
202 203

#ifdef CONFIG_SMP
204
static int get_irq_server(unsigned int virq, unsigned int strict_check)
L
Linus Torvalds 已提交
205
{
206
	int server;
L
Linus Torvalds 已提交
207
	/* For the moment only implement delivery to all cpus or one cpu */
208
	cpumask_t cpumask = irq_desc[virq].affinity;
L
Linus Torvalds 已提交
209 210 211 212 213
	cpumask_t tmp = CPU_MASK_NONE;

	if (!distribute_irqs)
		return default_server;

214
	if (!cpus_equal(cpumask, CPU_MASK_ALL)) {
L
Linus Torvalds 已提交
215 216
		cpus_and(tmp, cpu_online_map, cpumask);

217 218 219 220 221 222 223
		server = first_cpu(tmp);

		if (server < NR_CPUS)
			return get_hard_smp_processor_id(server);

		if (strict_check)
			return -1;
L
Linus Torvalds 已提交
224 225
	}

226 227
	if (cpus_equal(cpu_online_map, cpu_present_map))
		return default_distrib_server;
L
Linus Torvalds 已提交
228

229
	return default_server;
L
Linus Torvalds 已提交
230 231
}
#else
232
static int get_irq_server(unsigned int virq, unsigned int strict_check)
L
Linus Torvalds 已提交
233 234 235 236 237
{
	return default_server;
}
#endif

238 239

static void xics_unmask_irq(unsigned int virq)
L
Linus Torvalds 已提交
240 241 242
{
	unsigned int irq;
	int call_status;
243
	int server;
L
Linus Torvalds 已提交
244

245 246 247 248 249
	pr_debug("xics: unmask virq %d\n", virq);

	irq = (unsigned int)irq_map[virq].hwirq;
	pr_debug(" -> map to hwirq 0x%x\n", irq);
	if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS)
L
Linus Torvalds 已提交
250 251
		return;

252
	server = get_irq_server(virq, 0);
253

L
Linus Torvalds 已提交
254 255 256
	call_status = rtas_call(ibm_set_xive, 3, 1, NULL, irq, server,
				DEFAULT_PRIORITY);
	if (call_status != 0) {
A
Anton Blanchard 已提交
257 258 259
		printk(KERN_ERR "xics_enable_irq: irq=%u: ibm_set_xive "
		       "returned %d\n", irq, call_status);
		printk("set_xive %x, server %x\n", ibm_set_xive, server);
L
Linus Torvalds 已提交
260 261 262 263 264 265
		return;
	}

	/* Now unmask the interrupt (often a no-op) */
	call_status = rtas_call(ibm_int_on, 1, 1, NULL, irq);
	if (call_status != 0) {
A
Anton Blanchard 已提交
266 267
		printk(KERN_ERR "xics_enable_irq: irq=%u: ibm_int_on "
		       "returned %d\n", irq, call_status);
L
Linus Torvalds 已提交
268 269 270 271
		return;
	}
}

272
static void xics_mask_real_irq(unsigned int irq)
L
Linus Torvalds 已提交
273 274 275 276 277 278 279 280
{
	int call_status;

	if (irq == XICS_IPI)
		return;

	call_status = rtas_call(ibm_int_off, 1, 1, NULL, irq);
	if (call_status != 0) {
A
Anton Blanchard 已提交
281 282
		printk(KERN_ERR "xics_disable_real_irq: irq=%u: "
		       "ibm_int_off returned %d\n", irq, call_status);
L
Linus Torvalds 已提交
283 284 285 286
		return;
	}

	/* Have to set XIVE to 0xff to be able to remove a slot */
287 288
	call_status = rtas_call(ibm_set_xive, 3, 1, NULL, irq,
				default_server, 0xff);
L
Linus Torvalds 已提交
289
	if (call_status != 0) {
A
Anton Blanchard 已提交
290 291
		printk(KERN_ERR "xics_disable_irq: irq=%u: ibm_set_xive(0xff)"
		       " returned %d\n", irq, call_status);
L
Linus Torvalds 已提交
292 293 294 295
		return;
	}
}

296
static void xics_mask_irq(unsigned int virq)
L
Linus Torvalds 已提交
297 298 299
{
	unsigned int irq;

300 301 302 303 304 305
	pr_debug("xics: mask virq %d\n", virq);

	irq = (unsigned int)irq_map[virq].hwirq;
	if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS)
		return;
	xics_mask_real_irq(irq);
306 307
}

308
static unsigned int xics_startup(unsigned int virq)
309
{
310
	/* unmask it */
311 312 313 314
	xics_unmask_irq(virq);
	return 0;
}

315
static void xics_eoi_direct(unsigned int virq)
L
Linus Torvalds 已提交
316
{
317
	unsigned int irq = (unsigned int)irq_map[virq].hwirq;
L
Linus Torvalds 已提交
318 319

	iosync();
320
	direct_xirr_info_set((0xff << 24) | irq);
L
Linus Torvalds 已提交
321 322
}

323

324
static void xics_eoi_lpar(unsigned int virq)
L
Linus Torvalds 已提交
325
{
326
	unsigned int irq = (unsigned int)irq_map[virq].hwirq;
L
Linus Torvalds 已提交
327

328
	iosync();
329
	lpar_xirr_info_set((0xff << 24) | irq);
L
Linus Torvalds 已提交
330 331
}

332
static inline unsigned int xics_xirr_vector(unsigned int xirr)
L
Linus Torvalds 已提交
333
{
334 335 336 337 338 339 340 341 342 343 344 345
	/*
	 * The top byte is the old cppr, to be restored on EOI.
	 * The remaining 24 bits are the vector.
	 */
	return xirr & 0x00ffffff;
}

static void xics_mask_unknown_vec(unsigned int vec)
{
	printk(KERN_ERR "Interrupt %u (real) is invalid, disabling it.\n", vec);
	xics_mask_real_irq(vec);
}
L
Linus Torvalds 已提交
346

347 348 349 350 351
static unsigned int xics_get_irq_direct(void)
{
	unsigned int xirr = direct_xirr_info_get();
	unsigned int vec = xics_xirr_vector(xirr);
	unsigned int irq;
L
Linus Torvalds 已提交
352

353 354
	if (vec == XICS_IRQ_SPURIOUS)
		return NO_IRQ;
355

356
	irq = irq_radix_revmap_lookup(xics_host, vec);
357
	if (likely(irq != NO_IRQ))
358
		return irq;
359

360 361
	/* We don't have a linux mapping, so have rtas mask it. */
	xics_mask_unknown_vec(vec);
L
Linus Torvalds 已提交
362

363 364 365
	/* We might learn about it later, so EOI it */
	direct_xirr_info_set(xirr);
	return NO_IRQ;
366 367
}

O
Olaf Hering 已提交
368
static unsigned int xics_get_irq_lpar(void)
L
Linus Torvalds 已提交
369
{
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
	unsigned int xirr = lpar_xirr_info_get();
	unsigned int vec = xics_xirr_vector(xirr);
	unsigned int irq;

	if (vec == XICS_IRQ_SPURIOUS)
		return NO_IRQ;

	irq = irq_radix_revmap_lookup(xics_host, vec);
	if (likely(irq != NO_IRQ))
		return irq;

	/* We don't have a linux mapping, so have RTAS mask it. */
	xics_mask_unknown_vec(vec);

	/* We might learn about it later, so EOI it */
	lpar_xirr_info_set(xirr);
	return NO_IRQ;
387 388 389
}

#ifdef CONFIG_SMP
L
Linus Torvalds 已提交
390

391
static irqreturn_t xics_ipi_dispatch(int cpu)
392
{
L
Linus Torvalds 已提交
393 394 395 396 397 398
	WARN_ON(cpu_is_offline(cpu));

	while (xics_ipi_message[cpu].value) {
		if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION,
				       &xics_ipi_message[cpu].value)) {
			mb();
399
			smp_message_recv(PPC_MSG_CALL_FUNCTION);
L
Linus Torvalds 已提交
400 401 402 403
		}
		if (test_and_clear_bit(PPC_MSG_RESCHEDULE,
				       &xics_ipi_message[cpu].value)) {
			mb();
404
			smp_message_recv(PPC_MSG_RESCHEDULE);
L
Linus Torvalds 已提交
405
		}
406
		if (test_and_clear_bit(PPC_MSG_CALL_FUNC_SINGLE,
L
Linus Torvalds 已提交
407 408
				       &xics_ipi_message[cpu].value)) {
			mb();
409
			smp_message_recv(PPC_MSG_CALL_FUNC_SINGLE);
L
Linus Torvalds 已提交
410
		}
411
#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
L
Linus Torvalds 已提交
412 413 414
		if (test_and_clear_bit(PPC_MSG_DEBUGGER_BREAK,
				       &xics_ipi_message[cpu].value)) {
			mb();
415
			smp_message_recv(PPC_MSG_DEBUGGER_BREAK);
L
Linus Torvalds 已提交
416 417 418 419 420 421
		}
#endif
	}
	return IRQ_HANDLED;
}

422
static irqreturn_t xics_ipi_action_direct(int irq, void *dev_id)
423 424 425 426 427
{
	int cpu = smp_processor_id();

	direct_qirr_info(cpu, 0xff);

428
	return xics_ipi_dispatch(cpu);
429 430
}

431
static irqreturn_t xics_ipi_action_lpar(int irq, void *dev_id)
432 433 434 435 436
{
	int cpu = smp_processor_id();

	lpar_qirr_info(cpu, 0xff);

437
	return xics_ipi_dispatch(cpu);
438 439
}

L
Linus Torvalds 已提交
440 441
void xics_cause_IPI(int cpu)
{
442 443 444 445
	if (firmware_has_feature(FW_FEATURE_LPAR))
		lpar_qirr_info(cpu, IPI_PRIORITY);
	else
		direct_qirr_info(cpu, IPI_PRIORITY);
L
Linus Torvalds 已提交
446
}
447

448
#endif /* CONFIG_SMP */
L
Linus Torvalds 已提交
449

450
static void xics_set_cpu_priority(unsigned char cppr)
451 452
{
	if (firmware_has_feature(FW_FEATURE_LPAR))
453
		lpar_cppr_info(cppr);
454
	else
455
		direct_cppr_info(cppr);
456 457 458 459 460 461 462 463
	iosync();
}

static void xics_set_affinity(unsigned int virq, cpumask_t cpumask)
{
	unsigned int irq;
	int status;
	int xics_status[2];
464
	int irq_server;
465

466 467
	irq = (unsigned int)irq_map[virq].hwirq;
	if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS)
468 469 470 471 472 473 474 475 476 477
		return;

	status = rtas_call(ibm_get_xive, 1, 3, xics_status, irq);

	if (status) {
		printk(KERN_ERR "xics_set_affinity: irq=%u ibm,get-xive "
		       "returns %d\n", irq, status);
		return;
	}

478 479 480 481
	/*
	 * For the moment only implement delivery to all cpus or one cpu.
	 * Get current irq_server for the given irq
	 */
482
	irq_server = get_irq_server(virq, 1);
483 484 485 486 487 488
	if (irq_server == -1) {
		char cpulist[128];
		cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask);
		printk(KERN_WARNING "xics_set_affinity: No online cpus in "
				"the mask %s for irq %d\n", cpulist, virq);
		return;
489 490 491
	}

	status = rtas_call(ibm_set_xive, 3, 1, NULL,
492
				irq, irq_server, xics_status[1]);
493 494 495 496 497 498 499 500

	if (status) {
		printk(KERN_ERR "xics_set_affinity: irq=%u ibm,set-xive "
		       "returns %d\n", irq, status);
		return;
	}
}

501 502
void xics_setup_cpu(void)
{
503
	xics_set_cpu_priority(0xff);
504 505 506 507 508 509 510 511

	/*
	 * Put the calling processor into the GIQ.  This is really only
	 * necessary from a secondary thread as the OF start-cpu interface
	 * performs this function for us on primary threads.
	 *
	 * XXX: undo of teardown on kexec needs this too, as may hotplug
	 */
512
	rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE,
513 514 515 516
		(1UL << interrupt_server_size) - 1 - default_distrib_server, 1);
}


517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
static struct irq_chip xics_pic_direct = {
	.typename = " XICS     ",
	.startup = xics_startup,
	.mask = xics_mask_irq,
	.unmask = xics_unmask_irq,
	.eoi = xics_eoi_direct,
	.set_affinity = xics_set_affinity
};


static struct irq_chip xics_pic_lpar = {
	.typename = " XICS     ",
	.startup = xics_startup,
	.mask = xics_mask_irq,
	.unmask = xics_unmask_irq,
	.eoi = xics_eoi_lpar,
	.set_affinity = xics_set_affinity
};

536 537
/* Points to the irq_chip we're actually using */
static struct irq_chip *xics_irq_chip;
538

539
static int xics_host_match(struct irq_host *h, struct device_node *node)
L
Linus Torvalds 已提交
540
{
541 542 543 544
	/* IBM machines have interrupt parents of various funky types for things
	 * like vdevices, events, etc... The trick we use here is to match
	 * everything here except the legacy 8259 which is compatible "chrp,iic"
	 */
545
	return !of_device_is_compatible(node, "chrp,iic");
546
}
L
Linus Torvalds 已提交
547

548 549
static int xics_host_map(struct irq_host *h, unsigned int virq,
			 irq_hw_number_t hw)
550
{
551
	pr_debug("xics: map virq %d, hwirq 0x%lx\n", virq, hw);
552

553 554 555
	/* Insert the interrupt mapping into the radix tree for fast lookup */
	irq_radix_revmap_insert(xics_host, virq, hw);

556
	get_irq_desc(virq)->status |= IRQ_LEVEL;
557
	set_irq_chip_and_handler(virq, xics_irq_chip, handle_fasteoi_irq);
558 559 560 561 562 563 564 565 566 567 568
	return 0;
}

static int xics_host_xlate(struct irq_host *h, struct device_node *ct,
			   u32 *intspec, unsigned int intsize,
			   irq_hw_number_t *out_hwirq, unsigned int *out_flags)

{
	/* Current xics implementation translates everything
	 * to level. It is not technically right for MSIs but this
	 * is irrelevant at this point. We might get smarter in the future
569
	 */
570 571 572 573 574 575
	*out_hwirq = intspec[0];
	*out_flags = IRQ_TYPE_LEVEL_LOW;

	return 0;
}

576
static struct irq_host_ops xics_host_ops = {
577
	.match = xics_host_match,
578
	.map = xics_host_map,
579 580 581 582 583 584
	.xlate = xics_host_xlate,
};

static void __init xics_init_host(void)
{
	if (firmware_has_feature(FW_FEATURE_LPAR))
585
		xics_irq_chip = &xics_pic_lpar;
586
	else
587 588 589
		xics_irq_chip = &xics_pic_direct;

	xics_host = irq_alloc_host(NULL, IRQ_HOST_MAP_TREE, 0, &xics_host_ops,
590 591 592
				   XICS_IRQ_SPURIOUS);
	BUG_ON(xics_host == NULL);
	irq_set_default_host(xics_host);
593
}
L
Linus Torvalds 已提交
594

595 596
static void __init xics_map_one_cpu(int hw_id, unsigned long addr,
				     unsigned long size)
L
Linus Torvalds 已提交
597
{
598
#ifdef CONFIG_SMP
L
Linus Torvalds 已提交
599 600
	int i;

601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
	/* This may look gross but it's good enough for now, we don't quite
	 * have a hard -> linux processor id matching.
	 */
	for_each_possible_cpu(i) {
		if (!cpu_present(i))
			continue;
		if (hw_id == get_hard_smp_processor_id(i)) {
			xics_per_cpu[i] = ioremap(addr, size);
			return;
		}
	}
#else
	if (hw_id != 0)
		return;
	xics_per_cpu[0] = ioremap(addr, size);
#endif /* CONFIG_SMP */
}
L
Linus Torvalds 已提交
618

619 620 621 622
static void __init xics_init_one_node(struct device_node *np,
				      unsigned int *indx)
{
	unsigned int ilen;
623
	const u32 *ireg;
L
Linus Torvalds 已提交
624

625 626 627 628 629
	/* This code does the theorically broken assumption that the interrupt
	 * server numbers are the same as the hard CPU numbers.
	 * This happens to be the case so far but we are playing with fire...
	 * should be fixed one of these days. -BenH.
	 */
630
	ireg = of_get_property(np, "ibm,interrupt-server-ranges", NULL);
L
Linus Torvalds 已提交
631

632 633 634 635
	/* Do that ever happen ? we'll know soon enough... but even good'old
	 * f80 does have that property ..
	 */
	WARN_ON(ireg == NULL);
L
Linus Torvalds 已提交
636 637 638 639
	if (ireg) {
		/*
		 * set node starting index for this node
		 */
640
		*indx = *ireg;
L
Linus Torvalds 已提交
641
	}
642
	ireg = of_get_property(np, "reg", &ilen);
L
Linus Torvalds 已提交
643 644
	if (!ireg)
		panic("xics_init_IRQ: can't find interrupt reg property");
645

646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
	while (ilen >= (4 * sizeof(u32))) {
		unsigned long addr, size;

		/* XXX Use proper OF parsing code here !!! */
		addr = (unsigned long)*ireg++ << 32;
		ilen -= sizeof(u32);
		addr |= *ireg++;
		ilen -= sizeof(u32);
		size = (unsigned long)*ireg++ << 32;
		ilen -= sizeof(u32);
		size |= *ireg++;
		ilen -= sizeof(u32);
		xics_map_one_cpu(*indx, addr, size);
		(*indx)++;
	}
}

void __init xics_init_IRQ(void)
{
	struct device_node *np;
666
	u32 indx = 0;
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
	int found = 0;

	ppc64_boot_msg(0x20, "XICS Init");

	ibm_get_xive = rtas_token("ibm,get-xive");
	ibm_set_xive = rtas_token("ibm,set-xive");
	ibm_int_on  = rtas_token("ibm,int-on");
	ibm_int_off = rtas_token("ibm,int-off");

	for_each_node_by_type(np, "PowerPC-External-Interrupt-Presentation") {
		found = 1;
		if (firmware_has_feature(FW_FEATURE_LPAR))
			break;
		xics_init_one_node(np, &indx);
	}
	if (found == 0)
		return;

685
	xics_update_irq_servers();
686
	xics_init_host();
L
Linus Torvalds 已提交
687

688 689 690
	if (firmware_has_feature(FW_FEATURE_LPAR))
		ppc_md.get_irq = xics_get_irq_lpar;
	else
691
		ppc_md.get_irq = xics_get_irq_direct;
L
Linus Torvalds 已提交
692

693
	xics_setup_cpu();
L
Linus Torvalds 已提交
694

695
	ppc64_boot_msg(0x21, "XICS Done");
L
Linus Torvalds 已提交
696
}
697

L
Linus Torvalds 已提交
698 699 700 701

#ifdef CONFIG_SMP
void xics_request_IPIs(void)
{
702
	unsigned int ipi;
703
	int rc;
704

705
	ipi = irq_create_mapping(xics_host, XICS_IPI);
706
	BUG_ON(ipi == NO_IRQ);
L
Linus Torvalds 已提交
707

708 709 710 711
	/*
	 * IPIs are marked IRQF_DISABLED as they must run with irqs
	 * disabled
	 */
712
	set_irq_handler(ipi, handle_percpu_irq);
713
	if (firmware_has_feature(FW_FEATURE_LPAR))
714 715
		rc = request_irq(ipi, xics_ipi_action_lpar, IRQF_DISABLED,
				"IPI", NULL);
716
	else
717 718 719
		rc = request_irq(ipi, xics_ipi_action_direct, IRQF_DISABLED,
				"IPI", NULL);
	BUG_ON(rc);
L
Linus Torvalds 已提交
720
}
721
#endif /* CONFIG_SMP */
L
Linus Torvalds 已提交
722

A
Al Viro 已提交
723
void xics_teardown_cpu(void)
724 725 726
{
	int cpu = smp_processor_id();

727
	xics_set_cpu_priority(0);
728

729 730 731 732 733 734 735
	/*
	 * Clear IPI
	 */
	if (firmware_has_feature(FW_FEATURE_LPAR))
		lpar_qirr_info(cpu, 0xff);
	else
		direct_qirr_info(cpu, 0xff);
736 737 738 739 740 741 742 743
}

void xics_kexec_teardown_cpu(int secondary)
{
	unsigned int ipi;
	struct irq_desc *desc;

	xics_teardown_cpu();
744

745
	/*
746
	 * we need to EOI the IPI
747 748 749 750 751
	 *
	 * probably need to check all the other interrupts too
	 * should we be flagging idle loop instead?
	 * or creating some task to be scheduled?
	 */
752 753 754 755 756

	ipi = irq_find_mapping(xics_host, XICS_IPI);
	if (ipi == XICS_IRQ_SPURIOUS)
		return;
	desc = get_irq_desc(ipi);
757
	if (desc->chip && desc->chip->eoi)
758
		desc->chip->eoi(ipi);
759

760
	/*
761 762
	 * Some machines need to have at least one cpu in the GIQ,
	 * so leave the master cpu in the group.
763
	 */
764
	if (secondary)
765
		rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE,
766 767
				   (1UL << interrupt_server_size) - 1 -
				   default_distrib_server, 0);
768 769
}

L
Linus Torvalds 已提交
770 771 772 773 774 775
#ifdef CONFIG_HOTPLUG_CPU

/* Interrupts are disabled. */
void xics_migrate_irqs_away(void)
{
	int status;
776 777
	int cpu = smp_processor_id(), hw_cpu = hard_smp_processor_id();
	unsigned int irq, virq;
L
Linus Torvalds 已提交
778

779 780 781 782
	/* If we used to be the default server, move to the new "boot_cpuid" */
	if (hw_cpu == default_server)
		xics_update_irq_servers();

L
Linus Torvalds 已提交
783
	/* Reject any interrupt that was queued to us... */
784
	xics_set_cpu_priority(0);
L
Linus Torvalds 已提交
785 786

	/* remove ourselves from the global interrupt queue */
787
	status = rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE,
L
Linus Torvalds 已提交
788 789 790 791
		(1UL << interrupt_server_size) - 1 - default_distrib_server, 0);
	WARN_ON(status < 0);

	/* Allow IPIs again... */
792
	xics_set_cpu_priority(DEFAULT_PRIORITY);
L
Linus Torvalds 已提交
793 794

	for_each_irq(virq) {
795
		struct irq_desc *desc;
L
Linus Torvalds 已提交
796 797 798 799
		int xics_status[2];
		unsigned long flags;

		/* We cant set affinity on ISA interrupts */
800
		if (virq < NUM_ISA_INTERRUPTS)
L
Linus Torvalds 已提交
801
			continue;
802 803 804
		if (irq_map[virq].host != xics_host)
			continue;
		irq = (unsigned int)irq_map[virq].hwirq;
L
Linus Torvalds 已提交
805
		/* We need to get IPIs still. */
806
		if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS)
L
Linus Torvalds 已提交
807
			continue;
808
		desc = get_irq_desc(virq);
L
Linus Torvalds 已提交
809 810

		/* We only need to migrate enabled IRQS */
811
		if (desc == NULL || desc->chip == NULL
L
Linus Torvalds 已提交
812
		    || desc->action == NULL
813
		    || desc->chip->set_affinity == NULL)
L
Linus Torvalds 已提交
814 815 816 817 818 819
			continue;

		spin_lock_irqsave(&desc->lock, flags);

		status = rtas_call(ibm_get_xive, 1, 3, xics_status, irq);
		if (status) {
A
Anton Blanchard 已提交
820
			printk(KERN_ERR "migrate_irqs_away: irq=%u "
L
Linus Torvalds 已提交
821 822 823 824 825 826 827 828 829 830
					"ibm,get-xive returns %d\n",
					virq, status);
			goto unlock;
		}

		/*
		 * We only support delivery to all cpus or to one cpu.
		 * The irq has to be migrated only in the single cpu
		 * case.
		 */
831
		if (xics_status[0] != hw_cpu)
L
Linus Torvalds 已提交
832 833
			goto unlock;

A
Anton Blanchard 已提交
834
		printk(KERN_WARNING "IRQ %u affinity broken off cpu %u\n",
L
Linus Torvalds 已提交
835 836 837
		       virq, cpu);

		/* Reset affinity to all cpus */
838
		irq_desc[virq].affinity = CPU_MASK_ALL;
839
		desc->chip->set_affinity(virq, CPU_MASK_ALL);
L
Linus Torvalds 已提交
840 841 842 843 844
unlock:
		spin_unlock_irqrestore(&desc->lock, flags);
	}
}
#endif