summaryrefslogtreecommitdiff
path: root/cpu/blackfin
diff options
context:
space:
mode:
Diffstat (limited to 'cpu/blackfin')
-rw-r--r--cpu/blackfin/Makefile2
-rw-r--r--cpu/blackfin/cpu.c40
-rw-r--r--cpu/blackfin/flush.S230
-rw-r--r--cpu/blackfin/serial.c72
-rw-r--r--cpu/blackfin/serial.h8
-rw-r--r--cpu/blackfin/start.S27
-rw-r--r--cpu/blackfin/traps.c54
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();
}