setup.c 14.3 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 *  64-bit pSeries and RS/6000 setup code.
L
Linus Torvalds 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 *  Copyright (C) 1995  Linus Torvalds
 *  Adapted from 'alpha' version by Gary Thomas
 *  Modified by Cort Dougan (cort@cs.nmt.edu)
 *  Modified by PPC64 Team, IBM Corp
 *
 * 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.
 */

/*
 * bootup setup stuff..
 */

#undef DEBUG

21
#include <linux/cpu.h>
L
Linus Torvalds 已提交
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/user.h>
#include <linux/a.out.h>
#include <linux/tty.h>
#include <linux/major.h>
#include <linux/interrupt.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/console.h>
#include <linux/pci.h>
39
#include <linux/utsname.h>
L
Linus Torvalds 已提交
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
#include <linux/adb.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/seq_file.h>
#include <linux/root_dev.h>

#include <asm/mmu.h>
#include <asm/processor.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/prom.h>
#include <asm/rtas.h>
#include <asm/pci-bridge.h>
#include <asm/iommu.h>
#include <asm/dma.h>
#include <asm/machdep.h>
#include <asm/irq.h>
#include <asm/time.h>
#include <asm/nvram.h>
60
#include "xics.h"
61
#include <asm/pmc.h>
62
#include <asm/mpic.h>
63
#include <asm/ppc-pci.h>
64 65
#include <asm/i8259.h>
#include <asm/udbg.h>
P
Paul Mackerras 已提交
66
#include <asm/smp.h>
67
#include <asm/firmware.h>
68
#include <asm/eeh.h>
L
Linus Torvalds 已提交
69

70
#include "plpar_wrappers.h"
71
#include "pseries.h"
72

L
Linus Torvalds 已提交
73 74 75 76 77 78 79 80
#ifdef DEBUG
#define DBG(fmt...) udbg_printf(fmt)
#else
#define DBG(fmt...)
#endif

int fwnmi_active;  /* TRUE if an FWNMI handler is present */

81 82
static void pseries_shared_idle_sleep(void);
static void pseries_dedicated_idle_sleep(void);
83

84
static struct device_node *pSeries_mpic_node;
L
Linus Torvalds 已提交
85

86
static void pSeries_show_cpuinfo(struct seq_file *m)
L
Linus Torvalds 已提交
87 88 89 90 91 92
{
	struct device_node *root;
	const char *model = "";

	root = of_find_node_by_path("/");
	if (root)
93
		model = of_get_property(root, "model", NULL);
L
Linus Torvalds 已提交
94 95 96 97 98 99 100 101 102
	seq_printf(m, "machine\t\t: CHRP %s\n", model);
	of_node_put(root);
}

/* Initialize firmware assisted non-maskable interrupts if
 * the firmware supports this feature.
 */
static void __init fwnmi_init(void)
{
103 104
	unsigned long system_reset_addr, machine_check_addr;

L
Linus Torvalds 已提交
105 106 107
	int ibm_nmi_register = rtas_token("ibm,nmi-register");
	if (ibm_nmi_register == RTAS_UNKNOWN_SERVICE)
		return;
108 109 110 111 112 113 114 115

	/* If the kernel's not linked at zero we point the firmware at low
	 * addresses anyway, and use a trampoline to get to the real code. */
	system_reset_addr  = __pa(system_reset_fwnmi) - PHYSICAL_START;
	machine_check_addr = __pa(machine_check_fwnmi) - PHYSICAL_START;

	if (0 == rtas_call(ibm_nmi_register, 2, 1, NULL, system_reset_addr,
				machine_check_addr))
L
Linus Torvalds 已提交
116 117 118
		fwnmi_active = 1;
}

