sunxi: Add sun4i support

Add support for the Allwinner A10 SoC also known as the Allwinner sun4i family,
and add the Cubieboard board which uses the A10 SoC.

Compared to sun7 only the DRAM controller is a bit different:
-Controller reset bits are inverted, but only for Rev. A
-Different hpcr values
-No MBUS on sun4i
-Various other initialization changes

Signed-off-by: Henrik Nordstrom <henrik@henriknordstrom.net>
Signed-off-by: Stefan Roese <sr@denx.de>
Signed-off-by: Oliver Schinagl <oliver@schinagl.nl>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Ian Campbell <ijc@hellion.org.uk>
diff --git a/arch/arm/cpu/armv7/sunxi/Makefile b/arch/arm/cpu/armv7/sunxi/Makefile
index a64bfa1..856d353 100644
--- a/arch/arm/cpu/armv7/sunxi/Makefile
+++ b/arch/arm/cpu/armv7/sunxi/Makefile
@@ -11,6 +11,7 @@
 obj-y	+= board.o
 obj-y	+= clock.o
 obj-y	+= pinmux.o
+obj-$(CONFIG_SUN4I)	+= clock_sun4i.o
 obj-$(CONFIG_SUN7I)	+= clock_sun4i.o
 
 ifndef CONFIG_SPL_BUILD
@@ -18,6 +19,7 @@
 endif
 
 ifdef CONFIG_SPL_BUILD
+obj-$(CONFIG_SUN4I)	+= dram.o
 obj-$(CONFIG_SUN7I)	+= dram.o
 ifdef CONFIG_SPL_FEL
 obj-y	+= start.o
diff --git a/arch/arm/cpu/armv7/sunxi/cpu_info.c b/arch/arm/cpu/armv7/sunxi/cpu_info.c
index b4c3d5c..b4b5089 100644
--- a/arch/arm/cpu/armv7/sunxi/cpu_info.c
+++ b/arch/arm/cpu/armv7/sunxi/cpu_info.c
@@ -13,7 +13,14 @@
 #ifdef CONFIG_DISPLAY_CPUINFO
 int print_cpuinfo(void)
 {
+#ifdef CONFIG_SUN4I
+	puts("CPU:   Allwinner A10 (SUN4I)\n");
+#elif defined CONFIG_SUN7I
 	puts("CPU:   Allwinner A20 (SUN7I)\n");
+#else
+#warning Please update cpu_info.c with correct CPU information
+	puts("CPU:   SUNXI Family\n");
+#endif
 	return 0;
 }
 #endif
diff --git a/arch/arm/cpu/armv7/sunxi/dram.c b/arch/arm/cpu/armv7/sunxi/dram.c
index b43c4b4..1de7529 100644
--- a/arch/arm/cpu/armv7/sunxi/dram.c
+++ b/arch/arm/cpu/armv7/sunxi/dram.c
@@ -53,16 +53,37 @@
 	struct sunxi_dram_reg *dram =
 			(struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
 
-	clrbits_le32(&dram->mcr, DRAM_MCR_RESET);
-	udelay(2);
-	setbits_le32(&dram->mcr, DRAM_MCR_RESET);
+#ifdef CONFIG_SUN4I
+	struct sunxi_timer_reg *timer =
+			(struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
+	u32 reg_val;
+
+	writel(0, &timer->cpu_cfg);
+	reg_val = readl(&timer->cpu_cfg);
+
+	if ((reg_val & CPU_CFG_CHIP_VER_MASK) !=
+	    CPU_CFG_CHIP_VER(CPU_CFG_CHIP_REV_A)) {
+		setbits_le32(&dram->mcr, DRAM_MCR_RESET);
+		udelay(2);
+		clrbits_le32(&dram->mcr, DRAM_MCR_RESET);
+	} else
+#endif
+	{
+		clrbits_le32(&dram->mcr, DRAM_MCR_RESET);
+		udelay(2);
+		setbits_le32(&dram->mcr, DRAM_MCR_RESET);
+	}
 }
 
 static void mctl_set_drive(void)
 {
 	struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE;
 
+#ifdef CONFIG_SUN7I
 	clrsetbits_le32(&dram->mcr, DRAM_MCR_MODE_NORM(0x3) | (0x3 << 28),
+#else
+	clrsetbits_le32(&dram->mcr, DRAM_MCR_MODE_NORM(0x3),
+#endif
 			DRAM_MCR_MODE_EN(0x3) |
 			0xffc);
 }
@@ -134,6 +155,16 @@
 }
 
 static u32 hpcr_value[32] = {
+#ifdef CONFIG_SUN4I
+	0x0301, 0x0301, 0x0301, 0x0301,
+	0x0301, 0x0301, 0, 0,
+	0, 0, 0, 0,
+	0, 0, 0, 0,
+	0x1031, 0x1031, 0x0735, 0x5031,
+	0x1035, 0x0731, 0x1031, 0x0735,
+	0x1035, 0x1031, 0x0731, 0x1035,
+	0x1031, 0x0301, 0x0301, 0x0731
+#endif
 #ifdef CONFIG_SUN7I
 	0x0301, 0x0301, 0x0301, 0x0301,
 	0x0301, 0x0301, 0x0301, 0x0301,
@@ -223,22 +254,32 @@
 	clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_GPS);
 #endif
 
+#if defined(CONFIG_SUN5I) || defined(CONFIG_SUN7I)
 	/* setup MBUS clock */
 	reg_val = CCM_MBUS_CTRL_GATE |
 		  CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL6) |
 		  CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(2)) |
 		  CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(2));
 	writel(reg_val, &ccm->mbus_clk_cfg);
