]>
Commit | Line | Data |
---|---|---|
0aee53ba CK |
1 | /* |
2 | * Memory setup for SMDK5250 board based on EXYNOS5 | |
3 | * | |
4 | * Copyright (C) 2012 Samsung Electronics | |
5 | * | |
6 | * See file CREDITS for list of people who contributed to this | |
7 | * project. | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License as | |
11 | * published by the Free Software Foundation; either version 2 of | |
12 | * the License, or (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
22 | * MA 02111-1307 USA | |
23 | */ | |
24 | ||
25 | #include <config.h> | |
26 | #include <asm/io.h> | |
27 | #include <asm/arch/dmc.h> | |
28 | #include <asm/arch/clock.h> | |
29 | #include <asm/arch/cpu.h> | |
30 | #include "setup.h" | |
31 | ||
32 | /* APLL : 1GHz */ | |
33 | /* MCLK_CDREX: MCLK_CDREX_533*/ | |
34 | /* LPDDR support: LPDDR2 */ | |
35 | static void reset_phy_ctrl(void); | |
36 | static void config_zq(struct exynos5_phy_control *, | |
37 | struct exynos5_phy_control *); | |
38 | static void update_reset_dll(struct exynos5_dmc *); | |
39 | static void config_cdrex(void); | |
40 | static void config_mrs(struct exynos5_dmc *); | |
41 | static void sec_sdram_phy_init(struct exynos5_dmc *); | |
42 | static void config_prech(struct exynos5_dmc *); | |
43 | static void config_rdlvl(struct exynos5_dmc *, | |
44 | struct exynos5_phy_control *, | |
45 | struct exynos5_phy_control *); | |
46 | static void config_memory(struct exynos5_dmc *); | |
47 | ||
48 | static void config_offsets(unsigned int, | |
49 | struct exynos5_phy_control *, | |
50 | struct exynos5_phy_control *); | |
51 | ||
52 | static void reset_phy_ctrl(void) | |
53 | { | |
54 | struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; | |
55 | ||
56 | writel(PHY_RESET_VAL, &clk->lpddr3phy_ctrl); | |
57 | sdelay(0x10000); | |
58 | } | |
59 | ||
60 | static void config_zq(struct exynos5_phy_control *phy0_ctrl, | |
61 | struct exynos5_phy_control *phy1_ctrl) | |
62 | { | |
63 | unsigned long val = 0; | |
64 | /* | |
65 | * ZQ Calibration: | |
66 | * Select Driver Strength, | |
67 | * long calibration for manual calibration | |
68 | */ | |
69 | val = PHY_CON16_RESET_VAL; | |
70 | SET_ZQ_MODE_DDS_VAL(val); | |
71 | SET_ZQ_MODE_TERM_VAL(val); | |
72 | val |= ZQ_CLK_DIV_EN; | |
73 | writel(val, &phy0_ctrl->phy_con16); | |
74 | writel(val, &phy1_ctrl->phy_con16); | |
75 | ||
76 | /* Disable termination */ | |
77 | val |= ZQ_MODE_NOTERM; | |
78 | writel(val, &phy0_ctrl->phy_con16); | |
79 | writel(val, &phy1_ctrl->phy_con16); | |
80 | ||
81 | /* ZQ_MANUAL_START: Enable */ | |
82 | val |= ZQ_MANUAL_STR; | |
83 | writel(val, &phy0_ctrl->phy_con16); | |
84 | writel(val, &phy1_ctrl->phy_con16); | |
85 | sdelay(0x10000); | |
86 | ||
87 | /* ZQ_MANUAL_START: Disable */ | |
88 | val &= ~ZQ_MANUAL_STR; | |
89 | writel(val, &phy0_ctrl->phy_con16); | |
90 | writel(val, &phy1_ctrl->phy_con16); | |
91 | } | |
92 | ||
93 | static void update_reset_dll(struct exynos5_dmc *dmc) | |
94 | { | |
95 | unsigned long val; | |
96 | /* | |
97 | * Update DLL Information: | |
98 | * Force DLL Resyncronization | |
99 | */ | |
100 | val = readl(&dmc->phycontrol0); | |
101 | val |= FP_RSYNC; | |
102 | writel(val, &dmc->phycontrol0); | |
103 | ||
104 | /* Reset Force DLL Resyncronization */ | |
105 | val = readl(&dmc->phycontrol0); | |
106 | val &= ~FP_RSYNC; | |
107 | writel(val, &dmc->phycontrol0); | |
108 | } | |
109 | ||
110 | static void config_mrs(struct exynos5_dmc *dmc) | |
111 | { | |
112 | unsigned long channel, chip, mask = 0, val; | |
113 | ||
114 | for (channel = 0; channel < CONFIG_DMC_CHANNELS; channel++) { | |
115 | SET_CMD_CHANNEL(mask, channel); | |
116 | for (chip = 0; chip < CONFIG_CHIPS_PER_CHANNEL; chip++) { | |
117 | /* | |
118 | * NOP CMD: | |
119 | * Assert and hold CKE to logic high level | |
120 | */ | |
121 | SET_CMD_CHIP(mask, chip); | |
122 | val = DIRECT_CMD_NOP | mask; | |
123 | writel(val, &dmc->directcmd); | |
124 | sdelay(0x10000); | |
125 | ||
126 | /* EMRS, MRS Cmds(Mode Reg Settings) Using Direct Cmd */ | |
127 | val = DIRECT_CMD_MRS1 | mask; | |
128 | writel(val, &dmc->directcmd); | |
129 | sdelay(0x10000); | |
130 | ||
131 | val = DIRECT_CMD_MRS2 | mask; | |
132 | writel(val, &dmc->directcmd); | |
133 | sdelay(0x10000); | |
134 | ||
135 | /* MCLK_CDREX_533 */ | |
136 | val = DIRECT_CMD_MRS3 | mask; | |
137 | writel(val, &dmc->directcmd); | |
138 | sdelay(0x10000); | |
139 | ||
140 | val = DIRECT_CMD_MRS4 | mask; | |
141 | writel(val, &dmc->directcmd); | |
142 | sdelay(0x10000); | |
143 | } | |
144 | } | |
145 | } | |
146 | ||
147 | static void config_prech(struct exynos5_dmc *dmc) | |
148 | { | |
149 | unsigned long channel, chip, mask = 0, val; | |
150 | ||
151 | for (channel = 0; channel < CONFIG_DMC_CHANNELS; channel++) { | |
152 | SET_CMD_CHANNEL(mask, channel); | |
153 | for (chip = 0; chip < CONFIG_CHIPS_PER_CHANNEL; chip++) { | |
154 | SET_CMD_CHIP(mask, chip); | |
155 | /* PALL (all banks precharge) CMD */ | |
156 | val = DIRECT_CMD_PALL | mask; | |
157 | writel(val, &dmc->directcmd); | |
158 | sdelay(0x10000); | |
159 | } | |
160 | } | |
161 | } | |
162 | ||
163 | static void sec_sdram_phy_init(struct exynos5_dmc *dmc) | |
164 | { | |
165 | unsigned long val; | |
166 | val = readl(&dmc->concontrol); | |
167 | val |= DFI_INIT_START; | |
168 | writel(val, &dmc->concontrol); | |
169 | sdelay(0x10000); | |
170 | ||
171 | val = readl(&dmc->concontrol); | |
172 | val &= ~DFI_INIT_START; | |
173 | writel(val, &dmc->concontrol); | |
174 | } | |
175 | ||
176 | static void config_offsets(unsigned int state, | |
177 | struct exynos5_phy_control *phy0_ctrl, | |
178 | struct exynos5_phy_control *phy1_ctrl) | |
179 | { | |
180 | unsigned long val; | |
181 | /* Set Offsets to read DQS */ | |
182 | val = (state == SET) ? SET_DQS_OFFSET_VAL : RESET_DQS_OFFSET_VAL; | |
183 | writel(val, &phy0_ctrl->phy_con4); | |
184 | writel(val, &phy1_ctrl->phy_con4); | |
185 | ||
186 | /* Set Offsets to read DQ */ | |
187 | val = (state == SET) ? SET_DQ_OFFSET_VAL : RESET_DQ_OFFSET_VAL; | |
188 | writel(val, &phy0_ctrl->phy_con6); | |
189 | writel(val, &phy1_ctrl->phy_con6); | |
190 | ||
191 | /* Debug Offset */ | |
192 | val = (state == SET) ? SET_DEBUG_OFFSET_VAL : RESET_DEBUG_OFFSET_VAL; | |
193 | writel(val, &phy0_ctrl->phy_con10); | |
194 | writel(val, &phy1_ctrl->phy_con10); | |
195 | } | |
196 | ||
197 | static void config_cdrex(void) | |
198 | { | |
199 | struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; | |
200 | writel(CLK_DIV_CDREX_VAL, &clk->div_cdrex); | |
201 | writel(CLK_SRC_CDREX_VAL, &clk->src_cdrex); | |
202 | sdelay(0x30000); | |
203 | } | |
204 | ||
205 | static void config_ctrl_dll_on(unsigned int state, | |
206 | unsigned int ctrl_force_val, | |
207 | struct exynos5_phy_control *phy0_ctrl, | |
208 | struct exynos5_phy_control *phy1_ctrl) | |
209 | { | |
210 | unsigned long val; | |
211 | val = readl(&phy0_ctrl->phy_con12); | |
212 | CONFIG_CTRL_DLL_ON(val, state); | |
213 | SET_CTRL_FORCE_VAL(val, ctrl_force_val); | |
214 | writel(val, &phy0_ctrl->phy_con12); | |
215 | ||
216 | val = readl(&phy1_ctrl->phy_con12); | |
217 | CONFIG_CTRL_DLL_ON(val, state); | |
218 | SET_CTRL_FORCE_VAL(val, ctrl_force_val); | |
219 | writel(val, &phy1_ctrl->phy_con12); | |
220 | } | |
221 | ||
222 | static void config_ctrl_start(unsigned int state, | |
223 | struct exynos5_phy_control *phy0_ctrl, | |
224 | struct exynos5_phy_control *phy1_ctrl) | |
225 | { | |
226 | unsigned long val; | |
227 | val = readl(&phy0_ctrl->phy_con12); | |
228 | CONFIG_CTRL_START(val, state); | |
229 | writel(val, &phy0_ctrl->phy_con12); | |
230 | ||
231 | val = readl(&phy1_ctrl->phy_con12); | |
232 | CONFIG_CTRL_START(val, state); | |
233 | writel(val, &phy1_ctrl->phy_con12); | |
234 | } | |
235 | ||
236 | #if defined(CONFIG_RD_LVL) | |
237 | static void config_rdlvl(struct exynos5_dmc *dmc, | |
238 | struct exynos5_phy_control *phy0_ctrl, | |
239 | struct exynos5_phy_control *phy1_ctrl) | |
240 | { | |
241 | unsigned long val; | |
242 | ||
243 | /* Disable CTRL_DLL_ON and set ctrl_force */ | |
244 | config_ctrl_dll_on(RESET, 0x2D, phy0_ctrl, phy1_ctrl); | |
245 | ||
246 | /* | |
247 | * Set ctrl_gateadj, ctrl_readadj | |
248 | * ctrl_gateduradj, rdlvl_pass_adj | |
249 | * rdlvl_rddataPadj | |
250 | */ | |
251 | val = SET_RDLVL_RDDATAPADJ; | |
252 | writel(val, &phy0_ctrl->phy_con1); | |
253 | writel(val, &phy1_ctrl->phy_con1); | |
254 | ||
255 | /* LPDDR2 Address */ | |
256 | writel(LPDDR2_ADDR, &phy0_ctrl->phy_con22); | |
257 | writel(LPDDR2_ADDR, &phy1_ctrl->phy_con22); | |
258 | ||
259 | /* Enable Byte Read Leveling set ctrl_ddr_mode */ | |
260 | val = readl(&phy0_ctrl->phy_con0); | |
261 | val |= BYTE_RDLVL_EN; | |
262 | writel(val, &phy0_ctrl->phy_con0); | |
263 | val = readl(&phy1_ctrl->phy_con0); | |
264 | val |= BYTE_RDLVL_EN; | |
265 | writel(val, &phy1_ctrl->phy_con0); | |
266 | ||
267 | /* rdlvl_en: Use levelling offset instead ctrl_shiftc */ | |
268 | val = PHY_CON2_RESET_VAL | RDLVL_EN; | |
269 | writel(val, &phy0_ctrl->phy_con2); | |
270 | writel(val, &phy1_ctrl->phy_con2); | |
271 | sdelay(0x10000); | |
272 | ||
273 | /* Enable Data Eye Training */ | |
274 | val = readl(&dmc->rdlvl_config); | |
275 | val |= CTRL_RDLVL_DATA_EN; | |
276 | writel(val, &dmc->rdlvl_config); | |
277 | sdelay(0x10000); | |
278 | ||
279 | /* Disable Data Eye Training */ | |
280 | val = readl(&dmc->rdlvl_config); | |
281 | val &= ~CTRL_RDLVL_DATA_EN; | |
282 | writel(val, &dmc->rdlvl_config); | |
283 | ||
284 | /* RdDeSkew_clear: Clear */ | |
285 | val = readl(&phy0_ctrl->phy_con2); | |
286 | val |= RDDSKEW_CLEAR; | |
287 | writel(val, &phy0_ctrl->phy_con2); | |
288 | val = readl(&phy1_ctrl->phy_con2); | |
289 | val |= RDDSKEW_CLEAR; | |
290 | writel(val, &phy1_ctrl->phy_con2); | |
291 | ||
292 | /* Enable CTRL_DLL_ON */ | |
293 | config_ctrl_dll_on(SET, 0x0, phy0_ctrl, phy1_ctrl); | |
294 | ||
295 | update_reset_dll(dmc); | |
296 | sdelay(0x10000); | |
297 | ||
298 | /* ctrl_atgte: ctrl_gate_p*, ctrl_read_p* generated by PHY */ | |
299 | val = readl(&phy0_ctrl->phy_con0); | |
300 | val &= ~CTRL_ATGATE; | |
301 | writel(val, &phy0_ctrl->phy_con0); | |
302 | val = readl(&phy1_ctrl->phy_con0); | |
303 | val &= ~CTRL_ATGATE; | |
304 | writel(val, &phy1_ctrl->phy_con0); | |
305 | } | |
306 | #endif | |
307 | ||
308 | static void config_memory(struct exynos5_dmc *dmc) | |
309 | { | |
310 | /* | |
311 | * Memory Configuration Chip 0 | |
312 | * Address Mapping: Interleaved | |
313 | * Number of Column address Bits: 10 bits | |
314 | * Number of Rows Address Bits: 14 | |
315 | * Number of Banks: 8 | |
316 | */ | |
317 | writel(DMC_MEMCONFIG0_VAL, &dmc->memconfig0); | |
318 | ||
319 | /* | |
320 | * Memory Configuration Chip 1 | |
321 | * Address Mapping: Interleaved | |
322 | * Number of Column address Bits: 10 bits | |
323 | * Number of Rows Address Bits: 14 | |
324 | * Number of Banks: 8 | |
325 | */ | |
326 | writel(DMC_MEMCONFIG1_VAL, &dmc->memconfig1); | |
327 | ||
328 | /* | |
329 | * Chip0: AXI | |
330 | * AXI Base Address: 0x40000000 | |
331 | * AXI Base Address Mask: 0x780 | |
332 | */ | |
333 | writel(DMC_MEMBASECONFIG0_VAL, &dmc->membaseconfig0); | |
334 | ||
335 | /* | |
336 | * Chip1: AXI | |
337 | * AXI Base Address: 0x80000000 | |
338 | * AXI Base Address Mask: 0x780 | |
339 | */ | |
340 | writel(DMC_MEMBASECONFIG1_VAL, &dmc->membaseconfig1); | |
341 | } | |
342 | ||
343 | void mem_ctrl_init() | |
344 | { | |
345 | struct exynos5_phy_control *phy0_ctrl, *phy1_ctrl; | |
346 | struct exynos5_dmc *dmc; | |
347 | unsigned long val; | |
348 | ||
349 | phy0_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY0_BASE; | |
350 | phy1_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY1_BASE; | |
351 | dmc = (struct exynos5_dmc *)EXYNOS5_DMC_CTRL_BASE; | |
352 | ||
353 | /* Reset PHY Controllor: PHY_RESET[0] */ | |
354 | reset_phy_ctrl(); | |
355 | ||
356 | /*set Read Latancy and Burst Length for PHY0 and PHY1 */ | |
357 | writel(PHY_CON42_VAL, &phy0_ctrl->phy_con42); | |
358 | writel(PHY_CON42_VAL, &phy1_ctrl->phy_con42); | |
359 | ||
360 | /* ZQ Cofiguration */ | |
361 | config_zq(phy0_ctrl, phy1_ctrl); | |
362 | ||
363 | /* Operation Mode : LPDDR2 */ | |
364 | val = PHY_CON0_RESET_VAL; | |
365 | SET_CTRL_DDR_MODE(val, DDR_MODE_LPDDR2); | |
366 | writel(val, &phy0_ctrl->phy_con0); | |
367 | writel(val, &phy1_ctrl->phy_con0); | |
368 | ||
369 | /* DQS, DQ: Signal, for LPDDR2: Always Set */ | |
370 | val = CTRL_PULLD_DQ | CTRL_PULLD_DQS; | |
371 | writel(val, &phy0_ctrl->phy_con14); | |
372 | writel(val, &phy1_ctrl->phy_con14); | |
373 | ||
374 | /* Init SEC SDRAM PHY */ | |
375 | sec_sdram_phy_init(dmc); | |
376 | sdelay(0x10000); | |
377 | ||
378 | update_reset_dll(dmc); | |
379 | ||
380 | /* | |
381 | * Dynamic Clock: Always Running | |
382 | * Memory Burst length: 4 | |
383 | * Number of chips: 2 | |
384 | * Memory Bus width: 32 bit | |
385 | * Memory Type: LPDDR2-S4 | |
386 | * Additional Latancy for PLL: 1 Cycle | |
387 | */ | |
388 | writel(DMC_MEMCONTROL_VAL, &dmc->memcontrol); | |
389 | ||
390 | config_memory(dmc); | |
391 | ||
392 | /* Precharge Configuration */ | |
393 | writel(DMC_PRECHCONFIG_VAL, &dmc->prechconfig); | |
394 | ||
395 | /* Power Down mode Configuration */ | |
396 | writel(DMC_PWRDNCONFIG_VAL, &dmc->pwrdnconfig); | |
397 | ||
398 | /* Periodic Refrese Interval */ | |
399 | writel(DMC_TIMINGREF_VAL, &dmc->timingref); | |
400 | ||
401 | /* | |
402 | * TimingRow, TimingData, TimingPower Setting: | |
403 | * Values as per Memory AC Parameters | |
404 | */ | |
405 | writel(DMC_TIMINGROW_VAL, &dmc->timingrow); | |
406 | ||
407 | writel(DMC_TIMINGDATA_VAL, &dmc->timingdata); | |
408 | ||
409 | writel(DMC_TIMINGPOWER_VAL, &dmc->timingpower); | |
410 | ||
411 | /* Memory Channel Inteleaving Size: 128 Bytes */ | |
412 | writel(CONFIG_IV_SIZE, &dmc->ivcontrol); | |
413 | ||
414 | /* Set DQS, DQ and DEBUG offsets */ | |
415 | config_offsets(SET, phy0_ctrl, phy1_ctrl); | |
416 | ||
417 | /* Disable CTRL_DLL_ON and set ctrl_force */ | |
418 | config_ctrl_dll_on(RESET, 0x7F, phy0_ctrl, phy1_ctrl); | |
419 | sdelay(0x10000); | |
420 | ||
421 | update_reset_dll(dmc); | |
422 | ||
423 | /* Config MRS(Mode Register Settingg) */ | |
424 | config_mrs(dmc); | |
425 | ||
426 | config_cdrex(); | |
427 | ||
428 | /* Reset DQS DQ and DEBUG offsets */ | |
429 | config_offsets(RESET, phy0_ctrl, phy1_ctrl); | |
430 | ||
431 | /* Enable CTRL_DLL_ON */ | |
432 | config_ctrl_dll_on(SET, 0x0, phy0_ctrl, phy1_ctrl); | |
433 | ||
434 | /* Stop DLL Locking */ | |
435 | config_ctrl_start(RESET, phy0_ctrl, phy1_ctrl); | |
436 | sdelay(0x10000); | |
437 | ||
438 | /* Start DLL Locking */ | |
439 | config_ctrl_start(SET, phy0_ctrl, phy1_ctrl); | |
440 | sdelay(0x10000); | |
441 | ||
442 | update_reset_dll(dmc); | |
443 | ||
444 | #if defined(CONFIG_RD_LVL) | |
445 | config_rdlvl(dmc, phy0_ctrl, phy1_ctrl); | |
446 | #endif | |
447 | config_prech(dmc); | |
448 | ||
449 | /* | |
450 | * Dynamic Clock: Stops During Idle Period | |
451 | * Dynamic Power Down: Enable | |
452 | * Dynamic Self refresh: Enable | |
453 | */ | |
454 | val = readl(&dmc->memcontrol); | |
455 | val |= CLK_STOP_EN | DPWRDN_EN | DSREF_EN; | |
456 | writel(val, &dmc->memcontrol); | |
457 | ||
458 | /* Start Auto refresh */ | |
459 | val = readl(&dmc->concontrol); | |
460 | val |= AREF_EN; | |
461 | writel(val, &dmc->concontrol); | |
462 | } |