gen550_dbg.c 4.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
/*
 * A library of polled 16550 serial routines.  These are intended to
 * be used to support progress messages, xmon, kgdb, etc. on a
 * variety of platforms.
 *
 * Adapted from lots of code ripped from the arch/ppc/boot/ polled
 * 16550 support.
 *
 * Author: Matt Porter <mporter@mvista.com>
 *
 * 2002-2003 (c) MontaVista Software, Inc.  This file is licensed under
 * the terms of the GNU General Public License version 2.  This program
 * is licensed "as is" without any warranty of any kind, whether express
 * or implied.
 */

#include <linux/config.h>
#include <linux/types.h>
#include <linux/serial.h>
#include <linux/tty.h>		/* For linux/serial_core.h */
#include <linux/serial_core.h>
#include <linux/serialP.h>
#include <linux/serial_reg.h>
#include <asm/machdep.h>
#include <asm/serial.h>
#include <asm/io.h>

#define SERIAL_BAUD	9600

/* SERIAL_PORT_DFNS is defined in <asm/serial.h> */
#ifndef SERIAL_PORT_DFNS
#define SERIAL_PORT_DFNS
#endif

static struct serial_state rs_table[RS_TABLE_SIZE] = {
	SERIAL_PORT_DFNS	/* defined in <asm/serial.h> */
};

static void (*serial_outb)(unsigned long, unsigned char);
static unsigned long (*serial_inb)(unsigned long);

static int shift;

unsigned long direct_inb(unsigned long addr)
{
	return readb((void __iomem *)addr);
}

void direct_outb(unsigned long addr, unsigned char val)
{
	writeb(val, (void __iomem *)addr);
}

unsigned long io_inb(unsigned long port)
{
	return inb(port);
}

void io_outb(unsigned long port, unsigned char val)
{
	outb(val, port);
}

unsigned long serial_init(int chan, void *ignored)
{
	unsigned long com_port;
	unsigned char lcr, dlm;

	/* We need to find out which type io we're expecting.  If it's
	 * 'SERIAL_IO_PORT', we get an offset from the isa_io_base.
	 * If it's 'SERIAL_IO_MEM', we can the exact location.  -- Tom */
	switch (rs_table[chan].io_type) {
		case SERIAL_IO_PORT:
			com_port = rs_table[chan].port;
			serial_outb = io_outb;
			serial_inb = io_inb;
			break;
		case SERIAL_IO_MEM:
			com_port = (unsigned long)rs_table[chan].iomem_base;
			serial_outb = direct_outb;
			serial_inb = direct_inb;
			break;
		default:
			/* We can't deal with it. */
			return -1;
	}

	/* How far apart the registers are. */
	shift = rs_table[chan].iomem_reg_shift;

	/* save the LCR */
	lcr = serial_inb(com_port + (UART_LCR << shift));
	
	/* Access baud rate */
	serial_outb(com_port + (UART_LCR << shift), UART_LCR_DLAB);
	dlm = serial_inb(com_port + (UART_DLM << shift));

	/*
	 * Test if serial port is unconfigured
	 * We assume that no-one uses less than 110 baud or
	 * less than 7 bits per character these days.
	 *  -- paulus.
	 */
	if ((dlm <= 4) && (lcr & 2)) {
		/* port is configured, put the old LCR back */
		serial_outb(com_port + (UART_LCR << shift), lcr);
	}
	else {
		/* Input clock. */
		serial_outb(com_port + (UART_DLL << shift),
			(rs_table[chan].baud_base / SERIAL_BAUD) & 0xFF);
		serial_outb(com_port + (UART_DLM << shift),
				(rs_table[chan].baud_base / SERIAL_BAUD) >> 8);
		/* 8 data, 1 stop, no parity */
		serial_outb(com_port + (UART_LCR << shift), 0x03);
		/* RTS/DTR */
		serial_outb(com_port + (UART_MCR << shift), 0x03);

		/* Clear & enable FIFOs */
		serial_outb(com_port + (UART_FCR << shift), 0x07);
	}

	return (com_port);
}

void
serial_putc(unsigned long com_port, unsigned char c)
{
	while ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_THRE) == 0)
		;
	serial_outb(com_port, c);
}

unsigned char
serial_getc(unsigned long com_port)
{
	while ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) == 0)
		;
	return serial_inb(com_port);
}

int
serial_tstc(unsigned long com_port)
{
	return ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) != 0);
}

void
serial_close(unsigned long com_port)
{
}

void
gen550_init(int i, struct uart_port *serial_req)
{
	rs_table[i].io_type = serial_req->iotype;
	rs_table[i].port = serial_req->iobase;
	rs_table[i].iomem_base = serial_req->membase;
	rs_table[i].iomem_reg_shift = serial_req->regshift;
	rs_table[i].baud_base = serial_req->uartclk ? serial_req->uartclk / 16 : BASE_BAUD;
}

#ifdef CONFIG_SERIAL_TEXT_DEBUG
void
gen550_progress(char *s, unsigned short hex)
{
	volatile unsigned int progress_debugport;
	volatile char c;

	progress_debugport = serial_init(0, NULL);

	serial_putc(progress_debugport, '\r');

	while ((c = *s++) != 0)
		serial_putc(progress_debugport, c);

	serial_putc(progress_debugport, '\n');
	serial_putc(progress_debugport, '\r');
}
#endif /* CONFIG_SERIAL_TEXT_DEBUG */