| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  *  linux/arch/ppc/kernel/setup.c | 
 | 3 |  * | 
 | 4 |  *  Copyright (C) 1995  Linus Torvalds | 
 | 5 |  *  Adapted from 'alpha' version by Gary Thomas | 
 | 6 |  *  Modified by Cort Dougan (cort@cs.nmt.edu) | 
 | 7 |  *  Modified by PPC64 Team, IBM Corp | 
 | 8 |  * | 
 | 9 |  * This program is free software; you can redistribute it and/or | 
 | 10 |  * modify it under the terms of the GNU General Public License | 
 | 11 |  * as published by the Free Software Foundation; either version | 
 | 12 |  * 2 of the License, or (at your option) any later version. | 
 | 13 |  */ | 
 | 14 |  | 
 | 15 | /* | 
 | 16 |  * bootup setup stuff.. | 
 | 17 |  */ | 
 | 18 |  | 
 | 19 | #undef DEBUG | 
 | 20 |  | 
 | 21 | #include <linux/config.h> | 
| Michael Ellerman | 62d60e9 | 2005-07-07 17:56:30 -0700 | [diff] [blame] | 22 | #include <linux/cpu.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 23 | #include <linux/errno.h> | 
 | 24 | #include <linux/sched.h> | 
 | 25 | #include <linux/kernel.h> | 
 | 26 | #include <linux/mm.h> | 
 | 27 | #include <linux/stddef.h> | 
 | 28 | #include <linux/unistd.h> | 
 | 29 | #include <linux/slab.h> | 
 | 30 | #include <linux/user.h> | 
 | 31 | #include <linux/a.out.h> | 
 | 32 | #include <linux/tty.h> | 
 | 33 | #include <linux/major.h> | 
 | 34 | #include <linux/interrupt.h> | 
 | 35 | #include <linux/reboot.h> | 
 | 36 | #include <linux/init.h> | 
 | 37 | #include <linux/ioport.h> | 
 | 38 | #include <linux/console.h> | 
 | 39 | #include <linux/pci.h> | 
| Olaf Hering | cebb2b1 | 2005-07-10 19:35:15 +0000 | [diff] [blame] | 40 | #include <linux/utsname.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 41 | #include <linux/adb.h> | 
 | 42 | #include <linux/module.h> | 
 | 43 | #include <linux/delay.h> | 
 | 44 | #include <linux/irq.h> | 
 | 45 | #include <linux/seq_file.h> | 
 | 46 | #include <linux/root_dev.h> | 
 | 47 |  | 
 | 48 | #include <asm/mmu.h> | 
 | 49 | #include <asm/processor.h> | 
 | 50 | #include <asm/io.h> | 
 | 51 | #include <asm/pgtable.h> | 
 | 52 | #include <asm/prom.h> | 
 | 53 | #include <asm/rtas.h> | 
 | 54 | #include <asm/pci-bridge.h> | 
 | 55 | #include <asm/iommu.h> | 
 | 56 | #include <asm/dma.h> | 
 | 57 | #include <asm/machdep.h> | 
 | 58 | #include <asm/irq.h> | 
 | 59 | #include <asm/time.h> | 
 | 60 | #include <asm/nvram.h> | 
 | 61 | #include <asm/plpar_wrappers.h> | 
 | 62 | #include <asm/xics.h> | 
| Stephen Rothwell | 1ababe1 | 2005-08-03 14:35:25 +1000 | [diff] [blame] | 63 | #include <asm/firmware.h> | 
| Michael Ellerman | 180a336 | 2005-08-09 11:13:36 +1000 | [diff] [blame] | 64 | #include <asm/pmc.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 65 |  | 
 | 66 | #include "i8259.h" | 
 | 67 | #include "mpic.h" | 
 | 68 | #include "pci.h" | 
 | 69 |  | 
 | 70 | #ifdef DEBUG | 
 | 71 | #define DBG(fmt...) udbg_printf(fmt) | 
 | 72 | #else | 
 | 73 | #define DBG(fmt...) | 
 | 74 | #endif | 
 | 75 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 76 | extern void find_udbg_vterm(void); | 
 | 77 | extern void system_reset_fwnmi(void);	/* from head.S */ | 
 | 78 | extern void machine_check_fwnmi(void);	/* from head.S */ | 
 | 79 | extern void generic_find_legacy_serial_ports(u64 *physport, | 
 | 80 | 		unsigned int *default_speed); | 
 | 81 |  | 
 | 82 | int fwnmi_active;  /* TRUE if an FWNMI handler is present */ | 
 | 83 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 84 | extern void pSeries_system_reset_exception(struct pt_regs *regs); | 
 | 85 | extern int pSeries_machine_check_exception(struct pt_regs *regs); | 
 | 86 |  | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 87 | static int pseries_shared_idle(void); | 
 | 88 | static int pseries_dedicated_idle(void); | 
