/*
 * serial.h - common serial defines for early debug and serial driver.
 *            any functions defined here must be always_inline since
 *            initcode cannot have function calls.
 *
 * Copyright (c) 2004-2011 Analog Devices Inc.
 *
 * Licensed under the GPL-2 or later.
 */

#ifndef __BFIN_CPU_SERIAL_H__
#define __BFIN_CPU_SERIAL_H__

#include <asm/blackfin.h>
#include <asm/portmux.h>

#ifndef CONFIG_UART_CONSOLE
# define CONFIG_UART_CONSOLE 0
#endif

#ifdef CONFIG_DEBUG_EARLY_SERIAL
# define BFIN_DEBUG_EARLY_SERIAL 1
#else
# define BFIN_DEBUG_EARLY_SERIAL 0
#endif

#if defined(__ADSPBF60x__)
# define BFIN_UART_HW_VER 4
#elif defined(__ADSPBF50x__) || defined(__ADSPBF54x__)
# define BFIN_UART_HW_VER 2
#else
# define BFIN_UART_HW_VER 1
#endif

#define __PASTE_UART(num, pfx, sfx) pfx##num##_##sfx
#define _PASTE_UART(num, pfx, sfx) __PASTE_UART(num, pfx, sfx)
#define _P_UART(n, pin) _PASTE_UART(n, P_UART, pin)
#define P_UART(pin) _P_UART(CONFIG_UART_CONSOLE, pin)

#define pUART ((volatile struct bfin_mmr_serial *)uart_base)

#ifndef __ASSEMBLY__
__attribute__((always_inline))
static inline void serial_do_portmux(void);
#endif

#if BFIN_UART_HW_VER < 4
# include "serial1.h"
#else
# include "serial4.h"
#endif

#ifndef __ASSEMBLY__

__attribute__((always_inline))
static inline void serial_do_portmux(void)
{
	if (!BFIN_DEBUG_EARLY_SERIAL) {
		const unsigned short pins[] = { P_UART(RX), P_UART(TX), 0, };
		peripheral_request_list(pins, "bfin-uart");
		return;
	}

	serial_early_do_portmux();
}

#ifndef BFIN_IN_INITCODE
__attribute__((always_inline))
static inline void serial_early_puts(const char *s)
{
	if (BFIN_DEBUG_EARLY_SERIAL) {
		serial_puts("Early: ");
		serial_puts(s);
	}
}
#endif

#else

.macro serial_early_init
#if defined(CONFIG_DEBUG_EARLY_SERIAL) && !defined(CONFIG_UART_MEM)
	call __serial_early_init;
#endif
.endm

.macro serial_early_set_baud
#if defined(CONFIG_DEBUG_EARLY_SERIAL) && !defined(CONFIG_UART_MEM)
	R0.L = LO(CONFIG_BAUDRATE);
	R0.H = HI(CONFIG_BAUDRATE);
	call __serial_early_set_baud;
#endif
.endm

#if CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_BYPASS
#define update_serial_early_string_addr \
	R1.L = _start; \
	R1.H = _start; \
	R0 = R0 - R1; \
	R1.L = 0; \
	R1.H = 0x2000; \
	R0 = R0 + R1;
#else
#define update_serial_early_string_addr
#endif

/* Since we embed the string right into our .text section, we need
 * to find its address.  We do this by getting our PC and adding 2
 * bytes (which is the length of the jump instruction).  Then we
 * pass this address to serial_puts().
 */
#ifdef CONFIG_DEBUG_EARLY_SERIAL
# define serial_early_puts(str) \
	.section .rodata; \
	7: \
	.ascii "Early:"; \
	.ascii __FILE__; \
	.ascii ": "; \
	.ascii str; \
	.asciz "\n"; \
	.previous; \
	R0.L = 7b; \
	R0.H = 7b; \
	update_serial_early_string_addr \
	call _uart_early_puts;
#else
# define serial_early_puts(str)
#endif

#endif

#endif