diff options
author | Mike Frysinger <vapier@gentoo.org> | 2008-03-30 15:46:13 -0400 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2008-03-30 15:50:19 -0400 |
commit | 9171fc81722c20fdb5a829a58b17c9eaadd5fb44 (patch) | |
tree | d5b8741232955f2c01b0c36c46c867d15e8245a7 /cpu/blackfin | |
parent | 9ce7e53abd039decea1af67aec81bbd5df7a2593 (diff) | |
download | u-boot-imx-9171fc81722c20fdb5a829a58b17c9eaadd5fb44.zip u-boot-imx-9171fc81722c20fdb5a829a58b17c9eaadd5fb44.tar.gz u-boot-imx-9171fc81722c20fdb5a829a58b17c9eaadd5fb44.tar.bz2 |
Blackfin: unify cpu and boot modes
All of the duplicated code for Blackfin processors and boot modes have been
unified. After all, the core is the same for all processors, just the
peripheral set differs (which gets handled in the drivers).
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Diffstat (limited to 'cpu/blackfin')
-rw-r--r-- | cpu/blackfin/.gitignore | 1 | ||||
-rw-r--r-- | cpu/blackfin/Makefile | 65 | ||||
-rwxr-xr-x | cpu/blackfin/bootrom-asm-offsets.awk | 41 | ||||
-rw-r--r-- | cpu/blackfin/bootrom-asm-offsets.c.in | 12 | ||||
-rw-r--r-- | cpu/blackfin/cache.S | 61 | ||||
-rw-r--r-- | cpu/blackfin/cpu.c | 141 | ||||
-rw-r--r-- | cpu/blackfin/cpu.h | 38 | ||||
-rw-r--r-- | cpu/blackfin/flush.S | 230 | ||||
-rw-r--r-- | cpu/blackfin/i2c.c | 444 | ||||
-rw-r--r-- | cpu/blackfin/initcode.c | 353 | ||||
-rw-r--r-- | cpu/blackfin/interrupt.S | 33 | ||||
-rw-r--r-- | cpu/blackfin/interrupts.c | 155 | ||||
-rw-r--r-- | cpu/blackfin/reset.c | 96 | ||||
-rw-r--r-- | cpu/blackfin/serial.c | 124 | ||||
-rw-r--r-- | cpu/blackfin/serial.h | 275 | ||||
-rw-r--r-- | cpu/blackfin/start.S | 219 | ||||
-rw-r--r-- | cpu/blackfin/system_map.S | 18 | ||||
-rw-r--r-- | cpu/blackfin/traps.c | 353 | ||||
-rw-r--r-- | cpu/blackfin/watchdog.c | 25 |
19 files changed, 2684 insertions, 0 deletions
diff --git a/cpu/blackfin/.gitignore b/cpu/blackfin/.gitignore new file mode 100644 index 0000000..0ec9d56 --- /dev/null +++ b/cpu/blackfin/.gitignore @@ -0,0 +1 @@ +bootrom-asm-offsets.[chs] diff --git a/cpu/blackfin/Makefile b/cpu/blackfin/Makefile new file mode 100644 index 0000000..f194a38 --- /dev/null +++ b/cpu/blackfin/Makefile @@ -0,0 +1,65 @@ +# +# U-boot - Makefile +# +# Copyright (c) 2005-2008 Analog Device Inc. +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# Licensed under the GPL-2 or later. +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)lib$(CPU).a + +EXTRA := +CEXTRA := initcode.o +SEXTRA := start.o +SOBJS := interrupt.o cache.o flush.o +COBJS := cpu.o traps.o interrupts.o reset.o serial.o i2c.o watchdog.o + +ifeq ($(CONFIG_BFIN_BOOT_MODE),BFIN_BOOT_BYPASS) +COBJS += initcode.o +endif + +SRCS := $(SEXTRA:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS) $(SOBJS)) +EXTRA := $(addprefix $(obj),$(EXTRA)) +CEXTRA := $(addprefix $(obj),$(CEXTRA)) +SEXTRA := $(addprefix $(obj),$(SEXTRA)) + +all: $(obj).depend $(LIB) $(obj).depend $(EXTRA) $(CEXTRA) $(SEXTRA) check_initcode + +$(LIB): $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +$(OBJS): $(obj)bootrom-asm-offsets.h +$(obj)bootrom-asm-offsets.c: bootrom-asm-offsets.c.in bootrom-asm-offsets.awk + echo '#include <asm/mach-common/bits/bootrom.h>' | $(CPP) $(CPPFLAGS) - | gawk -f ./bootrom-asm-offsets.awk > $@.tmp + mv $@.tmp $@ +$(obj)bootrom-asm-offsets.s: $(obj)bootrom-asm-offsets.c + $(CC) $(CFLAGS) -S $^ -o $@.tmp + mv $@.tmp $@ +$(obj)bootrom-asm-offsets.h: $(obj)bootrom-asm-offsets.s + sed -ne "/^->/{s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; s:->::; p;}" $^ > $@ + +# make sure our initcode (which goes into LDR) does not +# have relocs or external references +READINIT = env LC_ALL=C $(CROSS_COMPILE)readelf -s $< +check_initcode: $(obj)initcode.o +ifneq ($(CONFIG_BFIN_BOOT_MODE),BFIN_BOOT_BYPASS) + @if $(READINIT) | grep '\<GLOBAL\>.*\<UND\>' ; then \ + echo "$< contains external references!" 1>&2 ; \ + exit 1 ; \ + fi +endif + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/cpu/blackfin/bootrom-asm-offsets.awk b/cpu/blackfin/bootrom-asm-offsets.awk new file mode 100755 index 0000000..1d61824 --- /dev/null +++ b/cpu/blackfin/bootrom-asm-offsets.awk @@ -0,0 +1,41 @@ +#!/usr/bin/gawk -f +BEGIN { + print "/* DO NOT EDIT: AUTOMATICALLY GENERATED" + print " * Input files: bootrom-asm-offsets.awk bootrom-asm-offsets.c.in" + print " * DO NOT EDIT: AUTOMATICALLY GENERATED" + print " */" + print "" + system("cat bootrom-asm-offsets.c.in") + print "{" +} + +{ + /* find a structure definition */ + if ($0 ~ /typedef struct .* {/) { + delete members; + i = 0; + + /* extract each member of the structure */ + while (1) { + getline + if ($1 == "}") + break; + gsub(/[*;]/, ""); + members[i++] = $NF; + } + + /* grab the structure's name */ + struct = $NF; + sub(/;$/, "", struct); + + /* output the DEFINE() macros */ + while (i-- > 0) + print "\tDEFINE(" struct ", " members[i] ");" + print "" + } +} + +END { + print "\treturn 0;" + print "}" +} diff --git a/cpu/blackfin/bootrom-asm-offsets.c.in b/cpu/blackfin/bootrom-asm-offsets.c.in new file mode 100644 index 0000000..3146e46 --- /dev/null +++ b/cpu/blackfin/bootrom-asm-offsets.c.in @@ -0,0 +1,12 @@ +/* A little trick taken from the kernel asm-offsets.h where we convert + * the C structures automatically into a bunch of defines for use in + * the assembly files. + */ + +#include <linux/stddef.h> +#include <asm/mach-common/bits/bootrom.h> + +#define _DEFINE(sym, val) asm volatile("\n->" #sym " %0 " #val : : "i" (val)) +#define DEFINE(s, m) _DEFINE(offset_##s##_##m, offsetof(s, m)) + +int main(int argc, char *argv[]) diff --git a/cpu/blackfin/cache.S b/cpu/blackfin/cache.S new file mode 100644 index 0000000..51bdb30 --- /dev/null +++ b/cpu/blackfin/cache.S @@ -0,0 +1,61 @@ +/* cache.S - low level cache handling routines + * Copyright (C) 2003-2007 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <asm/linkage.h> +#include <config.h> +#include <asm/blackfin.h> + +.text +.align 2 +ENTRY(_blackfin_icache_flush_range) + R2 = -32; + R2 = R0 & R2; + P0 = R2; + P1 = R1; + CSYNC; +1: + IFLUSH[P0++]; + CC = P0 < P1(iu); + IF CC JUMP 1b(bp); + IFLUSH[P0]; + SSYNC; + RTS; +ENDPROC(_blackfin_icache_flush_range) + +ENTRY(_blackfin_dcache_flush_range) + R2 = -32; + R2 = R0 & R2; + P0 = R2; + P1 = R1; + CSYNC; +1: + FLUSH[P0++]; + CC = P0 < P1(iu); + IF CC JUMP 1b(bp); + FLUSH[P0]; + SSYNC; + RTS; +ENDPROC(_blackfin_dcache_flush_range) + +ENTRY(_blackfin_dcache_invalidate_range) + R2 = -32; + R2 = R0 & R2; + P0 = R2; + P1 = R1; + CSYNC; +1: + FLUSHINV[P0++]; + CC = P0 < P1(iu); + IF CC JUMP 1b(bp); + + /* + * If the data crosses a cache line, then we'll be pointing to + * the last cache line, but won't have flushed/invalidated it yet, so do + * one more. + */ + FLUSHINV[P0]; + SSYNC; + RTS; +ENDPROC(_blackfin_dcache_invalidate_range) diff --git a/cpu/blackfin/cpu.c b/cpu/blackfin/cpu.c new file mode 100644 index 0000000..53de5ab --- /dev/null +++ b/cpu/blackfin/cpu.c @@ -0,0 +1,141 @@ +/* + * U-boot - cpu.c CPU specific functions + * + * Copyright (c) 2005-2008 Analog Devices Inc. + * + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <command.h> +#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) +{ + /* Build a NOP slide over the LDR jump block. Whee! */ + serial_early_puts("NOP Slide\n"); + char nops[0xC]; + memset(nops, 0x00, sizeof(nops)); + extern char _stext_l1; + memcpy(&_stext_l1 - sizeof(nops), nops, sizeof(nops)); + + if (!loaded_from_ldr) { + /* Relocate sections into L1 if the LDR didn't do it -- don't + * check length because the linker script does the size + * checking at build time. + */ + serial_early_puts("L1 Relocate\n"); + extern char _stext_l1, _etext_l1, _stext_l1_lma; + memcpy(&_stext_l1, &_stext_l1_lma, (&_etext_l1 - &_stext_l1)); + extern char _sdata_l1, _edata_l1, _sdata_l1_lma; + memcpy(&_sdata_l1, &_sdata_l1_lma, (&_edata_l1 - &_sdata_l1)); + } +#if defined(__ADSPBF537__) || defined(__ADSPBF536__) || defined(__ADSPBF534__) + /* The BF537 bootrom will reset the EBIU_AMGCTL register on us + * after it has finished loading the LDR. So configure it again. + */ + else + bfin_write_EBIU_AMGCTL(CONFIG_EBIU_AMGCTL_VAL); +#endif + +#ifdef CONFIG_DEBUG_DUMP + /* Turn on hardware trace buffer */ + bfin_write_TBUFCTL(TBUFPWR | TBUFEN); +#endif + +#ifndef CONFIG_PANIC_HANG + /* Reset upon a double exception rather than just hanging. + * Do not do bfin_read on SWRST as that will reset status bits. + */ + bfin_write_SWRST(DOUBLE_FAULT); +#endif + + serial_early_puts("Board init flash\n"); + board_init_f(bootflag); +} + +int exception_init(void) +{ + bfin_write_EVT3(trap); + return 0; +} + +int irq_init(void) +{ +#ifdef SIC_IMASK0 + bfin_write_SIC_IMASK0(0); + bfin_write_SIC_IMASK1(0); +# ifdef SIC_IMASK2 + bfin_write_SIC_IMASK2(0); +# endif +#elif defined(SICA_IMASK0) + bfin_write_SICA_IMASK0(0); + bfin_write_SICA_IMASK1(0); +#else + bfin_write_SIC_IMASK(0); +#endif + bfin_write_EVT2(evt_default); /* NMI */ + bfin_write_EVT5(evt_default); /* hardware error */ + bfin_write_EVT6(evt_default); /* core timer */ + bfin_write_EVT7(evt_default); + bfin_write_EVT8(evt_default); + bfin_write_EVT9(evt_default); + bfin_write_EVT10(evt_default); + bfin_write_EVT11(evt_default); + bfin_write_EVT12(evt_default); + bfin_write_EVT13(evt_default); + bfin_write_EVT14(evt_default); + bfin_write_EVT15(evt_default); + bfin_write_ILAT(0); + CSYNC(); + /* enable all interrupts except for core timer */ + irq_flags = 0xffffffbf; + local_irq_enable(); + CSYNC(); + return 0; +} diff --git a/cpu/blackfin/cpu.h b/cpu/blackfin/cpu.h new file mode 100644 index 0000000..0a13c28 --- /dev/null +++ b/cpu/blackfin/cpu.h @@ -0,0 +1,38 @@ +/* + * U-boot - cpu.h + * + * Copyright (c) 2005-2007 Analog Devices Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef _CPU_H_ +#define _CPU_H_ + +#include <command.h> + +void board_reset(void) __attribute__((__weak__)); +void bfin_reset_or_hang(void) __attribute__((__noreturn__)); +void bfin_panic(struct pt_regs *reg); +void dump(struct pt_regs *regs); + +asmlinkage void trap(void); +asmlinkage void evt_default(void); + +#endif diff --git a/cpu/blackfin/flush.S b/cpu/blackfin/flush.S new file mode 100644 index 0000000..8072b86 --- /dev/null +++ b/cpu/blackfin/flush.S @@ -0,0 +1,230 @@ +/* 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/i2c.c b/cpu/blackfin/i2c.c new file mode 100644 index 0000000..47be258 --- /dev/null +++ b/cpu/blackfin/i2c.c @@ -0,0 +1,444 @@ +/* + * i2c.c - driver for Blackfin on-chip TWI/I2C + * + * Copyright (c) 2006-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> + +#ifdef CONFIG_HARD_I2C + +#include <asm/blackfin.h> +#include <i2c.h> +#include <asm/io.h> +#include <asm/mach-common/bits/twi.h> + +/* Two-Wire Interface (0xFFC01400 - 0xFFC014FF) */ +#ifdef TWI0_CLKDIV +#define bfin_read_TWI_CLKDIV() bfin_read_TWI0_CLKDIV() +#define bfin_write_TWI_CLKDIV(val) bfin_write_TWI0_CLKDIV(val) +#define bfin_read_TWI_CONTROL() bfin_read_TWI0_CONTROL() +#define bfin_write_TWI_CONTROL(val) bfin_write_TWI0_CONTROL(val) +#define bfin_read_TWI_SLAVE_CTL() bfin_read_TWI0_SLAVE_CTL() +#define bfin_write_TWI_SLAVE_CTL(val) bfin_write_TWI0_SLAVE_CTL(val) +#define bfin_read_TWI_SLAVE_STAT() bfin_read_TWI0_SLAVE_STAT() +#define bfin_write_TWI_SLAVE_STAT(val) bfin_write_TWI0_SLAVE_STAT(val) +#define bfin_read_TWI_SLAVE_ADDR() bfin_read_TWI0_SLAVE_ADDR() +#define bfin_write_TWI_SLAVE_ADDR(val) bfin_write_TWI0_SLAVE_ADDR(val) +#define bfin_read_TWI_MASTER_CTL() bfin_read_TWI0_MASTER_CTL() +#define bfin_write_TWI_MASTER_CTL(val) bfin_write_TWI0_MASTER_CTL(val) +#define bfin_read_TWI_MASTER_STAT() bfin_read_TWI0_MASTER_STAT() +#define bfin_write_TWI_MASTER_STAT(val) bfin_write_TWI0_MASTER_STAT(val) +#define bfin_read_TWI_MASTER_ADDR() bfin_read_TWI0_MASTER_ADDR() +#define bfin_write_TWI_MASTER_ADDR(val) bfin_write_TWI0_MASTER_ADDR(val) +#define bfin_read_TWI_INT_STAT() bfin_read_TWI0_INT_STAT() +#define bfin_write_TWI_INT_STAT(val) bfin_write_TWI0_INT_STAT(val) +#define bfin_read_TWI_INT_MASK() bfin_read_TWI0_INT_MASK() +#define bfin_write_TWI_INT_MASK(val) bfin_write_TWI0_INT_MASK(val) +#define bfin_read_TWI_FIFO_CTL() bfin_read_TWI0_FIFO_CTL() +#define bfin_write_TWI_FIFO_CTL(val) bfin_write_TWI0_FIFO_CTL(val) +#define bfin_read_TWI_FIFO_STAT() bfin_read_TWI0_FIFO_STAT() +#define bfin_write_TWI_FIFO_STAT(val) bfin_write_TWI0_FIFO_STAT(val) +#define bfin_read_TWI_XMT_DATA8() bfin_read_TWI0_XMT_DATA8() +#define bfin_write_TWI_XMT_DATA8(val) bfin_write_TWI0_XMT_DATA8(val) +#define bfin_read_TWI_XMT_DATA_16() bfin_read_TWI0_XMT_DATA16() +#define bfin_write_TWI_XMT_DATA16(val) bfin_write_TWI0_XMT_DATA16(val) +#define bfin_read_TWI_RCV_DATA8() bfin_read_TWI0_RCV_DATA8() +#define bfin_write_TWI_RCV_DATA8(val) bfin_write_TWI0_RCV_DATA8(val) +#define bfin_read_TWI_RCV_DATA16() bfin_read_TWI0_RCV_DATA16() +#define bfin_write_TWI_RCV_DATA16(val) bfin_write_TWI0_RCV_DATA16(val) +#endif + +#ifdef DEBUG_I2C +#define PRINTD(fmt,args...) do { \ + DECLARE_GLOBAL_DATA_PTR; \ + if (gd->have_console) \ + printf(fmt ,##args); \ + } while (0) +#else +#define PRINTD(fmt,args...) +#endif + +#ifndef CONFIG_TWICLK_KHZ +#define CONFIG_TWICLK_KHZ 50 +#endif + +/* All transfers are described by this data structure */ +struct i2c_msg { + u16 addr; /* slave address */ + u16 flags; +#define I2C_M_STOP 0x2 +#define I2C_M_RD 0x1 + u16 len; /* msg length */ + u8 *buf; /* pointer to msg data */ +}; + +/** + * i2c_reset: - reset the host controller + */ +static void i2c_reset(void) +{ + /* Disable TWI */ + bfin_write_TWI_CONTROL(0); + SSYNC(); + + /* Set TWI internal clock as 10MHz */ + bfin_write_TWI_CONTROL(((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F); + + /* Set Twi interface clock as specified */ + if (CONFIG_TWICLK_KHZ > 400) + bfin_write_TWI_CLKDIV(((5 * 1024 / 400) << 8) | ((5 * 1024 / + 400) & 0xFF)); + else + bfin_write_TWI_CLKDIV(((5 * 1024 / + CONFIG_TWICLK_KHZ) << 8) | ((5 * 1024 / + CONFIG_TWICLK_KHZ) + & 0xFF)); + + /* Enable TWI */ + bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() | TWI_ENA); + SSYNC(); +} + +int wait_for_completion(struct i2c_msg *msg, int timeout_count) +{ + unsigned short twi_int_stat; + unsigned short mast_stat; + int i; + + for (i = 0; i < timeout_count; i++) { + twi_int_stat = bfin_read_TWI_INT_STAT(); + mast_stat = bfin_read_TWI_MASTER_STAT(); + + if (XMTSERV & twi_int_stat) { + /* Transmit next data */ + if (msg->len > 0) { + bfin_write_TWI_XMT_DATA8(*(msg->buf++)); + msg->len--; + } else if (msg->flags & I2C_M_STOP) + bfin_write_TWI_MASTER_CTL + (bfin_read_TWI_MASTER_CTL() | STOP); + SSYNC(); + /* Clear status */ + bfin_write_TWI_INT_STAT(XMTSERV); + SSYNC(); + i = 0; + } + if (RCVSERV & twi_int_stat) { + if (msg->len > 0) { + /* Receive next data */ + *(msg->buf++) = bfin_read_TWI_RCV_DATA8(); + msg->len--; + } else if (msg->flags & I2C_M_STOP) { + bfin_write_TWI_MASTER_CTL + (bfin_read_TWI_MASTER_CTL() | STOP); + SSYNC(); + } + /* Clear interrupt source */ + bfin_write_TWI_INT_STAT(RCVSERV); + SSYNC(); + i = 0; + } + if (MERR & twi_int_stat) { + bfin_write_TWI_INT_STAT(MERR); + bfin_write_TWI_INT_MASK(0); + bfin_write_TWI_MASTER_STAT(0x3e); + bfin_write_TWI_MASTER_CTL(0); + SSYNC(); + /* + * if both err and complete int stats are set, + * return proper results. + */ + if (MCOMP & twi_int_stat) { + bfin_write_TWI_INT_STAT(MCOMP); + bfin_write_TWI_INT_MASK(0); + bfin_write_TWI_MASTER_CTL(0); + SSYNC(); + /* + * If it is a quick transfer, + * only address bug no data, not an err. + */ + if (msg->len == 0 && mast_stat & BUFRDERR) + return 0; + /* + * If address not acknowledged return -3, + * else return 0. + */ + else if (!(mast_stat & ANAK)) + return 0; + else + return -3; + } + return -1; + } + if (MCOMP & twi_int_stat) { + bfin_write_TWI_INT_STAT(MCOMP); + SSYNC(); + bfin_write_TWI_INT_MASK(0); + bfin_write_TWI_MASTER_CTL(0); + SSYNC(); + return 0; + } + } + if (msg->flags & I2C_M_RD) + return -4; + else + return -2; +} + +/** + * i2c_transfer: - Transfer one byte over the i2c bus + * + * This function can tranfer a byte over the i2c bus in both directions. + * It is used by the public API functions. + * + * @return: 0: transfer successful + * -1: transfer fail + * -2: transmit timeout + * -3: ACK missing + * -4: receive timeout + * -5: controller not ready + */ +int i2c_transfer(struct i2c_msg *msg) +{ + int ret = 0; + int timeout_count = 10000; + int len = msg->len; + + if (!(bfin_read_TWI_CONTROL() & TWI_ENA)) { + ret = -5; + goto transfer_error; + } + + while (bfin_read_TWI_MASTER_STAT() & BUSBUSY) + continue; + + /* Set Transmit device address */ + bfin_write_TWI_MASTER_ADDR(msg->addr); + + /* + * FIFO Initiation. + * Data in FIFO should be discarded before start a new operation. + */ + bfin_write_TWI_FIFO_CTL(0x3); + SSYNC(); + bfin_write_TWI_FIFO_CTL(0); + SSYNC(); + + if (!(msg->flags & I2C_M_RD)) { + /* Transmit first data */ + if (msg->len > 0) { + PRINTD("1 in i2c_transfer: buf=%d, len=%d\n", *msg->buf, + len); + bfin_write_TWI_XMT_DATA8(*(msg->buf++)); + msg->len--; + SSYNC(); + } + } + + /* clear int stat */ + bfin_write_TWI_INT_STAT(MERR | MCOMP | XMTSERV | RCVSERV); + + /* Interrupt mask . Enable XMT, RCV interrupt */ + bfin_write_TWI_INT_MASK(MCOMP | MERR | + ((msg->flags & I2C_M_RD) ? RCVSERV : XMTSERV)); + SSYNC(); + + if (len > 0 && len <= 255) + bfin_write_TWI_MASTER_CTL((len << 6)); + else if (msg->len > 255) { + bfin_write_TWI_MASTER_CTL((0xff << 6)); + msg->flags &= I2C_M_STOP; + } else + bfin_write_TWI_MASTER_CTL(0); + + /* Master enable */ + bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN | + ((msg->flags & I2C_M_RD) + ? MDIR : 0) | ((CONFIG_TWICLK_KHZ > + 100) ? FAST : 0)); + SSYNC(); + + ret = wait_for_completion(msg, timeout_count); + PRINTD("3 in i2c_transfer: ret=%d\n", ret); + + transfer_error: + switch (ret) { + case 1: + PRINTD(("i2c_transfer: error: transfer fail\n")); + break; + case 2: + PRINTD(("i2c_transfer: error: transmit timeout\n")); + break; + case 3: + PRINTD(("i2c_transfer: error: ACK missing\n")); + break; + case 4: + PRINTD(("i2c_transfer: error: receive timeout\n")); + break; + case 5: + PRINTD(("i2c_transfer: error: controller not ready\n")); + i2c_reset(); + break; + default: + break; + } + return ret; + +} + +/* ---------------------------------------------------------------------*/ +/* API Functions */ +/* ---------------------------------------------------------------------*/ + +void i2c_init(int speed, int slaveaddr) +{ + i2c_reset(); +} + +/** + * i2c_probe: - Test if a chip answers for a given i2c address + * + * @chip: address of the chip which is searched for + * @return: 0 if a chip was found, -1 otherwhise + */ + +int i2c_probe(uchar chip) +{ + struct i2c_msg msg; + u8 probebuf; + + i2c_reset(); + + probebuf = 0; + msg.addr = chip; + msg.flags = 0; + msg.len = 1; + msg.buf = &probebuf; + if (i2c_transfer(&msg)) + return -1; + + msg.addr = chip; + msg.flags = I2C_M_RD; + msg.len = 1; + msg.buf = &probebuf; + if (i2c_transfer(&msg)) + return -1; + + return 0; +} + +/** + * i2c_read: - Read multiple bytes from an i2c device + * + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Where to read/write the data + * len: How many bytes to read/write + * + * Returns: 0 on success, not 0 on failure + */ + +int i2c_read(uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + struct i2c_msg msg; + u8 addr_bytes[3]; /* lowest...highest byte of data address */ + + PRINTD("i2c_read: chip=0x%x, addr=0x%x, alen=0x%x, len=0x%x\n", chip, + addr, alen, len); + + if (alen > 0) { + addr_bytes[0] = (u8) ((addr >> 0) & 0x000000FF); + addr_bytes[1] = (u8) ((addr >> 8) & 0x000000FF); + addr_bytes[2] = (u8) ((addr >> 16) & 0x000000FF); + msg.addr = chip; + msg.flags = 0; + msg.len = alen; + msg.buf = addr_bytes; + if (i2c_transfer(&msg)) + return -1; + } + + /* start read sequence */ + PRINTD(("i2c_read: start read sequence\n")); + msg.addr = chip; + msg.flags = I2C_M_RD; + msg.len = len; + msg.buf = buffer; + if (i2c_transfer(&msg)) + return -1; + + return 0; +} + +/** + * i2c_write: - Write multiple bytes to an i2c device + * + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Where to read/write the data + * len: How many bytes to read/write + * + * Returns: 0 on success, not 0 on failure + */ + +int i2c_write(uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + struct i2c_msg msg; + u8 addr_bytes[3]; /* lowest...highest byte of data address */ + + PRINTD + ("i2c_write: chip=0x%x, addr=0x%x, alen=0x%x, len=0x%x, buf0=0x%x\n", + chip, addr, alen, len, buffer[0]); + + /* chip address write */ + if (alen > 0) { + addr_bytes[0] = (u8) ((addr >> 0) & 0x000000FF); + addr_bytes[1] = (u8) ((addr >> 8) & 0x000000FF); + addr_bytes[2] = (u8) ((addr >> 16) & 0x000000FF); + msg.addr = chip; + msg.flags = 0; + msg.len = alen; + msg.buf = addr_bytes; + if (i2c_transfer(&msg)) + return -1; + } + + /* start read sequence */ + PRINTD(("i2c_write: start write sequence\n")); + msg.addr = chip; + msg.flags = 0; + msg.len = len; + msg.buf = buffer; + if (i2c_transfer(&msg)) + return -1; + + return 0; + +} + +uchar i2c_reg_read(uchar chip, uchar reg) +{ + uchar buf; + + PRINTD("i2c_reg_read: chip=0x%02x, reg=0x%02x\n", chip, reg); + i2c_read(chip, reg, 0, &buf, 1); + return (buf); +} + +void i2c_reg_write(uchar chip, uchar reg, uchar val) +{ + PRINTD("i2c_reg_write: chip=0x%02x, reg=0x%02x, val=0x%02x\n", chip, + reg, val); + i2c_write(chip, reg, 0, &val, 1); +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/cpu/blackfin/initcode.c b/cpu/blackfin/initcode.c new file mode 100644 index 0000000..ffc8420 --- /dev/null +++ b/cpu/blackfin/initcode.c @@ -0,0 +1,353 @@ +/* + * initcode.c - Initialize the processor. This is usually entails things + * like external memory, voltage regulators, etc... Note that this file + * cannot make any function calls as it may be executed all by itself by + * the Blackfin's bootrom in LDR format. + * + * Copyright (c) 2004-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <config.h> +#include <asm/blackfin.h> +#include <asm/mach-common/bits/bootrom.h> +#include <asm/mach-common/bits/ebiu.h> +#include <asm/mach-common/bits/pll.h> +#include <asm/mach-common/bits/uart.h> + +#define BFIN_IN_INITCODE +#include "serial.h" + +__attribute__((always_inline)) +static inline uint32_t serial_init(void) +{ +#ifdef __ADSPBF54x__ +# ifdef BFIN_BOOT_UART_USE_RTS +# define BFIN_UART_USE_RTS 1 +# else +# define BFIN_UART_USE_RTS 0 +# endif + if (BFIN_UART_USE_RTS && CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART) { + size_t i; + + /* force RTS rather than relying on auto RTS */ + bfin_write_UART1_MCR(bfin_read_UART1_MCR() | FCPOL); + + /* Wait for the line to clear up. We cannot rely on UART + * registers as none of them reflect the status of the RSR. + * Instead, we'll sleep for ~10 bit times at 9600 baud. + * We can precalc things here by assuming boot values for + * PLL rather than loading registers and calculating. + * baud = SCLK / (16 ^ (1 - EDBO) * Divisor) + * EDB0 = 0 + * Divisor = (SCLK / baud) / 16 + * SCLK = baud * 16 * Divisor + * SCLK = (0x14 * CONFIG_CLKIN_HZ) / 5 + * CCLK = (16 * Divisor * 5) * (9600 / 10) + * In reality, this will probably be just about 1 second delay, + * so assuming 9600 baud is OK (both as a very low and too high + * speed as this will buffer things enough). + */ +#define _NUMBITS (10) /* how many bits to delay */ +#define _LOWBAUD (9600) /* low baud rate */ +#define _SCLK ((0x14 * CONFIG_CLKIN_HZ) / 5) /* SCLK based on PLL */ +#define _DIVISOR ((_SCLK / _LOWBAUD) / 16) /* UART DLL/DLH */ +#define _NUMINS (3) /* how many instructions in loop */ +#define _CCLK (((16 * _DIVISOR * 5) * (_LOWBAUD / _NUMBITS)) / _NUMINS) + i = _CCLK; + while (i--) + asm volatile("" : : : "memory"); + } +#endif + + uint32_t old_baud = serial_early_get_baud(); + + if (BFIN_DEBUG_EARLY_SERIAL) { + serial_early_init(); + + /* If the UART is off, that means we need to program + * the baud rate ourselves initially. + */ + if (!old_baud) { + old_baud = CONFIG_BAUDRATE; + serial_early_set_baud(CONFIG_BAUDRATE); + } + } + + return old_baud; +} + +__attribute__((always_inline)) +static inline void serial_deinit(void) +{ +#ifdef __ADSPBF54x__ + if (BFIN_UART_USE_RTS && CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART) { + /* clear forced RTS rather than relying on auto RTS */ + bfin_write_UART1_MCR(bfin_read_UART1_MCR() & ~FCPOL); + } +#endif +} + +/* We need to reset the baud rate when we have early debug turned on + * or when we are booting over the UART. + * XXX: we should fix this to calc the old baud and restore it rather + * than hardcoding it via CONFIG_LDR_LOAD_BAUD ... but we have + * to figure out how to avoid the division in the baud calc ... + */ +__attribute__((always_inline)) +static inline void serial_reset_baud(uint32_t baud) +{ + if (!BFIN_DEBUG_EARLY_SERIAL && CONFIG_BFIN_BOOT_MODE != BFIN_BOOT_UART) + return; + +#ifndef CONFIG_LDR_LOAD_BAUD +# define CONFIG_LDR_LOAD_BAUD 115200 +#endif + + if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_BYPASS) + serial_early_set_baud(baud); + else if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART) + serial_early_set_baud(CONFIG_LDR_LOAD_BAUD); + else + serial_early_set_baud(CONFIG_BAUDRATE); +} + +__attribute__((always_inline)) +static inline void serial_putc(char c) +{ + if (!BFIN_DEBUG_EARLY_SERIAL) + return; + + if (c == '\n') + *pUART_THR = '\r'; + + *pUART_THR = c; + + while (!(*pUART_LSR & TEMT)) + continue; +} + + +/* Max SCLK can be 133MHz ... dividing that by 4 gives + * us a freq of 33MHz for SPI which should generally be + * slow enough for the slow reads the bootrom uses. + */ +#ifndef CONFIG_SPI_BAUD_INITBLOCK +# define CONFIG_SPI_BAUD_INITBLOCK 4 +#endif + +/* PLL_DIV defines */ +#ifndef CONFIG_PLL_DIV_VAL +# if (CONFIG_CCLK_DIV == 1) +# define CONFIG_CCLK_ACT_DIV CCLK_DIV1 +# elif (CONFIG_CCLK_DIV == 2) +# define CONFIG_CCLK_ACT_DIV CCLK_DIV2 +# elif (CONFIG_CCLK_DIV == 4) +# define CONFIG_CCLK_ACT_DIV CCLK_DIV4 +# elif (CONFIG_CCLK_DIV == 8) +# define CONFIG_CCLK_ACT_DIV CCLK_DIV8 +# else +# define CONFIG_CCLK_ACT_DIV CONFIG_CCLK_DIV_not_defined_properly +# endif +# define CONFIG_PLL_DIV_VAL (CONFIG_CCLK_ACT_DIV | CONFIG_SCLK_DIV) +#endif + +#ifndef CONFIG_PLL_LOCKCNT_VAL +# define CONFIG_PLL_LOCKCNT_VAL 0x0300 +#endif + +#ifndef CONFIG_PLL_CTL_VAL +# define CONFIG_PLL_CTL_VAL (SPORT_HYST | (CONFIG_VCO_MULT << 9)) +#endif + +#ifndef CONFIG_EBIU_RSTCTL_VAL +# define CONFIG_EBIU_RSTCTL_VAL 0 /* only MDDRENABLE is useful */ +#endif + +#ifndef CONFIG_EBIU_MBSCTL_VAL +# define CONFIG_EBIU_MBSCTL_VAL 0 +#endif + +/* Make sure our voltage value is sane so we don't blow up! */ +#ifndef CONFIG_VR_CTL_VAL +# define BFIN_CCLK ((CONFIG_CLKIN_HZ * CONFIG_VCO_MULT) / CONFIG_CCLK_DIV) +# if defined(__ADSPBF533__) || defined(__ADSPBF532__) || defined(__ADSPBF531__) +# define CCLK_VLEV_120 400000000 +# define CCLK_VLEV_125 533000000 +# elif defined(__ADSPBF537__) || defined(__ADSPBF536__) || defined(__ADSPBF534__) +# define CCLK_VLEV_120 401000000 +# define CCLK_VLEV_125 401000000 +# elif defined(__ADSPBF561__) +# define CCLK_VLEV_120 300000000 +# define CCLK_VLEV_125 501000000 +# endif +# if BFIN_CCLK < CCLK_VLEV_120 +# define CONFIG_VR_CTL_VLEV VLEV_120 +# elif BFIN_CCLK < CCLK_VLEV_125 +# define CONFIG_VR_CTL_VLEV VLEV_125 +# else +# define CONFIG_VR_CTL_VLEV VLEV_130 +# endif +# if defined(__ADSPBF52x__) /* TBD; use default */ +# undef CONFIG_VR_CTL_VLEV +# define CONFIG_VR_CTL_VLEV VLEV_110 +# elif defined(__ADSPBF54x__) /* TBD; use default */ +# undef CONFIG_VR_CTL_VLEV +# define CONFIG_VR_CTL_VLEV VLEV_120 +# endif + +# ifdef CONFIG_BFIN_MAC +# define CONFIG_VR_CTL_CLKBUF CLKBUFOE +# else +# define CONFIG_VR_CTL_CLKBUF 0 +# endif + +# if defined(__ADSPBF52x__) +# define CONFIG_VR_CTL_FREQ FREQ_1000 +# else +# define CONFIG_VR_CTL_FREQ (GAIN_20 | FREQ_1000) +# endif + +# define CONFIG_VR_CTL_VAL (CONFIG_VR_CTL_CLKBUF | CONFIG_VR_CTL_VLEV | CONFIG_VR_CTL_FREQ) +#endif + +__attribute__((saveall)) +void initcode(ADI_BOOT_DATA *bootstruct) +{ + uint32_t old_baud = serial_init(); + +#ifdef CONFIG_HW_WATCHDOG +# ifndef CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE +# define CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE 20000 +# endif + /* Program the watchdog with an initial timeout of ~20 seconds. + * Hopefully that should be long enough to load the u-boot LDR + * (from wherever) and then the common u-boot code can take over. + * In bypass mode, the start.S would have already set a much lower + * timeout, so don't clobber that. + */ + if (CONFIG_BFIN_BOOT_MODE != BFIN_BOOT_BYPASS) { + bfin_write_WDOG_CNT(MSEC_TO_SCLK(CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE)); + bfin_write_WDOG_CTL(0); + } +#endif + + serial_putc('S'); + + /* Blackfin bootroms use the SPI slow read opcode instead of the SPI + * fast read, so we need to slow down the SPI clock a lot more during + * boot. Once we switch over to u-boot's SPI flash driver, we'll + * increase the speed appropriately. + */ + if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_SPI_MASTER) +#ifdef SPI0_BAUD + bfin_write_SPI0_BAUD(CONFIG_SPI_BAUD_INITBLOCK); +#else + bfin_write_SPI_BAUD(CONFIG_SPI_BAUD_INITBLOCK); +#endif + + serial_putc('B'); + + /* Disable all peripheral wakeups except for the PLL event. */ +#ifdef SIC_IWR0 + bfin_write_SIC_IWR0(1); + bfin_write_SIC_IWR1(0); +# ifdef SIC_IWR2 + bfin_write_SIC_IWR2(0); +# endif +#elif defined(SICA_IWR0) + bfin_write_SICA_IWR0(1); + bfin_write_SICA_IWR1(0); +#else + bfin_write_SIC_IWR(1); +#endif + + serial_putc('L'); + + bfin_write_PLL_LOCKCNT(CONFIG_PLL_LOCKCNT_VAL); + + serial_putc('A'); + + /* Only reprogram when needed to avoid triggering unnecessary + * PLL relock sequences. + */ + if (bfin_read_VR_CTL() != CONFIG_VR_CTL_VAL) { + serial_putc('!'); + bfin_write_VR_CTL(CONFIG_VR_CTL_VAL); + asm("idle;"); + } + + serial_putc('C'); + + bfin_write_PLL_DIV(CONFIG_PLL_DIV_VAL); + + serial_putc('K'); + + /* Only reprogram when needed to avoid triggering unnecessary + * PLL relock sequences. + */ + if (bfin_read_PLL_CTL() != CONFIG_PLL_CTL_VAL) { + serial_putc('!'); + bfin_write_PLL_CTL(CONFIG_PLL_CTL_VAL); + asm("idle;"); + } + + /* Since we've changed the SCLK above, we may need to update + * the UART divisors (UART baud rates are based on SCLK). + */ + serial_reset_baud(old_baud); + + serial_putc('F'); + + /* Program the async banks controller. */ + bfin_write_EBIU_AMBCTL0(CONFIG_EBIU_AMBCTL0_VAL); + bfin_write_EBIU_AMBCTL1(CONFIG_EBIU_AMBCTL1_VAL); + bfin_write_EBIU_AMGCTL(CONFIG_EBIU_AMGCTL_VAL); + +#ifdef EBIU_MODE + /* Not all parts have these additional MMRs. */ + bfin_write_EBIU_MBSCTL(CONFIG_EBIU_MBSCTL_VAL); + bfin_write_EBIU_MODE(CONFIG_EBIU_MODE_VAL); + bfin_write_EBIU_FCTL(CONFIG_EBIU_FCTL_VAL); +#endif + + serial_putc('I'); + + /* Program the external memory controller. */ +#ifdef EBIU_RSTCTL + bfin_write_EBIU_RSTCTL(bfin_read_EBIU_RSTCTL() | 0x1 /*DDRSRESET*/ | CONFIG_EBIU_RSTCTL_VAL); + bfin_write_EBIU_DDRCTL0(CONFIG_EBIU_DDRCTL0_VAL); + bfin_write_EBIU_DDRCTL1(CONFIG_EBIU_DDRCTL1_VAL); + bfin_write_EBIU_DDRCTL2(CONFIG_EBIU_DDRCTL2_VAL); +# ifdef CONFIG_EBIU_DDRCTL3_VAL + /* default is disable, so don't need to force this */ + bfin_write_EBIU_DDRCTL3(CONFIG_EBIU_DDRCTL3_VAL); +# endif +#else + bfin_write_EBIU_SDRRC(CONFIG_EBIU_SDRRC_VAL); + bfin_write_EBIU_SDBCTL(CONFIG_EBIU_SDBCTL_VAL); + bfin_write_EBIU_SDGCTL(CONFIG_EBIU_SDGCTL_VAL); +#endif + + serial_putc('N'); + + /* Restore all peripheral wakeups. */ +#ifdef SIC_IWR0 + bfin_write_SIC_IWR0(-1); + bfin_write_SIC_IWR1(-1); +# ifdef SIC_IWR2 + bfin_write_SIC_IWR2(-1); +# endif +#elif defined(SICA_IWR0) + bfin_write_SICA_IWR0(-1); + bfin_write_SICA_IWR1(-1); +#else + bfin_write_SIC_IWR(-1); +#endif + + serial_putc('>'); + serial_putc('\n'); + + serial_deinit(); +} diff --git a/cpu/blackfin/interrupt.S b/cpu/blackfin/interrupt.S new file mode 100644 index 0000000..dd2cc53 --- /dev/null +++ b/cpu/blackfin/interrupt.S @@ -0,0 +1,33 @@ +/* + * interrupt.S - trampoline default exceptions/interrupts to C handlers + * + * Copyright (c) 2005-2007 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <asm/blackfin.h> +#include <asm/entry.h> + +.text + +/* default entry point for exceptions */ +ENTRY(_trap) + SAVE_ALL_SYS + r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ + sp += -12; + call _trap_c; + sp += 12; + RESTORE_ALL_SYS + rtx; +ENDPROC(_trap) + +/* default entry point for interrupts */ +ENTRY(_evt_default) + SAVE_ALL_SYS + r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ + sp += -12; + call _bfin_panic; + sp += 12; + RESTORE_ALL_SYS + rti; +ENDPROC(_evt_default) diff --git a/cpu/blackfin/interrupts.c b/cpu/blackfin/interrupts.c new file mode 100644 index 0000000..80c5505 --- /dev/null +++ b/cpu/blackfin/interrupts.c @@ -0,0 +1,155 @@ +/* + * U-boot - interrupts.c Interrupt related routines + * + * Copyright (c) 2005-2008 Analog Devices Inc. + * + * This file is based on interrupts.c + * Copyright 1996 Roman Zippel + * Copyright 1999 D. Jeff Dionne <jeff@uclinux.org> + * Copyright 2000-2001 Lineo, Inc. D. Jefff Dionne <jeff@lineo.ca> + * Copyright 2002 Arcturus Networks Inc. MaTed <mated@sympatico.ca> + * Copyright 2003 Metrowerks/Motorola + * Copyright 2003 Bas Vermeulen <bas@buyways.nl>, + * BuyWays B.V. (www.buyways.nl) + * + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <config.h> +#include <asm/blackfin.h> +#include "cpu.h" + +static ulong timestamp; +static ulong last_time; +static int int_flag; + +int irq_flags; /* needed by asm-blackfin/system.h */ + +/* Functions just to satisfy the linker */ + +/* + * This function is derived from PowerPC code (read timebase as long long). + * On Blackfin it just returns the timer value. + */ +unsigned long long get_ticks(void) +{ + return get_timer(0); +} + +/* + * This function is derived from PowerPC code (timebase clock frequency). + * On Blackfin it returns the number of timer ticks per second. + */ +ulong get_tbclk(void) +{ + ulong tbclk; + + tbclk = CFG_HZ; + return tbclk; +} + +void enable_interrupts(void) +{ + local_irq_restore(int_flag); +} + +int disable_interrupts(void) +{ + local_irq_save(int_flag); + return 1; +} + +void udelay(unsigned long usec) +{ + unsigned long delay, start, stop; + unsigned long cclk; + cclk = (CONFIG_CCLK_HZ); + + while (usec > 1) { + /* + * how many clock ticks to delay? + * - request(in useconds) * clock_ticks(Hz) / useconds/second + */ + if (usec < 1000) { + delay = (usec * (cclk / 244)) >> 12; + usec = 0; + } else { + delay = (1000 * (cclk / 244)) >> 12; + usec -= 1000; + } + + asm volatile (" %0 = CYCLES;" : "=r" (start)); + do { + asm volatile (" %0 = CYCLES; " : "=r" (stop)); + } while (stop - start < delay); + } + + return; +} + +#define MAX_TIM_LOAD 0xFFFFFFFF +int timer_init(void) +{ + *pTCNTL = 0x1; + *pTSCALE = 0x0; + *pTCOUNT = MAX_TIM_LOAD; + *pTPERIOD = MAX_TIM_LOAD; + *pTCNTL = 0x7; + asm("CSYNC;"); + + timestamp = 0; + last_time = 0; + + return 0; +} + +/* + * Any network command or flash + * command is started get_timer shall + * be called before TCOUNT gets reset, + * to implement the accurate timeouts. + * + * How ever milliconds doesn't return + * the number that has been elapsed from + * the last reset. + * + * As get_timer is used in the u-boot + * only for timeouts this should be + * sufficient + */ +ulong get_timer(ulong base) +{ + ulong milisec; + + /* Number of clocks elapsed */ + ulong clocks = (MAX_TIM_LOAD - (*pTCOUNT)); + + /* + * Find if the TCOUNT is reset + * timestamp gives the number of times + * TCOUNT got reset + */ + if (clocks < last_time) + timestamp++; + last_time = clocks; + + /* Get the number of milliseconds */ + milisec = clocks / (CONFIG_CCLK_HZ / 1000); + + /* + * Find the number of millisonds that + * got elapsed before this TCOUNT cycle + */ + milisec += timestamp * (MAX_TIM_LOAD / (CONFIG_CCLK_HZ / 1000)); + + return (milisec - base); +} + +void reset_timer(void) +{ + timestamp = 0; +} diff --git a/cpu/blackfin/reset.c b/cpu/blackfin/reset.c new file mode 100644 index 0000000..d1e34b3 --- /dev/null +++ b/cpu/blackfin/reset.c @@ -0,0 +1,96 @@ +/* + * reset.c - logic for resetting the cpu + * + * Copyright (c) 2005-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <command.h> +#include <asm/blackfin.h> +#include "cpu.h" + +/* A system soft reset makes external memory unusable so force + * this function into L1. We use the compiler ssync here rather + * than SSYNC() because it's safe (no interrupts and such) and + * we save some L1. We do not need to force sanity in the SYSCR + * register as the BMODE selection bit is cleared by the soft + * reset while the Core B bit (on dual core parts) is cleared by + * the core reset. + */ +__attribute__ ((__l1_text__, __noreturn__)) +void bfin_reset(void) +{ + /* Wait for completion of "system" events such as cache line + * line fills so that we avoid infinite stalls later on as + * much as possible. This code is in L1, so it won't trigger + * any such event after this point in time. + */ + __builtin_bfin_ssync(); + + while (1) { + /* Initiate System software reset. */ + bfin_write_SWRST(0x7); + + /* Due to the way reset is handled in the hardware, we need + * to delay for 7 SCLKS. The only reliable way to do this is + * to calculate the CCLK/SCLK ratio and multiply 7. For now, + * we'll assume worse case which is a 1:15 ratio. + */ + asm( + "LSETUP (1f, 1f) LC0 = %0\n" + "1: nop;" + : + : "a" (15 * 7) + : "LC0", "LB0", "LT0" + ); + + /* Clear System software reset */ + bfin_write_SWRST(0); + + /* Wait for the SWRST write to complete. Cannot rely on SSYNC + * though as the System state is all reset now. + */ + asm( + "LSETUP (1f, 1f) LC1 = %0\n" + "1: nop;" + : + : "a" (15 * 1) + : "LC1", "LB1", "LT1" + ); + + /* Issue core reset */ + asm("raise 1"); + } +} + +/* We need to trampoline ourselves up into L1 since our linker + * does not have relaxtion support and will only generate a + * PC relative call with a 25 bit immediate. This is not enough + * to get us from the top of SDRAM into L1. + */ +__attribute__ ((__noreturn__)) +static inline void bfin_reset_trampoline(void) +{ + if (board_reset) + board_reset(); + while (1) + asm("jump (%0);" : : "a" (bfin_reset)); +} + +__attribute__ ((__noreturn__)) +void bfin_reset_or_hang(void) +{ +#ifdef CONFIG_PANIC_HANG + hang(); +#else + bfin_reset_trampoline(); +#endif +} + +int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + bfin_reset_trampoline(); + return 0; +} diff --git a/cpu/blackfin/serial.c b/cpu/blackfin/serial.c new file mode 100644 index 0000000..0dfee51 --- /dev/null +++ b/cpu/blackfin/serial.c @@ -0,0 +1,124 @@ +/* + * U-boot - serial.c Blackfin Serial Driver + * + * Copyright (c) 2005-2008 Analog Devices Inc. + * + * Copyright (c) 2003 Bas Vermeulen <bas@buyways.nl>, + * BuyWays B.V. (www.buyways.nl) + * + * Based heavily on: + * blkfinserial.c: Serial driver for BlackFin DSP internal USRTs. + * Copyright(c) 2003 Metrowerks <mwaddel@metrowerks.com> + * Copyright(c) 2001 Tony Z. Kou <tonyko@arcturusnetworks.com> + * Copyright(c) 2001-2002 Arcturus Networks Inc. <www.arcturusnetworks.com> + * + * Based on code from 68328 version serial driver imlpementation which was: + * Copyright (C) 1995 David S. Miller <davem@caip.rutgers.edu> + * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com> + * Copyright (C) 1998, 1999 D. Jeff Dionne <jeff@uclinux.org> + * Copyright (C) 1999 Vladimir Gurevich <vgurevic@cisco.com> + * + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <watchdog.h> +#include <asm/blackfin.h> +#include <asm/mach-common/bits/uart.h> + +#if defined(UART_LSR) && (CONFIG_UART_CONSOLE != 0) +# error CONFIG_UART_CONSOLE must be 0 on parts with only one UART +#endif + +#include "serial.h" + +/* Symbol for our assembly to call. */ +void serial_set_baud(uint32_t baud) +{ + serial_early_set_baud(baud); +} + +/* Symbol for common u-boot code to call. + * Setup the baudrate (brg: baudrate generator). + */ +void serial_setbrg(void) +{ + DECLARE_GLOBAL_DATA_PTR; + serial_set_baud(gd->baudrate); +} + +/* Symbol for our assembly to call. */ +void serial_initialize(void) +{ + serial_early_init(); +} + +/* Symbol for common u-boot code to call. */ +int serial_init(void) +{ + serial_initialize(); + serial_setbrg(); + return 0; +} + +void serial_putc(const char c) +{ + /* send a \r for compatibility */ + if (c == '\n') + serial_putc('\r'); + + WATCHDOG_RESET(); + + /* wait for the hardware fifo to clear up */ + while (!(*pUART_LSR & THRE)) + continue; + + /* queue the character for transmission */ + *pUART_THR = c; + SSYNC(); + + WATCHDOG_RESET(); + + /* wait for the byte to be shifted over the line */ + while (!(*pUART_LSR & TEMT)) + continue; +} + +int serial_tstc(void) +{ + WATCHDOG_RESET(); + return (*pUART_LSR & DR) ? 1 : 0; +} + +int serial_getc(void) +{ + uint16_t uart_lsr_val, uart_rbr_val; + + /* wait for data ! */ + while (!serial_tstc()) + continue; + + /* clear the status and grab the new byte */ + uart_lsr_val = *pUART_LSR; + uart_rbr_val = *pUART_RBR; + + 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); + return -1; + } + + return uart_rbr_val & 0xFF; +} + +void serial_puts(const char *s) +{ + while (*s) + serial_putc(*s++); +} diff --git a/cpu/blackfin/serial.h b/cpu/blackfin/serial.h new file mode 100644 index 0000000..1f0f4b4 --- /dev/null +++ b/cpu/blackfin/serial.h @@ -0,0 +1,275 @@ +/* + * serial.h - common serial defines for early debug and serial driver. + * any functions defined here must be always_inline since + * initcode cannot have function calls. + * + * Copyright (c) 2004-2007 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __BFIN_CPU_SERIAL_H__ +#define __BFIN_CPU_SERIAL_H__ + +#include <asm/blackfin.h> +#include <asm/mach-common/bits/uart.h> + +#ifdef CONFIG_DEBUG_EARLY_SERIAL +# define BFIN_DEBUG_EARLY_SERIAL 1 +#else +# define BFIN_DEBUG_EARLY_SERIAL 0 +#endif + +#define LOB(x) ((x) & 0xFF) +#define HIB(x) (((x) >> 8) & 0xFF) + +#ifndef UART_LSR +# if (CONFIG_UART_CONSOLE == 3) +# define pUART_DLH pUART3_DLH +# define pUART_DLL pUART3_DLL +# define pUART_GCTL pUART3_GCTL +# define pUART_IER pUART3_IER +# define pUART_IERC pUART3_IER_CLEAR +# define pUART_LCR pUART3_LCR +# define pUART_LSR pUART3_LSR +# define pUART_RBR pUART3_RBR +# define pUART_THR pUART3_THR +# define UART_THR UART3_THR +# define UART_LSR UART3_LSR +# elif (CONFIG_UART_CONSOLE == 2) +# define pUART_DLH pUART2_DLH +# define pUART_DLL pUART2_DLL +# define pUART_GCTL pUART2_GCTL +# define pUART_IER pUART2_IER +# define pUART_IERC pUART2_IER_CLEAR +# define pUART_LCR pUART2_LCR +# define pUART_LSR pUART2_LSR +# define pUART_RBR pUART2_RBR +# define pUART_THR pUART2_THR +# define UART_THR UART2_THR +# define UART_LSR UART2_LSR +# elif (CONFIG_UART_CONSOLE == 1) +# define pUART_DLH pUART1_DLH +# define pUART_DLL pUART1_DLL +# define pUART_GCTL pUART1_GCTL +# define pUART_IER pUART1_IER +# define pUART_IERC pUART1_IER_CLEAR +# define pUART_LCR pUART1_LCR +# define pUART_LSR pUART1_LSR +# define pUART_RBR pUART1_RBR +# define pUART_THR pUART1_THR +# define UART_THR UART1_THR +# define UART_LSR UART1_LSR +# elif (CONFIG_UART_CONSOLE == 0) +# define pUART_DLH pUART0_DLH +# define pUART_DLL pUART0_DLL +# define pUART_GCTL pUART0_GCTL +# define pUART_IER pUART0_IER +# define pUART_IERC pUART0_IER_CLEAR +# define pUART_LCR pUART0_LCR +# define pUART_LSR pUART0_LSR +# define pUART_RBR pUART0_RBR +# define pUART_THR pUART0_THR +# define UART_THR UART0_THR +# define UART_LSR UART0_LSR +# endif +#endif + +#ifndef __ASSEMBLY__ + +/* We cannot use get_sclk() in initcode as it is defined elsewhere. */ +#ifdef BFIN_IN_INITCODE +# define get_sclk() (CONFIG_CLKIN_HZ * CONFIG_VCO_MULT / CONFIG_SCLK_DIV) +#endif + +#ifdef __ADSPBF54x__ +# define ACCESS_LATCH() +# define ACCESS_PORT_IER() +# define CLEAR_IER() (*pUART_IERC = 0) +#else +# define ACCESS_LATCH() (*pUART_LCR |= DLAB) +# define ACCESS_PORT_IER() (*pUART_LCR &= ~DLAB) +# define CLEAR_IER() (*pUART_IER = 0) +#endif + +__attribute__((always_inline)) +static inline void serial_do_portmux(void) +{ +#ifdef __ADSPBF52x__ +# define DO_MUX(port, mux, tx, rx) \ + bfin_write_PORT##port##_MUX((bfin_read_PORT##port##_MUX() & ~PORT_x_MUX_##mux##_MASK) | PORT_x_MUX_##mux##_FUNC_3); \ + bfin_write_PORT##port##_FER(bfin_read_PORT##port##_FER() | P##port##tx | P##port##rx); + switch (CONFIG_UART_CONSOLE) { + case 0: DO_MUX(G, 2, 7, 8); break; /* Port G; mux 2; PG2 and PG8 */ + case 1: DO_MUX(F, 5, 14, 15); break; /* Port F; mux 5; PF14 and PF15 */ + } + SSYNC(); +#elif defined(__ADSPBF537__) || defined(__ADSPBF536__) || defined(__ADSPBF534__) +# define DO_MUX(func, tx, rx) \ + bfin_write_PORT_MUX(bfin_read_PORT_MUX() & ~(func)); \ + bfin_write_PORTF_FER(bfin_read_PORTF_FER() | PF##tx | PF##rx); + switch (CONFIG_UART_CONSOLE) { + case 0: DO_MUX(PFDE, 0, 1); break; + case 1: DO_MUX(PFTE, 2, 3); break; + } + SSYNC(); +#elif defined(__ADSPBF54x__) +# define DO_MUX(port, tx, rx) \ + bfin_write_PORT##port##_MUX((bfin_read_PORT##port##_MUX() & ~(PORT_x_MUX_##tx##_MASK | PORT_x_MUX_##rx##_MASK)) | PORT_x_MUX_##tx##_FUNC_1 | PORT_x_MUX_##rx##_FUNC_1); \ + bfin_write_PORT##port##_FER(bfin_read_PORT##port##_FER() | P##port##tx | P##port##rx); + switch (CONFIG_UART_CONSOLE) { + case 0: DO_MUX(E, 7, 8); break; /* Port E; PE7 and PE8 */ + case 1: DO_MUX(H, 0, 1); break; /* Port H; PH0 and PH1 */ + case 2: DO_MUX(B, 4, 5); break; /* Port B; PB4 and PB5 */ + case 3: DO_MUX(B, 6, 7); break; /* Port B; PB6 and PB7 */ + } + SSYNC(); +#endif +} + +__attribute__((always_inline)) +static inline void serial_early_init(void) +{ + /* handle portmux crap on different Blackfins */ + serial_do_portmux(); + + /* Enable UART */ + *pUART_GCTL = UCEN; + + /* Set LCR to Word Lengh 8-bit word select */ + *pUART_LCR = WLS_8; + + SSYNC(); +} + +__attribute__((always_inline)) +static inline uint32_t serial_early_get_baud(void) +{ + /* If the UART isnt enabled, then we are booting an LDR + * from a non-UART source (so like flash) which means + * the baud rate here is meaningless. + */ + if ((*pUART_GCTL & UCEN) != UCEN) + return 0; + +#if (0) /* See comment for serial_reset_baud() in initcode.c */ + /* Set DLAB in LCR to Access DLL and DLH */ + ACCESS_LATCH(); + SSYNC(); + + uint8_t dll = *pUART_DLL; + uint8_t dlh = *pUART_DLH; + uint16_t divisor = (dlh << 8) | dll; + uint32_t baud = get_sclk() / (divisor * 16); + + /* Clear DLAB in LCR to Access THR RBR IER */ + ACCESS_PORT_IER(); + SSYNC(); + + return baud; +#else + return CONFIG_BAUDRATE; +#endif +} + +__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. + */ + uint16_t divisor = (get_sclk() / (baud * 16)) + 1; + + /* Set DLAB in LCR to Access DLL and DLH */ + ACCESS_LATCH(); + SSYNC(); + + /* Program the divisor to get the baud rate we want */ + *pUART_DLL = LOB(divisor); + *pUART_DLH = HIB(divisor); + SSYNC(); + + /* Clear DLAB in LCR to Access THR RBR IER */ + ACCESS_PORT_IER(); + SSYNC(); +} + +#ifndef BFIN_IN_INITCODE +__attribute__((always_inline)) +static inline void serial_early_puts(const char *s) +{ + if (BFIN_DEBUG_EARLY_SERIAL) { + serial_puts("Early: "); + serial_puts(s); + } +} +#endif + +#else + +.macro serial_early_init +#ifdef CONFIG_DEBUG_EARLY_SERIAL + call _serial_initialize; +#endif +.endm + +.macro serial_early_set_baud +#ifdef CONFIG_DEBUG_EARLY_SERIAL + R0.L = LO(CONFIG_BAUDRATE); + R0.H = HI(CONFIG_BAUDRATE); + call _serial_set_baud; +#endif +.endm + +/* Recursively expand calls to _serial_putc for every byte + * passed to us. Append a newline when we're all done. + */ +.macro _serial_early_putc byte:req morebytes:vararg +#ifdef CONFIG_DEBUG_EARLY_SERIAL + R0 = \byte; + call _serial_putc; +.ifnb \morebytes + _serial_early_putc \morebytes +.else +.if (\byte != '\n') + _serial_early_putc '\n' +.endif +.endif +#endif +.endm + +/* Wrapper around recurisve _serial_early_putc macro which + * simply prepends the string "Early: " + */ +.macro serial_early_putc byte:req morebytes:vararg +#ifdef CONFIG_DEBUG_EARLY_SERIAL + _serial_early_putc 'E', 'a', 'r', 'l', 'y', ':', ' ', \byte, \morebytes +#endif +.endm + +/* Since we embed the string right into our .text section, we need + * to find its address. We do this by getting our PC and adding 2 + * bytes (which is the length of the jump instruction). Then we + * pass this address to serial_puts(). + */ +#ifdef CONFIG_DEBUG_EARLY_SERIAL +# define serial_early_puts(str) \ + call _get_pc; \ + jump 1f; \ + .ascii "Early:"; \ + .ascii __FILE__; \ + .ascii ": "; \ + .ascii str; \ + .asciz "\n"; \ + .align 4; \ +1: \ + R0 += 2; \ + call _serial_puts; +#else +# define serial_early_puts(str) +#endif + +#endif + +#endif diff --git a/cpu/blackfin/start.S b/cpu/blackfin/start.S new file mode 100644 index 0000000..30212e9 --- /dev/null +++ b/cpu/blackfin/start.S @@ -0,0 +1,219 @@ +/* + * U-boot - start.S Startup file for Blackfin u-boot + * + * Copyright (c) 2005-2007 Analog Devices Inc. + * + * This file is based on head.S + * Copyright (c) 2003 Metrowerks/Motorola + * Copyright (C) 1998 D. Jeff Dionne <jeff@ryeham.ee.ryerson.ca>, + * Kenneth Albanowski <kjahds@kjahds.com>, + * The Silver Hammer Group, Ltd. + * (c) 1995, Dionne & Associates + * (c) 1995, DKG Display Tech. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <config.h> +#include <asm/blackfin.h> +#include <asm/mach-common/bits/core.h> +#include <asm/mach-common/bits/dma.h> +#include <asm/mach-common/bits/pll.h> + +#include "serial.h" + +/* It may seem odd that we make calls to functions even though we haven't + * relocated ourselves yet out of {flash,ram,wherever}. This is OK because + * the "call" instruction in the Blackfin architecture is actually PC + * relative. So we can call functions all we want and not worry about them + * not being relocated yet. + */ + +.text +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); + +#ifdef CONFIG_HW_WATCHDOG +# ifndef CONFIG_HW_WATCHDOG_TIMEOUT_START +# define CONFIG_HW_WATCHDOG_TIMEOUT_START 5000 +# endif + /* Program the watchdog with an initial timeout of ~5 seconds. + * That should be long enough to bootstrap ourselves up and + * then the common u-boot code can take over. + */ + P0.L = LO(WDOG_CNT); + P0.H = HI(WDOG_CNT); + R0.L = 0; + R0.H = HI(MSEC_TO_SCLK(CONFIG_HW_WATCHDOG_TIMEOUT_START)); + [P0] = R0; + /* fire up the watchdog - R0.L above needs to be 0x0000 */ + W[P0 + (WDOG_CTL - WDOG_CNT)] = R0; +#endif + + /* Turn on the serial for debugging the init process */ + serial_early_init + serial_early_set_baud + + serial_early_puts("Init Registers"); + + /* Disable nested interrupts and enable CYCLES for udelay() */ + R0 = CCEN | 0x30; + SYSCFG = R0; + + /* Zero out registers required by Blackfin ABI. + * http://docs.blackfin.uclinux.org/doku.php?id=application_binary_interface + */ + r1 = 0 (x); + /* Disable circular buffers */ + l0 = r1; + l1 = r1; + l2 = r1; + l3 = r1; + /* Disable hardware loops in case we were started by 'go' */ + lc0 = r1; + lc1 = r1; + + /* Save RETX so we can pass it while booting Linux */ + r7 = RETX; + +#if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_BYPASS) + /* In bypass mode, we don't have an LDR with an init block + * so we need to explicitly call it ourselves. This will + * reprogram our clocks and setup our async banks. + */ + /* XXX: we should DMA this into L1, put external memory into + * self refresh, and then jump there ... + */ + call _get_pc; + r3 = 0x0; + r3.h = 0x2000; + cc = r0 < r3 (iu); + if cc jump .Lproc_initialized; + + serial_early_puts("Program Clocks"); + + call _initcode; + + /* Since we reprogrammed SCLK, we need to update the serial divisor */ + serial_early_set_baud + +.Lproc_initialized: +#endif + + /* Inform upper layers if we had to do the relocation ourselves. + * This allows us to detect whether we were loaded by 'go 0x1000' + * or by the bootrom from an LDR. "r6" is "loaded_from_ldr". + */ + r6 = 1 (x); + + /* Relocate from wherever are (FLASH/RAM/etc...) to the + * hardcoded monitor location in the end of RAM. + */ + serial_early_puts("Relocate"); + call _get_pc; +.Loffset: + r2.l = .Loffset; + r2.h = .Loffset; + r3.l = _start; + r3.h = _start; + r1 = r2 - r3; + + r0 = r0 - r1; + + cc = r0 == r3; + if cc jump .Lnorelocate; + + r6 = 0 (x); + p1 = r0; + + p2.l = LO(CFG_MONITOR_BASE); + p2.h = HI(CFG_MONITOR_BASE); + + p3 = 0x04; + p4.l = LO(CFG_MONITOR_BASE + CFG_MONITOR_LEN); + p4.h = HI(CFG_MONITOR_BASE + CFG_MONITOR_LEN); +.Lloop1: + r1 = [p1 ++ p3]; + [p2 ++ p3] = r1; + cc=p2==p4; + if !cc jump .Lloop1; + + /* Initialize BSS section ... we know that memset() does not + * use the BSS, so it is safe to call here. The bootrom LDR + * takes care of clearing things for us. + */ + serial_early_puts("Zero BSS"); + r0.l = __bss_start; + r0.h = __bss_start; + r1 = 0 (x); + r2.l = __bss_end; + r2.h = __bss_end; + r2 = r2 - r0; + call _memset; + +.Lnorelocate: + + /* Setup the actual stack in external memory */ + r0.h = HI(CONFIG_STACKBASE); + r0.l = LO(CONFIG_STACKBASE); + sp = r0; + fp = sp; + + /* 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 + * interrupt, and then returning from the highest interrupt + * level to the dummy "jump" until the interrupt controller + * services the pending 15 interrupt. + */ + serial_early_puts("Lower to 15"); + r0 = r7; + r1 = r6; + p0.l = LO(EVT15); + p0.h = HI(EVT15); + p1.l = _cpu_init_f; + p1.h = _cpu_init_f; + [p0] = p1; + p2.l = LO(IMASK); + p2.h = HI(IMASK); + p3.l = LO(EVT_IVG15); + p3.h = HI(EVT_IVG15); + [p2] = p3; + raise 15; + p4.l = .LWAIT_HERE; + p4.h = .LWAIT_HERE; + reti = p4; + rti; + +.LWAIT_HERE: + jump .LWAIT_HERE; +ENDPROC(_start) + +LENTRY(_get_pc) + r0 = rets; +#if ANOMALY_05000371 + NOP; + NOP; + NOP; +#endif + rts; +ENDPROC(_get_pc) diff --git a/cpu/blackfin/system_map.S b/cpu/blackfin/system_map.S new file mode 100644 index 0000000..286d7f3 --- /dev/null +++ b/cpu/blackfin/system_map.S @@ -0,0 +1,18 @@ +/* + * system_map.S - optional symbol lookup for debugging + * + * Copyright (c) 2007 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <config.h> + +#ifdef CONFIG_DEBUG_DUMP_SYMS +.data +.global _system_map +.type _system_map,@object +_system_map: +#include SYM_FILE +.asciz "" +.size _system_map,.-_system_map +#endif diff --git a/cpu/blackfin/traps.c b/cpu/blackfin/traps.c new file mode 100644 index 0000000..4474fe5 --- /dev/null +++ b/cpu/blackfin/traps.c @@ -0,0 +1,353 @@ +/* + * U-boot - traps.c Routines related to interrupts and exceptions + * + * Copyright (c) 2005-2008 Analog Devices Inc. + * + * This file is based on + * No original Copyright holder listed, + * Probabily original (C) Roman Zippel (assigned DJD, 1999) + * + * Copyright 2003 Metrowerks - for Blackfin + * Copyright 2000-2001 Lineo, Inc. D. Jeff Dionne <jeff@lineo.ca> + * Copyright 1999-2000 D. Jeff Dionne, <jeff@uclinux.org> + * + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <linux/types.h> +#include <asm/traps.h> +#include <asm/cplb.h> +#include <asm/io.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" + +#define trace_buffer_save(x) \ + do { \ + (x) = bfin_read_TBUFCTL(); \ + bfin_write_TBUFCTL((x) & ~TBUFEN); \ + } while (0) + +#define trace_buffer_restore(x) \ + bfin_write_TBUFCTL((x)) + +/* The purpose of this map is to provide a mapping of address<->cplb settings + * rather than an exact map of what is actually addressable on the part. This + * map covers all current Blackfin parts. If you try to access an address that + * is in this map but not actually on the part, you won't get an exception and + * reboot, you'll get an external hardware addressing error and reboot. Since + * only the ends matter (you did something wrong and the board reset), the means + * are largely irrelevant. + */ +struct memory_map { + uint32_t start, end; + uint32_t data_flags, inst_flags; +}; +const struct memory_map const bfin_memory_map[] = { + { /* external memory */ + .start = 0x00000000, + .end = 0x20000000, + .data_flags = SDRAM_DGENERIC, + .inst_flags = SDRAM_IGENERIC, + }, + { /* async banks */ + .start = 0x20000000, + .end = 0x30000000, + .data_flags = SDRAM_EBIU, + .inst_flags = SDRAM_INON_CHBL, + }, + { /* everything on chip */ + .start = 0xE0000000, + .end = 0xFFFFFFFF, + .data_flags = L1_DMEMORY, + .inst_flags = L1_IMEMORY, + } +}; + +void trap_c(struct pt_regs *regs) +{ + uint32_t trapnr = (regs->seqstat & EXCAUSE); + bool data = false; + + switch (trapnr) { + /* 0x26 - Data CPLB Miss */ + case VEC_CPLB_M: + + if (ANOMALY_05000261) { + static uint32_t last_cplb_fault_retx; + /* + * Work around an anomaly: if we see a new DCPLB fault, + * return without doing anything. Then, + * if we get the same fault again, handle it. + */ + if (last_cplb_fault_retx != regs->retx) { + last_cplb_fault_retx = regs->retx; + return; + } + } + + data = true; + /* fall through */ + + /* 0x27 - Instruction CPLB Miss */ + case VEC_CPLB_I_M: { + volatile uint32_t *CPLB_ADDR_BASE, *CPLB_DATA_BASE, *CPLB_ADDR, *CPLB_DATA; + uint32_t new_cplb_addr = 0, new_cplb_data = 0; + static size_t last_evicted; + size_t i; + + new_cplb_addr = (data ? bfin_read_DCPLB_FAULT_ADDR() : bfin_read_ICPLB_FAULT_ADDR()) & ~(4 * 1024 * 1024 - 1); + + for (i = 0; i < ARRAY_SIZE(bfin_memory_map); ++i) { + /* if the exception is inside this range, lets use it */ + if (new_cplb_addr >= bfin_memory_map[i].start && + new_cplb_addr < bfin_memory_map[i].end) + break; + } + if (i == ARRAY_SIZE(bfin_memory_map)) { + printf("%cCPLB exception outside of memory map at 0x%p\n", + (data ? 'D' : 'I'), new_cplb_addr); + bfin_panic(regs); + } else + debug("CPLB addr %p matches map 0x%p - 0x%p\n", new_cplb_addr, bfin_memory_map[i].start, bfin_memory_map[i].end); + new_cplb_data = (data ? bfin_memory_map[i].data_flags : bfin_memory_map[i].inst_flags); + + /* Turn the cache off */ + SSYNC(); + if (data) { + asm(" .align 8; "); + *pDMEM_CONTROL &= ~ENDCPLB; + } else { + asm(" .align 8; "); + *pIMEM_CONTROL &= ~ENICPLB; + } + SSYNC(); + + if (data) { + CPLB_ADDR_BASE = (uint32_t *)DCPLB_ADDR0; + CPLB_DATA_BASE = (uint32_t *)DCPLB_DATA0; + } else { + CPLB_ADDR_BASE = (uint32_t *)ICPLB_ADDR0; + CPLB_DATA_BASE = (uint32_t *)ICPLB_DATA0; + } + + /* find the next unlocked entry and evict it */ + i = last_evicted & 0xF; + debug("last evicted = %i\n", i); + CPLB_DATA = CPLB_DATA_BASE + i; + while (*CPLB_DATA & CPLB_LOCK) { + debug("skipping %i %p - %08X\n", i, CPLB_DATA, *CPLB_DATA); + i = (i + 1) & 0xF; /* wrap around */ + CPLB_DATA = CPLB_DATA_BASE + i; + } + CPLB_ADDR = CPLB_ADDR_BASE + i; + + debug("evicting entry %i: 0x%p 0x%08X\n", i, *CPLB_ADDR, *CPLB_DATA); + last_evicted = i + 1; + *CPLB_ADDR = new_cplb_addr; + *CPLB_DATA = new_cplb_data; + + /* dump current table for debugging purposes */ + CPLB_ADDR = CPLB_ADDR_BASE; + CPLB_DATA = CPLB_DATA_BASE; + for (i = 0; i < 16; ++i) + debug("%2i 0x%p 0x%08X\n", i, *CPLB_ADDR++, *CPLB_DATA++); + + /* Turn the cache back on */ + SSYNC(); + if (data) { + asm(" .align 8; "); + *pDMEM_CONTROL |= ENDCPLB; + } else { + asm(" .align 8; "); + *pIMEM_CONTROL |= ENICPLB; + } + SSYNC(); + + break; + } + + default: + /* All traps come here */ + bfin_panic(regs); + } +} + +#ifdef CONFIG_DEBUG_DUMP +# define ENABLE_DUMP 1 +#else +# define ENABLE_DUMP 0 +#endif + +#ifdef CONFIG_DEBUG_DUMP_SYMS +# define ENABLE_DUMP_SYMS 1 +#else +# define ENABLE_DUMP_SYMS 0 +#endif + +static const char *symbol_lookup(unsigned long addr, unsigned long *caddr) +{ + if (!ENABLE_DUMP_SYMS) + return NULL; + + extern const char system_map[] __attribute__((__weak__)); + const char *sym, *csym; + char *esym; + unsigned long sym_addr; + + sym = system_map; + csym = NULL; + *caddr = 0; + + while (*sym) { + sym_addr = simple_strtoul(sym, &esym, 16); + sym = esym + 1; + if (sym_addr > addr) + break; + *caddr = sym_addr; + csym = sym; + sym += strlen(sym) + 1; + } + + return csym; +} + +static void decode_address(char *buf, unsigned long address) +{ + unsigned long sym_addr; + const char *sym = symbol_lookup(address, &sym_addr); + + if (sym) { + sprintf(buf, "<0x%p> { %s + 0x%x }", address, sym, address - sym_addr); + return; + } + + if (!address) + sprintf(buf, "<0x%p> /* Maybe null pointer? */", address); + else if (address >= CFG_MONITOR_BASE && + address < CFG_MONITOR_BASE + CFG_MONITOR_LEN) + sprintf(buf, "<0x%p> /* somewhere in u-boot */", address); + else + sprintf(buf, "<0x%p> /* unknown address */", address); +} + +void dump(struct pt_regs *fp) +{ + char buf[150]; + size_t i; + + if (!ENABLE_DUMP) + return; + + 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); + for (i = 6; i <= 15; ++i) { + if (fp->ipend & (1 << i)) { + decode_address(buf, bfin_read32(EVT0 + 4*i)); + printf(" physical IVG%i asserted : %s\n", i, buf); + } + } + decode_address(buf, fp->rete); + printf(" RETE: %s\n", buf); + decode_address(buf, fp->retn); + printf(" RETN: %s\n", buf); + decode_address(buf, fp->retx); + printf(" RETX: %s\n", buf); + decode_address(buf, fp->rets); + printf(" RETS: %s\n", buf); + decode_address(buf, fp->pc); + printf(" PC : %s\n", buf); + + if (fp->seqstat & EXCAUSE) { + decode_address(buf, bfin_read_DCPLB_FAULT_ADDR()); + printf("DCPLB_FAULT_ADDR: %s\n", buf); + decode_address(buf, bfin_read_ICPLB_FAULT_ADDR()); + printf("ICPLB_FAULT_ADDR: %s\n", buf); + } + + printf("\nPROCESSOR STATE:\n"); + printf(" R0 : %08lx R1 : %08lx R2 : %08lx R3 : %08lx\n", + fp->r0, fp->r1, fp->r2, fp->r3); + printf(" R4 : %08lx R5 : %08lx R6 : %08lx R7 : %08lx\n", + fp->r4, fp->r5, fp->r6, fp->r7); + printf(" P0 : %08lx P1 : %08lx P2 : %08lx P3 : %08lx\n", + fp->p0, fp->p1, fp->p2, fp->p3); + printf(" P4 : %08lx P5 : %08lx FP : %08lx SP : %08lx\n", + fp->p4, fp->p5, fp->fp, fp); + printf(" LB0: %08lx LT0: %08lx LC0: %08lx\n", + fp->lb0, fp->lt0, fp->lc0); + printf(" LB1: %08lx LT1: %08lx LC1: %08lx\n", + fp->lb1, fp->lt1, fp->lc1); + printf(" B0 : %08lx L0 : %08lx M0 : %08lx I0 : %08lx\n", + fp->b0, fp->l0, fp->m0, fp->i0); + printf(" B1 : %08lx L1 : %08lx M1 : %08lx I1 : %08lx\n", + fp->b1, fp->l1, fp->m1, fp->i1); + printf(" B2 : %08lx L2 : %08lx M2 : %08lx I2 : %08lx\n", + fp->b2, fp->l2, fp->m2, fp->i2); + printf(" B3 : %08lx L3 : %08lx M3 : %08lx I3 : %08lx\n", + fp->b3, fp->l3, fp->m3, fp->i3); + printf("A0.w: %08lx A0.x: %08lx A1.w: %08lx A1.x: %08lx\n", + fp->a0w, fp->a0x, fp->a1w, fp->a1x); + + printf("USP : %08lx ASTAT: %08lx\n", + fp->usp, fp->astat); + + printf("\n"); +} + +void dump_bfin_trace_buffer(void) +{ + char buf[150]; + unsigned long tflags; + size_t i = 0; + + if (!ENABLE_DUMP) + return; + + trace_buffer_save(tflags); + + printf("Hardware Trace:\n"); + + if (bfin_read_TBUFSTAT() & TBUFCNT) { + for (; bfin_read_TBUFSTAT() & TBUFCNT; i++) { + decode_address(buf, bfin_read_TBUF()); + printf("%4i Target : %s\n", i, buf); + decode_address(buf, bfin_read_TBUF()); + printf(" Source : %s\n", buf); + } + } + + trace_buffer_restore(tflags); +} + +void bfin_panic(struct pt_regs *regs) +{ + if (ENABLE_DUMP) { + unsigned long tflags; + trace_buffer_save(tflags); + } + + puts( + "\n" + "\n" + "\n" + "Ack! Something bad happened to the Blackfin!\n" + "\n" + ); + dump(regs); + dump_bfin_trace_buffer(); + printf( + "\n" + "Please reset the board\n" + "\n" + ); + bfin_reset_or_hang(); +} diff --git a/cpu/blackfin/watchdog.c b/cpu/blackfin/watchdog.c new file mode 100644 index 0000000..b47c6b6 --- /dev/null +++ b/cpu/blackfin/watchdog.c @@ -0,0 +1,25 @@ +/* + * watchdog.c - driver for Blackfin on-chip watchdog + * + * Copyright (c) 2007-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <watchdog.h> +#include <asm/blackfin.h> + +#ifdef CONFIG_HW_WATCHDOG +void hw_watchdog_reset(void) +{ + bfin_write_WDOG_STAT(0); +} + +void hw_watchdog_init(void) +{ + bfin_write_WDOG_CNT(5 * get_sclk()); /* 5 second timeout */ + hw_watchdog_reset(); + bfin_write_WDOG_CTL(0x0); +} +#endif |