cpu: Add initial support for Allwinner A10 SoC

Add minimal support needed to get a bootblock capable of initialising
a serial console.

Change-Id: I50dd85544549baf9c5ea0aa3b4296972136c02a4
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Reviewed-on: http://review.coreboot.org/4549
Tested-by: build bot (Jenkins)
Reviewed-by: David Hendricks <dhendrix@chromium.org>
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
diff --git a/src/cpu/Kconfig b/src/cpu/Kconfig
index cd6c7a0..f3883d3 100644
--- a/src/cpu/Kconfig
+++ b/src/cpu/Kconfig
@@ -3,6 +3,7 @@
 # (See also src/Kconfig)
 if ARCH_ARMV7
 
+source src/cpu/allwinner/Kconfig
 source src/cpu/armltd/Kconfig
 source src/cpu/samsung/Kconfig
 source src/cpu/ti/Kconfig
diff --git a/src/cpu/Makefile.inc b/src/cpu/Makefile.inc
index 7cd9d5f..f206fdc 100644
--- a/src/cpu/Makefile.inc
+++ b/src/cpu/Makefile.inc
@@ -1,6 +1,7 @@
 ################################################################################
 ## Subdirectories
 ################################################################################
+subdirs-y += allwinner
 subdirs-y += amd
 subdirs-y += dmp
 subdirs-y += armltd
