diff options
Diffstat (limited to 'cpu')
| -rw-r--r-- | cpu/blackfin/Makefile | 2 | ||||
| -rw-r--r-- | cpu/blackfin/cpu.c | 40 | ||||
| -rw-r--r-- | cpu/blackfin/flush.S | 230 | ||||
| -rw-r--r-- | cpu/blackfin/serial.c | 72 | ||||
| -rw-r--r-- | cpu/blackfin/serial.h | 8 | ||||
| -rw-r--r-- | cpu/blackfin/start.S | 27 | ||||
| -rw-r--r-- | cpu/blackfin/traps.c | 54 | 
7 files changed, 128 insertions, 305 deletions
diff --git a/cpu/blackfin/Makefile b/cpu/blackfin/Makefile index f194a3835..8fed4b422 100644 --- a/cpu/blackfin/Makefile +++ b/cpu/blackfin/Makefile @@ -16,7 +16,7 @@ LIB	= $(obj)lib$(CPU).a  EXTRA    :=  CEXTRA   := initcode.o  SEXTRA   := start.o -SOBJS    := interrupt.o cache.o flush.o +SOBJS    := interrupt.o cache.o  COBJS    := cpu.o traps.o interrupts.o reset.o serial.o i2c.o watchdog.o  ifeq ($(CONFIG_BFIN_BOOT_MODE),BFIN_BOOT_BYPASS) diff --git a/cpu/blackfin/cpu.c b/cpu/blackfin/cpu.c index 53de5aba6..9efd88e7e 100644 --- a/cpu/blackfin/cpu.c +++ b/cpu/blackfin/cpu.c @@ -14,46 +14,11 @@  #include <asm/blackfin.h>  #include <asm/cplb.h>  #include <asm/mach-common/bits/core.h> -#include <asm/mach-common/bits/mpu.h>  #include <asm/mach-common/bits/trace.h>  #include "cpu.h"  #include "serial.h" -void icache_enable(void) -{ -	bfin_write_IMEM_CONTROL(bfin_read_IMEM_CONTROL() | (IMC | ENICPLB)); -	SSYNC(); -} - -void icache_disable(void) -{ -	bfin_write_IMEM_CONTROL(bfin_read_IMEM_CONTROL() & ~(IMC | ENICPLB)); -	SSYNC(); -} - -int icache_status(void) -{ -	return bfin_read_IMEM_CONTROL() & ENICPLB; -} - -void dcache_enable(void) -{ -	bfin_write_DMEM_CONTROL(bfin_read_DMEM_CONTROL() | (ACACHE_BCACHE | ENDCPLB | PORT_PREF0)); -	SSYNC(); -} - -void dcache_disable(void) -{ -	bfin_write_DMEM_CONTROL(bfin_read_DMEM_CONTROL() & ~(ACACHE_BCACHE | ENDCPLB | PORT_PREF0)); -	SSYNC(); -} - -int dcache_status(void) -{ -	return bfin_read_DMEM_CONTROL() & ENDCPLB; -} -  __attribute__ ((__noreturn__))  void cpu_init_f(ulong bootflag, ulong loaded_from_ldr)  { @@ -133,9 +98,8 @@ int irq_init(void)  	bfin_write_EVT15(evt_default);  	bfin_write_ILAT(0);  	CSYNC(); -	/* enable all interrupts except for core timer */ -	irq_flags = 0xffffffbf; +	/* enable hardware error irq */ +	irq_flags = 0x3f;  	local_irq_enable(); -	CSYNC();  	return 0;  } diff --git a/cpu/blackfin/flush.S b/cpu/blackfin/flush.S deleted file mode 100644 index 417f798f8..000000000 --- a/cpu/blackfin/flush.S +++ /dev/null @@ -1,230 +0,0 @@ -/* flush.S - low level cache flushing routines - * Copyright (C) 2003-2007 Analog Devices Inc. - * Licensed under the GPL-2 or later. - */ - -#include <config.h> -#include <asm/blackfin.h> -#include <asm/cplb.h> -#include <asm/mach-common/bits/mpu.h> - -.text - -/* This is an external function being called by the user - * application through __flush_cache_all. Currently this function - * serves the purpose of flushing all the pending writes in - * in the data cache. - */ - -ENTRY(_flush_data_cache) -	[--SP] = ( R7:6, P5:4 ); -	LINK 12; -	SP += -12; -	P5.H = HI(DCPLB_ADDR0); -	P5.L = LO(DCPLB_ADDR0); -	P4.H = HI(DCPLB_DATA0); -	P4.L = LO(DCPLB_DATA0); -	R7 = CPLB_VALID | CPLB_L1_CHBL | CPLB_DIRTY (Z); -	R6 = 16; -.Lnext:	R0 = [P5++]; -	R1 = [P4++]; -	CC = BITTST(R1, 14);	/* Is it write-through?*/ -	IF CC JUMP .Lskip;	/* If so, ignore it.*/ -	R2 = R1 & R7;		/* Is it a dirty, cached page?*/ -	CC = R2; -	IF !CC JUMP .Lskip;	/* If not, ignore it.*/ -	[--SP] = RETS; -	CALL _dcplb_flush;	/* R0 = page, R1 = data*/ -	RETS = [SP++]; -.Lskip:	R6 += -1; -	CC = R6; -	IF CC JUMP .Lnext; -	SSYNC; -	SP += 12; -	UNLINK; -	( R7:6, P5:4 ) = [SP++]; -	RTS; -ENDPROC(_flush_data_cache) - -/* This is an internal function to flush all pending - * writes in the cache associated with a particular DCPLB. - * - * R0 -  page's start address - * R1 -  CPLB's data field. - */ - -.align 2 -ENTRY(_dcplb_flush) -	[--SP] = ( R7:0, P5:0 ); -	[--SP] = LC0; -	[--SP] = LT0; -	[--SP] = LB0; -	[--SP] = LC1; -	[--SP] = LT1; -	[--SP] = LB1; - -	/* If it's a 1K or 4K page, then it's quickest to -	 * just systematically flush all the addresses in -	 * the page, regardless of whether they're in the -	 * cache, or dirty. If it's a 1M or 4M page, there -	 * are too many addresses, and we have to search the -	 * cache for lines corresponding to the page. -	 */ - -	CC = BITTST(R1, 17);	/* 1MB or 4MB */ -	IF !CC JUMP .Ldflush_whole_page; - -	/* We're only interested in the page's size, so extract -	 * this from the CPLB (bits 17:16), and scale to give an -	 * offset into the page_size and page_prefix tables. -	 */ - -	R1 <<= 14; -	R1 >>= 30; -	R1 <<= 2; - -	/* The page could be mapped into Bank A or Bank B, depending -	 * on (a) whether both banks are configured as cache, and -	 * (b) on whether address bit A[x] is set. x is determined -	 * by DCBS in DMEM_CONTROL -	 */ - -	R2 = 0;			/* Default to Bank A (Bank B would be 1)*/ - -	P0.L = LO(DMEM_CONTROL); -	P0.H = HI(DMEM_CONTROL); - -	R3 = [P0];		/* If Bank B is not enabled as cache*/ -	CC = BITTST(R3, 2);	/* then Bank A is our only option.*/ -	IF CC JUMP .Lbank_chosen; - -	R4 = 1<<14;		/* If DCBS==0, use A[14].*/ -	R5 = R4 << 7;		/* If DCBS==1, use A[23];*/ -	CC = BITTST(R3, 4); -	IF CC R4 = R5;		/* R4 now has either bit 14 or bit 23 set.*/ -	R5 = R0 & R4;		/* Use it to test the Page address*/ -	CC = R5;		/* and if that bit is set, we use Bank B,*/ -	R2 = CC;		/* else we use Bank A.*/ -	R2 <<= 23;		/* The Bank selection's at posn 23.*/ - -.Lbank_chosen: - -	/* We can also determine the sub-bank used, because this is -	 * taken from bits 13:12 of the address. -	 */ - -	R3 = ((12<<8)|2);		/* Extraction pattern */ -	nop;				/*Anamoly 05000209*/ -	R4 = EXTRACT(R0, R3.L) (Z);	/* Extract bits*/ -	/* Save in extraction pattern for later deposit.*/ -	R3.H = R4.L << 0; - -	/* So: -	 * R0 = Page start -	 * R1 = Page length (actually, offset into size/prefix tables) -	 * R2 = Bank select mask -	 * R3 = sub-bank deposit values -	 * -	 * The cache has 2 Ways, and 64 sets, so we iterate through -	 * the sets, accessing the tag for each Way, for our Bank and -	 * sub-bank, looking for dirty, valid tags that match our -	 * address prefix. -	 */ - -	P5.L = LO(DTEST_COMMAND); -	P5.H = HI(DTEST_COMMAND); -	P4.L = LO(DTEST_DATA0); -	P4.H = HI(DTEST_DATA0); - -	P0.L = page_prefix_table; -	P0.H = page_prefix_table; -	P1 = R1; -	R5 = 0;			/* Set counter*/ -	P0 = P1 + P0; -	R4 = [P0];		/* This is the address prefix*/ - - -	/* We're reading (bit 1==0) the tag (bit 2==0), and we -	 * don't care about which double-word, since we're only -	 * fetching tags, so we only have to set Set, Bank, -	 * Sub-bank and Way. -	 */ - -	P2 = 2; -	LSETUP (.Lfs1, .Lfe1) LC1 = P2; -.Lfs1:	P0 = 64;		/* iterate over all sets*/ -	LSETUP (.Lfs0, .Lfe0) LC0 = P0; -.Lfs0:	R6 = R5 << 5;		/* Combine set*/ -	R6.H = R3.H << 0 ;	/* and sub-bank*/ -	R6 = R6 | R2;		/* and Bank. Leave Way==0 at first.*/ -	BITSET(R6,14); -	[P5] = R6;		/* Issue Command*/ -	SSYNC; -	R7 = [P4];		/* and read Tag.*/ -	CC = BITTST(R7, 0);	/* Check if valid*/ -	IF !CC JUMP .Lfskip;	/* and skip if not.*/ -	CC = BITTST(R7, 1);	/* Check if dirty*/ -	IF !CC JUMP .Lfskip;	/* and skip if not.*/ - -	/* Compare against the page address. First, plant bits 13:12 -	 * into the tag, since those aren't part of the returned data. -	 */ - -	R7 = DEPOSIT(R7, R3);	/* set 13:12*/ -	R1 = R7 & R4;		/* Mask off lower bits*/ -	CC = R1 == R0;		/* Compare against page start.*/ -	IF !CC JUMP .Lfskip;	/* Skip it if it doesn't match.*/ - -	/* Tag address matches against page, so this is an entry -	 * we must flush. -	 */ - -	R7 >>= 10;		/* Mask off the non-address bits*/ -	R7 <<= 10; -	P3 = R7; -	SSYNC; -	FLUSHINV [P3];		/* And flush the entry*/ -.Lfskip: -.Lfe0:	R5 += 1;		/* Advance to next Set*/ -.Lfe1:	BITSET(R2, 26);		/* Go to next Way.*/ - -.Ldfinished: -	SSYNC;			/* Ensure the data gets out to mem.*/ - -	/*Finished. Restore context.*/ -	LB1 = [SP++]; -	LT1 = [SP++]; -	LC1 = [SP++]; -	LB0 = [SP++]; -	LT0 = [SP++]; -	LC0 = [SP++]; -	( R7:0, P5:0 ) = [SP++]; -	RTS; - -.Ldflush_whole_page: - -	/* It's a 1K or 4K page, so quicker to just flush the -	 * entire page. -	 */ - -	P1 = 32;		/* For 1K pages*/ -	P2 = P1 << 2;		/* For 4K pages*/ -	P0 = R0;		/* Start of page*/ -	CC = BITTST(R1, 16);	/* Whether 1K or 4K*/ -	IF CC P1 = P2; -	P1 += -1;		/* Unroll one iteration*/ -	SSYNC; -	FLUSHINV [P0++];	/* because CSYNC can't end loops.*/ -	LSETUP (.Leall, .Leall) LC0 = P1; -.Leall:	FLUSHINV [P0++]; -	SSYNC; -	JUMP .Ldfinished; -ENDPROC(_dcplb_flush) - -.align 4; -page_prefix_table: -.byte4	0xFFFFFC00;	/* 1K */ -.byte4	0xFFFFF000;	/* 4K */ -.byte4	0xFFF00000;	/* 1M */ -.byte4	0xFFC00000;	/* 4M */ -.page_prefix_table.end: diff --git a/cpu/blackfin/serial.c b/cpu/blackfin/serial.c index 406d9d023..0d6f377c0 100644 --- a/cpu/blackfin/serial.c +++ b/cpu/blackfin/serial.c @@ -35,6 +35,32 @@  #include "serial.h" +#ifdef CONFIG_DEBUG_SERIAL +uint16_t cached_lsr[256]; +uint16_t cached_rbr[256]; +size_t cache_count; + +/* The LSR is read-to-clear on some parts, so we have to make sure status + * bits aren't inadvertently lost when doing various tests. + */ +static uint16_t uart_lsr_save; +static uint16_t uart_lsr_read(void) +{ +	uint16_t lsr = *pUART_LSR; +	uart_lsr_save |= (lsr & (OE|PE|FE|BI)); +	return lsr | uart_lsr_save; +} +/* Just do the clear for everyone since it can't hurt. */ +static void uart_lsr_clear(void) +{ +	uart_lsr_save = 0; +	*pUART_LSR |= -1; +} +#else +static inline uint16_t uart_lsr_read(void) { return *pUART_LSR; } +static inline void uart_lsr_clear(void) { *pUART_LSR = -1; } +#endif +  /* Symbol for our assembly to call. */  void serial_set_baud(uint32_t baud)  { @@ -61,6 +87,12 @@ int serial_init(void)  {  	serial_initialize();  	serial_setbrg(); +	uart_lsr_clear(); +#ifdef CONFIG_DEBUG_SERIAL +	cache_count = 0; +	memset(cached_lsr, 0x00, sizeof(cached_lsr)); +	memset(cached_rbr, 0x00, sizeof(cached_rbr)); +#endif  	return 0;  } @@ -73,7 +105,7 @@ void serial_putc(const char c)  	WATCHDOG_RESET();  	/* wait for the hardware fifo to clear up */ -	while (!(*pUART_LSR & THRE)) +	while (!(uart_lsr_read() & THRE))  		continue;  	/* queue the character for transmission */ @@ -83,38 +115,54 @@ void serial_putc(const char c)  	WATCHDOG_RESET();  	/* wait for the byte to be shifted over the line */ -	while (!(*pUART_LSR & TEMT)) +	while (!(uart_lsr_read() & TEMT))  		continue;  }  int serial_tstc(void)  {  	WATCHDOG_RESET(); -	return (*pUART_LSR & DR) ? 1 : 0; +	return (uart_lsr_read() & DR) ? 1 : 0;  }  int serial_getc(void)  { -	uint16_t uart_lsr_val, uart_rbr_val; +	uint16_t uart_rbr_val;  	/* wait for data ! */  	while (!serial_tstc())  		continue; -	/* clear the status and grab the new byte */ -	uart_lsr_val = *pUART_LSR; +	/* grab the new byte */  	uart_rbr_val = *pUART_RBR; +#ifdef CONFIG_DEBUG_SERIAL +	/* grab & clear the LSR */ +	uint16_t uart_lsr_val = uart_lsr_read(); + +	cached_lsr[cache_count] = uart_lsr_val; +	cached_rbr[cache_count] = uart_rbr_val; +	cache_count = (cache_count + 1) % ARRAY_SIZE(cached_lsr); +  	if (uart_lsr_val & (OE|PE|FE|BI)) { -		/* Some parts are read-to-clear while others are -		 * write-to-clear.  Just do the write for everyone -		 * since it cant hurt (other than code size). -		 */ -		*pUART_LSR = (OE|PE|FE|BI); +		uint16_t dll, dlh; +		printf("\n[SERIAL ERROR]\n"); +		ACCESS_LATCH(); +		dll = *pUART_DLL; +		dlh = *pUART_DLH; +		ACCESS_PORT_IER(); +		printf("\tDLL=0x%x DLH=0x%x\n", dll, dlh); +		do { +			--cache_count; +			printf("\t%3i: RBR=0x%02x LSR=0x%02x\n", cache_count, +				cached_rbr[cache_count], cached_lsr[cache_count]); +		} while (cache_count > 0);  		return -1;  	} +#endif +	uart_lsr_clear(); -	return uart_rbr_val & 0xFF; +	return uart_rbr_val;  }  void serial_puts(const char *s) diff --git a/cpu/blackfin/serial.h b/cpu/blackfin/serial.h index 1f0f4b46c..ec40c266a 100644 --- a/cpu/blackfin/serial.h +++ b/cpu/blackfin/serial.h @@ -175,11 +175,11 @@ static inline uint32_t serial_early_get_baud(void)  __attribute__((always_inline))  static inline void serial_early_set_baud(uint32_t baud)  { -	/* Translate from baud into divisor in terms of SCLK. -	 * The +1 is to make sure we over sample just a little -	 * rather than under sample the incoming signals. +	/* Translate from baud into divisor in terms of SCLK.  The +	 * weird multiplication is to make sure we over sample just +	 * a little rather than under sample the incoming signals.  	 */ -	uint16_t divisor = (get_sclk() / (baud * 16)) + 1; +	uint16_t divisor = (get_sclk() + (baud * 8)) / (baud * 16) - ANOMALY_05000230;  	/* Set DLAB in LCR to Access DLL and DLH */  	ACCESS_LATCH(); diff --git a/cpu/blackfin/start.S b/cpu/blackfin/start.S index 8303292a5..9975a0c62 100644 --- a/cpu/blackfin/start.S +++ b/cpu/blackfin/start.S @@ -1,7 +1,7 @@  /*   * U-boot - start.S Startup file for Blackfin u-boot   * - * Copyright (c) 2005-2007 Analog Devices Inc. + * Copyright (c) 2005-2008 Analog Devices Inc.   *   * This file is based on head.S   * Copyright (c) 2003  Metrowerks/Motorola @@ -49,8 +49,8 @@  ENTRY(_start)  	/* Set our initial stack to L1 scratch space */ -	sp.l = LO(L1_SRAM_SCRATCH + L1_SRAM_SCRATCH_SIZE); -	sp.h = HI(L1_SRAM_SCRATCH + L1_SRAM_SCRATCH_SIZE); +	sp.l = LO(L1_SRAM_SCRATCH_END - 20); +	sp.h = HI(L1_SRAM_SCRATCH_END - 20);  #ifdef CONFIG_HW_WATCHDOG  # ifndef CONFIG_HW_WATCHDOG_TIMEOUT_START @@ -75,7 +75,7 @@ ENTRY(_start)  	serial_early_puts("Init Registers"); -	/* Disable nested interrupts and enable CYCLES for udelay() */ +	/* Disable self-nested interrupts and enable CYCLES for udelay() */  	R0 = CCEN | 0x30;  	SYSCFG = R0; @@ -180,7 +180,7 @@ ENTRY(_start)  	/* Now lower ourselves from the highest interrupt level to  	 * the lowest.  We do this by masking all interrupts but 15, -	 * setting the 15 handler to "board_init_f", raising the 15 +	 * setting the 15 handler to ".Lenable_nested", raising the 15  	 * interrupt, and then returning from the highest interrupt  	 * level to the dummy "jump" until the interrupt controller  	 * services the pending 15 interrupt. @@ -190,20 +190,23 @@ ENTRY(_start)  	r1 = r6;  	p0.l = LO(EVT15);  	p0.h = HI(EVT15); -	p1.l = _cpu_init_f; -	p1.h = _cpu_init_f; +	p1.l = .Lenable_nested; +	p1.h = .Lenable_nested;  	[p0] = p1; -	p2.l = LO(IMASK); -	p2.h = HI(IMASK); -	p3.l = LO(EVT_IVG15); -	p3.h = HI(EVT_IVG15); -	[p2] = p3; +	r7 = EVT_IVG15 (z); +	sti r7;  	raise 15;  	p4.l = .LWAIT_HERE;  	p4.h = .LWAIT_HERE;  	reti = p4;  	rti; +	/* Enable nested interrupts before continuing with cpu init */ +.Lenable_nested: +	cli r7; +	[--sp] = reti; +	jump.l _cpu_init_f; +  .LWAIT_HERE:  	jump .LWAIT_HERE;  ENDPROC(_start) diff --git a/cpu/blackfin/traps.c b/cpu/blackfin/traps.c index 2eb45b59c..d17c0a195 100644 --- a/cpu/blackfin/traps.c +++ b/cpu/blackfin/traps.c @@ -236,19 +236,60 @@ static void decode_address(char *buf, unsigned long address)  		sprintf(buf, "<0x%p> /* unknown address */", address);  } +static char *strhwerrcause(uint16_t hwerrcause) +{ +	switch (hwerrcause) { +		case 0x02: return "system mmr error"; +		case 0x03: return "external memory addressing error"; +		case 0x12: return "performance monitor overflow"; +		case 0x18: return "raise 5 instruction"; +		default:   return "undef"; +	} +} + +static char *strexcause(uint16_t excause) +{ +	switch (excause) { +		case 0x00 ... 0xf: return "custom exception"; +		case 0x10: return "single step"; +		case 0x11: return "trace buffer full"; +		case 0x21: return "undef inst"; +		case 0x22: return "illegal inst"; +		case 0x23: return "dcplb prot violation"; +		case 0x24: return "misaligned data"; +		case 0x25: return "unrecoverable event"; +		case 0x26: return "dcplb miss"; +		case 0x27: return "multiple dcplb hit"; +		case 0x28: return "emulation watchpoint"; +		case 0x2a: return "misaligned inst"; +		case 0x2b: return "icplb prot violation"; +		case 0x2c: return "icplb miss"; +		case 0x2d: return "multiple icplb hit"; +		case 0x2e: return "illegal use of supervisor resource"; +		default:   return "undef"; +	} +} +  void dump(struct pt_regs *fp)  {  	char buf[150];  	size_t i; +	uint16_t hwerrcause, excause;  	if (!ENABLE_DUMP)  		return; +	/* fp->ipend is garbage, so load it ourself */ +	fp->ipend = bfin_read_IPEND(); + +	hwerrcause = (fp->seqstat & HWERRCAUSE) >> HWERRCAUSE_P; +	excause = (fp->seqstat & EXCAUSE) >> EXCAUSE_P; +  	printf("SEQUENCER STATUS:\n");  	printf(" SEQSTAT: %08lx  IPEND: %04lx  SYSCFG: %04lx\n",  		fp->seqstat, fp->ipend, fp->syscfg); -	printf("  HWERRCAUSE: 0x%lx\n", (fp->seqstat & HWERRCAUSE) >> HWERRCAUSE_P); -	printf("  EXCAUSE   : 0x%lx\n", (fp->seqstat & EXCAUSE) >> EXCAUSE_P); +	printf("  HWERRCAUSE: 0x%lx: %s\n", hwerrcause, strhwerrcause(hwerrcause)); +	printf("  EXCAUSE   : 0x%lx: %s\n", excause, strexcause(excause));  	for (i = 6; i <= 15; ++i) {  		if (fp->ipend & (1 << i)) {  			decode_address(buf, bfin_read32(EVT0 + 4*i)); @@ -263,8 +304,9 @@ void dump(struct pt_regs *fp)  	printf(" RETX: %s\n", buf);  	decode_address(buf, fp->rets);  	printf(" RETS: %s\n", buf); +	/* we lie and store RETI in "pc" */  	decode_address(buf, fp->pc); -	printf(" PC  : %s\n", buf); +	printf(" RETI: %s\n", buf);  	if (fp->seqstat & EXCAUSE) {  		decode_address(buf, bfin_read_DCPLB_FAULT_ADDR()); @@ -344,10 +386,6 @@ void bfin_panic(struct pt_regs *regs)  	);  	dump(regs);  	dump_bfin_trace_buffer(); -	printf( -		"\n" -		"Please reset the board\n" -		"\n" -	); +	puts("\n");  	bfin_reset_or_hang();  }  |