diff options
Diffstat (limited to 'roms/u-boot/arch/blackfin/cpu')
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/.gitignore | 2 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/Makefile | 45 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/cache.S | 87 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/cpu.c | 152 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/cpu.h | 23 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/gpio.c | 841 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/init.S | 9 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/init.lds.S | 25 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/initcode.c | 1040 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/initcode.h | 123 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/interrupt.S | 157 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/interrupts.c | 154 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/jtag-console.c | 228 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/os_log.c | 30 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/reset.c | 96 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/start.S | 253 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/traps.c | 435 | ||||
| -rw-r--r-- | roms/u-boot/arch/blackfin/cpu/u-boot.lds | 140 | 
18 files changed, 3840 insertions, 0 deletions
diff --git a/roms/u-boot/arch/blackfin/cpu/.gitignore b/roms/u-boot/arch/blackfin/cpu/.gitignore new file mode 100644 index 00000000..3df1fa21 --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/.gitignore @@ -0,0 +1,2 @@ +init.lds +init.elf diff --git a/roms/u-boot/arch/blackfin/cpu/Makefile b/roms/u-boot/arch/blackfin/cpu/Makefile new file mode 100644 index 00000000..cfbcd312 --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/Makefile @@ -0,0 +1,45 @@ +# +# 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. +# + +extra-y := init.elf +extra-y += initcode.o +extra-y += start.o +obj-y    := interrupt.o cache.o +obj-y  += cpu.o +obj-y += gpio.o +obj-y  += interrupts.o +obj-$(CONFIG_JTAG_CONSOLE) += jtag-console.o +obj-y  += os_log.o +obj-y  += reset.o +obj-y  += traps.o + +extra-y += check_initcode +clean-files := init.lds + +# make sure our initcode (which goes into LDR) does not +# have relocs or external references +CFLAGS_REMOVE_initcode.o := -ffunction-sections -fdata-sections +READINIT = env LC_ALL=C $(CROSS_COMPILE)readelf -s $< +$(obj)/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 + +CPPFLAGS_init.lds := -ansi + +quiet_cmd_link_init = LD      $@ +      cmd_link_init = $(LD) $(LDFLAGS) -T $^ -o $@ +$(obj)/init.elf: $(obj)/init.lds $(obj)/init.o $(obj)/initcode.o +	$(call if_changed,link_init) +targets += init.lds init.o diff --git a/roms/u-boot/arch/blackfin/cpu/cache.S b/roms/u-boot/arch/blackfin/cpu/cache.S new file mode 100644 index 00000000..5ca9e91d --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/cache.S @@ -0,0 +1,87 @@ +/* + * Blackfin cache control code + * + * Copyright 2003-2008 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include <config.h> +#include <linux/linkage.h> +#include <asm/blackfin.h> + +.text +/* Since all L1 caches work the same way, we use the same method for flushing + * them.  Only the actual flush instruction differs.  We write this in asm as + * GCC can be hard to coax into writing nice hardware loops. + * + * Also, we assume the following register setup: + * R0 = start address + * R1 = end address + */ +.macro do_flush flushins:req optflushins optnopins label + +	R2 = -L1_CACHE_BYTES; + +	/* start = (start & -L1_CACHE_BYTES) */ +	R0 = R0 & R2; + +	/* end = ((end - 1) & -L1_CACHE_BYTES) + L1_CACHE_BYTES; */ +	R1 += -1; +	R1 = R1 & R2; +	R1 += L1_CACHE_BYTES; + +	/* count = (end - start) >> L1_CACHE_SHIFT */ +	R2 = R1 - R0; +	R2 >>= L1_CACHE_SHIFT; +	P1 = R2; + +.ifnb \label +\label : +.endif +	P0 = R0; +	LSETUP (1f, 2f) LC1 = P1; +1: +.ifnb \optflushins +	\optflushins [P0]; +.endif +#if ANOMALY_05000443 +.ifb \optnopins +2: +.endif +	\flushins [P0++]; +.ifnb \optnopins +2:	\optnopins; +.endif +#else +2:	\flushins [P0++]; +#endif + +	RTS; +.endm + +/* Invalidate all instruction cache lines assocoiated with this memory area */ +ENTRY(_blackfin_icache_flush_range) +	do_flush IFLUSH, , nop +ENDPROC(_blackfin_icache_flush_range) + +/* Flush all cache lines assocoiated with this area of memory. */ +ENTRY(_blackfin_icache_dcache_flush_range) +	do_flush FLUSH, IFLUSH +ENDPROC(_blackfin_icache_dcache_flush_range) + +/* Throw away all D-cached data in specified region without any obligation to + * write them back.  Since the Blackfin ISA does not have an "invalidate" + * instruction, we use flush/invalidate.  Perhaps as a speed optimization we + * could bang on the DTEST MMRs ... + */ +ENTRY(_blackfin_dcache_flush_invalidate_range) +	do_flush FLUSHINV +ENDPROC(_blackfin_dcache_flush_invalidate_range) + +/* Flush all data cache lines assocoiated with this memory area */ +ENTRY(_blackfin_dcache_flush_range) +	do_flush FLUSH, , , .Ldfr +ENDPROC(_blackfin_dcache_flush_range) diff --git a/roms/u-boot/arch/blackfin/cpu/cpu.c b/roms/u-boot/arch/blackfin/cpu/cpu.c new file mode 100644 index 00000000..2409c300 --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/cpu.c @@ -0,0 +1,152 @@ +/* + * 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/ebiu.h> +#include <asm/mach-common/bits/trace.h> +#include <asm/serial.h> + +#include "cpu.h" +#include "initcode.h" + +ulong bfin_poweron_retx; + +#if defined(CONFIG_CORE1_RUN) && defined(COREB_L1_CODE_START) +void bfin_core1_start(void) +{ +#ifdef BF561_FAMILY +	/* Enable core 1 */ +	bfin_write_SYSCR(bfin_read_SYSCR() & ~0x0020); +#else +	/* Enable core 1 */ +	bfin_write32(RCU0_SVECT1, COREB_L1_CODE_START); +	bfin_write32(RCU0_CRCTL, 0); + +	bfin_write32(RCU0_CRCTL, 0x2); + +	/* Check if core 1 starts */ +	while (!(bfin_read32(RCU0_CRSTAT) & 0x2)) +		continue; + +	bfin_write32(RCU0_CRCTL, 0); + +	/* flag to notify cces core 1 application */ +	bfin_write32(SDU0_MSG_SET, (1 << 19)); +#endif +} +#endif + +__attribute__ ((__noreturn__)) +void cpu_init_f(ulong bootflag, ulong loaded_from_ldr) +{ +#ifndef CONFIG_BFIN_BOOTROM_USES_EVT1 +	/* Build a NOP slide over the LDR jump block.  Whee! */ +	char nops[0xC]; +	serial_early_puts("NOP Slide\n"); +	memset(nops, 0x00, sizeof(nops)); +	memcpy((void *)L1_INST_SRAM, nops, sizeof(nops)); +#endif + +	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[], _text_l1_lma[], _text_l1_len[]; +		memcpy(&_stext_l1, &_text_l1_lma, (unsigned long)_text_l1_len); +		extern char _sdata_l1[], _data_l1_lma[], _data_l1_len[]; +		memcpy(&_sdata_l1, &_data_l1_lma, (unsigned long)_data_l1_len); +	} + +	/* +	 * Make sure our async settings are committed.  Some bootroms +	 * (like the BF537) will reset some registers on us after it +	 * has finished loading the LDR.  Or if we're booting over +	 * JTAG, the initcode never got a chance to run.  Or if we +	 * aren't booting from parallel flash, the initcode skipped +	 * this step completely. +	 */ +	program_async_controller(NULL); + +	/* Save RETX so we can pass it while booting Linux */ +	bfin_poweron_retx = bootflag; + +#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. +	 */ +# ifdef SWRST +	bfin_write_SWRST(DOUBLE_FAULT); +# endif +#endif + +#if defined(CONFIG_CORE1_RUN) && defined(COREB_L1_CODE_START) +	bfin_core1_start(); +#endif + +	serial_early_puts("Board init flash\n"); +	board_init_f(bootflag); + +	/* should not be reached */ +	while (1); +} + +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); +#elif defined(SIC_IMASK) +	bfin_write_SIC_IMASK(0); +#endif +	/* Set up a dummy NMI handler if needed.  */ +	if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_BYPASS || ANOMALY_05000219) +		bfin_write_EVT2(evt_nmi);	/* 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 hardware error irq */ +	irq_flags = 0x3f; +	local_irq_enable(); +	return 0; +} diff --git a/roms/u-boot/arch/blackfin/cpu/cpu.h b/roms/u-boot/arch/blackfin/cpu/cpu.h new file mode 100644 index 00000000..f6aa1b65 --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/cpu.h @@ -0,0 +1,23 @@ +/* + *  U-boot - cpu.h + * + *  Copyright (c) 2005-2007 Analog Devices Inc. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#ifndef _CPU_H_ +#define _CPU_H_ + +#include <command.h> + +void board_reset(void) __attribute__((__weak__)); +void bfin_dump(struct pt_regs *reg); +void bfin_panic(struct pt_regs *reg); +void dump(struct pt_regs *regs); + +asmlinkage void trap(void); +asmlinkage void evt_nmi(void); +asmlinkage void evt_default(void); + +#endif diff --git a/roms/u-boot/arch/blackfin/cpu/gpio.c b/roms/u-boot/arch/blackfin/cpu/gpio.c new file mode 100644 index 00000000..4b4cf93c --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/gpio.c @@ -0,0 +1,841 @@ +/* + * ADI GPIO1 Abstraction Layer + * Support BF50x, BF51x, BF52x, BF53x and BF561 only. + * + * Copyright 2006-2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later + */ + +#include <common.h> +#include <asm/errno.h> +#include <asm/gpio.h> +#include <asm/portmux.h> + +#ifndef CONFIG_ADI_GPIO2 +#if ANOMALY_05000311 || ANOMALY_05000323 +enum { +	AWA_data = SYSCR, +	AWA_data_clear = SYSCR, +	AWA_data_set = SYSCR, +	AWA_toggle = SYSCR, +	AWA_maska = UART_SCR, +	AWA_maska_clear = UART_SCR, +	AWA_maska_set = UART_SCR, +	AWA_maska_toggle = UART_SCR, +	AWA_maskb = UART_GCTL, +	AWA_maskb_clear = UART_GCTL, +	AWA_maskb_set = UART_GCTL, +	AWA_maskb_toggle = UART_GCTL, +	AWA_dir = SPORT1_STAT, +	AWA_polar = SPORT1_STAT, +	AWA_edge = SPORT1_STAT, +	AWA_both = SPORT1_STAT, +#if ANOMALY_05000311 +	AWA_inen = TIMER_ENABLE, +#elif ANOMALY_05000323 +	AWA_inen = DMA1_1_CONFIG, +#endif +}; +	/* Anomaly Workaround */ +#define AWA_DUMMY_READ(name) bfin_read16(AWA_ ## name) +#else +#define AWA_DUMMY_READ(...)  do { } while (0) +#endif + +static struct gpio_port_t * const gpio_array[] = { +#if defined(BF533_FAMILY) +	(struct gpio_port_t *) FIO_FLAG_D, +#elif defined(CONFIG_BF52x) || defined(BF537_FAMILY) || defined(CONFIG_BF51x) \ +	|| defined(BF538_FAMILY) || defined(CONFIG_BF50x) +	(struct gpio_port_t *) PORTFIO, +# if !defined(BF538_FAMILY) +	(struct gpio_port_t *) PORTGIO, +	(struct gpio_port_t *) PORTHIO, +# endif +#elif defined(BF561_FAMILY) +	(struct gpio_port_t *) FIO0_FLAG_D, +	(struct gpio_port_t *) FIO1_FLAG_D, +	(struct gpio_port_t *) FIO2_FLAG_D, +#else +# error no gpio arrays defined +#endif +}; + +#if defined(CONFIG_BF52x) || defined(BF537_FAMILY) || defined(CONFIG_BF51x) || \ +    defined(CONFIG_BF50x) +static unsigned short * const port_fer[] = { +	(unsigned short *) PORTF_FER, +	(unsigned short *) PORTG_FER, +	(unsigned short *) PORTH_FER, +}; + +# if !defined(BF537_FAMILY) +static unsigned short * const port_mux[] = { +	(unsigned short *) PORTF_MUX, +	(unsigned short *) PORTG_MUX, +	(unsigned short *) PORTH_MUX, +}; + +static const +u8 pmux_offset[][16] = { +#  if defined(CONFIG_BF52x) +	{ 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 4, 6, 8, 8, 10, 10 }, /* PORTF */ +	{ 0, 0, 0, 0, 0, 2, 2, 4, 4, 6, 8, 10, 10, 10, 12, 12 }, /* PORTG */ +	{ 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 4, 4, 4, 4, 4, 4 }, /* PORTH */ +#  elif defined(CONFIG_BF51x) +	{ 0, 2, 2, 2, 2, 2, 2, 4, 6, 6, 6, 8, 8, 8, 8, 10 }, /* PORTF */ +	{ 0, 0, 0, 2, 4, 6, 6, 6, 8, 10, 10, 12, 14, 14, 14, 14 }, /* PORTG */ +	{ 0, 0, 0, 0, 2, 2, 4, 6, 10, 10, 10, 10, 10, 10, 10, 10 }, /* PORTH */ +#  endif +}; +# endif + +#elif defined(BF538_FAMILY) +static unsigned short * const port_fer[] = { +	(unsigned short *) PORTCIO_FER, +	(unsigned short *) PORTDIO_FER, +	(unsigned short *) PORTEIO_FER, +}; +#endif + +#ifdef CONFIG_BFIN_GPIO_TRACK +#define RESOURCE_LABEL_SIZE	16 + +static struct str_ident { +	char name[RESOURCE_LABEL_SIZE]; +} str_ident[MAX_RESOURCES]; + +static void gpio_error(unsigned gpio) +{ +	printf("bfin-gpio: GPIO %d wasn't requested!\n", gpio); +} + +static void set_label(unsigned short ident, const char *label) +{ +	if (label) { +		strncpy(str_ident[ident].name, label, +			 RESOURCE_LABEL_SIZE); +		str_ident[ident].name[RESOURCE_LABEL_SIZE - 1] = 0; +	} +} + +static char *get_label(unsigned short ident) +{ +	return (*str_ident[ident].name ? str_ident[ident].name : "UNKNOWN"); +} + +static int cmp_label(unsigned short ident, const char *label) +{ +	if (label == NULL) +		printf("bfin-gpio: please provide none-null label\n"); + +	if (label) +		return strcmp(str_ident[ident].name, label); +	else +		return -EINVAL; +} + +#define map_entry(m, i)      reserved_##m##_map[gpio_bank(i)] +#define is_reserved(m, i, e) (map_entry(m, i) & gpio_bit(i)) +#define reserve(m, i)        (map_entry(m, i) |= gpio_bit(i)) +#define unreserve(m, i)      (map_entry(m, i) &= ~gpio_bit(i)) +#define DECLARE_RESERVED_MAP(m, c) static unsigned short reserved_##m##_map[c] +#else +#define is_reserved(m, i, e) (!(e)) +#define reserve(m, i) +#define unreserve(m, i) +#define DECLARE_RESERVED_MAP(m, c) +#define gpio_error(gpio) +#define set_label(...) +#define get_label(...) "" +#define cmp_label(...) 1 +#endif + +DECLARE_RESERVED_MAP(gpio, GPIO_BANK_NUM); +DECLARE_RESERVED_MAP(peri, gpio_bank(MAX_RESOURCES)); + +inline int check_gpio(unsigned gpio) +{ +	if (gpio >= MAX_BLACKFIN_GPIOS) +		return -EINVAL; +	return 0; +} + +static void port_setup(unsigned gpio, unsigned short usage) +{ +#if defined(BF538_FAMILY) +	/* +	 * BF538/9 Port C,D and E are special. +	 * Inverted PORT_FER polarity on CDE and no PORF_FER on F +	 * Regular PORT F GPIOs are handled here, CDE are exclusively +	 * managed by GPIOLIB +	 */ + +	if (gpio < MAX_BLACKFIN_GPIOS || gpio >= MAX_RESOURCES) +		return; + +	gpio -= MAX_BLACKFIN_GPIOS; + +	if (usage == GPIO_USAGE) +		*port_fer[gpio_bank(gpio)] |= gpio_bit(gpio); +	else +		*port_fer[gpio_bank(gpio)] &= ~gpio_bit(gpio); +	SSYNC(); +	return; +#endif + +	if (check_gpio(gpio)) +		return; + +#if defined(CONFIG_BF52x) || defined(BF537_FAMILY) || defined(CONFIG_BF51x) || \ +    defined(CONFIG_BF50x) +	if (usage == GPIO_USAGE) +		*port_fer[gpio_bank(gpio)] &= ~gpio_bit(gpio); +	else +		*port_fer[gpio_bank(gpio)] |= gpio_bit(gpio); +	SSYNC(); +#endif +} + +#ifdef BF537_FAMILY +static struct { +	unsigned short res; +	unsigned short offset; +} port_mux_lut[] = { +	{.res = P_PPI0_D13, .offset = 11}, +	{.res = P_PPI0_D14, .offset = 11}, +	{.res = P_PPI0_D15, .offset = 11}, +	{.res = P_SPORT1_TFS, .offset = 11}, +	{.res = P_SPORT1_TSCLK, .offset = 11}, +	{.res = P_SPORT1_DTPRI, .offset = 11}, +	{.res = P_PPI0_D10, .offset = 10}, +	{.res = P_PPI0_D11, .offset = 10}, +	{.res = P_PPI0_D12, .offset = 10}, +	{.res = P_SPORT1_RSCLK, .offset = 10}, +	{.res = P_SPORT1_RFS, .offset = 10}, +	{.res = P_SPORT1_DRPRI, .offset = 10}, +	{.res = P_PPI0_D8, .offset = 9}, +	{.res = P_PPI0_D9, .offset = 9}, +	{.res = P_SPORT1_DRSEC, .offset = 9}, +	{.res = P_SPORT1_DTSEC, .offset = 9}, +	{.res = P_TMR2, .offset = 8}, +	{.res = P_PPI0_FS3, .offset = 8}, +	{.res = P_TMR3, .offset = 7}, +	{.res = P_SPI0_SSEL4, .offset = 7}, +	{.res = P_TMR4, .offset = 6}, +	{.res = P_SPI0_SSEL5, .offset = 6}, +	{.res = P_TMR5, .offset = 5}, +	{.res = P_SPI0_SSEL6, .offset = 5}, +	{.res = P_UART1_RX, .offset = 4}, +	{.res = P_UART1_TX, .offset = 4}, +	{.res = P_TMR6, .offset = 4}, +	{.res = P_TMR7, .offset = 4}, +	{.res = P_UART0_RX, .offset = 3}, +	{.res = P_UART0_TX, .offset = 3}, +	{.res = P_DMAR0, .offset = 3}, +	{.res = P_DMAR1, .offset = 3}, +	{.res = P_SPORT0_DTSEC, .offset = 1}, +	{.res = P_SPORT0_DRSEC, .offset = 1}, +	{.res = P_CAN0_RX, .offset = 1}, +	{.res = P_CAN0_TX, .offset = 1}, +	{.res = P_SPI0_SSEL7, .offset = 1}, +	{.res = P_SPORT0_TFS, .offset = 0}, +	{.res = P_SPORT0_DTPRI, .offset = 0}, +	{.res = P_SPI0_SSEL2, .offset = 0}, +	{.res = P_SPI0_SSEL3, .offset = 0}, +}; + +static void portmux_setup(unsigned short per) +{ +	u16 y, offset, muxreg, mask; +	u16 function = P_FUNCT2MUX(per); + +	for (y = 0; y < ARRAY_SIZE(port_mux_lut); y++) { +		if (port_mux_lut[y].res == per) { + +			/* SET PORTMUX REG */ + +			offset = port_mux_lut[y].offset; +			muxreg = bfin_read_PORT_MUX(); + +			if (offset == 1) +				mask = 3; +			else +				mask = 1; + +			muxreg &= ~(mask << offset); +			muxreg |= ((function & mask) << offset); +			bfin_write_PORT_MUX(muxreg); +		} +	} +} +#elif defined(CONFIG_BF52x) || defined(CONFIG_BF51x) +inline void portmux_setup(unsigned short per) +{ +	u16 pmux, ident = P_IDENT(per), function = P_FUNCT2MUX(per); +	u8 offset = pmux_offset[gpio_bank(ident)][gpio_sub_n(ident)]; + +	pmux = *port_mux[gpio_bank(ident)]; +	pmux &= ~(3 << offset); +	pmux |= (function & 3) << offset; +	*port_mux[gpio_bank(ident)] = pmux; +	SSYNC(); +} +#else +# define portmux_setup(...)  do { } while (0) +#endif + +/*********************************************************** +* +* FUNCTIONS: Blackfin General Purpose Ports Access Functions +* +* INPUTS/OUTPUTS: +* gpio - GPIO Number between 0 and MAX_BLACKFIN_GPIOS +* +* +* DESCRIPTION: These functions abstract direct register access +*              to Blackfin processor General Purpose +*              Ports Regsiters +* +* CAUTION: These functions do not belong to the GPIO Driver API +************************************************************* +* MODIFICATION HISTORY : +**************************************************************/ + +/* Set a specific bit */ + +#define SET_GPIO(name) \ +void set_gpio_ ## name(unsigned gpio, unsigned short arg) \ +{ \ +	unsigned long flags; \ +	local_irq_save(flags); \ +	if (arg) \ +		gpio_array[gpio_bank(gpio)]->name |= gpio_bit(gpio); \ +	else \ +		gpio_array[gpio_bank(gpio)]->name &= ~gpio_bit(gpio); \ +	AWA_DUMMY_READ(name); \ +	local_irq_restore(flags); \ +} + +SET_GPIO(dir)   /* set_gpio_dir() */ +SET_GPIO(inen)  /* set_gpio_inen() */ +SET_GPIO(polar) /* set_gpio_polar() */ +SET_GPIO(edge)  /* set_gpio_edge() */ +SET_GPIO(both)  /* set_gpio_both() */ + + +#define SET_GPIO_SC(name) \ +void set_gpio_ ## name(unsigned gpio, unsigned short arg) \ +{ \ +	unsigned long flags; \ +	if (ANOMALY_05000311 || ANOMALY_05000323) \ +		local_irq_save(flags); \ +	if (arg) \ +		gpio_array[gpio_bank(gpio)]->name ## _set = gpio_bit(gpio); \ +	else \ +		gpio_array[gpio_bank(gpio)]->name ## _clear = gpio_bit(gpio); \ +	if (ANOMALY_05000311 || ANOMALY_05000323) { \ +		AWA_DUMMY_READ(name); \ +		local_irq_restore(flags); \ +	} \ +} + +SET_GPIO_SC(maska) +SET_GPIO_SC(maskb) +SET_GPIO_SC(data) + +void set_gpio_toggle(unsigned gpio) +{ +	unsigned long flags; +	if (ANOMALY_05000311 || ANOMALY_05000323) +		local_irq_save(flags); +	gpio_array[gpio_bank(gpio)]->toggle = gpio_bit(gpio); +	if (ANOMALY_05000311 || ANOMALY_05000323) { +		AWA_DUMMY_READ(toggle); +		local_irq_restore(flags); +	} +} + +/* Set current PORT date (16-bit word) */ + +#define SET_GPIO_P(name) \ +void set_gpiop_ ## name(unsigned gpio, unsigned short arg) \ +{ \ +	unsigned long flags; \ +	if (ANOMALY_05000311 || ANOMALY_05000323) \ +		local_irq_save(flags); \ +	gpio_array[gpio_bank(gpio)]->name = arg; \ +	if (ANOMALY_05000311 || ANOMALY_05000323) { \ +		AWA_DUMMY_READ(name); \ +		local_irq_restore(flags); \ +	} \ +} + +SET_GPIO_P(data) +SET_GPIO_P(dir) +SET_GPIO_P(inen) +SET_GPIO_P(polar) +SET_GPIO_P(edge) +SET_GPIO_P(both) +SET_GPIO_P(maska) +SET_GPIO_P(maskb) + +/* Get a specific bit */ +#define GET_GPIO(name) \ +unsigned short get_gpio_ ## name(unsigned gpio) \ +{ \ +	unsigned long flags; \ +	unsigned short ret; \ +	if (ANOMALY_05000311 || ANOMALY_05000323) \ +		local_irq_save(flags); \ +	ret = 0x01 & (gpio_array[gpio_bank(gpio)]->name >> gpio_sub_n(gpio)); \ +	if (ANOMALY_05000311 || ANOMALY_05000323) { \ +		AWA_DUMMY_READ(name); \ +		local_irq_restore(flags); \ +	} \ +	return ret; \ +} + +GET_GPIO(data) +GET_GPIO(dir) +GET_GPIO(inen) +GET_GPIO(polar) +GET_GPIO(edge) +GET_GPIO(both) +GET_GPIO(maska) +GET_GPIO(maskb) + +/* Get current PORT date (16-bit word) */ + +#define GET_GPIO_P(name) \ +unsigned short get_gpiop_ ## name(unsigned gpio) \ +{ \ +	unsigned long flags; \ +	unsigned short ret; \ +	if (ANOMALY_05000311 || ANOMALY_05000323) \ +		local_irq_save(flags); \ +	ret = (gpio_array[gpio_bank(gpio)]->name); \ +	if (ANOMALY_05000311 || ANOMALY_05000323) { \ +		AWA_DUMMY_READ(name); \ +		local_irq_restore(flags); \ +	} \ +	return ret; \ +} + +GET_GPIO_P(data) +GET_GPIO_P(dir) +GET_GPIO_P(inen) +GET_GPIO_P(polar) +GET_GPIO_P(edge) +GET_GPIO_P(both) +GET_GPIO_P(maska) +GET_GPIO_P(maskb) + +/*********************************************************** +* +* FUNCTIONS:	Blackfin Peripheral Resource Allocation +*		and PortMux Setup +* +* INPUTS/OUTPUTS: +* per	Peripheral Identifier +* label	String +* +* DESCRIPTION: Blackfin Peripheral Resource Allocation and Setup API +* +* CAUTION: +************************************************************* +* MODIFICATION HISTORY : +**************************************************************/ + +int peripheral_request(unsigned short per, const char *label) +{ +	unsigned short ident = P_IDENT(per); + +	/* +	 * Don't cares are pins with only one dedicated function +	 */ + +	if (per & P_DONTCARE) +		return 0; + +	if (!(per & P_DEFINED)) +		return -ENODEV; + +	BUG_ON(ident >= MAX_RESOURCES); + +	/* If a pin can be muxed as either GPIO or peripheral, make +	 * sure it is not already a GPIO pin when we request it. +	 */ +	if (unlikely(!check_gpio(ident) && is_reserved(gpio, ident, 1))) { +		printf("%s: Peripheral %d is already reserved as GPIO by %s !\n", +		       __func__, ident, get_label(ident)); +		return -EBUSY; +	} + +	if (unlikely(is_reserved(peri, ident, 1))) { + +		/* +		 * Pin functions like AMC address strobes my +		 * be requested and used by several drivers +		 */ + +		if (!(per & P_MAYSHARE)) { +			/* +			 * Allow that the identical pin function can +			 * be requested from the same driver twice +			 */ + +			if (cmp_label(ident, label) == 0) +				goto anyway; + +			printf("%s: Peripheral %d function %d is already reserved by %s !\n", +			       __func__, ident, P_FUNCT2MUX(per), get_label(ident)); +			return -EBUSY; +		} +	} + + anyway: +	reserve(peri, ident); + +	portmux_setup(per); +	port_setup(ident, PERIPHERAL_USAGE); + +	set_label(ident, label); + +	return 0; +} + +int peripheral_request_list(const unsigned short per[], const char *label) +{ +	u16 cnt; +	int ret; + +	for (cnt = 0; per[cnt] != 0; cnt++) { + +		ret = peripheral_request(per[cnt], label); + +		if (ret < 0) { +			for ( ; cnt > 0; cnt--) +				peripheral_free(per[cnt - 1]); + +			return ret; +		} +	} + +	return 0; +} + +void peripheral_free(unsigned short per) +{ +	unsigned short ident = P_IDENT(per); + +	if (per & P_DONTCARE) +		return; + +	if (!(per & P_DEFINED)) +		return; + +	if (unlikely(!is_reserved(peri, ident, 0))) +		return; + +	if (!(per & P_MAYSHARE)) +		port_setup(ident, GPIO_USAGE); + +	unreserve(peri, ident); + +	set_label(ident, "free"); +} + +void peripheral_free_list(const unsigned short per[]) +{ +	u16 cnt; +	for (cnt = 0; per[cnt] != 0; cnt++) +		peripheral_free(per[cnt]); +} + +/*********************************************************** +* +* FUNCTIONS: Blackfin GPIO Driver +* +* INPUTS/OUTPUTS: +* gpio	PIO Number between 0 and MAX_BLACKFIN_GPIOS +* label	String +* +* DESCRIPTION: Blackfin GPIO Driver API +* +* CAUTION: +************************************************************* +* MODIFICATION HISTORY : +**************************************************************/ + +int gpio_request(unsigned gpio, const char *label) +{ +	if (check_gpio(gpio) < 0) +		return -EINVAL; + +	/* +	 * Allow that the identical GPIO can +	 * be requested from the same driver twice +	 * Do nothing and return - +	 */ + +	if (cmp_label(gpio, label) == 0) +		return 0; + +	if (unlikely(is_reserved(gpio, gpio, 1))) { +		printf("bfin-gpio: GPIO %d is already reserved by %s !\n", +		       gpio, get_label(gpio)); +		return -EBUSY; +	} +	if (unlikely(is_reserved(peri, gpio, 1))) { +		printf("bfin-gpio: GPIO %d is already reserved as Peripheral by %s !\n", +		       gpio, get_label(gpio)); +		return -EBUSY; +	} +	else {	/* Reset POLAR setting when acquiring a gpio for the first time */ +		set_gpio_polar(gpio, 0); +	} + +	reserve(gpio, gpio); +	set_label(gpio, label); + +	port_setup(gpio, GPIO_USAGE); + +	return 0; +} + +int gpio_free(unsigned gpio) +{ +	if (check_gpio(gpio) < 0) +		return -1; + +	if (unlikely(!is_reserved(gpio, gpio, 0))) { +		gpio_error(gpio); +		return -1; +	} + +	unreserve(gpio, gpio); + +	set_label(gpio, "free"); + +	return 0; +} + +#ifdef ADI_SPECIAL_GPIO_BANKS +DECLARE_RESERVED_MAP(special_gpio, gpio_bank(MAX_RESOURCES)); + +int special_gpio_request(unsigned gpio, const char *label) +{ +	/* +	 * Allow that the identical GPIO can +	 * be requested from the same driver twice +	 * Do nothing and return - +	 */ + +	if (cmp_label(gpio, label) == 0) +		return 0; + +	if (unlikely(is_reserved(special_gpio, gpio, 1))) { +		printf("bfin-gpio: GPIO %d is already reserved by %s !\n", +		       gpio, get_label(gpio)); +		return -EBUSY; +	} +	if (unlikely(is_reserved(peri, gpio, 1))) { +		printf("bfin-gpio: GPIO %d is already reserved as Peripheral by %s !\n", +		       gpio, get_label(gpio)); + +		return -EBUSY; +	} + +	reserve(special_gpio, gpio); +	reserve(peri, gpio); + +	set_label(gpio, label); +	port_setup(gpio, GPIO_USAGE); + +	return 0; +} + +void special_gpio_free(unsigned gpio) +{ +	if (unlikely(!is_reserved(special_gpio, gpio, 0))) { +		gpio_error(gpio); +		return; +	} + +	unreserve(special_gpio, gpio); +	unreserve(peri, gpio); +	set_label(gpio, "free"); +} +#endif + +static inline void __gpio_direction_input(unsigned gpio) +{ +	gpio_array[gpio_bank(gpio)]->dir &= ~gpio_bit(gpio); +	gpio_array[gpio_bank(gpio)]->inen |= gpio_bit(gpio); +} + +int gpio_direction_input(unsigned gpio) +{ +	unsigned long flags; + +	if (!is_reserved(gpio, gpio, 0)) { +		gpio_error(gpio); +		return -EINVAL; +	} + +	local_irq_save(flags); +	__gpio_direction_input(gpio); +	AWA_DUMMY_READ(inen); +	local_irq_restore(flags); + +	return 0; +} + +int gpio_set_value(unsigned gpio, int arg) +{ +	if (arg) +		gpio_array[gpio_bank(gpio)]->data_set = gpio_bit(gpio); +	else +		gpio_array[gpio_bank(gpio)]->data_clear = gpio_bit(gpio); + +	return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ +	unsigned long flags; + +	if (!is_reserved(gpio, gpio, 0)) { +		gpio_error(gpio); +		return -EINVAL; +	} + +	local_irq_save(flags); + +	gpio_array[gpio_bank(gpio)]->inen &= ~gpio_bit(gpio); +	gpio_set_value(gpio, value); +	gpio_array[gpio_bank(gpio)]->dir |= gpio_bit(gpio); + +	AWA_DUMMY_READ(dir); +	local_irq_restore(flags); + +	return 0; +} + +int gpio_get_value(unsigned gpio) +{ +	unsigned long flags; + +	if (unlikely(get_gpio_edge(gpio))) { +		int ret; +		local_irq_save(flags); +		set_gpio_edge(gpio, 0); +		ret = get_gpio_data(gpio); +		set_gpio_edge(gpio, 1); +		local_irq_restore(flags); +		return ret; +	} else +		return get_gpio_data(gpio); +} + +/* If we are booting from SPI and our board lacks a strong enough pull up, + * the core can reset and execute the bootrom faster than the resistor can + * pull the signal logically high.  To work around this (common) error in + * board design, we explicitly set the pin back to GPIO mode, force /CS + * high, and wait for the electrons to do their thing. + * + * This function only makes sense to be called from reset code, but it + * lives here as we need to force all the GPIO states w/out going through + * BUG() checks and such. + */ +void bfin_reset_boot_spi_cs(unsigned short pin) +{ +	unsigned short gpio = P_IDENT(pin); +	port_setup(gpio, GPIO_USAGE); +	gpio_array[gpio_bank(gpio)]->data_set = gpio_bit(gpio); +	AWA_DUMMY_READ(data_set); +	udelay(1); +} + +int name_to_gpio(const char *name) +{ +	int port_base; + +	if (tolower(*name) == 'p') { +		++name; + +		switch (tolower(*name)) { +#ifdef GPIO_PA0 +		case 'a': port_base = GPIO_PA0; break; +#endif +#ifdef GPIO_PB0 +		case 'b': port_base = GPIO_PB0; break; +#endif +#ifdef GPIO_PC0 +		case 'c': port_base = GPIO_PC0; break; +#endif +#ifdef GPIO_PD0 +		case 'd': port_base = GPIO_PD0; break; +#endif +#ifdef GPIO_PE0 +		case 'e': port_base = GPIO_PE0; break; +#endif +#ifdef GPIO_PF0 +		case 'f': port_base = GPIO_PF0; break; +#endif +#ifdef GPIO_PG0 +		case 'g': port_base = GPIO_PG0; break; +#endif +#ifdef GPIO_PH0 +		case 'h': port_base = GPIO_PH0; break; +#endif +#ifdef GPIO_PI0 +		case 'i': port_base = GPIO_PI0; break; +#endif +#ifdef GPIO_PJ +		case 'j': port_base = GPIO_PJ0; break; +#endif +		default:  return -1; +		} + +		++name; +	} else +		port_base = 0; + +	return port_base + simple_strtoul(name, NULL, 10); +} + +void gpio_labels(void) +{ +	int c, gpio; + +	for (c = 0; c < MAX_RESOURCES; c++) { +		gpio = is_reserved(gpio, c, 1); +		if (!check_gpio(c) && gpio) +			printf("GPIO_%d:\t%s\tGPIO %s\n", c, +				get_label(c), +				get_gpio_dir(c) ? "OUTPUT" : "INPUT"); +		else if (is_reserved(peri, c, 1)) +			printf("GPIO_%d:\t%s\tPeripheral\n", c, get_label(c)); +		else +			continue; +	} +} +#else +struct gpio_port_t * const gpio_array[] = { +	(struct gpio_port_t *)PORTA_FER, +	(struct gpio_port_t *)PORTB_FER, +	(struct gpio_port_t *)PORTC_FER, +	(struct gpio_port_t *)PORTD_FER, +	(struct gpio_port_t *)PORTE_FER, +	(struct gpio_port_t *)PORTF_FER, +	(struct gpio_port_t *)PORTG_FER, +#if defined(CONFIG_BF54x) +	(struct gpio_port_t *)PORTH_FER, +	(struct gpio_port_t *)PORTI_FER, +	(struct gpio_port_t *)PORTJ_FER, +#endif +}; +#endif diff --git a/roms/u-boot/arch/blackfin/cpu/init.S b/roms/u-boot/arch/blackfin/cpu/init.S new file mode 100644 index 00000000..f48c1132 --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/init.S @@ -0,0 +1,9 @@ +#include <asm/blackfin.h> +ENTRY(_start) +	sp.l = LO(L1_SRAM_SCRATCH_END - 20); +	sp.h = HI(L1_SRAM_SCRATCH_END - 20); +	call _initcode; +1: +	emuexcpt; +	jump 1b; +END(_start) diff --git a/roms/u-boot/arch/blackfin/cpu/init.lds.S b/roms/u-boot/arch/blackfin/cpu/init.lds.S new file mode 100644 index 00000000..602e7c87 --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/init.lds.S @@ -0,0 +1,25 @@ +/* + * linker script for simple init.elf + * + * Copyright (c) 2005-2011 Analog Device Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <config.h> +#include <asm/blackfin.h> +#undef ALIGN +#undef ENTRY + +OUTPUT_ARCH(bfin) + +MEMORY +{ +	l1_code : ORIGIN = L1_INST_SRAM, LENGTH = L1_INST_SRAM_SIZE +} + +ENTRY(_start) +SECTIONS +{ +	.text.l1 : { *(.text .text.*) } >l1_code +} diff --git a/roms/u-boot/arch/blackfin/cpu/initcode.c b/roms/u-boot/arch/blackfin/cpu/initcode.c new file mode 100644 index 00000000..2e640afc --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/initcode.c @@ -0,0 +1,1040 @@ +/* + * 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-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#define BFIN_IN_INITCODE + +#include <config.h> +#include <asm/blackfin.h> +#include <asm/mach-common/bits/watchdog.h> +#include <asm/mach-common/bits/bootrom.h> +#include <asm/mach-common/bits/core.h> +#include <asm/serial.h> + +#ifndef __ADSPBF60x__ +#include <asm/mach-common/bits/ebiu.h> +#include <asm/mach-common/bits/pll.h> +#else /* __ADSPBF60x__ */ +#include <asm/mach-common/bits/cgu.h> + +#define CONFIG_BFIN_GET_DCLK_M \ +	((CONFIG_CLKIN_HZ*CONFIG_VCO_MULT)/(CONFIG_DCLK_DIV*1000000)) + +#ifndef CONFIG_DMC_DDRCFG +#if ((CONFIG_BFIN_GET_DCLK_M != 125) && \ +	(CONFIG_BFIN_GET_DCLK_M != 133) && \ +	(CONFIG_BFIN_GET_DCLK_M != 150) && \ +	(CONFIG_BFIN_GET_DCLK_M != 166) && \ +	(CONFIG_BFIN_GET_DCLK_M != 200) && \ +	(CONFIG_BFIN_GET_DCLK_M != 225) && \ +	(CONFIG_BFIN_GET_DCLK_M != 250)) +#error "DDR2 CLK must be in (125, 133, 150, 166, 200, 225, 250)MHz" +#endif +#endif + +/* DMC control bits */ +#define SRREQ			0x8 + +/* DMC status bits */ +#define IDLE                    0x1 +#define MEMINITDONE             0x4 +#define SRACK                   0x8 +#define PDACK                   0x10 +#define DPDACK                  0x20 +#define DLLCALDONE              0x2000 +#define PENDREF                 0xF0000 +#define PHYRDPHASE              0xF00000 +#define PHYRDPHASE_OFFSET       20 + +/* DMC DLL control bits */ +#define DLLCALRDCNT             0xFF +#define DATACYC_OFFSET          8 + +struct ddr_config { +	u32 ddr_clk; +	u32 dmc_ddrctl; +	u32 dmc_ddrcfg; +	u32 dmc_ddrtr0; +	u32 dmc_ddrtr1; +	u32 dmc_ddrtr2; +	u32 dmc_ddrmr; +	u32 dmc_ddrmr1; +}; + +static struct ddr_config ddr_config_table[] = { +	[0] = { +		.ddr_clk    = 125,	/* 125MHz */ +		.dmc_ddrctl = 0x00000904, +		.dmc_ddrcfg = 0x00000422, +		.dmc_ddrtr0 = 0x20705212, +		.dmc_ddrtr1 = 0x201003CF, +		.dmc_ddrtr2 = 0x00320107, +		.dmc_ddrmr  = 0x00000422, +		.dmc_ddrmr1 = 0x4, +	}, +	[1] = { +		.ddr_clk    = 133,	/* 133MHz */ +		.dmc_ddrctl = 0x00000904, +		.dmc_ddrcfg = 0x00000422, +		.dmc_ddrtr0 = 0x20806313, +		.dmc_ddrtr1 = 0x2013040D, +		.dmc_ddrtr2 = 0x00320108, +		.dmc_ddrmr  = 0x00000632, +		.dmc_ddrmr1 = 0x4, +	}, +	[2] = { +		.ddr_clk    = 150,	/* 150MHz */ +		.dmc_ddrctl = 0x00000904, +		.dmc_ddrcfg = 0x00000422, +		.dmc_ddrtr0 = 0x20A07323, +		.dmc_ddrtr1 = 0x20160492, +		.dmc_ddrtr2 = 0x00320209, +		.dmc_ddrmr  = 0x00000632, +		.dmc_ddrmr1 = 0x4, +	}, +	[3] = { +		.ddr_clk    = 166,	/* 166MHz */ +		.dmc_ddrctl = 0x00000904, +		.dmc_ddrcfg = 0x00000422, +		.dmc_ddrtr0 = 0x20A07323, +		.dmc_ddrtr1 = 0x2016050E, +		.dmc_ddrtr2 = 0x00320209, +		.dmc_ddrmr  = 0x00000632, +		.dmc_ddrmr1 = 0x4, +	}, +	[4] = { +		.ddr_clk    = 200,	/* 200MHz */ +		.dmc_ddrctl = 0x00000904, +		.dmc_ddrcfg = 0x00000422, +		.dmc_ddrtr0 = 0x20a07323, +		.dmc_ddrtr1 = 0x2016050f, +		.dmc_ddrtr2 = 0x00320509, +		.dmc_ddrmr  = 0x00000632, +		.dmc_ddrmr1 = 0x4, +	}, +	[5] = { +		.ddr_clk    = 225,	/* 225MHz */ +		.dmc_ddrctl = 0x00000904, +		.dmc_ddrcfg = 0x00000422, +		.dmc_ddrtr0 = 0x20E0A424, +		.dmc_ddrtr1 = 0x302006DB, +		.dmc_ddrtr2 = 0x0032020D, +		.dmc_ddrmr  = 0x00000842, +		.dmc_ddrmr1 = 0x4, +	}, +	[6] = { +		.ddr_clk    = 250,	/* 250MHz */ +		.dmc_ddrctl = 0x00000904, +		.dmc_ddrcfg = 0x00000422, +		.dmc_ddrtr0 = 0x20E0A424, +		.dmc_ddrtr1 = 0x3020079E, +		.dmc_ddrtr2 = 0x0032050D, +		.dmc_ddrmr  = 0x00000842, +		.dmc_ddrmr1 = 0x4, +	}, +}; +#endif /* __ADSPBF60x__ */ + +__attribute__((always_inline)) +static inline void serial_init(void) +{ +#if defined(__ADSPBF54x__) || defined(__ADSPBF60x__) +# 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) { +		uint32_t uart_base = UART_BASE; +		size_t i; + +		/* force RTS rather than relying on auto RTS */ +#if BFIN_UART_HW_VER < 4 +		bfin_write16(&pUART->mcr, bfin_read16(&pUART->mcr) | FCPOL); +#else +		bfin_write32(&pUART->control, bfin_read32(&pUART->control) | +				FCPOL); +#endif + +		/* 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 + +#if CONFIG_BFIN_BOOT_MODE != BFIN_BOOT_BYPASS +	if (BFIN_DEBUG_EARLY_SERIAL) { +		serial_early_init(UART_BASE); +		serial_early_set_baud(UART_BASE, CONFIG_BAUDRATE); +	} +#endif +} + +__attribute__((always_inline)) +static inline void serial_deinit(void) +{ +#if defined(__ADSPBF54x__) || defined(__ADSPBF60x__) +	uint32_t uart_base = UART_BASE; + +	if (BFIN_UART_USE_RTS && CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART) { +		/* clear forced RTS rather than relying on auto RTS */ +#if BFIN_UART_HW_VER < 4 +		bfin_write16(&pUART->mcr, bfin_read16(&pUART->mcr) & ~FCPOL); +#else +		bfin_write32(&pUART->control, bfin_read32(&pUART->control) & +				~FCPOL); +#endif +	} +#endif +} + +__attribute__((always_inline)) +static inline void serial_putc(char c) +{ +	uint32_t uart_base = UART_BASE; + +	if (!BFIN_DEBUG_EARLY_SERIAL) +		return; + +	if (c == '\n') +		serial_putc('\r'); + +	bfin_write(&pUART->thr, c); + +	while (!(_lsr_read(pUART) & TEMT)) +		continue; +} + +#include "initcode.h" + +__attribute__((always_inline)) static inline void +program_nmi_handler(void) +{ +	u32 tmp1, tmp2; + +	/* Older bootroms don't create a dummy NMI handler, +	 * so make one ourselves ASAP in case it fires. +	 */ +	if (CONFIG_BFIN_BOOT_MODE != BFIN_BOOT_BYPASS && !ANOMALY_05000219) +		return; + +	asm volatile ( +		"%0 = RETS;" /* Save current RETS */ +		"CALL 1f;"   /* Figure out current PC */ +		"RTN;"       /* The simple NMI handler */ +		"1:" +		"%1 = RETS;" /* Load addr of NMI handler */ +		"RETS = %0;" /* Restore RETS */ +		"[%2] = %1;" /* Write NMI handler */ +		: "=d"(tmp1), "=d"(tmp2) +		: "ab"(EVT2) +	); +} + +/* Max SCLK can be 133MHz ... dividing that by (2*4) gives + * us a freq of 16MHz for SPI which should generally be + * slow enough for the slow reads the bootrom uses. + */ +#if !defined(CONFIG_SPI_FLASH_SLOW_READ) && \ +    ((defined(__ADSPBF52x__) && __SILICON_REVISION__ >= 2) || \ +     (defined(__ADSPBF54x__) && __SILICON_REVISION__ >= 1)) +# define BOOTROM_SUPPORTS_SPI_FAST_READ 1 +#else +# define BOOTROM_SUPPORTS_SPI_FAST_READ 0 +#endif +#ifndef CONFIG_SPI_BAUD_INITBLOCK +# define CONFIG_SPI_BAUD_INITBLOCK (BOOTROM_SUPPORTS_SPI_FAST_READ ? 2 : 4) +#endif +#ifdef SPI0_BAUD +# define bfin_write_SPI_BAUD bfin_write_SPI0_BAUD +#endif + +#ifdef __ADSPBF60x__ + +#ifndef CONFIG_CGU_CTL_VAL +# define CONFIG_CGU_CTL_VAL ((CONFIG_VCO_MULT << 8) | CONFIG_CLKIN_HALF) +#endif + +#ifndef CONFIG_CGU_DIV_VAL +# define CONFIG_CGU_DIV_VAL \ +	((CONFIG_CCLK_DIV   << CSEL_P)   | \ +	 (CONFIG_SCLK0_DIV  << S0SEL_P)  | \ +	 (CONFIG_SCLK_DIV << SYSSEL_P) | \ +	 (CONFIG_SCLK1_DIV  << S1SEL_P)  | \ +	 (CONFIG_DCLK_DIV   << DSEL_P)   | \ +	 (CONFIG_OCLK_DIV   << OSEL_P)) +#endif + +#else /* __ADSPBF60x__ */ + +/* 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) | CONFIG_CLKIN_HALF) +#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 +# elif defined(__ADSPBF538__) || defined(__ADSPBF539__)	/* TBD; use default */ +#  undef CONFIG_VR_CTL_VLEV +#  define CONFIG_VR_CTL_VLEV VLEV_125 +# 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 + +/* some parts do not have an on-chip voltage regulator */ +#if defined(__ADSPBF51x__) +# define CONFIG_HAS_VR 0 +# undef CONFIG_VR_CTL_VAL +# define CONFIG_VR_CTL_VAL 0 +#else +# define CONFIG_HAS_VR 1 +#endif + +#if CONFIG_MEM_SIZE +#ifndef EBIU_RSTCTL +/* Blackfin with SDRAM */ +#ifndef CONFIG_EBIU_SDBCTL_VAL +# if CONFIG_MEM_SIZE == 16 +#  define CONFIG_EBSZ_VAL EBSZ_16 +# elif CONFIG_MEM_SIZE == 32 +#  define CONFIG_EBSZ_VAL EBSZ_32 +# elif CONFIG_MEM_SIZE == 64 +#  define CONFIG_EBSZ_VAL EBSZ_64 +# elif CONFIG_MEM_SIZE == 128 +#  define CONFIG_EBSZ_VAL EBSZ_128 +# elif CONFIG_MEM_SIZE == 256 +#  define CONFIG_EBSZ_VAL EBSZ_256 +# elif CONFIG_MEM_SIZE == 512 +#  define CONFIG_EBSZ_VAL EBSZ_512 +# else +#  error You need to define CONFIG_EBIU_SDBCTL_VAL or CONFIG_MEM_SIZE +# endif +# if CONFIG_MEM_ADD_WDTH == 8 +#  define CONFIG_EBCAW_VAL EBCAW_8 +# elif CONFIG_MEM_ADD_WDTH == 9 +#  define CONFIG_EBCAW_VAL EBCAW_9 +# elif CONFIG_MEM_ADD_WDTH == 10 +#  define CONFIG_EBCAW_VAL EBCAW_10 +# elif CONFIG_MEM_ADD_WDTH == 11 +#  define CONFIG_EBCAW_VAL EBCAW_11 +# else +#  error You need to define CONFIG_EBIU_SDBCTL_VAL or CONFIG_MEM_ADD_WDTH +# endif +# define CONFIG_EBIU_SDBCTL_VAL (CONFIG_EBCAW_VAL | CONFIG_EBSZ_VAL | EBE) +#endif +#endif +#endif + +/* Conflicting Column Address Widths Causes SDRAM Errors: + * EB2CAW and EB3CAW must be the same + */ +#if ANOMALY_05000362 +# if ((CONFIG_EBIU_SDBCTL_VAL & 0x30000000) >> 8) != (CONFIG_EBIU_SDBCTL_VAL & 0x00300000) +#  error "Anomaly 05000362: EB2CAW and EB3CAW must be the same" +# endif +#endif + +#endif /*  __ADSPBF60x__ */ + +__attribute__((always_inline)) static inline void +program_early_devices(ADI_BOOT_DATA *bs, uint *sdivB, uint *divB, uint *vcoB) +{ +	serial_putc('a'); + +	/* Save the clock pieces that are used in baud rate calculation */ +	if (BFIN_DEBUG_EARLY_SERIAL || CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART) { +		serial_putc('b'); +#ifdef __ADSPBF60x__ +		*sdivB = bfin_read_CGU_DIV(); +		*sdivB = ((*sdivB >> 8) & 0x1f) * ((*sdivB >> 5) & 0x7); +		*vcoB = (bfin_read_CGU_CTL() >> 8) & 0x7f; +#else +		*sdivB = bfin_read_PLL_DIV() & 0xf; +		*vcoB = (bfin_read_PLL_CTL() >> 9) & 0x3f; +#endif +		*divB = serial_early_get_div(); +		serial_putc('c'); +	} + +	serial_putc('d'); + +#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) { +		serial_putc('e'); +#ifdef __ADSPBF60x__ +		/* Reset system event controller */ +		bfin_write_SEC_GCTL(0x2); +		bfin_write_SEC_CCTL(0x2); +		SSYNC(); + +		/* Enable fault event input and system reset action in fault +		 * controller. Route watchdog timeout event to fault interface. +		 */ +		bfin_write_SEC_FCTL(0xc1); +		/* Enable watchdog interrupt source */ +		bfin_write_SEC_SCTL(2, bfin_read_SEC_SCTL(2) | 0x6); +		SSYNC(); + +		/* Enable system event controller */ +		bfin_write_SEC_GCTL(0x1); +		bfin_write_SEC_CCTL(0x1); +		SSYNC(); +#endif +		bfin_write_WDOG_CTL(WDDIS); +		SSYNC(); +		bfin_write_WDOG_CNT(MSEC_TO_SCLK(CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE)); +#if CONFIG_BFIN_BOOT_MODE != BFIN_BOOT_UART +		bfin_write_WDOG_CTL(WDEN); +#endif +		serial_putc('f'); +	} +#endif + +	serial_putc('g'); + +	/* 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. +	 */ +#ifdef SPI_BAUD +	if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_SPI_MASTER) { +		serial_putc('h'); +		if (BOOTROM_SUPPORTS_SPI_FAST_READ && CONFIG_SPI_BAUD_INITBLOCK < 4) +			bs->dFlags |= BFLAG_FASTREAD; +		bfin_write_SPI_BAUD(CONFIG_SPI_BAUD_INITBLOCK); +		serial_putc('i'); +	} +#endif + +	serial_putc('j'); +} + +__attribute__((always_inline)) static inline bool +maybe_self_refresh(ADI_BOOT_DATA *bs) +{ +	serial_putc('a'); + +	if (!CONFIG_MEM_SIZE) +		return false; + +#ifdef __ADSPBF60x__ +	/* resume from hibernate, return false let ddr initialize */ +	if ((bfin_read32(DPM0_STAT) & 0xF0) == 0x50) { +		serial_putc('b'); +		return false; +	} + +#else /* __ADSPBF60x__ */ + +	/* If external memory is enabled, put it into self refresh first. */ +#if defined(EBIU_RSTCTL) +	if (bfin_read_EBIU_RSTCTL() & DDR_SRESET) { +		serial_putc('b'); +		bfin_write_EBIU_RSTCTL(bfin_read_EBIU_RSTCTL() | SRREQ); +		return true; +	} +#elif defined(EBIU_SDGCTL) +	if (bfin_read_EBIU_SDBCTL() & EBE) { +		serial_putc('b'); +		bfin_write_EBIU_SDGCTL(bfin_read_EBIU_SDGCTL() | SRFS); +		return true; +	} +#endif + +#endif /* __ADSPBF60x__ */ +	serial_putc('c'); + +	return false; +} + +__attribute__((always_inline)) static inline u16 +program_clocks(ADI_BOOT_DATA *bs, bool put_into_srfs) +{ +	u16 vr_ctl = 0; + +	serial_putc('a'); + +#ifdef __ADSPBF60x__ +	if (bfin_read_DMC0_STAT() & MEMINITDONE) { +		bfin_write_DMC0_CTL(bfin_read_DMC0_CTL() | SRREQ); +		SSYNC(); +		while (!(bfin_read_DMC0_STAT() & SRACK)) +			continue; +	} + +	/* Don't set the same value of MSEL and DF to CGU_CTL */ +	if ((bfin_read_CGU_CTL() & (MSEL_MASK | DF_MASK)) +			!= CONFIG_CGU_CTL_VAL) { +		bfin_write_CGU_DIV(CONFIG_CGU_DIV_VAL); +		bfin_write_CGU_CTL(CONFIG_CGU_CTL_VAL); +		while ((bfin_read_CGU_STAT() & (CLKSALGN | PLLBP)) || +				!(bfin_read_CGU_STAT() & PLLLK)) +			continue; +	} + +	bfin_write_CGU_DIV(CONFIG_CGU_DIV_VAL | UPDT); +	while (bfin_read_CGU_STAT() & CLKSALGN) +		continue; + +	if (bfin_read_DMC0_STAT() & MEMINITDONE) { +		bfin_write_DMC0_CTL(bfin_read_DMC0_CTL() & ~SRREQ); +		SSYNC(); +		while (bfin_read_DMC0_STAT() & SRACK) +			continue; +	} + +#else /* __ADSPBF60x__ */ + +	vr_ctl = bfin_read_VR_CTL(); + +	serial_putc('b'); + +	/* If we're entering self refresh, make sure it has happened. */ +	if (put_into_srfs) +#if defined(EBIU_RSTCTL) +		while (!(bfin_read_EBIU_RSTCTL() & SRACK)) +			continue; +#elif defined(EBIU_SDGCTL) +		while (!(bfin_read_EBIU_SDSTAT() & SDSRA)) +			continue; +#else +		; +#endif + +	serial_putc('c'); + +	/* With newer bootroms, we use the helper function to set up +	 * the memory controller.  Older bootroms lacks such helpers +	 * so we do it ourselves. +	 */ +	if (!ANOMALY_05000386) { +		serial_putc('d'); + +		/* Always programming PLL_LOCKCNT avoids Anomaly 05000430 */ +		ADI_SYSCTRL_VALUES memory_settings; +		uint32_t actions = SYSCTRL_WRITE | SYSCTRL_PLLCTL | SYSCTRL_LOCKCNT; +		if (!ANOMALY_05000440) +			actions |= SYSCTRL_PLLDIV; +		if (CONFIG_HAS_VR) { +			actions |= SYSCTRL_VRCTL; +			if (CONFIG_VR_CTL_VAL & FREQ_MASK) +				actions |= SYSCTRL_INTVOLTAGE; +			else +				actions |= SYSCTRL_EXTVOLTAGE; +			memory_settings.uwVrCtl = CONFIG_VR_CTL_VAL; +		} else +			actions |= SYSCTRL_EXTVOLTAGE; +		memory_settings.uwPllCtl = CONFIG_PLL_CTL_VAL; +		memory_settings.uwPllDiv = CONFIG_PLL_DIV_VAL; +		memory_settings.uwPllLockCnt = CONFIG_PLL_LOCKCNT_VAL; +#if ANOMALY_05000432 +		bfin_write_SIC_IWR1(0); +#endif +		serial_putc('e'); +		bfrom_SysControl(actions, &memory_settings, NULL); +		serial_putc('f'); +		if (ANOMALY_05000440) +			bfin_write_PLL_DIV(CONFIG_PLL_DIV_VAL); +#if ANOMALY_05000432 +		bfin_write_SIC_IWR1(-1); +#endif +#if ANOMALY_05000171 +		bfin_write_SICA_IWR0(-1); +		bfin_write_SICA_IWR1(-1); +#endif +		serial_putc('g'); +	} else { +		serial_putc('h'); + +		/* 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); +#elif defined(SIC_IWR) +		bfin_write_SIC_IWR(1); +#endif + +		serial_putc('i'); + +		/* Always programming PLL_LOCKCNT avoids Anomaly 05000430 */ +		bfin_write_PLL_LOCKCNT(CONFIG_PLL_LOCKCNT_VAL); + +		serial_putc('j'); + +		/* Only reprogram when needed to avoid triggering unnecessary +		 * PLL relock sequences. +		 */ +		if (vr_ctl != CONFIG_VR_CTL_VAL) { +			serial_putc('?'); +			bfin_write_VR_CTL(CONFIG_VR_CTL_VAL); +			asm("idle;"); +			serial_putc('!'); +		} + +		serial_putc('k'); + +		bfin_write_PLL_DIV(CONFIG_PLL_DIV_VAL); + +		serial_putc('l'); + +		/* Only reprogram when needed to avoid triggering unnecessary +		 * PLL relock sequences. +		 */ +		if (ANOMALY_05000242 || bfin_read_PLL_CTL() != CONFIG_PLL_CTL_VAL) { +			serial_putc('?'); +			bfin_write_PLL_CTL(CONFIG_PLL_CTL_VAL); +			asm("idle;"); +			serial_putc('!'); +		} + +		serial_putc('m'); + +		/* 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); +#elif defined(SIC_IWR) +		bfin_write_SIC_IWR(-1); +#endif + +		serial_putc('n'); +	} + +#endif /* __ADSPBF60x__ */ + +	serial_putc('o'); + +	return vr_ctl; +} + +__attribute__((always_inline)) static inline void +update_serial_clocks(ADI_BOOT_DATA *bs, uint sdivB, uint divB, uint vcoB) +{ +	/* Since we've changed the SCLK above, we may need to update +	 * the UART divisors (UART baud rates are based on SCLK). +	 * Do the division by hand as there are no native instructions +	 * for dividing which means we'd generate a libgcc reference. +	 */ +	unsigned int sdivR, vcoR; +	unsigned int dividend; +	unsigned int divisor; +	unsigned int quotient; + +	serial_putc('a'); + +	if (BFIN_DEBUG_EARLY_SERIAL || +		CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_UART) { +#ifdef __ADSPBF60x__ +	sdivR = bfin_read_CGU_DIV(); +	sdivR = ((sdivR >> 8) & 0x1f) * ((sdivR >> 5) & 0x7); +	vcoR = (bfin_read_CGU_CTL() >> 8) & 0x7f; +#else +	sdivR = bfin_read_PLL_DIV() & 0xf; +	vcoR = (bfin_read_PLL_CTL() >> 9) & 0x3f; +#endif + +	dividend = sdivB * divB * vcoR; +	divisor = vcoB * sdivR; +	quotient = early_division(dividend, divisor); +	serial_early_put_div(quotient - ANOMALY_05000230); +	} + +	serial_putc('c'); +} + +__attribute__((always_inline)) static inline void +program_memory_controller(ADI_BOOT_DATA *bs, bool put_into_srfs) +{ +	serial_putc('a'); + +	if (!CONFIG_MEM_SIZE) +		return; + +	serial_putc('b'); + +#ifdef __ADSPBF60x__ +	int dlldatacycle; +	int dll_ctl; +	int i = 0; + +	if (CONFIG_BFIN_GET_DCLK_M ==  125) +		i = 0; +	else if (CONFIG_BFIN_GET_DCLK_M ==  133) +		i = 1; +	else if (CONFIG_BFIN_GET_DCLK_M ==  150) +		i = 2; +	else if (CONFIG_BFIN_GET_DCLK_M ==  166) +		i = 3; +	else if (CONFIG_BFIN_GET_DCLK_M ==  200) +		i = 4; +	else if (CONFIG_BFIN_GET_DCLK_M ==  225) +		i = 5; +	else if (CONFIG_BFIN_GET_DCLK_M ==  250) +		i = 6; + +#if 0 +	for (i = 0; i < ARRAY_SIZE(ddr_config_table); i++) +		if (CONFIG_BFIN_GET_DCLK_M == ddr_config_table[i].ddr_clk) +			break; +#endif + +#ifndef CONFIG_DMC_DDRCFG +	bfin_write_DMC0_CFG(ddr_config_table[i].dmc_ddrcfg); +#else +	bfin_write_DMC0_CFG(CONFIG_DMC_DDRCFG); +#endif +#ifndef CONFIG_DMC_DDRTR0 +	bfin_write_DMC0_TR0(ddr_config_table[i].dmc_ddrtr0); +#else +	bfin_write_DMC0_TR0(CONFIG_DMC_DDRTR0); +#endif +#ifndef CONFIG_DMC_DDRTR1 +	bfin_write_DMC0_TR1(ddr_config_table[i].dmc_ddrtr1); +#else +	bfin_write_DMC0_TR1(CONFIG_DMC_DDRTR1); +#endif +#ifndef CONFIG_DMC_DDRTR2 +	bfin_write_DMC0_TR2(ddr_config_table[i].dmc_ddrtr2); +#else +	bfin_write_DMC0_TR2(CONFIG_DMC_DDRTR2); +#endif +#ifndef CONFIG_DMC_DDRMR +	bfin_write_DMC0_MR(ddr_config_table[i].dmc_ddrmr); +#else +	bfin_write_DMC0_MR(CONFIG_DMC_DDRMR); +#endif +#ifndef CONFIG_DMC_DDREMR1 +	bfin_write_DMC0_EMR1(ddr_config_table[i].dmc_ddrmr1); +#else +	bfin_write_DMC0_EMR1(CONFIG_DMC_DDREMR1); +#endif +#ifndef CONFIG_DMC_DDRCTL +	bfin_write_DMC0_CTL(ddr_config_table[i].dmc_ddrctl); +#else +	bfin_write_DMC0_CTL(CONFIG_DMC_DDRCTL); +#endif + +	SSYNC(); +	while (!(bfin_read_DMC0_STAT() & MEMINITDONE)) +		continue; + +	dlldatacycle = (bfin_read_DMC0_STAT() & PHYRDPHASE) >> +			PHYRDPHASE_OFFSET; +	dll_ctl = bfin_read_DMC0_DLLCTL(); +	dll_ctl &= 0x0ff; +	bfin_write_DMC0_DLLCTL(dll_ctl | (dlldatacycle << DATACYC_OFFSET)); + +	SSYNC(); +	while (!(bfin_read_DMC0_STAT() & DLLCALDONE)) +		continue; +	serial_putc('!'); + +#else /* __ADSPBF60x__ */ + +	/* Program the external memory controller before we come out of +	 * self-refresh.  This only works with our SDRAM controller. +	 */ +#ifdef EBIU_SDGCTL +# ifdef CONFIG_EBIU_SDRRC_VAL +	bfin_write_EBIU_SDRRC(CONFIG_EBIU_SDRRC_VAL); +# endif +# ifdef CONFIG_EBIU_SDBCTL_VAL +	bfin_write_EBIU_SDBCTL(CONFIG_EBIU_SDBCTL_VAL); +# endif +# ifdef CONFIG_EBIU_SDGCTL_VAL +	bfin_write_EBIU_SDGCTL(CONFIG_EBIU_SDGCTL_VAL); +# endif +#endif + +	serial_putc('c'); + +	/* Now that we've reprogrammed, take things out of self refresh. */ +	if (put_into_srfs) +#if defined(EBIU_RSTCTL) +		bfin_write_EBIU_RSTCTL(bfin_read_EBIU_RSTCTL() & ~(SRREQ)); +#elif defined(EBIU_SDGCTL) +		bfin_write_EBIU_SDGCTL(bfin_read_EBIU_SDGCTL() & ~(SRFS)); +#endif + +	serial_putc('d'); + +	/* Our DDR controller sucks and cannot be programmed while in +	 * self-refresh.  So we have to pull it out before programming. +	 */ +#ifdef EBIU_RSTCTL +# ifdef CONFIG_EBIU_RSTCTL_VAL +	bfin_write_EBIU_RSTCTL(bfin_read_EBIU_RSTCTL() | 0x1 /*DDRSRESET*/ | CONFIG_EBIU_RSTCTL_VAL); +# endif +# ifdef CONFIG_EBIU_DDRCTL0_VAL +	bfin_write_EBIU_DDRCTL0(CONFIG_EBIU_DDRCTL0_VAL); +# endif +# ifdef CONFIG_EBIU_DDRCTL1_VAL +	bfin_write_EBIU_DDRCTL1(CONFIG_EBIU_DDRCTL1_VAL); +# endif +# ifdef CONFIG_EBIU_DDRCTL2_VAL +	bfin_write_EBIU_DDRCTL2(CONFIG_EBIU_DDRCTL2_VAL); +# endif +# ifdef CONFIG_EBIU_DDRCTL3_VAL +	/* default is disable, so don't need to force this */ +	bfin_write_EBIU_DDRCTL3(CONFIG_EBIU_DDRCTL3_VAL); +# endif +# ifdef CONFIG_EBIU_DDRQUE_VAL +	bfin_write_EBIU_DDRQUE(bfin_read_EBIU_DDRQUE() | CONFIG_EBIU_DDRQUE_VAL); +# endif +#endif + +#endif /* __ADSPBF60x__ */ +	serial_putc('e'); +} + +__attribute__((always_inline)) static inline void +check_hibernation(ADI_BOOT_DATA *bs, u16 vr_ctl, bool put_into_srfs) +{ +	serial_putc('a'); + +	if (!CONFIG_MEM_SIZE) +		return; + +	serial_putc('b'); +#ifdef __ADSPBF60x__ +	if (bfin_read32(DPM0_RESTORE0) != 0) { +		uint32_t reg = bfin_read_DMC0_CTL(); +		reg &= ~0x8; +		bfin_write_DMC0_CTL(reg); + +		while ((bfin_read_DMC0_STAT() & 0x8)) +			continue; +		while (!(bfin_read_DMC0_STAT() & 0x1)) +			continue; + +		serial_putc('z'); +		uint32_t *hibernate_magic = +			(uint32_t *)bfin_read32(DPM0_RESTORE4); +		SSYNC(); /* make sure memory controller is done */ +		if (hibernate_magic[0] == 0xDEADBEEF) { +			serial_putc('c'); +			SSYNC(); +			bfin_write_EVT15(hibernate_magic[1]); +			bfin_write_IMASK(EVT_IVG15); +			__asm__ __volatile__ ( +				/* load reti early to avoid anomaly 281 */ +				"reti = %2;" +				/* clear hibernate magic */ +				"[%0] = %1;" +				/* load stack pointer */ +				"SP = [%0 + 8];" +				/* lower ourselves from reset ivg to ivg15 */ +				"raise 15;" +				"nop;nop;nop;" +				"rti;" +				: +				: "p"(hibernate_magic), +				"d"(0x2000 /* jump.s 0 */), +				"d"(0xffa00000) +			); +		} + + +	} +#else +	/* Are we coming out of hibernate (suspend to memory) ? +	 * The memory layout is: +	 * 0x0: hibernate magic for anomaly 307 (0xDEADBEEF) +	 * 0x4: return address +	 * 0x8: stack pointer +	 * +	 * SCKELOW is unreliable on older parts (anomaly 307) +	 */ +	if (ANOMALY_05000307 || vr_ctl & 0x8000) { +		uint32_t *hibernate_magic = 0; + +		SSYNC(); +		if (hibernate_magic[0] == 0xDEADBEEF) { +			serial_putc('c'); +			bfin_write_EVT15(hibernate_magic[1]); +			bfin_write_IMASK(EVT_IVG15); +			__asm__ __volatile__ ( +				/* load reti early to avoid anomaly 281 */ +				"reti = %0;" +				/* clear hibernate magic */ +				"[%0] = %1;" +				/* load stack pointer */ +				"SP = [%0 + 8];" +				/* lower ourselves from reset ivg to ivg15 */ +				"raise 15;" +				"rti;" +				: +				: "p"(hibernate_magic), "d"(0x2000 /* jump.s 0 */) +			); +		} +		serial_putc('d'); +	} +#endif + +	serial_putc('e'); +} + +BOOTROM_CALLED_FUNC_ATTR +void initcode(ADI_BOOT_DATA *bs) +{ +	ADI_BOOT_DATA bootstruct_scratch; + +	/* Setup NMI handler before anything else */ +	program_nmi_handler(); + +	serial_init(); + +	serial_putc('A'); + +	/* If the bootstruct is NULL, then it's because we're loading +	 * dynamically and not via LDR (bootrom).  So set the struct to +	 * some scratch space. +	 */ +	if (!bs) +		bs = &bootstruct_scratch; + +	serial_putc('B'); +	bool put_into_srfs = maybe_self_refresh(bs); + +	serial_putc('C'); +	uint sdivB, divB, vcoB; +	program_early_devices(bs, &sdivB, &divB, &vcoB); + +	serial_putc('D'); +	u16 vr_ctl = program_clocks(bs, put_into_srfs); + +	serial_putc('E'); +	update_serial_clocks(bs, sdivB, divB, vcoB); + +	serial_putc('F'); +	program_memory_controller(bs, put_into_srfs); + +	serial_putc('G'); +	check_hibernation(bs, vr_ctl, put_into_srfs); + +	serial_putc('H'); +	program_async_controller(bs); + +#ifdef CONFIG_BFIN_BOOTROM_USES_EVT1 +	serial_putc('I'); +	/* Tell the bootrom where our entry point is so that it knows +	 * where to jump to when finishing processing the LDR.  This +	 * allows us to avoid small jump blocks in the LDR, and also +	 * works around anomaly 05000389 (init address in external +	 * memory causes bootrom to trigger external addressing IVHW). +	 */ +	if (CONFIG_BFIN_BOOT_MODE != BFIN_BOOT_BYPASS) +		bfin_write_EVT1(CONFIG_SYS_MONITOR_BASE); +#endif + +	serial_putc('>'); +	serial_putc('\n'); + +	serial_deinit(); +} diff --git a/roms/u-boot/arch/blackfin/cpu/initcode.h b/roms/u-boot/arch/blackfin/cpu/initcode.h new file mode 100644 index 00000000..ab7fa450 --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/initcode.h @@ -0,0 +1,123 @@ +/* + * Code for early processor initialization + * + * Copyright (c) 2004-2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __BFIN_INITCODE_H__ +#define __BFIN_INITCODE_H__ + +#include <asm/mach-common/bits/bootrom.h> + +#ifndef BFIN_IN_INITCODE +# define serial_putc(c) +#endif + +#ifndef __ADSPBF60x__ + +#ifndef CONFIG_EBIU_RSTCTL_VAL +# define CONFIG_EBIU_RSTCTL_VAL 0 /* only MDDRENABLE is useful */ +#endif +#if ((CONFIG_EBIU_RSTCTL_VAL & 0xFFFFFFC4) != 0) +# error invalid EBIU_RSTCTL value: must not set reserved bits +#endif + +#ifndef CONFIG_EBIU_MBSCTL_VAL +# define CONFIG_EBIU_MBSCTL_VAL 0 +#endif + +#if defined(CONFIG_EBIU_DDRQUE_VAL) && ((CONFIG_EBIU_DDRQUE_VAL & 0xFFFF8000) != 0) +# error invalid EBIU_DDRQUE value: must not set reserved bits +#endif + +#endif /* __ADSPBF60x__ */ + +__attribute__((always_inline)) static inline void +program_async_controller(ADI_BOOT_DATA *bs) +{ +#ifdef BFIN_IN_INITCODE +	/* +	 * We really only need to setup the async banks early if we're +	 * booting out of it.  Otherwise, do it later on in cpu_init. +	 */ +	if (CONFIG_BFIN_BOOT_MODE != BFIN_BOOT_BYPASS && +	    CONFIG_BFIN_BOOT_MODE != BFIN_BOOT_PARA) +		return; +#endif + +	serial_putc('a'); + +#ifndef __ADSPBF60x__ +	/* Program the async banks controller. */ +#ifdef EBIU_AMGCTL +	bfin_write_EBIU_AMBCTL0(CONFIG_EBIU_AMBCTL0_VAL); +	bfin_write_EBIU_AMBCTL1(CONFIG_EBIU_AMBCTL1_VAL); +	bfin_write_EBIU_AMGCTL(CONFIG_EBIU_AMGCTL_VAL); +#endif + +	serial_putc('b'); + +	/* Not all parts have these additional MMRs. */ +#ifdef EBIU_MBSCTL +	bfin_write_EBIU_MBSCTL(CONFIG_EBIU_MBSCTL_VAL); +#endif +#ifdef EBIU_MODE +# ifdef CONFIG_EBIU_MODE_VAL +	bfin_write_EBIU_MODE(CONFIG_EBIU_MODE_VAL); +# endif +# ifdef CONFIG_EBIU_FCTL_VAL +	bfin_write_EBIU_FCTL(CONFIG_EBIU_FCTL_VAL); +# endif +#endif + +	serial_putc('c'); + +#else 	/* __ADSPBF60x__ */ +	/* Program the static memory controller. */ +# ifdef CONFIG_SMC_GCTL_VAL +	bfin_write_SMC_GCTL(CONFIG_SMC_GCTL_VAL); +# endif +# ifdef CONFIG_SMC_B0CTL_VAL +	bfin_write_SMC_B0CTL(CONFIG_SMC_B0CTL_VAL); +# endif +# ifdef CONFIG_SMC_B0TIM_VAL +	bfin_write_SMC_B0TIM(CONFIG_SMC_B0TIM_VAL); +# endif +# ifdef CONFIG_SMC_B0ETIM_VAL +	bfin_write_SMC_B0ETIM(CONFIG_SMC_B0ETIM_VAL); +# endif +# ifdef CONFIG_SMC_B1CTL_VAL +	bfin_write_SMC_B1CTL(CONFIG_SMC_B1CTL_VAL); +# endif +# ifdef CONFIG_SMC_B1TIM_VAL +	bfin_write_SMC_B1TIM(CONFIG_SMC_B1TIM_VAL); +# endif +# ifdef CONFIG_SMC_B1ETIM_VAL +	bfin_write_SMC_B1ETIM(CONFIG_SMC_B1ETIM_VAL); +# endif +# ifdef CONFIG_SMC_B2CTL_VAL +	bfin_write_SMC_B2CTL(CONFIG_SMC_B2CTL_VAL); +# endif +# ifdef CONFIG_SMC_B2TIM_VAL +	bfin_write_SMC_B2TIM(CONFIG_SMC_B2TIM_VAL); +# endif +# ifdef CONFIG_SMC_B2ETIM_VAL +	bfin_write_SMC_B2ETIM(CONFIG_SMC_B2ETIM_VAL); +# endif +# ifdef CONFIG_SMC_B3CTL_VAL +	bfin_write_SMC_B3CTL(CONFIG_SMC_B3CTL_VAL); +# endif +# ifdef CONFIG_SMC_B3TIM_VAL +	bfin_write_SMC_B3TIM(CONFIG_SMC_B3TIM_VAL); +# endif +# ifdef CONFIG_SMC_B3ETIM_VAL +	bfin_write_SMC_B3ETIM(CONFIG_SMC_B3ETIM_VAL); +# endif + +#endif /* __ADSPBF60x__ */ +	serial_putc('d'); +} + +#endif diff --git a/roms/u-boot/arch/blackfin/cpu/interrupt.S b/roms/u-boot/arch/blackfin/cpu/interrupt.S new file mode 100644 index 00000000..0e5e59e1 --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/interrupt.S @@ -0,0 +1,157 @@ +/* + * interrupt.S - trampoline default exceptions/interrupts to C handlers + * + * Copyright (c) 2005-2009 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <config.h> +#include <asm/blackfin.h> +#include <asm/entry.h> +#include <asm/ptrace.h> +#include <asm/deferred.h> +#include <asm/mach-common/bits/core.h> + +.text + +/* default entry point for exceptions */ +ENTRY(_trap) +	CONFIG_BFIN_SCRATCH_REG = sp; +	sp.l = LO(L1_SRAM_SCRATCH_END - 20); +	sp.h = HI(L1_SRAM_SCRATCH_END - 20); +	SAVE_ALL_SYS + +	r0 = sp;	/* stack frame pt_regs pointer argument ==> r0 */ +	r1 = 3;		/* EVT3 space */ +	sp += -12; +	call _trap_c; +	sp += 12; + +#ifdef CONFIG_EXCEPTION_DEFER +	CC = R0 == 0; +	IF CC JUMP .Lexit_trap; + +	/* To avoid double faults, lower our priority to IRQ5 */ +	p4.l = lo(COREMMR_BASE); +	p4.h = hi(COREMMR_BASE); + +	r7.h = _exception_to_level5; +	r7.l = _exception_to_level5; +	[p4 + (EVT5 - COREMMR_BASE)] = r7; + +	/* +	 * Save these registers, as they are only valid in exception context +	 *  (where we are now - as soon as we defer to IRQ5, they can change) +	 */ +	p5.l = _deferred_regs; +	p5.h = _deferred_regs; +	r6 = [p4 + (DCPLB_FAULT_ADDR - COREMMR_BASE)]; +	[p5 + (deferred_regs_DCPLB_FAULT_ADDR * 4)] = r6; + +	r6 = [p4 + (ICPLB_FAULT_ADDR - COREMMR_BASE)]; +	[p5 + (deferred_regs_ICPLB_FAULT_ADDR * 4)] = r6; + +	/* Save the state of single stepping */ +	r6 = SYSCFG; +	[p5 + (deferred_regs_SYSCFG * 4)] = r6; +	/* Clear it while we handle the exception in IRQ5 mode +	 * RESTORE_ALL_SYS will load it, so all we need to do is store it +	 * in the right place +	 */ +	BITCLR(r6, SYSCFG_SSSTEP_P); +	[SP + PT_SYSCFG] = r6; + +	/* Since we are going to clobber RETX, we need to save it */ +	r6 = retx; +	[p5 + (deferred_regs_retx * 4)] = r6; + +	/* Save the current IMASK, since we change in order to jump to level 5 */ +	cli r6; +	[p5 + (deferred_regs_IMASK * 4)] = r6; + +	/* Disable all interrupts, but make sure level 5 is enabled so +	 * we can switch to that level. +	 */ +	r6 = 0x3f; +	sti r6; + +	/* Clobber RETX so we don't end up back at a faulting instruction */ +	[sp + PT_RETX] = r7; + +	/* In case interrupts are disabled IPEND[4] (global interrupt disable bit) +	 * clear it (re-enabling interrupts again) by the special sequence of pushing +	 * RETI onto the stack.  This way we can lower ourselves to IVG5 even if the +	 * exception was taken after the interrupt handler was called but before it +	 * got a chance to enable global interrupts itself. +	 */ +	[--sp] = reti; +	sp += 4; + +	RAISE 5; +.Lexit_trap: +#endif + +#if ANOMALY_05000257 +	R7  = LC0; +	LC0 = R7; +	R7  = LC1; +	LC1 = R7; +#endif + +	RESTORE_ALL_SYS +	sp = CONFIG_BFIN_SCRATCH_REG; +	rtx; +ENDPROC(_trap) + +#ifdef CONFIG_EXCEPTION_DEFER +/* Deferred (IRQ5) exceptions */ +ENTRY(_exception_to_level5) +	SAVE_ALL_SYS + +	/* Now we have to fix things up */ +	p4.l = lo(EVT5); +	p4.h = hi(EVT5); +	r0.l = _evt_default; +	r0.h = _evt_default; +	[p4] = r0; +	csync; + +	p4.l = _deferred_regs; +	p4.h = _deferred_regs; +	r0 = [p4 + (deferred_regs_retx * 4)]; +	[sp + PT_PC] = r0; + +	r0 = [p4 + (deferred_regs_SYSCFG * 4)]; +	[sp + PT_SYSCFG] = r0; + +	r0 = sp;	/* stack frame pt_regs pointer argument ==> r0 */ +	r1 = 5;	/* EVT5 space */ +	sp += -12; +	call _trap_c; +	sp += 12; + +	/* Restore IMASK */ +	r0 = [p4 + (deferred_regs_IMASK * 4)]; +	sti r0; + +	RESTORE_ALL_SYS + +	rti; +ENDPROC(_exception_to_level5) +#endif + +/* 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) + +/* NMI handler */ +ENTRY(_evt_nmi) +	rtn; +ENDPROC(_evt_nmi) diff --git a/roms/u-boot/arch/blackfin/cpu/interrupts.c b/roms/u-boot/arch/blackfin/cpu/interrupts.c new file mode 100644 index 00000000..91898166 --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/interrupts.c @@ -0,0 +1,154 @@ +/* + * 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 <watchdog.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 = CONFIG_SYS_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) { +		WATCHDOG_RESET(); + +		/* +		 * 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) +{ +	bfin_write_TCNTL(0x1); +	CSYNC(); +	bfin_write_TSCALE(0x0); +	bfin_write_TCOUNT(MAX_TIM_LOAD); +	bfin_write_TPERIOD(MAX_TIM_LOAD); +	bfin_write_TCNTL(0x7); +	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 - bfin_read_TCOUNT()); + +	/* +	 * 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); +} diff --git a/roms/u-boot/arch/blackfin/cpu/jtag-console.c b/roms/u-boot/arch/blackfin/cpu/jtag-console.c new file mode 100644 index 00000000..7cddb85a --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/jtag-console.c @@ -0,0 +1,228 @@ +/* + * jtag-console.c - console driver over Blackfin JTAG + * + * Copyright (c) 2008-2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <malloc.h> +#include <stdio_dev.h> +#include <asm/blackfin.h> + +#ifdef DEBUG +# define dprintf(...) serial_printf(__VA_ARGS__) +#else +# define dprintf(...) do { if (0) printf(__VA_ARGS__); } while (0) +#endif + +static inline void dprintf_decode(const char *s, uint32_t len) +{ +	uint32_t i; +	for (i = 0; i < len; ++i) +		if (s[i] < 0x20 || s[i] >= 0x7f) +			dprintf("\\%o", s[i]); +		else +			dprintf("%c", s[i]); +} + +static inline uint32_t bfin_write_emudat(uint32_t emudat) +{ +	__asm__ __volatile__("emudat = %0;" : : "d"(emudat)); +	return emudat; +} + +static inline uint32_t bfin_read_emudat(void) +{ +	uint32_t emudat; +	__asm__ __volatile__("%0 = emudat;" : "=d"(emudat)); +	return emudat; +} + +#ifndef CONFIG_JTAG_CONSOLE_TIMEOUT +# define CONFIG_JTAG_CONSOLE_TIMEOUT 500 +#endif + +/* The Blackfin tends to be much much faster than the JTAG hardware. */ +static bool jtag_write_emudat(uint32_t emudat) +{ +	static bool overflowed = false; +	ulong timeout = get_timer(0); +	while (bfin_read_DBGSTAT() & 0x1) { +		if (overflowed) +			return overflowed; +		if (get_timer(timeout) > CONFIG_JTAG_CONSOLE_TIMEOUT) +			overflowed = true; +	} +	overflowed = false; +	bfin_write_emudat(emudat); +	return overflowed; +} +/* Transmit a buffer.  The format is: + * [32bit length][actual data] + */ +static void jtag_send(const char *raw_str, uint32_t len) +{ +	const char *cooked_str; +	uint32_t i, ex; + +	if (len == 0) +		return; + +	/* Ugh, need to output \r after \n */ +	ex = 0; +	for (i = 0; i < len; ++i) +		if (raw_str[i] == '\n') +			++ex; +	if (ex) { +		char *c = malloc(len + ex); +		cooked_str = c; +		for (i = 0; i < len; ++i) { +			*c++ = raw_str[i]; +			if (raw_str[i] == '\n') +				*c++ = '\r'; +		} +		len += ex; +	} else +		cooked_str = raw_str; + +	dprintf("%s(\"", __func__); +	dprintf_decode(cooked_str, len); +	dprintf("\", %i)\n", len); + +	/* First send the length */ +	if (jtag_write_emudat(len)) +		goto done; + +	/* Then send the data */ +	for (i = 0; i < len; i += 4) { +		uint32_t emudat = +			(cooked_str[i + 0] <<  0) | +			(cooked_str[i + 1] <<  8) | +			(cooked_str[i + 2] << 16) | +			(cooked_str[i + 3] << 24); +		if (jtag_write_emudat(emudat)) { +			bfin_write_emudat(0); +			goto done; +		} +	} + + done: +	if (cooked_str != raw_str) +		free((char *)cooked_str); +} +static void jtag_putc(const char c) +{ +	jtag_send(&c, 1); +} +static void jtag_puts(const char *s) +{ +	jtag_send(s, strlen(s)); +} + +static size_t inbound_len, leftovers_len; + +/* Lower layers want to know when jtag has data */ +static int jtag_tstc_dbg(void) +{ +	int ret = (bfin_read_DBGSTAT() & 0x2); +	if (ret) +		dprintf("%s: ret:%i\n", __func__, ret); +	return ret; +} + +/* Higher layers want to know when any data is available */ +static int jtag_tstc(void) +{ +	return jtag_tstc_dbg() || leftovers_len; +} + +/* Receive a buffer.  The format is: + * [32bit length][actual data] + */ +static uint32_t leftovers; +static int jtag_getc(void) +{ +	int ret; +	uint32_t emudat; + +	dprintf("%s: inlen:%zu leftlen:%zu left:%x\n", __func__, +		inbound_len, leftovers_len, leftovers); + +	/* see if any data is left over */ +	if (leftovers_len) { +		--leftovers_len; +		ret = leftovers & 0xff; +		leftovers >>= 8; +		return ret; +	} + +	/* wait for new data ! */ +	while (!jtag_tstc_dbg()) +		continue; +	emudat = bfin_read_emudat(); + +	if (inbound_len == 0) { +		/* grab the length */ +		inbound_len = emudat; +	} else { +		/* store the bytes */ +		leftovers_len = min(4, inbound_len); +		inbound_len -= leftovers_len; +		leftovers = emudat; +	} + +	return jtag_getc(); +} + +int drv_jtag_console_init(void) +{ +	struct stdio_dev dev; +	int ret; + +	memset(&dev, 0x00, sizeof(dev)); +	strcpy(dev.name, "jtag"); +	dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; +	dev.putc = jtag_putc; +	dev.puts = jtag_puts; +	dev.tstc = jtag_tstc; +	dev.getc = jtag_getc; + +	ret = stdio_register(&dev); +	return (ret == 0 ? 1 : ret); +} + +#ifdef CONFIG_UART_CONSOLE_IS_JTAG +#include <serial.h> +/* Since the JTAG is always available (at power on), allow it to fake a UART */ +void jtag_serial_setbrg(void) +{ +} + +int jtag_serial_init(void) +{ +	return 0; +} + +static struct serial_device serial_jtag_drv = { +	.name	= "jtag", +	.start	= jtag_serial_init, +	.stop	= NULL, +	.setbrg	= jtag_serial_setbrg, +	.putc	= jtag_putc, +	.puts	= jtag_puts, +	.tstc	= jtag_tstc, +	.getc	= jtag_getc, +}; + +void bfin_jtag_initialize(void) +{ +	serial_register(&serial_jtag_drv); +} + +struct serial_device *default_serial_console(void) +{ +	return &serial_jtag_drv; +} +#endif diff --git a/roms/u-boot/arch/blackfin/cpu/os_log.c b/roms/u-boot/arch/blackfin/cpu/os_log.c new file mode 100644 index 00000000..2092d9e3 --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/os_log.c @@ -0,0 +1,30 @@ +/* + * functions for handling OS log buffer + * + * Copyright (c) 2009 Analog Devices Inc. + * + * Licensed under the 2-clause BSD. + */ + +#include <common.h> + +#define OS_LOG_MAGIC       0xDEADBEEF +#define OS_LOG_MAGIC_ADDR  ((unsigned long *)0x4f0) +#define OS_LOG_PTR_ADDR    ((char **)0x4f4) + +int bfin_os_log_check(void) +{ +	if (*OS_LOG_MAGIC_ADDR != OS_LOG_MAGIC) +		return 0; +	*OS_LOG_MAGIC_ADDR = 0; +	return 1; +} + +void bfin_os_log_dump(void) +{ +	char *log = *OS_LOG_PTR_ADDR; +	while (*log) { +		puts(log); +		log += strlen(log) + 1; +	} +} diff --git a/roms/u-boot/arch/blackfin/cpu/reset.c b/roms/u-boot/arch/blackfin/cpu/reset.c new file mode 100644 index 00000000..b6718d3b --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/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 <asm/mach-common/bits/bootrom.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__)) +static void bfin_reset(void) +{ +#ifdef SWRST +	/* 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(); + +	/* Initiate System software reset. */ +	bfin_write_SWRST(0x7); + +	/* Due to the way reset is handled in the hardware, we need +	 * to delay for 10 SCLKS.  The only reliable way to do this is +	 * to calculate the CCLK/SCLK ratio and multiply 10.  For now, +	 * we'll assume worse case which is a 1:15 ratio. +	 */ +	asm( +		"LSETUP (1f, 1f) LC0 = %0\n" +		"1: nop;" +		: +		: "a" (15 * 10) +		: "LC0", "LB0", "LT0" +	); + +	/* Clear System software reset */ +	bfin_write_SWRST(0); + +	/* The BF526 ROM will crash during reset */ +#if defined(__ADSPBF522__) || defined(__ADSPBF524__) || defined(__ADSPBF526__) +	/* Seems to be fixed with newer parts though ... */ +	if (__SILICON_REVISION__ < 1 && bfin_revid() < 1) +		bfin_read_SWRST(); +#endif + +	/* 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" +	); +#endif + +	while (1) +#if defined(__ADSPBF60x__) +		bfin_write_RCU0_CTL(0x1); +#else +		/* Issue core reset */ +		asm("raise 1"); +#endif +} + +/* 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. + */ +int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +	if (board_reset) +		board_reset(); +	if (ANOMALY_05000353 || ANOMALY_05000386) +		while (1) +			asm("jump (%0);" : : "a" (bfin_reset)); +	else +		bfrom_SoftReset((void *)(L1_SRAM_SCRATCH_END - 20)); +	return 0; +} diff --git a/roms/u-boot/arch/blackfin/cpu/start.S b/roms/u-boot/arch/blackfin/cpu/start.S new file mode 100644 index 00000000..29a7c232 --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/start.S @@ -0,0 +1,253 @@ +/* + * U-boot - start.S Startup file for Blackfin u-boot + * + * Copyright (c) 2005-2008 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. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <config.h> +#include <asm/blackfin.h> +#include <asm/mach-common/bits/watchdog.h> +#include <asm/mach-common/bits/core.h> +#include <asm/mach-common/bits/pll.h> +#include <asm/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_END - 20); +	sp.h = HI(L1_SRAM_SCRATCH_END - 20); + +	/* Optimization register tricks: keep a base value in the +	 * reserved P registers so we use the load/store with an +	 * offset syntax.  R0 = [P5 + <constant>]; +	 *   P4 - system MMR base +	 *   P5 - core MMR base +	 */ +#ifdef CONFIG_HW_WATCHDOG +	p4.l = 0; +	p4.h = HI(SYSMMR_BASE); +#endif +	p5.l = 0; +	p5.h = HI(COREMMR_BASE); + +#ifdef CONFIG_HW_WATCHDOG +	/* Program the watchdog with default timeout of ~5 seconds. +	 * That should be long enough to bootstrap ourselves up and +	 * then the common u-boot code can take over. +	 */ +	r1 = WDDIS; +# ifdef __ADSPBF60x__ +	[p4 + (WDOG_CTL - SYSMMR_BASE)] = r1; +# else +	W[p4 + (WDOG_CTL - SYSMMR_BASE)] = r1; +# endif +	SSYNC; +	r0 = 0; +	r0.h = HI(MSEC_TO_SCLK(CONFIG_WATCHDOG_TIMEOUT_MSECS)); +	[p4 + (WDOG_CNT - SYSMMR_BASE)] = r0; +	SSYNC; +	r1 = WDEN; +	/* fire up the watchdog - R0.L above needs to be 0x0000 */ +# ifdef __ADSPBF60x__ +	[p4 + (WDOG_CTL - SYSMMR_BASE)] = r1; +# else +	W[p4 + (WDOG_CTL - SYSMMR_BASE)] = r1; +# endif +	SSYNC; +#endif + +	/* Turn on the serial for debugging the init process */ +	serial_early_init +	serial_early_set_baud + +	serial_early_puts("Init Registers"); + +	/* Disable self-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_MEM_SIZE +	/* Figure out where we are currently executing so that we can decide +	 * how to best reprogram and relocate things.  We'll pass below: +	 *  R4: load address of _start +	 *  R5: current (not load) address of _start +	 */ +	serial_early_puts("Find ourselves"); + +	call _get_pc; +.Loffset: +	r1.l = .Loffset; +	r1.h = .Loffset; +	r4.l = _start; +	r4.h = _start; +	r3 = r1 - r4; +	r5 = r0 - r3; + +	/* 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); +	cc = r4 == r5; +	if cc jump .Lnorelocate; +	r6 = 0 (x); + +	/* Turn off caches as they require CPLBs and a CPLB miss requires +	 * a software exception handler to process it.  But we're about to +	 * clobber any previous executing software (like U-Boot that just +	 * launched a new U-Boot via 'go'), so any handler state will be +	 * unreliable after the memcpy below. +	 */ +	serial_early_puts("Kill Caches"); +	r0 = 0; +	[p5 + (IMEM_CONTROL - COREMMR_BASE)] = r0; +	[p5 + (DMEM_CONTROL - COREMMR_BASE)] = r0; +	ssync; + +	/* 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, memory, and setup our async banks. +	 */ +	serial_early_puts("Program Clocks"); + +	/* if we're executing >=0x20000000, then we dont need to dma */ +	r3 = 0x0; +	r3.h = 0x2000; +	cc = r5 < r3 (iu); +	if cc jump .Ldma_and_reprogram; +#else +	r6 = 1 (x);	/* fake loaded_from_ldr = 1 */ +#endif +	r0 = 0 (x);	/* set bootstruct to NULL */ +	call _initcode; +	jump .Lprogrammed; + +	/* we're sitting in external memory, so dma into L1 and reprogram */ +.Ldma_and_reprogram: +	r0.l = LO(L1_INST_SRAM); +	r0.h = HI(L1_INST_SRAM); +	r1.l = __initcode_lma; +	r1.h = __initcode_lma; +	r2.l = __initcode_len; +	r2.h = __initcode_len; +	r1 = r1 - r4;	/* convert r1 from load address of initcode ... */ +	r1 = r1 + r5;	/* ... to current (not load) address of initcode */ +	p3 = r0; +	call _dma_memcpy_nocache; +	r0 = 0 (x);	/* set bootstruct to NULL */ +	call (p3); + +	/* Since we reprogrammed SCLK, we need to update the serial divisor */ +.Lprogrammed: +	serial_early_set_baud + +#if CONFIG_MEM_SIZE +	/* Relocate from wherever we are (FLASH/RAM/etc...) to the hardcoded +	 * monitor location in the end of RAM.  We know that memcpy() only +	 * uses registers, so it is safe to call here.  Note that this only +	 * copies to external memory ... we do not start executing out of +	 * it yet (see "lower to 15" below). +	 */ +	serial_early_puts("Relocate"); +	r0 = r4; +	r1 = r5; +	r2.l = LO(CONFIG_SYS_MONITOR_LEN); +	r2.h = HI(CONFIG_SYS_MONITOR_LEN); +	call _memcpy_ASM; +#endif + +.Lnorelocate: +	/* 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_vma; +	r0.h = __bss_vma; +	r1 = 0 (x); +	r2.l = __bss_len; +	r2.h = __bss_len; +	call _memset; + + +	/* Setup the actual stack in external memory */ +	sp.h = HI(CONFIG_STACKBASE); +	sp.l = LO(CONFIG_STACKBASE); +	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 ".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.  If executing out of +	 * flash, these steps also changes the code flow from flash +	 * to external memory. +	 */ +	serial_early_puts("Lower to 15"); +	r0 = r7; +	r1 = r6; +	p1.l = .Lenable_nested; +	p1.h = .Lenable_nested; +	[p5 + (EVT15 - COREMMR_BASE)] = p1; +	r7 = EVT_IVG15 (z); +	sti r7; +	raise 15; +	p3.l = .LWAIT_HERE; +	p3.h = .LWAIT_HERE; +	reti = p3; +	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) + +LENTRY(_get_pc) +	r0 = rets; +#if ANOMALY_05000371 +	NOP; +	NOP; +	NOP; +#endif +	rts; +ENDPROC(_get_pc) diff --git a/roms/u-boot/arch/blackfin/cpu/traps.c b/roms/u-boot/arch/blackfin/cpu/traps.c new file mode 100644 index 00000000..10f72f82 --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/traps.c @@ -0,0 +1,435 @@ +/* + * 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 <kgdb.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 <asm/deferred.h> +#include "cpu.h" + +#ifdef CONFIG_DEBUG_DUMP +# define ENABLE_DUMP 1 +#else +# define ENABLE_DUMP 0 +#endif + +#define trace_buffer_save(x) \ +	do { \ +		if (!ENABLE_DUMP) \ +			break; \ +		(x) = bfin_read_TBUFCTL(); \ +		bfin_write_TBUFCTL((x) & ~TBUFEN); \ +	} while (0) + +#define trace_buffer_restore(x) \ +	do { \ +		if (!ENABLE_DUMP) \ +			break; \ +		bfin_write_TBUFCTL((x)); \ +	} while (0); + +/* 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, +	} +}; + +#ifdef CONFIG_EXCEPTION_DEFER +unsigned int deferred_regs[deferred_regs_last]; +#endif + +/* + * Handle all exceptions while running in EVT3 or EVT5 + */ +int trap_c(struct pt_regs *regs, uint32_t level) +{ +	uint32_t ret = 0; +	uint32_t trapnr = (regs->seqstat & EXCAUSE); +	unsigned long tflags; +	bool data = false; + +	/* +	 * Keep the trace buffer so that a miss here points people +	 * to the right place (their code).  Crashes here rarely +	 * happen.  If they do, only the Blackfin maintainer cares. +	 */ +	trace_buffer_save(tflags); + +	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; +				break; +			} +		} + +		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; + +#ifdef CONFIG_EXCEPTION_DEFER +		/* This should never happen */ +		if (level == 5) +			bfin_panic(regs); +#endif + +		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'), (void *)new_cplb_addr); +			bfin_panic(regs); +		} else +			debug("CPLB addr %p matches map 0x%p - 0x%p\n", +				(void *)new_cplb_addr, +				(void *)bfin_memory_map[i].start, +				(void *)bfin_memory_map[i].end); +		new_cplb_data = (data ? bfin_memory_map[i].data_flags : bfin_memory_map[i].inst_flags); + +		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 = %zu\n", i); +		CPLB_DATA = CPLB_DATA_BASE + i; +		while (*CPLB_DATA & CPLB_LOCK) { +			debug("skipping %zu %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 %zu: 0x%p 0x%08X\n", i, +			(void *)*CPLB_ADDR, *CPLB_DATA); +		last_evicted = i + 1; + +		/* need to turn off cplbs whenever we muck with the cplb table */ +#if ENDCPLB != ENICPLB +# error cplb enable bit violates my sanity +#endif +		uint32_t mem_control = (data ? DMEM_CONTROL : IMEM_CONTROL); +		bfin_write32(mem_control, bfin_read32(mem_control) & ~ENDCPLB); +		*CPLB_ADDR = new_cplb_addr; +		*CPLB_DATA = new_cplb_data; +		bfin_write32(mem_control, bfin_read32(mem_control) | ENDCPLB); +		SSYNC(); + +		/* dump current table for debugging purposes */ +		CPLB_ADDR = CPLB_ADDR_BASE; +		CPLB_DATA = CPLB_DATA_BASE; +		for (i = 0; i < 16; ++i) +			debug("%2zu 0x%p 0x%08X\n", i, +				(void *)*CPLB_ADDR++, *CPLB_DATA++); + +		break; +	} +#ifdef CONFIG_CMD_KGDB +	/* Single step +	 * if we are in IRQ5, just ignore, otherwise defer, and handle it in kgdb +	 */ +	case VEC_STEP: +		if (level == 3) { +			/* If we just returned from an interrupt, the single step +			 * event is for the RTI instruction. +			 */ +			if (regs->retx == regs->pc) +				break; +			/* we just return if we are single stepping through IRQ5 */ +			if (regs->ipend & 0x20) +				break; +			/* Otherwise, turn single stepping off & fall through, +			 * which defers to IRQ5 +			 */ +			regs->syscfg &= ~1; +		} +		/* fall through */ +#endif +	default: +#ifdef CONFIG_CMD_KGDB +		if (level == 3) { +			/* We need to handle this at EVT5, so try again */ +			bfin_dump(regs); +			ret = 1; +			break; +		} +		if (debugger_exception_handler && (*debugger_exception_handler)(regs)) +			break; +#endif +		bfin_panic(regs); +	} + +	trace_buffer_restore(tflags); + +	return ret; +} + +#ifndef CONFIG_KALLSYMS +const char *symbol_lookup(unsigned long addr, unsigned long *caddr) +{ +	*caddr = addr; +	return "N/A"; +} +#endif + +static void decode_address(char *buf, unsigned long address) +{ +	unsigned long sym_addr; +	void *paddr = (void *)address; +	const char *sym = symbol_lookup(address, &sym_addr); + +	if (sym) { +		sprintf(buf, "<0x%p> { %s + 0x%lx }", paddr, sym, address - sym_addr); +		return; +	} + +	if (!address) +		sprintf(buf, "<0x%p> /* Maybe null pointer? */", paddr); +	else if (address >= CONFIG_SYS_MONITOR_BASE && +		 address < CONFIG_SYS_MONITOR_BASE + CONFIG_SYS_MONITOR_LEN) +		sprintf(buf, "<0x%p> /* somewhere in u-boot */", paddr); +	else +		sprintf(buf, "<0x%p> /* unknown address */", paddr); +} + +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]; +	int i; +	uint16_t hwerrcause, excause; + +	if (!ENABLE_DUMP) +		return; + +#ifndef CONFIG_CMD_KGDB +	/* fp->ipend is normally garbage, so load it ourself */ +	fp->ipend = bfin_read_IPEND(); +#endif + +	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%x: %s\n", hwerrcause, strhwerrcause(hwerrcause)); +	printf("  EXCAUSE   : 0x%x: %s\n", excause, strexcause(excause)); +	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); +	/* we lie and store RETI in "pc" */ +	decode_address(buf, fp->pc); +	printf(" RETI: %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, (unsigned long)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"); +} + +static void _dump_bfin_trace_buffer(void) +{ +	char buf[150]; +	int i = 0; + +	if (!ENABLE_DUMP) +		return; + +	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); +		} +	} +} + +void dump_bfin_trace_buffer(void) +{ +	unsigned long tflags; +	trace_buffer_save(tflags); +	_dump_bfin_trace_buffer(); +	trace_buffer_restore(tflags); +} + +void bfin_dump(struct pt_regs *regs) +{ +	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(); +	puts("\n"); + +	trace_buffer_restore(tflags); +} + +void bfin_panic(struct pt_regs *regs) +{ +	unsigned long tflags; +	trace_buffer_save(tflags); +	bfin_dump(regs); +	panic("PANIC: Blackfin internal error"); +} diff --git a/roms/u-boot/arch/blackfin/cpu/u-boot.lds b/roms/u-boot/arch/blackfin/cpu/u-boot.lds new file mode 100644 index 00000000..7f0411f6 --- /dev/null +++ b/roms/u-boot/arch/blackfin/cpu/u-boot.lds @@ -0,0 +1,140 @@ +/* + * U-boot - u-boot.lds.S + * + * Copyright (c) 2005-2010 Analog Device Inc. + * + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <config.h> +#include <asm/blackfin.h> +#undef ALIGN +#undef ENTRY + +#ifndef LDS_BOARD_TEXT +# define LDS_BOARD_TEXT +#endif + +/* If we don't actually load anything into L1 data, this will avoid + * a syntax error.  If we do actually load something into L1 data, + * we'll get a linker memory load error (which is what we'd want). + * This is here in the first place so we can quickly test building + * for different CPU's which may lack non-cache L1 data. + */ +#ifndef L1_DATA_A_SRAM +# define L1_DATA_A_SRAM      0 +# define L1_DATA_A_SRAM_SIZE 0 +#endif +#ifndef L1_DATA_B_SRAM +# define L1_DATA_B_SRAM      L1_DATA_A_SRAM +# define L1_DATA_B_SRAM_SIZE L1_DATA_A_SRAM_SIZE +#endif + +/* The 0xC offset is so we don't clobber the tiny LDR jump block. */ +#ifdef CONFIG_BFIN_BOOTROM_USES_EVT1 +# define L1_CODE_ORIGIN L1_INST_SRAM +#else +# define L1_CODE_ORIGIN L1_INST_SRAM + 0xC +#endif + +OUTPUT_ARCH(bfin) + +MEMORY +{ +#if CONFIG_MEM_SIZE +	ram     : ORIGIN = CONFIG_SYS_MONITOR_BASE, LENGTH = CONFIG_SYS_MONITOR_LEN +# define ram_code ram +# define ram_data ram +#else +# define ram_code l1_code +# define ram_data l1_data +#endif +	l1_code : ORIGIN = L1_CODE_ORIGIN,          LENGTH = L1_INST_SRAM_SIZE +	l1_data : ORIGIN = L1_DATA_B_SRAM,          LENGTH = L1_DATA_B_SRAM_SIZE +} + +ENTRY(_start) +SECTIONS +{ +	.text.pre : +	{ +		arch/blackfin/cpu/start.o (.text .text.*) + +		LDS_BOARD_TEXT +	} >ram_code + +	.text.init : +	{ +		arch/blackfin/cpu/initcode.o (.text .text.*) +	} >ram_code +	__initcode_lma = LOADADDR(.text.init); +	__initcode_len = SIZEOF(.text.init); + +	.text : +	{ +		*(.text .text.*) +	} >ram_code + +	.rodata : +	{ +		. = ALIGN(4); +		*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) +		. = ALIGN(4); +	} >ram_data + +	.data : +	{ +		. = ALIGN(4); +		*(.data .data.*) +		*(.data1) +		*(.sdata) +		*(.sdata2) +		*(.dynamic) +		CONSTRUCTORS +	} >ram_data + + +	.u_boot_list : { +		KEEP(*(SORT(.u_boot_list*))); +	} >ram_data + +	.text_l1 : +	{ +		. = ALIGN(4); +		__stext_l1 = .; +		*(.l1.text) +		. = ALIGN(4); +		__etext_l1 = .; +	} >l1_code AT>ram_code +	__text_l1_lma = LOADADDR(.text_l1); +	__text_l1_len = SIZEOF(.text_l1); +	ASSERT (__text_l1_len <= L1_INST_SRAM_SIZE, "L1 text overflow!") + +	.data_l1 : +	{ +		. = ALIGN(4); +		__sdata_l1 = .; +		*(.l1.data) +		*(.l1.bss) +		. = ALIGN(4); +		__edata_l1 = .; +	} >l1_data AT>ram_data +	__data_l1_lma = LOADADDR(.data_l1); +	__data_l1_len = SIZEOF(.data_l1); +	ASSERT (__data_l1_len <= L1_DATA_B_SRAM_SIZE, "L1 data overflow!") + +	.bss : +	{ +		. = ALIGN(4); +		*(.sbss) *(.scommon) +		*(.dynbss) +		*(.bss .bss.*) +		*(COMMON) +		. = ALIGN(4); +	} >ram_data +	__bss_vma = ADDR(.bss); +	__bss_len = SIZEOF(.bss); +}  | 