diff --git a/src/cpu/allwinner/Kconfig b/src/cpu/allwinner/Kconfig
new file mode 100644
index 0000000..d97cb64
--- /dev/null
+++ b/src/cpu/allwinner/Kconfig
@@ -0,0 +1 @@
+source src/cpu/allwinner/a10/Kconfig
diff --git a/src/cpu/allwinner/Makefile.inc b/src/cpu/allwinner/Makefile.inc
new file mode 100644
index 0000000..e52a12e
--- /dev/null
+++ b/src/cpu/allwinner/Makefile.inc
@@ -0,0 +1 @@
+subdirs-$(CONFIG_CPU_ALLWINNER_A10) += a10
diff --git a/src/cpu/allwinner/a10/Kconfig b/src/cpu/allwinner/a10/Kconfig
new file mode 100644
index 0000000..639108a
--- /dev/null
+++ b/src/cpu/allwinner/a10/Kconfig
@@ -0,0 +1,115 @@
+config CPU_ALLWINNER_A10
+	bool
+	default n
+
+if CPU_ALLWINNER_A10
+
+config CPU_SPECIFIC_OPTIONS
+	def_bool y
+	select HAVE_MONOTONIC_TIMER
+	select HAVE_UART_SPECIAL
+	select HAVE_UART_MEMORY_MAPPED
+	select BOOTBLOCK_CONSOLE
+	select EARLY_CONSOLE
+
+config BOOTBLOCK_CPU_INIT
+	string
+	default "cpu/allwinner/a10/bootblock.c"
+	help
+	  CPU/SoC-specific bootblock code. This is useful if the
+	  bootblock must load microcode or copy data from ROM before
+	  searching for the bootblock.
+
+# The "eGON.BT0" header takes 32 bytes
+config BOOTBLOCK_BASE
+	hex
+	default 0x20
+
+config BOOTBLOCK_ROM_OFFSET
+	hex
+	default 0x00
+
+config CBFS_HEADER_ROM_OFFSET
+	hex
+	default 0x10
+
+config CBFS_ROM_OFFSET
+	# Calculated by BL1 + max bootblock size.
+	default 0x4c00
+
+# FIXME: untested
+config ROMSTAGE_BASE
+	hex
+	default SYS_SDRAM_BASE
+
+# Keep the stack in SRAM
+config STACK_TOP
+	hex
+	default 0x00008000
+
+config STACK_BOTTOM
+	hex
+	default 0x00004000
+
+config STACK_SIZE
+	hex
+	default 0x00004000
+
+## TODO Change this to some better address not overlapping bootblock when
+## cbfstool supports creating header in arbitrary location.
+config CBFS_HEADER_ROM_OFFSET
+	hex "offset of master CBFS header in ROM"
+	default 0x40
+
+config SYS_SDRAM_BASE
+	hex
+	default 0x40000000
+
+choice CONSOLE_SERIAL_UART_CHOICES
+	prompt "Serial Console UART"
+	default CONSOLE_SERIAL_UART0
+	depends on CONSOLE_SERIAL_UART
+
+config CONSOLE_SERIAL_UART0
+	bool "UART0"
+	help
+	  Serial console on UART0
+
+config CONSOLE_SERIAL_UART1
+	bool "UART1"
+	help
+	  Serial console on UART1
+
+config CONSOLE_SERIAL_UART2
+	bool "UART2"
+	help
+	  Serial console on UART2
+
+config CONSOLE_SERIAL_UART3
+	bool "UART3"
+	help
+	  Serial console on UART3
+
+config CONSOLE_SERIAL_UART4
+	bool "UART4"
+	help
+	  Serial console on UART4
+
+config CONSOLE_SERIAL_UART5
+	bool "UART5"
+	help
+	  Serial console on UART5
+
+config CONSOLE_SERIAL_UART6
+	bool "UART6"
+	help
+	  Serial console on UART6
+
+config CONSOLE_SERIAL_UART7
+	bool "UART7"
+	help
+	  Serial console on UART7
+
+endchoice
+
+endif # if CPU_ALLWINNER_A10
diff --git a/src/cpu/allwinner/a10/Makefile.inc b/src/cpu/allwinner/a10/Makefile.inc
new file mode 100644
index 0000000..48f3110
--- /dev/null
+++ b/src/cpu/allwinner/a10/Makefile.inc
@@ -0,0 +1,35 @@
+bootblock-y	+= pinmux.c
+bootblock-y	+= bootblock_media.c
+bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += uart.c
+bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += uart_console.c
+
+romstage-y	+= uart.c
+romstage-y	+= uart_console.c
+romstage-y	+= bootblock_media.c
+
+ramstage-y	+= uart.c
+ramstage-y	+= uart_console.c
+ramstage-y	+= timer.c
+ramstage-y	+= monotonic_timer.c
+ramstage-y	+= bootblock_media.c
+
+
+real-target: $(obj)/BOOT0
+
+get_bootblock_size= \
+	$(eval bb_s=$(shell $(CBFSTOOL) $(1) print | grep bootblocksize | \
+					sed 's/[^0-9 ]//g')) \
+	$(shell echo $$(($(word 2, $(strip $(bb_s))))))
+
+# The boot ROM in the SoC will start loading code if a special boot0 header is
+# found (at an offset of 8KiB in either NAND or SD), and the checksum is
+# correct. this header is normally added by the 'mxsunxiboot' tool. The file
+# passed to mksunxiboot should only include the bootblock due to size
+# limitations.
+# FIXME: Figure out how to safely integrate in coreboot.rom. For now, only copy
+# the first 15 KiB of coreboot.rom (This will not collide with stack)
+$(obj)/BOOT0: $(obj)/coreboot.rom
+	@printf "    BOOT0      $(subst $(obj)/,,$(^))\n"
+	touch $@
+	dd if=$^ of=$^.tmp bs=1024 count=15
+	-mksunxiboot $^.tmp $@
diff --git a/src/cpu/allwinner/a10/bootblock.c b/src/cpu/allwinner/a10/bootblock.c
new file mode 100644
index 0000000..38fe953
--- /dev/null
+++ b/src/cpu/allwinner/a10/bootblock.c
@@ -0,0 +1,21 @@
+/*
+ * Allwinner A10 bootblock initialization
+ *
+ * Copyright (C) 2013 Google Inc.
+ * Copyright (C) 2013  Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Subject to the GNU GPL v2, or (at your option) any later version.
+ */
+
+#include <types.h>
+#include <arch/cache.h>
+
+void bootblock_cpu_init(void);
+void bootblock_cpu_init(void)
+{
+	uint32_t sctlr;
+
+	/* enable dcache */
+	sctlr = read_sctlr();
+	sctlr |= SCTLR_C;
+	write_sctlr(sctlr);
+}
diff --git a/src/cpu/allwinner/a10/bootblock_media.c b/src/cpu/allwinner/a10/bootblock_media.c
new file mode 100644
index 0000000..a5863b6
--- /dev/null
+++ b/src/cpu/allwinner/a10/bootblock_media.c
@@ -0,0 +1,14 @@
+/*
+ * CBFS accessors for bootblock stage.
+ *
+ * Copyright (C) 2013  Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Subject to the GNU GPL v2, or (at your option) any later version.
+ */
+#include <cbfs.h>
+#include <console/console.h>
+
+int init_default_cbfs_media(struct cbfs_media *media)
+{
+	printk(BIOS_ERR, "Oh my! I don't know how to access CBFS yet.");
+	return 0;
+}
diff --git a/src/cpu/allwinner/a10/clock.h b/src/cpu/allwinner/a10/clock.h
new file mode 100644
index 0000000..e028d13
--- /dev/null
+++ b/src/cpu/allwinner/a10/clock.h
@@ -0,0 +1,137 @@
+/*
+ * Definitions for clock control and gating on Allwinner CPUs
+ *
+ * Copyright (C) 2007-2011 Allwinner Technology Co., Ltd.
+ *	Tom Cubie <tangliang@allwinnertech.com>
+ * Copyright (C) 2013  Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Subject to the GNU GPL v2, or (at your option) any later version.
+ */
+
+#ifndef CPU_ALLWINNER_A10_CLOCK_H
+#define CPU_ALLWINNER_A10_CLOCK_H
+
+#include "memmap.h"
+#include <types.h>
+
+/* CPU_AHB_APB0 config values */
+#define CPU_CLK_SRC_MASK		(3 << 16)
+#define  CPU_CLK_SRC_OSC24M		(1 << 16)
+#define  CPU_CLK_SRC_PLL1		(2 << 16)
+#define APB0_DIV_MASK			(3 << 8)
+#define  APB0_DIV_1			(0 << 8)
+#define  APB0_DIV_2			(1 << 8)
+#define  APB0_DIV_4			(2 << 8)
+#define  APB0_DIV_8			(3 << 8)
+#define AHB_DIV_MASK			(3 << 4)
+#define  AHB_DIV_1			(0 << 4)
+#define  AHB_DIV_2			(1 << 4)
+#define  AHB_DIV_4			(2 << 4)
+#define  AHB_DIV_8			(3 << 4)
+#define AXI_DIV_MASK			(3 << 0)
+#define  AXI_DIV_1			(0 << 0)
+#define  AXI_DIV_2			(1 << 0)
+#define  AXI_DIV_3			(2 << 0)
+#define  AXI_DIV_4			(3 << 0)
+
+/* APB1_CLK_DIV values */
+#define APB1_CLK_SRC_MASK		(3 << 24)
+#define  APB1_CLK_SRC_OSC24M		(0 << 24)
+#define  APB1_CLK_SRC_PLL6		(1 << 24)
+#define  APB1_CLK_SRC_32K		(2 << 24)
+#define APB1_RAT_N_MASK			(3 << 16)
+#define  APB1_RAT_N(m)			(((m) & 0x3) << 16)
+#define APB1_RAT_M_MASK			0x1f << 0)
+#define  APB1_RAT_M(n)			(((n) & 0x1f) << 0)
+
+/* APB0_GATING values */
+#define APB0_GATE_KEYPAD		(1 << 10)
+#define APB0_GATE_IR(x)			(((1 << (x)) & 0x3) << 6)
+#define APB0_GATE_PIO			(1 << 5)
+#define APB0_GATE_IIS			(1 << 3)
+#define APB0_GATE_AC97			(1 << 2)
+#define APB0_GATE_CODEC			(1 << 0)
+
+/* APB1_GATING values */
+#define APB1_GATE_UART(x)		(((1 << (x)) & 0xff) << 16)
+#define APB1_GATE_PS2(x)		(((1 << (x)) & 0x3) << 6)
+#define APB1_GATE_CAN			(1 << 4)
+#define APB1_GATE_TWI(x)		(((1 << (x)) & 0x7) << 0)
+
+struct a10_ccm {
+	u32 pll1_cfg;		/* 0x00 pll1 control */
+	u32 pll1_tun;		/* 0x04 pll1 tuning */
+	u32 pll2_cfg;		/* 0x08 pll2 control */
+	u32 pll2_tun;		/* 0x0c pll2 tuning */
+	u32 pll3_cfg;		/* 0x10 pll3 control */
+	u8 res0[0x4];
+	u32 pll4_cfg;		/* 0x18 pll4 control */
+	u8 res1[0x4];
+	u32 pll5_cfg;		/* 0x20 pll5 control */
+	u32 pll5_tun;		/* 0x24 pll5 tuning */
+	u32 pll6_cfg;		/* 0x28 pll6 control */
+	u32 pll6_tun;		/* 0x2c pll6 tuning */
+	u32 pll7_cfg;		/* 0x30 pll7 control */
+	u32 pll1_tun2;		/* 0x34 pll5 tuning2 */
+	u8 res2[0x4];
+	u32 pll5_tun2;		/* 0x3c pll5 tuning2 */
+	u8 res3[0xc];
+	u32 pll_lock_dbg;	/* 0x4c pll lock time debug */
+	u32 osc24m_cfg;		/* 0x50 osc24m control */
+	u32 cpu_ahb_apb0_cfg;	/* 0x54 cpu,ahb and apb0 divide ratio */
+	u32 apb1_clk_div_cfg;	/* 0x58 apb1 clock dividor */
+	u32 axi_gate;		/* 0x5c axi module clock gating */
+	u32 ahb_gate0;		/* 0x60 ahb module clock gating 0 */
+	u32 ahb_gate1;		/* 0x64 ahb module clock gating 1 */
+	u32 apb0_gate;		/* 0x68 apb0 module clock gating */
+	u32 apb1_gate;		/* 0x6c apb1 module clock gating */
+	u8 res4[0x10];
+	u32 nand_sclk_cfg;	/* 0x80 nand sub clock control */
+	u32 ms_sclk_cfg;	/* 0x84 memory stick sub clock control */
+	u32 sd0_clk_cfg;	/* 0x88 sd0 clock control */
+	u32 sd1_clk_cfg;	/* 0x8c sd1 clock control */
+	u32 sd2_clk_cfg;	/* 0x90 sd2 clock control */
+	u32 sd3_clk_cfg;	/* 0x94 sd3 clock control */
+	u32 ts_clk_cfg;		/* 0x98 transport stream clock control */
+	u32 ss_clk_cfg;		/* 0x9c */
+	u32 spi0_clk_cfg;	/* 0xa0 */
+	u32 spi1_clk_cfg;	/* 0xa4 */
+	u32 spi2_clk_cfg;	/* 0xa8 */
+	u32 pata_clk_cfg;	/* 0xac */
+	u32 ir0_clk_cfg;	/* 0xb0 */
+	u32 ir1_clk_cfg;	/* 0xb4 */
+	u32 iis_clk_cfg;	/* 0xb8 */
+	u32 ac97_clk_cfg;	/* 0xbc */
+	u32 spdif_clk_cfg;	/* 0xc0 */
+	u32 keypad_clk_cfg;	/* 0xc4 */
+	u32 sata_clk_cfg;	/* 0xc8 */
+	u32 usb_clk_cfg;	/* 0xcc */
+	u32 gps_clk_cfg;	/* 0xd0 */
+	u32 spi3_clk_cfg;	/* 0xd4 */
+	u8 res5[0x28];
+	u32 dram_clk_cfg;	/* 0x100 */
+	u32 be0_clk_cfg;	/* 0x104 */
+	u32 be1_clk_cfg;	/* 0x108 */
+	u32 fe0_clk_cfg;	/* 0x10c */
+	u32 fe1_clk_cfg;	/* 0x110 */
+	u32 mp_clk_cfg;		/* 0x114 */
+	u32 lcd0_ch0_clk_cfg;	/* 0x118 */
+	u32 lcd1_ch0_clk_cfg;	/* 0x11c */
+	u32 csi_isp_clk_cfg;	/* 0x120 */
+	u8 res6[0x4];
+	u32 tvd_clk_reg;	/* 0x128 */
+	u32 lcd0_ch1_clk_cfg;	/* 0x12c */
+	u32 lcd1_ch1_clk_cfg;	/* 0x130 */
+	u32 csi0_clk_cfg;	/* 0x134 */
+	u32 csi1_clk_cfg;	/* 0x138 */
+	u32 ve_clk_cfg;		/* 0x13c */
+	u32 audio_codec_clk_cfg;	/* 0x140 */
+	u32 avs_clk_cfg;	/* 0x144 */
+	u32 ace_clk_cfg;	/* 0x148 */
+	u32 lvds_clk_cfg;	/* 0x14c */
+	u32 hdmi_clk_cfg;	/* 0x150 */
+	u32 mali_clk_cfg;	/* 0x154 */
+	u8 res7[0x4];
+	u32 mbus_clk_cfg;	/* 0x15c */
+} __attribute__ ((packed));
+
+#endif				/* CPU_ALLWINNER_A10_CLOCK_H */
diff --git a/src/cpu/allwinner/a10/gpio.h b/src/cpu/allwinner/a10/gpio.h
new file mode 100644
index 0000000..f285451
--- /dev/null
+++ b/src/cpu/allwinner/a10/gpio.h
@@ -0,0 +1,51 @@
+/*
+ * Definitions for GPIO and pin multiplexing on Allwinner CPUs
+ *
+ * Copyright (C) 2013  Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Subject to the GNU GPL v2, or (at your option) any later version.
+ */
+
+#ifndef __CPU_ALLWINNER_A10_PINMUX_H
+#define __CPU_ALLWINNER_A10_PINMUX_H
+
+#include <types.h>
+
+#define GPIO_BASE		 0x01C20800
+
+#define GPA			0
+#define GPB			1
+#define GPC			2
+#define GPD			3
+#define GPE			4
+#define GPF			5
+#define GPG			6
+#define GPH			7
+#define GPI			8
+#define GPS			9
+
+struct a10_gpio_port {
+	u32 cfg[4];
+	u32 dat;
+	u32 drv[2];
+	u32 pul[2];
+} __attribute__ ((packed));
+
+struct a10_gpio {
+	struct a10_gpio_port port[10];
+	u8 reserved_0x168[0x98];
+
+	/* Offset 0x200 */
+	u32 int_cfg[4];
+
+	u32 int_ctl;
+	u32 int_sta;
+	u8 reserved_0x21C[4];
+	u32 int_deb;
+
+	u32 sdr_pad_drv;
+	u32 sdr_pad_pul;
+} __attribute__ ((packed));
+
+void gpio_set_func(u8 port, u8 pin, u8 pad_func);
+
+#endif				/* __CPU_ALLWINNER_A10_PINMUX_H */
diff --git a/src/cpu/allwinner/a10/memmap.h b/src/cpu/allwinner/a10/memmap.h
new file mode 100644
index 0000000..78d81dc
--- /dev/null
+++ b/src/cpu/allwinner/a10/memmap.h
@@ -0,0 +1,117 @@
+/*
+ * Memory map definitions for Allwinner A10 CPUs
+ *
+ * Copyright (C) 2007-2011 Allwinner Technology Co., Ltd.
+ *	Tom Cubie <tangliang@allwinnertech.com>
+ * Copyright (C) 2013  Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Subject to the GNU GPL v2, or (at your option) any later version.
+ */
+
+#ifndef CPU_ALLWINNER_A10_MEMMAP_H
+#define CPU_ALLWINNER_A10_MEMMAP_H
+
+#define A1X_SRAM_A1_BASE		0x00000000
+#define A1X_SRAM_A1_SIZE		(16 * 1024)	/* 16 kiB */
+
+#define A1X_SRAM_A2_BASE		0x00004000	/* 16 kiB */
+#define A1X_SRAM_A3_BASE		0x00008000	/* 13 kiB */
+#define A1X_SRAM_A4_BASE		0x0000b400	/* 3 kiB */
+#define A1X_SRAM_D_BASE			0x01c00000
+#define A1X_SRAM_B_BASE			0x01c00000	/* 64 kiB (secure) */
+
+#define A1X_SRAMC_BASE			0x01c00000
+#define A1X_DRAMC_BASE			0x01c01000
+#define A1X_DMA_BASE			0x01c02000
+#define A1X_NFC_BASE			0x01c03000
+#define A1X_TS_BASE			0x01c04000
+#define A1X_SPI0_BASE			0x01c05000
+#define A1X_SPI1_BASE			0x01c06000
+#define A1X_MS_BASE			0x01c07000
+#define A1X_TVD_BASE			0x01c08000
+#define A1X_CSI0_BASE			0x01c09000
+#define A1X_TVE0_BASE			0x01c0a000
+#define A1X_EMAC_BASE			0x01c0b000
+#define A1X_LCD0_BASE			0x01c0C000
+#define A1X_LCD1_BASE			0x01c0d000
+#define A1X_VE_BASE			0x01c0e000
+#define A1X_MMC0_BASE			0x01c0f000
+#define A1X_MMC1_BASE			0x01c10000
+#define A1X_MMC2_BASE			0x01c11000
+#define A1X_MMC3_BASE			0x01c12000
+#define A1X_USB0_BASE			0x01c13000
+#define A1X_USB1_BASE			0x01c14000
+#define A1X_SS_BASE			0x01c15000
+#define A1X_HDMI_BASE			0x01c16000
+#define A1X_SPI2_BASE			0x01c17000
+#define A1X_SATA_BASE			0x01c18000
+#define A1X_PATA_BASE			0x01c19000
+#define A1X_ACE_BASE			0x01c1a000
+#define A1X_TVE1_BASE			0x01c1b000
+#define A1X_USB2_BASE			0x01c1c000
+#define A1X_CSI1_BASE			0x01c1d000
+#define A1X_TZASC_BASE			0x01c1e000
+#define A1X_SPI3_BASE			0x01c1f000
+
+#define A1X_CCM_BASE			0x01c20000
+#define A1X_INTC_BASE			0x01c20400
+#define A1X_PIO_BASE			0x01c20800
+#define A1X_TIMER_BASE			0x01c20c00
+#define A1X_SPDIF_BASE			0x01c21000
+#define A1X_AC97_BASE			0x01c21400
+#define A1X_IR0_BASE			0x01c21800
+#define A1X_IR1_BASE			0x01c21c00
+
+#define A1X_IIS_BASE			0x01c22400
+#define A1X_LRADC_BASE			0x01c22800
+#define A1X_AD_DA_BASE			0x01c22c00
+#define A1X_KEYPAD_BASE			0x01c23000
+#define A1X_TZPC_BASE			0x01c23400
+#define A1X_SID_BASE			0x01c23800
+#define A1X_SJTAG_BASE			0x01c23c00
+
+#define A1X_TP_BASE			0x01c25000
+#define A1X_PMU_BASE			0x01c25400
+#define A1X_CPUCFG_BASE			0x01c25c00	/* sun7i only ? */
+
+#define A1X_UART0_BASE			0x01c28000
+#define A1X_UART1_BASE			0x01c28400
+#define A1X_UART2_BASE			0x01c28800
+#define A1X_UART3_BASE			0x01c28c00
+#define A1X_UART4_BASE			0x01c29000
+#define A1X_UART5_BASE			0x01c29400
+#define A1X_UART6_BASE			0x01c29800
+#define A1X_UART7_BASE			0x01c29c00
+#define A1X_PS2_0_BASE			0x01c2a000
+#define A1X_PS2_1_BASE			0x01c2a400
+
+#define A1X_TWI0_BASE			0x01c2ac00
+#define A1X_TWI1_BASE			0x01c2b000
+#define A1X_TWI2_BASE			0x01c2b400
+
+#define A1X_CAN_BASE			0x01c2bc00
+
+#define A1X_SCR_BASE			0x01c2c400
+
+#define A1X_GPS_BASE			0x01c30000
+#define A1X_MALI400_BASE		0x01c40000
+
+/* module sram */
+#define A1X_SRAM_C_BASE			0x01d00000
+
+#define A1X_DE_FE0_BASE			0x01e00000
+#define A1X_DE_FE1_BASE			0x01e20000
+#define A1X_DE_BE0_BASE			0x01e60000
+#define A1X_DE_BE1_BASE			0x01e40000
+#define A1X_MP_BASE			0x01e80000
+#define A1X_AVG_BASE			0x01ea0000
+
+/* CoreSight Debug Module */
+#define A1X_CSDM_BASE			0x3f500000
+
+#define A1X_DRAM_BASE			0x40000000	/* 2 GiB */
+
+#define A1X_BROM_BASE			0xffff0000	/* 32 kiB */
+
+#define A1X_CPU_CFG			(A1X_TIMER_BASE + 0x13c)
+
+#endif				/* CPU_ALLWINNER_A10_MEMMAP_H */
diff --git a/src/cpu/allwinner/a10/monotonic_timer.c b/src/cpu/allwinner/a10/monotonic_timer.c
new file mode 100644
index 0000000..16d478a
--- /dev/null
+++ b/src/cpu/allwinner/a10/monotonic_timer.c
@@ -0,0 +1,13 @@
+/*
+ * Placeholder for code to come (needed to complete build)
+ *
+ * Copyright (C) 2013  Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Subject to the GNU GPL v2, or (at your option) any later version.
+ */
+
+#include <timer.h>
+
+void timer_monotonic_get(struct mono_time *mt)
+{
+	(void)mt;
+}
diff --git a/src/cpu/allwinner/a10/pinmux.c b/src/cpu/allwinner/a10/pinmux.c
new file mode 100644
index 0000000..2525de5
--- /dev/null
+++ b/src/cpu/allwinner/a10/pinmux.c
@@ -0,0 +1,30 @@
+/*
+ * Helpers to multiplex and configure pins on Allwinner SoCs
+ *
+ * Copyright (C) 2013  Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Subject to the GNU GPL v2, or (at your option) any later version.
+ */
+
+#include "gpio.h"
+
+#include <arch/io.h>
+
+static struct a10_gpio *const gpio = (void *)GPIO_BASE;
+
+void gpio_set_func(u8 port, u8 pin, u8 pad_func)
+{
+	u8 reg, bit;
+	u32 reg32;
+
+	if ((port > GPS))
+		return;
+
+	pin &= 0x1f;
+	reg = pin / 8;
+	bit = (pin % 8) * 4;
+
+	reg32 = read32(&gpio->port[port].cfg[reg]);
+	reg32 &= ~(0xf << bit);
+	reg32 |= (pad_func & 0xf) << bit;
+	write32(reg32, &gpio->port[port].cfg[reg]);
+}
diff --git a/src/cpu/allwinner/a10/timer.c b/src/cpu/allwinner/a10/timer.c
new file mode 100644
index 0000000..e156398
--- /dev/null
+++ b/src/cpu/allwinner/a10/timer.c
@@ -0,0 +1,19 @@
+/*
+ * Placeholder for code to come(needed to complete build)
+ *
+ * Copyright (C) 2013  Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Subject to the GNU GPL v2, or (at your option) any later version.
+ */
+
+#include <delay.h>
+#include <timer.h>
+
+void init_timer(void)
+{
+	/* Stub */
+}
+
+void udelay(unsigned usec)
+{
+	/* Stub */
+}
diff --git a/src/cpu/allwinner/a10/uart.c b/src/cpu/allwinner/a10/uart.c
new file mode 100644
index 0000000..dc98bff
--- /dev/null
+++ b/src/cpu/allwinner/a10/uart.c
@@ -0,0 +1,98 @@
+/*
+ * Uart setup helpers for Allwinner SoCs
+ *
+ * Copyright (C) 2013  Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Subject to the GNU GPL v2, or (at your option) any later version.
+ */
+
+#include "uart.h"
+#include <arch/io.h>
+
+/* Give me my 8250 UART definitions!!!! */
+/* TODO: Clean this up when uart8250mem works on ARM */
+#undef CONFIG_CONSOLE_SERIAL8250MEM
+#define CONFIG_CONSOLE_SERIAL8250MEM 1
+#include <uart8250.h>
+
+/**
+ * \brief Configure line control settings for UART
+ */
+void a10_uart_configure(void *uart_base, u32 baud_rate, u8 data_bits,
+			enum uart_parity parity, u8 stop_bits)
+{
+	u32 reg32;
+	u16 div;
+	struct a10_uart *uart = uart_base;
+
+	/* Enable access to Divisor Latch register */
+	write32(UART_LCR_DLAB, &uart->lcr);
+	/* Set baudrate */
+	/* FIXME: We assume clock is 24MHz, which may not be the case */
+	div = 24000000 / 16 / baud_rate;
+	write32((div >> 8) & 0xff, &uart->dlh);
+	write32(div & 0xff, &uart->dll);
+	/* Set line control */
+	reg32 = (data_bits - 5) & UART_LCR_WLS_MSK;
+	switch (parity) {
+	case UART_PARITY_ODD:
+		reg32 |= UART_LCR_PEN;
+		break;
+	case UART_PARITY_EVEN:
+		reg32 |= UART_LCR_PEN;
+		reg32 |= UART_LCR_EPS;
+		break;
+	case UART_PARITY_NONE:	/* Fall through */
+	default:
+		break;
+	}
+	write32(reg32, &uart->lcr);
+}
+
+void a10_uart_enable_fifos(void *uart_base)
+{
+	struct a10_uart *uart = uart_base;
+
+	write32(UART_FCR_FIFO_EN, &uart->fcr);
+}
+
+static int tx_fifo_full(struct a10_uart *uart)
+{
+	/* This may be a misnomer, or a typo in the datasheet. THRE indicates
+	 * that the TX register is empty, not that the FIFO is not full, but
+	 * this may be due to a datasheet typo. Keep the current name to signal
+	 * intent. */
+	return !(read32(&uart->lsr) & UART_LSR_THRE);
+}
+
+static int rx_fifo_empty(struct a10_uart *uart)
+{
+	return !(read32(&uart->lsr) & UART_LSR_DR);
+}
+
+/**
+ * \brief Read a single byte from the UART.
+ *
+ * Blocks until at least a byte is available.
+ */
+u8 a10_uart_rx_blocking(void *uart_base)
+{
+	struct a10_uart *uart = uart_base;
+
+	while (rx_fifo_empty(uart)) ;
+
+	return read32(&uart->rbr);
+}
+
+/**
+ * \brief Write a single byte to the UART.
+ *
+ * Blocks until there is space in the FIFO.
+ */
+void a10_uart_tx_blocking(void *uart_base, u8 data)
+{
+	struct a10_uart *uart = uart_base;
+
+	while (tx_fifo_full(uart)) ;
+
+	return write32(data, &uart->thr);
+}
diff --git a/src/cpu/allwinner/a10/uart.h b/src/cpu/allwinner/a10/uart.h
new file mode 100644
index 0000000..86ab487
--- /dev/null
+++ b/src/cpu/allwinner/a10/uart.h
@@ -0,0 +1,77 @@
+/*
+ * Definitions for UART on Allwinner CPUs
+ *
+ * The UART on the A10 seems to be 8250-compatible, however, this has not been
+ * verified. Our 8250mem code is specific to x86, and does not yet work, so we
+ * have to re-implement it ARM-style for the time being. The register
+ * definitions are present in <uart7250.h>, and are not redefined here.
+ *
+ * Copyright (C) 2013  Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Subject to the GNU GPL v2, or (at your option) any later version.
+ */
+
+#ifndef CPU_ALLWINNER_A10_UART_H
+#define CPU_ALLWINNER_A10_UART_H
+
+#include "memmap.h"
+#include <types.h>
+
+struct a10_uart {
+	union {
+		/* operational mode */
+		u32 rbr;	/* receiver buffer (read) */
+		u32 thr;	/* transmit holding (write) */
+		/* config mode (DLAB set) */
+		u32 dll;	/* divisor latches low */
+	};
+
+	union {
+		/* operational mode */
+		u32 ier;	/* interrupt enable */
+		/* config mode (DLAB set) */
+		u32 dlh;	/* divisor latches high */
+	};
+
+	union {
+		u32 iir;	/* interrupt ID (read) */
+		u32 fcr;	/* FIFO control (write) */
+	};
+
+	u32 lcr;		/* line control */
+
+	/* 0x10 */
+	u32 mcr;		/* modem control */
+	u32 lsr;		/* line status, read-only */
+	u32 msr;		/* modem status */
+	u32 sch;		/* scratch register */
+
+	u8 reserved_0x20[0x50];
+
+	/* 0x70 */
+	u8 reserved_0x70[0xc];
+	u32 usr;		/* UART status register */
+
+	/* 0x80 */
+	u32 tfl;		/* Transmit FIFO level */
+	u32 rfl;		/* Receive FIFO level */
+	u8 reserved_0x88[0x18];
+
+	/* 0xa0 */
+	u8 reserved_0xa0[4];
+	u32 halt;		/* Halt register */
+
+} __attribute__ ((packed));
+
+enum uart_parity {
+	UART_PARITY_NONE,
+	UART_PARITY_EVEN,
+	UART_PARITY_ODD,
+};
+
+void a10_uart_configure(void *uart_base, u32 baud_rate, u8 data_bits,
+			enum uart_parity parity, u8 stop_bits);
+void a10_uart_enable_fifos(void *uart_base);
+u8 a10_uart_rx_blocking(void *uart_base);
+void a10_uart_tx_blocking(void *uart_base, u8 data);
+
+#endif				/* CPU_ALLWINNER_A10_UART_H */
diff --git a/src/cpu/allwinner/a10/uart_console.c b/src/cpu/allwinner/a10/uart_console.c
new file mode 100644
index 0000000..58930dd
--- /dev/null
+++ b/src/cpu/allwinner/a10/uart_console.c
@@ -0,0 +1,109 @@
+/*
+ * Glue to UART code to enable serial console
+ *
+ * Copyright 2013 Google Inc.
+ * Copyright (C) 2013  Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ * Subject to the GNU GPL v2, or (at your option) any later version.
+ */
+
+#include <config.h>
+#include <types.h>
+#include <uart.h>
+#include <arch/io.h>
+
+#include <console/console.h>
+#include <cpu/allwinner/a10/uart.h>
+
+static void *get_console_uart_base_addr(void)
+{
+	/* This big block gets compiled to a constant, not a function call */
+	if (CONFIG_CONSOLE_SERIAL_UART0)
+		return (void *)A1X_UART0_BASE;
+	else if (CONFIG_CONSOLE_SERIAL_UART1)
+		return (void *)A1X_UART1_BASE;
+	else if (CONFIG_CONSOLE_SERIAL_UART2)
+		return (void *)A1X_UART2_BASE;
+	else if (CONFIG_CONSOLE_SERIAL_UART3)
+		return (void *)A1X_UART3_BASE;
+	else if (CONFIG_CONSOLE_SERIAL_UART4)
+		return (void *)A1X_UART4_BASE;
+	else if (CONFIG_CONSOLE_SERIAL_UART5)
+		return (void *)A1X_UART5_BASE;
+	else if (CONFIG_CONSOLE_SERIAL_UART6)
+		return (void *)A1X_UART6_BASE;
+	else if (CONFIG_CONSOLE_SERIAL_UART7)
+		return (void *)A1X_UART7_BASE;
+
+	/* If selection is invalid, default to UART0 */
+	return (void *)A1X_UART0_BASE;
+}
+
+static u32 get_console_uart_baud(void)
+{
+	if (CONFIG_CONSOLE_SERIAL_115200)
+		return 115200;
+	else if (CONFIG_CONSOLE_SERIAL_57600)
+		return 57600;
+	else if (CONFIG_CONSOLE_SERIAL_38400)
+		return 34800;
+	else if (CONFIG_CONSOLE_SERIAL_19200)
+		return 19200;
+	else if (CONFIG_CONSOLE_SERIAL_9600)
+		return 9600;
+
+	/* Default to 115200 if selection is invalid */
+	return 115200;
+}
+
+static void a10_uart_init_dev(void)
+{
+	void *uart_base = get_console_uart_base_addr();
+	/* Use default 8N1 encoding */
+	a10_uart_configure(uart_base, get_console_uart_baud(),
+			   8, UART_PARITY_NONE, 1);
+	a10_uart_enable_fifos(uart_base);
+}
+
+static unsigned char a10_uart_rx_byte(void)
+{
+	return a10_uart_rx_blocking(get_console_uart_base_addr());
+}
+
+static void a10_uart_tx_byte(unsigned char data)
+{
+	a10_uart_tx_blocking(get_console_uart_base_addr(), data);
+}
+
+uint32_t uartmem_getbaseaddr(void)
+{
+	return (uint32_t) get_console_uart_base_addr();
+}
+
+#if !defined(__PRE_RAM__)
+static const struct console_driver a10_uart_console __console = {
+	.init = a10_uart_init_dev,
+	.tx_byte = a10_uart_tx_byte,
+	.rx_byte = a10_uart_rx_byte,
+};
+#else
+
+void uart_init(void)
+{
+	a10_uart_init_dev();
+}
+
+unsigned char uart_rx_byte(void)
+{
+	return a10_uart_rx_byte();
+}
+
+void uart_tx_byte(unsigned char data)
+{
+	a10_uart_tx_byte(data);
+}
+
+void uart_tx_flush(void)
+{
+}
+
+#endif