119
void pseries_8259_cascade(unsigned int irq, struct irq_desc *desc)
120
{
O
Olaf Hering 已提交
121
	unsigned int cascade_irq = i8259_irq();
122
	if (cascade_irq != NO_IRQ)
123
		generic_handle_irq(cascade_irq);
124
	desc->chip->eoi(irq);
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
static void __init xics_setup_8259_cascade(void)
{
	struct device_node *np, *old, *found = NULL;
	int cascade, naddr;
	const u32 *addrp;
	unsigned long intack = 0;

	for_each_node_by_type(np, "interrupt-controller")
		if (of_device_is_compatible(np, "chrp,iic")) {
			found = np;
			break;
		}
	if (found == NULL) {
		printk(KERN_DEBUG "xics: no ISA interrupt controller\n");
		return;
	}
	cascade = irq_of_parse_and_map(found, 0);
	if (cascade == NO_IRQ) {
		printk(KERN_ERR "xics: failed to map cascade interrupt");
		return;
	}
	pr_debug("xics: cascade mapped to irq %d\n", cascade);

	for (old = of_node_get(found); old != NULL ; old = np) {
		np = of_get_parent(old);
		of_node_put(old);
		if (np == NULL)
			break;
		if (strcmp(np->name, "pci") != 0)
			continue;
		addrp = of_get_property(np, "8259-interrupt-acknowledge", NULL);
		if (addrp == NULL)
			continue;
		naddr = of_n_addr_cells(np);
		intack = addrp[naddr-1];
		if (naddr > 1)
			intack |= ((unsigned long)addrp[naddr-2]) << 32;
	}
	if (intack)
		printk(KERN_DEBUG "xics: PCI 8259 intack at 0x%016lx\n", intack);
	i8259_init(found, intack);
	of_node_put(found);
	set_irq_chained_handler(cascade, pseries_8259_cascade);
}

172
static void __init pseries_mpic_init_IRQ(void)
L
Linus Torvalds 已提交
173
{
174
	struct device_node *np, *old, *cascade = NULL;
175
        const unsigned int *addrp;
176
	unsigned long intack = 0;
177
	const unsigned int *opprop;
L
Linus Torvalds 已提交
178
	unsigned long openpic_addr = 0;
179 180 181
	unsigned int cascade_irq;
	int naddr, n, i, opplen;
	struct mpic *mpic;
L
Linus Torvalds 已提交
182

183
	np = of_find_node_by_path("/");
184
	naddr = of_n_addr_cells(np);
185
	opprop = of_get_property(np, "platform-open-pic", &opplen);
L
Linus Torvalds 已提交
186
	if (opprop != 0) {
187
		openpic_addr = of_read_number(opprop, naddr);
L
Linus Torvalds 已提交
188 189
		printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr);
	}
190
	of_node_put(np);
L
Linus Torvalds 已提交
191 192 193 194

	BUG_ON(openpic_addr == 0);

	/* Setup the openpic driver */
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
	mpic = mpic_alloc(pSeries_mpic_node, openpic_addr,
			  MPIC_PRIMARY,
			  16, 250, /* isu size, irq count */
			  " MPIC     ");
	BUG_ON(mpic == NULL);

	/* Add ISUs */
	opplen /= sizeof(u32);
	for (n = 0, i = naddr; i < opplen; i += naddr, n++) {
		unsigned long isuaddr = of_read_number(opprop + i, naddr);
		mpic_assign_isu(mpic, n, isuaddr);
	}

	/* All ISUs are setup, complete initialization */
	mpic_init(mpic);

	/* Look for cascade */
	for_each_node_by_type(np, "interrupt-controller")
213
		if (of_device_is_compatible(np, "chrp,iic")) {
214 215 216 217 218 219 220
			cascade = np;
			break;
		}
	if (cascade == NULL)
		return;

	cascade_irq = irq_of_parse_and_map(cascade, 0);
221
	if (cascade_irq == NO_IRQ) {
222
		printk(KERN_ERR "mpic: failed to map cascade interrupt");
223 224 225 226 227 228 229 230 231 232 233
		return;
	}

	/* Check ACK type */
	for (old = of_node_get(cascade); old != NULL ; old = np) {
		np = of_get_parent(old);
		of_node_put(old);
		if (np == NULL)
			break;
		if (strcmp(np->name, "pci") != 0)
			continue;
234
		addrp = of_get_property(np, "8259-interrupt-acknowledge",
235 236 237
					    NULL);
		if (addrp == NULL)
			continue;
238
		naddr = of_n_addr_cells(np);
239 240 241 242 243 244 245 246 247 248
		intack = addrp[naddr-1];
		if (naddr > 1)
			intack |= ((unsigned long)addrp[naddr-2]) << 32;
	}
	if (intack)
		printk(KERN_DEBUG "mpic: PCI 8259 intack at 0x%016lx\n",
		       intack);
	i8259_init(cascade, intack);
	of_node_put(cascade);
	set_irq_chained_handler(cascade_irq, pseries_8259_cascade);
L
Linus Torvalds 已提交
249 250
}

