diff mbox

Support for GRLIB APBUART serial port

Message ID 1257352368-32377-1-git-send-email-kristoffer@gaisler.com
State Accepted
Delegated to: David Miller
Headers show

Commit Message

Kristoffer Glembo Nov. 4, 2009, 4:32 p.m. UTC
This patch adds support for the APBUART serial port from Aeroflex
Gaisler's IP library GRLIB. It is currently used in all LEON3 designs
(SPARC V8) but can be used on other platforms as well (which support OF).


Signed-off-by: Kristoffer Glembo <kristoffer@gaisler.com>

---
 drivers/serial/Kconfig      |   13 +
 drivers/serial/Makefile     |    1 +
 drivers/serial/apbuart.c    |  710 +++++++++++++++++++++++++++++++++++++++++++
 drivers/serial/apbuart.h    |   64 ++++
 include/linux/serial_core.h |    3 +
 5 files changed, 791 insertions(+), 0 deletions(-)
 create mode 100644 drivers/serial/apbuart.c
 create mode 100644 drivers/serial/apbuart.h

Comments

Kristoffer Glembo Nov. 5, 2009, 2:39 p.m. UTC | #1
David Miller wrote:
> From: Kristoffer Glembo <kristoffer@gaisler.com>
> Date: Wed,  4 Nov 2009 17:32:48 +0100
> 
>> This patch adds support for the APBUART serial port from Aeroflex
>> Gaisler's IP library GRLIB. It is currently used in all LEON3 designs
>> (SPARC V8) but can be used on other platforms as well (which support OF).
>>
>>
>> Signed-off-by: Kristoffer Glembo <kristoffer@gaisler.com>
> 
> Applied, but I had to make two fixes:
>

Thanks a lot. I will remember to test drivers as modules and on other
architectures in the future!

Best regards,
Kristoffer Glembo
--
To unsubscribe from this list: send the line "unsubscribe sparclinux" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index e522572..6c3a94e 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -1477,4 +1477,17 @@  config SERIAL_BCM63XX_CONSOLE
 	  If you have enabled the serial port on the bcm63xx CPU
 	  you can make it the console by answering Y to this option.
 
+config SERIAL_GRLIB_GAISLER_APBUART
+	tristate "GRLIB APBUART serial support"
+	depends on OF
+	---help---
+	Add support for the GRLIB APBUART serial port.
+
+config SERIAL_GRLIB_GAISLER_APBUART_CONSOLE
+	bool "Console on GRLIB APBUART serial port"
+	depends on SERIAL_GRLIB_GAISLER_APBUART
+	select SERIAL_CORE_CONSOLE
+	help
+	Support for running a console on the GRLIB APBUART
+
 endmenu
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index d21d5dd..5548fe7 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -81,3 +81,4 @@  obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
 obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
 obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
 obj-$(CONFIG_SERIAL_TIMBERDALE)	+= timbuart.o
+obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
diff --git a/drivers/serial/apbuart.c b/drivers/serial/apbuart.c
new file mode 100644
index 0000000..8a34304
--- /dev/null
+++ b/drivers/serial/apbuart.c
@@ -0,0 +1,710 @@ 
+/*
+ *  Driver for GRLIB serial ports (APBUART)
+ *
+ *  Based on linux/drivers/serial/amba.c
+ *
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *  Copyright (C) 2003 Konrad Eisele <eiselekd@web.de>
+ *  Copyright (C) 2006 Daniel Hellstrom <daniel@gaisler.com>, Aeroflex Gaisler AB
+ *  Copyright (C) 2008 Gilead Kutnick <kutnickg@zin-tech.com>
+ *  Copyright (C) 2009 Kristoffer Glembo <kristoffer@gaisler.com>, Aeroflex Gaisler AB
+ */
+
+#if defined(CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/kthread.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/serial_core.h>
+#include <asm/irq.h>
+#include <asm/oplib.h>
+
+#include "apbuart.h"
+
+#define SERIAL_APBUART_MAJOR	TTY_MAJOR
+#define SERIAL_APBUART_MINOR	64
+#define UART_DUMMY_RSR_RX	0x8000	/* for ignore all read */
+
+static void apbuart_tx_chars(struct uart_port *port);
+
+static void apbuart_stop_tx(struct uart_port *port)
+{
+	unsigned int cr;
+
+	cr = UART_GET_CTRL(port);
+	cr &= ~UART_CTRL_TI;
+	UART_PUT_CTRL(port, cr);
+}
+
+static void apbuart_start_tx(struct uart_port *port)
+{
+	unsigned int cr;
+
+	cr = UART_GET_CTRL(port);
+	cr |= UART_CTRL_TI;
+	UART_PUT_CTRL(port, cr);
+
+	if (UART_GET_STATUS(port) & UART_STATUS_THE)
+		apbuart_tx_chars(port);
+}
+
+static void apbuart_stop_rx(struct uart_port *port)
+{
+	unsigned int cr;
+
+	cr = UART_GET_CTRL(port);
+	cr &= ~(UART_CTRL_RI);
+	UART_PUT_CTRL(port, cr);
+}
+
+static void apbuart_enable_ms(struct uart_port *port)
+{
+	/* No modem status change interrupts for APBUART */
+}
+
+static void apbuart_rx_chars(struct uart_port *port)
+{
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned int status, ch, rsr, flag;
+	unsigned int max_chars = port->fifosize;
+
+	status = UART_GET_STATUS(port);
+
+	while (UART_RX_DATA(status) && (max_chars--)) {
+
+		ch = UART_GET_CHAR(port);
+		flag = TTY_NORMAL;
+
+		port->icount.rx++;
+
+		rsr = UART_GET_STATUS(port) | UART_DUMMY_RSR_RX;
+		UART_PUT_STATUS(port, 0);
+		if (rsr & UART_STATUS_ERR) {
+
+			if (rsr & UART_STATUS_BR) {
+				rsr &= ~(UART_STATUS_FE | UART_STATUS_PE);
+				port->icount.brk++;
+				if (uart_handle_break(port))
+					goto ignore_char;
+			} else if (rsr & UART_STATUS_PE) {
+				port->icount.parity++;
+			} else if (rsr & UART_STATUS_FE) {
+				port->icount.frame++;
+			}
+			if (rsr & UART_STATUS_OE)
+				port->icount.overrun++;
+
+			rsr &= port->read_status_mask;
+
+			if (rsr & UART_STATUS_PE)
+				flag = TTY_PARITY;
+			else if (rsr & UART_STATUS_FE)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(port, ch))
+			goto ignore_char;
+
+		uart_insert_char(port, rsr, UART_STATUS_OE, ch, flag);
+
+
+	      ignore_char:
+		status = UART_GET_STATUS(port);
+	}
+
+	tty_flip_buffer_push(tty);
+}
+
+static void apbuart_tx_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+	int count;
+
+	if (port->x_char) {
+		UART_PUT_CHAR(port, port->x_char);
+		port->icount.tx++;
+		port->x_char = 0;
+		return;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		apbuart_stop_tx(port);
+		return;
+	}
+
+	/* amba: fill FIFO */
+	count = port->fifosize >> 1;
+	do {
+		UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		apbuart_stop_tx(port);
+}
+
+static irqreturn_t apbuart_int(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	unsigned int status;
+
+	spin_lock(&port->lock);
+
+	status = UART_GET_STATUS(port);
+	if (status & UART_STATUS_DR)
+		apbuart_rx_chars(port);
+	if (status & UART_STATUS_THE)
+		apbuart_tx_chars(port);
+
+	spin_unlock(&port->lock);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int apbuart_tx_empty(struct uart_port *port)
+{
+	unsigned int status = UART_GET_STATUS(port);
+	return status & UART_STATUS_THE ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int apbuart_get_mctrl(struct uart_port *port)
+{
+	/* The GRLIB APBUART handles flow control in hardware */
+	return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+static void apbuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* The GRLIB APBUART handles flow control in hardware */
+}
+
+static void apbuart_break_ctl(struct uart_port *port, int break_state)
+{
+	/* We don't support sending break */
+}
+
+static int apbuart_startup(struct uart_port *port)
+{
+	int retval;
+	unsigned int cr;
+
+	/* Allocate the IRQ */
+	retval = request_irq(port->irq, apbuart_int, 0, "apbuart", port);
+	if (retval)
+		return retval;
+
+	/* Finally, enable interrupts */
+	cr = UART_GET_CTRL(port);
+	UART_PUT_CTRL(port,
+		      cr | UART_CTRL_RE | UART_CTRL_TE |
+		      UART_CTRL_RI | UART_CTRL_TI);
+
+	return 0;
+}
+
+static void apbuart_shutdown(struct uart_port *port)
+{
+	unsigned int cr;
+
+	/* disable all interrupts, disable the port */
+	cr = UART_GET_CTRL(port);
+	UART_PUT_CTRL(port,
+		      cr & ~(UART_CTRL_RE | UART_CTRL_TE |
+			     UART_CTRL_RI | UART_CTRL_TI));
+
+	/* Free the interrupt */
+	free_irq(port->irq, port);
+}
+
+static void apbuart_set_termios(struct uart_port *port,
+				struct ktermios *termios, struct ktermios *old)
+{
+	unsigned int cr;
+	unsigned long flags;
+	unsigned int baud, quot;
+
+	/* Ask the core to calculate the divisor for us. */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+	if (baud == 0)
+		panic("invalid baudrate %i\n", port->uartclk / 16);
+
+	/* uart_get_divisor calc a *16 uart freq, apbuart is *8 */
+	quot = (uart_get_divisor(port, baud)) * 2;
+	cr = UART_GET_CTRL(port);
+	cr &= ~(UART_CTRL_PE | UART_CTRL_PS);
+
+	if (termios->c_cflag & PARENB) {
+		cr |= UART_CTRL_PE;
+		if ((termios->c_cflag & PARODD))
+			cr |= UART_CTRL_PS;
+	}
+
+	/* Enable flow control. */
+	if (termios->c_cflag & CRTSCTS)
+		cr |= UART_CTRL_FL;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Update the per-port timeout. */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	port->read_status_mask = UART_STATUS_OE;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= UART_STATUS_FE | UART_STATUS_PE;
+
+	/* Characters to ignore */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= UART_STATUS_FE | UART_STATUS_PE;
+
+	/* Ignore all characters if CREAD is not set. */
+	if ((termios->c_cflag & CREAD) == 0)
+		port->ignore_status_mask |= UART_DUMMY_RSR_RX;
+
+	/* Set baud rate */
+	quot -= 1;
+	UART_PUT_SCAL(port, quot);
+	UART_PUT_CTRL(port, cr);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *apbuart_type(struct uart_port *port)
+{
+	return port->type == PORT_APBUART ? "GRLIB/APBUART" : NULL;
+}
+
+static void apbuart_release_port(struct uart_port *port)
+{
+	release_mem_region(port->mapbase, 0x100);
+}
+
+static int apbuart_request_port(struct uart_port *port)
+{
+	return request_mem_region(port->mapbase, 0x100, "grlib-apbuart")
+	    != NULL ? 0 : -EBUSY;
+	return 0;
+}
+
+/* Configure/autoconfigure the port */
+static void apbuart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_APBUART;
+		apbuart_request_port(port);
+	}
+}
+
+/* Verify the new serial_struct (for TIOCSSERIAL) */
+static int apbuart_verify_port(struct uart_port *port,
+			       struct serial_struct *ser)
+{
+	int ret = 0;
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_APBUART)
+		ret = -EINVAL;
+	if (ser->irq < 0 || ser->irq >= NR_IRQS)
+		ret = -EINVAL;
+	if (ser->baud_base < 9600)
+		ret = -EINVAL;
+	return ret;
+}
+
+static struct uart_ops grlib_apbuart_ops = {
+	.tx_empty = apbuart_tx_empty,
+	.set_mctrl = apbuart_set_mctrl,
+	.get_mctrl = apbuart_get_mctrl,
+	.stop_tx = apbuart_stop_tx,
+	.start_tx = apbuart_start_tx,
+	.stop_rx = apbuart_stop_rx,
+	.enable_ms = apbuart_enable_ms,
+	.break_ctl = apbuart_break_ctl,
+	.startup = apbuart_startup,
+	.shutdown = apbuart_shutdown,
+	.set_termios = apbuart_set_termios,
+	.type = apbuart_type,
+	.release_port = apbuart_release_port,
+	.request_port = apbuart_request_port,
+	.config_port = apbuart_config_port,
+	.verify_port = apbuart_verify_port,
+};
+
+static struct uart_port grlib_apbuart_ports[UART_NR];
+static struct device_node *grlib_apbuart_nodes[UART_NR];
+
+static int apbuart_scan_fifo_size(struct uart_port *port, int portnumber)
+{
+	int ctrl, loop = 0;
+	int status;
+	int fifosize;
+	unsigned long flags;
+
+	ctrl = UART_GET_CTRL(port);
+
+	/*
+	 * Enable the transceiver and wait for it to be ready to send data.
+	 * Clear interrupts so that this process will not be externally
+	 * interrupted in the middle (which can cause the transceiver to
+	 * drain prematurely).
+	 */
+
+	local_irq_save(flags);
+
+	UART_PUT_CTRL(port, ctrl | UART_CTRL_TE);
+
+	while (!UART_TX_READY(UART_GET_STATUS(port)))
+		loop++;
+
+	/*
+	 * Disable the transceiver so data isn't actually sent during the
+	 * actual test.
+	 */
+
+	UART_PUT_CTRL(port, ctrl & ~(UART_CTRL_TE));
+
+	fifosize = 1;
+	UART_PUT_CHAR(port, 0);
+
+	/*
+	 * So long as transmitting a character increments the tranceivier FIFO
+	 * length the FIFO must be at least that big. These bytes will
+	 * automatically drain off of the FIFO.
+	 */
+
+	status = UART_GET_STATUS(port);
+	while (((status >> 20) & 0x3F) == fifosize) {
+		fifosize++;
+		UART_PUT_CHAR(port, 0);
+		status = UART_GET_STATUS(port);
+	}
+
+	fifosize--;
+
+	UART_PUT_CTRL(port, ctrl);
+	local_irq_restore(flags);
+
+	if (fifosize == 0)
+		fifosize = 1;
+
+	return fifosize;
+}
+
+static void apbuart_flush_fifo(struct uart_port *port)
+{
+	int i;
+
+	for (i = 0; i < port->fifosize; i++)
+		UART_GET_CHAR(port);
+}
+
+
+/* ======================================================================== */
+/* Console driver, if enabled                                               */
+/* ======================================================================== */
+
+#ifdef CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE
+
+static void apbuart_console_putchar(struct uart_port *port, int ch)
+{
+	unsigned int status;
+	do {
+		status = UART_GET_STATUS(port);
+	} while (!UART_TX_READY(status));
+	UART_PUT_CHAR(port, ch);
+}
+
+static void
+apbuart_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_port *port = &grlib_apbuart_ports[co->index];
+	unsigned int status, old_cr, new_cr;
+
+	/* First save the CR then disable the interrupts */
+	old_cr = UART_GET_CTRL(port);
+	new_cr = old_cr & ~(UART_CTRL_RI | UART_CTRL_TI);
+	UART_PUT_CTRL(port, new_cr);
+
+	uart_console_write(port, s, count, apbuart_console_putchar);
+
+	/*
+	 *      Finally, wait for transmitter to become empty
+	 *      and restore the TCR
+	 */
+	do {
+		status = UART_GET_STATUS(port);
+	} while (!UART_TX_READY(status));
+	UART_PUT_CTRL(port, old_cr);
+}
+
+static void __init
+apbuart_console_get_options(struct uart_port *port, int *baud,
+			    int *parity, int *bits)
+{
+	if (UART_GET_CTRL(port) & (UART_CTRL_RE | UART_CTRL_TE)) {
+
+		unsigned int quot, status;
+		status = UART_GET_STATUS(port);
+
+		*parity = 'n';
+		if (status & UART_CTRL_PE) {
+			if ((status & UART_CTRL_PS) == 0)
+				*parity = 'e';
+			else
+				*parity = 'o';
+		}
+
+		*bits = 8;
+		quot = UART_GET_SCAL(port) / 8;
+		*baud = port->uartclk / (16 * (quot + 1));
+	}
+}
+
+static int __init apbuart_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 38400;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	pr_debug("apbuart_console_setup co=%p, co->index=%i, options=%s\n",
+		 co, co->index, options);
+
+	/*
+	 * Check whether an invalid uart number has been specified, and
+	 * if so, search for the first available port that does have
+	 * console support.
+	 */
+	if (co->index >= grlib_apbuart_port_nr)
+		co->index = 0;
+
+	port = &grlib_apbuart_ports[co->index];
+
+	spin_lock_init(&port->lock);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		apbuart_console_get_options(port, &baud, &parity, &bits);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver grlib_apbuart_driver;
+
+static struct console grlib_apbuart_console = {
+	.name = "ttyS",
+	.write = apbuart_console_write,
+	.device = uart_console_device,
+	.setup = apbuart_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &grlib_apbuart_driver,
+};
+
+
+static void grlib_apbuart_configure(void);
+
+static int __init apbuart_console_init(void)
+{
+	grlib_apbuart_configure();
+	register_console(&grlib_apbuart_console);
+	return 0;
+}
+
+console_initcall(apbuart_console_init);
+
+#define APBUART_CONSOLE	(&grlib_apbuart_console)
+#else
+#define APBUART_CONSOLE	NULL
+#endif
+
+static struct uart_driver grlib_apbuart_driver = {
+	.owner = THIS_MODULE,
+	.driver_name = "serial",
+	.dev_name = "ttyS",
+	.major = SERIAL_APBUART_MAJOR,
+	.minor = SERIAL_APBUART_MINOR,
+	.nr = UART_NR,
+	.cons = APBUART_CONSOLE,
+};
+
+
+/* ======================================================================== */
+/* OF Platform Driver                                                       */
+/* ======================================================================== */
+
+static int __devinit apbuart_probe(struct of_device *op,
+				   const struct of_device_id *match)
+{
+	int i = -1;
+	struct uart_port *port = NULL;
+
+	i = 0;
+	for (i = 0; i < grlib_apbuart_port_nr; i++) {
+		if (op->node == grlib_apbuart_nodes[i])
+			break;
+	}
+
+	port = &grlib_apbuart_ports[i];
+	port->dev = &op->dev;
+
+	uart_add_one_port(&grlib_apbuart_driver, (struct uart_port *) port);
+
+	apbuart_flush_fifo((struct uart_port *) port);
+
+	printk(KERN_INFO "grlib-apbuart at 0x%x, irq %d\n",
+	       port->mapbase, port->irq);
+	return 0;
+
+}
+
+static struct of_device_id __initdata apbuart_match[] = {
+	{
+	 .name = "GAISLER_APBUART",
+	 },
+	{},
+};
+
+static struct of_platform_driver grlib_apbuart_of_driver = {
+	.match_table = apbuart_match,
+	.probe = apbuart_probe,
+	.driver = {
+		   .owner = THIS_MODULE,
+		   .name = "grlib-apbuart",
+		   },
+};
+
+
+static void grlib_apbuart_configure(void)
+{
+	static int enum_done;
+	struct device_node *np;
+	struct uart_port *port = NULL;
+
+	int node;
+	int freq_khz;
+	int v = 0, d = 0;
+	unsigned int addr;
+	int irq, line;
+	struct amba_prom_registers *regs;
+
+	if (enum_done)
+		return;
+
+	/* Get bus frequency */
+	node = prom_getchild(prom_root_node);
+	freq_khz = prom_getint(node, "clock-frequency");
+
+	line = 0;
+	for_each_matching_node(np, apbuart_match) {
+
+		int *vendor = (int *) of_get_property(np, "vendor", NULL);
+		int *device = (int *) of_get_property(np, "device", NULL);
+		int *irqs = (int *) of_get_property(np, "interrupts", NULL);
+		regs = (struct amba_prom_registers *)
+		    of_get_property(np, "reg", NULL);
+
+		if (vendor)
+			v = *vendor;
+		if (device)
+			d = *device;
+
+		if (!irqs || !regs)
+			return;
+
+		grlib_apbuart_nodes[line] = np;
+
+		addr = regs->phys_addr;
+		irq = *irqs;
+
+		port = &grlib_apbuart_ports[line];
+
+		port->mapbase = addr;
+		port->membase = ioremap(addr, sizeof(struct grlib_apbuart_regs_map));
+		port->irq = irq;
+		port->iotype = UPIO_MEM;
+		port->ops = &grlib_apbuart_ops;
+		port->flags = UPF_BOOT_AUTOCONF;
+		port->line = line;
+		port->uartclk = freq_khz * 1000;
+		port->fifosize = apbuart_scan_fifo_size((struct uart_port *) port, line);
+		line++;
+
+		/* We support maximum UART_NR uarts ... */
+		if (line == UART_NR)
+			break;
+
+	}
+
+	enum_done = 1;
+
+	grlib_apbuart_driver.nr = grlib_apbuart_port_nr = line;
+}
+
+static int __init grlib_apbuart_init(void)
+{
+	int ret;
+
+	/* Find all APBUARTS in device the tree and initialize their ports */
+	grlib_apbuart_configure();
+
+	printk(KERN_INFO "Serial: GRLIB APBUART driver\n");
+
+	ret = uart_register_driver(&grlib_apbuart_driver);
+
+	if (ret) {
+		printk(KERN_ERR "%s: uart_register_driver failed (%i)\n",
+		       __FILE__, ret);
+		return ret;
+	}
+
+	ret = of_register_driver(&grlib_apbuart_of_driver, &of_platform_bus_type);
+
+	if (ret) {
+		printk(KERN_ERR
+		       "%s: of_register_platform_driver failed (%i)\n",
+		       __FILE__, ret);
+		uart_unregister_driver(&grlib_apbuart_driver);
+		return ret;
+	}
+
+	return ret;
+}
+
+static void __exit grlib_apbuart_exit(void)
+{
+	int i;
+
+	for (i = 0; i < grlib_apbuart_port_nr; i++)
+		uart_remove_one_port(&grlib_apbuart_driver,
+				     &grlib_apbuart_ports[i]);
+
+	uart_unregister_driver(&grlib_apbuart_driver);
+
+}
+
+module_init(grlib_apbuart_init);
+module_exit(grlib_apbuart_exit);
+
+MODULE_AUTHOR("Aeroflex Gaisler AB");
+MODULE_DESCRIPTION("GRLIB APBUART serial driver");
+MODULE_VERSION("2.1");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/apbuart.h b/drivers/serial/apbuart.h
new file mode 100644
index 0000000..5faf87c
--- /dev/null
+++ b/drivers/serial/apbuart.h
@@ -0,0 +1,64 @@ 
+#ifndef __GRLIB_APBUART_H__
+#define __GRLIB_APBUART_H__
+
+#include <asm/io.h>
+
+#define UART_NR		8
+static int grlib_apbuart_port_nr;
+
+struct grlib_apbuart_regs_map {
+	u32 data;
+	u32 status;
+	u32 ctrl;
+	u32 scaler;
+};
+
+struct amba_prom_registers {
+	unsigned int phys_addr;
+	unsigned int reg_size;
+};
+
+/*
+ *  The following defines the bits in the APBUART Status Registers.
+ */
+#define UART_STATUS_DR   0x00000001	/* Data Ready */
+#define UART_STATUS_TSE  0x00000002	/* TX Send Register Empty */
+#define UART_STATUS_THE  0x00000004	/* TX Hold Register Empty */
+#define UART_STATUS_BR   0x00000008	/* Break Error */
+#define UART_STATUS_OE   0x00000010	/* RX Overrun Error */
+#define UART_STATUS_PE   0x00000020	/* RX Parity Error */
+#define UART_STATUS_FE   0x00000040	/* RX Framing Error */
+#define UART_STATUS_ERR  0x00000078	/* Error Mask */
+
+/*
+ *  The following defines the bits in the APBUART Ctrl Registers.
+ */
+#define UART_CTRL_RE     0x00000001	/* Receiver enable */
+#define UART_CTRL_TE     0x00000002	/* Transmitter enable */
+#define UART_CTRL_RI     0x00000004	/* Receiver interrupt enable */
+#define UART_CTRL_TI     0x00000008	/* Transmitter irq */
+#define UART_CTRL_PS     0x00000010	/* Parity select */
+#define UART_CTRL_PE     0x00000020	/* Parity enable */
+#define UART_CTRL_FL     0x00000040	/* Flow control enable */
+#define UART_CTRL_LB     0x00000080	/* Loopback enable */
+
+#define APBBASE(port) ((struct grlib_apbuart_regs_map *)((port)->membase))
+
+#define APBBASE_DATA_P(port)	(&(APBBASE(port)->data))
+#define APBBASE_STATUS_P(port)	(&(APBBASE(port)->status))
+#define APBBASE_CTRL_P(port)	(&(APBBASE(port)->ctrl))
+#define APBBASE_SCALAR_P(port)	(&(APBBASE(port)->scaler))
+
+#define UART_GET_CHAR(port)	(__raw_readl(APBBASE_DATA_P(port)))
+#define UART_PUT_CHAR(port, v)	(__raw_writel(v, APBBASE_DATA_P(port)))
+#define UART_GET_STATUS(port)	(__raw_readl(APBBASE_STATUS_P(port)))
+#define UART_PUT_STATUS(port, v)(__raw_writel(v, APBBASE_STATUS_P(port)))
+#define UART_GET_CTRL(port)	(__raw_readl(APBBASE_CTRL_P(port)))
+#define UART_PUT_CTRL(port, v)	(__raw_writel(v, APBBASE_CTRL_P(port)))
+#define UART_GET_SCAL(port)	(__raw_readl(APBBASE_SCALAR_P(port)))
+#define UART_PUT_SCAL(port, v)	(__raw_writel(v, APBBASE_SCALAR_P(port)))
+
+#define UART_RX_DATA(s)		(((s) & UART_STATUS_DR) != 0)
+#define UART_TX_READY(s)	(((s) & UART_STATUS_THE) != 0)
+
+#endif /* __GRLIB_APBUART_H__ */
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index db532ce..8c3dd36 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -179,6 +179,9 @@ 
 /* BCM63xx family SoCs */
 #define PORT_BCM63XX	89
 
+/* Aeroflex Gaisler GRLIB APBUART */
+#define PORT_APBUART    90
+
 #ifdef __KERNEL__
 
 #include <linux/compiler.h>