diff --git a/Kconfig b/Kconfig
index d3804761d21dfc8a080d1116e313bd2a62ad61c3..0c86f1538fb3f856242448942ee15ca8ebb750d9 100644
--- a/Kconfig
+++ b/Kconfig
@@ -136,3 +136,14 @@ config CMD_CONFIG
 	  This options adds the "config" command to the shell, which
 	  reports the current configuration.  This adds half a kilobyte
 	  to the binary size (100b for the code plus the .config file).
+
+config UART_SW
+	boolean "Use software uart (not the hardware one)"
+	help
+	  The software uart is made up of two circular buffers. They
+	  waste some RAM but allow to avoid the USB cable.  Software
+	  uarts can be accessed using tools/wrpc-uart-sw.
+
+config UART
+       boolean
+       default !UART_SW
diff --git a/dev/dev.mk b/dev/dev.mk
index 299d3fea68b5e0848c7936480b5ecf26a205c30e..56ec0be8cf7b779515e1059e1f596de403b81551 100644
--- a/dev/dev.mk
+++ b/dev/dev.mk
@@ -6,7 +6,9 @@ obj-y += \
 	dev/minic.o \
 	dev/pps_gen.o \
 	dev/syscon.o \
-	dev/uart.o \
 	dev/sfp.o \
 	dev/onewire.o \
 	dev/sdb.o
+
+obj-$(CONFIG_UART) +=		dev/uart.o
+obj-$(CONFIG_UART_SW) +=	dev/uart-sw.o
diff --git a/dev/uart-sw.c b/dev/uart-sw.c
new file mode 100644
index 0000000000000000000000000000000000000000..c8d6cc11f886e7e06f388417ca170f2b5dc27e5c
--- /dev/null
+++ b/dev/uart-sw.c
@@ -0,0 +1,61 @@
+/*
+ * This work is part of the White Rabbit project
+ *
+ * Copyright (C) 2013 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ */
+#include <wrc.h>
+#include <uart-sw.h>
+
+static struct wrc_uart_sw __attribute__((aligned(16)))  uart_sw_dev = {
+	.magic = UART_SW_MAGIC,
+	.wsize = CONFIG_UART_SW_WSIZE,
+	.rsize = CONFIG_UART_SW_RSIZE,
+};
+
+static uint16_t nreturned;
+
+void uart_init(void)
+{
+	/* zero fields, as we may be reloaded */
+	uart_sw_dev.nwritten = uart_sw_dev.nread = 0;
+}
+
+void uart_write_byte(int b)
+{
+	int index;
+
+	if (b == '\n')
+		uart_write_byte('\r');
+
+	index = uart_sw_dev.nwritten % CONFIG_UART_SW_WSIZE;
+	uart_sw_dev.wbuffer[index] = b;
+	uart_sw_dev.nwritten++;
+
+	/* fake a real uart, so user-space can poll not-too-fast */
+	usleep(1000 * 1000 / 11520);
+}
+
+int uart_write_string(const char *s)
+{
+	const char *t = s;
+	while (*s)
+		uart_write_byte(*(s++));
+	return s - t;
+}
+
+int puts(const char *s) __attribute__((alias("uart_write_string")));
+
+
+int uart_read_byte()
+{
+	int index;
+
+	if (nreturned == uart_sw_dev.nread) /* nread == written by host */
+		return -1;
+
+	index = (nreturned++) % CONFIG_UART_SW_RSIZE;
+	return uart_sw_dev.rbuffer[index];
+}
diff --git a/include/uart-sw.h b/include/uart-sw.h
new file mode 100644
index 0000000000000000000000000000000000000000..5555d3e61229b324984713d8a365f8b21f6674e0
--- /dev/null
+++ b/include/uart-sw.h
@@ -0,0 +1,30 @@
+/*
+ * This work is part of the White Rabbit project
+ *
+ * Copyright (C) 2013 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ */
+#ifndef __UART_SW_H
+#define __UART_SW_H
+
+/* The host code (tools/wrpc-uart-sw) must include this too, for the struct */
+#ifdef __lm32__
+#include "uart.h" /* we need to offer the same prototypes */
+#endif
+
+/* These are currently static but can become Kconfig items */
+#define CONFIG_UART_SW_WSIZE  256
+#define CONFIG_UART_SW_RSIZE   32
+
+#define UART_SW_MAGIC 0x752d7377 /* "u-sw" */
+struct wrc_uart_sw {
+	uint32_t magic;
+	uint16_t wsize, nwritten;
+	uint16_t rsize, nread;
+	unsigned char wbuffer[CONFIG_UART_SW_WSIZE];
+	unsigned char rbuffer[CONFIG_UART_SW_RSIZE];
+};
+
+#endif /* __UART_SW_H */