251 252 253 254 255 256
static void __init pseries_xics_init_IRQ(void)
{
	xics_init_IRQ();
	xics_setup_8259_cascade();
}

257 258 259 260 261 262 263 264 265 266
static void pseries_lpar_enable_pmcs(void)
{
	unsigned long set, reset;

	set = 1UL << 63;
	reset = 0;
	plpar_hcall_norets(H_PERFMON, set, reset);

	/* instruct hypervisor to maintain PMCs */
	if (firmware_has_feature(FW_FEATURE_SPLPAR))
267
		get_lppaca()->pmcregs_in_use = 1;
268 269
}

270 271 272
static void __init pseries_discover_pic(void)
{
	struct device_node *np;
273
	const char *typep;
274 275 276

	for (np = NULL; (np = of_find_node_by_name(np,
						   "interrupt-controller"));) {
277
		typep = of_get_property(np, "compatible", NULL);
278 279 280 281
		if (strstr(typep, "open-pic")) {
			pSeries_mpic_node = of_node_get(np);
			ppc_md.init_IRQ       = pseries_mpic_init_IRQ;
			ppc_md.get_irq        = mpic_get_irq;
282
			setup_kexec_cpu_down_mpic();
283 284 285
			smp_init_pseries_mpic();
			return;
		} else if (strstr(typep, "ppc-xicp")) {
286
			ppc_md.init_IRQ       = pseries_xics_init_IRQ;
287
			setup_kexec_cpu_down_xics();
288 289 290 291 292 293 294 295 296 297 298 299 300
			smp_init_pseries_xics();
			return;
		}
	}
	printk(KERN_ERR "pSeries_discover_pic: failed to recognize"
	       " interrupt-controller\n");
}

static void __init pSeries_setup_arch(void)
{
	/* Discover PIC type and setup ppc_md accordingly */
	pseries_discover_pic();

L
Linus Torvalds 已提交
301 302 303 304 305 306 307 308 309 310 311 312
	/* openpic global configuration register (64-bit format). */
	/* openpic Interrupt Source Unit pointer (64-bit format). */
	/* python0 facility area (mmio) (64-bit format) REAL address. */

	/* init to some ~sane value until calibrate_delay() runs */
	loops_per_jiffy = 50000000;

	fwnmi_init();

	/* Find and initialize PCI host bridges */
	init_pci_config_tokens();
	find_and_init_phbs();
313
	eeh_init();
L
Linus Torvalds 已提交
314 315 316

	pSeries_nvram_init();

317
	/* Choose an idle loop */
318
	if (firmware_has_feature(FW_FEATURE_SPLPAR)) {
319
		vpa_init(boot_cpuid);
320
		if (get_lppaca()->shared_proc) {
321
			printk(KERN_DEBUG "Using shared processor idle loop\n");
322
			ppc_md.power_save = pseries_shared_idle_sleep;
323
		} else {
324
			printk(KERN_DEBUG "Using dedicated idle loop\n");
325
			ppc_md.power_save = pseries_dedicated_idle_sleep;
326 327
		}
	} else {
328
		printk(KERN_DEBUG "Using default idle loop\n");
329
	}
330

331
	if (firmware_has_feature(FW_FEATURE_LPAR))
332 333 334
		ppc_md.enable_pmcs = pseries_lpar_enable_pmcs;
	else
		ppc_md.enable_pmcs = power4_enable_pmcs;
L
Linus Torvalds 已提交
335 336 337 338 339 340
}

static int __init pSeries_init_panel(void)
{
	/* Manually leave the kernel version on the panel. */
	ppc_md.progress("Linux ppc64\n", 0);
341
	ppc_md.progress(init_utsname()->version, 0);
L
Linus Torvalds 已提交
342 343 344 345 346

	return 0;
}
arch_initcall(pSeries_init_panel);

347 348
static int pseries_set_dabr(unsigned long dabr)
{
349
	return plpar_hcall_norets(H_SET_DABR, dabr);
350 351
}

