diff options
Diffstat (limited to 'arch/blackfin/cpu')
| -rw-r--r-- | arch/blackfin/cpu/Makefile | 3 | ||||
| -rw-r--r-- | arch/blackfin/cpu/bootcount.c | 34 | ||||
| -rw-r--r-- | arch/blackfin/cpu/cmd_gpio.c | 120 | ||||
| -rw-r--r-- | arch/blackfin/cpu/cpu.c | 4 | ||||
| -rw-r--r-- | arch/blackfin/cpu/cpu.h | 2 | ||||
| -rw-r--r-- | arch/blackfin/cpu/gpio.c | 854 | ||||
| -rw-r--r-- | arch/blackfin/cpu/initcode.c | 32 | ||||
| -rw-r--r-- | arch/blackfin/cpu/interrupt.S | 5 | ||||
| -rw-r--r-- | arch/blackfin/cpu/serial.h | 19 | ||||
| -rw-r--r-- | arch/blackfin/cpu/traps.c | 76 | 
10 files changed, 1119 insertions, 30 deletions
diff --git a/arch/blackfin/cpu/Makefile b/arch/blackfin/cpu/Makefile index 211b8d545..b7f991dea 100644 --- a/arch/blackfin/cpu/Makefile +++ b/arch/blackfin/cpu/Makefile @@ -17,7 +17,10 @@ EXTRA    :=  CEXTRA   := initcode.o  SEXTRA   := start.o  SOBJS    := interrupt.o cache.o +COBJS-$(CONFIG_BOOTCOUNT_LIMIT) += bootcount.o +COBJS-$(CONFIG_CMD_GPIO) += cmd_gpio.o  COBJS-y  += cpu.o +COBJS-y  += gpio.o  COBJS-y  += interrupts.o  COBJS-$(CONFIG_JTAG_CONSOLE) += jtag-console.o  COBJS-y  += os_log.o diff --git a/arch/blackfin/cpu/bootcount.c b/arch/blackfin/cpu/bootcount.c new file mode 100644 index 000000000..6cf6dd58b --- /dev/null +++ b/arch/blackfin/cpu/bootcount.c @@ -0,0 +1,34 @@ +/* + * functions for handling bootcount support + * + * Copyright (c) 2010 Analog Devices Inc. + * + * Licensed under the 2-clause BSD. + */ + +/* This version uses one 32bit storage and combines the magic/count */ + +#include <common.h> + +/* We abuse the EVT0 MMR for bootcount storage by default */ +#ifndef CONFIG_SYS_BOOTCOUNT_ADDR +# define CONFIG_SYS_BOOTCOUNT_ADDR EVT0 +#endif + +#define MAGIC_MASK 0xffff0000 +#define COUNT_MASK 0x0000ffff + +void bootcount_store(ulong cnt) +{ +	ulong magic = (BOOTCOUNT_MAGIC & MAGIC_MASK) | (cnt & COUNT_MASK); +	bfin_write32(CONFIG_SYS_BOOTCOUNT_ADDR, magic); +} + +ulong bootcount_load(void) +{ +	ulong magic = bfin_read32(CONFIG_SYS_BOOTCOUNT_ADDR); +	if ((magic & MAGIC_MASK) == (BOOTCOUNT_MAGIC & MAGIC_MASK)) +		return magic & COUNT_MASK; +	else +		return 0; +} diff --git a/arch/blackfin/cpu/cmd_gpio.c b/arch/blackfin/cpu/cmd_gpio.c new file mode 100644 index 000000000..9e505b661 --- /dev/null +++ b/arch/blackfin/cpu/cmd_gpio.c @@ -0,0 +1,120 @@ +/* + * Control GPIO pins on the fly + * + * Copyright (c) 2008-2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <command.h> + +#include <asm/blackfin.h> +#include <asm/gpio.h> + +enum { +	GPIO_INPUT, +	GPIO_SET, +	GPIO_CLEAR, +	GPIO_TOGGLE, +}; + +int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +	if (argc == 2 && !strcmp(argv[1], "status")) { +		bfin_gpio_labels(); +		return 0; +	} + +	if (argc != 3) { + show_usage: +		printf("Usage:\n%s\n", cmdtp->usage); +		return 1; +	} + +	/* parse the behavior */ +	ulong sub_cmd; +	switch (argv[1][0]) { +		case 'i': sub_cmd = GPIO_INPUT;  break; +		case 's': sub_cmd = GPIO_SET;    break; +		case 'c': sub_cmd = GPIO_CLEAR;  break; +		case 't': sub_cmd = GPIO_TOGGLE; break; +		default:  goto show_usage; +	} + +	/* parse the pin with format: [p][port]<#> */ +	const char *str_pin = argv[2]; + +	/* grab the [p]<port> portion */ +	ulong port_base; +	if (*str_pin == 'p') ++str_pin; +	switch (*str_pin) { +#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:  goto show_usage; +	} + +	/* grab the <#> portion */ +	ulong pin = simple_strtoul(str_pin + 1, NULL, 10); +	if (pin > 15) +		goto show_usage; + +	/* grab the pin before we tweak it */ +	ulong gpio = port_base + pin; +	gpio_request(gpio, "cmd_gpio"); + +	/* finally, let's do it: set direction and exec command */ +	if (sub_cmd == GPIO_INPUT) { +		gpio_direction_input(gpio); +		printf("gpio: pin %lu on port %c set to input\n", pin, *str_pin); +		return 0; +	} + +	ulong value; +	switch (sub_cmd) { +		case GPIO_SET:    value = 1; break; +		case GPIO_CLEAR:  value = 0; break; +		case GPIO_TOGGLE: value = !gpio_get_value(gpio); break; +		default:          goto show_usage; +	} +	gpio_direction_output(gpio, value); +	printf("gpio: pin %lu on port %c (gpio %lu) value is %lu\n", +		pin, *str_pin, gpio, value); + +	gpio_free(gpio); + +	return 0; +} + +U_BOOT_CMD(gpio, 3, 0, do_gpio, +	"set/clear/toggle gpio output pins", +	"<set|clear|toggle> <port><pin>\n" +	"    - set/clear/toggle the specified pin (e.g. PF10)"); diff --git a/arch/blackfin/cpu/cpu.c b/arch/blackfin/cpu/cpu.c index 2c8fd86b8..18dbdf7ab 100644 --- a/arch/blackfin/cpu/cpu.c +++ b/arch/blackfin/cpu/cpu.c @@ -91,7 +91,9 @@ int irq_init(void)  #else  	bfin_write_SIC_IMASK(0);  #endif -	bfin_write_EVT2(evt_default);	/* NMI */ +	/* 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); diff --git a/arch/blackfin/cpu/cpu.h b/arch/blackfin/cpu/cpu.h index 0a13c285e..ba85e0b9a 100644 --- a/arch/blackfin/cpu/cpu.h +++ b/arch/blackfin/cpu/cpu.h @@ -29,10 +29,12 @@  void board_reset(void) __attribute__((__weak__));  void bfin_reset_or_hang(void) __attribute__((__noreturn__)); +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/arch/blackfin/cpu/gpio.c b/arch/blackfin/cpu/gpio.c new file mode 100644 index 000000000..488ca11bb --- /dev/null +++ b/arch/blackfin/cpu/gpio.c @@ -0,0 +1,854 @@ +/* + * GPIO Abstraction Layer + * + * 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> + +#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) +	(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, +#elif defined(CONFIG_BF54x) +	(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, +	(struct gpio_port_t *)PORTH_FER, +	(struct gpio_port_t *)PORTI_FER, +	(struct gpio_port_t *)PORTJ_FER, +#else +# error no gpio arrays defined +#endif +}; + +#if defined(CONFIG_BF52x) || defined(BF537_FAMILY) || defined(CONFIG_BF51x) +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 defined(CONFIG_BF54x) +	if (gpio == GPIO_PB15 || gpio == GPIO_PC14 || gpio == GPIO_PC15 +	    || gpio == GPIO_PH14 || gpio == GPIO_PH15 +	    || gpio == GPIO_PJ14 || gpio == GPIO_PJ15) +		return -EINVAL; +#endif +	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) +	if (usage == GPIO_USAGE) +		*port_fer[gpio_bank(gpio)] &= ~gpio_bit(gpio); +	else +		*port_fer[gpio_bank(gpio)] |= gpio_bit(gpio); +	SSYNC(); +#elif defined(CONFIG_BF54x) +	if (usage == GPIO_USAGE) +		gpio_array[gpio_bank(gpio)]->port_fer &= ~gpio_bit(gpio); +	else +		gpio_array[gpio_bank(gpio)]->port_fer |= 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; +	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) +				muxreg &= ~(1 << offset); +			else +				muxreg &= ~(3 << 1); + +			muxreg |= (function << offset); +			bfin_write_PORT_MUX(muxreg); +		} +	} +} +#elif defined(CONFIG_BF54x) +inline void portmux_setup(unsigned short per) +{ +	u32 pmux; +	u16 ident = P_IDENT(per); +	u16 function = P_FUNCT2MUX(per); + +	pmux = gpio_array[gpio_bank(ident)]->port_mux; + +	pmux &= ~(0x3 << (2 * gpio_sub_n(ident))); +	pmux |= (function & 0x3) << (2 * gpio_sub_n(ident)); + +	gpio_array[gpio_bank(ident)]->port_mux = pmux; +} + +inline u16 get_portmux(unsigned short per) +{ +	u32 pmux; +	u16 ident = P_IDENT(per); + +	pmux = gpio_array[gpio_bank(ident)]->port_mux; + +	return (pmux >> (2 * gpio_sub_n(ident)) & 0x3); +} +#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 + +#ifndef CONFIG_BF54x +/*********************************************************** +* +* 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) + +#else /* CONFIG_BF54x */ + +unsigned short get_gpio_dir(unsigned gpio) +{ +	return (0x01 & (gpio_array[gpio_bank(gpio)]->dir_clear >> gpio_sub_n(gpio))); +} + +#endif /* CONFIG_BF54x */ + +/*********************************************************** +* +* 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 +		 */ + +#ifdef CONFIG_BF54x +		if (!((per & P_MAYSHARE) && get_portmux(per) == P_FUNCT2MUX(per))) { +#else +		if (!(per & P_MAYSHARE)) { +#endif +			/* +			 * 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 bfin_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; +	} +#ifndef CONFIG_BF54x +	else {	/* Reset POLAR setting when acquiring a gpio for the first time */ +		set_gpio_polar(gpio, 0); +	} +#endif + +	reserve(gpio, gpio); +	set_label(gpio, label); + +	port_setup(gpio, GPIO_USAGE); + +	return 0; +} + +void bfin_gpio_free(unsigned gpio) +{ +	if (check_gpio(gpio) < 0) +		return; + +	if (unlikely(!is_reserved(gpio, gpio, 0))) { +		gpio_error(gpio); +		return; +	} + +	unreserve(gpio, gpio); + +	set_label(gpio, "free"); +} + +#ifdef BFIN_SPECIAL_GPIO_BANKS +DECLARE_RESERVED_MAP(special_gpio, gpio_bank(MAX_RESOURCES)); + +int bfin_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 bfin_special_gpio_free(unsigned gpio) +{ +	if (unlikely(!is_reserved(special_gpio, gpio, 0))) { +		gpio_error(gpio); +		return; +	} + +	reserve(special_gpio, gpio); +	reserve(peri, gpio); +	set_label(gpio, "free"); +} +#endif + +static inline void __bfin_gpio_direction_input(unsigned gpio) +{ +#ifdef CONFIG_BF54x +	gpio_array[gpio_bank(gpio)]->dir_clear = gpio_bit(gpio); +#else +	gpio_array[gpio_bank(gpio)]->dir &= ~gpio_bit(gpio); +#endif +	gpio_array[gpio_bank(gpio)]->inen |= gpio_bit(gpio); +} + +int bfin_gpio_direction_input(unsigned gpio) +{ +	unsigned long flags; + +	if (!is_reserved(gpio, gpio, 0)) { +		gpio_error(gpio); +		return -EINVAL; +	} + +	local_irq_save(flags); +	__bfin_gpio_direction_input(gpio); +	AWA_DUMMY_READ(inen); +	local_irq_restore(flags); + +	return 0; +} + +void bfin_gpio_toggle_value(unsigned gpio) +{ +#ifdef CONFIG_BF54x +	gpio_set_value(gpio, !gpio_get_value(gpio)); +#else +	gpio_array[gpio_bank(gpio)]->toggle = gpio_bit(gpio); +#endif +} + +void bfin_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); +} + +int bfin_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); +#ifdef CONFIG_BF54x +	gpio_array[gpio_bank(gpio)]->dir_set = gpio_bit(gpio); +#else +	gpio_array[gpio_bank(gpio)]->dir |= gpio_bit(gpio); +#endif + +	AWA_DUMMY_READ(dir); +	local_irq_restore(flags); + +	return 0; +} + +int bfin_gpio_get_value(unsigned gpio) +{ +#ifdef CONFIG_BF54x +	return (1 & (gpio_array[gpio_bank(gpio)]->data >> gpio_sub_n(gpio))); +#else +	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); +#endif +} + +/* 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); +} + +#ifdef CONFIG_BFIN_GPIO_TRACK +void bfin_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; +	} +} +#endif diff --git a/arch/blackfin/cpu/initcode.c b/arch/blackfin/cpu/initcode.c index 5f80ad615..007f5ce77 100644 --- a/arch/blackfin/cpu/initcode.c +++ b/arch/blackfin/cpu/initcode.c @@ -101,6 +101,28 @@ static inline void serial_putc(char c)  		continue;  } +__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 */ +		: "=r"(tmp1), "=r"(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 @@ -640,6 +662,9 @@ 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'); @@ -675,7 +700,12 @@ void initcode(ADI_BOOT_DATA *bs)  #ifdef CONFIG_BFIN_BOOTROM_USES_EVT1  	serial_putc('I'); -	/* tell the bootrom where our entry point is */ +	/* 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 diff --git a/arch/blackfin/cpu/interrupt.S b/arch/blackfin/cpu/interrupt.S index 69bba3f5e..0e5e59e15 100644 --- a/arch/blackfin/cpu/interrupt.S +++ b/arch/blackfin/cpu/interrupt.S @@ -150,3 +150,8 @@ ENTRY(_evt_default)  	RESTORE_ALL_SYS  	rti;  ENDPROC(_evt_default) + +/* NMI handler */ +ENTRY(_evt_nmi) +	rtn; +ENDPROC(_evt_nmi) diff --git a/arch/blackfin/cpu/serial.h b/arch/blackfin/cpu/serial.h index 5f9be8622..f9e311f3e 100644 --- a/arch/blackfin/cpu/serial.h +++ b/arch/blackfin/cpu/serial.h @@ -26,6 +26,8 @@  #ifndef __ASSEMBLY__ +#include <asm/portmux.h> +  #define LOB(x) ((x) & 0xFF)  #define HIB(x) (((x) >> 8) & 0xFF) @@ -103,6 +105,23 @@ struct bfin_mmr_serial {  __attribute__((always_inline))  static inline void serial_do_portmux(void)  { +	if (!BFIN_DEBUG_EARLY_SERIAL) { +		const unsigned short pins[] = { +#if CONFIG_UART_CONSOLE == 0 +			P_UART0_TX, P_UART0_RX, +#elif CONFIG_UART_CONSOLE == 1 +			P_UART1_TX, P_UART1_RX, +#elif CONFIG_UART_CONSOLE == 2 +			P_UART2_TX, P_UART2_RX, +#elif CONFIG_UART_CONSOLE == 3 +			P_UART3_TX, P_UART3_RX, +#endif +			0, +		}; +		peripheral_request_list(pins, "bfin-uart"); +		return; +	} +  #if defined(__ADSPBF51x__)  # define DO_MUX(port, mux_tx, mux_rx, tx, rx) \  	bfin_write_PORT##port##_MUX((bfin_read_PORT##port##_MUX() & ~(PORT_x_MUX_##mux_tx##_MASK | PORT_x_MUX_##mux_rx##_MASK)) | PORT_x_MUX_##mux_tx##_FUNC_2 | PORT_x_MUX_##mux_rx##_FUNC_2); \ diff --git a/arch/blackfin/cpu/traps.c b/arch/blackfin/cpu/traps.c index caaea9410..09388aa3d 100644 --- a/arch/blackfin/cpu/traps.c +++ b/arch/blackfin/cpu/traps.c @@ -29,14 +29,26 @@  #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) \ -	bfin_write_TBUFCTL((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 @@ -82,8 +94,16 @@ 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: @@ -97,7 +117,7 @@ int trap_c(struct pt_regs *regs, uint32_t level)  			 */  			if (last_cplb_fault_retx != regs->retx) {  				last_cplb_fault_retx = regs->retx; -				return ret; +				break;  			}  		} @@ -110,7 +130,6 @@ int trap_c(struct pt_regs *regs, uint32_t level)  		uint32_t new_cplb_addr = 0, new_cplb_data = 0;  		static size_t last_evicted;  		size_t i; -		unsigned long tflags;  #ifdef CONFIG_EXCEPTION_DEFER  		/* This should never happen */ @@ -118,13 +137,6 @@ int trap_c(struct pt_regs *regs, uint32_t level)  			bfin_panic(regs);  #endif -		/* -		 * 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); -  		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) { @@ -180,7 +192,6 @@ int trap_c(struct pt_regs *regs, uint32_t level)  		for (i = 0; i < 16; ++i)  			debug("%2i 0x%p 0x%08X\n", i, *CPLB_ADDR++, *CPLB_DATA++); -		trace_buffer_restore(tflags);  		break;  	}  #ifdef CONFIG_CMD_KGDB @@ -208,23 +219,21 @@ int trap_c(struct pt_regs *regs, uint32_t level)  #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)) -			return 0; +			break;  #endif  		bfin_panic(regs);  	} + +	trace_buffer_restore(tflags); +  	return ret;  } -#ifdef CONFIG_DEBUG_DUMP -# define ENABLE_DUMP 1 -#else -# define ENABLE_DUMP 0 -#endif -  #ifndef CONFIG_KALLSYMS  const char *symbol_lookup(unsigned long addr, unsigned long *caddr)  { @@ -364,17 +373,14 @@ void dump(struct pt_regs *fp)  	printf("\n");  } -void dump_bfin_trace_buffer(void) +static void _dump_bfin_trace_buffer(void)  {  	char buf[150]; -	unsigned long tflags;  	int i = 0;  	if (!ENABLE_DUMP)  		return; -	trace_buffer_save(tflags); -  	printf("Hardware Trace:\n");  	if (bfin_read_TBUFSTAT() & TBUFCNT) { @@ -385,16 +391,21 @@ void dump_bfin_trace_buffer(void)  			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_panic(struct pt_regs *regs) +void bfin_dump(struct pt_regs *regs)  { -	if (ENABLE_DUMP) { -		unsigned long tflags; -		trace_buffer_save(tflags); -	} +	unsigned long tflags; + +	trace_buffer_save(tflags);  	puts(  		"\n" @@ -404,7 +415,16 @@ void bfin_panic(struct pt_regs *regs)  		"\n"  	);  	dump(regs); -	dump_bfin_trace_buffer(); +	_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);  	bfin_reset_or_hang();  }  |