/*
 * Early console drivers.
 * (C) Nov 2001, William Irwin, IBM
 *
 * These are low-level pseudodrivers to enable early console output
 * to aid in debugging during early boot.
 *
 * They are crude, but hopefully effective. They rely on the fact
 * that consoles are largely unused prior to the true console_init(),
 * and that printk() uses the ->write callback and that callback
 * only during its operation.
 *
 * Serial port routines are derived from Linux serial.c, and
 * vga_putc() is derived from vsta, (C) Andrew Valencia.
 */

#include <linux/kernel.h>
#include <linux/console.h>
#include <linux/serial_reg.h>
#include <asm/io.h>

/*
 * I/O ports are not linearly mapped on all architectures.
 * On IA64 in particular, port I/O is just reading/writing from
 * an uncached address, but ioremap there requires ia64_io_base
 * to be initialized, which does not happen until the middle of
 * setup_arch(). So a port remapping macro is provided here.
 *
 * The IA64 case is not handled here, although the port remapping
 * is demonstrated for the purposes of understanding its necessity.
 * The IO_BASE is taken from Lion systems; in general, this varies.
 * True handling for IA64 will be merged in given testing.
 */

#ifdef CONFIG_IA64

#define IO_BASE   0xC0000FFFFC000000UL
#define MK_PORT(port) ((char *)(IO_BASE|(((port)>>2)<<12)|((port) & 0xFFF)))

#else

/*
 * This works for i386, but not everywhere.
 * Other architectures with port I/O mapping needs will need to
 * add to the preprocessor case analysis above.
 */

#define MK_PORT(port) (port)

#endif

#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)


/*
 * This serial output driver derived from the one appearing
 * in serial.c
 *
 * It is a simple "bitbanging" style output routine, with
 * initialization performed at every call.
 */

#if defined(CONFIG_EARLY_CONSOLE_3F8) || defined(CONFIG_EARLY_CONSOLE_3E8)

static inline __init void wait_for_readiness(unsigned port)
{
	unsigned retries;
	unsigned char status;

	/*
	 * Wait for transmitter holding and shift registers to empty,
	 * which is required for output to succeed. If the retries are
	 * exceeded, this deliberately fails to ensure termination.
	 */
	for(retries = 0; retries < 65536; ++retries) {
		status = inb(MK_PORT(port + 5));
		if((status & BOTH_EMPTY) == BOTH_EMPTY)
			break;
	}
}

static void __init write_serial_io_port(unsigned port,
					const char *s,
					unsigned n)
{
	unsigned k;

	wait_for_readiness(port);

	/*
	 * Disable interrupts.
	 */
	outb(0x0, MK_PORT(port + 1));

	/*
	 * Set the baud rate divisor's LSB.
	 */
	outb(0x83, MK_PORT(port + 3));

	/*
	 * Set the baud rate divisor's MSB.
	 */
	outb(0xC, MK_PORT(port));

	/*
	 * Set no parity, 8 bits, 1 stop bit, and select
	 * interrupt enable register.
	 */
	outb(0x3, MK_PORT(port + 3));

	/*
	 * Set data terminal ready and request to send.
	 */

	for(k = 0; k < n; ++k) {
		wait_for_readiness(port);
		outb(s[k], MK_PORT(port));
		if(s[k] == '\n') {
			wait_for_readiness(port);
			outb('\r', MK_PORT(port));
		}
	}
}

#endif /* CONFIG_EARLY_CONSOLE_3F8  || CONFIG_EARLY_CONSOLE_3E8 */



/*
 * On Intel-derived architectures it is customary for onboard serial
 * ports to have I/O ports at these two port addresses.
 */

#ifdef CONFIG_EARLY_CONSOLE_3F8
static void __init write_3F8(struct console *c, const char *s, unsigned n)
{
	write_serial_io_port(0x3F8, s, n);
}

static struct console __initdata early_console_3F8 =
{
	write: write_3F8
};
#endif

#ifdef CONFIG_EARLY_CONSOLE_3E8
static void __init write_3E8(struct console *c, const char *s, unsigned n)
{
	write_serial_io_port(0x3E8, s, n);
}

static struct console __initdata early_console_3E8 =
{
	write: write_3E8
};
#endif




/*
 * This should work for a variety of Intel-derived architectures,
 * as it is customary for VGA memory to reside in this address range.
 * vga_putc() is derived from vsta sources, (C) Andrew Valencia.
 *
 * Several forms of functionality are intentionally omitted in the
 * interest of robustness, in particular, cursor movement and cursor
 * position determination.
 */

#ifdef CONFIG_EARLY_CONSOLE_VGA

#define VGA_MAXCOL 80
#define VGA_MAXROW 25
#define VGA_SCRNSZ (VGA_MAXCOL * VGA_MAXROW)
#define VGA_REG_PORT 0x3D4
#define VGA_VAL_PORT 0x3D5
#define VGA_TEXT_BUFFER 0xB8000

#define VGA_CHAR(_row_, _col_) vga_mem[(_row_)*VGA_MAXCOL + (_col_)].c

struct vga_char_desc
{
	unsigned char c;
	unsigned char color;
};

static struct vga_char_desc * __initdata vga_mem = 
	(struct vga_char_desc *)(VGA_TEXT_BUFFER + PAGE_OFFSET);

