/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2013 by Amaury Pouly * * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "emi-imx233.h" #include "clkctrl-imx233.h" #include "string.h" #include "regs/clkctrl.h" #include "regs/emi.h" #include "regs/dram.h" #define HW_DRAM_CTLxx(xx) (*(&HW_DRAM_CTL00 + (xx))) struct emi_reg_t { int index; uint32_t value; }; /* hardcode all the register values for the different settings. This avoid * computing the register values at runtime since they never change and also * avoid wasting some space in iram. * Values from IMX233 manual, for Mobile DDR 7.5ns (133 MHz and 64MHz) * Make sure the last value is written to register 40. */ static struct emi_reg_t settings_60M[15] ICONST_ATTR = { {4, 0x01000101}, /* DLL bypass mode, concurrent auto-precharge and bank split */ {7, 0x01000101}, /* Read/write grouping, extra clock for back to back, priority placement */ {12, 0x02020002}, /* tWR = 2 cycles, tRRD = 1 cycles, tCKE = 2 cycles */ {13, 0x06060a02}, /* CAS lat gate = 3.0 cycles, CAS lat = 3.0 cycles, tWTR = 2 */ {15, 0x02040000}, /* tRP = 2 cycles, tDAL = 4 cycles */ {17, 0x2d000005}, /* DDL: start point = 45, lock = 0, increment = 0, tRC = 5 cycles */ {18, 0x00000000}, /* */ {19, 0x01000b0b}, /* DLL: DQS out shift (bypass) = 1, DQS delay bypass (1/0) = 11 / 11 */ {20, 0x02030a00}, /* tRCD = 2 cycles, tRAS (min) = 3 cycles, DQS write shift (bypass) = 10 */ {21, 0x00000005}, /* tRFC = 5 cycles */ {26, 0x000001cc}, /* tREF = 460 cycles */ {32, 0x00081060}, /* tRAS (max) = 4192 cycles, tXSNR = 8 cycles */ {33, 0x00000008}, /* tXSR = 8 cycles */ {34, 0x00002ee5}, /* tINIT = 12005 cycles */ {40, 0x00020000} /* tPDEX = 2 */ }; static struct emi_reg_t settings_133M[15] ICONST_ATTR = { {4, 0x00000101}, /* concurrent auto-precharge and bank split */ {7, 0x01000001}, /* Read/write grouping, priority placement */ {12, 0x02020002}, /* tWR = 2 cycles, tRRD = 2 cycles, tCKE = 2 cycles */ {13, 0x06070a02}, /* CAS lat gate = 3.0 cycles, CAS lat = 3.5 cycles, tWTR = 2 */ {15, 0x03050000}, /* tRP = 3 cycles, tDAL = 5 cycles */ {17, 0x19000f0a}, /* DDL: start point = 25, lock = 0, increment = 15, tRC = 10 cycles */ {18, 0x1f1f0000}, /* DLL: DQS delay (1/0) = 31 / 31 */ {19, 0x000a0000}, /* DLL: DQS out shift = 10 */ {20, 0x03060023}, /* tRCD = 3 cycles, tRAS (min) = 6 cycles, DQS write shift = 35 */ {21, 0x0000000a}, /* tRFC = 10 cycles */ {26, 0x000003f7}, /* tREF = 1015 cycles */ {32, 0x001023cd}, /* tRAS (max) = 9165 cycles, tXSNR = 16 cycles */ {33, 0x00000010}, /* tXSR = 16 cycles */ {34, 0x00006665}, /* tINIT = 26213 cycles */ {40, 0x00040000} /* tPDEX = 4 */ }; static struct emi_reg_t settings_155M[15] ICONST_ATTR __attribute__((alias("settings_133M"))); static void set_frequency(unsigned long freq) ICODE_ATTR; #if IMX233_SUBTARGET >= 3700 static void set_frequency(unsigned long freq) { /** WARNING all restriction of imx233_emi_set_frequency apply here !! */ /* Set divider and clear clkgate. */ unsigned fracdiv; unsigned div; switch(freq) { case IMX233_EMIFREQ_151_MHz: /* clk_emi@ref_emi/3*18/19 */ fracdiv = 19; div = 3; /* ref_emi@480 MHz * clk_emi@151.58 MHz */ break; case IMX233_EMIFREQ_130_MHz: /* clk_emi@ref_emi/2*18/33 */ fracdiv = 33; div = 2; /* ref_emi@480 MHz * clk_emi@130.91 MHz */ break; case IMX233_EMIFREQ_64_MHz: default: /* clk_emi@ref_emi/5*18/27 */ fracdiv = 27; div = 5; /* ref_emi@480 MHz * clk_emi@64 MHz */ break; } BF_WR(CLKCTRL_FRAC, CLKGATEEMI(0)); BF_WR(CLKCTRL_FRAC, EMIFRAC(fracdiv)); BF_WR(CLKCTRL_EMI, CLKGATE(0)); BF_WR(CLKCTRL_EMI, DIV_EMI(div)); } void imx233_emi_set_frequency(unsigned long freq) ICODE_ATTR; void imx233_emi_set_frequency(unsigned long freq) { /** FIXME we rely on the compiler to NOT use the stack here because it's * not in iram ! If it's not smart enough, one can switch the switch to use * the irq stack since we are running interrupts disable here ! */ /** BUG for freq<=24 MHz we must keep bypass mode since we run on xtal * since this setting is unused by our code so ignore this bug for now */ /** WARNING DANGER * Changing the EMI frequency is complicated because it requires to * completely shutdown the external memory interface. We must make sure * that this code and all the data it uses in in iram and that no access to * the sdram will be made during the change. Care must be taken w.r.t to * the cache also. */ /** FIXME assume that auto-slow is disabled here since that could put some * clock below the minimum value and we want to spend as least time as * possible in this state anyway. * WARNING DANGER don't call any external function when sdram is disabled * otherwise you'll poke sdram and trigger a fatal data abort ! */ static unsigned long cur_freq = -1; /* avoid changes if unneeded */ if(cur_freq == freq) return; cur_freq = freq; /* first disable all interrupts */ int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); /* flush the cache */ commit_discard_idcache(); /* put DRAM into self-refresh mode */ HW_DRAM_CTL08 |= BM_DRAM_CTL08_SREFRESH; /* wait for DRAM to be halted */ while(!BF_RD(EMI_STAT, DRAM_HALTED)); /* load timings */ struct emi_reg_t *regs; switch(freq) { case IMX233_EMIFREQ_151_MHz: regs = settings_155M; break; case IMX233_EMIFREQ_130_MHz: regs = settings_133M; break; case IMX233_EMIFREQ_64_MHz: default: regs = settings_60M; break; } do HW_DRAM_CTLxx(regs->index) = regs->value; while((regs++)->index != 40); /* switch emi to xtal */ BF_SET(CLKCTRL_CLKSEQ, BYPASS_EMI); /* wait for transition */ while(BF_RD(CLKCTRL_EMI, BUSY_REF_XTAL)); /* put emi dll into reset mode */ // FIXME Unsure about what to do for stmp37xx #if IMX233_SUBTARGET >= 3780 HW_EMI_CTRL_SET = BM_EMI_CTRL_DLL_RESET | BM_EMI_CTRL_DLL_SHIFT_RESET; #endif /* load the new frequency dividers */ set_frequency(freq); /* switch emi back to pll */ BF_CLR(CLKCTRL_CLKSEQ, BYPASS_EMI); /* wait for transition */ while(BF_RD(CLKCTRL_EMI, BUSY_REF_EMI)); /* allow emi dll to lock again */ #if IMX233_SUBTARGET >= 3780 HW_EMI_CTRL_CLR = BM_EMI_CTRL_DLL_RESET | BM_EMI_CTRL_DLL_SHIFT_RESET; #endif /* wait for lock */ while(!BF_RD(DRAM_CTL04, DLLLOCKREG)); /* get DRAM out of self-refresh mode */ HW_DRAM_CTL08 &= ~BM_DRAM_CTL08_SREFRESH; /* wait for DRAM to be to run again */ while(HW_EMI_STAT & BM_EMI_STAT_DRAM_HALTED); restore_interrupt(oldstatus); } #endif struct imx233_emi_info_t imx233_emi_get_info(void) { struct imx233_emi_info_t info; memset(&info, 0, sizeof(info)); #if IMX233_SUBTARGET >= 3700 info.rows = 13 - BF_RD(DRAM_CTL10, ADDR_PINS); info.columns = 12 - BF_RD(DRAM_CTL11, COLUMN_SIZE); info.cas = BF_RD(DRAM_CTL13, CASLAT_LIN); info.banks = 4; info.chips = __builtin_popcount(BF_RD(DRAM_CTL14, CS_MAP)); info.size = 2 * (1 << (info.rows + info.columns)) * info.chips * info.banks; #endif return info; }