| Michael Ellerman | 62d60e9 | 2005-07-07 17:56:30 -0700 | [diff] [blame] | 89 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 90 | static volatile void __iomem * chrp_int_ack_special; | 
 | 91 | struct mpic *pSeries_mpic; | 
 | 92 |  | 
 | 93 | void pSeries_get_cpuinfo(struct seq_file *m) | 
 | 94 | { | 
 | 95 | 	struct device_node *root; | 
 | 96 | 	const char *model = ""; | 
 | 97 |  | 
 | 98 | 	root = of_find_node_by_path("/"); | 
 | 99 | 	if (root) | 
 | 100 | 		model = get_property(root, "model", NULL); | 
 | 101 | 	seq_printf(m, "machine\t\t: CHRP %s\n", model); | 
 | 102 | 	of_node_put(root); | 
 | 103 | } | 
 | 104 |  | 
 | 105 | /* Initialize firmware assisted non-maskable interrupts if | 
 | 106 |  * the firmware supports this feature. | 
 | 107 |  * | 
 | 108 |  */ | 
 | 109 | static void __init fwnmi_init(void) | 
 | 110 | { | 
 | 111 | 	int ret; | 
 | 112 | 	int ibm_nmi_register = rtas_token("ibm,nmi-register"); | 
 | 113 | 	if (ibm_nmi_register == RTAS_UNKNOWN_SERVICE) | 
 | 114 | 		return; | 
 | 115 | 	ret = rtas_call(ibm_nmi_register, 2, 1, NULL, | 
 | 116 | 			__pa((unsigned long)system_reset_fwnmi), | 
 | 117 | 			__pa((unsigned long)machine_check_fwnmi)); | 
 | 118 | 	if (ret == 0) | 
 | 119 | 		fwnmi_active = 1; | 
 | 120 | } | 
 | 121 |  | 
 | 122 | static int pSeries_irq_cascade(struct pt_regs *regs, void *data) | 
 | 123 | { | 
 | 124 | 	if (chrp_int_ack_special) | 
 | 125 | 		return readb(chrp_int_ack_special); | 
 | 126 | 	else | 
 | 127 | 		return i8259_irq(smp_processor_id()); | 
 | 128 | } | 
 | 129 |  | 
 | 130 | static void __init pSeries_init_mpic(void) | 
 | 131 | { | 
 | 132 |         unsigned int *addrp; | 
 | 133 | 	struct device_node *np; | 
 | 134 |         int i; | 
 | 135 |  | 
 | 136 | 	/* All ISUs are setup, complete initialization */ | 
 | 137 | 	mpic_init(pSeries_mpic); | 
 | 138 |  | 
 | 139 | 	/* Check what kind of cascade ACK we have */ | 
 | 140 |         if (!(np = of_find_node_by_name(NULL, "pci")) | 
 | 141 |             || !(addrp = (unsigned int *) | 
 | 142 |                  get_property(np, "8259-interrupt-acknowledge", NULL))) | 
 | 143 |                 printk(KERN_ERR "Cannot find pci to get ack address\n"); | 
 | 144 |         else | 
 | 145 | 		chrp_int_ack_special = ioremap(addrp[prom_n_addr_cells(np)-1], 1); | 
 | 146 | 	of_node_put(np); | 
 | 147 |  | 
 | 148 | 	/* Setup the legacy interrupts & controller */ | 
 | 149 |         for (i = 0; i < NUM_ISA_INTERRUPTS; i++) | 
 | 150 |                 irq_desc[i].handler = &i8259_pic; | 
 | 151 | 	i8259_init(0); | 
 | 152 |  | 
 | 153 | 	/* Hook cascade to mpic */ | 
 | 154 | 	mpic_setup_cascade(NUM_ISA_INTERRUPTS, pSeries_irq_cascade, NULL); | 
 | 155 | } | 
 | 156 |  | 
 | 157 | static void __init pSeries_setup_mpic(void) | 
 | 158 | { | 
 | 159 | 	unsigned int *opprop; | 
 | 160 | 	unsigned long openpic_addr = 0; | 
 | 161 |         unsigned char senses[NR_IRQS - NUM_ISA_INTERRUPTS]; | 
 | 162 |         struct device_node *root; | 
 | 163 | 	int irq_count; | 
 | 164 |  | 
 | 165 | 	/* Find the Open PIC if present */ | 
 | 166 | 	root = of_find_node_by_path("/"); | 
 | 167 | 	opprop = (unsigned int *) get_property(root, "platform-open-pic", NULL); | 
 | 168 | 	if (opprop != 0) { | 
 | 169 | 		int n = prom_n_addr_cells(root); | 
 | 170 |  | 
 | 171 | 		for (openpic_addr = 0; n > 0; --n) | 
 | 172 | 			openpic_addr = (openpic_addr << 32) + *opprop++; | 
 | 173 | 		printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr); | 
 | 174 | 	} | 
 | 175 | 	of_node_put(root); | 
 | 176 |  | 
 | 177 | 	BUG_ON(openpic_addr == 0); | 
 | 178 |  | 
 | 179 | 	/* Get the sense values from OF */ | 
 | 180 | 	prom_get_irq_senses(senses, NUM_ISA_INTERRUPTS, NR_IRQS); | 
 | 181 | 	 | 
 | 182 | 	/* Setup the openpic driver */ | 
 | 183 | 	irq_count = NR_IRQS - NUM_ISA_INTERRUPTS - 4; /* leave room for IPIs */ | 
 | 184 | 	pSeries_mpic = mpic_alloc(openpic_addr, MPIC_PRIMARY, | 
 | 185 | 				  16, 16, irq_count, /* isu size, irq offset, irq count */  | 
 | 186 | 				  NR_IRQS - 4, /* ipi offset */ | 
 | 187 | 				  senses, irq_count, /* sense & sense size */ | 
 | 188 | 				  " MPIC     "); | 
 | 189 | } | 
 | 190 |  | 
