1468 lines · 39.4 KB · C
RawDownload
/* * Copyright 2004-2010 Analog Devices Inc. * * Licensed under the GPL-2 or later. */ #include <linux/delay.h>#include <linux/console.h>#include <linux/bootmem.h>#include <linux/seq_file.h>#include <linux/cpu.h>#include <linux/mm.h>#include <linux/module.h>#include <linux/tty.h>#include <linux/pfn.h> #ifdef CONFIG_MTD_UCLINUX#include <linux/mtd/map.h>#include <linux/ext2_fs.h>#include <linux/cramfs_fs.h>#include <linux/romfs_fs.h>#endif #include <asm/cplb.h>#include <asm/cacheflush.h>#include <asm/blackfin.h>#include <asm/cplbinit.h>#include <asm/clocks.h>#include <asm/div64.h>#include <asm/cpu.h>#include <asm/fixed_code.h>#include <asm/early_printk.h>#include <asm/irq_handler.h>#include <asm/pda.h>#ifdef CONFIG_BF60x#include <mach/pm.h>#endif#ifdef CONFIG_SCB_PRIORITY#include <asm/scb.h>#endif u16 _bfin_swrst;EXPORT_SYMBOL(_bfin_swrst); unsigned long memory_start, memory_end, physical_mem_end;unsigned long _rambase, _ramstart, _ramend;unsigned long reserved_mem_dcache_on;unsigned long reserved_mem_icache_on;EXPORT_SYMBOL(memory_start);EXPORT_SYMBOL(memory_end);EXPORT_SYMBOL(physical_mem_end);EXPORT_SYMBOL(_ramend);EXPORT_SYMBOL(reserved_mem_dcache_on); #ifdef CONFIG_MTD_UCLINUXextern struct map_info uclinux_ram_map;unsigned long memory_mtd_end, memory_mtd_start, mtd_size;EXPORT_SYMBOL(memory_mtd_end);EXPORT_SYMBOL(memory_mtd_start);EXPORT_SYMBOL(mtd_size);#endif char __initdata command_line[COMMAND_LINE_SIZE];struct blackfin_initial_pda __initdata initial_pda; /* boot memmap, for parsing "memmap=" */#define BFIN_MEMMAP_MAX		128 /* number of entries in bfin_memmap */#define BFIN_MEMMAP_RAM		1#define BFIN_MEMMAP_RESERVED	2static struct bfin_memmap {	int nr_map;	struct bfin_memmap_entry {		unsigned long long addr; /* start of memory segment */		unsigned long long size;		unsigned long type;	} map[BFIN_MEMMAP_MAX];} bfin_memmap __initdata; /* for memmap sanitization */struct change_member {	struct bfin_memmap_entry *pentry; /* pointer to original entry */	unsigned long long addr; /* address for this change point */};static struct change_member change_point_list[2*BFIN_MEMMAP_MAX] __initdata;static struct change_member *change_point[2*BFIN_MEMMAP_MAX] __initdata;static struct bfin_memmap_entry *overlap_list[BFIN_MEMMAP_MAX] __initdata;static struct bfin_memmap_entry new_map[BFIN_MEMMAP_MAX] __initdata; DEFINE_PER_CPU(struct blackfin_cpudata, cpu_data); static int early_init_clkin_hz(char *buf); #if defined(CONFIG_BFIN_DCACHE) || defined(CONFIG_BFIN_ICACHE)void __init generate_cplb_tables(void){	unsigned int cpu; 	generate_cplb_tables_all();	/* Generate per-CPU I&D CPLB tables */	for (cpu = 0; cpu < num_possible_cpus(); ++cpu)		generate_cplb_tables_cpu(cpu);}#endif void bfin_setup_caches(unsigned int cpu){#ifdef CONFIG_BFIN_ICACHE	bfin_icache_init(icplb_tbl[cpu]);#endif #ifdef CONFIG_BFIN_DCACHE	bfin_dcache_init(dcplb_tbl[cpu]);#endif 	bfin_setup_cpudata(cpu); 	/*	 * In cache coherence emulation mode, we need to have the	 * D-cache enabled before running any atomic operation which	 * might involve cache invalidation (i.e. spinlock, rwlock).	 * So printk's are deferred until then.	 */#ifdef CONFIG_BFIN_ICACHE	printk(KERN_INFO "Instruction Cache Enabled for CPU%u\n", cpu);	printk(KERN_INFO "  External memory:"# ifdef CONFIG_BFIN_EXTMEM_ICACHEABLE	       " cacheable"# else	       " uncacheable"# endif	       " in instruction cache\n");	if (L2_LENGTH)		printk(KERN_INFO "  L2 SRAM        :"# ifdef CONFIG_BFIN_L2_ICACHEABLE		       " cacheable"# else		       " uncacheable"# endif		       " in instruction cache\n"); #else	printk(KERN_INFO "Instruction Cache Disabled for CPU%u\n", cpu);#endif #ifdef CONFIG_BFIN_DCACHE	printk(KERN_INFO "Data Cache Enabled for CPU%u\n", cpu);	printk(KERN_INFO "  External memory:"# if defined CONFIG_BFIN_EXTMEM_WRITEBACK	       " cacheable (write-back)"# elif defined CONFIG_BFIN_EXTMEM_WRITETHROUGH	       " cacheable (write-through)"# else	       " uncacheable"# endif	       " in data cache\n");	if (L2_LENGTH)		printk(KERN_INFO "  L2 SRAM        :"# if defined CONFIG_BFIN_L2_WRITEBACK		       " cacheable (write-back)"# elif defined CONFIG_BFIN_L2_WRITETHROUGH		       " cacheable (write-through)"# else		       " uncacheable"# endif		       " in data cache\n");#else	printk(KERN_INFO "Data Cache Disabled for CPU%u\n", cpu);#endif} void bfin_setup_cpudata(unsigned int cpu){	struct blackfin_cpudata *cpudata = &per_cpu(cpu_data, cpu); 	cpudata->imemctl = bfin_read_IMEM_CONTROL();	cpudata->dmemctl = bfin_read_DMEM_CONTROL();} void __init bfin_cache_init(void){#if defined(CONFIG_BFIN_DCACHE) || defined(CONFIG_BFIN_ICACHE)	generate_cplb_tables();#endif	bfin_setup_caches(0);} void __init bfin_relocate_l1_mem(void){	unsigned long text_l1_len = (unsigned long)_text_l1_len;	unsigned long data_l1_len = (unsigned long)_data_l1_len;	unsigned long data_b_l1_len = (unsigned long)_data_b_l1_len;	unsigned long l2_len = (unsigned long)_l2_len; 	early_shadow_stamp(); 	/*	 * due to the ALIGN(4) in the arch/blackfin/kernel/vmlinux.lds.S	 * we know that everything about l1 text/data is nice and aligned,	 * so copy by 4 byte chunks, and don't worry about overlapping	 * src/dest.	 *	 * We can't use the dma_memcpy functions, since they can call	 * scheduler functions which might be in L1 :( and core writes	 * into L1 instruction cause bad access errors, so we are stuck,	 * we are required to use DMA, but can't use the common dma	 * functions. We can't use memcpy either - since that might be	 * going to be in the relocated L1	 */ 	blackfin_dma_early_init(); 	/* if necessary, copy L1 text to L1 instruction SRAM */	if (L1_CODE_LENGTH && text_l1_len)		early_dma_memcpy(_stext_l1, _text_l1_lma, text_l1_len); 	/* if necessary, copy L1 data to L1 data bank A SRAM */	if (L1_DATA_A_LENGTH && data_l1_len)		early_dma_memcpy(_sdata_l1, _data_l1_lma, data_l1_len); 	/* if necessary, copy L1 data B to L1 data bank B SRAM */	if (L1_DATA_B_LENGTH && data_b_l1_len)		early_dma_memcpy(_sdata_b_l1, _data_b_l1_lma, data_b_l1_len); 	early_dma_memcpy_done(); #if defined(CONFIG_SMP) && defined(CONFIG_ICACHE_FLUSH_L1)	blackfin_iflush_l1_entry[0] = (unsigned long)blackfin_icache_flush_range_l1;#endif 	/* if necessary, copy L2 text/data to L2 SRAM */	if (L2_LENGTH && l2_len)		memcpy(_stext_l2, _l2_lma, l2_len);} #ifdef CONFIG_SMPvoid __init bfin_relocate_coreb_l1_mem(void){	unsigned long text_l1_len = (unsigned long)_text_l1_len;	unsigned long data_l1_len = (unsigned long)_data_l1_len;	unsigned long data_b_l1_len = (unsigned long)_data_b_l1_len; 	blackfin_dma_early_init(); 	/* if necessary, copy L1 text to L1 instruction SRAM */	if (L1_CODE_LENGTH && text_l1_len)		early_dma_memcpy((void *)COREB_L1_CODE_START, _text_l1_lma,				text_l1_len); 	/* if necessary, copy L1 data to L1 data bank A SRAM */	if (L1_DATA_A_LENGTH && data_l1_len)		early_dma_memcpy((void *)COREB_L1_DATA_A_START, _data_l1_lma,				data_l1_len); 	/* if necessary, copy L1 data B to L1 data bank B SRAM */	if (L1_DATA_B_LENGTH && data_b_l1_len)		early_dma_memcpy((void *)COREB_L1_DATA_B_START, _data_b_l1_lma,				data_b_l1_len); 	early_dma_memcpy_done(); #ifdef CONFIG_ICACHE_FLUSH_L1	blackfin_iflush_l1_entry[1] = (unsigned long)blackfin_icache_flush_range_l1 -			(unsigned long)_stext_l1 + COREB_L1_CODE_START;#endif}#endif #ifdef CONFIG_ROMKERNELvoid __init bfin_relocate_xip_data(void){	early_shadow_stamp(); 	memcpy(_sdata, _data_lma, (unsigned long)_data_len - THREAD_SIZE + sizeof(struct thread_info));	memcpy(_sinitdata, _init_data_lma, (unsigned long)_init_data_len);}#endif /* add_memory_region to memmap */static void __init add_memory_region(unsigned long long start,			      unsigned long long size, int type){	int i; 	i = bfin_memmap.nr_map; 	if (i == BFIN_MEMMAP_MAX) {		printk(KERN_ERR "Ooops! Too many entries in the memory map!\n");		return;	} 	bfin_memmap.map[i].addr = start;	bfin_memmap.map[i].size = size;	bfin_memmap.map[i].type = type;	bfin_memmap.nr_map++;} /* * Sanitize the boot memmap, removing overlaps. */static int __init sanitize_memmap(struct bfin_memmap_entry *map, int *pnr_map){	struct change_member *change_tmp;	unsigned long current_type, last_type;	unsigned long long last_addr;	int chgidx, still_changing;	int overlap_entries;	int new_entry;	int old_nr, new_nr, chg_nr;	int i; 	/*		Visually we're performing the following (1,2,3,4 = memory types) 		Sample memory map (w/overlaps):		   ____22__________________		   ______________________4_		   ____1111________________		   _44_____________________		   11111111________________		   ____________________33__		   ___________44___________		   __________33333_________		   ______________22________		   ___________________2222_		   _________111111111______		   _____________________11_		   _________________4______ 		Sanitized equivalent (no overlap):		   1_______________________		   _44_____________________		   ___1____________________		   ____22__________________		   ______11________________		   _________1______________		   __________3_____________		   ___________44___________		   _____________33_________		   _______________2________		   ________________1_______		   _________________4______		   ___________________2____		   ____________________33__		   ______________________4_	*/	/* if there's only one memory region, don't bother */	if (*pnr_map < 2)		return -1; 	old_nr = *pnr_map; 	/* bail out if we find any unreasonable addresses in memmap */	for (i = 0; i < old_nr; i++)		if (map[i].addr + map[i].size < map[i].addr)			return -1; 	/* create pointers for initial change-point information (for sorting) */	for (i = 0; i < 2*old_nr; i++)		change_point[i] = &change_point_list[i]; 	/* record all known change-points (starting and ending addresses),	   omitting those that are for empty memory regions */	chgidx = 0;	for (i = 0; i < old_nr; i++) {		if (map[i].size != 0) {			change_point[chgidx]->addr = map[i].addr;			change_point[chgidx++]->pentry = &map[i];			change_point[chgidx]->addr = map[i].addr + map[i].size;			change_point[chgidx++]->pentry = &map[i];		}	}	chg_nr = chgidx;	/* true number of change-points */ 	/* sort change-point list by memory addresses (low -> high) */	still_changing = 1;	while (still_changing) {		still_changing = 0;		for (i = 1; i < chg_nr; i++) {			/* if <current_addr> > <last_addr>, swap */			/* or, if current=<start_addr> & last=<end_addr>, swap */			if ((change_point[i]->addr < change_point[i-1]->addr) ||				((change_point[i]->addr == change_point[i-1]->addr) &&				 (change_point[i]->addr == change_point[i]->pentry->addr) &&				 (change_point[i-1]->addr != change_point[i-1]->pentry->addr))			   ) {				change_tmp = change_point[i];				change_point[i] = change_point[i-1];				change_point[i-1] = change_tmp;				still_changing = 1;			}		}	} 	/* create a new memmap, removing overlaps */	overlap_entries = 0;	/* number of entries in the overlap table */	new_entry = 0;		/* index for creating new memmap entries */	last_type = 0;		/* start with undefined memory type */	last_addr = 0;		/* start with 0 as last starting address */	/* loop through change-points, determining affect on the new memmap */	for (chgidx = 0; chgidx < chg_nr; chgidx++) {		/* keep track of all overlapping memmap entries */		if (change_point[chgidx]->addr == change_point[chgidx]->pentry->addr) {			/* add map entry to overlap list (> 1 entry implies an overlap) */			overlap_list[overlap_entries++] = change_point[chgidx]->pentry;		} else {			/* remove entry from list (order independent, so swap with last) */			for (i = 0; i < overlap_entries; i++) {				if (overlap_list[i] == change_point[chgidx]->pentry)					overlap_list[i] = overlap_list[overlap_entries-1];			}			overlap_entries--;		}		/* if there are overlapping entries, decide which "type" to use */		/* (larger value takes precedence -- 1=usable, 2,3,4,4+=unusable) */		current_type = 0;		for (i = 0; i < overlap_entries; i++)			if (overlap_list[i]->type > current_type)				current_type = overlap_list[i]->type;		/* continue building up new memmap based on this information */		if (current_type != last_type) {			if (last_type != 0) {				new_map[new_entry].size =					change_point[chgidx]->addr - last_addr;				/* move forward only if the new size was non-zero */				if (new_map[new_entry].size != 0)					if (++new_entry >= BFIN_MEMMAP_MAX)						break;	/* no more space left for new entries */			}			if (current_type != 0) {				new_map[new_entry].addr = change_point[chgidx]->addr;				new_map[new_entry].type = current_type;				last_addr = change_point[chgidx]->addr;			}			last_type = current_type;		}	}	new_nr = new_entry;	/* retain count for new entries */ 	/* copy new mapping into original location */	memcpy(map, new_map, new_nr*sizeof(struct bfin_memmap_entry));	*pnr_map = new_nr; 	return 0;} static void __init print_memory_map(char *who){	int i; 	for (i = 0; i < bfin_memmap.nr_map; i++) {		printk(KERN_DEBUG " %s: %016Lx - %016Lx ", who,			bfin_memmap.map[i].addr,			bfin_memmap.map[i].addr + bfin_memmap.map[i].size);		switch (bfin_memmap.map[i].type) {		case BFIN_MEMMAP_RAM:			printk(KERN_CONT "(usable)\n");			break;		case BFIN_MEMMAP_RESERVED:			printk(KERN_CONT "(reserved)\n");			break;		default:			printk(KERN_CONT "type %lu\n", bfin_memmap.map[i].type);			break;		}	}} static __init int parse_memmap(char *arg){	unsigned long long start_at, mem_size; 	if (!arg)		return -EINVAL; 	mem_size = memparse(arg, &arg);	if (*arg == '@') {		start_at = memparse(arg+1, &arg);		add_memory_region(start_at, mem_size, BFIN_MEMMAP_RAM);	} else if (*arg == '$') {		start_at = memparse(arg+1, &arg);		add_memory_region(start_at, mem_size, BFIN_MEMMAP_RESERVED);	} 	return 0;} /* * Initial parsing of the command line.  Currently, we support: *  - Controlling the linux memory size: mem=xxx[KMG] *  - Controlling the physical memory size: max_mem=xxx[KMG][$][#] *       $ -> reserved memory is dcacheable *       # -> reserved memory is icacheable *  - "memmap=XXX[KkmM][@][$]XXX[KkmM]" defines a memory region *       @ from <start> to <start>+<mem>, type RAM *       $ from <start> to <start>+<mem>, type RESERVED */static __init void parse_cmdline_early(char *cmdline_p){	char c = ' ', *to = cmdline_p;	unsigned int memsize;

Lines 1–500 of 1468.