From: Icenowy Zheng <icen...@aosc.io>

Add support for F1C100s internal dram controller.

Signed-off-by: Icenowy Zheng <icen...@aosc.io>
Signed-off-by: Jesse Taube <mr.bossman...@gmail.com>
---
V1->V2:
* Nothing done
---
 arch/arm/include/asm/arch-sunxi/dram.h       |   2 +
 arch/arm/include/asm/arch-sunxi/dram_suniv.h |  46 ++
 arch/arm/mach-sunxi/Makefile                 |   2 +
 arch/arm/mach-sunxi/dram_helpers.c           |   4 +
 arch/arm/mach-sunxi/dram_suniv.c             | 420 +++++++++++++++++++
 5 files changed, 474 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-sunxi/dram_suniv.h
 create mode 100644 arch/arm/mach-sunxi/dram_suniv.c

diff --git a/arch/arm/include/asm/arch-sunxi/dram.h 
b/arch/arm/include/asm/arch-sunxi/dram.h
index c3b3e1f512..682daae6b1 100644
--- a/arch/arm/include/asm/arch-sunxi/dram.h
+++ b/arch/arm/include/asm/arch-sunxi/dram.h
@@ -31,6 +31,8 @@
 #include <asm/arch/dram_sun50i_h6.h>
 #elif defined(CONFIG_MACH_SUN50I_H616)
 #include <asm/arch/dram_sun50i_h616.h>
+#elif defined(CONFIG_MACH_SUNIV)
+#include <asm/arch/dram_suniv.h>
 #else
 #include <asm/arch/dram_sun4i.h>
 #endif
diff --git a/arch/arm/include/asm/arch-sunxi/dram_suniv.h 
b/arch/arm/include/asm/arch-sunxi/dram_suniv.h
new file mode 100644
index 0000000000..6f4c0512d6
--- /dev/null
+++ b/arch/arm/include/asm/arch-sunxi/dram_suniv.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * suniv DRAM controller register definition
+ *
+ * Copyright (C) 2018 Icenowy Zheng <icen...@aosc.io>
+ *
+ * Based on xboot's arch/arm32/mach-f1c100s/sys-dram.c, which is:
+ *
+ * Copyright(c) 2007-2018 Jianjun Jiang <8192...@qq.com>
+ */
+
+#define PIO_SDRAM_DRV                  (0x2c0)
+#define PIO_SDRAM_PULL                 (0x2c4)
+
+#define DRAM_SCONR                     (0x00)
+#define DRAM_STMG0R                    (0x04)
+#define DRAM_STMG1R                    (0x08)
+#define DRAM_SCTLR                     (0x0c)
+#define DRAM_SREFR                     (0x10)
+#define DRAM_SEXTMR                    (0x14)
+#define DRAM_DDLYR                     (0x24)
+#define DRAM_DADRR                     (0x28)
+#define DRAM_DVALR                     (0x2c)
+#define DRAM_DRPTR0                    (0x30)
+#define DRAM_DRPTR1                    (0x34)
+#define DRAM_DRPTR2                    (0x38)
+#define DRAM_DRPTR3                    (0x3c)
+#define DRAM_SEFR                      (0x40)
+#define DRAM_MAE                       (0x44)
+#define DRAM_ASPR                      (0x48)
+#define DRAM_SDLY0                     (0x4C)
+#define DRAM_SDLY1                     (0x50)
+#define DRAM_SDLY2                     (0x54)
+#define DRAM_MCR0                      (0x100)
+#define DRAM_MCR1                      (0x104)
+#define DRAM_MCR2                      (0x108)
+#define DRAM_MCR3                      (0x10c)
+#define DRAM_MCR4                      (0x110)
+#define DRAM_MCR5                      (0x114)
+#define DRAM_MCR6                      (0x118)
+#define DRAM_MCR7                      (0x11c)
+#define DRAM_MCR8                      (0x120)
+#define DRAM_MCR9                      (0x124)
+#define DRAM_MCR10                     (0x128)
+#define DRAM_MCR11                     (0x12c)
+#define DRAM_BWCR                      (0x140)
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index b1adb75e17..58f807cb82 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -13,6 +13,7 @@ obj-y += dram_helpers.o
 obj-y  += pinmux.o
 obj-$(CONFIG_SUN6I_PRCM)       += prcm.o
 obj-$(CONFIG_AXP_PMIC_BUS)     += pmic_bus.o