+#endif
 
 	/*
 	 * open DRAMC AHB & DLL register clock
 	 * close it first
 	 */
+#if defined(CONFIG_SUN5I) || defined(CONFIG_SUN7I)
 	clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM | CCM_AHB_GATE_DLL);
+#else
+	clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM);
+#endif
 	udelay(22);
 
 	/* then open it */
+#if defined(CONFIG_SUN5I) || defined(CONFIG_SUN7I)
 	setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM | CCM_AHB_GATE_DLL);
+#else
+	setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SDRAM);
+#endif
 	udelay(22);
 }
 
@@ -385,6 +426,13 @@
 	else
 		clrbits_le32(&dram->mcr, DRAM_MCR_DCLK_OUT);
 #endif
+#ifdef CONFIG_SUN4I
+	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	if (on)
+		setbits_le32(&ccm->dram_clk_cfg, CCM_DRAM_CTRL_DCLK_OUT);
+	else
+		clrbits_le32(&ccm->dram_clk_cfg, CCM_DRAM_CTRL_DCLK_OUT);
+#endif
 }
 
 static const u16 tRFC_table[2][6] = {
@@ -421,11 +469,19 @@
 	mctl_setup_dram_clock(para->clock);
 
 	/* reset external DRAM */
+#ifndef CONFIG_SUN7I
+	mctl_ddr3_reset();
+#endif
 	mctl_set_drive();
 
 	/* dram clock off */
 	dramc_clock_output_en(0);
 
+#ifdef CONFIG_SUN4I
+	/* select dram controller 1 */
+	writel(DRAM_CSEL_MAGIC, &dram->csel);
+#endif
+
 	mctl_itm_disable();
 	mctl_enable_dll0(para->tpr3);
 
@@ -482,6 +538,9 @@
 		mctl_ddr3_reset();
 	else
 		setbits_le32(&dram->mcr, DRAM_MCR_RESET);
+#else
+	/* dram clock on */
+	dramc_clock_output_en(1);
 #endif
 
 	udelay(1);
@@ -490,6 +549,22 @@
 
 	mctl_enable_dllx(para->tpr3);
 
+#ifdef CONFIG_SUN4I
+	/* set odt impedance divide ratio */
+	reg_val = ((para->zq) >> 8) & 0xfffff;
+	reg_val |= ((para->zq) & 0xff) << 20;
+	reg_val |= (para->zq) & 0xf0000000;
+	writel(reg_val, &dram->zqcr0);
+#endif
+
+#ifdef CONFIG_SUN4I
+	/* set I/O configure register */
+	reg_val = 0x00cc0000;
+	reg_val |= (para->odt_en) & 0x3;
+	reg_val |= ((para->odt_en) & 0x3) << 30;
+	writel(reg_val, &dram->iocr);
+#endif
+
 	/* set refresh period */
 	dramc_set_autorefresh_cycle(para->clock, para->type - 2, density);