Merge branch 'master' of git://git.denx.de/u-boot-blackfin

This commit is contained in:
Wolfgang Denk
2008-11-01 15:59:35 +01:00
59 changed files with 427 additions and 805 deletions

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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:

View File

@@ -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)

View File

@@ -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();

View File

@@ -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)

View File

@@ -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();
}