352 353 354 355 356 357
static int pseries_set_xdabr(unsigned long dabr)
{
	/* We want to catch accesses from kernel and userspace */
	return plpar_hcall_norets(H_SET_XDABR, dabr,
			H_DABRX_KERNEL | H_DABRX_USER);
}
L
Linus Torvalds 已提交
358 359 360 361 362 363 364 365

/*
 * Early initialization.  Relocation is on but do not reference unbolted pages
 */
static void __init pSeries_init_early(void)
{
	DBG(" -> pSeries_init_early()\n");

366
	if (firmware_has_feature(FW_FEATURE_LPAR))
L
Linus Torvalds 已提交
367 368
		find_udbg_vterm();

369
	if (firmware_has_feature(FW_FEATURE_DABR))
370
		ppc_md.set_dabr = pseries_set_dabr;
371 372
	else if (firmware_has_feature(FW_FEATURE_XDABR))
		ppc_md.set_dabr = pseries_set_xdabr;
L
Linus Torvalds 已提交
373 374 375 376 377 378 379 380 381 382

	iommu_init_early_pSeries();

	DBG(" <- pSeries_init_early()\n");
}

/*
 * Called very early, MMU is off, device-tree isn't unflattened
 */

383 384 385
static int __init pSeries_probe_hypertas(unsigned long node,
					 const char *uname, int depth,
					 void *data)
L
Linus Torvalds 已提交
386
{
387 388 389
	const char *hypertas;
	unsigned long len;

390 391
	if (depth != 1 ||
	    (strcmp(uname, "rtas") != 0 && strcmp(uname, "rtas@0") != 0))
392 393 394 395 396
		return 0;

	hypertas = of_get_flat_dt_prop(node, "ibm,hypertas-functions", &len);
	if (!hypertas)
		return 1;
397

398 399
	powerpc_firmware_features |= FW_FEATURE_LPAR;
	fw_feature_init(hypertas, len);
400

401
	return 1;
402 403 404 405
}

static int __init pSeries_probe(void)
{
406
	unsigned long root = of_get_flat_dt_root();
407 408
 	char *dtype = of_get_flat_dt_prop(root, "device_type", NULL);

409 410 411
 	if (dtype == NULL)
 		return 0;
 	if (strcmp(dtype, "chrp"))
L
Linus Torvalds 已提交
412 413
		return 0;

414 415 416 417 418 419 420
	/* Cell blades firmware claims to be chrp while it's not. Until this
	 * is fixed, we need to avoid those here.
	 */
	if (of_flat_dt_is_compatible(root, "IBM,CPBW-1.0") ||
	    of_flat_dt_is_compatible(root, "IBM,CBEA"))
		return 0;

421
	DBG("pSeries detected, looking for LPAR capability...\n");
L
Linus Torvalds 已提交
422

423 424 425
	/* Now try to figure out if we are running on LPAR */
	of_scan_flat_dt(pSeries_probe_hypertas, NULL);

426 427 428 429 430
	if (firmware_has_feature(FW_FEATURE_LPAR))
		hpte_init_lpar();
	else
		hpte_init_native();

431 432
	DBG("Machine is%s LPAR !\n",
	    (powerpc_firmware_features & FW_FEATURE_LPAR) ? "" : " not");
433

L
Linus Torvalds 已提交
434 435 436
	return 1;
}

437

438 439
DECLARE_PER_CPU(unsigned long, smt_snooze_delay);

440
static void pseries_dedicated_idle_sleep(void)
P
Paul Mackerras 已提交
441
{ 
442
	unsigned int cpu = smp_processor_id();
443
	unsigned long start_snooze;
444
	unsigned long in_purr, out_purr;
445

446 447 448 449 450
	/*
	 * Indicate to the HV that we are idle. Now would be
	 * a good time to find other work to dispatch.
	 */
	get_lppaca()->idle = 1;
451
	get_lppaca()->donate_dedicated_cpu = 1;
452
	in_purr = mfspr(SPRN_PURR);
453

454 455 456 457 458
	/*
	 * We come in with interrupts disabled, and need_resched()
	 * has been checked recently.  If we should poll for a little
	 * while, do so.
	 */
459
	if (__get_cpu_var(smt_snooze_delay)) {
460
		start_snooze = get_tb() +
461
			__get_cpu_var(smt_snooze_delay) * tb_ticks_per_usec;
462 463
		local_irq_enable();
		set_thread_flag(TIF_POLLING_NRFLAG);
464

465 466 467 468 469 470 471 472 473 474 475 476 477 478
		while (get_tb() < start_snooze) {
			if (need_resched() || cpu_is_offline(cpu))
				goto out;
			ppc64_runlatch_off();
			HMT_low();
			HMT_very_low();
		}

		HMT_medium();
		clear_thread_flag(TIF_POLLING_NRFLAG);
		smp_mb();
		local_irq_disable();
		if (need_resched() || cpu_is_offline(cpu))
			goto out;
479
	}
480

481
	cede_processor();
482 483 484

out:
	HMT_medium();
485 486
	out_purr = mfspr(SPRN_PURR);
	get_lppaca()->wait_state_cycles += out_purr - in_purr;
487
	get_lppaca()->donate_dedicated_cpu = 0;
488
	get_lppaca()->idle = 0;
489 490
}

