970 lines · 23.2 KB · C
RawDownload
/* * Common boot and setup code for both 32-bit and 64-bit. * Extracted from arch/powerpc/kernel/setup_64.c. * * Copyright (C) 2001 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. */ #undef DEBUG #include <linux/export.h>#include <linux/string.h>#include <linux/sched.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/reboot.h>#include <linux/delay.h>#include <linux/initrd.h>#include <linux/platform_device.h>#include <linux/seq_file.h>#include <linux/ioport.h>#include <linux/console.h>#include <linux/screen_info.h>#include <linux/root_dev.h>#include <linux/notifier.h>#include <linux/cpu.h>#include <linux/unistd.h>#include <linux/serial.h>#include <linux/serial_8250.h>#include <linux/percpu.h>#include <linux/memblock.h>#include <linux/of_platform.h>#include <linux/hugetlb.h>#include <asm/debugfs.h>#include <asm/io.h>#include <asm/paca.h>#include <asm/prom.h>#include <asm/processor.h>#include <asm/vdso_datapage.h>#include <asm/pgtable.h>#include <asm/smp.h>#include <asm/elf.h>#include <asm/machdep.h>#include <asm/time.h>#include <asm/cputable.h>#include <asm/sections.h>#include <asm/firmware.h>#include <asm/btext.h>#include <asm/nvram.h>#include <asm/setup.h>#include <asm/rtas.h>#include <asm/iommu.h>#include <asm/serial.h>#include <asm/cache.h>#include <asm/page.h>#include <asm/mmu.h>#include <asm/xmon.h>#include <asm/cputhreads.h>#include <mm/mmu_decl.h>#include <asm/fadump.h>#include <asm/udbg.h>#include <asm/hugetlb.h>#include <asm/livepatch.h>#include <asm/mmu_context.h>#include <asm/cpu_has_feature.h> #include "setup.h" #ifdef DEBUG#include <asm/udbg.h>#define DBG(fmt...) udbg_printf(fmt)#else#define DBG(fmt...)#endif /* The main machine-dep calls structure */struct machdep_calls ppc_md;EXPORT_SYMBOL(ppc_md);struct machdep_calls *machine_id;EXPORT_SYMBOL(machine_id); int boot_cpuid = -1;EXPORT_SYMBOL_GPL(boot_cpuid); /* * 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;  unsigned long klimit = (unsigned long) _end; /* * This still seems to be needed... -- paulus */ struct screen_info screen_info = {	.orig_x = 0,	.orig_y = 25,	.orig_video_cols = 80,	.orig_video_lines = 25,	.orig_video_isVGA = 1,	.orig_video_points = 16};#if defined(CONFIG_FB_VGA16_MODULE)EXPORT_SYMBOL(screen_info);#endif /* Variables required to store legacy IO irq routing */int of_i8042_kbd_irq;EXPORT_SYMBOL_GPL(of_i8042_kbd_irq);int of_i8042_aux_irq;EXPORT_SYMBOL_GPL(of_i8042_aux_irq); #ifdef __DO_IRQ_CANON/* XXX should go elsewhere eventually */int ppc_do_canonicalize_irqs;EXPORT_SYMBOL(ppc_do_canonicalize_irqs);#endif #ifdef CONFIG_CRASH_CORE/* This keeps a track of which one is the crashing cpu. */int crashing_cpu = -1;#endif /* also used by kexec */void machine_shutdown(void){#ifdef CONFIG_FA_DUMP	/*	 * if fadump is active, cleanup the fadump registration before we	 * shutdown.	 */	fadump_cleanup();#endif 	if (ppc_md.machine_shutdown)		ppc_md.machine_shutdown();} static void machine_hang(void){	pr_emerg("System Halted, OK to turn off power\n");	local_irq_disable();	while (1)		;} void machine_restart(char *cmd){	machine_shutdown();	if (ppc_md.restart)		ppc_md.restart(cmd); 	smp_send_stop(); 	do_kernel_restart(cmd);	mdelay(1000); 	machine_hang();} void machine_power_off(void){	machine_shutdown();	if (pm_power_off)		pm_power_off(); 	smp_send_stop();	machine_hang();}/* Used by the G5 thermal driver */EXPORT_SYMBOL_GPL(machine_power_off); void (*pm_power_off)(void);EXPORT_SYMBOL_GPL(pm_power_off); void machine_halt(void){	machine_shutdown();	if (ppc_md.halt)		ppc_md.halt(); 	smp_send_stop();	machine_hang();}  #ifdef CONFIG_TAUextern u32 cpu_temp(unsigned long cpu);extern u32 cpu_temp_both(unsigned long cpu);#endif /* CONFIG_TAU */ #ifdef CONFIG_SMPDEFINE_PER_CPU(unsigned int, cpu_pvr);#endif static void show_cpuinfo_summary(struct seq_file *m){	struct device_node *root;	const char *model = NULL;#if defined(CONFIG_SMP) && defined(CONFIG_PPC32)	unsigned long bogosum = 0;	int i;	for_each_online_cpu(i)		bogosum += loops_per_jiffy;	seq_printf(m, "total bogomips\t: %lu.%02lu\n",		   bogosum/(500000/HZ), bogosum/(5000/HZ) % 100);#endif /* CONFIG_SMP && CONFIG_PPC32 */	seq_printf(m, "timebase\t: %lu\n", ppc_tb_freq);	if (ppc_md.name)		seq_printf(m, "platform\t: %s\n", ppc_md.name);	root = of_find_node_by_path("/");	if (root)		model = of_get_property(root, "model", NULL);	if (model)		seq_printf(m, "model\t\t: %s\n", model);	of_node_put(root); 	if (ppc_md.show_cpuinfo != NULL)		ppc_md.show_cpuinfo(m); #ifdef CONFIG_PPC32	/* Display the amount of memory */	seq_printf(m, "Memory\t\t: %d MB\n",		   (unsigned int)(total_memory / (1024 * 1024)));#endif} static int show_cpuinfo(struct seq_file *m, void *v){	unsigned long cpu_id = (unsigned long)v - 1;	unsigned int pvr;	unsigned long proc_freq;	unsigned short maj;	unsigned short min; 	/* We only show online cpus: disable preempt (overzealous, I	 * knew) to prevent cpu going down. */	preempt_disable();	if (!cpu_online(cpu_id)) {		preempt_enable();		return 0;	} #ifdef CONFIG_SMP	pvr = per_cpu(cpu_pvr, cpu_id);#else	pvr = mfspr(SPRN_PVR);#endif	maj = (pvr >> 8) & 0xFF;	min = pvr & 0xFF; 	seq_printf(m, "processor\t: %lu\n", cpu_id);	seq_printf(m, "cpu\t\t: "); 	if (cur_cpu_spec->pvr_mask && cur_cpu_spec->cpu_name)		seq_printf(m, "%s", cur_cpu_spec->cpu_name);	else		seq_printf(m, "unknown (%08x)", pvr); #ifdef CONFIG_ALTIVEC	if (cpu_has_feature(CPU_FTR_ALTIVEC))		seq_printf(m, ", altivec supported");#endif /* CONFIG_ALTIVEC */ 	seq_printf(m, "\n"); #ifdef CONFIG_TAU	if (cur_cpu_spec->cpu_features & CPU_FTR_TAU) {#ifdef CONFIG_TAU_AVERAGE		/* more straightforward, but potentially misleading */		seq_printf(m,  "temperature \t: %u C (uncalibrated)\n",			   cpu_temp(cpu_id));#else		/* show the actual temp sensor range */		u32 temp;		temp = cpu_temp_both(cpu_id);		seq_printf(m, "temperature \t: %u-%u C (uncalibrated)\n",			   temp & 0xff, temp >> 16);#endif	}#endif /* CONFIG_TAU */ 	/*	 * Platforms that have variable clock rates, should implement	 * the method ppc_md.get_proc_freq() that reports the clock	 * rate of a given cpu. The rest can use ppc_proc_freq to	 * report the clock rate that is same across all cpus.	 */	if (ppc_md.get_proc_freq)		proc_freq = ppc_md.get_proc_freq(cpu_id);	else		proc_freq = ppc_proc_freq; 	if (proc_freq)		seq_printf(m, "clock\t\t: %lu.%06luMHz\n",			   proc_freq / 1000000, proc_freq % 1000000); 	if (ppc_md.show_percpuinfo != NULL)		ppc_md.show_percpuinfo(m, cpu_id); 	/* If we are a Freescale core do a simple check so	 * we dont have to keep adding cases in the future */	if (PVR_VER(pvr) & 0x8000) {		switch (PVR_VER(pvr)) {		case 0x8000:	/* 7441/7450/7451, Voyager */		case 0x8001:	/* 7445/7455, Apollo 6 */		case 0x8002:	/* 7447/7457, Apollo 7 */		case 0x8003:	/* 7447A, Apollo 7 PM */		case 0x8004:	/* 7448, Apollo 8 */		case 0x800c:	/* 7410, Nitro */			maj = ((pvr >> 8) & 0xF);			min = PVR_MIN(pvr);			break;		default:	/* e500/book-e */			maj = PVR_MAJ(pvr);			min = PVR_MIN(pvr);			break;		}	} else {		switch (PVR_VER(pvr)) {			case 0x0020:	/* 403 family */				maj = PVR_MAJ(pvr) + 1;				min = PVR_MIN(pvr);				break;			case 0x1008:	/* 740P/750P ?? */				maj = ((pvr >> 8) & 0xFF) - 1;				min = pvr & 0xFF;				break;			case 0x004e: /* POWER9 bits 12-15 give chip type */				maj = (pvr >> 8) & 0x0F;				min = pvr & 0xFF;				break;			default:				maj = (pvr >> 8) & 0xFF;				min = pvr & 0xFF;				break;		}	} 	seq_printf(m, "revision\t: %hd.%hd (pvr %04x %04x)\n",		   maj, min, PVR_VER(pvr), PVR_REV(pvr)); #ifdef CONFIG_PPC32	seq_printf(m, "bogomips\t: %lu.%02lu\n",		   loops_per_jiffy / (500000/HZ),		   (loops_per_jiffy / (5000/HZ)) % 100);#endif #ifdef CONFIG_SMP	seq_printf(m, "\n");#endif 	preempt_enable(); 	/* If this is the last cpu, print the summary */	if (cpumask_next(cpu_id, cpu_online_mask) >= nr_cpu_ids)		show_cpuinfo_summary(m); 	return 0;} static void *c_start(struct seq_file *m, loff_t *pos){	if (*pos == 0)	/* just in case, cpu 0 is not the first */		*pos = cpumask_first(cpu_online_mask);	else		*pos = cpumask_next(*pos - 1, cpu_online_mask);	if ((*pos) < nr_cpu_ids)		return (void *)(unsigned long)(*pos + 1);	return NULL;} static void *c_next(struct seq_file *m, void *v, loff_t *pos){	(*pos)++;	return c_start(m, pos);} static void c_stop(struct seq_file *m, void *v){} const struct seq_operations cpuinfo_op = {	.start =c_start,	.next =	c_next,	.stop =	c_stop,	.show =	show_cpuinfo,}; void __init check_for_initrd(void){#ifdef CONFIG_BLK_DEV_INITRD	DBG(" -> check_for_initrd()  initrd_start=0x%lx  initrd_end=0x%lx\n",	    initrd_start, initrd_end); 	/* If we were passed an initrd, set the ROOT_DEV properly if the values	 * look sensible. If not, clear initrd reference.	 */	if (is_kernel_addr(initrd_start) && is_kernel_addr(initrd_end) &&	    initrd_end > initrd_start)		ROOT_DEV = Root_RAM0;	else		initrd_start = initrd_end = 0; 	if (initrd_start)		pr_info("Found initrd at 0x%lx:0x%lx\n", initrd_start, initrd_end); 	DBG(" <- check_for_initrd()\n");#endif /* CONFIG_BLK_DEV_INITRD */} #ifdef CONFIG_SMP int threads_per_core, threads_per_subcore, threads_shift;cpumask_t threads_core_mask;EXPORT_SYMBOL_GPL(threads_per_core);EXPORT_SYMBOL_GPL(threads_per_subcore);EXPORT_SYMBOL_GPL(threads_shift);EXPORT_SYMBOL_GPL(threads_core_mask); static void __init cpu_init_thread_core_maps(int tpc){	int i; 	threads_per_core = tpc;	threads_per_subcore = tpc;	cpumask_clear(&threads_core_mask); 	/* This implementation only supports power of 2 number of threads	 * for simplicity and performance	 */	threads_shift = ilog2(tpc);	BUG_ON(tpc != (1 << threads_shift)); 	for (i = 0; i < tpc; i++)		cpumask_set_cpu(i, &threads_core_mask); 	printk(KERN_INFO "CPU maps initialized for %d thread%s per core\n",	       tpc, tpc > 1 ? "s" : "");	printk(KERN_DEBUG " (thread shift is %d)\n", threads_shift);}  /** * setup_cpu_maps - initialize the following cpu maps: *                  cpu_possible_mask *                  cpu_present_mask * * Having the possible map set up early allows us to restrict allocations * of things like irqstacks to nr_cpu_ids rather than NR_CPUS. * * We do not initialize the online map here; cpus set their own bits in * cpu_online_mask 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. * * NOTE: This must match the parsing done in early_init_dt_scan_cpus. */void __init smp_setup_cpu_maps(void){	struct device_node *dn = NULL;	int cpu = 0;	int nthreads = 1; 	DBG("smp_setup_cpu_maps()\n"); 	while ((dn = of_find_node_by_type(dn, "cpu")) && cpu < nr_cpu_ids) {		const __be32 *intserv;		__be32 cpu_be;		int j, len; 		DBG("  * %s...\n", dn->full_name); 		intserv = of_get_property(dn, "ibm,ppc-interrupt-server#s",				&len);		if (intserv) {			DBG("    ibm,ppc-interrupt-server#s -> %d threads\n",			    nthreads);		} else {			DBG("    no ibm,ppc-interrupt-server#s -> 1 thread\n");			intserv = of_get_property(dn, "reg", &len);			if (!intserv) {				cpu_be = cpu_to_be32(cpu);				intserv = &cpu_be;	/* assume logical == phys */				len = 4;			}		} 

Lines 1–500 of 970.