/*
 * The characters displayed at a screen position can be discerned by
 * reading from the corresponding memory location. This can be used
 * to simulate scrolling movement. Line blanking is simulated by
 * overwriting the displayed characters with the space character.
 *
 * In the interest of robustness, cursor movement is also omitted.
 */
static inline void __init vga_scroll_up(void)
{
	unsigned k;

	for(k = 0; k < (VGA_SCRNSZ - VGA_MAXCOL); ++k)
		vga_mem[k].c = vga_mem[k + VGA_MAXCOL].c;

	for(k = VGA_SCRNSZ - VGA_MAXCOL; k < VGA_SCRNSZ; ++k)
		vga_mem[k].c = ' ';
}

/*
 * The screen position can actually be determined by port I/O,
 * but in the interest of robustness, these are always initialized
 * to the (0, 0) position. These position indices must always be
 * strictly less than the bounds VGA_MAXROW and VGA_MAXCOL.
 */
static unsigned short __initdata row;
static unsigned short __initdata col;


/*
 * Line advancement must preserve the invariant that the row and
 * column indices are in-bounds. The semantics of this mean that
 * when line advancement "beyond" the last line results in scrolling.
 */
static inline void __init vga_line_advance(void)
{
	++row;

	if(row >= VGA_MAXROW) {
		row = VGA_MAXROW - 1;
		vga_scroll_up();
	}
}


/*
 * Character advancement must once again preserve the in-bounds
 * invariants, and in so doing line wrapping and advancement may occur.
 */
static inline void __init vga_char_advance(void)
{
	++col;

	if(col >= VGA_MAXCOL) {
		col = 0;
		vga_line_advance();
	}
}


/*
 * Derived from vsta sources (C) Andrew Valencia.
 * Here the interpretation of several common special characters occurs,
 * namely linefeeds, newlines, tabs, and backspaces. The position
 * indices are updated using the vga_char_advance() and vga_line_advance()
 * routines, and a vga_char_advance() is triggered on the printing of
 * each ordinary character. The special characters have specialized
 * position update semantics in order to be faithful to their customary
 * cursor movement effects, although the cursor position is not updated.
 */
static void __init vga_putc(char c)
{
	unsigned k;
	switch(c) {
		case '\t':
			for(k = 0; k < 8; ++k) {
				VGA_CHAR(row, col) = ' ';
				vga_char_advance();
			}
			break;

		case '\r':
			col = 0;
			break;

		case '\n':
			col = 0;
			vga_line_advance();
			break;

		case '\b':
			if(col > 0) {
				--col;
				VGA_CHAR(row, col) = ' ';
			}
			break;

		default:
			VGA_CHAR(row, col) = c;
			vga_char_advance();
			break;
	}
}


/*
 * write_vga(), given a NUL-terminated character array, writes
 * characters to VGA space in bulk, and is the callback used for the
 * driver structure.
 */
static void __init write_vga(struct console *c, const char *s, unsigned n)
{
	unsigned k;
	for(k = 0; k < n; ++k)
		vga_putc(s[k]);
}

static struct console __initdata early_console_vga =
{
	write: write_vga
};

#endif /* CONFIG_EARLY_CONSOLE_VGA */


/*
 * The bochs x86 simulator has an optional feature for enabling
 * debugging output through a normally unused ISA I/O port. The
 * protocol for communicating with the simulated device is simply
 * using port I/O writes to write a stream of characters to the
 * device, and these are then relayed by the simulator to the
 * controlling terminal of the simulator process.
 */
#ifdef CONFIG_EARLY_CONSOLE_BOCHS_E9_HACK
static void __init write_bochs(struct console *c, const char *s, unsigned n)
{
	unsigned k;

	for(k = 0; k < n; ++k)
		outb(s[k], MK_PORT(0xE9));
}

static struct console __initdata early_console_bochs =
{
	write: write_bochs
};
#endif /* CONFIG_EARLY_CONSOLE_BOCHS_E9_HACK */


/*
 * In order to minimize the number of #ifdefs whch must
 * appear in-line, this direct-mapped, NULL-terminated table
 * of console entries is used to provide a configuration-independent
 * structure which may be traversed to discover all of the available
 * early console devices for registration and unregistration.
 *
 * This is the ugliest part of the code, thanks to #ifdef
 */
static struct console * __initdata early_console_table[] =
	{
#ifdef CONFIG_EARLY_CONSOLE_3F8
		&early_console_3F8,
#endif
#ifdef CONFIG_EARLY_CONSOLE_3E8
		&early_console_3E8,
#endif
#ifdef CONFIG_EARLY_CONSOLE_VGA
		&early_console_vga,
#endif
#ifdef CONFIG_EARLY_CONSOLE_BOCHS_E9_HACK
		&early_console_bochs,
#endif
		NULL
	};


/*
 * The above implementations are quite far from complete console
 * devices, but printk() only requires the ->write callback, so this is
 * somewhat deceptive, but still cleaner than editing printk.c itself.
 */
void __init register_early_consoles(void)
{
	struct console **c = early_console_table;
	while(*c)
		register_console(*c++);
}

void __init unregister_early_consoles(void)
{
	struct console **c = early_console_table;
	while(*c)
		unregister_console(*c++);
}
