SmartAudio/lichee/linux-4.9/drivers/devfreq/dramfreq/sunxi-mdfs.c

1225 lines
33 KiB
C
Raw Normal View History

2018-07-13 01:31:50 +00:00
/*
* 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;
}