/* * Jz4740 common routines * * Copyright (c) 2006 * Ingenic Semiconductor, * * 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 #ifdef CONFIG_JZ4740 #include #include #include 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)< 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)< 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