| Michael Ellerman | 180a336 | 2005-08-09 11:13:36 +1000 | [diff] [blame] | 191 | static void pseries_lpar_enable_pmcs(void) | 
 | 192 | { | 
 | 193 | 	unsigned long set, reset; | 
 | 194 |  | 
 | 195 | 	power4_enable_pmcs(); | 
 | 196 |  | 
 | 197 | 	set = 1UL << 63; | 
 | 198 | 	reset = 0; | 
 | 199 | 	plpar_hcall_norets(H_PERFMON, set, reset); | 
 | 200 |  | 
 | 201 | 	/* instruct hypervisor to maintain PMCs */ | 
 | 202 | 	if (firmware_has_feature(FW_FEATURE_SPLPAR)) | 
 | 203 | 		get_paca()->lppaca.pmcregs_in_use = 1; | 
 | 204 | } | 
 | 205 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 206 | static void __init pSeries_setup_arch(void) | 
 | 207 | { | 
 | 208 | 	/* Fixup ppc_md depending on the type of interrupt controller */ | 
 | 209 | 	if (ppc64_interrupt_controller == IC_OPEN_PIC) { | 
| R Sharada | fce0d57 | 2005-06-25 14:58:10 -0700 | [diff] [blame] | 210 | 		ppc_md.init_IRQ       = pSeries_init_mpic; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 211 | 		ppc_md.get_irq        = mpic_get_irq; | 
| R Sharada | fce0d57 | 2005-06-25 14:58:10 -0700 | [diff] [blame] | 212 | 	 	ppc_md.cpu_irq_down   = mpic_teardown_this_cpu; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 213 | 		/* Allocate the mpic now, so that find_and_init_phbs() can | 
 | 214 | 		 * fill the ISUs */ | 
 | 215 | 		pSeries_setup_mpic(); | 
 | 216 | 	} else { | 
 | 217 | 		ppc_md.init_IRQ       = xics_init_IRQ; | 
 | 218 | 		ppc_md.get_irq        = xics_get_irq; | 
| R Sharada | fce0d57 | 2005-06-25 14:58:10 -0700 | [diff] [blame] | 219 | 		ppc_md.cpu_irq_down   = xics_teardown_cpu; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 220 | 	} | 
 | 221 |  | 
 | 222 | #ifdef CONFIG_SMP | 
 | 223 | 	smp_init_pSeries(); | 
 | 224 | #endif | 
 | 225 | 	/* openpic global configuration register (64-bit format). */ | 
 | 226 | 	/* openpic Interrupt Source Unit pointer (64-bit format). */ | 
 | 227 | 	/* python0 facility area (mmio) (64-bit format) REAL address. */ | 
 | 228 |  | 
 | 229 | 	/* init to some ~sane value until calibrate_delay() runs */ | 
 | 230 | 	loops_per_jiffy = 50000000; | 
 | 231 |  | 
 | 232 | 	if (ROOT_DEV == 0) { | 
 | 233 | 		printk("No ramdisk, default root is /dev/sda2\n"); | 
 | 234 | 		ROOT_DEV = Root_SDA2; | 
 | 235 | 	} | 
 | 236 |  | 
 | 237 | 	fwnmi_init(); | 
 | 238 |  | 
 | 239 | 	/* Find and initialize PCI host bridges */ | 
 | 240 | 	init_pci_config_tokens(); | 
 | 241 | 	eeh_init(); | 
 | 242 | 	find_and_init_phbs(); | 
 | 243 |  | 
 | 244 | #ifdef CONFIG_DUMMY_CONSOLE | 
 | 245 | 	conswitchp = &dummy_con; | 
 | 246 | #endif | 
 | 247 |  | 
 | 248 | 	pSeries_nvram_init(); | 
 | 249 |  | 
| Michael Ellerman | 62d60e9 | 2005-07-07 17:56:30 -0700 | [diff] [blame] | 250 | 	/* Choose an idle loop */ | 
| Stephen Rothwell | 1ababe1 | 2005-08-03 14:35:25 +1000 | [diff] [blame] | 251 | 	if (firmware_has_feature(FW_FEATURE_SPLPAR)) { | 
| Stephen Rothwell | 8d15a3e | 2005-08-03 14:40:16 +1000 | [diff] [blame] | 252 | 		vpa_init(boot_cpuid); | 
| Michael Ellerman | 62d60e9 | 2005-07-07 17:56:30 -0700 | [diff] [blame] | 253 | 		if (get_paca()->lppaca.shared_proc) { | 
 | 254 | 			printk(KERN_INFO "Using shared processor idle loop\n"); | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 255 | 			ppc_md.idle_loop = pseries_shared_idle; | 
| Michael Ellerman | 62d60e9 | 2005-07-07 17:56:30 -0700 | [diff] [blame] | 256 | 		} else { | 
 | 257 | 			printk(KERN_INFO "Using dedicated idle loop\n"); | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 258 | 			ppc_md.idle_loop = pseries_dedicated_idle; | 
| Michael Ellerman | 62d60e9 | 2005-07-07 17:56:30 -0700 | [diff] [blame] | 259 | 		} | 
 | 260 | 	} else { | 
 | 261 | 		printk(KERN_INFO "Using default idle loop\n"); | 
 | 262 | 		ppc_md.idle_loop = default_idle; | 
 | 263 | 	} | 
| Michael Ellerman | 180a336 | 2005-08-09 11:13:36 +1000 | [diff] [blame] | 264 |  | 
 | 265 | 	if (systemcfg->platform & PLATFORM_LPAR) | 
 | 266 | 		ppc_md.enable_pmcs = pseries_lpar_enable_pmcs; | 
 | 267 | 	else | 
 | 268 | 		ppc_md.enable_pmcs = power4_enable_pmcs; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 269 | } | 
 | 270 |  | 
 | 271 | static int __init pSeries_init_panel(void) | 
 | 272 | { | 
 | 273 | 	/* Manually leave the kernel version on the panel. */ | 
 | 274 | 	ppc_md.progress("Linux ppc64\n", 0); | 
| Olaf Hering | cebb2b1 | 2005-07-10 19:35:15 +0000 | [diff] [blame] | 275 | 	ppc_md.progress(system_utsname.version, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 276 |  | 
 | 277 | 	return 0; | 
 | 278 | } | 
 | 279 | arch_initcall(pSeries_init_panel); | 
 | 280 |  | 
 | 281 |  | 
| Stephen Rothwell | 7a6af5e | 2005-08-03 14:32:30 +1000 | [diff] [blame] | 282 | /* Build up the ppc64_firmware_features bitmask field | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 283 |  * using contents of device-tree/ibm,hypertas-functions. | 
 | 284 |  * Ultimately this functionality may be moved into prom.c prom_init(). | 
 | 285 |  */ | 
| Stephen Rothwell | aed3135 | 2005-08-03 14:43:21 +1000 | [diff] [blame] | 286 | static void __init fw_feature_init(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 287 | { | 
 | 288 | 	struct device_node * dn; | 
 | 289 | 	char * hypertas; | 
 | 290 | 	unsigned int len; | 
 | 291 |  | 
 | 292 | 	DBG(" -> fw_feature_init()\n"); | 
 | 293 |  | 
| Stephen Rothwell | 7a6af5e | 2005-08-03 14:32:30 +1000 | [diff] [blame] | 294 | 	ppc64_firmware_features = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 295 | 	dn = of_find_node_by_path("/rtas"); | 
 | 296 | 	if (dn == NULL) { | 
 | 297 | 		printk(KERN_ERR "WARNING ! Cannot find RTAS in device-tree !\n"); | 
 | 298 | 		goto no_rtas; | 
 | 299 | 	} | 
 | 300 |  | 
 | 301 | 	hypertas = get_property(dn, "ibm,hypertas-functions", &len); | 
 | 302 | 	if (hypertas) { | 
 | 303 | 		while (len > 0){ | 
 | 304 | 			int i, hypertas_len; | 
 | 305 | 			/* check value against table of strings */ | 
 | 306 | 			for(i=0; i < FIRMWARE_MAX_FEATURES ;i++) { | 
 | 307 | 				if ((firmware_features_table[i].name) && | 
 | 308 | 				    (strcmp(firmware_features_table[i].name,hypertas))==0) { | 
 | 309 | 					/* we have a match */ | 
| Stephen Rothwell | 7a6af5e | 2005-08-03 14:32:30 +1000 | [diff] [blame] | 310 | 					ppc64_firmware_features |=  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 311 | 						(firmware_features_table[i].val); | 
 | 312 | 					break; | 
 | 313 | 				}  | 
 | 314 | 			} | 
 | 315 | 			hypertas_len = strlen(hypertas); | 
 | 316 | 			len -= hypertas_len +1; | 
 | 317 | 			hypertas+= hypertas_len +1; | 
 | 318 | 		} | 
 | 319 | 	} | 
 | 320 |  | 
 | 321 | 	of_node_put(dn); | 
 | 322 |  no_rtas: | 
 | 323 | 	printk(KERN_INFO "firmware_features = 0x%lx\n",  | 
| Stephen Rothwell | 7a6af5e | 2005-08-03 14:32:30 +1000 | [diff] [blame] | 324 | 	       ppc64_firmware_features); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 325 |  | 
 | 326 | 	DBG(" <- fw_feature_init()\n"); | 
 | 327 | } | 
 | 328 |  | 
 | 329 |  | 
 | 330 | static  void __init pSeries_discover_pic(void) | 
 | 331 | { | 
 | 332 | 	struct device_node *np; | 
 | 333 | 	char *typep; | 
 | 334 |  | 
 | 335 | 	/* | 
 | 336 | 	 * Setup interrupt mapping options that are needed for finish_device_tree | 
 | 337 | 	 * to properly parse the OF interrupt tree & do the virtual irq mapping | 
 | 338 | 	 */ | 
 | 339 | 	__irq_offset_value = NUM_ISA_INTERRUPTS; | 
 | 340 | 	ppc64_interrupt_controller = IC_INVALID; | 
 | 341 | 	for (np = NULL; (np = of_find_node_by_name(np, "interrupt-controller"));) { | 
 | 342 | 		typep = (char *)get_property(np, "compatible", NULL); | 
 | 343 | 		if (strstr(typep, "open-pic")) | 
 | 344 | 			ppc64_interrupt_controller = IC_OPEN_PIC; | 
 | 345 | 		else if (strstr(typep, "ppc-xicp")) | 
 | 346 | 			ppc64_interrupt_controller = IC_PPC_XIC; | 
 | 347 | 		else | 
 | 348 | 			printk("pSeries_discover_pic: failed to recognize" | 
 | 349 | 			       " interrupt-controller\n"); | 
 | 350 | 		break; | 
 | 351 | 	} | 
 | 352 | } | 
 | 353 |  | 
 | 354 | static void pSeries_mach_cpu_die(void) | 
 | 355 | { | 
 | 356 | 	local_irq_disable(); | 
 | 357 | 	idle_task_exit(); | 
 | 358 | 	/* Some hardware requires clearing the CPPR, while other hardware does not | 
 | 359 | 	 * it is safe either way | 
 | 360 | 	 */ | 
 | 361 | 	pSeriesLP_cppr_info(0, 0); | 
 | 362 | 	rtas_stop_self(); | 
 | 363 | 	/* Should never get here... */ | 
 | 364 | 	BUG(); | 
 | 365 | 	for(;;); | 
 | 366 | } | 
 | 367 |  | 
 | 368 |  | 
 | 369 | /* | 
 | 370 |  * Early initialization.  Relocation is on but do not reference unbolted pages | 
 | 371 |  */ | 
 | 372 | static void __init pSeries_init_early(void) | 
 | 373 | { | 
 | 374 | 	void *comport; | 
 | 375 | 	int iommu_off = 0; | 
 | 376 | 	unsigned int default_speed; | 
 | 377 | 	u64 physport; | 
 | 378 |  | 
 | 379 | 	DBG(" -> pSeries_init_early()\n"); | 
 | 380 |  | 
 | 381 | 	fw_feature_init(); | 
 | 382 | 	 | 
 | 383 | 	if (systemcfg->platform & PLATFORM_LPAR) | 
 | 384 | 		hpte_init_lpar(); | 
 | 385 | 	else { | 
 | 386 | 		hpte_init_native(); | 
 | 387 | 		iommu_off = (of_chosen && | 
 | 388 | 			     get_property(of_chosen, "linux,iommu-off", NULL)); | 
 | 389 | 	} | 
 | 390 |  | 
 | 391 | 	generic_find_legacy_serial_ports(&physport, &default_speed); | 
 | 392 |  | 
 | 393 | 	if (systemcfg->platform & PLATFORM_LPAR) | 
 | 394 | 		find_udbg_vterm(); | 
 | 395 | 	else if (physport) { | 
 | 396 | 		/* Map the uart for udbg. */ | 
| Benjamin Herrenschmidt | dfbacdc | 2005-04-16 15:24:33 -0700 | [diff] [blame] | 397 | 		comport = (void *)ioremap(physport, 16); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 398 | 		udbg_init_uart(comport, default_speed); | 
 | 399 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 400 | 		DBG("Hello World !\n"); | 
 | 401 | 	} | 
 | 402 |  | 
 | 403 |  | 
 | 404 | 	iommu_init_early_pSeries(); | 
 | 405 |  | 
 | 406 | 	pSeries_discover_pic(); | 
 | 407 |  | 
 | 408 | 	DBG(" <- pSeries_init_early()\n"); | 
 | 409 | } | 
 | 410 |  | 
 | 411 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 412 | static int pSeries_check_legacy_ioport(unsigned int baseport) | 
 | 413 | { | 
 | 414 | 	struct device_node *np; | 
 | 415 |  | 
 | 416 | #define I8042_DATA_REG	0x60 | 
 | 417 | #define FDC_BASE	0x3f0 | 
 | 418 |  | 
 | 419 |  | 
 | 420 | 	switch(baseport) { | 
 | 421 | 	case I8042_DATA_REG: | 
 | 422 | 		np = of_find_node_by_type(NULL, "8042"); | 
 | 423 | 		if (np == NULL) | 
 | 424 | 			return -ENODEV; | 
 | 425 | 		of_node_put(np); | 
 | 426 | 		break; | 
 | 427 | 	case FDC_BASE: | 
 | 428 | 		np = of_find_node_by_type(NULL, "fdc"); | 
 | 429 | 		if (np == NULL) | 
 | 430 | 			return -ENODEV; | 
 | 431 | 		of_node_put(np); | 
 | 432 | 		break; | 
 | 433 | 	} | 
 | 434 | 	return 0; | 
 | 435 | } | 
 | 436 |  | 
 | 437 | /* | 
 | 438 |  * Called very early, MMU is off, device-tree isn't unflattened | 
 | 439 |  */ | 
 | 440 | extern struct machdep_calls pSeries_md; | 
 | 441 |  | 
 | 442 | static int __init pSeries_probe(int platform) | 
 | 443 | { | 
 | 444 | 	if (platform != PLATFORM_PSERIES && | 
 | 445 | 	    platform != PLATFORM_PSERIES_LPAR) | 
 | 446 | 		return 0; | 
 | 447 |  | 
 | 448 | 	/* if we have some ppc_md fixups for LPAR to do, do | 
 | 449 | 	 * it here ... | 
 | 450 | 	 */ | 
 | 451 |  | 
 | 452 | 	return 1; | 
 | 453 | } | 
 | 454 |  | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 455 | DECLARE_PER_CPU(unsigned long, smt_snooze_delay); | 
 | 456 |  | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 457 | static inline void dedicated_idle_sleep(unsigned int cpu) | 
 | 458 | { | 
 | 459 | 	struct paca_struct *ppaca = &paca[cpu ^ 1]; | 
 | 460 |  | 
 | 461 | 	/* Only sleep if the other thread is not idle */ | 
 | 462 | 	if (!(ppaca->lppaca.idle)) { | 
 | 463 | 		local_irq_disable(); | 
 | 464 |  | 
 | 465 | 		/* | 
 | 466 | 		 * We are about to sleep the thread and so wont be polling any | 
 | 467 | 		 * more. | 
 | 468 | 		 */ | 
 | 469 | 		clear_thread_flag(TIF_POLLING_NRFLAG); | 
 | 470 |  | 
 | 471 | 		/* | 
 | 472 | 		 * SMT dynamic mode. Cede will result in this thread going | 
 | 473 | 		 * dormant, if the partner thread is still doing work.  Thread | 
 | 474 | 		 * wakes up if partner goes idle, an interrupt is presented, or | 
 | 475 | 		 * a prod occurs.  Returning from the cede enables external | 
 | 476 | 		 * interrupts. | 
 | 477 | 		 */ | 
 | 478 | 		if (!need_resched()) | 
 | 479 | 			cede_processor(); | 
 | 480 | 		else | 
 | 481 | 			local_irq_enable(); | 
 | 482 | 	} else { | 
 | 483 | 		/* | 
 | 484 | 		 * Give the HV an opportunity at the processor, since we are | 
 | 485 | 		 * not doing any work. | 
 | 486 | 		 */ | 
 | 487 | 		poll_pending(); | 
 | 488 | 	} | 
 | 489 | } | 
 | 490 |  | 
 | 491 | static int pseries_dedicated_idle(void) | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 492 | { | 
 | 493 | 	long oldval; | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 494 | 	struct paca_struct *lpaca = get_paca(); | 
 | 495 | 	unsigned int cpu = smp_processor_id(); | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 496 | 	unsigned long start_snooze; | 
 | 497 | 	unsigned long *smt_snooze_delay = &__get_cpu_var(smt_snooze_delay); | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 498 |  | 
 | 499 | 	while (1) { | 
 | 500 | 		/* | 
 | 501 | 		 * Indicate to the HV that we are idle. Now would be | 
 | 502 | 		 * a good time to find other work to dispatch. | 
 | 503 | 		 */ | 
 | 504 | 		lpaca->lppaca.idle = 1; | 
 | 505 |  | 
 | 506 | 		oldval = test_and_clear_thread_flag(TIF_NEED_RESCHED); | 
 | 507 | 		if (!oldval) { | 
 | 508 | 			set_thread_flag(TIF_POLLING_NRFLAG); | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 509 |  | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 510 | 			start_snooze = __get_tb() + | 
 | 511 | 				*smt_snooze_delay * tb_ticks_per_usec; | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 512 |  | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 513 | 			while (!need_resched() && !cpu_is_offline(cpu)) { | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 514 | 				ppc64_runlatch_off(); | 
 | 515 |  | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 516 | 				/* | 
 | 517 | 				 * Go into low thread priority and possibly | 
 | 518 | 				 * low power mode. | 
 | 519 | 				 */ | 
 | 520 | 				HMT_low(); | 
 | 521 | 				HMT_very_low(); | 
 | 522 |  | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 523 | 				if (*smt_snooze_delay != 0 && | 
 | 524 | 				    __get_tb() > start_snooze) { | 
 | 525 | 					HMT_medium(); | 
 | 526 | 					dedicated_idle_sleep(cpu); | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 527 | 				} | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 528 |  | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 529 | 			} | 
 | 530 |  | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 531 | 			HMT_medium(); | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 532 | 			clear_thread_flag(TIF_POLLING_NRFLAG); | 
 | 533 | 		} else { | 
 | 534 | 			set_need_resched(); | 
 | 535 | 		} | 
 | 536 |  | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 537 | 		lpaca->lppaca.idle = 0; | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 538 | 		ppc64_runlatch_on(); | 
 | 539 |  | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 540 | 		schedule(); | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 541 |  | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 542 | 		if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING) | 
 | 543 | 			cpu_die(); | 
 | 544 | 	} | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 545 | } | 
 | 546 |  | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 547 | static int pseries_shared_idle(void) | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 548 | { | 
 | 549 | 	struct paca_struct *lpaca = get_paca(); | 
 | 550 | 	unsigned int cpu = smp_processor_id(); | 
 | 551 |  | 
 | 552 | 	while (1) { | 
 | 553 | 		/* | 
 | 554 | 		 * Indicate to the HV that we are idle. Now would be | 
 | 555 | 		 * a good time to find other work to dispatch. | 
 | 556 | 		 */ | 
 | 557 | 		lpaca->lppaca.idle = 1; | 
 | 558 |  | 
 | 559 | 		while (!need_resched() && !cpu_is_offline(cpu)) { | 
 | 560 | 			local_irq_disable(); | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 561 | 			ppc64_runlatch_off(); | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 562 |  | 
 | 563 | 			/* | 
 | 564 | 			 * Yield the processor to the hypervisor.  We return if | 
 | 565 | 			 * an external interrupt occurs (which are driven prior | 
 | 566 | 			 * to returning here) or if a prod occurs from another | 
 | 567 | 			 * processor. When returning here, external interrupts | 
 | 568 | 			 * are enabled. | 
 | 569 | 			 * | 
 | 570 | 			 * Check need_resched() again with interrupts disabled | 
 | 571 | 			 * to avoid a race. | 
 | 572 | 			 */ | 
 | 573 | 			if (!need_resched()) | 
 | 574 | 				cede_processor(); | 
 | 575 | 			else | 
 | 576 | 				local_irq_enable(); | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 577 |  | 
 | 578 | 			HMT_medium(); | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 579 | 		} | 
 | 580 |  | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 581 | 		lpaca->lppaca.idle = 0; | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 582 | 		ppc64_runlatch_on(); | 
 | 583 |  | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 584 | 		schedule(); | 
| Anton Blanchard | 050a093 | 2005-07-07 17:56:33 -0700 | [diff] [blame] | 585 |  | 
 | 586 | 		if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING) | 
| Michael Ellerman | c66d5dd | 2005-07-07 17:56:29 -0700 | [diff] [blame] | 587 | 			cpu_die(); | 
 | 588 | 	} | 
 | 589 |  | 
 | 590 | 	return 0; | 
 | 591 | } | 
 | 592 |  | 
