|
@@ -0,0 +1,1943 @@
|
|
|
+From 9f612f3a1fd3d0759abca3720d488a17d159aa17 Mon Sep 17 00:00:00 2001
|
|
|
+From: Samuel Holland <[email protected]>
|
|
|
+Date: Sun, 30 Oct 2022 14:54:08 -0500
|
|
|
+Subject: [PATCH 83/90] ram: sunxi: Add Allwinner D1 DRAM driver
|
|
|
+
|
|
|
+Signed-off-by: Samuel Holland <[email protected]>
|
|
|
+---
|
|
|
+ drivers/ram/Kconfig | 1 +
|
|
|
+ drivers/ram/Makefile | 1 +
|
|
|
+ drivers/ram/sunxi/Kconfig | 6 +
|
|
|
+ drivers/ram/sunxi/Makefile | 3 +
|
|
|
+ drivers/ram/sunxi/dram_v2.h | 65 +
|
|
|
+ drivers/ram/sunxi/mctl_hal-sun20iw1p1.c | 1771 +++++++++++++++++++++++
|
|
|
+ drivers/ram/sunxi/sdram.h | 46 +
|
|
|
+ 7 files changed, 1893 insertions(+)
|
|
|
+ create mode 100644 drivers/ram/sunxi/Kconfig
|
|
|
+ create mode 100644 drivers/ram/sunxi/Makefile
|
|
|
+ create mode 100644 drivers/ram/sunxi/dram_v2.h
|
|
|
+ create mode 100644 drivers/ram/sunxi/mctl_hal-sun20iw1p1.c
|
|
|
+ create mode 100644 drivers/ram/sunxi/sdram.h
|
|
|
+
|
|
|
+--- a/drivers/ram/Kconfig
|
|
|
++++ b/drivers/ram/Kconfig
|
|
|
+@@ -101,3 +101,4 @@ source "drivers/ram/rockchip/Kconfig"
|
|
|
+ source "drivers/ram/sifive/Kconfig"
|
|
|
+ source "drivers/ram/stm32mp1/Kconfig"
|
|
|
+ source "drivers/ram/octeon/Kconfig"
|
|
|
++source "drivers/ram/sunxi/Kconfig"
|
|
|
+--- a/drivers/ram/Makefile
|
|
|
++++ b/drivers/ram/Makefile
|
|
|
+@@ -20,5 +20,6 @@ obj-$(CONFIG_K3_DDRSS) += k3-ddrss/
|
|
|
+ obj-$(CONFIG_IMXRT_SDRAM) += imxrt_sdram.o
|
|
|
+
|
|
|
+ obj-$(CONFIG_RAM_SIFIVE) += sifive/
|
|
|
++obj-$(CONFIG_RAM_SUNXI) += sunxi/
|
|
|
+
|
|
|
+ obj-$(CONFIG_ARCH_OCTEON) += octeon/
|
|
|
+--- /dev/null
|
|
|
++++ b/drivers/ram/sunxi/Kconfig
|
|
|
+@@ -0,0 +1,6 @@
|
|
|
++config RAM_SUNXI
|
|
|
++ bool "Ram drivers support for sunxi SoCs"
|
|
|
++ depends on RAM && BOARD_SUNXI
|
|
|
++ default y
|
|
|
++ help
|
|
|
++ This enables support for ram drivers of sunxi SoCs.
|
|
|
+--- /dev/null
|
|
|
++++ b/drivers/ram/sunxi/Makefile
|
|
|
+@@ -0,0 +1,3 @@
|
|
|
++# SPDX-License-Identifier: GPL-2.0+
|
|
|
++
|
|
|
++obj-$(CONFIG_RAM_SUNXI) += mctl_hal-sun20iw1p1.o
|
|
|
+--- /dev/null
|
|
|
++++ b/drivers/ram/sunxi/dram_v2.h
|
|
|
+@@ -0,0 +1,65 @@
|
|
|
++/*
|
|
|
++ * (C) Copyright 2007-2013
|
|
|
++* SPDX-License-Identifier: GPL-2.0+
|
|
|
++ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
|
|
++ * Jerry Wang <[email protected]>
|
|
|
++ *
|
|
|
++ * See file CREDITS for list of people who contributed to this
|
|
|
++ * project.
|
|
|
++ *
|
|
|
++ * This program is free software; you can redistribute it and/or
|
|
|
++ * modify it under the terms of the GNU General Public License as
|
|
|
++ * published by the Free Software Foundation; either version 2 of
|
|
|
++ * the License, or (at your option) any later version.
|
|
|
++ *
|
|
|
++ * This program is distributed in the hope that it will be useful,
|
|
|
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
++ * GNU General Public License for more details.
|
|
|
++ *
|
|
|
++ * You should have received a copy of the GNU General Public License
|
|
|
++ * along with this program; if not, write to the Free Software
|
|
|
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
|
++ * MA 02111-1307 USA
|
|
|
++ */
|
|
|
++
|
|
|
++#ifndef __dram_head_h__
|
|
|
++#define __dram_head_h__
|
|
|
++
|
|
|
++struct dram_para_t
|
|
|
++{
|
|
|
++ //normal configuration
|
|
|
++ unsigned int dram_clk;
|
|
|
++ unsigned int dram_type; //dram_type DDR2: 2 DDR3: 3 LPDDR2: 6 LPDDR3: 7 DDR3L: 31
|
|
|
++ //unsigned int lpddr2_type; //LPDDR2 type S4:0 S2:1 NVM:2
|
|
|
++ unsigned int dram_zq; //do not need
|
|
|
++ unsigned int dram_odt_en;
|
|
|
++
|
|
|
++ //control configuration
|
|
|
++ unsigned int dram_para1;
|
|
|
++ unsigned int dram_para2;
|
|
|
++
|
|
|
++ //timing configuration
|
|
|
++ unsigned int dram_mr0;
|
|
|
++ unsigned int dram_mr1;
|
|
|
++ unsigned int dram_mr2;
|
|
|
++ unsigned int dram_mr3;
|
|
|
++ unsigned int dram_tpr0; //DRAMTMG0
|
|
|
++ unsigned int dram_tpr1; //DRAMTMG1
|
|
|
++ unsigned int dram_tpr2; //DRAMTMG2
|
|
|
++ unsigned int dram_tpr3; //DRAMTMG3
|
|
|
++ unsigned int dram_tpr4; //DRAMTMG4
|
|
|
++ unsigned int dram_tpr5; //DRAMTMG5
|
|
|
++ unsigned int dram_tpr6; //DRAMTMG8
|
|
|
++ //reserved for future use
|
|
|
++ unsigned int dram_tpr7;
|
|
|
++ unsigned int dram_tpr8;
|
|
|
++ unsigned int dram_tpr9;
|
|
|
++ unsigned int dram_tpr10;
|
|
|
++ unsigned int dram_tpr11;
|
|
|
++ unsigned int dram_tpr12;
|
|
|
++ unsigned int dram_tpr13;
|
|
|
++
|
|
|
++};
|
|
|
++
|
|
|
++#endif
|
|
|
+--- /dev/null
|
|
|
++++ b/drivers/ram/sunxi/mctl_hal-sun20iw1p1.c
|
|
|
+@@ -0,0 +1,1771 @@
|
|
|
++// SPDX-License-Identifier: GPL-2.0+
|
|
|
++
|
|
|
++#include <common.h>
|
|
|
++#include <dm.h>
|
|
|
++#include <ram.h>
|
|
|
++#include <linux/delay.h>
|
|
|
++
|
|
|
++#include "dram_v2.h"
|
|
|
++#include "sdram.h"
|
|
|
++
|
|
|
++#define readl rv_readl
|
|
|
++#define writel rv_writel
|
|
|
++#include <asm/io.h>
|
|
|
++#undef readl
|
|
|
++#undef writel
|
|
|
++
|
|
|
++#define readl(x) rv_readl((const volatile void __iomem *)(u64)(x))
|
|
|
++#define writel(x, v) rv_writel(v, (volatile void __iomem *)(u64)(x))
|
|
|
++
|
|
|
++#if defined(CONFIG_SPL_BUILD)
|
|
|
++
|
|
|
++char* memcpy_self(char* dst, char* src, int len)
|
|
|
++{
|
|
|
++ int i;
|
|
|
++ for(i=0; i!=len; i++) {
|
|
|
++ dst[i] = src[i];
|
|
|
++ }
|
|
|
++ return dst;
|
|
|
++}
|
|
|
++
|
|
|
++void dram_vol_set(struct dram_para_t *para)
|
|
|
++{
|
|
|
++ int reg, vol = 0;
|
|
|
++
|
|
|
++ switch( para->dram_type ) {
|
|
|
++ case 2: vol = 47; break;
|
|
|
++ case 3: vol = 25; break;
|
|
|
++ default: vol = 0;
|
|
|
++ }
|
|
|
++vol = 25; // XXX
|
|
|
++ reg = readl(0x3000150);
|
|
|
++ reg &= ~(0xff00);
|
|
|
++ reg |= vol << 8;
|
|
|
++ reg &= ~(0x200000);
|
|
|
++ writel(0x3000150, reg);
|
|
|
++
|
|
|
++ udelay(1);
|
|
|
++}
|
|
|
++
|
|
|
++void paraconfig(unsigned int *para, unsigned int mask, unsigned int value)
|
|
|
++{
|
|
|
++ *para &= ~(mask);
|
|
|
++ *para |= value;
|
|
|
++}
|
|
|
++
|
|
|
++
|
|
|
++void dram_enable_all_master(void)
|
|
|
++{
|
|
|
++ writel(0x3102020, -1);
|
|
|
++ writel(0x3102024, 0xff);
|
|
|
++ writel(0x3102028, 0xffff);
|
|
|
++ udelay(10);
|
|
|
++}
|
|
|
++
|
|
|
++
|
|
|
++void dram_disable_all_master(void)
|
|
|
++{
|
|
|
++ writel(0x3102020, 1);
|
|
|
++ writel(0x3102024, 0);
|
|
|
++ writel(0x3102028, 0);
|
|
|
++ udelay(10);
|
|
|
++}
|
|
|
++
|
|
|
++
|
|
|
++void eye_delay_compensation(struct dram_para_t *para) // s1
|
|
|
++{
|
|
|
++ unsigned int val, ptr;
|
|
|
++
|
|
|
++ // DATn0IOCR, n = 0...7
|
|
|
++ for (ptr = 0x3103310; ptr != 0x3103334; ptr += 4) {
|
|
|
++ val = readl(ptr);
|
|
|
++ val |= (para->dram_tpr11 << 9) & 0x1e00;
|
|
|
++ val |= (para->dram_tpr12 << 1) & 0x001e;
|
|
|
++ writel(ptr, val);
|
|
|
++ }
|
|
|
++
|
|
|
++ // DATn1IOCR, n = 0...7
|
|
|
++ for (ptr = 0x3103390; ptr != 0x31033b4; ptr += 4) {
|
|
|
++ val = readl(ptr);
|
|
|
++ val |= ((para->dram_tpr11 >> 4) << 9) & 0x1e00;
|
|
|
++ val |= ((para->dram_tpr12 >> 4) << 1) & 0x001e;
|
|
|
++ writel(ptr, val);
|
|
|
++ }
|
|
|
++
|
|
|
++ // PGCR0: assert AC loopback FIFO reset
|
|
|
++ val = readl(0x3103100);
|
|
|
++ val &= 0xfbffffff;
|
|
|
++ writel(0x3103100, val);
|
|
|
++
|
|
|
++ // ??
|
|
|
++ val = readl(0x3103334);
|
|
|
++ val |= ((para->dram_tpr11 >> 16) << 9) & 0x1e00;
|
|
|
++ val |= ((para->dram_tpr12 >> 16) << 1) & 0x001e;
|
|
|
++ writel(0x3103334, val);
|
|
|
++
|
|
|
++ val = readl(0x3103338);
|
|
|
++ val |= ((para->dram_tpr11 >> 16) << 9) & 0x1e00;
|
|
|
++ val |= ((para->dram_tpr12 >> 16) << 1) & 0x001e;
|
|
|
++ writel(0x3103338, val);
|
|
|
++
|
|
|
++ val = readl(0x31033b4);
|
|
|
++ val |= ((para->dram_tpr11 >> 20) << 9) & 0x1e00;
|
|
|
++ val |= ((para->dram_tpr12 >> 20) << 1) & 0x001e;
|
|
|
++ writel(0x31033b4, val);
|
|
|
++
|
|
|
++ val = readl(0x31033b8);
|
|
|
++ val |= ((para->dram_tpr11 >> 20) << 9) & 0x1e00;
|
|
|
++ val |= ((para->dram_tpr12 >> 20) << 1) & 0x001e;
|
|
|
++ writel(0x31033b8, val);
|
|
|
++
|
|
|
++ val = readl(0x310333c);
|
|
|
++ val |= ((para->dram_tpr11 >> 16) << 25) & 0x1e000000;
|
|
|
++ writel(0x310333c, val);
|
|
|
++
|
|
|
++ val = readl(0x31033bc);
|
|
|
++ val |= ((para->dram_tpr11 >> 20) << 25) & 0x1e000000;
|
|
|
++ writel(0x31033bc, val);
|
|
|
++
|
|
|
++ // PGCR0: release AC loopback FIFO reset
|
|
|
++ val = readl(0x3103100);
|
|
|
++ val |= 0x04000000;
|
|
|
++ writel(0x3103100, val);
|
|
|
++
|
|
|
++ udelay(1);
|
|
|
++
|
|
|
++ for (ptr = 0x3103240; ptr != 0x310327c; ptr += 4) {
|
|
|
++ val = readl(ptr);
|
|
|
++ val |= ((para->dram_tpr10 >> 4) << 8) & 0x0f00;
|
|
|
++ writel(ptr, val);
|
|
|
++ }
|
|
|
++
|
|
|
++ for (ptr = 0x3103228; ptr != 0x3103240; ptr += 4) {
|
|
|
++ val = readl(ptr);
|
|
|
++ val |= ((para->dram_tpr10 >> 4) << 8) & 0x0f00;
|
|
|
++ writel(ptr, val);
|
|
|
++ }
|
|
|
++
|
|
|
++ val = readl(0x3103218);
|
|
|
++ val |= (para->dram_tpr10 << 8) & 0x0f00;
|
|
|
++ writel(0x3103218, val);
|
|
|
++
|
|
|
++ val = readl(0x310321c);
|
|
|
++ val |= (para->dram_tpr10 << 8) & 0x0f00;
|
|
|
++ writel(0x310321c, val);
|
|
|
++
|
|
|
++ val = readl(0x3103280);
|
|
|
++ val |= ((para->dram_tpr10 >> 12) << 8) & 0x0f00;
|
|
|
++ writel(0x3103280, val);
|
|
|
++}
|
|
|
++
|
|
|
++
|
|
|
++// Not used ??
|
|
|
++//
|
|
|
++void bit_delay_compensation(void)
|
|
|
++{
|
|
|
++ const unsigned int data0[44] = {
|
|
|
++ 0, 1, 2, 3, 2, 3, 3, 3, 0, 0, 0,
|
|
|
++ 6, 6, 6, 5, 5, 5, 5, 5, 0, 0, 0,
|
|
|
++ 0, 2, 4, 2, 6, 5, 5, 5, 0, 0, 0,
|
|
|
++ 3, 3, 3, 2, 2, 1, 1, 1, 0, 0, 0,
|
|
|
++ };
|
|
|
++ const unsigned int data1[44] = {
|
|
|
++ 0, 1, 3, 3, 3, 3, 3, 4, 3, 3, 3,
|
|
|
++ 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3,
|
|
|
++ 0, 3, 3, 1, 6, 6, 5, 6, 3, 3, 3,
|
|
|
++ 5, 5, 6, 6, 4, 5, 3, 3, 3, 3, 3,
|
|
|
++ };
|
|
|
++
|
|
|
++ unsigned int *start = (unsigned int *)0x3102310; // DATX0IOCR
|
|
|
++ unsigned int *end = (unsigned int *)0x3102510; // DATX0IOCR x + 4 * size
|
|
|
++ unsigned int *datxiocr;
|
|
|
++ unsigned int i, j, k, rval;
|
|
|
++
|
|
|
++ rval = readl(0x3102100) & 0x03ffffff;
|
|
|
++ writel(0x3102100, rval);
|
|
|
++
|
|
|
++ // Fill DATX0IOCR - DATX3IOCR, 11 registers per block, blocks 0x20 words apart
|
|
|
++ for(i = 0, datxiocr = start; datxiocr != end; i += 11, datxiocr += 0x20) {
|
|
|
++ for(j = 0, k = i; j != 11; j++, k++) {
|
|
|
++ rval = readl((unsigned int)datxiocr[j]);
|
|
|
++ rval += data1[k] << 8;
|
|
|
++ rval += data0[k];
|
|
|
++ writel((unsigned int)datxiocr[j], rval);
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ rval = readl(0x3102100) | 0x04000000;
|
|
|
++ writel(0x3102100, rval);
|
|
|
++}
|
|
|
++
|
|
|
++// Not used ??
|
|
|
++//
|
|
|
++void set_master_priority_pad(struct dram_para_t *para)
|
|
|
++{
|
|
|
++ unsigned int val;
|
|
|
++
|
|
|
++ val = readl(0x310200c) & 0xfffff000;
|
|
|
++ val |= (para->dram_clk >> 1) - 1;
|
|
|
++ writel(0x310200c, val);
|
|
|
++
|
|
|
++ writel(0x3102200, 0x00001000);
|
|
|
++ writel(0x3102210, 0x01000009);
|
|
|
++ writel(0x3102214, 0x00500100);
|
|
|
++ writel(0x3102230, 0x0200000d);
|
|
|
++ writel(0x3102234, 0x00600100);
|
|
|
++ writel(0x3102240, 0x01000009);
|
|
|
++ writel(0x3102244, 0x00500100);
|
|
|
++ writel(0x3102260, 0x00640209);
|
|
|
++ writel(0x3102264, 0x00200040);
|
|
|
++ writel(0x3102290, 0x01000009);
|
|
|
++ writel(0x3102294, 0x00400080);
|
|
|
++ writel(0x3102470, 0);
|
|
|
++ writel(0x3102474, 0);
|
|
|
++
|
|
|
++ writel(0x31031c0, 0x0f802f05);
|
|
|
++ writel(0x31031c8, 0x0f0000ff);
|
|
|
++ writel(0x31031d0, 0x3f00005f);
|
|
|
++}
|
|
|
++
|
|
|
++int auto_cal_timing(unsigned int time, unsigned int freq)
|
|
|
++{
|
|
|
++ unsigned int t = time*freq;
|
|
|
++ return t/1000 + ( ((t%1000) != 0) ? 1 : 0);
|
|
|
++}
|
|
|
++
|
|
|
++// Main purpose of the auto_set_timing routine seems to be to calculate all
|
|
|
++// timing settings for the specific type of sdram used. Read together with
|
|
|
++// an sdram datasheet for context on the various variables.
|
|
|
++//
|
|
|
++void auto_set_timing_para(struct dram_para_t *para) // s5
|
|
|
++{
|
|
|
++ unsigned int freq; // s4
|
|
|
++ unsigned int type; // s8
|
|
|
++ unsigned int tpr13; // 80(sp)
|
|
|
++ unsigned int reg_val;
|
|
|
++
|
|
|
++ unsigned char tccd; // 88(sp)
|
|
|
++ unsigned char trrd; // s7
|
|
|
++ unsigned char trcd; // s3
|
|
|
++ unsigned char trc; // s9
|
|
|
++ unsigned char tfaw; // s10
|
|
|
++ unsigned char tras; // s11
|
|
|
++ unsigned char trp; // 0(sp)
|
|
|
++ unsigned char twtr; // s1
|
|
|
++ unsigned char twr; // s6
|
|
|
++ unsigned char trtp; // 64(sp)
|
|
|
++ unsigned char txp; // a6
|
|
|
++ unsigned short trefi; // s2
|
|
|
++ unsigned short trfc; // a5 / 8(sp)
|
|
|
++
|
|
|
++ freq = para->dram_clk;
|
|
|
++ type = para->dram_type;
|
|
|
++ tpr13 = para->dram_tpr13;
|
|
|
++
|
|
|
++ //printf("type = %d\n", type);
|
|
|
++ //printf("tpr13 = %p\n", tpr13);
|
|
|
++
|
|
|
++ if (para->dram_tpr13 & 0x2)
|
|
|
++ {
|
|
|
++ //dram_tpr0
|
|
|
++ tccd = ( (para->dram_tpr0 >> 21) & 0x7 ); // [23:21]
|
|
|
++ tfaw = ( (para->dram_tpr0 >> 15) & 0x3f ); // [20:15]
|
|
|
++ trrd = ( (para->dram_tpr0 >> 11) & 0xf ); // [14:11]
|
|
|
++ trcd = ( (para->dram_tpr0 >> 6) & 0x1f ); // [10:6 ]
|
|
|
++ trc = ( (para->dram_tpr0 >> 0) & 0x3f ); // [ 5:0 ]
|
|
|
++ //dram_tpr1
|
|
|
++ txp = ( (para->dram_tpr1 >> 23) & 0x1f ); // [27:23]
|
|
|
++ twtr = ( (para->dram_tpr1 >> 20) & 0x7 ); // [22:20]
|
|
|
++ trtp = ( (para->dram_tpr1 >> 15) & 0x1f ); // [19:15]
|
|
|
++ twr = ( (para->dram_tpr1 >> 11) & 0xf ); // [14:11]
|
|
|
++ trp = ( (para->dram_tpr1 >> 6) & 0x1f ); // [10:6 ]
|
|
|
++ tras = ( (para->dram_tpr1 >> 0) & 0x3f ); // [ 5:0 ]
|
|
|
++ //dram_tpr2
|
|
|
++ trfc = ( (para->dram_tpr2 >> 12)& 0x1ff); // [20:12]
|
|
|
++ trefi = ( (para->dram_tpr2 >> 0) & 0xfff); // [11:0 ]
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ unsigned int frq2 = freq >> 1; // s0
|
|
|
++
|
|
|
++ if (type == 3) {
|
|
|
++ // DDR3
|
|
|
++ trfc = auto_cal_timing( 350, frq2);
|
|
|
++ trefi = auto_cal_timing(7800, frq2) / 32 + 1; // XXX
|
|
|
++ twr = auto_cal_timing( 8, frq2);
|
|
|
++ trcd = auto_cal_timing( 15, frq2);
|
|
|
++ twtr = twr + 2; // + 2 ? XXX
|
|
|
++ if (twr < 2) twtr = 2;
|
|
|
++ twr = trcd;
|
|
|
++ if (trcd < 2) twr = 2;
|
|
|
++ if (freq <= 800) {
|
|
|
++ tfaw = auto_cal_timing(50, frq2);
|
|
|
++ trrd = auto_cal_timing(10, frq2);
|
|
|
++ if (trrd < 2) trrd = 2;
|
|
|
++ trc = auto_cal_timing(53, frq2);
|
|
|
++ tras = auto_cal_timing(38, frq2);
|
|
|
++ txp = trrd; // 10
|
|
|
++ trp = trcd; // 15
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ tfaw = auto_cal_timing(35, frq2);
|
|
|
++ trrd = auto_cal_timing(10, frq2);
|
|
|
++ if (trrd < 2) trrd = 2;
|
|
|
++ trcd = auto_cal_timing(14, frq2);
|
|
|
++ trc = auto_cal_timing(48, frq2);
|
|
|
++ tras = auto_cal_timing(34, frq2);
|
|
|
++ txp = trrd; // 10
|
|
|
++ trp = trcd; // 14
|
|
|
++ }
|
|
|
++ }
|
|
|
++ else if (type == 2) {
|
|
|
++ // DDR2
|
|
|
++ tfaw = auto_cal_timing( 50, frq2);
|
|
|
++ trrd = auto_cal_timing( 10, frq2);
|
|
|
++ trcd = auto_cal_timing( 20, frq2);
|
|
|
++ trc = auto_cal_timing( 65, frq2);
|
|
|
++ twtr = auto_cal_timing( 8, frq2);
|
|
|
++ trp = auto_cal_timing( 15, frq2);
|
|
|
++ tras = auto_cal_timing( 45, frq2);
|
|
|
++ trefi = auto_cal_timing(7800, frq2) / 32;
|
|
|
++ trfc = auto_cal_timing( 328, frq2);
|
|
|
++ txp = 2;
|
|
|
++ twr = trp; // 15
|
|
|
++ }
|
|
|
++ else if (type == 6) {
|
|
|
++ // LPDDR2
|
|
|
++ tfaw = auto_cal_timing( 50, frq2);
|
|
|
++ if (tfaw < 4) tfaw = 4;
|
|
|
++ trrd = auto_cal_timing( 10, frq2);
|
|
|
++ if (trrd == 0) trrd = 1;
|
|
|
++ trcd = auto_cal_timing( 24, frq2);
|
|
|
++ if (trcd < 2) trcd = 2;
|
|
|
++ trc = auto_cal_timing( 70, frq2);
|
|
|
++ txp = auto_cal_timing( 8, frq2);
|
|
|
++ if (txp == 0) {
|
|
|
++ txp = 1;
|
|
|
++ twtr = 2;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ twtr = txp;
|
|
|
++ if (txp < 2) {
|
|
|
++ txp = 2;
|
|
|
++ twtr = 2;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ twr = auto_cal_timing( 15, frq2);
|
|
|
++ if (twr < 2) twr = 2;
|
|
|
++ trp = auto_cal_timing( 17, frq2);
|
|
|
++ tras = auto_cal_timing( 42, frq2);
|
|
|
++ trefi = auto_cal_timing(3900, frq2) / 32;
|
|
|
++ trfc = auto_cal_timing( 210, frq2);
|
|
|
++ }
|
|
|
++ else if (type == 7) {
|
|
|
++ // LPDDR3
|
|
|
++ tfaw = auto_cal_timing( 50, frq2);
|
|
|
++ if (tfaw < 4) tfaw = 4;
|
|
|
++ trrd = auto_cal_timing( 10, frq2);
|
|
|
++ if (trrd == 0) trrd = 1;
|
|
|
++ trcd = auto_cal_timing( 24, frq2);
|
|
|
++ if (trcd < 2) trcd = 2;
|
|
|
++ trc = auto_cal_timing( 70, frq2);
|
|
|
++ twtr = auto_cal_timing( 8, frq2);
|
|
|
++ if (twtr < 2) twtr = 2;
|
|
|
++ twr = auto_cal_timing( 15, frq2);
|
|
|
++ if (twr < 2) twr = 2;
|
|
|
++ trp = auto_cal_timing( 17, frq2);
|
|
|
++ tras = auto_cal_timing( 42, frq2);
|
|
|
++ trefi = auto_cal_timing(3900, frq2) / 32;
|
|
|
++ trfc = auto_cal_timing( 210, frq2);
|
|
|
++ txp = twtr;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ // default
|
|
|
++ trfc = 128;
|
|
|
++ trp = 6;
|
|
|
++ trefi = 98;
|
|
|
++ txp = 10;
|
|
|
++ twr = 8;
|
|
|
++ twtr = 3;
|
|
|
++ tras = 14;
|
|
|
++ tfaw = 16;
|
|
|
++ trc = 20;
|
|
|
++ trcd = 6;
|
|
|
++ trrd = 3;
|
|
|
++ }
|
|
|
++ //assign the value back to the DRAM structure
|
|
|
++ tccd = 2;
|
|
|
++ trtp = 4; // not in .S ?
|
|
|
++ para->dram_tpr0 = (trc<<0) | (trcd<<6) | (trrd<<11) | (tfaw<<15) | (tccd<<21);
|
|
|
++ para->dram_tpr1 = (tras<<0) | (trp<<6) | (twr<<11) | (trtp<<15) | (twtr<<20) | (txp<<23);
|
|
|
++ para->dram_tpr2 = (trefi<<0) | (trfc<<12);
|
|
|
++ }
|
|
|
++
|
|
|
++ unsigned int tcksrx; // t1
|
|
|
++ unsigned int tckesr; // t4;
|
|
|
++ unsigned int trd2wr; // t6
|
|
|
++ unsigned int trasmax; // t3;
|
|
|
++ unsigned int twtp; // s6 (was twr!)
|
|
|
++ unsigned int tcke; // s8
|
|
|
++ unsigned int tmod; // t0
|
|
|
++ unsigned int tmrd; // t5
|
|
|
++ unsigned int tmrw; // a1
|
|
|
++ unsigned int t_rdata_en;// a4 (was tcwl!)
|
|
|
++ unsigned int tcl; // a0
|
|
|
++ unsigned int wr_latency;// a7
|
|
|
++ unsigned int tcwl; // first a4, then a5
|
|
|
++ unsigned int mr3; // s0
|
|
|
++ unsigned int mr2; // t2
|
|
|
++ unsigned int mr1; // s1
|
|
|
++ unsigned int mr0; // a3
|
|
|
++ unsigned int dmr3; // 72(sp)
|
|
|
++ //unsigned int trtp; // 64(sp)
|
|
|
++ unsigned int dmr1; // 56(sp)
|
|
|
++ unsigned int twr2rd; // 48(sp)
|
|
|
++ unsigned int tdinit3; // 40(sp)
|
|
|
++ unsigned int tdinit2; // 32(sp)
|
|
|
++ unsigned int tdinit1; // 24(sp)
|
|
|
++ unsigned int tdinit0; // 16(sp)
|
|
|
++
|
|
|
++ dmr1 = para->dram_mr1;
|
|
|
++ dmr3 = para->dram_mr3;
|
|
|
++
|
|
|
++ switch (type) {
|
|
|
++
|
|
|
++ case 2: // DDR2
|
|
|
++ {
|
|
|
++ trasmax = freq / 30;
|
|
|
++ if (freq < 409) {
|
|
|
++ tcl = 3;
|
|
|
++ t_rdata_en = 1;
|
|
|
++ mr0 = 0x06a3;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ t_rdata_en = 2;
|
|
|
++ tcl = 4;
|
|
|
++ mr0 = 0x0e73;
|
|
|
++ }
|
|
|
++ tmrd = 2;
|
|
|
++ twtp = twr + 5;
|
|
|
++ tcksrx = 5;
|
|
|
++ tckesr = 4;
|
|
|
++ trd2wr = 4;
|
|
|
++ tcke = 3;
|
|
|
++ tmod = 12;
|
|
|
++ wr_latency = 1;
|
|
|
++ mr3 = 0;
|
|
|
++ mr2 = 0;
|
|
|
++ tdinit0 = 200*freq + 1;
|
|
|
++ tdinit1 = 100*freq / 1000 + 1;
|
|
|
++ tdinit2 = 200*freq + 1;
|
|
|
++ tdinit3 = 1*freq + 1;
|
|
|
++ tmrw = 0;
|
|
|
++ twr2rd = twtr + 5;
|
|
|
++ tcwl = 0;
|
|
|
++ mr1 = dmr1;
|
|
|
++ break;
|
|
|
++ }
|
|
|
++
|
|
|
++ case 3: // DDR3
|
|
|
++ {
|
|
|
++ trasmax = freq / 30;
|
|
|
++ if (freq <= 800) {
|
|
|
++ mr0 = 0x1c70;
|
|
|
++ tcl = 6;
|
|
|
++ wr_latency = 2;
|
|
|
++ tcwl = 4;
|
|
|
++ mr2 = 24;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ mr0 = 0x1e14;
|
|
|
++ tcl = 7;
|
|
|
++ wr_latency = 3;
|
|
|
++ tcwl = 5;
|
|
|
++ mr2 = 32;
|
|
|
++ }
|
|
|
++
|
|
|
++ twtp = tcwl + 2 + twtr; // WL+BL/2+tWTR
|
|
|
++ trd2wr = tcwl + 2 + twr; // WL+BL/2+tWR
|
|
|
++ twr2rd = tcwl + twtr; // WL+tWTR
|
|
|
++
|
|
|
++ tdinit0 = 500*freq + 1; // 500 us
|
|
|
++ tdinit1 = 360*freq / 1000 + 1; // 360 ns
|
|
|
++ tdinit2 = 200*freq + 1; // 200 us
|
|
|
++ tdinit3 = 1*freq + 1; // 1 us
|
|
|
++
|
|
|
++ if (((tpr13>>2) & 0x03) == 0x01 || freq < 912) {
|
|
|
++ mr1 = dmr1;
|
|
|
++ t_rdata_en = tcwl; // a5 <- a4
|
|
|
++ tcksrx = 5;
|
|
|
++ tckesr = 4;
|
|
|
++ trd2wr = 5;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ mr1 = dmr1;
|
|
|
++ t_rdata_en = tcwl; // a5 <- a4
|
|
|
++ tcksrx = 5;
|
|
|
++ tckesr = 4;
|
|
|
++ trd2wr = 6;
|
|
|
++ }
|
|
|
++ tcke = 3; // not in .S ?
|
|
|
++ tmod = 12;
|
|
|
++ tmrd = 4;
|
|
|
++ tmrw = 0;
|
|
|
++ mr3 = 0;
|
|
|
++ break;
|
|
|
++ }
|
|
|
++
|
|
|
++ case 6: // LPDDR2
|
|
|
++ {
|
|
|
++ trasmax = freq / 60;
|
|
|
++ mr3 = dmr3;
|
|
|
++ twtp = twr + 5;
|
|
|
++ mr2 = 6;
|
|
|
++ mr1 = 5;
|
|
|
++ tcksrx = 5;
|
|
|
++ tckesr = 5;
|
|
|
++ trd2wr = 10;
|
|
|
++ tcke = 2;
|
|
|
++ tmod = 5;
|
|
|
++ tmrd = 5;
|
|
|
++ tmrw = 3;
|
|
|
++ tcl = 4;
|
|
|
++ wr_latency = 1;
|
|
|
++ t_rdata_en = 1;
|
|
|
++ tdinit0 = 200*freq + 1;
|
|
|
++ tdinit1 = 100*freq / 1000 + 1;
|
|
|
++ tdinit2 = 11*freq + 1;
|
|
|
++ tdinit3 = 1*freq + 1;
|
|
|
++ twr2rd = twtr + 5;
|
|
|
++ tcwl = 2;
|
|
|
++ mr1 = 195;
|
|
|
++ mr0 = 0;
|
|
|
++ break;
|
|
|
++ }
|
|
|
++
|
|
|
++ case 7: // LPDDR3
|
|
|
++ {
|
|
|
++ trasmax = freq / 60;
|
|
|
++ if (freq < 800) {
|
|
|
++ tcwl = 4;
|
|
|
++ wr_latency = 3;
|
|
|
++ t_rdata_en = 6;
|
|
|
++ mr2 = 12;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ tcwl = 3;
|
|
|
++ tcke = 6;
|
|
|
++ wr_latency = 2;
|
|
|
++ t_rdata_en = 5;
|
|
|
++ mr2 = 10;
|
|
|
++ }
|
|
|
++ twtp = tcwl + 5;
|
|
|
++ tcl = 7;
|
|
|
++ mr3 = dmr3;
|
|
|
++ tcksrx = 5;
|
|
|
++ tckesr = 5;
|
|
|
++ trd2wr = 13;
|
|
|
++ tcke = 3;
|
|
|
++ tmod = 12;
|
|
|
++ tdinit0 = 400*freq + 1;
|
|
|
++ tdinit1 = 500*freq / 1000 + 1;
|
|
|
++ tdinit2 = 11*freq + 1;
|
|
|
++ tdinit3 = 1*freq + 1;
|
|
|
++ tmrd = 5;
|
|
|
++ tmrw = 5;
|
|
|
++ twr2rd = tcwl + twtr + 5;
|
|
|
++ mr1 = 195;
|
|
|
++ mr0 = 0;
|
|
|
++ break;
|
|
|
++ }
|
|
|
++
|
|
|
++ default:
|
|
|
++ twr2rd = 8; // 48(sp)
|
|
|
++ tcksrx = 4; // t1
|
|
|
++ tckesr = 3; // t4
|
|
|
++ trd2wr = 4; // t6
|
|
|
++ trasmax = 27; // t3
|
|
|
++ twtp = 12; // s6
|
|
|
++ tcke = 2; // s8
|
|
|
++ tmod = 6; // t0
|
|
|
++ tmrd = 2; // t5
|
|
|
++ tmrw = 0; // a1
|
|
|
++ tcwl = 3; // a5
|
|
|
++ tcl = 3; // a0
|
|
|
++ wr_latency = 1; // a7
|
|
|
++ t_rdata_en = 1; // a4
|
|
|
++ mr3 = 0; // s0
|
|
|
++ mr2 = 0; // t2
|
|
|
++ mr1 = 0; // s1
|
|
|
++ mr0 = 0; // a3
|
|
|
++ tdinit3 = 0; // 40(sp)
|
|
|
++ tdinit2 = 0; // 32(sp)
|
|
|
++ tdinit1 = 0; // 24(sp)
|
|
|
++ tdinit0 = 0; // 16(sp)
|
|
|
++ break;
|
|
|
++ }
|
|
|
++ if (trtp < tcl - trp + 2) {
|
|
|
++ trtp = tcl - trp + 2;
|
|
|
++ }
|
|
|
++ trtp = 4;
|
|
|
++
|
|
|
++ // Update mode block when permitted
|
|
|
++ if ((para->dram_mr0 & 0xffff0000) == 0) para->dram_mr0 = mr0;
|
|
|
++ if ((para->dram_mr1 & 0xffff0000) == 0) para->dram_mr1 = mr1;
|
|
|
++ if ((para->dram_mr2 & 0xffff0000) == 0) para->dram_mr2 = mr2;
|
|
|
++ if ((para->dram_mr3 & 0xffff0000) == 0) para->dram_mr3 = mr3;
|
|
|
++
|
|
|
++ // Set mode registers
|
|
|
++ writel(0x3103030, para->dram_mr0);
|
|
|
++ writel(0x3103034, para->dram_mr1);
|
|
|
++ writel(0x3103038, para->dram_mr2);
|
|
|
++ writel(0x310303c, para->dram_mr3);
|
|
|
++ writel(0x310302c, (para->dram_odt_en >> 4) & 0x3); // ??
|
|
|
++
|
|
|
++ // Set dram timing DRAMTMG0 - DRAMTMG5
|
|
|
++ reg_val= (twtp<<24) | (tfaw<<16) | (trasmax<<8) | (tras<<0);
|
|
|
++ writel(0x3103058, reg_val);
|
|
|
++ reg_val= (txp<<16) | (trtp<<8) | (trc<<0);
|
|
|
++ writel(0x310305c, reg_val);
|
|
|
++ reg_val= (tcwl<<24) | (tcl<<16) | (trd2wr<<8) | (twr2rd<<0);
|
|
|
++ writel(0x3103060, reg_val);
|
|
|
++ reg_val= (tmrw<<16) | (tmrd<<12) | (tmod<<0);
|
|
|
++ writel(0x3103064, reg_val);
|
|
|
++ reg_val= (trcd<<24) | (tccd<<16) | (trrd<<8) | (trp<<0);
|
|
|
++ writel(0x3103068, reg_val);
|
|
|
++ reg_val= (tcksrx<<24) | (tcksrx<<16) | (tckesr<<8) | (tcke<<0);
|
|
|
++ writel(0x310306c, reg_val);
|
|
|
++
|
|
|
++ // Set two rank timing
|
|
|
++ reg_val = readl(0x3103078);
|
|
|
++ reg_val &= 0x0fff0000;
|
|
|
++ reg_val |= (para->dram_clk < 800) ? 0xf0006600 : 0xf0007600;
|
|
|
++ reg_val |= 0x10;
|
|
|
++ writel(0x3103078, reg_val);
|
|
|
++
|
|
|
++ // Set phy interface time PITMG0, PTR3, PTR4
|
|
|
++ reg_val = (0x2<<24) | (t_rdata_en<<16) | (0x1<<8) | (wr_latency<<0);
|
|
|
++ writel(0x3103080, reg_val);
|
|
|
++ writel(0x3103050, ((tdinit0<<0)|(tdinit1<<20)));
|
|
|
++ writel(0x3103054, ((tdinit2<<0)|(tdinit3<<20)));
|
|
|
++
|
|
|
++ // Set refresh timing and mode
|
|
|
++ reg_val = (trefi<<16) | (trfc<<0);
|
|
|
++ writel(0x3103090, reg_val);
|
|
|
++ reg_val = 0x0fff0000 & (trefi<<15);
|
|
|
++ writel(0x3103094, reg_val);
|
|
|
++}
|
|
|
++
|
|
|
++// Not used ?
|
|
|
++//
|
|
|
++void ccm_get_sscg(void)
|
|
|
++{
|
|
|
++ // NOTE: function is present in the assembly, but was not translated.
|
|
|
++}
|
|
|
++
|
|
|
++// Not used ?
|
|
|
++//
|
|
|
++void ccm_set_pll_sscg(void)
|
|
|
++{
|
|
|
++ // NOTE: function is present in the assembly, but was not translated.
|
|
|
++}
|
|
|
++
|
|
|
++// Purpose of this routine seems to be to initialize the PLL driving
|
|
|
++// the MBUS and sdram.
|
|
|
++//
|
|
|
++int ccm_set_pll_ddr_clk(int index, struct dram_para_t *para)
|
|
|
++{
|
|
|
++ unsigned int val, clk, n;
|
|
|
++
|
|
|
++ clk = (para->dram_tpr13 & (1 << 6)) ? para->dram_tpr9
|
|
|
++ : para->dram_clk;
|
|
|
++
|
|
|
++ // set VCO clock divider
|
|
|
++ n = (clk * 2) / 24;
|
|
|
++ val = readl(0x2001010);
|
|
|
++ val &= 0xfff800fc; // clear dividers
|
|
|
++ val |= (n - 1) << 8; // set PLL division
|
|
|
++ val |= 0xc0000000; // enable PLL and LDO
|
|
|
++ writel(0x2001010, val);
|
|
|
++
|
|
|
++ // Restart PLL locking
|
|
|
++ val &= 0xdfffffff; // disbable lock
|
|
|
++ val |= 0xc0000000; // enable PLL and LDO
|
|
|
++ writel(0x2001010, val);
|
|
|
++ val |= 0xe0000000; // re-enable lock
|
|
|
++ writel(0x2001010, val);
|
|
|
++
|
|
|
++ // wait for PLL to lock
|
|
|
++ while ((readl(0x2001010) & 0x10000000) == 0) {;}
|
|
|
++
|
|
|
++ udelay(20);
|
|
|
++
|
|
|
++ // enable PLL output
|
|
|
++ val = readl(0x2001000);
|
|
|
++ val |= 0x08000000;
|
|
|
++ writel(0x2001000, val);
|
|
|
++
|
|
|
++ // turn clock gate on
|
|
|
++ val = readl(0x2001800);
|
|
|
++ val &= 0xfcfffcfc; // select DDR clk source, n=1, m=1
|
|
|
++ val |= 0x80000000; // turn clock on
|
|
|
++ writel(0x2001800, val);
|
|
|
++
|
|
|
++ return n * 24;
|
|
|
++}
|
|
|
++
|
|
|
++// Main purpose of sys_init seems to be to initalise the clocks for
|
|
|
++// the sdram controller.
|
|
|
++//
|
|
|
++void mctl_sys_init(struct dram_para_t *para)
|
|
|
++{
|
|
|
++ unsigned int val;
|
|
|
++
|
|
|
++ // s1 = 0x02001000
|
|
|
++
|
|
|
++ // assert MBUS reset
|
|
|
++ val = readl(0x2001540);
|
|
|
++ val &= 0xbfffffff;
|
|
|
++ writel(0x2001540, val);
|
|
|
++
|
|
|
++ // turn off sdram clock gate, assert sdram reset
|
|
|
++ val = readl(0x200180c);
|
|
|
++ val &= 0xfffffffe;
|
|
|
++ writel(0x200180c, val);
|
|
|
++ val &= 0xfffefffe;
|
|
|
++ writel(0x200180c, val);
|
|
|
++
|
|
|
++ // turn of bit 30 [??]
|
|
|
++ val = readl(0x2001800);
|
|
|
++ writel(0x2001800, val & 0xbfffffff);
|
|
|
++ // and toggle dram clock gating off + trigger update
|
|
|
++ val &= 0x7fffffff;
|
|
|
++ writel(0x2001800, val);
|
|
|
++ val |= 0x08000000;
|
|
|
++ writel(0x2001800, val);
|
|
|
++ udelay(10);
|
|
|
++
|
|
|
++ // set ddr pll clock
|
|
|
++ val = ccm_set_pll_ddr_clk(0, para);
|
|
|
++ para->dram_clk = val >> 1;
|
|
|
++ udelay(100);
|
|
|
++ dram_disable_all_master();
|
|
|
++
|
|
|
++ // release sdram reset
|
|
|
++ val = readl(0x200180c);
|
|
|
++ val |= 0x00010000;
|
|
|
++ writel(0x200180c, val);
|
|
|
++
|
|
|
++ // release MBUS reset
|
|
|
++ val = readl(0x2001540);
|
|
|
++ val |= 0x40000000;
|
|
|
++ writel(0x2001540, val);
|
|
|
++
|
|
|
++ // turn bit 30 back on [?]
|
|
|
++ val = readl(0x2001800);
|
|
|
++ val |= 0x40000000;
|
|
|
++ writel(0x2001800, val);
|
|
|
++ udelay(5);
|
|
|
++
|
|
|
++ // turn on sdram clock gate
|
|
|
++ val = readl(0x200180c);
|
|
|
++ val |= 0x0000001; // (1<<0)
|
|
|
++ writel(0x200180c, val);
|
|
|
++
|
|
|
++ // turn dram clock gate on, trigger sdr clock update
|
|
|
++ val = readl(0x2001800);
|
|
|
++ val |= 0x80000000;
|
|
|
++ writel(0x2001800, val);
|
|
|
++ val |= 0x88000000;
|
|
|
++ writel(0x2001800, val);
|
|
|
++ udelay(5);
|
|
|
++
|
|
|
++ // mCTL clock enable
|
|
|
++ writel(0x310300c, 0x00008000);
|
|
|
++ udelay(10);
|
|
|
++}
|
|
|
++
|
|
|
++// The main purpose of this routine seems to be to copy an address configuration
|
|
|
++// from the dram_para1 and dram_para2 fields to the PHY configuration registers
|
|
|
++// (0x3102000, 0x3102004).
|
|
|
++//
|
|
|
++void mctl_com_init(struct dram_para_t *para)
|
|
|
++{
|
|
|
++ unsigned int val, end, ptr;
|
|
|
++ int i;
|
|
|
++
|
|
|
++ // purpose ??
|
|
|
++ val = readl(0x3102008) & 0xffffc0ff;
|
|
|
++ val |= 0x2000;
|
|
|
++ writel(0x3102008, val);
|
|
|
++
|
|
|
++ // Set sdram type and word width
|
|
|
++ val = readl(0x3102000) & 0xff000fff;
|
|
|
++ val |= (para->dram_type &0x7) << 16; // DRAM type
|
|
|
++ val |= (~para->dram_para2 & 0x1) << 12; // DQ width
|
|
|
++ if((para->dram_type) != 6 && (para->dram_type) != 7) {
|
|
|
++ val |= ((para->dram_tpr13 >> 5) & 0x1) << 19; // 2T or 1T
|
|
|
++ val |= 0x400000;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ val |= 0x480000; // type 6 and 7 must use 1T
|
|
|
++ }
|
|
|
++ writel(0x3102000, val);
|
|
|
++
|
|
|
++ // init rank / bank / row for single/dual or two different ranks
|
|
|
++ val = para->dram_para2;
|
|
|
++ end = ((val & 0x100) && (((val >> 12) & 0xf) != 1)) ? 32 : 16;
|
|
|
++ ptr = 0x3102000;
|
|
|
++
|
|
|
++ for (i = 0 ; i != end; i += 16) {
|
|
|
++
|
|
|
++ val = readl(ptr) & 0xfffff000;
|
|
|
++
|
|
|
++ val |= (para->dram_para2 >> 12) & 0x3; // rank
|
|
|
++ val |= ((para->dram_para1 >> (i + 12)) << 2) & 0x4; // bank - 2
|
|
|
++ val |= (((para->dram_para1 >> (i + 4)) - 1) << 4) & 0xff; // row - 1
|
|
|
++
|
|
|
++ // convert from page size to column addr width - 3
|
|
|
++ switch ((para->dram_para1 >> i) & 0xf) {
|
|
|
++ case 8: val |= 0xa00; break;
|
|
|
++ case 4: val |= 0x900; break;
|
|
|
++ case 2: val |= 0x800; break;
|
|
|
++ case 1: val |= 0x700; break;
|
|
|
++ default: val |= 0x600; break;
|
|
|
++ }
|
|
|
++ writel(ptr, val);
|
|
|
++ ptr += 4;
|
|
|
++ }
|
|
|
++
|
|
|
++ // set ODTMAP based on number of ranks in use
|
|
|
++ val = (readl(0x3102000) & 0x1) ? 0x303 : 0x201;
|
|
|
++ writel(0x3103120, val);
|
|
|
++
|
|
|
++ // set mctl reg 3c4 to zero when using half DQ
|
|
|
++ if (para->dram_para2 & (1 << 0)) {
|
|
|
++ writel(0x31033c4, 0);
|
|
|
++ }
|
|
|
++
|
|
|
++ // purpose ??
|
|
|
++ if (para->dram_tpr4) {
|
|
|
++ val = readl(0x3102000);
|
|
|
++ val |= (para->dram_tpr4 << 25) & 0x06000000;
|
|
|
++ writel(0x3102000, val);
|
|
|
++
|
|
|
++ val = readl(0x3102004);
|
|
|
++ val |= ((para->dram_tpr4 >> 2) << 12) & 0x001ff000;
|
|
|
++ writel(0x3102004, val);
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++// This routine seems to have several remapping tables for 22 lines.
|
|
|
++// It is unclear which lines are being remapped. It seems to pick
|
|
|
++// table cfg7 for the Nezha board.
|
|
|
++//
|
|
|
++void mctl_phy_ac_remapping(struct dram_para_t *para)
|
|
|
++{
|
|
|
++ char cfg0[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
|
++ static char cfg1[] = { 1, 9, 3, 7, 8, 18, 4, 13, 5, 6, 10,
|
|
|
++ 2, 14, 12, 0, 0, 21, 17, 20, 19, 11, 22 };
|
|
|
++ static char cfg2[] = { 4, 9, 3, 7, 8, 18, 1, 13, 2, 6, 10,
|
|
|
++ 5, 14, 12, 0, 0, 21, 17, 20, 19, 11, 22 };
|
|
|
++ static char cfg3[] = { 1, 7, 8, 12, 10, 18, 4, 13, 5, 6, 3,
|
|
|
++ 2, 9, 0, 0, 0, 21, 17, 20, 19, 11, 22 };
|
|
|
++ static char cfg4[] = { 4, 12, 10, 7, 8, 18, 1, 13, 2, 6, 3,
|
|
|
++ 5, 9, 0, 0, 0, 21, 17, 20, 19, 11, 22 };
|
|
|
++ static char cfg5[] = { 13, 2, 7, 9, 12, 19, 5, 1, 6, 3, 4,
|
|
|
++ 8, 10, 0, 0, 0, 21, 22, 18, 17, 11, 20 };
|
|
|
++ static char cfg6[] = { 3, 10, 7, 13, 9, 11, 1, 2, 4, 6, 8,
|
|
|
++ 5, 12, 0, 0, 0, 20, 1, 0, 21, 22, 17 };
|
|
|
++ static char cfg7[] = { 3, 2, 4, 7, 9, 1, 17, 12, 18, 14, 13,
|
|
|
++ 8, 15, 6, 10, 5, 19, 22, 16, 21, 20, 11 };
|
|
|
++
|
|
|
++ unsigned int fuse, val;
|
|
|
++
|
|
|
++ // read SID info @ 0x228
|
|
|
++ fuse = (readl(0x3002228) >> 8) & 0x4;
|
|
|
++ printf("ddr_efuse_type: 0x%x\n", fuse);
|
|
|
++
|
|
|
++ if ((para->dram_tpr13 >> 18) & 0x3) {
|
|
|
++ memcpy_self(cfg0, cfg7, 22);
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ switch (fuse) {
|
|
|
++ case 8: memcpy_self(cfg0, cfg2, 22); break;
|
|
|
++ case 9: memcpy_self(cfg0, cfg3, 22); break;
|
|
|
++ case 10: memcpy_self(cfg0, cfg5, 22); break;
|
|
|
++ case 11: memcpy_self(cfg0, cfg4, 22); break;
|
|
|
++ default:
|
|
|
++ case 12: memcpy_self(cfg0, cfg1, 22); break;
|
|
|
++ case 13:
|
|
|
++ case 14: break;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ if (para->dram_type == 2) {
|
|
|
++ if (fuse == 15) return;
|
|
|
++ memcpy_self(cfg0, cfg6, 22);
|
|
|
++ }
|
|
|
++ if ( para->dram_type == 2 || para->dram_type == 3) {
|
|
|
++
|
|
|
++ val = (cfg0[ 4] << 25) | (cfg0[ 3] << 20) | (cfg0[ 2] << 15) |
|
|
|
++ (cfg0[ 1] << 10) | (cfg0[ 0] << 5);
|
|
|
++ writel(0x3102500, val);
|
|
|
++
|
|
|
++ val = (cfg0[10] << 25) | (cfg0[ 9] << 20) | (cfg0[ 8] << 15) |
|
|
|
++ (cfg0[ 7] << 10) | (cfg0[ 6] << 5) | cfg0[ 5];
|
|
|
++ writel(0x3102504, val);
|
|
|
++
|
|
|
++ val = (cfg0[15] << 20) | (cfg0[14] << 15) |
|
|
|
++ (cfg0[13] << 10) | (cfg0[12] << 5) | cfg0[11];
|
|
|
++ writel(0x3102508, val);
|
|
|
++
|
|
|
++ val = (cfg0[21] << 25) | (cfg0[20] << 20) | (cfg0[19] << 15) |
|
|
|
++ (cfg0[18] << 10) | (cfg0[17] << 5) | cfg0[16];
|
|
|
++ writel(0x310250c, val);
|
|
|
++
|
|
|
++ val = (cfg0[ 4] << 25) | (cfg0 [3] << 20) | (cfg0[2] << 15) |
|
|
|
++ (cfg0[ 1] << 10) | (cfg0 [0] << 5) | 1;
|
|
|
++ writel(0x3102500, val);
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++// Init the controller channel. The key part is placing commands in the main
|
|
|
++// command register (PIR, 0x3103000) and checking command status (PGSR0, 0x3103010).
|
|
|
++//
|
|
|
++unsigned int mctl_channel_init(unsigned int ch_index, struct dram_para_t *para)
|
|
|
++{
|
|
|
++ unsigned int val, dqs_gating_mode;
|
|
|
++
|
|
|
++ dqs_gating_mode = (para->dram_tpr13 >> 2) & 0x3;
|
|
|
++
|
|
|
++ // set DDR clock to half of CPU clock
|
|
|
++ val = readl(0x310200c) & 0xfffff000;
|
|
|
++ val |= (para->dram_clk >> 1 ) - 1;
|
|
|
++ writel(0x310200c, val);
|
|
|
++
|
|
|
++ // MRCTRL0 nibble 3 undocumented
|
|
|
++ val = readl(0x3103108) & 0xfffff0ff;
|
|
|
++ val |= 0x300;
|
|
|
++ writel(0x3103108, val);
|
|
|
++
|
|
|
++ // DX0GCR0
|
|
|
++ val = readl(0x3103344) & 0xffffffcf;
|
|
|
++ val |= ((~para->dram_odt_en) << 5) & 0x20;
|
|
|
++ if (para->dram_clk > 672) {
|
|
|
++ val &= 0xffff09f1;
|
|
|
++ val |= 0x00000400;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ val &= 0xffff0ff1;
|
|
|
++ }
|
|
|
++ writel(0x3103344, val);
|
|
|
++
|
|
|
++ // DX1GCR0
|
|
|
++ val = readl(0x31033c4) & 0xffffffcf;
|
|
|
++ val |= ((~para->dram_odt_en) << 5) & 0x20;
|
|
|
++ if (para->dram_clk > 672) {
|
|
|
++ val &= 0xffff09f1;
|
|
|
++ val |= 0x00000400;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ val &= 0xffff0ff1;
|
|
|
++ }
|
|
|
++ writel(0x31033c4, val);
|
|
|
++
|
|
|
++ // 0x3103208 undocumented
|
|
|
++ val = readl(0x3103208);
|
|
|
++ val |= 0x2;
|
|
|
++ writel(0x3103208, val);
|
|
|
++
|
|
|
++ eye_delay_compensation(para);
|
|
|
++
|
|
|
++ //set PLL SSCG ?
|
|
|
++ //
|
|
|
++ val = readl(0x3103108);
|
|
|
++ if (dqs_gating_mode == 1) {
|
|
|
++
|
|
|
++ val &= ~(0xc0);
|
|
|
++ writel(0x3103108, val);
|
|
|
++
|
|
|
++ val = readl(0x31030bc);
|
|
|
++ val &= 0xfffffef8;
|
|
|
++ writel(0x31030bc, val);
|
|
|
++ }
|
|
|
++ else if (dqs_gating_mode == 2) {
|
|
|
++
|
|
|
++ val &= ~(0xc0);
|
|
|
++ val |= 0x80;
|
|
|
++ writel(0x3103108, val);
|
|
|
++
|
|
|
++ val = readl(0x31030bc);
|
|
|
++ val &= 0xfffffef8;
|
|
|
++ val |= ((para->dram_tpr13 >> 16) & 0x1f) - 2;
|
|
|
++ val |= 0x100;
|
|
|
++ writel(0x31030bc, val);
|
|
|
++
|
|
|
++ val = readl(0x310311c) & 0x7fffffff;
|
|
|
++ val |= 0x08000000;
|
|
|
++ writel(0x310311c, val);
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ val &= ~(0x40);
|
|
|
++ writel(0x3103108, val);
|
|
|
++
|
|
|
++ udelay(10);
|
|
|
++
|
|
|
++ val = readl(0x3103108);
|
|
|
++ val |= 0xc0;
|
|
|
++ writel(0x3103108, val);
|
|
|
++ }
|
|
|
++
|
|
|
++ if (para->dram_type == 6 || para->dram_type == 7) {
|
|
|
++ val = readl(0x310311c);
|
|
|
++ if (dqs_gating_mode == 1) {
|
|
|
++ val &= 0xf7ffff3f;
|
|
|
++ val |= 0x80000000;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ val &= 0x88ffffff;
|
|
|
++ val |= 0x22000000;
|
|
|
++ }
|
|
|
++ writel(0x310311c, val);
|
|
|
++ }
|
|
|
++
|
|
|
++ val = readl(0x31030c0);
|
|
|
++ val &= 0xf0000000;
|
|
|
++ val |= (para->dram_para2 & (1 << 12)) ? 0x03000001 : 0x01000007; // 0x01003087 XXX
|
|
|
++ writel(0x31030c0, val);
|
|
|
++
|
|
|
++ if (readl(0x70005d4) & (1 << 16)) {
|
|
|
++ val = readl(0x7010250);
|
|
|
++ val &= 0xfffffffd;
|
|
|
++ writel(0x7010250, val);
|
|
|
++
|
|
|
++ udelay(10);
|
|
|
++ }
|
|
|
++
|
|
|
++ // Set ZQ config
|
|
|
++ val = readl(0x3103140) & 0xfc000000;
|
|
|
++ val |= para->dram_zq & 0x00ffffff;
|
|
|
++ val |= 0x02000000;
|
|
|
++ writel(0x3103140, val);
|
|
|
++
|
|
|
++ // Initialise DRAM controller
|
|
|
++ if (dqs_gating_mode == 1) {
|
|
|
++ writel(0x3103000, 0x52); // prep PHY reset + PLL init + z-cal
|
|
|
++ writel(0x3103000, 0x53); // Go
|
|
|
++
|
|
|
++ while ((readl(0x3103010) & 0x1) == 0) {;} // wait for IDONE
|
|
|
++ udelay(10);
|
|
|
++
|
|
|
++ // 0x520 = prep DQS gating + DRAM init + d-cal
|
|
|
++ val = (para->dram_type == 3) ? 0x5a0 // + DRAM reset
|
|
|
++ : 0x520;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ if ((readl(0x70005d4) & (1 << 16)) == 0) {
|
|
|
++ // prep DRAM init + PHY reset + d-cal + PLL init + z-cal
|
|
|
++ val = (para->dram_type == 3) ? 0x1f2 // + DRAM reset
|
|
|
++ : 0x172;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ // prep PHY reset + d-cal + z-cal
|
|
|
++ val = 0x62;
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ writel(0x3103000, val); // Prep
|
|
|
++ val |= 1;
|
|
|
++ writel(0x3103000, val); // Go
|
|
|
++
|
|
|
++ udelay(10);
|
|
|
++ while ((readl(0x3103010) & 0x1) == 0) {;} // wait for IDONE
|
|
|
++
|
|
|
++ if (readl(0x70005d4) & (1 << 16)) {
|
|
|
++
|
|
|
++ val = readl(0x310310c);
|
|
|
++ val &= 0xf9ffffff;
|
|
|
++ val |= 0x04000000;
|
|
|
++ writel(0x310310c, val);
|
|
|
++
|
|
|
++ udelay(10);
|
|
|
++
|
|
|
++ val = readl(0x3103004);
|
|
|
++ val |= 0x1;
|
|
|
++ writel(0x3103004, val);
|
|
|
++
|
|
|
++ while ((readl(0x3103018) & 0x7) != 0x3) {;}
|
|
|
++
|
|
|
++ val = readl(0x7010250);
|
|
|
++ val &= 0xfffffffe;
|
|
|
++ writel(0x7010250, val);
|
|
|
++
|
|
|
++ udelay(10);
|
|
|
++
|
|
|
++ val = readl(0x3103004);
|
|
|
++ val &= 0xfffffffe;
|
|
|
++ writel(0x3103004, val);
|
|
|
++
|
|
|
++ while ((readl(0x3103018) & 0x7) != 0x1) {;}
|
|
|
++
|
|
|
++ udelay(15);
|
|
|
++
|
|
|
++ if (dqs_gating_mode == 1) {
|
|
|
++
|
|
|
++ val = readl(0x3103108);
|
|
|
++ val &= 0xffffff3f;
|
|
|
++ writel(0x3103108, val);
|
|
|
++
|
|
|
++ val = readl(0x310310c);
|
|
|
++ val &= 0xf9ffffff;
|
|
|
++ val |= 0x02000000;
|
|
|
++ writel(0x310310c, val);
|
|
|
++
|
|
|
++ udelay(1);
|
|
|
++ writel(0x3103000, 0x401);
|
|
|
++
|
|
|
++ while ((readl(0x3103010) & 0x1) == 0) {;}
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ // Check for training error
|
|
|
++ val = readl(0x3103010);
|
|
|
++ if (((val >> 20) & 0xff) && (val & 0x100000)) {
|
|
|
++ printf("ZQ calibration error, check external 240 ohm resistor.\n");
|
|
|
++ return 0;
|
|
|
++ }
|
|
|
++
|
|
|
++ // STATR = Zynq STAT? Wait for status 'normal'?
|
|
|
++ while ((readl(0x3103018) & 0x1) == 0) {;}
|
|
|
++
|
|
|
++ val = readl(0x310308c);
|
|
|
++ val |= 0x80000000;
|
|
|
++ writel(0x310308c, val);
|
|
|
++
|
|
|
++ udelay(10);
|
|
|
++
|
|
|
++ val = readl(0x310308c);
|
|
|
++ val &= 0x7fffffff;
|
|
|
++ writel(0x310308c, val);
|
|
|
++
|
|
|
++ udelay(10);
|
|
|
++
|
|
|
++ val = readl(0x3102014);
|
|
|
++ val |= 0x80000000;
|
|
|
++ writel(0x3102014, val);
|
|
|
++
|
|
|
++ udelay(10);
|
|
|
++
|
|
|
++ val = readl(0x310310c);
|
|
|
++ val &= 0xf9ffffff;
|
|
|
++ writel(0x310310c, val);
|
|
|
++
|
|
|
++ if (dqs_gating_mode == 1) {
|
|
|
++ val = readl(0x310311c);
|
|
|
++ val &= 0xffffff3f;
|
|
|
++ val |= 0x00000040;
|
|
|
++ writel(0x310311c, val);
|
|
|
++ }
|
|
|
++ return 1;
|
|
|
++}
|
|
|
++
|
|
|
++// The below routine reads the dram config registers and extracts
|
|
|
++// the number of address bits in each rank available. It then calculates
|
|
|
++// total memory size in MB.
|
|
|
++//
|
|
|
++int DRAMC_get_dram_size(void)
|
|
|
++{
|
|
|
++ unsigned int rval, temp, size0, size1;
|
|
|
++
|
|
|
++ rval = readl(0x3102000); // MC_WORK_MODE0
|
|
|
++
|
|
|
++ temp = (rval>>8) & 0xf; // page size - 3
|
|
|
++ temp += (rval>>4) & 0xf; // row width - 1
|
|
|
++ temp += (rval>>2) & 0x3; // bank count - 2
|
|
|
++ temp -= 14; // 1MB = 20 bits, minus above 6 = 14
|
|
|
++ size0 = 1 << temp;
|
|
|
++
|
|
|
++ temp = rval & 0x3; // rank count = 0? -> done
|
|
|
++ if (temp == 0) {
|
|
|
++ return size0;
|
|
|
++ }
|
|
|
++
|
|
|
++ rval = readl(0x3102004); // MC_WORK_MODE1
|
|
|
++
|
|
|
++ temp = rval & 0x3;
|
|
|
++ if (temp == 0) { // two identical ranks
|
|
|
++ return 2 * size0;
|
|
|
++ }
|
|
|
++
|
|
|
++ temp = (rval>>8) & 0xf; // page size - 3
|
|
|
++ temp += (rval>>4) & 0xf; // row width - 1
|
|
|
++ temp += (rval>>2) & 0x3; // bank number - 2
|
|
|
++ temp -= 14; // 1MB = 20 bits, minus above 6 = 14
|
|
|
++ size1 = 1 << temp;
|
|
|
++
|
|
|
++ return size0 + size1; // add size of each rank
|
|
|
++}
|
|
|
++
|
|
|
++// The below routine reads the command status register to extract
|
|
|
++// DQ width and rank count. This follows the DQS training command in
|
|
|
++// channel_init. If error bit 22 is reset, we have two ranks and full DQ.
|
|
|
++// If there was an error, figure out whether it was half DQ, single rank,
|
|
|
++// or both. Set bit 12 and 0 in dram_para2 with the results.
|
|
|
++//
|
|
|
++int dqs_gate_detect(struct dram_para_t *para)
|
|
|
++{
|
|
|
++ unsigned int rval, dx0, dx1;
|
|
|
++
|
|
|
++ if (readl(0x3103010) & (1 << 22)) {
|
|
|
++
|
|
|
++ dx0 = (readl(0x3103348) >> 24) & 0x3;
|
|
|
++ dx1 = (readl(0x31033c8) >> 24) & 0x3;
|
|
|
++
|
|
|
++ if (dx0 == 2) {
|
|
|
++ rval = para->dram_para2;
|
|
|
++ rval &= 0xffff0ff0;
|
|
|
++ if (dx0 != dx1) {
|
|
|
++ rval |= 0x1;
|
|
|
++ para->dram_para2 = rval;
|
|
|
++ printf("[AUTO DEBUG] single rank and half DQ!\n");
|
|
|
++ return 1;
|
|
|
++ }
|
|
|
++ para->dram_para2 = rval;
|
|
|
++ printf("[AUTO DEBUG] single rank and full DQ!\n");
|
|
|
++ return 1;
|
|
|
++ }
|
|
|
++ else if (dx0 == 0) {
|
|
|
++ rval = para->dram_para2;
|
|
|
++ rval &= 0xfffffff0;
|
|
|
++ rval |= 0x00001001;
|
|
|
++ para->dram_para2 = rval;
|
|
|
++ printf("[AUTO DEBUG] dual rank and half DQ!\n");
|
|
|
++ return 1;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ if (para->dram_tpr13 & (1 << 29)) {
|
|
|
++ printf("DX0 state:%d\n", dx0);
|
|
|
++ printf("DX1 state:%d\n", dx1);
|
|
|
++ }
|
|
|
++ return 0;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ rval = para->dram_para2;
|
|
|
++ rval &= 0xfffffff0;
|
|
|
++ rval |= 0x00001000;
|
|
|
++ para->dram_para2 = rval;
|
|
|
++ printf("[AUTO DEBUG] two rank and full DQ!\n");
|
|
|
++ return 1;
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++
|
|
|
++#define SDRAM_BASE ((unsigned int *)0x40000000)
|
|
|
++#define uint unsigned int
|
|
|
++
|
|
|
++int dramc_simple_wr_test(uint mem_mb, int len)
|
|
|
++{
|
|
|
++ unsigned int offs = (mem_mb >> 1) << 18; // half of memory size
|
|
|
++ unsigned int patt1 = 0x01234567;
|
|
|
++ unsigned int patt2 = 0xfedcba98;
|
|
|
++ unsigned int *addr, v1, v2, i;
|
|
|
++
|
|
|
++ addr = SDRAM_BASE;
|
|
|
++ for (i = 0; i != len; i++, addr++) {
|
|
|
++ writel(addr, patt1 + i);
|
|
|
++ writel(addr + offs, patt2 + i);
|
|
|
++ }
|
|
|
++
|
|
|
++ addr = SDRAM_BASE;
|
|
|
++ for (i = 0; i != len; i++) {
|
|
|
++ v1 = readl(addr+i);
|
|
|
++ v2 = patt1 + i;
|
|
|
++ if (v1 != v2) {
|
|
|
++ printf("DRAM simple test FAIL.\n");
|
|
|
++ printf("%x != %x at address %p\n", v1, v2, addr+i);
|
|
|
++ return 1;
|
|
|
++ }
|
|
|
++ v1 = readl(addr+offs+i);
|
|
|
++ v2 = patt2 + i;
|
|
|
++ if (v1 != v2) {
|
|
|
++ printf("DRAM simple test FAIL.\n");
|
|
|
++ printf("%x != %x at address %p\n", v1, v2, addr+offs+i);
|
|
|
++ return 1;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ printf("DRAM simple test OK.\n");
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++
|
|
|
++// Set the Vref mode for the controller
|
|
|
++//
|
|
|
++void mctl_vrefzq_init(struct dram_para_t *para)
|
|
|
++{
|
|
|
++ unsigned int val;
|
|
|
++
|
|
|
++ if ((para->dram_tpr13 & (1 << 17)) == 0) {
|
|
|
++ val = readl(0x3103110) & 0x80808080; // IOCVR0
|
|
|
++ val |= para->dram_tpr5;
|
|
|
++ writel(0x3103110, val);
|
|
|
++
|
|
|
++ if ((para->dram_tpr13 & (1 << 16)) == 0) {
|
|
|
++ val = readl(0x3103114) & 0xffffff80; // IOCVR1
|
|
|
++ val |= para->dram_tpr6 & 0x7f;
|
|
|
++ writel(0x3103114, val);
|
|
|
++ }
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++// Perform an init of the controller. This is actually done 3 times. The first
|
|
|
++// time to establish the number of ranks and DQ width. The second time to
|
|
|
++// establish the actual ram size. The third time is final one, with the final
|
|
|
++// settings.
|
|
|
++//
|
|
|
++int mctl_core_init(struct dram_para_t *para)
|
|
|
++{
|
|
|
++ mctl_sys_init(para);
|
|
|
++ mctl_vrefzq_init(para);
|
|
|
++ mctl_com_init(para);
|
|
|
++ mctl_phy_ac_remapping(para);
|
|
|
++ auto_set_timing_para(para);
|
|
|
++ return mctl_channel_init(0, para);
|
|
|
++}
|
|
|
++
|
|
|
++
|
|
|
++#define RAM_BASE (0x40000000)
|
|
|
++
|
|
|
++// Autoscan sizes a dram device by cycling through address lines and figuring
|
|
|
++// out if it is connected to a real address line, or if the address is a mirror.
|
|
|
++// First the column and bank bit allocations are set to low values (2 and 9 address
|
|
|
++// lines. Then a maximum allocation (16 lines) is set for rows and this is tested.
|
|
|
++// Next the BA2 line is checked. This seems to be placed above the column, BA0-1 and
|
|
|
++// row addresses. Finally, the column address is allocated 13 lines and these are
|
|
|
++// tested. The results are placed in dram_para1 and dram_para2.
|
|
|
++//
|
|
|
++int auto_scan_dram_size(struct dram_para_t *para) // s7
|
|
|
++{
|
|
|
++ unsigned int rval, i, j, rank, maxrank, offs, mc_work_mode;
|
|
|
++ unsigned int chk, ptr, shft, banks;
|
|
|
++
|
|
|
++ if (mctl_core_init(para) == 0) {
|
|
|
++ printf("[ERROR DEBUG] DRAM initialisation error : 0!\n");
|
|
|
++ return 0;
|
|
|
++ }
|
|
|
++
|
|
|
++ maxrank = (para->dram_para2 & 0xf000) ? 2 : 1;
|
|
|
++ mc_work_mode = 0x3102000;
|
|
|
++ offs = 0;
|
|
|
++
|
|
|
++ // write test pattern
|
|
|
++ for (i = 0, ptr = RAM_BASE; i < 64; i++, ptr += 4) {
|
|
|
++ writel(ptr, (i & 1) ? ptr : ~ptr);
|
|
|
++ }
|
|
|
++
|
|
|
++ for (rank = 0; rank < maxrank; ) {
|
|
|
++
|
|
|
++ // Set row mode
|
|
|
++ rval = readl(mc_work_mode);
|
|
|
++ rval &= 0xfffff0f3;
|
|
|
++ rval |= 0x000006f0;
|
|
|
++ writel(mc_work_mode, rval);
|
|
|
++ while (readl(mc_work_mode) != rval);
|
|
|
++
|
|
|
++ // Scan per address line, until address wraps (i.e. see shadow)
|
|
|
++ for(i = 11; i < 17; i++) {
|
|
|
++ chk = RAM_BASE + (1 << (i + 11));
|
|
|
++ ptr = RAM_BASE;
|
|
|
++ for (j = 0; j < 64; j++) {
|
|
|
++ if (readl(chk) != ((j & 1) ? ptr : ~ptr))
|
|
|
++ goto out1;
|
|
|
++ ptr += 4;
|
|
|
++ chk += 4;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++ out1: ;
|
|
|
++ }
|
|
|
++ if (i > 16) i = 16;
|
|
|
++ printf("[AUTO DEBUG] rank %d row = %d\n", rank, i);
|
|
|
++
|
|
|
++ // Store rows in para 1
|
|
|
++ shft = 4 + offs;
|
|
|
++ rval = para->dram_para1;
|
|
|
++ rval &= ~(0xff << shft);
|
|
|
++ rval |= i << shft;
|
|
|
++ para->dram_para1 = rval;
|
|
|
++
|
|
|
++ if (rank == 1) {
|
|
|
++ // Set bank mode for rank0
|
|
|
++ rval = readl(0x3102000);
|
|
|
++ rval &= 0xfffff003;
|
|
|
++ rval |= 0x000006a4;
|
|
|
++ writel(0x3102000, rval);
|
|
|
++ }
|
|
|
++
|
|
|
++ // Set bank mode for current rank
|
|
|
++ rval = readl(mc_work_mode);
|
|
|
++ rval &= 0xfffff003;
|
|
|
++ rval |= 0x000006a4;
|
|
|
++ writel(mc_work_mode, rval);
|
|
|
++ while (readl(mc_work_mode) != rval);
|
|
|
++
|
|
|
++ // Test if bit A23 is BA2 or mirror XXX A22?
|
|
|
++ chk = RAM_BASE + (1 << 22);
|
|
|
++ ptr = RAM_BASE;
|
|
|
++ for (i = 0, j = 0; i < 64; i++) {
|
|
|
++ if (readl(chk) != ((i & 1) ? ptr : ~ptr)) {
|
|
|
++ j = 1;
|
|
|
++ break;
|
|
|
++ }
|
|
|
++ ptr += 4;
|
|
|
++ chk += 4;
|
|
|
++ }
|
|
|
++ banks = (j + 1) << 2; // 4 or 8
|
|
|
++ printf("[AUTO DEBUG] rank %d bank = %d\n", rank, banks);
|
|
|
++
|
|
|
++ // Store banks in para 1
|
|
|
++ shft = 12 + offs;
|
|
|
++ rval = para->dram_para1;
|
|
|
++ rval &= ~(0xf << shft);
|
|
|
++ rval |= j << shft;
|
|
|
++ para->dram_para1 = rval;
|
|
|
++
|
|
|
++ if (rank == 1) {
|
|
|
++ // Set page mode for rank0
|
|
|
++ rval = readl(0x3102000);
|
|
|
++ rval &= 0xfffff003;
|
|
|
++ rval |= 0x00000aa0;
|
|
|
++ writel(0x3102000, rval);
|
|
|
++ }
|
|
|
++
|
|
|
++ // Set page mode for current rank
|
|
|
++ rval = readl(mc_work_mode);
|
|
|
++ rval &= 0xfffff003;
|
|
|
++ rval |= 0x00000aa0;
|
|
|
++ writel(mc_work_mode, rval);
|
|
|
++ while (readl(mc_work_mode) != rval);
|
|
|
++
|
|
|
++ // Scan per address line, until address wraps (i.e. see shadow)
|
|
|
++ for(i = 9; i < 14; i++) {
|
|
|
++ chk = RAM_BASE + (1 << i);
|
|
|
++ ptr = RAM_BASE;
|
|
|
++ for (j = 0; j < 64; j++) {
|
|
|
++ if (readl(chk) != ((j & 1) ? ptr : ~ptr))
|
|
|
++ goto out2;
|
|
|
++ ptr += 4;
|
|
|
++ chk += 4;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++ out2:;
|
|
|
++ }
|
|
|
++ if (i > 13) i = 13;
|
|
|
++ int pgsize = (i==9) ? 0 : (1 << (i-10));
|
|
|
++ printf("[AUTO DEBUG] rank %d page size = %d KB\n", rank, pgsize);
|
|
|
++
|
|
|
++ // Store page size
|
|
|
++ shft = offs;
|
|
|
++ rval = para->dram_para1;
|
|
|
++ rval &= ~(0xf << shft);
|
|
|
++ rval |= pgsize << shft;
|
|
|
++ para->dram_para1 = rval;
|
|
|
++
|
|
|
++ // Move to next rank
|
|
|
++ rank++;
|
|
|
++ if (rank != maxrank) {
|
|
|
++ if (rank == 1) {
|
|
|
++ rval = readl(0x3202000); // MC_WORK_MODE
|
|
|
++ rval &= 0xfffff003;
|
|
|
++ rval |= 0x000006f0;
|
|
|
++ writel(0x3202000, rval);
|
|
|
++
|
|
|
++ rval = readl(0x3202004); // MC_WORK_MODE2
|
|
|
++ rval &= 0xfffff003;
|
|
|
++ rval |= 0x000006f0;
|
|
|
++ writel(0x3202004, rval);
|
|
|
++ }
|
|
|
++ offs += 16; // store rank1 config in upper half of para1
|
|
|
++ mc_work_mode += 4; // move to MC_WORK_MODE2
|
|
|
++ }
|
|
|
++ }
|
|
|
++ if (maxrank == 2) {
|
|
|
++ para->dram_para2 &= 0xfffff0ff;
|
|
|
++ // note: rval is equal to para->dram_para1 here
|
|
|
++ if ((rval & 0xffff) == ((rval >> 16) & 0xffff)) {
|
|
|
++ printf("rank1 config same as rank0\n");
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ para->dram_para2 |= 0x00000100;
|
|
|
++ printf("rank1 config different from rank0\n");
|
|
|
++ }
|
|
|
++ }
|
|
|
++ return 1;
|
|
|
++}
|
|
|
++
|
|
|
++// This routine sets up parameters with dqs_gating_mode equal to 1 and two
|
|
|
++// ranks enabled. It then configures the core and tests for 1 or 2 ranks and
|
|
|
++// full or half DQ width. it then resets the parameters to the original values.
|
|
|
++// dram_para2 is updated with the rank & width findings.
|
|
|
++//
|
|
|
++int auto_scan_dram_rank_width(struct dram_para_t *para)
|
|
|
++{
|
|
|
++ unsigned int s1 = para->dram_tpr13;
|
|
|
++ unsigned int s2 = para->dram_para1;
|
|
|
++ unsigned int v;
|
|
|
++
|
|
|
++ para->dram_para1 = 0x00b000b0;
|
|
|
++ v = (para->dram_para2 & 0xfffffff0) | 0x1000;
|
|
|
++ para->dram_para2 = v;
|
|
|
++
|
|
|
++ v = (s1 & 0xfffffff7) | 0x5; // set DQS probe mode
|
|
|
++ para->dram_tpr13 = v;
|
|
|
++
|
|
|
++ mctl_core_init(para);
|
|
|
++ if (readl(0x3103010) & (1 << 20)) {
|
|
|
++ return 0;
|
|
|
++ }
|
|
|
++ if (dqs_gate_detect(para) == 0) {
|
|
|
++ return 0;
|
|
|
++ }
|
|
|
++
|
|
|
++ para->dram_tpr13 = s1;
|
|
|
++ para->dram_para1 = s2;
|
|
|
++ return 1;
|
|
|
++}
|
|
|
++
|
|
|
++// This routine determines the sdram topology. It first establishes the number
|
|
|
++// of ranks and the DQ width. Then it scans the sdram address lines to establish
|
|
|
++// the size of each rank. It then updates dram_tpr13 to reflect that the sizes
|
|
|
++// are now known: a re-init will not repeat the autoscan.
|
|
|
++//
|
|
|
++int auto_scan_dram_config(struct dram_para_t *para)
|
|
|
++{
|
|
|
++ if (((para->dram_tpr13 & (1 << 14)) == 0) &&
|
|
|
++ (auto_scan_dram_rank_width(para) == 0))
|
|
|
++ {
|
|
|
++ printf("[ERROR DEBUG] auto scan dram rank & width failed !\n");
|
|
|
++ return 0;
|
|
|
++ }
|
|
|
++ if (((para->dram_tpr13 & (1 << 0)) == 0) &&
|
|
|
++ (auto_scan_dram_size(para) == 0 ))
|
|
|
++ {
|
|
|
++ printf("[ERROR DEBUG] auto scan dram size failed !\n");
|
|
|
++ return 0;
|
|
|
++ }
|
|
|
++ if ((para->dram_tpr13 & (1 << 15)) == 0) {
|
|
|
++ para->dram_tpr13 |= 0x6003;
|
|
|
++ }
|
|
|
++ return 1;
|
|
|
++}
|
|
|
++
|
|
|
++
|
|
|
++signed int init_DRAM(int type, struct dram_para_t *para) // s0
|
|
|
++{
|
|
|
++ int rc, mem_size;
|
|
|
++
|
|
|
++ // Test ZQ status
|
|
|
++ if (para->dram_tpr13 & (1 << 16)) {
|
|
|
++ printf("DRAM only have internal ZQ!!\n");
|
|
|
++ writel(0x3000160, readl(0x3000160) | 0x100);
|
|
|
++ writel(0x3000168, 0);
|
|
|
++ udelay(10);
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ writel(0x7010254, 0);
|
|
|
++ writel(0x3000160, readl(0x3000160) & ~0x003);
|
|
|
++ udelay(10);
|
|
|
++ writel(0x3000160, readl(0x3000160) & ~0x108);
|
|
|
++ udelay(10);
|
|
|
++ writel(0x3000160, readl(0x3000160) | 0x001);
|
|
|
++ udelay(20);
|
|
|
++ printf("ZQ value = 0x%x***********\n", readl(0x3000172));
|
|
|
++ }
|
|
|
++
|
|
|
++ // Set voltage
|
|
|
++ dram_vol_set(para);
|
|
|
++
|
|
|
++ // Set SDRAM controller auto config
|
|
|
++ if ( (para->dram_tpr13 & 0x1)==0 ) {
|
|
|
++ if ( auto_scan_dram_config(para)==0 ) {
|
|
|
++ return 0;
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ // Print header message (too late)
|
|
|
++ printf("DRAM BOOT DRIVE INFO: %s\n", "V0.24");
|
|
|
++ printf("DRAM CLK = %d MHz\n", para->dram_clk);
|
|
|
++ printf("DRAM Type = %d (2:DDR2,3:DDR3)\n", para->dram_type);
|
|
|
++ if ( (para->dram_odt_en & 0x1) == 0 ) {
|
|
|
++ printf("DRAMC read ODT off.\n");
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ printf("DRAMC ZQ value: 0x%x\n", para->dram_zq);
|
|
|
++ }
|
|
|
++
|
|
|
++ // report ODT
|
|
|
++ rc = para->dram_mr1;
|
|
|
++ if ( (rc & 0x44)==0 ) {
|
|
|
++ printf("DRAM ODT off.\n");
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ printf("DRAM ODT value: 0x%x.\n", rc);
|
|
|
++ }
|
|
|
++
|
|
|
++ // Init core, final run
|
|
|
++ if ( mctl_core_init(para)==0 ) {
|
|
|
++ printf("DRAM initialisation error : 1 !\n");
|
|
|
++ return 0;
|
|
|
++ }
|
|
|
++
|
|
|
++ // Get sdram size
|
|
|
++ rc = para->dram_para2;
|
|
|
++ if ( rc<0 ) {
|
|
|
++ rc = (rc & 0x7fff0000U) >> 16;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ rc = DRAMC_get_dram_size();
|
|
|
++ printf("DRAM SIZE =%d M\n", rc);
|
|
|
++ para->dram_para2 = (para->dram_para2 & 0xffffu) | rc << 16;
|
|
|
++ }
|
|
|
++ mem_size = rc;
|
|
|
++
|
|
|
++ // Purpose ??
|
|
|
++ if ( para->dram_tpr13 & (1 << 30) ) {
|
|
|
++ rc = readl(¶->dram_tpr8);
|
|
|
++ if ( rc==0 ) {
|
|
|
++ rc = 0x10000200;
|
|
|
++ }
|
|
|
++ writel(0x31030a0, rc);
|
|
|
++ writel(0x310309c, 0x40a);
|
|
|
++ writel(0x3103004, readl(0x3103004) | 1 );
|
|
|
++ printf("Enable Auto SR");
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ writel(0x31030a0, readl(0x31030a0) & 0xffff0000);
|
|
|
++ writel(0x3103004, readl(0x3103004) & (~0x1) );
|
|
|
++ }
|
|
|
++
|
|
|
++ // Pupose ??
|
|
|
++ rc = readl(0x3103100) & ~(0xf000);
|
|
|
++ if ( (para->dram_tpr13 & 0x200)==0 ) {
|
|
|
++ if ( para->dram_type != 6 ) {
|
|
|
++ writel(0x3103100, rc);
|
|
|
++ }
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ writel(0x3103100, rc | 0x5000);
|
|
|
++ }
|
|
|
++
|
|
|
++ writel(0x3103140, readl(0x3103140) | (1 << 31));
|
|
|
++ if (para->dram_tpr13 & (1 << 8)) {
|
|
|
++ writel(0x31030b8, readl(0x3103140) | 0x300);
|
|
|
++ }
|
|
|
++
|
|
|
++ rc = readl(0x3103108);
|
|
|
++ if (para->dram_tpr13 & (1 << 16)) {
|
|
|
++ rc &= 0xffffdfff;
|
|
|
++ }
|
|
|
++ else {
|
|
|
++ rc |= 0x00002000;
|
|
|
++ }
|
|
|
++ writel(0x3103108, rc);
|
|
|
++
|
|
|
++
|
|
|
++ // Purpose ??
|
|
|
++ if (para->dram_type == 7) {
|
|
|
++ rc = readl(0x310307c) & 0xfff0ffff;
|
|
|
++ rc |= 0x0001000;
|
|
|
++ writel(0x310307c, rc);
|
|
|
++ }
|
|
|
++
|
|
|
++ dram_enable_all_master();
|
|
|
++ //if (dramc_simple_wr_test(mem_size, 64)) return 0;
|
|
|
++ if (para->dram_tpr13 & (1 << 28)) {
|
|
|
++ rc = readl(0x70005d4);
|
|
|
++ if ( (rc & (1 << 16)) || dramc_simple_wr_test(mem_size, 4096) ) {
|
|
|
++ return 0;
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ return mem_size;
|
|
|
++}
|
|
|
++
|
|
|
++struct sunxi_ram_priv {
|
|
|
++ size_t size;
|
|
|
++};
|
|
|
++
|
|
|
++static struct dram_para_t dram_para = {
|
|
|
++ 0x00000318,
|
|
|
++ 0x00000003,
|
|
|
++ 0x007b7bfb,
|
|
|
++ 0x00000001,
|
|
|
++ 0x000010d2,
|
|
|
++ 0x00000000,
|
|
|
++ 0x00001c70,
|
|
|
++ 0x00000042,
|
|
|
++ 0x00000018,
|
|
|
++ 0x00000000,
|
|
|
++ 0x004a2195,
|
|
|
++ 0x02423190,
|
|
|
++ 0x0008b061,
|
|
|
++ 0xb4787896,
|
|
|
++ 0x00000000,
|
|
|
++ 0x48484848,
|
|
|
++ 0x00000048,
|
|
|
++ 0x1620121e,
|
|
|
++ 0x00000000,
|
|
|
++ 0x00000000,
|
|
|
++ 0x00000000,
|
|
|
++ 0x00870000,
|
|
|
++ 0x00000024,
|
|
|
++ 0x34050100,
|
|
|
++};
|
|
|
++
|
|
|
++static int sunxi_ram_probe(struct udevice *dev)
|
|
|
++{
|
|
|
++ struct sunxi_ram_priv *priv = dev_get_priv(dev);
|
|
|
++ int ret;
|
|
|
++
|
|
|
++ printf("%s: %s: probing\n", __func__, dev->name);
|
|
|
++
|
|
|
++ ret = init_DRAM(0, &dram_para);
|
|
|
++ if (ret <= 0) {
|
|
|
++ printf("DRAM init failed: %d\n", ret);
|
|
|
++ return ret;
|
|
|
++ }
|
|
|
++
|
|
|
++ priv->size = ret * 1024 * 1024;
|
|
|
++
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++
|
|
|
++static int sunxi_ram_get_info(struct udevice *dev, struct ram_info *info)
|
|
|
++{
|
|
|
++ struct sunxi_ram_priv *priv = dev_get_priv(dev);
|
|
|
++
|
|
|
++ printf("%s: %s: getting info\n", __func__, dev->name);
|
|
|
++
|
|
|
++ info->base = CONFIG_SYS_SDRAM_BASE;
|
|
|
++ info->size = priv->size;
|
|
|
++
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++
|
|
|
++static struct ram_ops sunxi_ram_ops = {
|
|
|
++ .get_info = sunxi_ram_get_info,
|
|
|
++};
|
|
|
++
|
|
|
++static const struct udevice_id sunxi_ram_ids[] = {
|
|
|
++ { .compatible = "allwinner,sun20i-d1-mbus" },
|
|
|
++ { }
|
|
|
++};
|
|
|
++
|
|
|
++U_BOOT_DRIVER(sunxi_ram) = {
|
|
|
++ .name = "sunxi_ram",
|
|
|
++ .id = UCLASS_RAM,
|
|
|
++ .of_match = sunxi_ram_ids,
|
|
|
++ .ops = &sunxi_ram_ops,
|
|
|
++ .probe = sunxi_ram_probe,
|
|
|
++ .priv_auto = sizeof(struct sunxi_ram_priv),
|
|
|
++};
|
|
|
++
|
|
|
++#endif
|
|
|
+--- /dev/null
|
|
|
++++ b/drivers/ram/sunxi/sdram.h
|
|
|
+@@ -0,0 +1,46 @@
|
|
|
++// SPDX-License-Identifier: GPL-2.0+
|
|
|
++
|
|
|
++/*
|
|
|
++ * dram_para1 bits:
|
|
|
++ * 16-19 = page size
|
|
|
++ * 20-27 = row count
|
|
|
++ * 28 = banks 4 or 8
|
|
|
++ *
|
|
|
++ * dram_para2 bits:
|
|
|
++ * 0 = DQ width
|
|
|
++ * 4 = CS1 control
|
|
|
++ * 8-11 = rank flags? bit 8 = ranks differ in config
|
|
|
++ * 12-13 = rank
|
|
|
++ */
|
|
|
++
|
|
|
++/* MC_WORK_MODE bits
|
|
|
++ * 0- 1 = ranks code
|
|
|
++ * 2- 3 = banks, log2 - 2 2 3 2
|
|
|
++ * 4- 7 = row width, log2 - 1 16 11 11
|
|
|
++ * 8-11 = page size, log2 - 3 9 9 13
|
|
|
++ * 12-15 = DQ width (or 12-14?)
|
|
|
++ * 16-18 = dram type (2=DDR2, 3=DDR3, 6=LPDDR2, 7=LPDDR3)
|
|
|
++ * 19 = 2T or 1T
|
|
|
++ * 23-24 = ranks code (again?)
|
|
|
++ */
|
|
|
++
|
|
|
++#define DRAM_MR0 ((void*)0x3103030)
|
|
|
++#define DRAM_MR1 ((void*)0x3103034)
|
|
|
++#define DRAM_MR2 ((void*)0x3103038)
|
|
|
++#define DRAM_MR3 ((void*)0x310303c)
|
|
|
++
|
|
|
++#define DRAMTMG0 ((void*)0x3103058)
|
|
|
++#define DRAMTMG1 ((void*)0x310305c)
|
|
|
++#define DRAMTMG2 ((void*)0x3103060)
|
|
|
++#define DRAMTMG3 ((void*)0x3103064)
|
|
|
++#define DRAMTMG4 ((void*)0x3103068)
|
|
|
++#define DRAMTMG5 ((void*)0x310306c)
|
|
|
++#define DRAMTMG6 ((void*)0x3103070)
|
|
|
++#define DRAMTMG7 ((void*)0x3103074)
|
|
|
++#define DRAMTMG8 ((void*)0x3103078)
|
|
|
++
|
|
|
++#define PITMG0 ((void*)0x3103080)
|
|
|
++#define PTR3 ((void*)0x3103050)
|
|
|
++#define PTR4 ((void*)0x3103054)
|
|
|
++#define RFSHTMG ((void*)0x3103090)
|
|
|
++#define RFSHCTL1 ((void*)0x3103094)
|