diff options
Diffstat (limited to 'cpu/blackfin')
-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 f194a38..8fed4b4 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 53de5ab..9efd88e 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 417f798..0000000 --- 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 406d9d0..0d6f377 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 1f0f4b4..ec40c26 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 8303292..9975a0c 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 2eb45b5..d17c0a1 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(); } |