+obj-$(CONFIG_MACH_SUNIV)       += clock_sun6i.o
 obj-$(CONFIG_MACH_SUN4I)       += clock_sun4i.o
 obj-$(CONFIG_MACH_SUN5I)       += clock_sun4i.o
 obj-$(CONFIG_MACH_SUN6I)       += clock_sun6i.o
@@ -30,6 +31,7 @@ obj-y += timer.o
 endif
 
 ifdef CONFIG_SPL_BUILD
+obj-$(CONFIG_MACH_SUNIV)       += dram_suniv.o
 obj-$(CONFIG_DRAM_SUN4I)       += dram_sun4i.o
 obj-$(CONFIG_DRAM_SUN6I)       += dram_sun6i.o
 obj-$(CONFIG_DRAM_SUN8I_A23)   += dram_sun8i_a23.o
diff --git a/arch/arm/mach-sunxi/dram_helpers.c 
b/arch/arm/mach-sunxi/dram_helpers.c
index 520b597fcc..2c873192e6 100644
--- a/arch/arm/mach-sunxi/dram_helpers.c
+++ b/arch/arm/mach-sunxi/dram_helpers.c
@@ -26,7 +26,10 @@ void mctl_await_completion(u32 *reg, u32 mask, u32 val)
 
 /*
  * Test if memory at offset offset matches memory at begin of DRAM
+ *
+ * Note: dsb() is not available on ARMv5 in Thumb mode
  */
+#ifndef CONFIG_MACH_SUNIV
 bool mctl_mem_matches(u32 offset)
 {
        /* Try to write different values to RAM at two addresses */
@@ -37,3 +40,4 @@ bool mctl_mem_matches(u32 offset)
        return readl(CONFIG_SYS_SDRAM_BASE) ==
               readl((ulong)CONFIG_SYS_SDRAM_BASE + offset);
 }
