X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fppc64%2Fkernel%2Fsetup.c;h=1951866a7acef67fa5eeea77f2bcc935610be153;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=dc090d425e2174e60ab346f709a2bdcbeb276a0c;hpb=a2c21200f1c81b08cb55e417b68150bba439b646;p=linux-2.6.git diff --git a/arch/ppc64/kernel/setup.c b/arch/ppc64/kernel/setup.c index dc090d425..1951866a7 100644 --- a/arch/ppc64/kernel/setup.c +++ b/arch/ppc64/kernel/setup.c @@ -10,6 +10,8 @@ * 2 of the License, or (at your option) any later version. */ +#undef DEBUG + #include #include #include @@ -27,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -47,60 +50,79 @@ #include #include #include +#include + +#ifdef DEBUG +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif + +/* + * Here are some early debugging facilities. You can enable one + * but your kernel will not boot on anything else if you do so + */ + +/* This one is for use on LPAR machines that support an HVC console + * on vterm 0 + */ +extern void udbg_init_debug_lpar(void); +/* This one is for use on Apple G5 machines + */ +extern void udbg_init_pmac_realmode(void); +/* That's RTAS panel debug */ +extern void call_rtas_display_status_delay(unsigned char c); + +#define EARLY_DEBUG_INIT() do {} while(0) + +#if 0 +#define EARLY_DEBUG_INIT() udbg_init_debug_lpar() +#define EARLY_DEBUG_INIT() udbg_init_pmac_realmode() +#define EARLY_DEBUG_INIT() \ + do { ppc_md.udbg_putc = call_rtas_display_status_delay; } while(0) +#endif -extern unsigned long klimit; /* extern void *stab; */ extern HTAB htab_data; -extern unsigned long loops_per_jiffy; - -int have_of = 1; +extern unsigned long klimit; -extern void chrp_init(unsigned long r3, - unsigned long r4, - unsigned long r5, - unsigned long r6, - unsigned long r7); - -extern void pmac_init(unsigned long r3, - unsigned long r4, - unsigned long r5, - unsigned long r6, - unsigned long r7); - -extern void iSeries_init( void ); -extern void iSeries_init_early( void ); -extern void pSeries_init_early( void ); -extern void pSeriesLP_init_early(void); -extern void pmac_init_early(void); -extern void mm_init_ppc64( void ); -extern void pseries_secondary_smp_init(unsigned long); +extern void mm_init_ppc64(void); extern int idle_setup(void); -extern void vpa_init(int cpu); +extern void stab_initialize(unsigned long stab); +extern void htab_initialize(void); +extern void early_init_devtree(void *flat_dt); +extern void unflatten_device_tree(void); unsigned long decr_overclock = 1; unsigned long decr_overclock_proc0 = 1; unsigned long decr_overclock_set = 0; unsigned long decr_overclock_proc0_set = 0; -int powersave_nap; +int have_of = 1; +int boot_cpuid = 0; +dev_t boot_dev; -unsigned char aux_device_present; +/* + * These are used in binfmt_elf.c to put aux entries on the stack + * for each elf executable being started. + */ +int dcache_bsize; +int icache_bsize; +int ucache_bsize; -void parse_cmd_line(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7); -int parse_bootinfo(void); +/* The main machine-dep calls structure + */ +struct machdep_calls ppc_md; #ifdef CONFIG_MAGIC_SYSRQ unsigned long SYSRQ_KEY; #endif /* CONFIG_MAGIC_SYSRQ */ -struct machdep_calls ppc_md; static int ppc64_panic_event(struct notifier_block *, unsigned long, void *); - static struct notifier_block ppc64_panic_block = { - notifier_call: ppc64_panic_event, - priority: INT_MIN /* may not return; must be done last */ + .notifier_call = ppc64_panic_event, + .priority = INT_MIN /* may not return; must be done last */ }; /* @@ -112,42 +134,32 @@ static struct notifier_block ppc64_panic_block = { * these processors use on existing boards. -- Dan */ struct screen_info screen_info = { - 0, 25, /* orig-x, orig-y */ - 0, /* unused */ - 0, /* orig-video-page */ - 0, /* orig-video-mode */ - 80, /* orig-video-cols */ - 0,0,0, /* ega_ax, ega_bx, ega_cx */ - 25, /* orig-video-lines */ - 1, /* orig-video-isVGA */ - 16 /* orig-video-points */ + .orig_x = 0, + .orig_y = 25, + .orig_video_cols = 80, + .orig_video_lines = 25, + .orig_video_isVGA = 1, + .orig_video_points = 16 }; -/* - * These are used in binfmt_elf.c to put aux entries on the stack - * for each elf executable being started. - */ -int dcache_bsize; -int icache_bsize; -int ucache_bsize; - /* * Initialize the PPCDBG state. Called before relocation has been enabled. */ -void ppcdbg_initialize(void) { - unsigned long offset = reloc_offset(); - struct naca_struct *_naca = RELOC(naca); - - _naca->debug_switch = PPC_DEBUG_DEFAULT; /* | PPCDBG_BUSWALK | PPCDBG_PHBINIT | PPCDBG_MM | PPCDBG_MMINIT | PPCDBG_TCEINIT | PPCDBG_TCE */; +void __init ppcdbg_initialize(void) +{ + naca->debug_switch = PPC_DEBUG_DEFAULT; /* | PPCDBG_BUSWALK | */ + /* PPCDBG_PHBINIT | PPCDBG_MM | PPCDBG_MMINIT | PPCDBG_TCEINIT | PPCDBG_TCE */; } +/* + * Early boot console based on udbg + */ static struct console udbg_console = { .name = "udbg", .write = udbg_console_write, .flags = CON_PRINTBUFFER, .index = -1, }; - static int early_console_initialized; void __init disable_early_printk(void) @@ -158,111 +170,457 @@ void __init disable_early_printk(void) early_console_initialized = 0; } -/* - * Do some initial setup of the system. The parameters are those which - * were passed in from the bootloader. +#if defined(CONFIG_PPC_MULTIPLATFORM) && defined(CONFIG_SMP) + +static int smt_enabled_cmdline; + +/* Look for ibm,smt-enabled OF option */ +static void check_smt_enabled(void) +{ + struct device_node *dn; + char *smt_option; + + /* Allow the command line to overrule the OF option */ + if (smt_enabled_cmdline) + return; + + dn = of_find_node_by_path("/options"); + + if (dn) { + smt_option = (char *)get_property(dn, "ibm,smt-enabled", NULL); + + if (smt_option) { + if (!strcmp(smt_option, "on")) + smt_enabled_at_boot = 1; + else if (!strcmp(smt_option, "off")) + smt_enabled_at_boot = 0; + } + } +} + +/* Look for smt-enabled= cmdline option */ +static int __init early_smt_enabled(char *p) +{ + smt_enabled_cmdline = 1; + + if (!p) + return 0; + + if (!strcmp(p, "on") || !strcmp(p, "1")) + smt_enabled_at_boot = 1; + else if (!strcmp(p, "off") || !strcmp(p, "0")) + smt_enabled_at_boot = 0; + + return 0; +} +early_param("smt-enabled", early_smt_enabled); + +/** + * setup_cpu_maps - initialize the following cpu maps: + * cpu_possible_map + * cpu_present_map + * cpu_sibling_map + * + * Having the possible map set up early allows us to restrict allocations + * of things like irqstacks to num_possible_cpus() rather than NR_CPUS. + * + * We do not initialize the online map here; cpus set their own bits in + * cpu_online_map as they come up. + * + * This function is valid only for Open Firmware systems. finish_device_tree + * must be called before using this. + * + * While we're here, we may as well set the "physical" cpu ids in the paca. */ -void setup_system(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) +static void __init setup_cpu_maps(void) { -#if defined(CONFIG_SMP) && defined(CONFIG_PPC_PSERIES) - int ret, i; -#endif + struct device_node *dn = NULL; + int cpu = 0; + + check_smt_enabled(); + + while ((dn = of_find_node_by_type(dn, "cpu")) && cpu < NR_CPUS) { + u32 *intserv; + int j, len = sizeof(u32), nthreads; + + intserv = (u32 *)get_property(dn, "ibm,ppc-interrupt-server#s", + &len); + if (!intserv) + intserv = (u32 *)get_property(dn, "reg", NULL); + + nthreads = len / sizeof(u32); + + for (j = 0; j < nthreads && cpu < NR_CPUS; j++) { + /* + * Only spin up secondary threads if SMT is enabled. + * We must leave space in the logical map for the + * threads. + */ + if (j == 0 || smt_enabled_at_boot) { + cpu_set(cpu, cpu_present_map); + set_hard_smp_processor_id(cpu, intserv[j]); + } + cpu_set(cpu, cpu_possible_map); + cpu++; + } + } -#ifdef CONFIG_XMON_DEFAULT - xmon_init(); -#endif + /* + * On pSeries LPAR, we need to know how many cpus + * could possibly be added to this partition. + */ + if (systemcfg->platform == PLATFORM_PSERIES_LPAR && + (dn = of_find_node_by_path("/rtas"))) { + int num_addr_cell, num_size_cell, maxcpus; + unsigned int *ireg; + + num_addr_cell = prom_n_addr_cells(dn); + num_size_cell = prom_n_size_cells(dn); + + ireg = (unsigned int *) + get_property(dn, "ibm,lrdr-capacity", NULL); + + if (!ireg) + goto out; + + maxcpus = ireg[num_addr_cell + num_size_cell]; + + /* Double maxcpus for processors which have SMT capability */ + if (cur_cpu_spec->cpu_features & CPU_FTR_SMT) + maxcpus *= 2; + + if (maxcpus > NR_CPUS) { + printk(KERN_WARNING + "Partition configured for %d cpus, " + "operating system maximum is %d.\n", + maxcpus, NR_CPUS); + maxcpus = NR_CPUS; + } else + printk(KERN_INFO "Partition configured for %d cpus.\n", + maxcpus); + + for (cpu = 0; cpu < maxcpus; cpu++) + cpu_set(cpu, cpu_possible_map); + out: + of_node_put(dn); + } + + /* + * Do the sibling map; assume only two threads per processor. + */ + for_each_cpu(cpu) { + cpu_set(cpu, cpu_sibling_map[cpu]); + if (cur_cpu_spec->cpu_features & CPU_FTR_SMT) + cpu_set(cpu ^ 0x1, cpu_sibling_map[cpu]); + } + + systemcfg->processorCount = num_present_cpus(); +} +#endif /* defined(CONFIG_PPC_MULTIPLATFORM) && defined(CONFIG_SMP) */ -#ifdef CONFIG_PPC_ISERIES - /* pSeries systems are identified in prom.c via OF. */ - if ( itLpNaca.xLparInstalled == 1 ) - systemcfg->platform = PLATFORM_ISERIES_LPAR; -#endif - - switch (systemcfg->platform) { -#ifdef CONFIG_PPC_ISERIES - case PLATFORM_ISERIES_LPAR: - iSeries_init_early(); - break; -#endif +#ifdef CONFIG_PPC_MULTIPLATFORM + +extern struct machdep_calls pSeries_md; +extern struct machdep_calls pmac_md; + +/* Ultimately, stuff them in an elf section like initcalls... */ +static struct machdep_calls __initdata *machines[] = { #ifdef CONFIG_PPC_PSERIES - case PLATFORM_PSERIES: - pSeries_init_early(); - parse_bootinfo(); - break; - - case PLATFORM_PSERIES_LPAR: - pSeriesLP_init_early(); - parse_bootinfo(); - break; + &pSeries_md, #endif /* CONFIG_PPC_PSERIES */ #ifdef CONFIG_PPC_PMAC - case PLATFORM_POWERMAC: - pmac_init_early(); - parse_bootinfo(); + &pmac_md, #endif /* CONFIG_PPC_PMAC */ + NULL +}; + +/* + * Early initialization entry point. This is called by head.S + * with MMU translation disabled. We rely on the "feature" of + * the CPU that ignores the top 2 bits of the address in real + * mode so we can access kernel globals normally provided we + * only toy with things in the RMO region. From here, we do + * some early parsing of the device-tree to setup out LMB + * data structures, and allocate & initialize the hash table + * and segment tables so we can start running with translation + * enabled. + * + * It is this function which will call the probe() callback of + * the various platform types and copy the matching one to the + * global ppc_md structure. Your platform can eventually do + * some very early initializations from the probe() routine, but + * this is not recommended, be very careful as, for example, the + * device-tree is not accessible via normal means at this point. + */ + +void __init early_setup(unsigned long dt_ptr) +{ + struct paca_struct *lpaca = get_paca(); + static struct machdep_calls **mach; + + /* + * Enable early debugging if any specified (see top of + * this file) + */ + EARLY_DEBUG_INIT(); + + DBG(" -> early_setup()\n"); + + /* + * Fill the default DBG level in naca (do we want to keep + * that old mecanism around forever ?) + */ + ppcdbg_initialize(); + + /* + * Do early initializations using the flattened device + * tree, like retreiving the physical memory map or + * calculating/retreiving the hash table size + */ + early_init_devtree(__va(dt_ptr)); + + /* + * Iterate all ppc_md structures until we find the proper + * one for the current machine type + */ + DBG("Probing machine type for platform %x...\n", + systemcfg->platform); + + for (mach = machines; *mach; mach++) { + if ((*mach)->probe(systemcfg->platform)) + break; + } + /* What can we do if we didn't find ? */ + if (*mach == NULL) { + DBG("No suitable machine found !\n"); + for (;;); + } + ppc_md = **mach; + + /* our udbg callbacks got overriden by the above, let's put them + * back in. Ultimately, I want those things to be split from the + * main ppc_md + */ + EARLY_DEBUG_INIT(); + + DBG("Found, Initializing memory management...\n"); + + /* + * Initialize stab / SLB management + */ + stab_initialize(lpaca->stab_real); + + /* + * Initialize the MMU Hash table and create the linear mapping + * of memory + */ + htab_initialize(); + + DBG(" <- early_setup()\n"); +} + + +/* + * Initialize some remaining members of the naca and systemcfg structures + * (at least until we get rid of them completely). This is mostly some + * cache informations about the CPU that will be used by cache flush + * routines and/or provided to userland + */ +static void __init initialize_naca(void) +{ + struct device_node *np; + unsigned long num_cpus = 0; + + DBG(" -> initialize_naca()\n"); + + for (np = NULL; (np = of_find_node_by_type(np, "cpu"));) { + num_cpus += 1; + + /* We're assuming *all* of the CPUs have the same + * d-cache and i-cache sizes... -Peter + */ + + if ( num_cpus == 1 ) { + u32 *sizep, *lsizep; + u32 size, lsize; + const char *dc, *ic; + + /* Then read cache informations */ + if (systemcfg->platform == PLATFORM_POWERMAC) { + dc = "d-cache-block-size"; + ic = "i-cache-block-size"; + } else { + dc = "d-cache-line-size"; + ic = "i-cache-line-size"; + } + + size = 0; + lsize = cur_cpu_spec->dcache_bsize; + sizep = (u32 *)get_property(np, "d-cache-size", NULL); + if (sizep != NULL) + size = *sizep; + lsizep = (u32 *) get_property(np, dc, NULL); + if (lsizep != NULL) + lsize = *lsizep; + + if (sizep == 0 || lsizep == 0) + DBG("Argh, can't find dcache properties ! " + "sizep: %p, lsizep: %p\n", sizep, lsizep); + + systemcfg->dCacheL1Size = size; + systemcfg->dCacheL1LineSize = lsize; + naca->dCacheL1LogLineSize = __ilog2(lsize); + naca->dCacheL1LinesPerPage = PAGE_SIZE/(lsize); + + size = 0; + lsize = cur_cpu_spec->icache_bsize; + sizep = (u32 *)get_property(np, "i-cache-size", NULL); + if (sizep != NULL) + size = *sizep; + lsizep = (u32 *)get_property(np, ic, NULL); + if (lsizep != NULL) + lsize = *lsizep; + if (sizep == 0 || lsizep == 0) + DBG("Argh, can't find icache properties ! " + "sizep: %p, lsizep: %p\n", sizep, lsizep); + + systemcfg->iCacheL1Size = size; + systemcfg->iCacheL1LineSize = lsize; + naca->iCacheL1LogLineSize = __ilog2(lsize); + naca->iCacheL1LinesPerPage = PAGE_SIZE/(lsize); + + } + } + + /* Add an eye catcher and the systemcfg layout version number */ + strcpy(systemcfg->eye_catcher, "SYSTEMCFG:PPC64"); + systemcfg->version.major = SYSTEMCFG_MAJOR; + systemcfg->version.minor = SYSTEMCFG_MINOR; + systemcfg->processor = mfspr(SPRN_PVR); + + DBG(" <- initialize_naca()\n"); +} + +static void __init check_for_initrd(void) +{ +#ifdef CONFIG_BLK_DEV_INITRD + u64 *prop; + + DBG(" -> check_for_initrd()\n"); + + prop = (u64 *)get_property(of_chosen, "linux,initrd-start", NULL); + if (prop != NULL) { + initrd_start = (unsigned long)__va(*prop); + prop = (u64 *)get_property(of_chosen, "linux,initrd-end", NULL); + if (prop != NULL) { + initrd_end = (unsigned long)__va(*prop); + initrd_below_start_ok = 1; + } else + initrd_start = 0; } /* If we were passed an initrd, set the ROOT_DEV properly if the values * look sensible. If not, clear initrd reference. */ -#ifdef CONFIG_BLK_DEV_INITRD if (initrd_start >= KERNELBASE && initrd_end >= KERNELBASE && initrd_end > initrd_start) ROOT_DEV = Root_RAM0; else initrd_start = initrd_end = 0; + + if (initrd_start) + printk("Found initrd at 0x%lx:0x%lx\n", initrd_start, initrd_end); + + DBG(" <- check_for_initrd()\n"); #endif /* CONFIG_BLK_DEV_INITRD */ +} -#ifdef CONFIG_BOOTX_TEXT - map_boot_text(); - if (systemcfg->platform == PLATFORM_POWERMAC) { - early_console_initialized = 1; - register_console(&udbg_console); - } -#endif /* CONFIG_BOOTX_TEXT */ +#endif /* CONFIG_PPC_MULTIPLATFORM */ -#ifdef CONFIG_PPC_PSERIES - if (systemcfg->platform & PLATFORM_PSERIES) { - early_console_initialized = 1; - register_console(&udbg_console); - __irq_offset_value = NUM_ISA_INTERRUPTS; - finish_device_tree(); - chrp_init(r3, r4, r5, r6, r7); +/* + * Do some initial setup of the system. The parameters are those which + * were passed in from the bootloader. + */ +void __init setup_system(void) +{ + DBG(" -> setup_system()\n"); -#ifdef CONFIG_SMP - /* Start secondary threads on SMT systems */ - for (i = 0; i < NR_CPUS; i++) { - if (cpu_available(i) && !cpu_possible(i)) { - printk("%16.16x : starting thread\n", i); - rtas_call(rtas_token("start-cpu"), 3, 1, &ret, - get_hard_smp_processor_id(i), - (u32)*((unsigned long *)pseries_secondary_smp_init), - i); - cpu_set(i, cpu_possible_map); - systemcfg->processorCount++; - } - } -#endif /* CONFIG_SMP */ - } +#ifdef CONFIG_PPC_ISERIES + /* pSeries systems are identified in prom.c via OF. */ + if (itLpNaca.xLparInstalled == 1) + systemcfg->platform = PLATFORM_ISERIES_LPAR; + + ppc_md.init_early(); +#else /* CONFIG_PPC_ISERIES */ + + /* + * Unflatten the device-tree passed by prom_init or kexec + */ + unflatten_device_tree(); + + /* + * Fill the naca & systemcfg structures with informations + * retreived from the device-tree. Need to be called before + * finish_device_tree() since the later requires some of the + * informations filled up here to properly parse the interrupt + * tree. + * It also sets up the cache line sizes which allows to call + * routines like flush_icache_range (used by the hash init + * later on). + */ + initialize_naca(); + +#ifdef CONFIG_PPC_PSERIES + /* + * Initialize RTAS if available + */ + rtas_initialize(); #endif /* CONFIG_PPC_PSERIES */ -#ifdef CONFIG_PPC_PMAC - if (systemcfg->platform == PLATFORM_POWERMAC) { - finish_device_tree(); - pmac_init(r3, r4, r5, r6, r7); - } -#endif /* CONFIG_PPC_PMAC */ + /* + * Check if we have an initrd provided via the device-tree + */ + check_for_initrd(); -#if defined(CONFIG_HOTPLUG_CPU) && !defined(CONFIG_PPC_PMAC) - rtas_stop_self_args.token = rtas_token("stop-self"); -#endif /* CONFIG_HOTPLUG_CPU && !CONFIG_PPC_PMAC */ + /* + * Do some platform specific early initializations, that includes + * setting up the hash table pointers. It also sets up some interrupt-mapping + * related options that will be used by finish_device_tree() + */ + ppc_md.init_early(); - /* Finish initializing the hash table (do the dynamic - * patching for the fast-path hashtable.S code) + /* + * "Finish" the device-tree, that is do the actual parsing of + * some of the properties like the interrupt map */ - htab_finish_init(); + finish_device_tree(); + + /* + * Initialize xmon + */ +#ifdef CONFIG_XMON_DEFAULT + xmon_init(); +#endif + /* + * Register early console + */ + early_console_initialized = 1; + register_console(&udbg_console); + +#endif /* !CONFIG_PPC_ISERIES */ + + /* Save unparsed command line copy for /proc/cmdline */ + strlcpy(saved_command_line, cmd_line, COMMAND_LINE_SIZE); + + parse_early_param(); + +#if defined(CONFIG_SMP) && !defined(CONFIG_PPC_ISERIES) + /* + * iSeries has already initialized the cpu maps at this point. + */ + setup_cpu_maps(); +#endif /* defined(CONFIG_SMP) && !defined(CONFIG_PPC_ISERIES) */ printk("Starting Linux PPC64 %s\n", UTS_RELEASE); @@ -282,28 +640,10 @@ void setup_system(unsigned long r3, unsigned long r4, unsigned long r5, mm_init_ppc64(); -#if defined(CONFIG_SMP) && defined(CONFIG_PPC_PSERIES) - if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) { - vpa_init(boot_cpuid); - } -#endif - - /* Select the correct idle loop for the platform. */ - idle_setup(); - - switch (systemcfg->platform) { -#ifdef CONFIG_PPC_ISERIES - case PLATFORM_ISERIES_LPAR: - iSeries_init(); - break; -#endif - default: - /* The following relies on the device tree being */ - /* fully configured. */ - parse_cmd_line(r3, r4, r5, r6, r7); - } + DBG(" <- setup_system()\n"); } + void machine_restart(char *cmd) { if (ppc_md.nvram_sync) @@ -373,7 +713,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) #ifdef CONFIG_SMP pvr = per_cpu(pvr, cpu_id); #else - pvr = _get_PVR(); + pvr = mfspr(SPRN_PVR); #endif maj = (pvr >> 8) & 0xFF; min = pvr & 0xFF; @@ -425,74 +765,75 @@ struct seq_operations cpuinfo_op = { .show = show_cpuinfo, }; -/* - * Fetch the cmd_line from open firmware. - */ -void parse_cmd_line(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ - cmd_line[0] = 0; +#if 0 /* XXX not currently used */ +unsigned long memory_limit; -#ifdef CONFIG_CMDLINE - strlcpy(cmd_line, CONFIG_CMDLINE, sizeof(cmd_line)); -#endif /* CONFIG_CMDLINE */ +static int __init early_parsemem(char *p) +{ + if (!p) + return 0; -#ifdef CONFIG_PPC_PSERIES - { - struct device_node *chosen; - - chosen = of_find_node_by_name(NULL, "chosen"); - if (chosen != NULL) { - char *p; - p = get_property(chosen, "bootargs", NULL); - if (p != NULL && p[0] != 0) - strlcpy(cmd_line, p, sizeof(cmd_line)); - of_node_put(chosen); - } - } -#endif + memory_limit = memparse(p, &p); - /* Look for mem= option on command line */ - if (strstr(cmd_line, "mem=")) { - char *p, *q; - unsigned long maxmem = 0; - extern unsigned long __max_memory; - - for (q = cmd_line; (p = strstr(q, "mem=")) != 0; ) { - q = p + 4; - if (p > cmd_line && p[-1] != ' ') - continue; - maxmem = simple_strtoul(q, &q, 0); - if (*q == 'k' || *q == 'K') { - maxmem <<= 10; - ++q; - } else if (*q == 'm' || *q == 'M') { - maxmem <<= 20; - ++q; - } - } - __max_memory = maxmem; - } + return 0; } +early_param("mem", early_parsemem); +#endif -#ifdef CONFIG_PPC_PSERIES +#ifdef CONFIG_PPC_MULTIPLATFORM static int __init set_preferred_console(void) { struct device_node *prom_stdout; char *name; - int offset; + int offset = 0; +#if 0 + phandle *stdout_ph; +#endif + DBG(" -> set_preferred_console()\n"); /* The user has requested a console so this is already set up. */ - if (strstr(saved_command_line, "console=")) + if (strstr(saved_command_line, "console=")) { + DBG(" console was specified !\n"); return -EBUSY; + } - prom_stdout = find_path_device(of_stdout_device); - if (!prom_stdout) + if (!of_chosen) { + DBG(" of_chosen is NULL !\n"); return -ENODEV; + } + /* We are getting a weird phandle from OF ... */ +#if 0 + stdout_ph = (phandle *)get_property(of_chosen, "linux,stdout-package", NULL); + if (stdout_ph == NULL) { + DBG(" no linux,stdout-package !\n"); + return -ENODEV; + } + prom_stdout = of_find_node_by_phandle(*stdout_ph); + if (!prom_stdout) { + DBG(" can't find stdout package for phandle 0x%x !\n", *stdout_ph); + return -ENODEV; + } +#endif + /* ... So use the full path instead */ +#if 1 + name = (char *)get_property(of_chosen, "linux,stdout-path", NULL); + if (name == NULL) { + DBG(" no linux,stdout-path !\n"); + return -ENODEV; + } + prom_stdout = of_find_node_by_path(name); + if (!prom_stdout) { + DBG(" can't find stdout package %s !\n", name); + return -ENODEV; + } +#endif + DBG("stdout is %s\n", prom_stdout->full_name); name = (char *)get_property(prom_stdout, "name", NULL); - if (!name) - return -ENODEV; + if (!name) { + DBG(" stdout package has no name !\n"); + goto not_found; + } if (strcmp(name, "serial") == 0) { int i; @@ -513,76 +854,69 @@ static int __init set_preferred_console(void) break; default: /* We dont recognise the serial port */ - return -ENODEV; + goto not_found; } } - } else if (strcmp(name, "vty") == 0) - /* pSeries LPAR virtual console */ - return add_preferred_console("hvc", 0, NULL); + } +#ifdef CONFIG_PPC_PSERIES + else if (strcmp(name, "vty") == 0) { + u32 *reg = (u32 *)get_property(prom_stdout, "reg", NULL); + char *compat = (char *)get_property(prom_stdout, "compatible", NULL); + + if (reg && compat && (strcmp(compat, "hvterm-protocol") == 0)) { + /* Host Virtual Serial Interface */ + int offset; + switch (reg[0]) { + case 0x30000000: + offset = 0; + break; + case 0x30000001: + offset = 1; + break; + default: + goto not_found; + } + of_node_put(prom_stdout); + DBG("Found hvsi console at offset %d\n", offset); + return add_preferred_console("hvsi", offset, NULL); + } else { + /* pSeries LPAR virtual console */ + of_node_put(prom_stdout); + DBG("Found hvc console\n"); + return add_preferred_console("hvc", 0, NULL); + } + } +#endif /* CONFIG_PPC_PSERIES */ else if (strcmp(name, "ch-a") == 0) offset = 0; else if (strcmp(name, "ch-b") == 0) offset = 1; else - return -ENODEV; + goto not_found; + of_node_put(prom_stdout); + + DBG("Found serial console at ttyS%d\n", offset); return add_preferred_console("ttyS", offset, NULL); + not_found: + DBG("No preferred console found !\n"); + of_node_put(prom_stdout); + return -ENODEV; } console_initcall(set_preferred_console); - -int parse_bootinfo(void) -{ - struct bi_record *rec; - - rec = prom.bi_recs; - - if ( rec == NULL || rec->tag != BI_FIRST ) - return -1; - - for ( ; rec->tag != BI_LAST ; rec = bi_rec_next(rec) ) { - switch (rec->tag) { - case BI_CMD_LINE: - strlcpy(cmd_line, (void *)rec->data, sizeof(cmd_line)); - break; - } - } - - return 0; -} -#endif - -int __init ppc_init(void) -{ - /* clear the progress line */ - ppc_md.progress(" ", 0xffff); - - if (ppc_md.init != NULL) { - ppc_md.init(); - } - return 0; -} - -arch_initcall(ppc_init); - -void __init ppc64_calibrate_delay(void) -{ - loops_per_jiffy = tb_ticks_per_jiffy; - - printk("Calibrating delay loop... %lu.%02lu BogoMips\n", - loops_per_jiffy/(500000/HZ), - loops_per_jiffy/(5000/HZ) % 100); -} - -extern void (*calibrate_delay)(void); +#endif /* CONFIG_PPC_MULTIPLATFORM */ #ifdef CONFIG_IRQSTACKS static void __init irqstack_early_init(void) { - int i; + unsigned int i; - /* interrupt stacks must be under 256MB, we cannot afford to take SLB misses on them */ - for (i = 0; i < NR_CPUS; i++) { + /* + * interrupt stacks must be under 256MB, we cannot afford to take + * SLB misses on them. + */ + for_each_cpu(i) { softirq_ctx[i] = (struct thread_info *)__va(lmb_alloc_base(THREAD_SIZE, THREAD_SIZE, 0x10000000)); hardirq_ctx[i] = (struct thread_info *)__va(lmb_alloc_base(THREAD_SIZE, @@ -593,6 +927,31 @@ static void __init irqstack_early_init(void) #define irqstack_early_init() #endif +/* + * Stack space used when we detect a bad kernel stack pointer, and + * early in SMP boots before relocation is enabled. + */ +static void __init emergency_stack_init(void) +{ + unsigned long limit; + unsigned int i; + + /* + * Emergency stacks must be under 256MB, we cannot afford to take + * SLB misses on them. The ABI also requires them to be 128-byte + * aligned. + * + * Since we use these as temporary stacks during secondary CPU + * bringup, we need to get at them in real mode. This means they + * must also be within the RMO region. + */ + limit = min(0x10000000UL, lmb.rmo_size); + + for_each_cpu(i) + paca[i].emergency_sp = __va(lmb_alloc_base(PAGE_SIZE, 128, + limit)) + PAGE_SIZE; +} + /* * Called into from start_kernel, after lock_kernel has been called. * Initializes bootmem, which is unsed to manage page allocation until @@ -603,17 +962,9 @@ void __init setup_arch(char **cmdline_p) extern int panic_timeout; extern void do_init_bootmem(void); - calibrate_delay = ppc64_calibrate_delay; - ppc64_boot_msg(0x12, "Setup Arch"); -#ifdef CONFIG_XMON - if (strstr(cmd_line, "xmon")) { - /* ensure xmon is enabled */ - xmon_init(); - debugger(0); - } -#endif /* CONFIG_XMON */ + *cmdline_p = cmd_line; /* * Set cache line size based on type of cpu as a default. @@ -634,21 +985,22 @@ void __init setup_arch(char **cmdline_p) init_mm.end_data = (unsigned long) _edata; init_mm.brk = klimit; - /* Save unparsed command line copy for /proc/cmdline */ - strlcpy(saved_command_line, cmd_line, COMMAND_LINE_SIZE); - *cmdline_p = cmd_line; - irqstack_early_init(); + emergency_stack_init(); /* set up the bootmem stuff with available memory */ do_init_bootmem(); + /* Select the correct idle loop for the platform. */ + idle_setup(); + ppc_md.setup_arch(); paging_init(); ppc64_boot_msg(0x15, "Setup Done"); } + /* ToDo: do something useful if ppc_md is not yet setup. */ #define PPC64_LINUX_FUNCTION 0x0f000000 #define PPC64_IPL_MESSAGE 0xc0000000 @@ -753,3 +1105,16 @@ int set_decr_overclock( char * str ) __setup("spread_lpevents=", set_spread_lpevents ); __setup("decr_overclock_proc0=", set_decr_overclock_proc0 ); __setup("decr_overclock=", set_decr_overclock ); + +#ifdef CONFIG_XMON +static int __init early_xmon(char *p) +{ + /* ensure xmon is enabled */ + xmon_init(); + debugger(0); + + return 0; +} +early_param("xmon", early_xmon); +#endif +