diff options
Diffstat (limited to 'package/uboot-xburst/files/cpu/mips/jz4740.c')
-rw-r--r-- | package/uboot-xburst/files/cpu/mips/jz4740.c | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/package/uboot-xburst/files/cpu/mips/jz4740.c b/package/uboot-xburst/files/cpu/mips/jz4740.c new file mode 100644 index 0000000000..5ae57971d7 --- /dev/null +++ b/package/uboot-xburst/files/cpu/mips/jz4740.c @@ -0,0 +1,559 @@ +/* + * Jz4740 common routines + * + * Copyright (c) 2006 + * Ingenic Semiconductor, <jlwei@ingenic.cn> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> + +#ifdef CONFIG_JZ4740 +#include <common.h> +#include <command.h> +#include <asm/jz4740.h> + +extern void board_early_init(void); + +/* PLL output clock = EXTAL * NF / (NR * NO) + * + * NF = FD + 2, NR = RD + 2 + * NO = 1 (if OD = 0), NO = 2 (if OD = 1 or 2), NO = 4 (if OD = 3) + */ +void pll_init(void) +{ + register unsigned int cfcr, plcr1; + int n2FR[33] = { + 0, 0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0, + 7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, + 9 + }; + int div[5] = {1, 3, 3, 3, 3}; /* divisors of I:S:P:L:M */ + int nf, pllout2; + + cfcr = CPM_CPCCR_CLKOEN | + CPM_CPCCR_PCS | + (n2FR[div[0]] << CPM_CPCCR_CDIV_BIT) | + (n2FR[div[1]] << CPM_CPCCR_HDIV_BIT) | + (n2FR[div[2]] << CPM_CPCCR_PDIV_BIT) | + (n2FR[div[3]] << CPM_CPCCR_MDIV_BIT) | + (n2FR[div[4]] << CPM_CPCCR_LDIV_BIT); + + pllout2 = (cfcr & CPM_CPCCR_PCS) ? CONFIG_SYS_CPU_SPEED : (CONFIG_SYS_CPU_SPEED / 2); + + /* Init USB Host clock, pllout2 must be n*48MHz */ + REG_CPM_UHCCDR = pllout2 / 48000000 - 1; + + nf = CONFIG_SYS_CPU_SPEED * 2 / CONFIG_SYS_EXTAL; + plcr1 = ((nf - 2) << CPM_CPPCR_PLLM_BIT) | /* FD */ + (0 << CPM_CPPCR_PLLN_BIT) | /* RD=0, NR=2 */ + (0 << CPM_CPPCR_PLLOD_BIT) | /* OD=0, NO=1 */ + (0x20 << CPM_CPPCR_PLLST_BIT) | /* PLL stable time */ + CPM_CPPCR_PLLEN; /* enable PLL */ + + /* init PLL */ + REG_CPM_CPCCR = cfcr; + REG_CPM_CPPCR = plcr1; +} + +void pll_add_test(int new_freq) +{ + register unsigned int cfcr, plcr1; + int n2FR[33] = { + 0, 0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0, + 7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, + 9 + }; + int div[5] = {1, 4, 4, 4, 4}; /* divisors of I:S:P:M:L */ + int nf, pllout2; + + cfcr = CPM_CPCCR_CLKOEN | + (n2FR[div[0]] << CPM_CPCCR_CDIV_BIT) | + (n2FR[div[1]] << CPM_CPCCR_HDIV_BIT) | + (n2FR[div[2]] << CPM_CPCCR_PDIV_BIT) | + (n2FR[div[3]] << CPM_CPCCR_MDIV_BIT) | + (n2FR[div[4]] << CPM_CPCCR_LDIV_BIT); + + pllout2 = (cfcr & CPM_CPCCR_PCS) ? new_freq : (new_freq / 2); + + /* Init UHC clock */ + REG_CPM_UHCCDR = pllout2 / 48000000 - 1; + + /* nf = new_freq * 2 / CONFIG_SYS_EXTAL; */ + nf = new_freq / 1000000; /* step length is 1M */ + plcr1 = ((nf - 2) << CPM_CPPCR_PLLM_BIT) | /* FD */ + (10 << CPM_CPPCR_PLLN_BIT) | /* RD=0, NR=2 */ + (0 << CPM_CPPCR_PLLOD_BIT) | /* OD=0, NO=1 */ + (0x20 << CPM_CPPCR_PLLST_BIT) | /* PLL stable time */ + CPM_CPPCR_PLLEN; /* enable PLL */ + + /* init PLL */ + REG_CPM_CPCCR = cfcr; + REG_CPM_CPPCR = plcr1; +} + +void calc_clocks_add_test(void) +{ + DECLARE_GLOBAL_DATA_PTR; + + unsigned int pllout; + unsigned int div[10] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; + + pllout = __cpm_get_pllout(); + + gd->cpu_clk = pllout / div[__cpm_get_cdiv()]; + gd->sys_clk = pllout / div[__cpm_get_hdiv()]; + gd->per_clk = pllout / div[__cpm_get_pdiv()]; + gd->mem_clk = pllout / div[__cpm_get_mdiv()]; + gd->dev_clk = CONFIG_SYS_EXTAL; +} + +void sdram_add_test(int new_freq) +{ + register unsigned int dmcr, sdmode, tmp, cpu_clk, mem_clk, ns; + + unsigned int cas_latency_sdmr[2] = { + EMC_SDMR_CAS_2, + EMC_SDMR_CAS_3, + }; + + unsigned int cas_latency_dmcr[2] = { + 1 << EMC_DMCR_TCL_BIT, /* CAS latency is 2 */ + 2 << EMC_DMCR_TCL_BIT /* CAS latency is 3 */ + }; + + int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; + + cpu_clk = new_freq; + mem_clk = cpu_clk * div[__cpm_get_cdiv()] / div[__cpm_get_mdiv()]; + + REG_EMC_RTCSR = EMC_RTCSR_CKS_DISABLE; + REG_EMC_RTCOR = 0; + REG_EMC_RTCNT = 0; + + /* Basic DMCR register value. */ + dmcr = ((SDRAM_ROW-11)<<EMC_DMCR_RA_BIT) | + ((SDRAM_COL-8)<<EMC_DMCR_CA_BIT) | + (SDRAM_BANK4<<EMC_DMCR_BA_BIT) | + (SDRAM_BW16<<EMC_DMCR_BW_BIT) | + EMC_DMCR_EPIN | + cas_latency_dmcr[((SDRAM_CASL == 3) ? 1 : 0)]; + + /* SDRAM timimg parameters */ + ns = 1000000000 / mem_clk; + +#if 0 + tmp = SDRAM_TRAS/ns; + if (tmp < 4) tmp = 4; + if (tmp > 11) tmp = 11; + dmcr |= ((tmp-4) << EMC_DMCR_TRAS_BIT); + + tmp = SDRAM_RCD/ns; + if (tmp > 3) tmp = 3; + dmcr |= (tmp << EMC_DMCR_RCD_BIT); + + tmp = SDRAM_TPC/ns; + if (tmp > 7) tmp = 7; + dmcr |= (tmp << EMC_DMCR_TPC_BIT); + + tmp = SDRAM_TRWL/ns; + if (tmp > 3) tmp = 3; + dmcr |= (tmp << EMC_DMCR_TRWL_BIT); + + tmp = (SDRAM_TRAS + SDRAM_TPC)/ns; + if (tmp > 14) tmp = 14; + dmcr |= (((tmp + 1) >> 1) << EMC_DMCR_TRC_BIT); +#else + dmcr |= 0xfffc; +#endif + + /* First, precharge phase */ + REG_EMC_DMCR = dmcr; + + /* Set refresh registers */ + tmp = SDRAM_TREF/ns; + tmp = tmp/64 + 1; + if (tmp > 0xff) tmp = 0xff; + + REG_EMC_RTCOR = tmp; + REG_EMC_RTCSR = EMC_RTCSR_CKS_64; /* Divisor is 64, CKO/64 */ + + /* SDRAM mode values */ + sdmode = EMC_SDMR_BT_SEQ | + EMC_SDMR_OM_NORMAL | + EMC_SDMR_BL_4 | + cas_latency_sdmr[((SDRAM_CASL == 3) ? 1 : 0)]; + + /* precharge all chip-selects */ + REG8(EMC_SDMR0|sdmode) = 0; + + /* wait for precharge, > 200us */ + tmp = (cpu_clk / 1000000) * 200; + while (tmp--); + + /* enable refresh and set SDRAM mode */ + REG_EMC_DMCR = dmcr | EMC_DMCR_RFSH | EMC_DMCR_MRSET; + + /* write sdram mode register for each chip-select */ + REG8(EMC_SDMR0|sdmode) = 0; + + /* everything is ok now */ +} + +void sdram_init(void) +{ + register unsigned int dmcr0, dmcr, sdmode, tmp, cpu_clk, mem_clk, ns; + + unsigned int cas_latency_sdmr[2] = { + EMC_SDMR_CAS_2, + EMC_SDMR_CAS_3, + }; + + unsigned int cas_latency_dmcr[2] = { + 1 << EMC_DMCR_TCL_BIT, /* CAS latency is 2 */ + 2 << EMC_DMCR_TCL_BIT /* CAS latency is 3 */ + }; + + int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; + + cpu_clk = CONFIG_SYS_CPU_SPEED; + mem_clk = cpu_clk * div[__cpm_get_cdiv()] / div[__cpm_get_mdiv()]; + + REG_EMC_BCR = 0; /* Disable bus release */ + REG_EMC_RTCSR = 0; /* Disable clock for counting */ + + /* Fault DMCR value for mode register setting*/ +#define SDRAM_ROW0 11 +#define SDRAM_COL0 8 +#define SDRAM_BANK40 0 + + dmcr0 = ((SDRAM_ROW0-11)<<EMC_DMCR_RA_BIT) | + ((SDRAM_COL0-8)<<EMC_DMCR_CA_BIT) | + (SDRAM_BANK40<<EMC_DMCR_BA_BIT) | + (SDRAM_BW16<<EMC_DMCR_BW_BIT) | + EMC_DMCR_EPIN | + cas_latency_dmcr[((SDRAM_CASL == 3) ? 1 : 0)]; + + /* Basic DMCR value */ + dmcr = ((SDRAM_ROW-11)<<EMC_DMCR_RA_BIT) | + ((SDRAM_COL-8)<<EMC_DMCR_CA_BIT) | + (SDRAM_BANK4<<EMC_DMCR_BA_BIT) | + (SDRAM_BW16<<EMC_DMCR_BW_BIT) | + EMC_DMCR_EPIN | + cas_latency_dmcr[((SDRAM_CASL == 3) ? 1 : 0)]; + + /* SDRAM timimg */ + ns = 1000000000 / mem_clk; + tmp = SDRAM_TRAS/ns; + if (tmp < 4) tmp = 4; + if (tmp > 11) tmp = 11; + dmcr |= ((tmp-4) << EMC_DMCR_TRAS_BIT); + tmp = SDRAM_RCD/ns; + if (tmp > 3) tmp = 3; + dmcr |= (tmp << EMC_DMCR_RCD_BIT); + tmp = SDRAM_TPC/ns; + if (tmp > 7) tmp = 7; + dmcr |= (tmp << EMC_DMCR_TPC_BIT); + tmp = SDRAM_TRWL/ns; + if (tmp > 3) tmp = 3; + dmcr |= (tmp << EMC_DMCR_TRWL_BIT); + tmp = (SDRAM_TRAS + SDRAM_TPC)/ns; + if (tmp > 14) tmp = 14; + dmcr |= (((tmp + 1) >> 1) << EMC_DMCR_TRC_BIT); + + /* SDRAM mode value */ + sdmode = EMC_SDMR_BT_SEQ | + EMC_SDMR_OM_NORMAL | + EMC_SDMR_BL_4 | + cas_latency_sdmr[((SDRAM_CASL == 3) ? 1 : 0)]; + + /* Stage 1. Precharge all banks by writing SDMR with DMCR.MRSET=0 */ + REG_EMC_DMCR = dmcr; + REG8(EMC_SDMR0|sdmode) = 0; + + /* Wait for precharge, > 200us */ + tmp = (cpu_clk / 1000000) * 1000; + while (tmp--); + + /* Stage 2. Enable auto-refresh */ + REG_EMC_DMCR = dmcr | EMC_DMCR_RFSH; + + tmp = SDRAM_TREF/ns; + tmp = tmp/64 + 1; + if (tmp > 0xff) tmp = 0xff; + REG_EMC_RTCOR = tmp; + REG_EMC_RTCNT = 0; + REG_EMC_RTCSR = EMC_RTCSR_CKS_64; /* Divisor is 64, CKO/64 */ + + /* Wait for number of auto-refresh cycles */ + tmp = (cpu_clk / 1000000) * 1000; + while (tmp--); + + /* Stage 3. Mode Register Set */ + REG_EMC_DMCR = dmcr0 | EMC_DMCR_RFSH | EMC_DMCR_MRSET; + REG8(EMC_SDMR0|sdmode) = 0; + + /* Set back to basic DMCR value */ + REG_EMC_DMCR = dmcr | EMC_DMCR_RFSH | EMC_DMCR_MRSET; + + /* everything is ok now */ +} + +#ifndef CONFIG_NAND_SPL + +static void calc_clocks(void) +{ + DECLARE_GLOBAL_DATA_PTR; + + unsigned int pllout; + unsigned int div[10] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; + + pllout = __cpm_get_pllout(); + + gd->cpu_clk = pllout / div[__cpm_get_cdiv()]; + gd->sys_clk = pllout / div[__cpm_get_hdiv()]; + gd->per_clk = pllout / div[__cpm_get_pdiv()]; + gd->mem_clk = pllout / div[__cpm_get_mdiv()]; + gd->dev_clk = CONFIG_SYS_EXTAL; +} + +static void rtc_init(void) +{ + unsigned long rtcsta; + + while ( !__rtc_write_ready()) ; + __rtc_enable_alarm(); /* enable alarm */ + + while ( !__rtc_write_ready()) + ; + REG_RTC_RGR = 0x00007fff; /* type value */ + + while ( !__rtc_write_ready()) + ; + REG_RTC_HWFCR = 0x0000ffe0; /* Power on delay 2s */ + + while ( !__rtc_write_ready()) + ; + REG_RTC_HRCR = 0x00000fe0; /* reset delay 125ms */ +#if 0 + while ( !__rtc_write_ready()) + ; + rtcsta = REG_RTC_HWRSR; + while ( !__rtc_write_ready()) + ; + if (rtcsta & 0x33) { + if (rtcsta & 0x10) { + while ( !__rtc_write_ready()) + ; + REG_RTC_RSR = 0x0; + } + while ( !__rtc_write_ready()) + ; + REG_RTC_HWRSR = 0x0; + } +#endif +} + +/* + * jz4740 board init routine + */ +int jz_board_init(void) +{ + board_early_init(); /* init gpio, pll etc. */ +#ifndef CONFIG_NAND_U_BOOT + pll_init(); /* init PLL */ + sdram_init(); /* init sdram memory */ +#endif + calc_clocks(); /* calc the clocks */ + rtc_init(); /* init rtc on any reset: */ + return 0; +} + +/* U-Boot common routines */ +phys_size_t initdram(int board_type) +{ + u32 dmcr; + u32 rows, cols, dw, banks; + ulong size; + + dmcr = REG_EMC_DMCR; + rows = 11 + ((dmcr & EMC_DMCR_RA_MASK) >> EMC_DMCR_RA_BIT); + cols = 8 + ((dmcr & EMC_DMCR_CA_MASK) >> EMC_DMCR_CA_BIT); + dw = (dmcr & EMC_DMCR_BW) ? 2 : 4; + banks = (dmcr & EMC_DMCR_BA) ? 4 : 2; + + size = (1 << (rows + cols)) * dw * banks; + + return size; +} + +/* + * Timer routines + */ + +#define TIMER_CHAN 0 +#define TIMER_FDATA 0xffff /* Timer full data value */ +#define TIMER_HZ CONFIG_SYS_HZ + +#define READ_TIMER REG_TCU_TCNT(TIMER_CHAN) /* macro to read the 16 bit timer */ + +static ulong timestamp; +static ulong lastdec; + +void reset_timer_masked (void); +ulong get_timer_masked (void); +void udelay_masked (unsigned long usec); + +/* + * timer without interrupts + */ + +int timer_init(void) +{ + REG_TCU_TCSR(TIMER_CHAN) = TCU_TCSR_PRESCALE256 | TCU_TCSR_EXT_EN; + REG_TCU_TCNT(TIMER_CHAN) = 0; + REG_TCU_TDHR(TIMER_CHAN) = 0; + REG_TCU_TDFR(TIMER_CHAN) = TIMER_FDATA; + + REG_TCU_TMSR = (1 << TIMER_CHAN) | (1 << (TIMER_CHAN + 16)); /* mask irqs */ + REG_TCU_TSCR = (1 << TIMER_CHAN); /* enable timer clock */ + REG_TCU_TESR = (1 << TIMER_CHAN); /* start counting up */ + + lastdec = 0; + timestamp = 0; + + return 0; +} + +void reset_timer(void) +{ + reset_timer_masked (); +} + +ulong get_timer(ulong base) +{ + return get_timer_masked () - base; +} + +void set_timer(ulong t) +{ + timestamp = t; +} + +void udelay (unsigned long usec) +{ + ulong tmo,tmp; + + /* normalize */ + if (usec >= 1000) { + tmo = usec / 1000; + tmo *= TIMER_HZ; + tmo /= 1000; + } + else { + if (usec >= 1) { + tmo = usec * TIMER_HZ; + tmo /= (1000*1000); + } + else + tmo = 1; + } + + /* check for rollover during this delay */ + tmp = get_timer (0); + if ((tmp + tmo) < tmp ) + reset_timer_masked(); /* timer would roll over */ + else + tmo += tmp; + + while (get_timer_masked () < tmo); +} + +void reset_timer_masked (void) +{ + /* reset time */ + lastdec = READ_TIMER; + timestamp = 0; +} + +ulong get_timer_masked (void) +{ + ulong now = READ_TIMER; + + if (lastdec <= now) { + /* normal mode */ + timestamp += (now - lastdec); + } else { + /* we have an overflow ... */ + timestamp += TIMER_FDATA + now - lastdec; + } + lastdec = now; + + return timestamp; +} + +void udelay_masked (unsigned long usec) +{ + ulong tmo; + ulong endtime; + signed long diff; + + /* normalize */ + if (usec >= 1000) { + tmo = usec / 1000; + tmo *= TIMER_HZ; + tmo /= 1000; + } else { + if (usec > 1) { + tmo = usec * TIMER_HZ; + tmo /= (1000*1000); + } else { + tmo = 1; + } + } + + endtime = get_timer_masked () + tmo; + + do { + ulong now = get_timer_masked (); + diff = endtime - now; + } while (diff >= 0); +} + +/* + * This function is derived from PowerPC code (read timebase as long long). + * On MIPS it just returns the timer value. + */ +unsigned long long get_ticks(void) +{ + return get_timer(0); +} + +/* + * This function is derived from PowerPC code (timebase clock frequency). + * On MIPS it returns the number of timer ticks per second. + */ +ulong get_tbclk (void) +{ + return TIMER_HZ; +} + +#endif /* CONFIG_NAND_SPL */ + +/* End of timer routine. */ + +#endif |