1225 lines
33 KiB
C
Executable File
1225 lines
33 KiB
C
Executable File
/*
|
|
* drivers/devfreq/dramfreq/sunxi-mdfs.c
|
|
*
|
|
* Copyright(c) 2013-2015 Allwinnertech Co., Ltd.
|
|
*
|
|
* Author: Pan Nan <pannan@allwinnertech.com>
|
|
*
|
|
* 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.
|
|
*/
|
|
/* History:
|
|
* 2014/05/22 ysz 1.0 average the dqs gate delay between 2 ranks after training
|
|
* 2014/06/15 ysz 1.1 LPDDR2/3 training set changed, before training, gate extend disalbe, then enable
|
|
*2014/09/24 ysz 1.2 A80 MDFS stability, make 50us delay after self refresh enter & tREFI reconfig when mdfs
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/ctype.h>
|
|
#include <asm/memory.h>
|
|
#include "sunxi-mdfs.h"
|
|
|
|
#if (0)
|
|
#define __MDFS_DEBUG
|
|
#else
|
|
#undef __MDFS_DEBUG
|
|
#endif
|
|
|
|
#ifdef __MDFS_DEBUG
|
|
#define MDFS_DBG(format, args...) mdfs_printk("[mdfs] "format, ##args)
|
|
#else
|
|
#define MDFS_DBG(format, args...)
|
|
#endif
|
|
|
|
static unsigned int __sramdata sp_backup;
|
|
static unsigned int __sramdata __sram_pllx_para;
|
|
static unsigned int __sramdata __sram_div;
|
|
static unsigned int __sramdata __sram_jump;
|
|
static __dram_para_t __sramdata __sram_dram_para;
|
|
|
|
#ifdef __MDFS_DEBUG
|
|
static void __sram __mdfs_serial_put_char(char c)
|
|
{
|
|
while (!(mctl_read_w(SUART_USR) & 2))
|
|
;
|
|
mctl_write_w(c, SUART_THR);
|
|
}
|
|
static int __sram __mdfs_serial_puts(const char *string)
|
|
{
|
|
while (*string != '\0') {
|
|
if (*string == '\n') {
|
|
/* if current character is '\n', */
|
|
/* insert output with '\r'. */
|
|
__mdfs_serial_put_char('\r');
|
|
}
|
|
__mdfs_serial_put_char(*string++);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int __sram __mdfs_strlen(const char *s)
|
|
{
|
|
const char *sc;
|
|
|
|
for (sc = s; *sc != '\0'; ++sc)
|
|
;
|
|
|
|
return sc - s;
|
|
}
|
|
static char __sram *__mdfs_strcpy(char *dest, const char *src)
|
|
{
|
|
char *tmp = dest;
|
|
|
|
while ((*dest++ = *src++) != '\0')
|
|
;
|
|
|
|
return tmp;
|
|
}
|
|
static char __sram *__mdfs_itoa(int value, char *string, int radix)
|
|
{
|
|
char stack[16];
|
|
int negative = 0; /* defualt is positive value */
|
|
int i;
|
|
int j;
|
|
char digit_string[] = "0123456789ABCDEF";
|
|
|
|
if (value == 0) {
|
|
string[0] = '0';
|
|
string[1] = '\0';
|
|
return string;
|
|
}
|
|
|
|
if (value < 0) {
|
|
/* 'value' is negative, convert to postive first */
|
|
negative = 1;
|
|
value = -value;
|
|
}
|
|
|
|
for (i = 0; value > 0; ++i) {
|
|
/* characters in reverse order are put in 'stack'. */
|
|
stack[i] = digit_string[value % radix];
|
|
value /= radix;
|
|
}
|
|
|
|
/* restore reversed order result to user string */
|
|
j = 0;
|
|
if (negative)
|
|
string[j++] = '-';
|
|
|
|
for (--i; i >= 0; --i, ++j)
|
|
string[j] = stack[i];
|
|
|
|
string[j] = '\0';
|
|
|
|
return string;
|
|
}
|
|
|
|
static char __sram *__mdfs_utoa(unsigned int value, char *string, int radix)
|
|
{
|
|
char stack[16];
|
|
int i;
|
|
int j;
|
|
char digit_string[] = "0123456789ABCDEF";
|
|
|
|
if (value == 0) {
|
|
string[0] = '0';
|
|
string[1] = '\0';
|
|
return string;
|
|
}
|
|
|
|
for (i = 0; value > 0; ++i) {
|
|
/* characters in reverse order are put in 'stack'. */
|
|
stack[i] = digit_string[value % radix];
|
|
value /= radix;
|
|
}
|
|
|
|
/* restore reversed order result to user string */
|
|
for (--i, j = 0; i >= 0; --i, ++j)
|
|
string[j] = stack[i];
|
|
|
|
/* must end with '\0'. */
|
|
string[j] = '\0';
|
|
|
|
return string;
|
|
}
|
|
|
|
static char __sram *__mdfs_strncat(char *dest, const char *src, unsigned int count)
|
|
{
|
|
char *tmp = dest;
|
|
|
|
if (count) {
|
|
while (*dest)
|
|
dest++;
|
|
|
|
while ((*dest++ = *src++) != 0) {
|
|
if (--count == 0) {
|
|
*dest = '\0';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return tmp;
|
|
}
|
|
static int __sram __mdfs_print_align(char *string, int len, int align)
|
|
{
|
|
/* fill with space ' ' when align request, */
|
|
/* the max align length is 16 byte. */
|
|
char fill_ch[] = " ";
|
|
if (len < align) {
|
|
/* fill at right */
|
|
__mdfs_strncat(string, fill_ch, align - len);
|
|
return align - len;
|
|
}
|
|
/* not fill anything */
|
|
return 0;
|
|
}
|
|
static char __sramdata mdfs_debugger_buffer[256];
|
|
static int __sram mdfs_printk(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
char string[16]; /* align by cpu word */
|
|
char *pdest, *psrc;
|
|
int align, len = 0;
|
|
|
|
pdest = mdfs_debugger_buffer;
|
|
va_start(args, format);
|
|
while (*format) {
|
|
if (*format == '%') {
|
|
++format;
|
|
if (('0' < (*format)) && ((*format) <= '9')) {
|
|
/* we just suport wide from 1 to 9. */
|
|
align = *format - '0';
|
|
++format;
|
|
} else
|
|
align = 0;
|
|
|
|
switch (*format)
|
|
case 'd':
|
|
/* int */
|
|
__mdfs_itoa(va_arg(args, int), string, 10);
|
|
len = __mdfs_strlen(string);
|
|
len += __mdfs_print_align(string, len, align);
|
|
__mdfs_strcpy(pdest, string);
|
|
pdest += len;
|
|
break;
|
|
case 'x':
|
|
case 'p':
|
|
/* hex */
|
|
__mdfs_utoa(va_arg(args, int), string, 16);
|
|
len = __mdfs_strlen(string);
|
|
len += __mdfs_print_align(string, len, align);
|
|
__mdfs_strcpy(pdest, string);
|
|
pdest += len;
|
|
break;
|
|
case 'u':
|
|
/* unsigned int */
|
|
__mdfs_utoa(va_arg(args, int), string, 10);
|
|
len = __mdfs_strlen(string);
|
|
len += __mdfs_print_align(string, len, align);
|
|
__mdfs_strcpy(pdest, string);
|
|
pdest += len;
|
|
break;
|
|
case 'c':
|
|
/* charset, aligned by cpu word */
|
|
*pdest = (char)va_arg(args, int);
|
|
break;
|
|
case 's':
|
|
/* string */
|
|
psrc = va_arg(args, char *);
|
|
__mdfs_strcpy(pdest, psrc);
|
|
pdest += __mdfs_strlen(psrc);
|
|
break;
|
|
default:
|
|
/* no-conversion */
|
|
*pdest++ = '%';
|
|
*pdest++ = *format;
|
|
}
|
|
} else
|
|
*pdest++ = *format;
|
|
|
|
/* parse next token */
|
|
++format;
|
|
}
|
|
va_end(args);
|
|
|
|
/* must end with '\0' */
|
|
*pdest = '\0';
|
|
pdest++;
|
|
__mdfs_serial_puts(mdfs_debugger_buffer);
|
|
|
|
return pdest - mdfs_debugger_buffer;
|
|
}
|
|
#endif /* __MDFS_DEBUG */
|
|
|
|
#ifndef CONFIG_ARCH_SUN8IW6P1
|
|
static unsigned long long __sram __cnt64_read(void)
|
|
{
|
|
volatile unsigned int high;
|
|
volatile unsigned int low;
|
|
unsigned long long counter;
|
|
|
|
/* latch 64bit counter and wait ready for read */
|
|
low = mctl_read_w(CNT64_CTRL_REG);
|
|
low |= (1<<1);
|
|
mctl_write_w(low, CNT64_CTRL_REG);
|
|
while (mctl_read_w(CNT64_CTRL_REG) & (1<<1))
|
|
;
|
|
|
|
low = mctl_read_w(CNT64_LOW_REG);
|
|
high = mctl_read_w(CNT64_HIGH_REG);
|
|
|
|
counter = high;
|
|
counter = (counter << 32) + low;
|
|
|
|
return counter;
|
|
}
|
|
|
|
static void __sram mdfs_udelay(unsigned int us)
|
|
{
|
|
unsigned long long expire = 0;
|
|
|
|
if (us == 0)
|
|
return;
|
|
|
|
expire = (24 * us) + __cnt64_read();
|
|
while (expire > __cnt64_read())
|
|
;
|
|
}
|
|
#endif
|
|
|
|
static unsigned int __sram __load_tlb(unsigned int addr, unsigned int len)
|
|
{
|
|
unsigned int tmp_addr = addr;
|
|
unsigned int tmp_value = 0;
|
|
|
|
while (tmp_addr < (addr + len)) {
|
|
/* access the mapping virtual addr of page table entry, */
|
|
/* it is use for load pte to TLB. */
|
|
tmp_value = *((unsigned int *)tmp_addr);
|
|
|
|
/* access the next page table entry, the least pte size of A7 is 4*1024 */
|
|
tmp_addr += 4*1024;
|
|
}
|
|
return tmp_value;
|
|
}
|
|
|
|
static void __sram mdfs_init_tlb(void)
|
|
{
|
|
/* load sram code space to tlb */
|
|
__load_tlb((unsigned int)SRAM_DDRFREQ_OFFSET, SRAM_DDRFREQ_SP_ADDR - SRAM_DDRFREQ_OFFSET);
|
|
|
|
/* load dram controller to tlb */
|
|
__load_tlb((unsigned int)MCTL_COM_BASE, 0x1000);
|
|
#if defined(CONFIG_ARCH_SUN9IW1P1)
|
|
__load_tlb((unsigned int)MCTL_CTL_BASE, 0x2000);
|
|
__load_tlb((unsigned int)MCTL_PHY_BASE, 0x2000);
|
|
#elif defined(CONFIG_ARCH_SUN8IW5P1) || defined(CONFIG_ARCH_SUN8IW6P1) || defined(CONFIG_ARCH_SUN8IW7P1)
|
|
__load_tlb((unsigned int)MCTL_CTL_BASE, 0x1000);
|
|
#endif
|
|
|
|
/* load ccm to tlb */
|
|
__load_tlb((unsigned int)CCM_PLL_BASE, 0x1000);
|
|
|
|
#ifdef __MDFS_DEBUG
|
|
/* load uart0 to tlb */
|
|
__load_tlb((unsigned int)UART_BASE, 0x1000);
|
|
#endif
|
|
|
|
#if defined(CONFIG_ARCH_SUN8IW6P1) || defined(CONFIG_ARCH_SUN8IW7P1)
|
|
/* load counter-64 to tlb */
|
|
__load_tlb((unsigned int)R_CPU_CFG_REG, 0x1000);
|
|
#else
|
|
/* load sys-power-off-gating and counter-64 to tlb */
|
|
__load_tlb((unsigned int)R_PRCM_BASE, 0x1000);
|
|
#endif
|
|
}
|
|
|
|
static void __sram mdfs_init_para(unsigned int jump, __dram_para_t *dram_para,
|
|
unsigned int pll_para_from_dram, unsigned int div)
|
|
{
|
|
__sram_dram_para.dram_clk = dram_para->dram_clk;
|
|
__sram_pllx_para = pll_para_from_dram;
|
|
__sram_jump = jump;
|
|
#if defined(CONFIG_ARCH_SUN9IW1P1)
|
|
__sram_dram_para.dram_type = dram_para->dram_type;
|
|
__sram_dram_para.dram_tpr2 = dram_para->dram_tpr2;
|
|
__sram_div = div;
|
|
#elif defined(CONFIG_ARCH_SUN8IW5P1)
|
|
__sram_dram_para.dram_tpr13 = dram_para->dram_tpr13;
|
|
__sram_dram_para.dram_tpr2 = dram_para->dram_tpr2;
|
|
#elif defined(CONFIG_ARCH_SUN8IW6P1)
|
|
__sram_dram_para.dram_odt_en = dram_para->dram_odt_en;
|
|
#endif
|
|
}
|
|
|
|
#if defined(CONFIG_ARCH_SUN9IW1P1)
|
|
static void __sram mdfs_start(int type, int freq_jump, __dram_para_t *para,
|
|
unsigned int pll6_para, unsigned int div)
|
|
{
|
|
unsigned int reg_val = 0;
|
|
unsigned int ch_num = 0;
|
|
unsigned int rank_num = 0;
|
|
unsigned int dev_clk = para->dram_clk;
|
|
unsigned int pll_ddr = pll6_para;
|
|
unsigned int ret_val = 0;
|
|
unsigned int dram_div = div;
|
|
unsigned int i = 0, j = 0;
|
|
unsigned int rank0_lcdlr2 = 0;
|
|
unsigned int rank1_lcdlr2 = 0;
|
|
unsigned int rank0_gtr = 0;
|
|
unsigned int rank1_gtr = 0;
|
|
unsigned int tprd = 0;
|
|
unsigned int zq0cr = 0;
|
|
|
|
reg_val = mctl_read_w(MC_CR);
|
|
if ((reg_val >> 19) & 0x1)
|
|
ch_num = 1; /* 0: 1 channel 1: 2 channel */
|
|
if (reg_val & 0x1)
|
|
rank_num = 1; /* 0: 1 rank 1: 2 rank */
|
|
|
|
if (ch_num) {
|
|
/* disbale dram master before change frequency */
|
|
mctl_write_w(0x1, M0_DCMDAPC); /* channel 0 access disable */
|
|
mctl_write_w(0x1, M0_DCMDAPC + 0x1000); /* channel 1 access disable */
|
|
mdfs_udelay(20);
|
|
|
|
/* the max delay between self exit to valid command is txsdll */
|
|
mctl_write_w(0x9, M0_DRAMTMG8);
|
|
mctl_write_w(0x9, M0_DRAMTMG8 + 0x1000);
|
|
|
|
/* disable ZQC before MDFS */
|
|
zq0cr = mctl_read_w(P0_ZQ0CR);
|
|
reg_val = (zq0cr & (~(0x7<<11)));
|
|
mctl_write_w(reg_val, P0_ZQ0CR);
|
|
mctl_write_w(reg_val, P0_ZQ0CR + 0x1000);
|
|
|
|
/* disable auto refresh */
|
|
mctl_write_w(0x01, M0_RFSHCTL3);
|
|
mctl_write_w(0x01, M0_RFSHCTL3 + 0x1000);
|
|
|
|
/* update tREFI */
|
|
reg_val = mctl_read_w(M0_RFSHTMG);
|
|
reg_val &= ~(0xfff0000);
|
|
if (dev_clk < 300)
|
|
reg_val |= (((para->dram_tpr2) & 0xffc)<<14);
|
|
else if (dev_clk < 672)
|
|
reg_val |= (((para->dram_tpr2) & 0xffe)<<15);
|
|
else
|
|
reg_val |= (((para->dram_tpr2) & 0xfff)<<16);
|
|
mctl_write_w(reg_val, M0_RFSHTMG);
|
|
mctl_write_w(reg_val, M0_RFSHTMG + 0x1000);
|
|
/* tREFI end */
|
|
|
|
/* enter self-refersh before power down */
|
|
mctl_write_w(1, M0_PWRCTL);
|
|
mctl_write_w(1, M0_PWRCTL + 0x1000);
|
|
mdfs_udelay(10);
|
|
|
|
/* make sure enter self-refresh */
|
|
while (((mctl_read_w(M0_STATR) & 0x7) != 0x3))
|
|
;
|
|
while (((mctl_read_w(M0_STATR + 0x1000) & 0x7) != 0x3))
|
|
;
|
|
|
|
if (dram_div != 0) {
|
|
reg_val = mctl_read_w(CCM_DRAMCLK_CFG_REG);
|
|
reg_val &= ~(0xf<<8);
|
|
reg_val |= (0x3<<12);
|
|
reg_val |= ((dram_div-1)<<8);
|
|
mctl_write_w(reg_val, CCM_DRAMCLK_CFG_REG);
|
|
reg_val |= (0x1<<16);
|
|
mctl_write_w(reg_val, CCM_DRAMCLK_CFG_REG);
|
|
mdfs_udelay(1);
|
|
} else {
|
|
if ((dev_clk == 480) || (dev_clk == 240)) {
|
|
reg_val = mctl_read_w(CCM_DRAMCLK_CFG_REG);
|
|
reg_val &= ~(0xf<<8);
|
|
reg_val &= ~(0x3<<12);
|
|
if (dev_clk == 240)
|
|
reg_val |= (0x1<<8);
|
|
mctl_write_w(reg_val, CCM_DRAMCLK_CFG_REG);
|
|
reg_val |= (0x1<<16);
|
|
mctl_write_w(reg_val, CCM_DRAMCLK_CFG_REG);
|
|
mdfs_udelay(1);
|
|
} else {
|
|
/* set pll stable time 80us */
|
|
mctl_write_w(0x50, CCM_PLL_BASE + 0x090);
|
|
|
|
reg_val = mctl_read_w(CCM_DRAMCLK_CFG_REG);
|
|
reg_val &= ~(0xf<<8);
|
|
reg_val |= (0x3<<12);
|
|
mctl_write_w(reg_val, CCM_DRAMCLK_CFG_REG);
|
|
reg_val |= (0x1<<16);
|
|
mctl_write_w(reg_val, CCM_DRAMCLK_CFG_REG);
|
|
mdfs_udelay(1);
|
|
|
|
mctl_write_w(pll_ddr, CCM_PLL6_DDR_REG);
|
|
pll_ddr |= (1U << 30);
|
|
mctl_write_w(pll_ddr, CCM_PLL6_DDR_REG); /* Update PLL6 Setting */
|
|
|
|
/* make delay for debug at 20140909 */
|
|
mdfs_udelay(1);
|
|
|
|
do {
|
|
reg_val = (mctl_read_w(CCM_PLL_BASE + 0x09c) >> 5) & 0x1;
|
|
} while (reg_val == 0);
|
|
}
|
|
}
|
|
|
|
/* set pll lock time */
|
|
mctl_write_w(0x25800708, P0_PTR1);
|
|
mctl_write_w(0x25800708, P0_PTR1 + 0x1000);
|
|
/* trigger, PHY reset/DDL cal */
|
|
if (dev_clk <= 800) /* PLL disable */
|
|
ret_val = 0x40020061;
|
|
else
|
|
ret_val = 0x40000071; /* PLL enable */
|
|
mctl_write_w(ret_val, P0_PIR);
|
|
mctl_write_w(ret_val, P0_PIR + 0x1000);
|
|
mdfs_udelay(1);/* 1us delay here */
|
|
while ((mctl_read_w(P0_PGSR0) & 0x1) != 0x1)
|
|
; /* wait for DLL Lock */
|
|
while ((mctl_read_w(P0_PGSR0 + 0x1000) & 0x1) != 0x1)
|
|
; /* wait for DLL Lock */
|
|
|
|
/* exit self refresh */
|
|
mctl_write_w(0, M0_PWRCTL);
|
|
mctl_write_w(0, M0_PWRCTL + 0x1000);
|
|
|
|
/* make delay for debug at 20140909 */
|
|
mdfs_udelay(200);/* 20fial/100 ok */
|
|
|
|
while (((mctl_read_w(M0_STATR) & 0x7) != 0x1))
|
|
;
|
|
while (((mctl_read_w(M0_STATR + 0x1000) & 0x7) != 0x1))
|
|
;
|
|
|
|
/* DQS Gate extend should be disabled before training */
|
|
reg_val = mctl_read_w(P0_DSGCR);
|
|
reg_val &= (~(0x3<<6)); /* set DQSGX to 1 */
|
|
mctl_write_w(reg_val, P0_DSGCR);
|
|
|
|
mctl_write_w(reg_val, P0_DSGCR + 0x1000);
|
|
|
|
/* DQS gate trainning */
|
|
if (dev_clk <= 800) {
|
|
if (para->dram_type == 3)
|
|
ret_val = 0x40020c01;
|
|
else
|
|
ret_val = 0x40020401;/* ret_val = 0x40020c01; */
|
|
} else {
|
|
if (para->dram_type == 3)
|
|
ret_val = 0x40000c01;
|
|
else
|
|
ret_val = 0x40000401;/* ret_val = 0x40000c01; */
|
|
}
|
|
mctl_write_w(ret_val, P0_PIR);
|
|
mctl_write_w(ret_val, P0_PIR + 0x1000);
|
|
mdfs_udelay(1);/* 1us delay here */
|
|
while ((mctl_read_w(P0_PGSR0) & 0x1) != 0x1)
|
|
; /* wait for DLL Lock */
|
|
while ((mctl_read_w(P0_PGSR0 + 0x1000) & 0x1) != 0x1)
|
|
; /* wait for DLL Lock */
|
|
|
|
/* in order to save power,disable DRAM ODT */
|
|
if (para->dram_type == 3) {
|
|
if (dev_clk < 400) {
|
|
mctl_write_w(0x0, M0_ODTMAP);
|
|
mctl_write_w(0x0, M0_ODTMAP + 0x1000);
|
|
} else {
|
|
mctl_write_w(0x00002211, M0_ODTMAP);
|
|
mctl_write_w(0x00002211, M0_ODTMAP + 0x1000);
|
|
}
|
|
}
|
|
|
|
if (rank_num) {
|
|
/* set the dqs gate delay to average between 2rank */
|
|
for (i = 0; i < 4; i++) {
|
|
reg_val = mctl_read_w(P0_DX0LCDLR2 + i * 0x80);
|
|
rank0_lcdlr2 = (reg_val & 0xff);
|
|
rank1_lcdlr2 = ((reg_val>>8) & 0xff);
|
|
|
|
reg_val = mctl_read_w(P0_DX0GTR + i * 0x80);
|
|
rank0_gtr = (reg_val & 0x7);
|
|
rank1_gtr = ((reg_val>>3) & 0x7);
|
|
|
|
reg_val = mctl_read_w(P0_DX0MDLR + i * 0x80);
|
|
tprd = ((reg_val >> 8) & 0xff);
|
|
|
|
/* calculate the average delay */
|
|
reg_val = (rank0_lcdlr2 + rank1_lcdlr2 + ((rank0_gtr + rank1_gtr) * tprd));
|
|
reg_val >>= 1;
|
|
|
|
for (j = 0; tprd <= reg_val; j++)
|
|
reg_val -= tprd;
|
|
rank0_lcdlr2 = reg_val;
|
|
rank1_lcdlr2 = reg_val;
|
|
rank0_gtr = j;
|
|
rank1_gtr = j;
|
|
mctl_write_w((rank0_lcdlr2 | (rank1_lcdlr2<<8)), (P0_DX0LCDLR2 + i * 0x80));
|
|
|
|
reg_val = mctl_read_w(P0_DX0GTR + i * 0x80);
|
|
reg_val &= ~(0x3f);
|
|
reg_val |= ((rank1_gtr<<3) | rank0_gtr);
|
|
mctl_write_w(reg_val, (P0_DX0GTR + i * 0x80));
|
|
}
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
reg_val = mctl_read_w(P0_DX0LCDLR2 + 0x1000 + i * 0x80);
|
|
rank0_lcdlr2 = (reg_val & 0xff);
|
|
rank1_lcdlr2 = ((reg_val>>8) & 0xff);
|
|
|
|
reg_val = mctl_read_w(P0_DX0GTR + 0x1000 + i * 0x80);
|
|
rank0_gtr = (reg_val & 0x7);
|
|
rank1_gtr = ((reg_val>>3) & 0x7);
|
|
|
|
reg_val = mctl_read_w(P0_DX0MDLR + 0x1000 + i * 0x80);
|
|
tprd = ((reg_val >> 8) & 0xff);
|
|
|
|
/* calculate the average delay */
|
|
reg_val = (rank0_lcdlr2 + rank1_lcdlr2 + ((rank0_gtr + rank1_gtr) * tprd));
|
|
reg_val >>= 1;
|
|
|
|
for (j = 0; tprd <= reg_val; j++)
|
|
reg_val -= tprd;
|
|
rank0_lcdlr2 = reg_val;
|
|
rank1_lcdlr2 = reg_val;
|
|
rank0_gtr = j;
|
|
rank1_gtr = j;
|
|
mctl_write_w((rank0_lcdlr2 | (rank1_lcdlr2<<8)), (P0_DX0LCDLR2 + 0x1000 + i * 0x80));
|
|
|
|
reg_val = mctl_read_w(P0_DX0GTR + 0x1000 + i * 0x80);
|
|
reg_val &= ~(0x3f);
|
|
reg_val |= ((rank1_gtr<<3) | rank0_gtr);
|
|
mctl_write_w(reg_val, (P0_DX0GTR + 0x1000 + i * 0x80));
|
|
}
|
|
}
|
|
|
|
/* LPDDR2 and LPDDR3 */
|
|
if ((para->dram_type) == 6 || (para->dram_type) == 7) {
|
|
reg_val = mctl_read_w(P0_DSGCR);
|
|
reg_val &= (~(0x3<<6)); /* set DQSGX to 1 */
|
|
reg_val |= (0x1<<6); /* dqs gate extend */
|
|
mctl_write_w(reg_val, P0_DSGCR);
|
|
|
|
mctl_write_w(reg_val, P0_DSGCR + 0x1000);
|
|
}
|
|
|
|
/* enable auto-refresh-------------add at 20140917 */
|
|
mctl_write_w(0x0, M0_RFSHCTL3);
|
|
mctl_write_w(0x0, M0_RFSHCTL3 + 0x1000);
|
|
/* end of 20140917 */
|
|
|
|
/* channel master access enable */
|
|
mctl_write_w(0x0, M0_DCMDAPC); /* channel 0 access enable */
|
|
mctl_write_w(0x0, M0_DCMDAPC + 0x1000); /* channel 1 access enable */
|
|
|
|
/* enable ZQC after MDFS */
|
|
mctl_write_w(zq0cr, P0_ZQ0CR);
|
|
mctl_write_w(zq0cr, P0_ZQ0CR + 0x1000);
|
|
} else {
|
|
/* disable dram master before change frequency */
|
|
mctl_write_w(0x1, M0_DCMDAPC); /* channel 0 access disable */
|
|
mdfs_udelay(20);
|
|
|
|
/* the max delay between self exit to valid command is txsdll */
|
|
mctl_write_w(0x9, M0_DRAMTMG8);
|
|
|
|
/* Disable ZQC before MDFS */
|
|
zq0cr = mctl_read_w(P0_ZQ0CR);
|
|
reg_val = (zq0cr & (~(0x7<<11)));
|
|
mctl_write_w(reg_val, P0_ZQ0CR);
|
|
|
|
/* disable auto refresh */
|
|
mctl_write_w(0x01, M0_RFSHCTL3);
|
|
|
|
/* update tREFI */
|
|
reg_val = mctl_read_w(M0_RFSHTMG);
|
|
reg_val &= ~(0xfff0000);
|
|
if (dev_clk < 300)
|
|
reg_val |= (((para->dram_tpr2) & 0xffc)<<14);
|
|
else if (dev_clk < 672)
|
|
reg_val |= (((para->dram_tpr2) & 0xffe)<<15);
|
|
else
|
|
reg_val |= (((para->dram_tpr2) & 0xfff)<<16);
|
|
mctl_write_w(reg_val, M0_RFSHTMG);
|
|
/* tREFI end */
|
|
|
|
/* enter self-refersh before power down */
|
|
mctl_write_w(1, M0_PWRCTL);
|
|
|
|
/* make sure enter self-refresh */
|
|
while (((mctl_read_w(M0_STATR) & 0x7) != 0x3))
|
|
;
|
|
|
|
if (dram_div != 0) {
|
|
reg_val = mctl_read_w(CCM_DRAMCLK_CFG_REG);
|
|
reg_val &= ~(0xf<<8);
|
|
reg_val |= (0x3<<12);
|
|
reg_val |= ((dram_div-1)<<8);
|
|
mctl_write_w(reg_val, CCM_DRAMCLK_CFG_REG);
|
|
reg_val |= (0x1<<16);
|
|
mctl_write_w(reg_val, CCM_DRAMCLK_CFG_REG);
|
|
mdfs_udelay(1);
|
|
} else {
|
|
if ((dev_clk == 480) || (dev_clk == 240)) {
|
|
reg_val = mctl_read_w(CCM_DRAMCLK_CFG_REG);
|
|
reg_val &= ~(0xf<<8);
|
|
reg_val &= ~(0x3<<12);
|
|
if (dev_clk == 240)
|
|
reg_val |= (0x1<<8);
|
|
mctl_write_w(reg_val, CCM_DRAMCLK_CFG_REG);
|
|
reg_val |= (0x1<<16);
|
|
mctl_write_w(reg_val, CCM_DRAMCLK_CFG_REG);
|
|
mdfs_udelay(1);
|
|
} else {
|
|
/* set pll stable time 80us */
|
|
mctl_write_w(0x50, CCM_PLL_BASE + 0x090);
|
|
reg_val = mctl_read_w(CCM_DRAMCLK_CFG_REG);
|
|
reg_val &= ~(0xf<<8);
|
|
reg_val |= (0x3<<12);
|
|
mctl_write_w(reg_val, CCM_DRAMCLK_CFG_REG);
|
|
reg_val |= (0x1<<16);
|
|
mctl_write_w(reg_val, CCM_DRAMCLK_CFG_REG);
|
|
mdfs_udelay(1);
|
|
|
|
mctl_write_w(pll_ddr, CCM_PLL6_DDR_REG);
|
|
pll_ddr |= (1U << 30);
|
|
mctl_write_w(pll_ddr, CCM_PLL6_DDR_REG); /* Update PLL6 Setting */
|
|
|
|
do {
|
|
reg_val = (mctl_read_w(CCM_PLL_BASE + 0x09c) >> 5) & 0x1;
|
|
} while (reg_val == 0);
|
|
}
|
|
}
|
|
|
|
/* set phy pll lock time */
|
|
mctl_write_w(0x25800708, P0_PTR1);
|
|
/* trigger, PHY reset/DDL cal */
|
|
if (dev_clk <= 800) /* PLL disable */
|
|
ret_val = 0x40020061;
|
|
else
|
|
ret_val = 0x40000071; /* PLL enable */
|
|
mctl_write_w(ret_val, P0_PIR);
|
|
mdfs_udelay(1);/* 1us delay here */
|
|
while ((mctl_read_w(P0_PGSR0) & 0x1) != 0x1)
|
|
; /* wait for DLL Lock */
|
|
|
|
/* exit self refresh */
|
|
mctl_write_w(0, M0_PWRCTL);
|
|
|
|
mdfs_udelay(200);/* 20fial/100 ok */
|
|
|
|
while (((mctl_read_w(M0_STATR) & 0x7) != 0x1))
|
|
;
|
|
|
|
/* DQS Gate extend should be disabled before training */
|
|
reg_val = mctl_read_w(P0_DSGCR);
|
|
reg_val &= (~(0x3<<6)); /* set DQSGX to 1 */
|
|
mctl_write_w(reg_val, P0_DSGCR);
|
|
|
|
/* DQS gate trainning */
|
|
if (dev_clk <= 800) {
|
|
if (para->dram_type == 3)
|
|
ret_val = 0x40020c01;
|
|
else
|
|
ret_val = 0x40020401;/* ret_val = 0x40020c01; */
|
|
} else {
|
|
if (para->dram_type == 3)
|
|
ret_val = 0x40000c01;
|
|
else
|
|
ret_val = 0x40000401;/* ret_val = 0x40000c01; */
|
|
}
|
|
mctl_write_w(ret_val, P0_PIR);
|
|
mdfs_udelay(1);/* 1us delay here */
|
|
while ((mctl_read_w(P0_PGSR0) & 0x1) != 0x1)
|
|
; /* wait for DLL Lock */
|
|
|
|
/* in order to save power,disable DRAM ODT */
|
|
if (para->dram_type == 3) {
|
|
if (dev_clk < 400)
|
|
mctl_write_w(0x0, M0_ODTMAP);
|
|
else
|
|
mctl_write_w(0x00002211, M0_ODTMAP);
|
|
}
|
|
|
|
if (rank_num) {
|
|
/* set the dqs gate delay to average between 2rank */
|
|
for (i = 0; i < 4; i++) {
|
|
reg_val = mctl_read_w(P0_DX0LCDLR2 + i * 0x80);
|
|
rank0_lcdlr2 = (reg_val & 0xff);
|
|
rank1_lcdlr2 = ((reg_val>>8) & 0xff);
|
|
|
|
reg_val = mctl_read_w(P0_DX0GTR + i * 0x80);
|
|
rank0_gtr = (reg_val & 0x7);
|
|
rank1_gtr = ((reg_val>>3) & 0x7);
|
|
|
|
reg_val = mctl_read_w(P0_DX0MDLR + i * 0x80);
|
|
tprd = ((reg_val >> 8) & 0xff);
|
|
|
|
/* calculate the average delay */
|
|
reg_val = (rank0_lcdlr2 + rank1_lcdlr2 + ((rank0_gtr + rank1_gtr) * tprd));
|
|
reg_val >>= 1;
|
|
|
|
for (j = 0; tprd <= reg_val; j++)
|
|
reg_val -= tprd;
|
|
rank0_lcdlr2 = reg_val;
|
|
rank1_lcdlr2 = reg_val;
|
|
rank0_gtr = j;
|
|
rank1_gtr = j;
|
|
mctl_write_w((rank0_lcdlr2 | (rank1_lcdlr2<<8)), (P0_DX0LCDLR2 + i * 0x80));
|
|
|
|
reg_val = mctl_read_w(P0_DX0GTR + i * 0x80);
|
|
reg_val &= ~(0x3f);
|
|
reg_val |= ((rank1_gtr<<3) | rank0_gtr);
|
|
mctl_write_w(reg_val, (P0_DX0GTR + i * 0x80));
|
|
}
|
|
}
|
|
|
|
/* LPDDR2 and LPDDR3 */
|
|
if ((para->dram_type) == 6 || (para->dram_type) == 7) {
|
|
reg_val = mctl_read_w(P0_DSGCR);
|
|
reg_val &= (~(0x3<<6)); /* set DQSGX to 1 */
|
|
reg_val |= (0x1<<6); /* dqs gate extend */
|
|
mctl_write_w(reg_val, P0_DSGCR);
|
|
}
|
|
|
|
/* enable auto-refresh-------------add at 20140917 */
|
|
mctl_write_w(0x0, M0_RFSHCTL3);
|
|
|
|
/* channel 0 access enable */
|
|
mctl_write_w(0x0, M0_DCMDAPC);
|
|
|
|
/* enable ZQC after MDFS */
|
|
mctl_write_w(zq0cr, P0_ZQ0CR);
|
|
}
|
|
|
|
#if 0
|
|
/* the max delay between self exit to valid command is txsdll */
|
|
mctl_write_w(0x8, M0_DRAMTMG8);
|
|
mctl_write_w(0x8, M0_DRAMTMG8 + 0x1000);
|
|
|
|
if (dev_clk < 200) {
|
|
/* auto self-refersh and close DRAM CLK */
|
|
reg_val = mctl_read_w(M0_PWRCTL);
|
|
reg_val |= ((0x1<<0) | (0x1<<3));
|
|
mctl_write_w(reg_val, M0_PWRCTL);
|
|
reg_val = mctl_read_w(M0_PWRCTL + 0x1000);
|
|
reg_val |= ((0x1<<0) | (0x1<<3));
|
|
mctl_write_w(reg_val, M0_PWRCTL + 0x1000);
|
|
} else {
|
|
/* auto self-refersh and close DRAM CLK */
|
|
reg_val = mctl_read_w(M0_PWRCTL);
|
|
reg_val &= ~((0x1<<0) | (0x1<<3));
|
|
mctl_write_w(reg_val, M0_PWRCTL);
|
|
reg_val = mctl_read_w(M0_PWRCTL + 0x1000);
|
|
reg_val &= ~((0x1<<0) | (0x1<<3));
|
|
mctl_write_w(reg_val, M0_PWRCTL + 0x1000);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#elif defined(CONFIG_ARCH_SUN8IW5P1)
|
|
static void __sram mdfs_start(int type, int freq_jump, __dram_para_t *para,
|
|
unsigned int pllx_para, unsigned int div)
|
|
{
|
|
unsigned int pll_source = 0;
|
|
unsigned int reg_val = 0;
|
|
unsigned int m_div = 0;
|
|
unsigned int rank_num = 0;
|
|
unsigned int trefi = 0;
|
|
unsigned int trfc = 0;
|
|
|
|
if (para->dram_clk <= 192) {
|
|
trefi = 0x16;
|
|
trfc = 0x21;
|
|
} else {
|
|
trefi = (para->dram_tpr2>>0)&0xfff;
|
|
trfc = (para->dram_tpr2>>12)&0xfff;
|
|
}
|
|
|
|
pll_source = (mctl_read_w(_CCM_PLL_DDR_CFG_REG) >> 16) & 0x1;
|
|
/* disbale master access */
|
|
mctl_write_w(0x0, MC_MAER);
|
|
|
|
/* enter self-refresh and pad hold */
|
|
reg_val = mctl_read_w(PWRCTL);
|
|
reg_val |= 0x1<<0;
|
|
reg_val |= 0x1<<8;
|
|
mctl_write_w(reg_val, PWRCTL);
|
|
mdfs_udelay(100);
|
|
|
|
/* set new refresh timing */
|
|
reg_val = 0;
|
|
reg_val = (trefi<<16)|(trfc<<0);
|
|
mctl_write_w(reg_val, RFSHTMG);
|
|
|
|
/* confirm dram controller has enter selfrefresh */
|
|
while (((mctl_read_w(STATR) & 0x7) != 0x3))
|
|
;
|
|
|
|
/* pad hold */
|
|
reg_val = mctl_read_w(VDD_SYS_POFF_GATING);
|
|
reg_val |= 0x3<<0;
|
|
mctl_write_w(reg_val, VDD_SYS_POFF_GATING);
|
|
mdfs_udelay(10);
|
|
|
|
/* disable global clk */
|
|
mctl_write_w(0x0, MC_CLKEN);
|
|
/* disable pll-ddr0 */
|
|
reg_val = mctl_read_w(_CCM_PLL_DDR0_REG);
|
|
reg_val &= ~((1U<<31)|(0x1<<24));
|
|
mctl_write_w(reg_val, _CCM_PLL_DDR0_REG);
|
|
/* disable pll-ddr1 */
|
|
reg_val = mctl_read_w(_CCM_PLL_DDR1_REG);
|
|
reg_val &= ~((1U<<31)|(0x1<<24));
|
|
mctl_write_w(reg_val, _CCM_PLL_DDR1_REG);
|
|
mdfs_udelay(100);
|
|
|
|
/* change pll-div and set global clk and phy pll */
|
|
if (para->dram_clk <= 192) {
|
|
/* set pll 1344 div 2 */
|
|
if (pll_source) {
|
|
mctl_write_w(0x0, _CCM_PLL_DDR1_PATTERN_REG);
|
|
mctl_write_w(0x80003700, _CCM_PLL_DDR1_REG);
|
|
mctl_write_w(0xc0003700, _CCM_PLL_DDR1_REG);
|
|
mdfs_udelay(1000);
|
|
while (mctl_read_w(_CCM_PLL_DDR1_REG) & (0x1<<30))
|
|
;
|
|
} else {
|
|
mctl_write_w(0x0, _CCM_PLL_DDR0_PATTERN_REG);
|
|
mctl_write_w(0x80001b10, _CCM_PLL_DDR0_REG);
|
|
mctl_write_w(0x80101b10, _CCM_PLL_DDR0_REG);
|
|
mdfs_udelay(1000);
|
|
while (mctl_read_w(_CCM_PLL_DDR0_REG) & (0x1<<20))
|
|
;
|
|
}
|
|
|
|
reg_val = mctl_read_w(CCM_DRAM_CFG_REG);
|
|
reg_val &= ~(0xf<<0);
|
|
reg_val |= (0x1<<0);
|
|
mctl_write_w(reg_val, CCM_DRAM_CFG_REG);
|
|
reg_val |= (0x1<<16); /* update */
|
|
mctl_write_w(reg_val, CCM_DRAM_CFG_REG);
|
|
while (mctl_read_w(CCM_DRAM_CFG_REG) & (0x1<<16))
|
|
;
|
|
mdfs_udelay(100);
|
|
mctl_write_w(0xc10f, MC_CLKEN);
|
|
mdfs_udelay(100);
|
|
|
|
/* disable PHY PLL */
|
|
reg_val = mctl_read_w(PLLGCR);
|
|
reg_val |= ((0x1U<<29) | (0x1U<<31)); /* PLL disable */
|
|
mctl_write_w(reg_val, PLLGCR);
|
|
|
|
/* phy pll reset */
|
|
reg_val = mctl_read_w(PLLGCR);
|
|
reg_val |= (0x1U<<30);
|
|
mctl_write_w(reg_val, PLLGCR);
|
|
mdfs_udelay(100);
|
|
reg_val = mctl_read_w(PLLGCR);
|
|
reg_val &= ~(0x1U<<30);
|
|
mctl_write_w(reg_val, PLLGCR);
|
|
mdfs_udelay(100);
|
|
} else {
|
|
/* normal status */
|
|
if (pll_source) {
|
|
mctl_write_w(pllx_para, _CCM_PLL_DDR1_REG);
|
|
mctl_write_w((pllx_para | (0x1U<<30)), _CCM_PLL_DDR1_REG);
|
|
mdfs_udelay(1000);
|
|
m_div = 4;
|
|
} else {
|
|
mctl_write_w(pllx_para, _CCM_PLL_DDR0_REG);
|
|
mctl_write_w((pllx_para | (0x1U<<20)), _CCM_PLL_DDR0_REG);
|
|
mdfs_udelay(1000);
|
|
m_div = 2;
|
|
}
|
|
|
|
/* dram pll EMC function */
|
|
if ((para->dram_tpr13 >> 16) & 0x3f) {
|
|
if ((para->dram_tpr13 >> 8) & 0x1) {
|
|
if (para->dram_tpr13 & (0x1 << 16))
|
|
mctl_write_w(((0x3U<<17)|(0x158U<<20)|(0x3U<<29)|(0x1U<<31)), _CCM_PLL_DDR1_PATTERN_REG);
|
|
|
|
if (para->dram_tpr13 & (0x1 << 17))
|
|
mctl_write_w((0x1999U|(0x3U<<17)|(0x135U<<20)|(0x3U<<29)|(0x1U<<31)), _CCM_PLL_DDR1_PATTERN_REG);
|
|
|
|
if (para->dram_tpr13 & (0x1 << 18))
|
|
mctl_write_w((0x3333U|(0x3U<<17)|(0x120U<<20)|(0x3U<<29)|(0x1U<<31)), _CCM_PLL_DDR1_PATTERN_REG);
|
|
else if (para->dram_tpr13 & (0x1<<19))
|
|
mctl_write_w((0x6666U|(0x3U<<17)|(0xD8U<<20)|(0x3U<<29)|(0x1U<<31)), _CCM_PLL_DDR1_PATTERN_REG);
|
|
else if (para->dram_tpr13 & (0x1<<20))
|
|
mctl_write_w((0x9999U|(0x3U<<17)|(0x90U<<20)|(0x3U<<29)|(0x1U<<31)), _CCM_PLL_DDR1_PATTERN_REG);
|
|
else if (para->dram_tpr13 & (0x1<<21))
|
|
mctl_write_w((0xccccU|(0x3U<<17)|(0x48U<<20)|(0x3U<<29)|(0x1U<<31)), _CCM_PLL_DDR1_PATTERN_REG);
|
|
|
|
reg_val = mctl_read_w(_CCM_PLL_DDR1_REG);
|
|
reg_val |= ((0x1U<<24)|(0x1U<<30));
|
|
mctl_write_w(reg_val, _CCM_PLL_DDR1_REG);
|
|
while (mctl_read_w(_CCM_PLL_DDR1_REG) & (0x1 << 30))
|
|
;
|
|
} else {
|
|
if (para->dram_tpr13 & (0x1 << 18))
|
|
mctl_write_w((0x3333U|(0x3U<<17)|(0x120U<<20)|(0x3U<<29)|(0x1U<<31)), _CCM_PLL_DDR0_PATTERN_REG);
|
|
else if (para->dram_tpr13 & (0x1 << 19))
|
|
mctl_write_w((0x6666U|(0x3U<<17)|(0xD8U<<20)|(0x3U<<29)|(0x1U<<31)), _CCM_PLL_DDR0_PATTERN_REG);
|
|
else if (para->dram_tpr13 & (0x1 << 20))
|
|
mctl_write_w((0x9999U|(0x3U<<17)|(0x90U<<20)|(0x3U<<29)|(0x1U<<31)), _CCM_PLL_DDR0_PATTERN_REG);
|
|
else if (para->dram_tpr13 & (0x1 << 21))
|
|
mctl_write_w((0xccccU|(0x3U<<17)|(0x48U<<20)|(0x3U<<29)|(0x1U<<31)), _CCM_PLL_DDR0_PATTERN_REG);
|
|
|
|
reg_val = mctl_read_w(_CCM_PLL_DDR0_REG);
|
|
reg_val |= ((0x1U<<24)|(0x1U<<20));
|
|
mctl_write_w(reg_val, _CCM_PLL_DDR0_REG);
|
|
while (mctl_read_w(_CCM_PLL_DDR0_REG) & (0x1<<20))
|
|
;
|
|
}
|
|
}
|
|
|
|
reg_val = mctl_read_w(CCM_DRAM_CFG_REG);
|
|
reg_val &= ~(0xf<<0);
|
|
reg_val |= ((m_div - 1)<<0);
|
|
mctl_write_w(reg_val, CCM_DRAM_CFG_REG);
|
|
reg_val |= (0x1<<16);
|
|
mctl_write_w(reg_val, CCM_DRAM_CFG_REG);
|
|
while (mctl_read_w(CCM_DRAM_CFG_REG) & (0x1<<16))
|
|
;
|
|
mdfs_udelay(100);
|
|
mctl_write_w(0x400f, MC_CLKEN);
|
|
mdfs_udelay(100);
|
|
|
|
/* enable PHY PLL */
|
|
reg_val = mctl_read_w(PLLGCR);
|
|
reg_val &= ~((0x1U<<29) | (0x1U<<31)); /* PLL enable */
|
|
mctl_write_w(reg_val, PLLGCR);
|
|
}
|
|
|
|
/* PIR trigger, PHY reset/DDL cal */
|
|
if (para->dram_clk <= 192) /* PLL disable */
|
|
reg_val = 0x40020061;
|
|
else
|
|
reg_val = 0x40000071; /* PLL enable */
|
|
mctl_write_w(reg_val, PIR);
|
|
while ((mctl_read_w(PGSR0) & 0x1) != 0x1)
|
|
; /* wait for DLL Lock */
|
|
|
|
/* pad release */
|
|
reg_val = mctl_read_w(VDD_SYS_POFF_GATING);
|
|
reg_val &= ~(0x3<<0);
|
|
mctl_write_w(reg_val, VDD_SYS_POFF_GATING);
|
|
mdfs_udelay(10);
|
|
|
|
/* self refresh exit */
|
|
mctl_write_w(0x0, PWRCTL);
|
|
/* confirm dram controller has enter self-refresh */
|
|
while ((mctl_read_w(STATR) & 0x7) != 0x1)
|
|
;
|
|
|
|
if (para->dram_clk <= 192) {
|
|
/* turn off DRAMC ODT */
|
|
reg_val = mctl_read_w(DXnGCR0(0));
|
|
reg_val &= ~(0x3 << 9);
|
|
mctl_write_w(reg_val, DXnGCR0(0));
|
|
|
|
reg_val = mctl_read_w(DXnGCR0(1));
|
|
reg_val &= ~(0x3 << 9);
|
|
mctl_write_w(reg_val, DXnGCR0(1));
|
|
|
|
/* turn off DRAM ODT */
|
|
mctl_write_w(0x0, ODTMAP);
|
|
|
|
if (((para->dram_tpr13 >> 12) & 0x1) == 0) {
|
|
/* enable auto enter self-refresh */
|
|
mctl_write_w(0x9, PWRCTL);
|
|
}
|
|
} else {
|
|
/* turn on DRAMC odt */
|
|
reg_val = mctl_read_w(DXnGCR0(0));
|
|
reg_val |= (0x3 << 9);
|
|
mctl_write_w(reg_val, DXnGCR0(0));
|
|
reg_val = mctl_read_w(DXnGCR0(1));
|
|
reg_val |= (0x3 << 9);
|
|
mctl_write_w(reg_val, DXnGCR0(1));
|
|
|
|
/* turn on DRAM ODT */
|
|
rank_num = mctl_read_w(MC_WORK_MODE) & 0x1;
|
|
if (rank_num)
|
|
mctl_write_w(0x00000303, ODTMAP);
|
|
else
|
|
mctl_write_w(0x00000201, ODTMAP);
|
|
}
|
|
|
|
/* enable master access */
|
|
mctl_write_w(0xffff, MC_MAER);
|
|
}
|
|
|
|
#elif defined(CONFIG_ARCH_SUN8IW6P1)
|
|
static void __sram mdfs_start(int type, int freq_jump, __dram_para_t *para,
|
|
unsigned int pllx_para, unsigned int div)
|
|
{
|
|
unsigned int reg_val = 0;
|
|
unsigned int i = 0;
|
|
unsigned int odt_type = 0;
|
|
|
|
/* bit0 must be 0 for new MDFS process */
|
|
reg_val = (mctl_read_w(MC_MDFSCR) & 0x1);
|
|
if (!reg_val) {
|
|
/* disbale all master access */
|
|
reg_val = mctl_read_w(PWRCTL);
|
|
reg_val |= (0x1<<8);
|
|
mctl_write_w(reg_val, PWRCTL);
|
|
|
|
/* add dqs delay when frequency down */
|
|
if (para->dram_clk < 200) {
|
|
for (i = 0; i < 8; i++) {
|
|
/* byte 0 */
|
|
reg_val = mctl_read_w(DATX0IOCR(i));
|
|
reg_val &= ~(0x3U<<24);
|
|
reg_val |= (0x2<<24);
|
|
reg_val &= ~(0x1f<<0);
|
|
mctl_write_w(reg_val, DATX0IOCR(i));
|
|
/* byte 1 */
|
|
mctl_write_w(reg_val, DATX1IOCR(i));
|
|
/* byte 2 */
|
|
mctl_write_w(reg_val, DATX2IOCR(i));
|
|
/* byte 3 */
|
|
mctl_write_w(reg_val, DATX3IOCR(i));
|
|
}
|
|
} else {
|
|
odt_type = (para->dram_odt_en & 0x2) >> 1;
|
|
for (i = 0; i < 8; i++) {
|
|
/* byte 0 */
|
|
reg_val = mctl_read_w(DATX0IOCR(i));
|
|
reg_val &= ~(0x3U<<24);
|
|
reg_val |= (odt_type<<24);
|
|
reg_val |= (0x8<<0);
|
|
mctl_write_w(reg_val, DATX0IOCR(i));
|
|
/* byte 1 */
|
|
mctl_write_w(reg_val, DATX1IOCR(i));
|
|
/* byte 2 */
|
|
mctl_write_w(reg_val, DATX2IOCR(i));
|
|
/* byte 3 */
|
|
mctl_write_w(reg_val, DATX3IOCR(i));
|
|
}
|
|
}
|
|
|
|
/* enable master access */
|
|
reg_val = mctl_read_w(PWRCTL);
|
|
reg_val &= ~(0x1<<8);
|
|
mctl_write_w(reg_val, PWRCTL);
|
|
}
|
|
}
|
|
|
|
#elif defined(CONFIG_ARCH_SUN8IW7P1)
|
|
static void __sram mdfs_start(int type, int freq_jump, __dram_para_t *para,
|
|
unsigned int pll_ddr_para, unsigned int div)
|
|
{
|
|
unsigned int reg_val = 0;
|
|
unsigned int pll_ddr = pll_ddr_para;
|
|
unsigned int rank_num = 0;
|
|
unsigned int i = 0;
|
|
|
|
rank_num = mctl_read_w(MC_WORK_MODE) & 0x1;
|
|
|
|
/* 1. enter self-refresh and disable all master access */
|
|
reg_val = mctl_read_w(PWRCTL);
|
|
reg_val |= (0x1<<0);
|
|
reg_val |= (0x1<<8);
|
|
mctl_write_w(reg_val, PWRCTL);
|
|
mdfs_udelay(1);
|
|
|
|
/* make sure enter self-refresh */
|
|
while ((mctl_read_w(STATR) & 0x7) != 0x3)
|
|
;
|
|
|
|
/* 2.Update PLL setting and wait 1ms */
|
|
mctl_write_w(pll_ddr, _CCM_PLL_DDR_REG);
|
|
pll_ddr |= (1U << 20);
|
|
mctl_write_w(pll_ddr, _CCM_PLL_DDR_REG);
|
|
mdfs_udelay(1000);
|
|
|
|
/* 3.set PIR register issue phy reset and DDL calibration */
|
|
if (rank_num) {
|
|
reg_val = mctl_read_w(DTCR);
|
|
reg_val &= ~(0x3<<24);
|
|
reg_val |= (0x3<<24);
|
|
mctl_write_w(reg_val, DTCR);
|
|
} else {
|
|
reg_val = mctl_read_w(DTCR);
|
|
reg_val &= ~(0x3<<24);
|
|
reg_val |= (0x1<<24);
|
|
mctl_write_w(reg_val, DTCR);
|
|
}
|
|
|
|
/* trigger phy reset and DDL calibration */
|
|
mctl_write_w(0x40000061, PIR);
|
|
/* add 1us delay here */
|
|
mdfs_udelay(1);
|
|
|
|
/* wait for DLL Lock */
|
|
while ((mctl_read_w(PGSR0) & 0x1) != 0x1)
|
|
;
|
|
|
|
/*4.setting ODT configure */
|
|
if (para->dram_clk <= 408) {
|
|
/* turn off DRAMC ODT */
|
|
for (i = 0; i < 4; i++) {
|
|
reg_val = mctl_read_w(DXnGCR0(i));
|
|
reg_val &= ~(0x3U<<4);
|
|
reg_val |= (0x2<<4);
|
|
mctl_write_w(reg_val, DXnGCR0(i));
|
|
}
|
|
} else {
|
|
/* turn on DRAMC dynamic ODT */
|
|
for (i = 0; i < 4; i++) {
|
|
reg_val = mctl_read_w(DXnGCR0(i));
|
|
reg_val &= ~(0x3U<<4);
|
|
mctl_write_w(reg_val, DXnGCR0(i));
|
|
}
|
|
}
|
|
|
|
/* 5.exit self-refresh and enable all master access */
|
|
reg_val = mctl_read_w(PWRCTL);
|
|
reg_val &= ~(0x1<<0);
|
|
reg_val &= ~(0x1<<8);
|
|
mctl_write_w(reg_val, PWRCTL);
|
|
mdfs_udelay(1);
|
|
|
|
/* make sure exit self-refresh */
|
|
while ((mctl_read_w(STATR) & 0x7) != 0x1)
|
|
;
|
|
}
|
|
#endif
|
|
|
|
int __sram mdfs_main(unsigned int jump, __dram_para_t *dram_para,
|
|
unsigned int pll_para_from_dram, unsigned int div)
|
|
{
|
|
MDFS_DBG("enter\n");
|
|
#ifdef CONFIG_ARCH_SUN9IW1P1
|
|
prefetch_and_prediction_disable();
|
|
#endif
|
|
|
|
mdfs_init_tlb();
|
|
mdfs_init_para(jump, dram_para, pll_para_from_dram, div);
|
|
|
|
asm volatile ("mov %0, sp" : "=r" (sp_backup));
|
|
asm volatile ("mov sp, %0" : : "r" (SRAM_DDRFREQ_SP_ADDR));
|
|
|
|
asm("dsb");
|
|
asm("isb");
|
|
|
|
mdfs_start(0, __sram_jump, &__sram_dram_para, __sram_pllx_para, __sram_div);
|
|
|
|
#ifdef CONFIG_ARCH_SUN9IW1P1
|
|
prefetch_and_prediction_enable();
|
|
#endif
|
|
|
|
asm volatile ("mov sp, %0" : : "r" (sp_backup));
|
|
MDFS_DBG("done\n");
|
|
|
|
return 0;
|
|
}
|