diff options
Diffstat (limited to 'firmware/target/arm/imx233/emi-imx233.c')
-rw-r--r-- | firmware/target/arm/imx233/emi-imx233.c | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/firmware/target/arm/imx233/emi-imx233.c b/firmware/target/arm/imx233/emi-imx233.c new file mode 100644 index 0000000000..b10d08134d --- /dev/null +++ b/firmware/target/arm/imx233/emi-imx233.c | |||
@@ -0,0 +1,186 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2013 by Amaury Pouly | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "emi-imx233.h" | ||
22 | #include "clkctrl-imx233.h" | ||
23 | |||
24 | struct emi_reg_t | ||
25 | { | ||
26 | int index; | ||
27 | uint32_t value; | ||
28 | }; | ||
29 | |||
30 | /* hardcode all the register values for the different settings. This is ugly | ||
31 | * but I don't understand what they mean and it's faster this way so... | ||
32 | * Recall that everything should be put in iram ! | ||
33 | * Make sure the last value is written to register 40. */ | ||
34 | |||
35 | /* Values extracted from Sigmatel linux port (GPL) */ | ||
36 | |||
37 | /** mDDR value */ | ||
38 | static struct emi_reg_t settings_24M[15] ICONST_ATTR = | ||
39 | { | ||
40 | {4, 0x01000101}, {7, 0x01000101}, {12, 0x02010002}, {13, 0x06060a02}, | ||
41 | {15, 0x01030000}, {17, 0x2d000102}, {18, 0x20200000}, {19, 0x027f1414}, | ||
42 | {20, 0x01021608}, {21, 0x00000002}, {26, 0x000000b3}, {32, 0x00030687}, | ||
43 | {33, 0x00000003}, {34, 0x000012c1}, {40, 0x00010000} | ||
44 | }; | ||
45 | |||
46 | static struct emi_reg_t settings_48M[15] ICONST_ATTR = | ||
47 | { | ||
48 | {4, 0x01000101}, {7, 0x01000101}, {13, 0x06060a02}, {12, 0x02010002}, | ||
49 | {15, 0x02040000}, {17, 0x2d000104}, {18, 0x1f1f0000}, {19, 0x027f0a0a}, | ||
50 | {20, 0x01021608}, {21, 0x00000004}, {26, 0x0000016f}, {32, 0x00060d17}, | ||
51 | {33, 0x00000006}, {34, 0x00002582}, {40, 0x00020000} | ||
52 | }; | ||
53 | |||
54 | static struct emi_reg_t settings_60M[15] ICONST_ATTR = | ||
55 | { | ||
56 | {4, 0x01000101}, {7, 0x01000101}, {12, 0x02020002}, {13, 0x06060a02}, | ||
57 | {15, 0x02040000}, {17, 0x2d000005}, {18, 0x1f1f0000}, {19, 0x027f0a0a}, | ||
58 | {20, 0x02040a10}, {21, 0x00000006}, {26, 0x000001cc}, {32, 0x00081060}, | ||
59 | {33, 0x00000008}, {34, 0x00002ee5}, {40, 0x00020000} | ||
60 | }; | ||
61 | |||
62 | static struct emi_reg_t settings_80M[15] ICONST_ATTR __attribute__((alias("settings_60M"))); | ||
63 | |||
64 | static struct emi_reg_t settings_96M[15] ICONST_ATTR = | ||
65 | { | ||
66 | {4, 0x00000101}, {7, 0x01000001}, {12, 0x02020002}, {13, 0x06070a02}, | ||
67 | {15, 0x03050000}, {17, 0x2d000808}, {18, 0x1f1f0000}, {19, 0x020c1010}, | ||
68 | {20, 0x0305101c}, {21, 0x00000007}, {26, 0x000002e6}, {32, 0x000c1a3b}, | ||
69 | {33, 0x0000000c}, {34, 0x00004b0d}, {40, 0x00030000} | ||
70 | }; | ||
71 | |||
72 | static struct emi_reg_t settings_120M[15] ICONST_ATTR = | ||
73 | { | ||
74 | {4, 0x00000101}, {7, 0x01000001}, {12, 0x02020002}, {13, 0x06070a02}, | ||
75 | {15, 0x03050000}, {17, 0x2300080a}, {18, 0x1f1f0000}, {19, 0x020c1010}, | ||
76 | {20, 0x0306101c}, {21, 0x00000009}, {26, 0x000003a1}, {32, 0x000f20ca}, | ||
77 | {33, 0x0000000f}, {34, 0x00005dca}, {40, 0x00040000} | ||
78 | }; | ||
79 | |||
80 | static struct emi_reg_t settings_133M[15] ICONST_ATTR = | ||
81 | { | ||
82 | {4, 0x00000101}, {7, 0x01000001}, {12, 0x02020002}, {13, 0x06070a02}, | ||
83 | {15, 0x03050000}, {17, 0x2000080a}, {18, 0x1f1f0000}, {19, 0x020c1010}, | ||
84 | {20, 0x0306101c}, {21, 0x0000000a}, {26, 0x00000408}, {32, 0x0010245f}, | ||
85 | {33, 0x00000010}, {34, 0x00006808}, {40, 0x00040000} | ||
86 | }; | ||
87 | |||
88 | static struct emi_reg_t settings_155M[15] ICONST_ATTR __attribute__((alias("settings_133M"))); | ||
89 | |||
90 | static void set_frequency(unsigned long freq) ICODE_ATTR; | ||
91 | |||
92 | static void set_frequency(unsigned long freq) | ||
93 | { | ||
94 | /* Set divider and clear clkgate. Do byte access to register to avoid bothering | ||
95 | * with other PFDs */ | ||
96 | switch(freq) | ||
97 | { | ||
98 | case IMX233_EMIFREQ_151_MHz: | ||
99 | /* clk_emi@ref_emi/3*18/19 */ | ||
100 | HW_CLKCTRL_FRAC_EMI = 19; | ||
101 | __FIELD_SET(HW_CLKCTRL_EMI, DIV_EMI, 3); | ||
102 | /* ref_emi@480 MHz | ||
103 | * clk_emi@151.58 MHz */ | ||
104 | break; | ||
105 | case IMX233_EMIFREQ_130_MHz: | ||
106 | /* clk_emi@ref_emi/2*18/33 */ | ||
107 | HW_CLKCTRL_FRAC_EMI = 33; | ||
108 | __FIELD_SET(HW_CLKCTRL_EMI, DIV_EMI, 2); | ||
109 | /* ref_emi@480 MHz | ||
110 | * clk_emi@130.91 MHz */ | ||
111 | break; | ||
112 | case IMX233_EMIFREQ_64_MHz: | ||
113 | default: | ||
114 | /* clk_emi@ref_emi/5*18/27 */ | ||
115 | HW_CLKCTRL_FRAC_EMI = 27; | ||
116 | __FIELD_SET(HW_CLKCTRL_EMI, DIV_EMI, 5); | ||
117 | /* ref_emi@480 MHz | ||
118 | * clk_emi@64 MHz */ | ||
119 | break; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | void imx233_emi_set_frequency(unsigned long freq) ICODE_ATTR; | ||
124 | |||
125 | void imx233_emi_set_frequency(unsigned long freq) | ||
126 | { | ||
127 | /** FIXME we rely on the compiler to NOT use the stack here because it's | ||
128 | * in iram ! If it's not smart enough, one can switch the switch to use | ||
129 | * the irq stack since we are running interrupts disable here ! */ | ||
130 | /** BUG for freq<=24 MHz we must keep bypass mode since we run on xtal | ||
131 | * we this setting is unused by our code so ignore this bug for now */ | ||
132 | /** WARNING DANGER | ||
133 | * Changing the EMI frequency is complicated because it requires to | ||
134 | * completely shutdown the external memory interface. We must make sure | ||
135 | * that this code and all the data it uses in in iram and that no access to | ||
136 | * the sdram will be made during the change. Care must be taken w.r.t to | ||
137 | * the cache also. */ | ||
138 | /** FIXME assume that auto-slow is disabled here since that could put some | ||
139 | * clock below the minimum value and we want to spend as less time as | ||
140 | * possible in this state anyway. */ | ||
141 | |||
142 | /* first disable all interrupts */ | ||
143 | int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); | ||
144 | /* flush the cache */ | ||
145 | commit_discard_idcache(); | ||
146 | /* put DRAM into self-refresh mode */ | ||
147 | HW_DRAM_CTL08 |= HW_DRAM_CTL08__SREFRESH; | ||
148 | /* wait for DRAM to be halted */ | ||
149 | while(!(HW_EMI_STAT & HW_EMI_STAT__DRAM_HALTED)); | ||
150 | /* load timings */ | ||
151 | struct emi_reg_t *regs; | ||
152 | if(freq <= 24000) regs = settings_24M; | ||
153 | else if(freq <= 48000) regs = settings_48M; | ||
154 | else if(freq <= 60000) regs = settings_60M; | ||
155 | else if(freq <= 80000) regs = settings_80M; | ||
156 | else if(freq <= 96000) regs = settings_96M; | ||
157 | else if(freq <= 120000) regs = settings_120M; | ||
158 | else if(freq <= 133000) regs = settings_133M; | ||
159 | else regs = settings_155M; | ||
160 | |||
161 | do | ||
162 | HW_DRAM_CTLxx(regs->index) = regs->value; | ||
163 | while((regs++)->index != 40); | ||
164 | /* switch emi to xtal */ | ||
165 | __REG_SET(HW_CLKCTRL_CLKSEQ) = HW_CLKCTRL_CLKSEQ__BYPASS_EMI; | ||
166 | /* wait for transition */ | ||
167 | while(HW_CLKCTRL_EMI & HW_CLKCTRL_EMI__BUSY_REF_XTAL); | ||
168 | /* put emi dll into reset mode */ | ||
169 | __REG_SET(HW_EMI_CTRL) = HW_EMI_CTRL__DLL_RESET | HW_EMI_CTRL__DLL_SHIFT_RESET; | ||
170 | /* load the new frequency dividers */ | ||
171 | set_frequency(freq); | ||
172 | /* switch emi back to pll */ | ||
173 | __REG_CLR(HW_CLKCTRL_CLKSEQ) = HW_CLKCTRL_CLKSEQ__BYPASS_EMI; | ||
174 | /* wait for transition */ | ||
175 | while(HW_CLKCTRL_EMI & HW_CLKCTRL_EMI__BUSY_REF_EMI); | ||
176 | /* allow emi dll to lock again */ | ||
177 | __REG_CLR(HW_EMI_CTRL) = HW_EMI_CTRL__DLL_RESET | HW_EMI_CTRL__DLL_SHIFT_RESET; | ||
178 | /* wait for lock */ | ||
179 | while(!(HW_DRAM_CTL04 & HW_DRAM_CTL04__DLLLOCKREG)); | ||
180 | /* get DRAM out of self-refresh mode */ | ||
181 | HW_DRAM_CTL08 &= ~HW_DRAM_CTL08__SREFRESH; | ||
182 | /* wait for DRAM to be to run again */ | ||
183 | while(HW_EMI_STAT & HW_EMI_STAT__DRAM_HALTED); | ||
184 | |||
185 | restore_interrupt(oldstatus); | ||
186 | } | ||