491
static void pseries_shared_idle_sleep(void)
492
{
493 494 495 496 497
	/*
	 * Indicate to the HV that we are idle. Now would be
	 * a good time to find other work to dispatch.
	 */
	get_lppaca()->idle = 1;
498

499 500 501 502 503 504 505 506
	/*
	 * Yield the processor to the hypervisor.  We return if
	 * an external interrupt occurs (which are driven prior
	 * to returning here) or if a prod occurs from another
	 * processor. When returning here, external interrupts
	 * are enabled.
	 */
	cede_processor();
507

508
	get_lppaca()->idle = 0;
509 510
}

511 512
static int pSeries_pci_probe_mode(struct pci_bus *bus)
{
513
	if (firmware_has_feature(FW_FEATURE_LPAR))
514 515 516 517
		return PCI_PROBE_DEVTREE;
	return PCI_PROBE_NORMAL;
}

518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
/**
 * pSeries_power_off - tell firmware about how to power off the system.
 *
 * This function calls either the power-off rtas token in normal cases
 * or the ibm,power-off-ups token (if present & requested) in case of
 * a power failure. If power-off token is used, power on will only be
 * possible with power button press. If ibm,power-off-ups token is used
 * it will allow auto poweron after power is restored.
 */
void pSeries_power_off(void)
{
	int rc;
	int rtas_poweroff_ups_token = rtas_token("ibm,power-off-ups");

	if (rtas_flash_term_hook)
		rtas_flash_term_hook(SYS_POWER_OFF);

	if (rtas_poweron_auto == 0 ||
		rtas_poweroff_ups_token == RTAS_UNKNOWN_SERVICE) {
		rc = rtas_call(rtas_token("power-off"), 2, 1, NULL, -1, -1);
		printk(KERN_INFO "RTAS power-off returned %d\n", rc);
	} else {
		rc = rtas_call(rtas_poweroff_ups_token, 0, 1, NULL);
		printk(KERN_INFO "RTAS ibm,power-off-ups returned %d\n", rc);
	}
	for (;;);
}

546 547 548 549
#ifndef CONFIG_PCI
void pSeries_final_fixup(void) { }
#endif

550 551
define_machine(pseries) {
	.name			= "pSeries",
L
Linus Torvalds 已提交
552 553 554
	.probe			= pSeries_probe,
	.setup_arch		= pSeries_setup_arch,
	.init_early		= pSeries_init_early,
555
	.show_cpuinfo		= pSeries_show_cpuinfo,
L
Linus Torvalds 已提交
556 557
	.log_error		= pSeries_log_error,
	.pcibios_fixup		= pSeries_final_fixup,
558
	.pci_probe_mode		= pSeries_pci_probe_mode,
559
	.restart		= rtas_restart,
560
	.power_off		= pSeries_power_off,
561
	.halt			= rtas_halt,
562
	.panic			= rtas_os_term,
563 564 565
	.get_boot_time		= rtas_get_boot_time,
	.get_rtc_time		= rtas_get_rtc_time,
	.set_rtc_time		= rtas_set_rtc_time,
566
	.calibrate_decr		= generic_calibrate_decr,
567
	.progress		= rtas_progress,
L
Linus Torvalds 已提交
568 569 570
	.system_reset_exception = pSeries_system_reset_exception,
	.machine_check_exception = pSeries_machine_check_exception,
};