X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fppc64%2Fkernel%2Fsetup.c;h=870c69ee8c5902f1ab7593432a90371d1815964f;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=1951866a7acef67fa5eeea77f2bcc935610be153;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/arch/ppc64/kernel/setup.c b/arch/ppc64/kernel/setup.c index 1951866a7..870c69ee8 100644 --- a/arch/ppc64/kernel/setup.c +++ b/arch/ppc64/kernel/setup.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -39,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -51,6 +53,10 @@ #include #include #include +#include +#include +#include +#include #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) @@ -72,18 +78,20 @@ extern void udbg_init_debug_lpar(void); extern void udbg_init_pmac_realmode(void); /* That's RTAS panel debug */ extern void call_rtas_display_status_delay(unsigned char c); +/* Here's maple real mode debug */ +extern void udbg_init_maple_realmode(void); #define EARLY_DEBUG_INIT() do {} while(0) #if 0 #define EARLY_DEBUG_INIT() udbg_init_debug_lpar() +#define EARLY_DEBUG_INIT() udbg_init_maple_realmode() #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 void *stab; */ -extern HTAB htab_data; extern unsigned long klimit; extern void mm_init_ppc64(void); @@ -93,6 +101,8 @@ extern void htab_initialize(void); extern void early_init_devtree(void *flat_dt); extern void unflatten_device_tree(void); +extern void smp_release_cpus(void); + unsigned long decr_overclock = 1; unsigned long decr_overclock_proc0 = 1; unsigned long decr_overclock_set = 0; @@ -100,7 +110,13 @@ unsigned long decr_overclock_proc0_set = 0; int have_of = 1; int boot_cpuid = 0; +int boot_cpuid_phys = 0; dev_t boot_dev; +u64 ppc64_pft_size; +u64 ppc64_debug_switch; + +struct ppc64_caches ppc64_caches; +EXPORT_SYMBOL_GPL(ppc64_caches); /* * These are used in binfmt_elf.c to put aux entries on the stack @@ -147,7 +163,7 @@ struct screen_info screen_info = { */ void __init ppcdbg_initialize(void) { - naca->debug_switch = PPC_DEBUG_DEFAULT; /* | PPCDBG_BUSWALK | */ + ppc64_debug_switch = PPC_DEBUG_DEFAULT; /* | PPCDBG_BUSWALK | */ /* PPCDBG_PHBINIT | PPCDBG_MM | PPCDBG_MMINIT | PPCDBG_TCEINIT | PPCDBG_TCE */; } @@ -236,6 +252,7 @@ static void __init setup_cpu_maps(void) { struct device_node *dn = NULL; int cpu = 0; + int swap_cpuid = 0; check_smt_enabled(); @@ -260,11 +277,23 @@ static void __init setup_cpu_maps(void) cpu_set(cpu, cpu_present_map); set_hard_smp_processor_id(cpu, intserv[j]); } + if (intserv[j] == boot_cpuid_phys) + swap_cpuid = cpu; cpu_set(cpu, cpu_possible_map); cpu++; } } + /* Swap CPU id 0 with boot_cpuid_phys, so we can always assume that + * boot cpu is logical 0. + */ + if (boot_cpuid_phys != get_hard_smp_processor_id(0)) { + u32 tmp; + tmp = get_hard_smp_processor_id(0); + set_hard_smp_processor_id(0, boot_cpuid_phys); + set_hard_smp_processor_id(swap_cpuid, tmp); + } + /* * On pSeries LPAR, we need to know how many cpus * could possibly be added to this partition. @@ -323,6 +352,7 @@ static void __init setup_cpu_maps(void) extern struct machdep_calls pSeries_md; extern struct machdep_calls pmac_md; +extern struct machdep_calls maple_md; /* Ultimately, stuff them in an elf section like initcalls... */ static struct machdep_calls __initdata *machines[] = { @@ -332,6 +362,9 @@ static struct machdep_calls __initdata *machines[] = { #ifdef CONFIG_PPC_PMAC &pmac_md, #endif /* CONFIG_PPC_PMAC */ +#ifdef CONFIG_PPC_MAPLE + &maple_md, +#endif /* CONFIG_PPC_MAPLE */ NULL }; @@ -368,7 +401,7 @@ void __init early_setup(unsigned long dt_ptr) DBG(" -> early_setup()\n"); /* - * Fill the default DBG level in naca (do we want to keep + * Fill the default DBG level (do we want to keep * that old mecanism around forever ?) */ ppcdbg_initialize(); @@ -422,17 +455,17 @@ void __init early_setup(unsigned long dt_ptr) /* - * Initialize some remaining members of the naca and systemcfg structures + * Initialize some remaining members of the ppc64_caches 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) +static void __init initialize_cache_info(void) { struct device_node *np; unsigned long num_cpus = 0; - DBG(" -> initialize_naca()\n"); + DBG(" -> initialize_cache_info()\n"); for (np = NULL; (np = of_find_node_by_type(np, "cpu"));) { num_cpus += 1; @@ -463,15 +496,15 @@ static void __init initialize_naca(void) 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); + systemcfg->dcache_size = ppc64_caches.dsize = size; + systemcfg->dcache_line_size = + ppc64_caches.dline_size = lsize; + ppc64_caches.log_dline_size = __ilog2(lsize); + ppc64_caches.dlines_per_page = PAGE_SIZE / lsize; size = 0; lsize = cur_cpu_spec->icache_bsize; @@ -485,11 +518,11 @@ static void __init initialize_naca(void) 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); - + systemcfg->icache_size = ppc64_caches.isize = size; + systemcfg->icache_line_size = + ppc64_caches.iline_size = lsize; + ppc64_caches.log_iline_size = __ilog2(lsize); + ppc64_caches.ilines_per_page = PAGE_SIZE / lsize; } } @@ -499,7 +532,7 @@ static void __init initialize_naca(void) systemcfg->version.minor = SYSTEMCFG_MINOR; systemcfg->processor = mfspr(SPRN_PVR); - DBG(" <- initialize_naca()\n"); + DBG(" <- initialize_cache_info()\n"); } static void __init check_for_initrd(void) @@ -560,7 +593,7 @@ void __init setup_system(void) unflatten_device_tree(); /* - * Fill the naca & systemcfg structures with informations + * Fill the ppc64_caches & 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 @@ -569,7 +602,7 @@ void __init setup_system(void) * routines like flush_icache_range (used by the hash init * later on). */ - initialize_naca(); + initialize_cache_info(); #ifdef CONFIG_PPC_PSERIES /* @@ -620,22 +653,29 @@ void __init setup_system(void) * iSeries has already initialized the cpu maps at this point. */ setup_cpu_maps(); + + /* Release secondary cpus out of their spinloops at 0x60 now that + * we can map physical -> logical CPU ids + */ + smp_release_cpus(); #endif /* defined(CONFIG_SMP) && !defined(CONFIG_PPC_ISERIES) */ printk("Starting Linux PPC64 %s\n", UTS_RELEASE); printk("-----------------------------------------------------\n"); - printk("naca = 0x%p\n", naca); - printk("naca->pftSize = 0x%lx\n", naca->pftSize); - printk("naca->debug_switch = 0x%lx\n", naca->debug_switch); - printk("naca->interrupt_controller = 0x%ld\n", naca->interrupt_controller); + printk("ppc64_pft_size = 0x%lx\n", ppc64_pft_size); + printk("ppc64_debug_switch = 0x%lx\n", ppc64_debug_switch); + printk("ppc64_interrupt_controller = 0x%ld\n", ppc64_interrupt_controller); printk("systemcfg = 0x%p\n", systemcfg); + printk("systemcfg->platform = 0x%x\n", systemcfg->platform); printk("systemcfg->processorCount = 0x%lx\n", systemcfg->processorCount); printk("systemcfg->physicalMemorySize = 0x%lx\n", systemcfg->physicalMemorySize); - printk("systemcfg->dCacheL1LineSize = 0x%x\n", systemcfg->dCacheL1LineSize); - printk("systemcfg->iCacheL1LineSize = 0x%x\n", systemcfg->iCacheL1LineSize); - printk("htab_data.htab = 0x%p\n", htab_data.htab); - printk("htab_data.num_ptegs = 0x%lx\n", htab_data.htab_num_ptegs); + printk("ppc64_caches.dcache_line_size = 0x%x\n", + ppc64_caches.dline_size); + printk("ppc64_caches.icache_line_size = 0x%x\n", + ppc64_caches.iline_size); + printk("htab_address = 0x%p\n", htab_address); + printk("htab_hash_mask = 0x%lx\n", htab_hash_mask); printk("-----------------------------------------------------\n"); mm_init_ppc64(); @@ -783,12 +823,11 @@ early_param("mem", early_parsemem); #ifdef CONFIG_PPC_MULTIPLATFORM static int __init set_preferred_console(void) { - struct device_node *prom_stdout; + struct device_node *prom_stdout = NULL; char *name; + u32 *spd; 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. */ @@ -802,20 +841,7 @@ static int __init set_preferred_console(void) 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"); @@ -826,7 +852,6 @@ static int __init set_preferred_console(void) 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); @@ -834,8 +859,12 @@ static int __init set_preferred_console(void) DBG(" stdout package has no name !\n"); goto not_found; } + spd = (u32 *)get_property(prom_stdout, "current-speed", NULL); - if (strcmp(name, "serial") == 0) { + if (0) + ; +#ifdef CONFIG_SERIAL_8250_CONSOLE + else if (strcmp(name, "serial") == 0) { int i; u32 *reg = (u32 *)get_property(prom_stdout, "reg", &i); if (i > 8) { @@ -858,6 +887,7 @@ static int __init set_preferred_console(void) } } } +#endif /* CONFIG_SERIAL_8250_CONSOLE */ #ifdef CONFIG_PPC_PSERIES else if (strcmp(name, "vty") == 0) { u32 *reg = (u32 *)get_property(prom_stdout, "reg", NULL); @@ -887,17 +917,24 @@ static int __init set_preferred_console(void) } } #endif /* CONFIG_PPC_PSERIES */ +#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE else if (strcmp(name, "ch-a") == 0) offset = 0; else if (strcmp(name, "ch-b") == 0) offset = 1; +#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */ else goto not_found; of_node_put(prom_stdout); DBG("Found serial console at ttyS%d\n", offset); - return add_preferred_console("ttyS", offset, NULL); + if (spd) { + static char __initdata opt[16]; + sprintf(opt, "%d", *spd); + return add_preferred_console("ttyS", offset, opt); + } else + return add_preferred_console("ttyS", offset, NULL); not_found: DBG("No preferred console found !\n"); @@ -959,7 +996,6 @@ static void __init emergency_stack_init(void) */ void __init setup_arch(char **cmdline_p) { - extern int panic_timeout; extern void do_init_bootmem(void); ppc64_boot_msg(0x12, "Setup Arch"); @@ -971,8 +1007,8 @@ void __init setup_arch(char **cmdline_p) * Systems with OF can look in the properties on the cpu node(s) * for a possibly more accurate value. */ - dcache_bsize = systemcfg->dCacheL1LineSize; - icache_bsize = systemcfg->iCacheL1LineSize; + dcache_bsize = ppc64_caches.dline_size; + icache_bsize = ppc64_caches.iline_size; /* reboot on panic */ panic_timeout = 180; @@ -991,11 +1027,11 @@ void __init setup_arch(char **cmdline_p) /* set up the bootmem stuff with available memory */ do_init_bootmem(); + ppc_md.setup_arch(); + /* Select the correct idle loop for the platform. */ idle_setup(); - ppc_md.setup_arch(); - paging_init(); ppc64_boot_msg(0x15, "Setup Done"); } @@ -1106,15 +1142,212 @@ __setup("spread_lpevents=", set_spread_lpevents ); __setup("decr_overclock_proc0=", set_decr_overclock_proc0 ); __setup("decr_overclock=", set_decr_overclock ); +#ifndef CONFIG_PPC_ISERIES +/* + * This function can be used by platforms to "find" legacy serial ports. + * It works for "serial" nodes under an "isa" node, and will try to + * respect the "ibm,aix-loc" property if any. It works with up to 8 + * ports. + */ + +#define MAX_LEGACY_SERIAL_PORTS 8 +static struct plat_serial8250_port serial_ports[MAX_LEGACY_SERIAL_PORTS+1]; +static unsigned int old_serial_count; + +void __init generic_find_legacy_serial_ports(u64 *physport, + unsigned int *default_speed) +{ + struct device_node *np; + u32 *sizeprop; + + struct isa_reg_property { + u32 space; + u32 address; + u32 size; + }; + struct pci_reg_property { + struct pci_address addr; + u32 size_hi; + u32 size_lo; + }; + + DBG(" -> generic_find_legacy_serial_port()\n"); + + *physport = 0; + if (default_speed) + *default_speed = 0; + + np = of_find_node_by_path("/"); + if (!np) + return; + + /* First fill our array */ + for (np = NULL; (np = of_find_node_by_type(np, "serial"));) { + struct device_node *isa, *pci; + struct isa_reg_property *reg; + unsigned long phys_size, addr_size, io_base; + u32 *rangesp; + u32 *interrupts, *clk, *spd; + char *typep; + int index, rlen, rentsize; + + /* Ok, first check if it's under an "isa" parent */ + isa = of_get_parent(np); + if (!isa || strcmp(isa->name, "isa")) { + DBG("%s: no isa parent found\n", np->full_name); + continue; + } + + /* Now look for an "ibm,aix-loc" property that gives us ordering + * if any... + */ + typep = (char *)get_property(np, "ibm,aix-loc", NULL); + + /* Get the ISA port number */ + reg = (struct isa_reg_property *)get_property(np, "reg", NULL); + if (reg == NULL) + goto next_port; + /* We assume the interrupt number isn't translated ... */ + interrupts = (u32 *)get_property(np, "interrupts", NULL); + /* get clock freq. if present */ + clk = (u32 *)get_property(np, "clock-frequency", NULL); + /* get default speed if present */ + spd = (u32 *)get_property(np, "current-speed", NULL); + /* Default to locate at end of array */ + index = old_serial_count; /* end of the array by default */ + + /* If we have a location index, then use it */ + if (typep && *typep == 'S') { + index = simple_strtol(typep+1, NULL, 0) - 1; + /* if index is out of range, use end of array instead */ + if (index >= MAX_LEGACY_SERIAL_PORTS) + index = old_serial_count; + /* if our index is still out of range, that mean that + * array is full, we could scan for a free slot but that + * make little sense to bother, just skip the port + */ + if (index >= MAX_LEGACY_SERIAL_PORTS) + goto next_port; + if (index >= old_serial_count) + old_serial_count = index + 1; + /* Check if there is a port who already claimed our slot */ + if (serial_ports[index].iobase != 0) { + /* if we still have some room, move it, else override */ + if (old_serial_count < MAX_LEGACY_SERIAL_PORTS) { + DBG("Moved legacy port %d -> %d\n", index, + old_serial_count); + serial_ports[old_serial_count++] = + serial_ports[index]; + } else { + DBG("Replacing legacy port %d\n", index); + } + } + } + if (index >= MAX_LEGACY_SERIAL_PORTS) + goto next_port; + if (index >= old_serial_count) + old_serial_count = index + 1; + + /* Now fill the entry */ + memset(&serial_ports[index], 0, sizeof(struct plat_serial8250_port)); + serial_ports[index].uartclk = clk ? *clk : BASE_BAUD * 16; + serial_ports[index].iobase = reg->address; + serial_ports[index].irq = interrupts ? interrupts[0] : 0; + serial_ports[index].flags = ASYNC_BOOT_AUTOCONF; + + DBG("Added legacy port, index: %d, port: %x, irq: %d, clk: %d\n", + index, + serial_ports[index].iobase, + serial_ports[index].irq, + serial_ports[index].uartclk); + + /* Get phys address of IO reg for port 1 */ + if (index != 0) + goto next_port; + + pci = of_get_parent(isa); + if (!pci) { + DBG("%s: no pci parent found\n", np->full_name); + goto next_port; + } + + rangesp = (u32 *)get_property(pci, "ranges", &rlen); + if (rangesp == NULL) { + of_node_put(pci); + goto next_port; + } + rlen /= 4; + + /* we need the #size-cells of the PCI bridge node itself */ + phys_size = 1; + sizeprop = (u32 *)get_property(pci, "#size-cells", NULL); + if (sizeprop != NULL) + phys_size = *sizeprop; + /* we need the parent #addr-cells */ + addr_size = prom_n_addr_cells(pci); + rentsize = 3 + addr_size + phys_size; + io_base = 0; + for (;rlen >= rentsize; rlen -= rentsize,rangesp += rentsize) { + if (((rangesp[0] >> 24) & 0x3) != 1) + continue; /* not IO space */ + io_base = rangesp[3]; + if (addr_size == 2) + io_base = (io_base << 32) | rangesp[4]; + } + if (io_base != 0) { + *physport = io_base + reg->address; + if (default_speed && spd) + *default_speed = *spd; + } + of_node_put(pci); + next_port: + of_node_put(isa); + } + + DBG(" <- generic_find_legacy_serial_port()\n"); +} + +static struct platform_device serial_device = { + .name = "serial8250", + .id = 0, + .dev = { + .platform_data = serial_ports, + }, +}; + +static int __init serial_dev_init(void) +{ + return platform_device_register(&serial_device); +} +arch_initcall(serial_dev_init); + +#endif /* CONFIG_PPC_ISERIES */ + +int check_legacy_ioport(unsigned long base_port) +{ + if (ppc_md.check_legacy_ioport == NULL) + return 0; + return ppc_md.check_legacy_ioport(base_port); +} +EXPORT_SYMBOL(check_legacy_ioport); + #ifdef CONFIG_XMON static int __init early_xmon(char *p) { /* ensure xmon is enabled */ xmon_init(); - debugger(0); + debugger(NULL); return 0; } early_param("xmon", early_xmon); #endif +void cpu_die(void) +{ + idle_task_exit(); + if (ppc_md.cpu_die) + ppc_md.cpu_die(); + local_irq_disable(); + for (;;); +}