Index: kernel/arch/arm32/src/mach/integratorcp/integratorcp.c
===================================================================
--- kernel/arch/arm32/src/mach/integratorcp/integratorcp.c	(revision 0a4e1c77c74b5ead21db7cd9a738ffaacc357307)
+++ kernel/arch/arm32/src/mach/integratorcp/integratorcp.c	(revision 3ccba143f0e65b2bc3d74365b212d9d8c55bc2a8)
@@ -38,5 +38,7 @@
 #include <console/chardev.h>
 #include <genarch/drivers/pl050/pl050.h>
+#include <genarch/drivers/arm926_uart/arm926_uart.h>
 #include <genarch/kbrd/kbrd.h>
+#include <genarch/srln/srln.h>
 #include <console/console.h>
 #include <sysinfo/sysinfo.h>
@@ -53,7 +55,15 @@
 #include <print.h>
 
+
 #define SDRAM_SIZE	(sdram[((*(uint32_t *)(ICP_CMCR+ICP_SDRAMCR_OFFSET) & ICP_SDRAM_MASK) >> 2)])
-static icp_hw_map_t icp_hw_map;
-static irq_t icp_timer_irq;
+
+static struct {
+	icp_hw_map_t hw_map;
+	irq_t timer_irq;
+	arm926_uart_t uart;
+} icp;
+
+
+
 struct arm_machine_ops icp_machine_ops = {
 	icp_init,
@@ -88,12 +98,12 @@
 void icp_vga_init(void)
 {
-	*(uint32_t*)((char *)(icp_hw_map.cmcr)+0x14) = 0xA05F0000;
-	*(uint32_t*)((char *)(icp_hw_map.cmcr)+0x1C) = 0x12C11000;
-	*(uint32_t*)icp_hw_map.vga = 0x3F1F3F9C;
-	*(uint32_t*)((char *)(icp_hw_map.vga) + 0x4) = 0x080B61DF;
-	*(uint32_t*)((char *)(icp_hw_map.vga) + 0x8) = 0x067F3800;
-	*(uint32_t*)((char *)(icp_hw_map.vga) + 0x10) = ICP_FB;
-	*(uint32_t *)((char *)(icp_hw_map.vga) + 0x1C) = 0x182B;
-	*(uint32_t*)((char *)(icp_hw_map.cmcr)+0xC) = 0x33805000;
+	*(uint32_t*)((char *)(icp.hw_map.cmcr)+0x14) = 0xA05F0000;
+	*(uint32_t*)((char *)(icp.hw_map.cmcr)+0x1C) = 0x12C11000;
+	*(uint32_t*)icp.hw_map.vga = 0x3F1F3F9C;
+	*(uint32_t*)((char *)(icp.hw_map.vga) + 0x4) = 0x080B61DF;
+	*(uint32_t*)((char *)(icp.hw_map.vga) + 0x8) = 0x067F3800;
+	*(uint32_t*)((char *)(icp.hw_map.vga) + 0x10) = ICP_FB;
+	*(uint32_t *)((char *)(icp.hw_map.vga) + 0x1C) = 0x182B;
+	*(uint32_t*)((char *)(icp.hw_map.cmcr)+0xC) = 0x33805000;
 	
 }
@@ -102,5 +112,5 @@
 static inline uint32_t icp_irqc_get_sources(void)
 {
-	return *((uint32_t *) icp_hw_map.irqc);
+	return *((uint32_t *) icp.hw_map.irqc);
 }
 
@@ -112,5 +122,5 @@
 static inline void icp_irqc_mask(uint32_t irq)
 {
-	*((uint32_t *) icp_hw_map.irqc_mask) = (1 << irq);
+	*((uint32_t *) icp.hw_map.irqc_mask) = (1 << irq);
 }
 
@@ -122,33 +132,33 @@
 static inline void icp_irqc_unmask(uint32_t irq)
 {
-	*((uint32_t *) icp_hw_map.irqc_unmask) |= (1 << irq);
-}
-
-/** Initializes icp_hw_map. */
+	*((uint32_t *) icp.hw_map.irqc_unmask) |= (1 << irq);
+}
+
+/** Initializes icp.hw_map. */
 void icp_init(void)
 {
-	icp_hw_map.uart = km_map(ICP_UART, PAGE_SIZE,
-	    PAGE_WRITE | PAGE_NOT_CACHEABLE);
-	icp_hw_map.kbd_ctrl = km_map(ICP_KBD, PAGE_SIZE, PAGE_NOT_CACHEABLE);
-	icp_hw_map.kbd_stat = icp_hw_map.kbd_ctrl + ICP_KBD_STAT;
-	icp_hw_map.kbd_data = icp_hw_map.kbd_ctrl + ICP_KBD_DATA;
-	icp_hw_map.kbd_intstat = icp_hw_map.kbd_ctrl + ICP_KBD_INTR_STAT;
-	icp_hw_map.rtc = km_map(ICP_RTC, PAGE_SIZE,
-	    PAGE_WRITE | PAGE_NOT_CACHEABLE);
-	icp_hw_map.rtc1_load = icp_hw_map.rtc + ICP_RTC1_LOAD_OFFSET;
-	icp_hw_map.rtc1_read = icp_hw_map.rtc + ICP_RTC1_READ_OFFSET;
-	icp_hw_map.rtc1_ctl = icp_hw_map.rtc + ICP_RTC1_CTL_OFFSET;
-	icp_hw_map.rtc1_intrclr = icp_hw_map.rtc + ICP_RTC1_INTRCLR_OFFSET;
-	icp_hw_map.rtc1_bgload = icp_hw_map.rtc + ICP_RTC1_BGLOAD_OFFSET;
-	icp_hw_map.rtc1_intrstat = icp_hw_map.rtc + ICP_RTC1_INTRSTAT_OFFSET;
-
-	icp_hw_map.irqc = km_map(ICP_IRQC, PAGE_SIZE,
-	    PAGE_WRITE | PAGE_NOT_CACHEABLE);
-	icp_hw_map.irqc_mask = icp_hw_map.irqc + ICP_IRQC_MASK_OFFSET;
-	icp_hw_map.irqc_unmask = icp_hw_map.irqc + ICP_IRQC_UNMASK_OFFSET;
-	icp_hw_map.cmcr = km_map(ICP_CMCR, PAGE_SIZE,
-	    PAGE_WRITE | PAGE_NOT_CACHEABLE);
-	icp_hw_map.sdramcr = icp_hw_map.cmcr + ICP_SDRAMCR_OFFSET;
-	icp_hw_map.vga = km_map(ICP_VGA, PAGE_SIZE,
+	icp.hw_map.uart = km_map(ICP_UART, PAGE_SIZE,
+	    PAGE_WRITE | PAGE_NOT_CACHEABLE);
+	icp.hw_map.kbd_ctrl = km_map(ICP_KBD, PAGE_SIZE, PAGE_NOT_CACHEABLE);
+	icp.hw_map.kbd_stat = icp.hw_map.kbd_ctrl + ICP_KBD_STAT;
+	icp.hw_map.kbd_data = icp.hw_map.kbd_ctrl + ICP_KBD_DATA;
+	icp.hw_map.kbd_intstat = icp.hw_map.kbd_ctrl + ICP_KBD_INTR_STAT;
+	icp.hw_map.rtc = km_map(ICP_RTC, PAGE_SIZE,
+	    PAGE_WRITE | PAGE_NOT_CACHEABLE);
+	icp.hw_map.rtc1_load = icp.hw_map.rtc + ICP_RTC1_LOAD_OFFSET;
+	icp.hw_map.rtc1_read = icp.hw_map.rtc + ICP_RTC1_READ_OFFSET;
+	icp.hw_map.rtc1_ctl = icp.hw_map.rtc + ICP_RTC1_CTL_OFFSET;
+	icp.hw_map.rtc1_intrclr = icp.hw_map.rtc + ICP_RTC1_INTRCLR_OFFSET;
+	icp.hw_map.rtc1_bgload = icp.hw_map.rtc + ICP_RTC1_BGLOAD_OFFSET;
+	icp.hw_map.rtc1_intrstat = icp.hw_map.rtc + ICP_RTC1_INTRSTAT_OFFSET;
+
+	icp.hw_map.irqc = km_map(ICP_IRQC, PAGE_SIZE,
+	    PAGE_WRITE | PAGE_NOT_CACHEABLE);
+	icp.hw_map.irqc_mask = icp.hw_map.irqc + ICP_IRQC_MASK_OFFSET;
+	icp.hw_map.irqc_unmask = icp.hw_map.irqc + ICP_IRQC_UNMASK_OFFSET;
+	icp.hw_map.cmcr = km_map(ICP_CMCR, PAGE_SIZE,
+	    PAGE_WRITE | PAGE_NOT_CACHEABLE);
+	icp.hw_map.sdramcr = icp.hw_map.cmcr + ICP_SDRAMCR_OFFSET;
+	icp.hw_map.vga = km_map(ICP_VGA, PAGE_SIZE,
 	    PAGE_WRITE | PAGE_NOT_CACHEABLE);
 
@@ -163,7 +173,7 @@
 {
 	icp_irqc_mask(ICP_TIMER_IRQ);
-	*((uint32_t*) icp_hw_map.rtc1_load) = frequency;
-	*((uint32_t*) icp_hw_map.rtc1_bgload) = frequency;
-	*((uint32_t*) icp_hw_map.rtc1_ctl) = ICP_RTC_CTL_VALUE;
+	*((uint32_t*) icp.hw_map.rtc1_load) = frequency;
+	*((uint32_t*) icp.hw_map.rtc1_bgload) = frequency;
+	*((uint32_t*) icp.hw_map.rtc1_ctl) = ICP_RTC_CTL_VALUE;
 	icp_irqc_unmask(ICP_TIMER_IRQ);
 }
@@ -171,6 +181,6 @@
 static irq_ownership_t icp_timer_claim(irq_t *irq)
 {
-	if (icp_hw_map.rtc1_intrstat) {
-		*((uint32_t*) icp_hw_map.rtc1_intrclr) = 1;
+	if (icp.hw_map.rtc1_intrstat) {
+		*((uint32_t*) icp.hw_map.rtc1_intrclr) = 1;
 		return IRQ_ACCEPT;
 	} else
@@ -199,11 +209,11 @@
 static void icp_timer_irq_init(void)
 {
-	irq_initialize(&icp_timer_irq);
-	icp_timer_irq.devno = device_assign_devno();
-	icp_timer_irq.inr = ICP_TIMER_IRQ;
-	icp_timer_irq.claim = icp_timer_claim;
-	icp_timer_irq.handler = icp_timer_irq_handler;
-
-	irq_register(&icp_timer_irq);
+	irq_initialize(&icp.timer_irq);
+	icp.timer_irq.devno = device_assign_devno();
+	icp.timer_irq.inr = ICP_TIMER_IRQ;
+	icp.timer_irq.claim = icp_timer_claim;
+	icp.timer_irq.handler = icp_timer_irq_handler;
+
+	irq_register(&icp.timer_irq);
 }
 
@@ -230,5 +240,5 @@
 
 	if (hw_map_init_called) {
-		*size = (sdram[((*(uint32_t *)icp_hw_map.sdramcr &
+		*size = (sdram[((*(uint32_t *)icp.hw_map.sdramcr &
 		    ICP_SDRAM_MASK) >> 2)]);
 	} else {
@@ -304,4 +314,9 @@
 		stdout_wire(fbdev);
 #endif
+#ifdef CONFIG_ARM926_UART
+	if (arm926_uart_init(&icp.uart, ARM926_UART0_IRQ,
+	    ARM926_UART0_BASE_ADDRESS, sizeof(arm926_uart_regs_t)))
+		stdout_wire(&icp.uart.outdev);
+#endif
 }
 
@@ -310,7 +325,7 @@
 
 	pl050_t *pl050 = malloc(sizeof(pl050_t), FRAME_ATOMIC);
-	pl050->status = (ioport8_t *)icp_hw_map.kbd_stat;
-	pl050->data = (ioport8_t *)icp_hw_map.kbd_data;
-	pl050->ctrl = (ioport8_t *)icp_hw_map.kbd_ctrl;
+	pl050->status = (ioport8_t *)icp.hw_map.kbd_stat;
+	pl050->data = (ioport8_t *)icp.hw_map.kbd_data;
+	pl050->ctrl = (ioport8_t *)icp.hw_map.kbd_ctrl;
 		
 	pl050_instance_t *pl050_instance = pl050_init(pl050, ICP_KBD_IRQ);
@@ -335,4 +350,13 @@
 	    ICP_KBD);
 
+#ifdef CONFIG_ARM926_UART
+        srln_instance_t *srln_instance = srln_init();
+        if (srln_instance) {
+                indev_t *sink = stdin_wire();
+                indev_t *srln = srln_wire(srln_instance, sink);
+                arm926_uart_input_wire(&icp.uart, srln);
+		icp_irqc_unmask(ARM926_UART0_IRQ);
+        }
+#endif
 }
 
Index: kernel/genarch/Makefile.inc
===================================================================
--- kernel/genarch/Makefile.inc	(revision 0a4e1c77c74b5ead21db7cd9a738ffaacc357307)
+++ kernel/genarch/Makefile.inc	(revision 3ccba143f0e65b2bc3d74365b212d9d8c55bc2a8)
@@ -91,4 +91,9 @@
 endif
 
+ifeq ($(CONFIG_ARM926_UART),y)
+	GENARCH_SOURCES += \
+		genarch/src/drivers/arm926_uart/arm926_uart.c
+endif
+
 ifeq ($(CONFIG_S3C24XX_IRQC),y)
 	GENARCH_SOURCES += \
Index: kernel/genarch/include/drivers/arm926_uart/arm926_uart.h
===================================================================
--- kernel/genarch/include/drivers/arm926_uart/arm926_uart.h	(revision 3ccba143f0e65b2bc3d74365b212d9d8c55bc2a8)
+++ kernel/genarch/include/drivers/arm926_uart/arm926_uart.h	(revision 3ccba143f0e65b2bc3d74365b212d9d8c55bc2a8)
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup genarch
+ * @{
+ */
+/**
+ * @file
+ * @brief ARM926 on-chip UART (PrimeCell UART, PL011) driver.
+ */
+
+#ifndef KERN_S3C24XX_UART_H_
+#define KERN_S3C24XX_UART_H_
+
+#include <ddi/irq.h>
+#include <console/chardev.h>
+#include <typedefs.h>
+
+
+/** ARM926 User Guide ch. 4.8.5 (p. 106 in the pdf) */
+#define ARM926_UART0_BASE_ADDRESS   0x16000000
+#define ARM926_UART1_BASE_ADDRESS   0x16000000
+
+/** ARM926 User Guide ch. A.1 (p. 124 in the pdf) */
+#define ARM926_UART0_IRQ   1
+#define ARM926_UART1_IRQ   2
+
+/** PrimeCell UART TRM ch. 3.3 (p. 49 in the pdf) */
+typedef struct {
+	/** UART data register */
+	ioport32_t data;
+#define ARM926_UART_DATA_DATA_MASK   0xff
+#define ARM926_UART_DATA_FE_FLAG   (1 <<  7)
+#define ARM926_UART_DATA_PE_FLAG   (1 <<  9)
+#define ARM926_UART_DATA_BE_FLAG   (1 << 10)
+#define ARM926_UART_DATA_OE_FLAG   (1 << 11)
+
+	union {
+		/* Same values that are in upper bits of data register*/
+		const ioport32_t status;
+#define ARM926_UART_STATUS_FE_FLAG   (1 << 0)
+#define ARM926_UART_STATUS_PE_FLAG   (1 << 1)
+#define ARM926_UART_STATUS_BE_FLAG   (1 << 2)
+#define ARM926_UART_STATUS_OE_FLAG   (1 << 3)
+		/* Writing anything clears all errors */
+		ioport32_t error_clear;
+	};
+	uint32_t padd0_[4];
+
+	const ioport32_t flag;
+#define ARM926_UART_FLAG_CTS_FLAG    (1 << 0)
+#define ARM926_UART_FLAG_DSR_FLAG    (1 << 1)
+#define ARM926_UART_FLAG_DCD_FLAG    (1 << 2)
+#define ARM926_UART_FLAG_BUSY_FLAG   (1 << 3)
+#define ARM926_UART_FLAG_RXFE_FLAG   (1 << 4)
+#define ARM926_UART_FLAG_TXFF_FLAG   (1 << 5)
+#define ARM926_UART_FLAG_RXFF_FLAG   (1 << 6)
+#define ARM926_UART_FLAG_TXFE_FLAG   (1 << 7)
+#define ARM926_UART_FLAG_RI_FLAG     (1 << 8)
+	uint32_t padd1_;
+
+	ioport32_t irda_low_power;
+#define ARM926_UART_IRDA_LOW_POWER_MASK   0xff
+
+	ioport32_t int_baud_divisor;
+#define ARM926_UART_INT_BAUD_DIVISOR_MASK   0xffff
+
+	ioport32_t fract_baud_divisor;
+#define ARM926_UART_FRACT_BAUD_DIVISOR_MASK   0x1f
+
+	ioport32_t line_control_high;
+#define ARM926_UART_CONTROLHI_BRK_FLAG    (1 << 0)
+#define ARM926_UART_CONTROLHI_PEN_FLAG    (1 << 1)
+#define ARM926_UART_CONTROLHI_EPS_FLAG    (1 << 2)
+#define ARM926_UART_CONTROLHI_STP2_FLAG   (1 << 3)
+#define ARM926_UART_CONTROLHI_FEN_FLAG    (1 << 4)
+#define ARM926_UART_CONTROLHI_WLEN_MASK   0x3
+#define ARM926_UART_CONTROLHI_WLEN_SHIFT    5
+#define ARM926_UART_CONTROLHI_SPS_FLAG    (1 << 5)
+
+	ioport32_t control;
+#define ARM926_UART_CONTROL_UARTEN_FLAG   (1 << 0)
+#define ARM926_UART_CONTROL_SIREN_FLAG    (1 << 1)
+#define ARM926_UART_CONTROL_SIRLP_FLAG    (1 << 2)
+#define ARM926_UART_CONTROL_LBE_FLAG      (1 << 7)
+#define ARM926_UART_CONTROL_TXE_FLAG      (1 << 8)
+#define ARM926_UART_CONTROL_RXE_FLAG      (1 << 9)
+#define ARM926_UART_CONTROL_DTR_FLAG     (1 << 10)
+#define ARM926_UART_CONTROL_RTS_FLAG     (1 << 11)
+#define ARM926_UART_CONTROL_OUT1_FLAG    (1 << 12)
+#define ARM926_UART_CONTROL_OUT2_FLAG    (1 << 13)
+#define ARM926_UART_CONTROL_RTSE_FLAG    (1 << 14)
+#define ARM926_UART_CONTROL_CTSE_FLAG    (1 << 15)
+
+	ioport32_t interrupt_fifo;
+#define ARM926_UART_INTERRUPTFIFO_TX_MASK   0x7
+#define ARM926_UART_INTERRUPTFIFO_TX_SHIFT    0
+#define ARM926_UART_INTERRUPTFIFO_RX_MASK   0x7
+#define ARM926_UART_INTERRUPTFIFO_RX_SHIFT    3
+
+	/** Interrupt mask register */
+	ioport32_t interrupt_mask;
+	/** Pending interrupts before applying the mask */
+	const ioport32_t raw_interrupt_status;
+	/** Pending interrupts after applying the mask */
+	const ioport32_t masked_interrupt_status;
+	/** Write 1s to clear pending interrupts */
+	ioport32_t interrupt_clear;
+#define ARM926_UART_INTERRUPT_RIM_FLAG    (1 << 0)
+#define ARM926_UART_INTERRUPT_CTSM_FLAG   (1 << 1)
+#define ARM926_UART_INTERRUPT_DCDM_FLAG   (1 << 2)
+#define ARM926_UART_INTERRUPT_DSRM_FLAG   (1 << 3)
+#define ARM926_UART_INTERRUPT_RX_FLAG     (1 << 4)
+#define ARM926_UART_INTERRUPT_TX_FLAG     (1 << 5)
+#define ARM926_UART_INTERRUPT_RT_FLAG     (1 << 6)
+#define ARM926_UART_INTERRUPT_FE_FLAG     (1 << 7)
+#define ARM926_UART_INTERRUPT_PE_FLAG     (1 << 8)
+#define ARM926_UART_INTERRUPT_BE_FLAG     (1 << 9)
+#define ARM926_UART_INTERRUPT_OE_FLAG    (1 << 10)
+#define ARM926_UART_INTERRUPT_ALL           0x3ff
+
+	ioport32_t dma_control;
+#define ARM926_UART_DMACONTROL_RXDMAEN_FLAG    (1 << 0)
+#define ARM926_UART_DMACONTROL_TXDMAEN_FLAG    (1 << 1)
+#define ARM926_UART_DMACONTROL_DMAONERR_FLAG   (1 << 2)
+
+	// TODO There is some reserved space here followed by
+	// peripheral identification registers.
+} arm926_uart_regs_t;
+
+typedef struct {
+	arm926_uart_regs_t *regs;
+	indev_t *indev;
+	outdev_t outdev;
+	irq_t irq;
+} arm926_uart_t;
+
+bool arm926_uart_init(arm926_uart_t *, inr_t, uintptr_t, size_t);
+void arm926_uart_input_wire(arm926_uart_t *, indev_t *);
+
+#endif
+/**
+ * @}
+ */
Index: kernel/genarch/src/drivers/arm926_uart/arm926_uart.c
===================================================================
--- kernel/genarch/src/drivers/arm926_uart/arm926_uart.c	(revision 3ccba143f0e65b2bc3d74365b212d9d8c55bc2a8)
+++ kernel/genarch/src/drivers/arm926_uart/arm926_uart.c	(revision 3ccba143f0e65b2bc3d74365b212d9d8c55bc2a8)
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup genarch
+ * @{
+ */
+/**
+ * @file
+ * @brief ARM926 on-chip UART (PrimeCell UART, PL011) driver.
+ */
+
+#include <genarch/drivers/arm926_uart/arm926_uart.h>
+#include <console/chardev.h>
+#include <console/console.h>
+#include <ddi/device.h>
+#include <arch/asm.h>
+#include <mm/slab.h>
+#include <mm/page.h>
+#include <mm/km.h>
+#include <sysinfo/sysinfo.h>
+#include <str.h>
+
+static void arm926_uart_sendb(arm926_uart_t *uart, uint8_t byte)
+{
+	/* Wait for space becoming available in Tx FIFO. */
+	// TODO make pio_read accept consts pointers and remove the cast
+	while ((pio_read_32((ioport32_t*)&uart->regs->flag) & ARM926_UART_FLAG_TXFF_FLAG) != 0)
+		;
+
+	pio_write_32(&uart->regs->data, byte);
+}
+
+static void arm926_uart_putchar(outdev_t *dev, wchar_t ch)
+{
+	arm926_uart_t *uart = dev->data;
+
+	if (!ascii_check(ch)) {
+		arm926_uart_sendb(uart, U_SPECIAL);
+	} else {
+		if (ch == '\n')
+			arm926_uart_sendb(uart, (uint8_t) '\r');
+		arm926_uart_sendb(uart, (uint8_t) ch);
+	}
+}
+
+static outdev_operations_t arm926_uart_ops = {
+	.write = arm926_uart_putchar,
+	.redraw = NULL,
+};
+
+static irq_ownership_t arm926_uart_claim(irq_t *irq)
+{
+	return IRQ_ACCEPT;
+}
+
+static void arm926_uart_irq_handler(irq_t *irq)
+{
+	arm926_uart_t *uart = irq->instance;
+
+	// TODO make pio_read accept consts pointers and remove the cast
+	while ((pio_read_32((ioport32_t*)&uart->regs->flag) & ARM926_UART_FLAG_RXFE_FLAG) == 0) {
+		/* We ignore all error flags here */
+		const uint8_t data = pio_read_32(&uart->regs->data);
+		if (uart->indev)
+			indev_push_character(uart->indev, data);
+	}
+	/* Ack interrupts */
+	pio_write_32(&uart->regs->interrupt_clear, ARM926_UART_INTERRUPT_ALL);
+}
+
+bool arm926_uart_init(
+    arm926_uart_t *uart, inr_t interrupt, uintptr_t addr, size_t size)
+{
+	ASSERT(uart);
+	uart->regs = (void*)km_map(addr, size, PAGE_NOT_CACHEABLE);
+
+	ASSERT(uart->regs);
+
+	/* Enable hw flow control */
+	uart->regs->control = 0 |
+	    ARM926_UART_CONTROL_UARTEN_FLAG |
+	    ARM926_UART_CONTROL_RTSE_FLAG |
+	    ARM926_UART_CONTROL_CTSE_FLAG;
+
+	/* Mask all interrupts */
+	uart->regs->interrupt_mask = ARM926_UART_INTERRUPT_ALL;
+
+	outdev_initialize("arm926_uart_dev", &uart->outdev, &arm926_uart_ops);
+	uart->outdev.data = uart;
+
+	/* Initialize IRQ */
+	irq_initialize(&uart->irq);
+        uart->irq.devno = device_assign_devno();
+        uart->irq.inr = interrupt;
+        uart->irq.claim = arm926_uart_claim;
+        uart->irq.handler = arm926_uart_irq_handler;
+        uart->irq.instance = uart;
+
+	return true;
+}
+
+void arm926_uart_input_wire(arm926_uart_t *uart, indev_t *indev)
+{
+	ASSERT(uart);
+	ASSERT(indev);
+
+	uart->indev = indev;
+	irq_register(&uart->irq);
+	/* Enable receive interrupt */
+	uart->regs->interrupt_mask &= ~ARM926_UART_INTERRUPT_RX_FLAG;
+}
+
+/** @}
+ */
+
