diff -drupN a/drivers/devfreq/dramfreq/sunxi-mdfs.c b/drivers/devfreq/dramfreq/sunxi-mdfs.c --- a/drivers/devfreq/dramfreq/sunxi-mdfs.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/devfreq/dramfreq/sunxi-mdfs.c 2022-06-12 05:28:14.000000000 +0300 @@ -0,0 +1,1224 @@ +/* + * drivers/devfreq/dramfreq/sunxi-mdfs.c + * + * Copyright(c) 2013-2015 Allwinnertech Co., Ltd. + * + * Author: Pan Nan + * + * 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 +#include +#include +#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; +}