summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx233/emi-imx233.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/imx233/emi-imx233.c')
-rw-r--r--firmware/target/arm/imx233/emi-imx233.c186
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
24struct 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 */
38static 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
46static 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
54static 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
62static struct emi_reg_t settings_80M[15] ICONST_ATTR __attribute__((alias("settings_60M")));
63
64static 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
72static 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
80static 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
88static struct emi_reg_t settings_155M[15] ICONST_ATTR __attribute__((alias("settings_133M")));
89
90static void set_frequency(unsigned long freq) ICODE_ATTR;
91
92static 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
123void imx233_emi_set_frequency(unsigned long freq) ICODE_ATTR;
124
125void 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}