]>
Commit | Line | Data |
---|---|---|
6570438a YL |
1 | /* |
2 | * (C) Copyright 2010 - 2011 | |
3 | * NVIDIA Corporation <www.nvidia.com> | |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
6570438a YL |
6 | */ |
7 | ||
8 | #include <common.h> | |
9 | #include <asm/io.h> | |
10 | #include <asm/errno.h> | |
6570438a | 11 | #include <asm/arch/clock.h> |
6570438a YL |
12 | #include <asm/arch/emc.h> |
13 | #include <asm/arch/gp_padctrl.h> | |
150c2493 | 14 | #include <asm/arch/pinmux.h> |
6570438a | 15 | #include <asm/arch/sdram_param.h> |
150c2493 TW |
16 | #include <asm/arch/tegra.h> |
17 | #include <asm/arch-tegra/ap.h> | |
18 | #include <asm/arch-tegra/clk_rst.h> | |
19 | #include <asm/arch-tegra/pmc.h> | |
20 | #include <asm/arch-tegra/fuse.h> | |
21 | #include <asm/arch-tegra/warmboot.h> | |
6570438a YL |
22 | |
23 | DECLARE_GLOBAL_DATA_PTR; | |
24 | ||
25 | #ifndef CONFIG_TEGRA_CLOCK_SCALING | |
29f3e3f2 | 26 | #error "You must enable CONFIG_TEGRA_CLOCK_SCALING to use CONFIG_TEGRA_LP0" |
6570438a YL |
27 | #endif |
28 | ||
29 | /* | |
30 | * This is the place in SRAM where the SDRAM parameters are stored. There | |
31 | * are 4 blocks, one for each RAM code | |
32 | */ | |
b2871037 | 33 | #define SDRAM_PARAMS_BASE (NV_PA_BASE_SRAM + 0x188) |
6570438a YL |
34 | |
35 | /* TODO: If we later add support for the Misc GP controller, refactor this */ | |
36 | union xm2cfga_reg { | |
37 | struct { | |
38 | u32 reserved0:2; | |
39 | u32 hsm_en:1; | |
40 | u32 reserved1:2; | |
41 | u32 preemp_en:1; | |
42 | u32 vref_en:1; | |
43 | u32 reserved2:5; | |
44 | u32 cal_drvdn:5; | |
45 | u32 reserved3:3; | |
46 | u32 cal_drvup:5; | |
47 | u32 reserved4:3; | |
48 | u32 cal_drvdn_slwr:2; | |
49 | u32 cal_drvup_slwf:2; | |
50 | }; | |
51 | u32 word; | |
52 | }; | |
53 | ||
54 | union xm2cfgd_reg { | |
55 | struct { | |
56 | u32 reserved0:2; | |
57 | u32 hsm_en:1; | |
58 | u32 schmt_en:1; | |
59 | u32 lpmd:2; | |
60 | u32 vref_en:1; | |
61 | u32 reserved1:5; | |
62 | u32 cal_drvdn:5; | |
63 | u32 reserved2:3; | |
64 | u32 cal_drvup:5; | |
65 | u32 reserved3:3; | |
66 | u32 cal_drvdn_slwr:2; | |
67 | u32 cal_drvup_slwf:2; | |
68 | }; | |
69 | u32 word; | |
70 | }; | |
71 | ||
72 | /* | |
73 | * TODO: This register is not documented in the TRM yet. We could move this | |
74 | * into the EMC and give it a proper interface, but not while it is | |
75 | * undocumented. | |
76 | */ | |
77 | union fbio_spare_reg { | |
78 | struct { | |
79 | u32 reserved:24; | |
80 | u32 cfg_wb0:8; | |
81 | }; | |
82 | u32 word; | |
83 | }; | |
84 | ||
85 | /* We pack the resume information into these unions for later */ | |
86 | union scratch2_reg { | |
87 | struct { | |
88 | u32 pllm_base_divm:5; | |
89 | u32 pllm_base_divn:10; | |
90 | u32 pllm_base_divp:3; | |
91 | u32 pllm_misc_lfcon:4; | |
92 | u32 pllm_misc_cpcon:4; | |
93 | u32 gp_xm2cfga_padctrl_preemp:1; | |
94 | u32 gp_xm2cfgd_padctrl_schmt:1; | |
95 | u32 osc_ctrl_xobp:1; | |
96 | u32 memory_type:3; | |
97 | }; | |
98 | u32 word; | |
99 | }; | |
100 | ||
101 | union scratch4_reg { | |
102 | struct { | |
103 | u32 emc_clock_divider:8; | |
104 | u32 pllm_stable_time:8; | |
105 | u32 pllx_stable_time:8; | |
106 | u32 emc_fbio_spare_cfg_wb0:8; | |
107 | }; | |
108 | u32 word; | |
109 | }; | |
110 | ||
111 | union scratch24_reg { | |
112 | struct { | |
113 | u32 emc_auto_cal_wait:8; | |
114 | u32 emc_pin_program_wait:8; | |
115 | u32 warmboot_wait:8; | |
116 | u32 reserved:8; | |
117 | }; | |
118 | u32 word; | |
119 | }; | |
120 | ||
121 | int warmboot_save_sdram_params(void) | |
122 | { | |
123 | u32 ram_code; | |
124 | struct sdram_params sdram; | |
125 | struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; | |
29f3e3f2 | 126 | struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; |
6570438a | 127 | struct apb_misc_gp_ctlr *gp = |
29f3e3f2 | 128 | (struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE; |
6570438a YL |
129 | struct emc_ctlr *emc = emc_get_controller(gd->fdt_blob); |
130 | union scratch2_reg scratch2; | |
131 | union scratch4_reg scratch4; | |
132 | union scratch24_reg scratch24; | |
133 | union xm2cfga_reg xm2cfga; | |
134 | union xm2cfgd_reg xm2cfgd; | |
135 | union fbio_spare_reg fbio_spare; | |
136 | ||
137 | /* get ram code that is used as index to array sdram_params in BCT */ | |
138 | ram_code = (readl(&pmt->pmt_strap_opt_a) >> | |
139 | STRAP_OPT_A_RAM_CODE_SHIFT) & 3; | |
140 | memcpy(&sdram, | |
141 | (char *)((struct sdram_params *)SDRAM_PARAMS_BASE + ram_code), | |
142 | sizeof(sdram)); | |
143 | ||
144 | xm2cfga.word = readl(&gp->xm2cfga); | |
145 | xm2cfgd.word = readl(&gp->xm2cfgd); | |
146 | ||
147 | scratch2.word = 0; | |
148 | scratch2.osc_ctrl_xobp = clock_get_osc_bypass(); | |
149 | ||
150 | /* Get the memory PLL settings */ | |
151 | { | |
152 | u32 divm, divn, divp, cpcon, lfcon; | |
153 | ||
154 | if (clock_ll_read_pll(CLOCK_ID_MEMORY, &divm, &divn, &divp, | |
155 | &cpcon, &lfcon)) | |
156 | return -1; | |
157 | scratch2.pllm_base_divm = divm; | |
158 | scratch2.pllm_base_divn = divn; | |
159 | scratch2.pllm_base_divp = divp; | |
160 | scratch2.pllm_misc_cpcon = cpcon; | |
161 | scratch2.pllm_misc_lfcon = lfcon; | |
162 | } | |
163 | ||
164 | scratch2.gp_xm2cfga_padctrl_preemp = xm2cfga.preemp_en; | |
165 | scratch2.gp_xm2cfgd_padctrl_schmt = xm2cfgd.schmt_en; | |
166 | scratch2.memory_type = sdram.memory_type; | |
167 | writel(scratch2.word, &pmc->pmc_scratch2); | |
168 | ||
169 | /* collect data from various sources for pmc_scratch4 */ | |
170 | fbio_spare.word = readl(&emc->fbio_spare); | |
171 | scratch4.word = 0; | |
172 | scratch4.emc_fbio_spare_cfg_wb0 = fbio_spare.cfg_wb0; | |
173 | scratch4.emc_clock_divider = sdram.emc_clock_divider; | |
174 | scratch4.pllm_stable_time = -1; | |
175 | scratch4.pllx_stable_time = -1; | |
176 | writel(scratch4.word, &pmc->pmc_scratch4); | |
177 | ||
178 | /* collect various data from sdram for pmc_scratch24 */ | |
179 | scratch24.word = 0; | |
180 | scratch24.emc_pin_program_wait = sdram.emc_pin_program_wait; | |
181 | scratch24.emc_auto_cal_wait = sdram.emc_auto_cal_wait; | |
182 | scratch24.warmboot_wait = sdram.warm_boot_wait; | |
183 | writel(scratch24.word, &pmc->pmc_scratch24); | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
188 | static u32 get_major_version(void) | |
189 | { | |
190 | u32 major_id; | |
191 | struct apb_misc_gp_ctlr *gp = | |
29f3e3f2 | 192 | (struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE; |
6570438a YL |
193 | |
194 | major_id = (readl(&gp->hidrev) & HIDREV_MAJORPREV_MASK) >> | |
195 | HIDREV_MAJORPREV_SHIFT; | |
196 | return major_id; | |
197 | } | |
198 | ||
199 | static int is_production_mode_fuse_set(struct fuse_regs *fuse) | |
200 | { | |
201 | return readl(&fuse->production_mode); | |
202 | } | |
203 | ||
204 | static int is_odm_production_mode_fuse_set(struct fuse_regs *fuse) | |
205 | { | |
206 | return readl(&fuse->security_mode); | |
207 | } | |
208 | ||
209 | static int is_failure_analysis_mode(struct fuse_regs *fuse) | |
210 | { | |
211 | return readl(&fuse->fa); | |
212 | } | |
213 | ||
214 | static int ap20_is_odm_production_mode(void) | |
215 | { | |
29f3e3f2 | 216 | struct fuse_regs *fuse = (struct fuse_regs *)NV_PA_FUSE_BASE; |
6570438a YL |
217 | |
218 | if (!is_failure_analysis_mode(fuse) && | |
219 | is_odm_production_mode_fuse_set(fuse)) | |
220 | return 1; | |
221 | else | |
222 | return 0; | |
223 | } | |
224 | ||
225 | static int ap20_is_production_mode(void) | |
226 | { | |
29f3e3f2 | 227 | struct fuse_regs *fuse = (struct fuse_regs *)NV_PA_FUSE_BASE; |
6570438a YL |
228 | |
229 | if (get_major_version() == 0) | |
230 | return 1; | |
231 | ||
232 | if (!is_failure_analysis_mode(fuse) && | |
233 | is_production_mode_fuse_set(fuse) && | |
234 | !is_odm_production_mode_fuse_set(fuse)) | |
235 | return 1; | |
236 | else | |
237 | return 0; | |
238 | } | |
239 | ||
240 | static enum fuse_operating_mode fuse_get_operation_mode(void) | |
241 | { | |
242 | u32 chip_id; | |
243 | struct apb_misc_gp_ctlr *gp = | |
29f3e3f2 | 244 | (struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE; |
6570438a YL |
245 | |
246 | chip_id = (readl(&gp->hidrev) & HIDREV_CHIPID_MASK) >> | |
247 | HIDREV_CHIPID_SHIFT; | |
00a2749d | 248 | if (chip_id == CHIPID_TEGRA20) { |
6570438a YL |
249 | if (ap20_is_odm_production_mode()) { |
250 | printf("!! odm_production_mode is not supported !!\n"); | |
251 | return MODE_UNDEFINED; | |
252 | } else | |
253 | if (ap20_is_production_mode()) | |
254 | return MODE_PRODUCTION; | |
255 | else | |
256 | return MODE_UNDEFINED; | |
257 | } | |
258 | return MODE_UNDEFINED; | |
259 | } | |
260 | ||
261 | static void determine_crypto_options(int *is_encrypted, int *is_signed, | |
262 | int *use_zero_key) | |
263 | { | |
264 | switch (fuse_get_operation_mode()) { | |
265 | case MODE_PRODUCTION: | |
266 | *is_encrypted = 0; | |
267 | *is_signed = 1; | |
268 | *use_zero_key = 1; | |
269 | break; | |
270 | case MODE_UNDEFINED: | |
271 | default: | |
272 | *is_encrypted = 0; | |
273 | *is_signed = 0; | |
274 | *use_zero_key = 0; | |
275 | break; | |
276 | } | |
277 | } | |
278 | ||
279 | static int sign_wb_code(u32 start, u32 length, int use_zero_key) | |
280 | { | |
281 | int err; | |
282 | u8 *source; /* Pointer to source */ | |
283 | u8 *hash; | |
284 | ||
285 | /* Calculate AES block parameters. */ | |
286 | source = (u8 *)(start + offsetof(struct wb_header, random_aes_block)); | |
287 | length -= offsetof(struct wb_header, random_aes_block); | |
288 | hash = (u8 *)(start + offsetof(struct wb_header, hash)); | |
289 | err = sign_data_block(source, length, hash); | |
290 | ||
291 | return err; | |
292 | } | |
293 | ||
294 | int warmboot_prepare_code(u32 seg_address, u32 seg_length) | |
295 | { | |
296 | int err = 0; | |
297 | u32 length; /* length of the signed/encrypt code */ | |
298 | struct wb_header *dst_header; /* Pointer to dest WB header */ | |
299 | int is_encrypted; /* Segment is encrypted */ | |
300 | int is_signed; /* Segment is signed */ | |
301 | int use_zero_key; /* Use key of all zeros */ | |
302 | ||
303 | /* Determine crypto options. */ | |
304 | determine_crypto_options(&is_encrypted, &is_signed, &use_zero_key); | |
305 | ||
306 | /* Get the actual code limits. */ | |
307 | length = roundup(((u32)wb_end - (u32)wb_start), 16); | |
308 | ||
309 | /* | |
310 | * The region specified by seg_address must be in SDRAM and must be | |
311 | * nonzero in length. | |
312 | */ | |
313 | if (seg_length == 0 || seg_address < NV_PA_SDRAM_BASE || | |
314 | seg_address + seg_length >= NV_PA_SDRAM_BASE + gd->ram_size) { | |
315 | err = -EFAULT; | |
316 | goto fail; | |
317 | } | |
318 | ||
319 | /* Things must be 16-byte aligned. */ | |
320 | if ((seg_length & 0xF) || (seg_address & 0xF)) { | |
321 | err = -EINVAL; | |
322 | goto fail; | |
323 | } | |
324 | ||
325 | /* Will the code fit? (destination includes wb_header + wb code) */ | |
326 | if (seg_length < (length + sizeof(struct wb_header))) { | |
327 | err = -EINVAL; | |
328 | goto fail; | |
329 | } | |
330 | ||
331 | dst_header = (struct wb_header *)seg_address; | |
332 | memset((char *)dst_header, 0, sizeof(struct wb_header)); | |
333 | ||
334 | /* Populate the random_aes_block as requested. */ | |
335 | { | |
336 | u32 *aes_block = (u32 *)&(dst_header->random_aes_block); | |
337 | u32 *end = (u32 *)(((u32)aes_block) + | |
338 | sizeof(dst_header->random_aes_block)); | |
339 | ||
340 | do { | |
341 | *aes_block++ = 0; | |
342 | } while (aes_block < end); | |
343 | } | |
344 | ||
345 | /* Populate the header. */ | |
346 | dst_header->length_insecure = length + sizeof(struct wb_header); | |
347 | dst_header->length_secure = length + sizeof(struct wb_header); | |
150c2493 TW |
348 | dst_header->destination = NV_WB_RUN_ADDRESS; |
349 | dst_header->entry_point = NV_WB_RUN_ADDRESS; | |
6570438a YL |
350 | dst_header->code_length = length; |
351 | ||
352 | if (is_encrypted) { | |
353 | printf("!!!! Encryption is not supported !!!!\n"); | |
354 | dst_header->length_insecure = 0; | |
355 | err = -EACCES; | |
356 | goto fail; | |
357 | } else | |
358 | /* copy the wb code directly following dst_header. */ | |
359 | memcpy((char *)(dst_header+1), (char *)wb_start, length); | |
360 | ||
361 | if (is_signed) | |
362 | err = sign_wb_code(seg_address, dst_header->length_insecure, | |
363 | use_zero_key); | |
364 | ||
365 | fail: | |
366 | if (err) | |
367 | printf("Warning: warmboot code copy failed (error=%d)\n", err); | |
368 | ||
369 | return err; | |
370 | } |