| Paul Mackerras | 4267292 | 2005-09-12 17:17:36 +1000 | [diff] [blame] | 593 | static int pSeries_pci_probe_mode(struct pci_bus *bus) | 
 | 594 | { | 
 | 595 | 	if (systemcfg->platform & PLATFORM_LPAR) | 
 | 596 | 		return PCI_PROBE_DEVTREE; | 
 | 597 | 	return PCI_PROBE_NORMAL; | 
 | 598 | } | 
 | 599 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 600 | struct machdep_calls __initdata pSeries_md = { | 
 | 601 | 	.probe			= pSeries_probe, | 
 | 602 | 	.setup_arch		= pSeries_setup_arch, | 
 | 603 | 	.init_early		= pSeries_init_early, | 
 | 604 | 	.get_cpuinfo		= pSeries_get_cpuinfo, | 
 | 605 | 	.log_error		= pSeries_log_error, | 
 | 606 | 	.pcibios_fixup		= pSeries_final_fixup, | 
| Paul Mackerras | 4267292 | 2005-09-12 17:17:36 +1000 | [diff] [blame] | 607 | 	.pci_probe_mode		= pSeries_pci_probe_mode, | 
| John Rose | dad32bb | 2005-06-23 17:09:54 +1000 | [diff] [blame] | 608 | 	.irq_bus_setup		= pSeries_irq_bus_setup, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 609 | 	.restart		= rtas_restart, | 
 | 610 | 	.power_off		= rtas_power_off, | 
 | 611 | 	.halt			= rtas_halt, | 
 | 612 | 	.panic			= rtas_os_term, | 
 | 613 | 	.cpu_die		= pSeries_mach_cpu_die, | 
| Arnd Bergmann | 773bf9c | 2005-06-23 09:43:18 +1000 | [diff] [blame] | 614 | 	.get_boot_time		= rtas_get_boot_time, | 
 | 615 | 	.get_rtc_time		= rtas_get_rtc_time, | 
 | 616 | 	.set_rtc_time		= rtas_set_rtc_time, | 
| Arnd Bergmann | 10f7e7c | 2005-06-23 09:43:07 +1000 | [diff] [blame] | 617 | 	.calibrate_decr		= generic_calibrate_decr, | 
| Arnd Bergmann | 6566c6f | 2005-06-23 09:43:28 +1000 | [diff] [blame] | 618 | 	.progress		= rtas_progress, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 619 | 	.check_legacy_ioport	= pSeries_check_legacy_ioport, | 
 | 620 | 	.system_reset_exception = pSeries_system_reset_exception, | 
 | 621 | 	.machine_check_exception = pSeries_machine_check_exception, | 
 | 622 | }; |