+#endif
diff --git a/arch/arm/mach-sunxi/dram_suniv.c b/arch/arm/mach-sunxi/dram_suniv.c
new file mode 100644
index 0000000000..56c2d557ff
--- /dev/null
+++ b/arch/arm/mach-sunxi/dram_suniv.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: (GPL-2.0+)
+/*
+ * suniv DRAM initialization
+ *
+ * Copyright (C) 2018 Icenowy Zheng <icen...@aosc.io>
+ *
+ * Based on xboot's arch/arm32/mach-f1c100s/sys-dram.c, which is:
+ *
+ * Copyright(c) 2007-2018 Jianjun Jiang <8192...@qq.com>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/dram.h>
+#include <asm/arch/gpio.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <hang.h>
+
+#define SDR_T_CAS                      (0x2)
+#define SDR_T_RAS                      (0x8)
+#define SDR_T_RCD                      (0x3)
+#define SDR_T_RP                       (0x3)
+#define SDR_T_WR                       (0x3)
+#define SDR_T_RFC                      (0xd)
+#define SDR_T_XSR                      (0xf9)
+#define SDR_T_RC                       (0xb)
+#define SDR_T_INIT                     (0x8)
+#define SDR_T_INIT_REF                 (0x7)
+#define SDR_T_WTR                      (0x2)
+#define SDR_T_RRD                      (0x2)
+#define SDR_T_XP                       (0x0)
+
+enum dram_type {
+       DRAM_TYPE_SDR   = 0,
+       DRAM_TYPE_DDR   = 1,
+       /* Not supported yet. */
+       DRAM_TYPE_MDDR  = 2,
+};
+
+struct dram_para {
+       u32 size;               /* dram size (unit: MByte) */
+       u32 clk;                /* dram work clock (unit: MHz) */
+       u32 access_mode;        /* 0: interleave mode 1: sequence mode */
+       u32 cs_num;             /* dram chip count  1: one chip  2: two chip */
+       u32 ddr8_remap;         /* for 8bits data width DDR 0: normal  1: 8bits 
*/
+       enum dram_type sdr_ddr;
+       u32 bwidth;             /* dram bus width */
+       u32 col_width;          /* column address width */
+       u32 row_width;          /* row address width */
+       u32 bank_size;          /* dram bank count */
+       u32 cas;                /* dram cas */
+};
+
+struct dram_para suniv_dram_para = {
+       .size = 32,
+       .clk = 156,
+       .access_mode = 1,
+       .cs_num = 1,
+       .ddr8_remap = 0,
+       .sdr_ddr = DRAM_TYPE_DDR,
+       .bwidth = 16,
+       .col_width = 10,
+       .row_width = 13,
+       .bank_size = 4,
+       .cas = 0x3,
+};
+
+static int dram_initial(void)
+{
+       unsigned int time = 0xffffff;
+
+       setbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR, 0x1);
+       while ((readl(SUNXI_DRAMC_BASE + DRAM_SCTLR) & 0x1) && time--) {
+               if (time == 0)
+                       return 0;
+       }
+       return 1;
+}
+
+static int dram_delay_scan(void)
+{
+       unsigned int time = 0xffffff;
+
+       setbits_le32(SUNXI_DRAMC_BASE + DRAM_DDLYR, 0x1);
+       while ((readl(SUNXI_DRAMC_BASE + DRAM_DDLYR) & 0x1) && time--) {
+               if (time == 0)
+                       return 0;
+       }
+       return 1;
+}
+
+static void dram_set_autofresh_cycle(u32 clk)
+{
+       u32 val = 0;
+       u32 row = 0;
+       u32 temp = 0;
+
+       row = readl(SUNXI_DRAMC_BASE + DRAM_SCONR);
+       row &= 0x1e0;
+       row >>= 0x5;
+
+       if (row == 0xc) {
+               if (clk >= 1000000) {
+                       temp = clk + (clk >> 3) + (clk >> 4) + (clk >> 5);
+                       while (temp >= (10000000 >> 6)) {
+                               temp -= (10000000 >> 6);
+                               val++;
+                       }
+               } else {
+                       val = (clk * 499) >> 6;
+               }
+       } else if (row == 0xb) {
+               if (clk >= 1000000) {
+                       temp = clk + (clk >> 3) + (clk >> 4) + (clk >> 5);
+                       while (temp >= (10000000 >> 7)) {
+                               temp -= (10000000 >> 7);
+                               val++;
+                       }
+               } else {
+                       val = (clk * 499) >> 5;
+               }
+       }
+       writel(val, SUNXI_DRAMC_BASE + DRAM_SREFR);
+}
+
+static int dram_para_setup(struct dram_para *para)
+{
+       u32 val = 0;
+
+       val = (para->ddr8_remap) | (0x1 << 1) |
+             ((para->bank_size >> 2) << 3) |
+             ((para->cs_num >> 1) << 4) |
+             ((para->row_width - 1) << 5) |
+             ((para->col_width - 1) << 9) |
+             ((para->sdr_ddr ? (para->bwidth >> 4) : (para->bwidth >> 5)) << 
13) |
+             (para->access_mode << 15) |
+             (para->sdr_ddr << 16);
+
+       writel(val, SUNXI_DRAMC_BASE + DRAM_SCONR);
+       setbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR, 0x1 << 19);
+       return dram_initial();
+}
+
+static u32 dram_check_delay(u32 bwidth)
+{
+       u32 dsize;
+       int i, j;
+       u32 num = 0;
+       u32 dflag = 0;
+
+       dsize = ((bwidth == 16) ? 4 : 2);
+       for (i = 0; i < dsize; i++) {
+               if (i == 0)
+                       dflag = readl(SUNXI_DRAMC_BASE + DRAM_DRPTR0);
+               else if (i == 1)
+                       dflag = readl(SUNXI_DRAMC_BASE + DRAM_DRPTR1);
+               else if (i == 2)
+                       dflag = readl(SUNXI_DRAMC_BASE + DRAM_DRPTR2);
+               else if (i == 3)
+                       dflag = readl(SUNXI_DRAMC_BASE + DRAM_DRPTR3);
+
+               for (j = 0; j < 32; j++) {
+                       if (dflag & 0x1)
+                               num++;
+                       dflag >>= 1;
+               }
+       }
+       return num;
+}
+
+static int sdr_readpipe_scan(void)
+{
+       u32 k = 0;
+
+       for (k = 0; k < 32; k++)
+               writel(k, CONFIG_SYS_SDRAM_BASE + 4 * k);
+       for (k = 0; k < 32; k++) {
+               if (readl(CONFIG_SYS_SDRAM_BASE + 4 * k) != k)
+                       return 0;
+       }
+       return 1;
+}
+
+static u32 sdr_readpipe_select(void)
+{
+       u32 value = 0;
+       u32 i = 0;
+
+       for (i = 0; i < 8; i++) {
+               clrsetbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR,
+                               0x7 << 6, i << 6);
+               if (sdr_readpipe_scan()) {
+                       value = i;
+                       return value;
+               }
+       }
+       return value;
+}
+
+static u32 dram_check_type(struct dram_para *para)
+{
+       u32 times = 0;
+       int i;
+
+       for (i = 0; i < 8; i++) {
+               clrsetbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR,
+                               0x7 << 6, i << 6);
+               dram_delay_scan();
+               if (readl(SUNXI_DRAMC_BASE + DRAM_DDLYR) & 0x30)
+                       times++;
+       }
+
+       if (times == 8) {
+               para->sdr_ddr = DRAM_TYPE_SDR;
+               return 0;
+       }
+       para->sdr_ddr = DRAM_TYPE_DDR;
+       return 1;
+}
+
+static u32 dram_scan_readpipe(struct dram_para *para)
+{
+       u32 rp_best = 0, rp_val = 0;
+       u32 readpipe[8];
+       int i;
+
+       if (para->sdr_ddr == DRAM_TYPE_DDR) {
+               for (i = 0; i < 8; i++) {
+                       clrsetbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR,
+                                       0x7 << 6, i << 6);
+                       dram_delay_scan();
+                       readpipe[i] = 0;
+                       if ((((readl(SUNXI_DRAMC_BASE + DRAM_DDLYR) >> 4) & 
0x3) == 0x0) &&
+                           (((readl(SUNXI_DRAMC_BASE + DRAM_DDLYR) >> 4) & 
0x1) == 0x0))
+                               readpipe[i] = dram_check_delay(para->bwidth);
+                       if (rp_val < readpipe[i]) {
+                               rp_val = readpipe[i];
+                               rp_best = i;
+                       }
+               }
+               clrsetbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR,
+                               0x7 << 6, rp_best << 6);
+               dram_delay_scan();
+       } else {
+               clrbits_le32(SUNXI_DRAMC_BASE + DRAM_SCONR,
+                            (0x1 << 16) | (0x3 << 13));
+               rp_best = sdr_readpipe_select();
+               clrsetbits_le32(SUNXI_DRAMC_BASE + DRAM_SCTLR,
+                               0x7 << 6, rp_best << 6);
+       }
+       return 0;
+}
+
+static u32 dram_get_dram_size(struct dram_para *para)
+{
+       u32 colflag = 10, rowflag = 13;
+       u32 val1 = 0;
+       u32 count = 0;
+       u32 addr1, addr2;
+       int i;
+
+       para->col_width = colflag;
+       para->row_width = rowflag;
+       dram_para_setup(para);
+       dram_scan_readpipe(para);
+       for (i = 0; i < 32; i++) {
+               *((u8 *)(CONFIG_SYS_SDRAM_BASE + 0x200 + i)) = 0x11;
+               *((u8 *)(CONFIG_SYS_SDRAM_BASE + 0x600 + i)) = 0x22;
+       }
+       for (i = 0; i < 32; i++) {
+               val1 = *((u8 *)(CONFIG_SYS_SDRAM_BASE + 0x200 + i));
+               if (val1 == 0x22)
+                       count++;
+       }
+       if (count == 32)
+               colflag = 9;
+       else
+               colflag = 10;
+       count = 0;
+       para->col_width = colflag;
+       para->row_width = rowflag;
+       dram_para_setup(para);
+       if (colflag == 10) {
+               addr1 = CONFIG_SYS_SDRAM_BASE + 0x400000;
+               addr2 = CONFIG_SYS_SDRAM_BASE + 0xc00000;
+       } else {
+               addr1 = CONFIG_SYS_SDRAM_BASE + 0x200000;
+               addr2 = CONFIG_SYS_SDRAM_BASE + 0x600000;
+       }
+       for (i = 0; i < 32; i++) {
+               *((u8 *)(addr1 + i)) = 0x33;
+               *((u8 *)(addr2 + i)) = 0x44;
+       }
+       for (i = 0; i < 32; i++) {
+               val1 = *((u8 *)(addr1 + i));
+               if (val1 == 0x44)
+                       count++;
+       }
+       if (count == 32)
+               rowflag = 12;
+       else
+               rowflag = 13;
+       para->col_width = colflag;
+       para->row_width = rowflag;
+       if (para->row_width != 13)
+               para->size = 16;
+       else if (para->col_width == 10)
+               para->size = 64;
+       else
+               para->size = 32;
+       dram_set_autofresh_cycle(para->clk);
+       para->access_mode = 0;
+       dram_para_setup(para);
+
+       return 0;
+}
+
+static void simple_dram_check(void)
+{
+       volatile u32 *dram = (u32 *)CONFIG_SYS_SDRAM_BASE;
+       int i;
+
+       for (i = 0; i < 0x40; i++)
+               dram[i] = i;
+
+       for (i = 0; i < 0x40; i++) {
+               if (dram[i] != i) {
+                       printf("DRAM initialization failed: dram[0x%x] != 
0x%x.", i, dram[i]);
+                       hang();
+               }
+       }
+
+       for (i = 0; i < 0x10000; i += 0x40)
+               dram[i] = i;
+
+       for (i = 0; i < 0x10000; i += 0x40) {
+               if (dram[i] != i) {
+                       printf("DRAM initialization failed: dram[0x%x] != 
0x%x.", i, dram[i]);
+                       hang();
+               }
+       }
+}
+
+static void do_dram_init(struct dram_para *para)
+{
+       struct sunxi_ccm_reg * const ccm =
+               (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+       u32 val;
+       u8 m; /* PLL_DDR clock factor */
+
+       sunxi_gpio_set_cfgpin(SUNXI_GPB(3), 0x7);
+       mdelay(5);
+       /* TODO: dig out what's them... some analog register? */
+       if ((para->cas >> 3) & 0x1)
+               setbits_le32(SUNXI_PIO_BASE + 0x2c4, (0x1 << 23) | (0x20 << 
17));
+
+       if (para->clk >= 144 && para->clk <= 180)
+               writel(0xaaa, SUNXI_PIO_BASE + 0x2c0);
+       if (para->clk >= 180)
+               writel(0xfff, SUNXI_PIO_BASE + 0x2c0);
+
+       if (para->cas & BIT(4))
+               writel(0xd1303333, &ccm->pll5_pattern_cfg);
+       else if (para->cas & BIT(5))
+               writel(0xcce06666, &ccm->pll5_pattern_cfg);
+       else if (para->cas & BIT(6))
+               writel(0xc8909999, &ccm->pll5_pattern_cfg);
+       else if (para->cas & BIT(7))
+               writel(0xc440cccc, &ccm->pll5_pattern_cfg);
+
+       if (para->clk <= 96)
+               m = 2;
+       else
+               m = 1;
+
+       val = CCM_PLL5_CTRL_EN | CCM_PLL5_CTRL_UPD |
+             CCM_PLL5_CTRL_N((para->clk * 2) / (24 / m)) |
+             CCM_PLL5_CTRL_K(1) | CCM_PLL5_CTRL_M(m);
+       if (para->cas & GENMASK(7, 4))
+               val |= CCM_PLL5_CTRL_SIGMA_DELTA_EN;
+       writel(val, &ccm->pll5_cfg);
+       setbits_le32(&ccm->pll5_cfg, CCM_PLL5_CTRL_UPD);
+       mctl_await_completion(&ccm->pll5_cfg, BIT(28), BIT(28));
+       mdelay(5);
+
+       setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_MCTL));
+       clrbits_le32(&ccm->ahb_reset0_cfg, (1 << AHB_RESET_OFFSET_MCTL));
+       udelay(50);
+       setbits_le32(&ccm->ahb_reset0_cfg, (1 << AHB_RESET_OFFSET_MCTL));
+
+       clrsetbits_le32(SUNXI_PIO_BASE + 0x2c4, (1 << 16),
+                       ((para->sdr_ddr == DRAM_TYPE_DDR) << 16));
+
+       val = (SDR_T_CAS << 0) | (SDR_T_RAS << 3) | (SDR_T_RCD << 7) |
+             (SDR_T_RP << 10) | (SDR_T_WR << 13) | (SDR_T_RFC << 15) |
+             (SDR_T_XSR << 19) | (SDR_T_RC << 28);
+       writel(val, SUNXI_DRAMC_BASE + DRAM_STMG0R);
+       val = (SDR_T_INIT << 0) | (SDR_T_INIT_REF << 16) | (SDR_T_WTR << 20) |
+             (SDR_T_RRD << 22) | (SDR_T_XP << 25);
+       writel(val, SUNXI_DRAMC_BASE + DRAM_STMG1R);
+       dram_para_setup(para);
+       dram_check_type(para);
+
+       clrsetbits_le32(SUNXI_PIO_BASE + 0x2c4, (1 << 16),
+                       ((para->sdr_ddr == DRAM_TYPE_DDR) << 16));
+
+       dram_set_autofresh_cycle(para->clk);
+       dram_scan_readpipe(para);
+       dram_get_dram_size(para);
+       simple_dram_check();
+}
+
+unsigned long sunxi_dram_init(void)
+{
+       do_dram_init(&suniv_dram_para);
+
+       return suniv_dram_para.size * 1024 * 1024;
+}
-- 
2.34.1

Reply via email to