diff options
author | Terje Bergstrom <tbergstrom@nvidia.com> | 2016-03-23 11:43:53 -0400 |
---|---|---|
committer | Terje Bergstrom <tbergstrom@nvidia.com> | 2016-05-23 17:15:25 -0400 |
commit | fb64e1f1b9c2162f521dfe3956655accb911d6a7 (patch) | |
tree | 15236c00323c8d24102f5a1221f1b313d62bfac2 /drivers/gpu/nvgpu/gm206/bios_gm206.c | |
parent | c25e1da93e09a421b8c2b91df720d4bc603db378 (diff) |
gpu: nvgpu: Add support for gm204 and gm206
Add support for chips gm204 and gm206. Adds also support for reading
VBIOS and booting devinit and pre-os images on PMU.
Change-Id: I4824b44245611e5379ace62793cc37158048f432
Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com>
Reviewed-on: http://git-master/r/1120467
GVS: Gerrit_Virtual_Submit
Reviewed-by: Ken Adams <kadams@nvidia.com>
Diffstat (limited to 'drivers/gpu/nvgpu/gm206/bios_gm206.c')
-rw-r--r-- | drivers/gpu/nvgpu/gm206/bios_gm206.c | 718 |
1 files changed, 718 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/gm206/bios_gm206.c b/drivers/gpu/nvgpu/gm206/bios_gm206.c new file mode 100644 index 00000000..005507bc --- /dev/null +++ b/drivers/gpu/nvgpu/gm206/bios_gm206.c | |||
@@ -0,0 +1,718 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/delay.h> | ||
15 | #include <linux/types.h> | ||
16 | |||
17 | #include "gk20a/gk20a.h" | ||
18 | #include "gm20b/fifo_gm20b.h" | ||
19 | #include "fifo_gm206.h" | ||
20 | #include "hw_pwr_gm206.h" | ||
21 | #include "hw_mc_gm206.h" | ||
22 | #include "hw_xve_gm206.h" | ||
23 | #include "hw_top_gm206.h" | ||
24 | |||
25 | #define BIT_HEADER_ID 0xb8ff | ||
26 | #define BIT_HEADER_SIGNATURE 0x00544942 | ||
27 | #define BIOS_SIZE 0x40000 | ||
28 | #define NV_PCFG 0x88000 | ||
29 | #define PMU_BOOT_TIMEOUT_DEFAULT 100 /* usec */ | ||
30 | #define PMU_BOOT_TIMEOUT_MAX 2000000 /* usec */ | ||
31 | |||
32 | static u16 gm206_bios_rdu16(struct gk20a *g, int offset) | ||
33 | { | ||
34 | u16 val = (g->bios.data[offset+1] << 8) + g->bios.data[offset]; | ||
35 | return val; | ||
36 | } | ||
37 | |||
38 | static u32 gm206_bios_rdu32(struct gk20a *g, int offset) | ||
39 | { | ||
40 | u32 val = (g->bios.data[offset+3] << 24) + | ||
41 | (g->bios.data[offset+2] << 16) + | ||
42 | (g->bios.data[offset+1] << 8) + | ||
43 | g->bios.data[offset]; | ||
44 | return val; | ||
45 | } | ||
46 | |||
47 | struct bit { | ||
48 | u16 id; | ||
49 | u32 signature; | ||
50 | u16 bcd_version; | ||
51 | u8 header_size; | ||
52 | u8 token_size; | ||
53 | u8 token_entries; | ||
54 | u8 header_checksum; | ||
55 | } __packed; | ||
56 | |||
57 | struct bit_token { | ||
58 | u8 token_id; | ||
59 | u8 data_version; | ||
60 | u16 data_size; | ||
61 | u16 data_ptr; | ||
62 | } __packed; | ||
63 | |||
64 | #define TOKEN_ID_NVINIT_PTRS 0x49 | ||
65 | #define TOKEN_ID_FALCON_DATA 0x70 | ||
66 | |||
67 | struct nvinit_ptrs { | ||
68 | u16 initscript_table_ptr; | ||
69 | u16 macro_index_table_ptr; | ||
70 | u16 macro_table_ptr; | ||
71 | u16 condition_table_ptr; | ||
72 | u16 io_condition_table_ptr; | ||
73 | u16 io_flag_condition_table_ptr; | ||
74 | u16 init_function_table_ptr; | ||
75 | u16 vbios_private_table_ptr; | ||
76 | u16 data_arrays_table_ptr; | ||
77 | u16 pcie_settings_script_ptr; | ||
78 | u16 devinit_tables_ptr; | ||
79 | u16 devinit_tables_size; | ||
80 | u16 bootscripts_ptr; | ||
81 | u16 bootscripts_size; | ||
82 | u16 nvlink_config_data_ptr; | ||
83 | } __packed; | ||
84 | |||
85 | struct falcon_data_v2 { | ||
86 | u32 falcon_ucode_table_ptr; | ||
87 | } __packed; | ||
88 | |||
89 | struct falcon_ucode_table_hdr_v1 { | ||
90 | u8 version; | ||
91 | u8 header_size; | ||
92 | u8 entry_size; | ||
93 | u8 entry_count; | ||
94 | u8 desc_version; | ||
95 | u8 desc_size; | ||
96 | } __packed; | ||
97 | |||
98 | struct falcon_ucode_table_entry_v1 { | ||
99 | u8 application_id; | ||
100 | u8 target_id; | ||
101 | u32 desc_ptr; | ||
102 | } __packed; | ||
103 | |||
104 | #define TARGET_ID_PMU 0x01 | ||
105 | #define APPLICATION_ID_DEVINIT 0x04 | ||
106 | #define APPLICATION_ID_PRE_OS 0x01 | ||
107 | |||
108 | struct falcon_ucode_desc_v1 { | ||
109 | union { | ||
110 | u32 v_desc; | ||
111 | u32 stored_size; | ||
112 | } hdr_size; | ||
113 | u32 uncompressed_size; | ||
114 | u32 virtual_entry; | ||
115 | u32 interface_offset; | ||
116 | u32 imem_phys_base; | ||
117 | u32 imem_load_size; | ||
118 | u32 imem_virt_base; | ||
119 | u32 imem_sec_base; | ||
120 | u32 imem_sec_size; | ||
121 | u32 dmem_offset; | ||
122 | u32 dmem_phys_base; | ||
123 | u32 dmem_load_size; | ||
124 | } __packed; | ||
125 | |||
126 | struct application_interface_table_hdr_v1 { | ||
127 | u8 version; | ||
128 | u8 header_size; | ||
129 | u8 entry_size; | ||
130 | u8 entry_count; | ||
131 | } __packed; | ||
132 | |||
133 | struct application_interface_entry_v1 { | ||
134 | u32 id; | ||
135 | u32 dmem_offset; | ||
136 | } __packed; | ||
137 | |||
138 | #define APPINFO_ID_DEVINIT 0x01 | ||
139 | |||
140 | struct devinit_engine_interface { | ||
141 | u32 field0; | ||
142 | u32 field1; | ||
143 | u32 tables_phys_base; | ||
144 | u32 tables_virt_base; | ||
145 | u32 script_phys_base; | ||
146 | u32 script_virt_base; | ||
147 | u32 script_virt_entry; | ||
148 | u16 script_size; | ||
149 | u8 memory_strap_count; | ||
150 | u8 reserved; | ||
151 | u32 memory_information_table_virt_base; | ||
152 | u32 empty_script_virt_base; | ||
153 | u32 cond_table_virt_base; | ||
154 | u32 io_cond_table_virt_base; | ||
155 | u32 data_arrays_table_virt_base; | ||
156 | u32 gpio_assignment_table_virt_base; | ||
157 | } __packed; | ||
158 | |||
159 | struct pci_exp_rom { | ||
160 | u16 sig; | ||
161 | u8 reserved[0x16]; | ||
162 | u16 pci_data_struct_ptr; | ||
163 | u32 size_of_block; | ||
164 | } __packed; | ||
165 | |||
166 | struct pci_data_struct { | ||
167 | u32 sig; | ||
168 | u16 vendor_id; | ||
169 | u16 device_id; | ||
170 | u16 device_list_ptr; | ||
171 | u16 pci_data_struct_len; | ||
172 | u8 pci_data_struct_rev; | ||
173 | u8 class_code[3]; | ||
174 | u16 image_len; | ||
175 | u16 vendor_rom_rev; | ||
176 | u8 code_type; | ||
177 | u8 last_image; | ||
178 | u16 max_runtime_image_len; | ||
179 | } __packed; | ||
180 | |||
181 | struct pci_ext_data_struct { | ||
182 | u32 sig; | ||
183 | u16 nv_pci_data_ext_rev; | ||
184 | u16 nv_pci_data_ext_len; | ||
185 | u16 sub_image_len; | ||
186 | u8 priv_last_image; | ||
187 | u8 flags; | ||
188 | } __packed; | ||
189 | |||
190 | static void gm206_bios_parse_rom(struct gk20a *g) | ||
191 | { | ||
192 | int offset = 0; | ||
193 | int last = 0; | ||
194 | |||
195 | while (!last) { | ||
196 | struct pci_exp_rom *pci_rom; | ||
197 | struct pci_data_struct *pci_data; | ||
198 | struct pci_ext_data_struct *pci_ext_data; | ||
199 | |||
200 | pci_rom = (struct pci_exp_rom *)&g->bios.data[offset]; | ||
201 | gk20a_dbg_fn("pci rom sig %04x ptr %04x block %x", | ||
202 | pci_rom->sig, pci_rom->pci_data_struct_ptr, | ||
203 | pci_rom->size_of_block); | ||
204 | |||
205 | pci_data = | ||
206 | (struct pci_data_struct *) | ||
207 | &g->bios.data[offset + pci_rom->pci_data_struct_ptr]; | ||
208 | gk20a_dbg_fn("pci data sig %08x len %d image len %x type %x last %d max %08x", | ||
209 | pci_data->sig, pci_data->pci_data_struct_len, | ||
210 | pci_data->image_len, pci_data->code_type, | ||
211 | pci_data->last_image, | ||
212 | pci_data->max_runtime_image_len); | ||
213 | |||
214 | if (pci_data->code_type == 0x3) { | ||
215 | pci_ext_data = (struct pci_ext_data_struct *) | ||
216 | &g->bios.data[(offset + | ||
217 | pci_rom->pci_data_struct_ptr + | ||
218 | pci_data->pci_data_struct_len + | ||
219 | 0xf) | ||
220 | & ~0xf]; | ||
221 | gk20a_dbg_fn("pci ext data sig %08x rev %x len %x sub_image_len %x priv_last %d flags %x", | ||
222 | pci_ext_data->sig, | ||
223 | pci_ext_data->nv_pci_data_ext_rev, | ||
224 | pci_ext_data->nv_pci_data_ext_len, | ||
225 | pci_ext_data->sub_image_len, | ||
226 | pci_ext_data->priv_last_image, | ||
227 | pci_ext_data->flags); | ||
228 | |||
229 | gk20a_dbg_fn("expansion rom offset %x", | ||
230 | pci_data->image_len * 512); | ||
231 | g->bios.expansion_rom_offset = | ||
232 | pci_data->image_len * 512; | ||
233 | offset += pci_ext_data->sub_image_len * 512; | ||
234 | last = pci_ext_data->priv_last_image; | ||
235 | } else { | ||
236 | offset += pci_data->image_len * 512; | ||
237 | last = pci_data->last_image; | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | |||
242 | static void gm206_bios_parse_nvinit_ptrs(struct gk20a *g, int offset) | ||
243 | { | ||
244 | struct nvinit_ptrs nvinit_ptrs; | ||
245 | |||
246 | memcpy(&nvinit_ptrs, &g->bios.data[offset], sizeof(nvinit_ptrs)); | ||
247 | gk20a_dbg_fn("devinit ptr %x size %d", nvinit_ptrs.devinit_tables_ptr, | ||
248 | nvinit_ptrs.devinit_tables_size); | ||
249 | gk20a_dbg_fn("bootscripts ptr %x size %d", nvinit_ptrs.bootscripts_ptr, | ||
250 | nvinit_ptrs.bootscripts_size); | ||
251 | |||
252 | g->bios.devinit_tables = &g->bios.data[nvinit_ptrs.devinit_tables_ptr]; | ||
253 | g->bios.devinit_tables_size = nvinit_ptrs.devinit_tables_size; | ||
254 | g->bios.bootscripts = &g->bios.data[nvinit_ptrs.bootscripts_ptr]; | ||
255 | g->bios.bootscripts_size = nvinit_ptrs.bootscripts_size; | ||
256 | } | ||
257 | |||
258 | static void gm206_bios_parse_devinit_appinfo(struct gk20a *g, int dmem_offset) | ||
259 | { | ||
260 | struct devinit_engine_interface interface; | ||
261 | |||
262 | memcpy(&interface, &g->bios.devinit.dmem[dmem_offset], sizeof(interface)); | ||
263 | gk20a_dbg_fn("devinit tables phys %x script phys %x size %d", | ||
264 | interface.tables_phys_base, | ||
265 | interface.script_phys_base, | ||
266 | interface.script_size); | ||
267 | |||
268 | g->bios.devinit_tables_phys_base = interface.tables_phys_base; | ||
269 | g->bios.devinit_script_phys_base = interface.script_phys_base; | ||
270 | } | ||
271 | |||
272 | static int gm206_bios_parse_appinfo_table(struct gk20a *g, int offset) | ||
273 | { | ||
274 | struct application_interface_table_hdr_v1 hdr; | ||
275 | int i; | ||
276 | |||
277 | memcpy(&hdr, &g->bios.data[offset], sizeof(hdr)); | ||
278 | |||
279 | gk20a_dbg_fn("appInfoHdr ver %d size %d entrySize %d entryCount %d", | ||
280 | hdr.version, hdr.header_size, | ||
281 | hdr.entry_size, hdr.entry_count); | ||
282 | |||
283 | if (hdr.version != 1) | ||
284 | return 0; | ||
285 | |||
286 | offset += sizeof(hdr); | ||
287 | for (i = 0; i < hdr.entry_count; i++) { | ||
288 | struct application_interface_entry_v1 entry; | ||
289 | |||
290 | memcpy(&entry, &g->bios.data[offset], sizeof(entry)); | ||
291 | |||
292 | gk20a_dbg_fn("appInfo id %d dmem_offset %d", | ||
293 | entry.id, entry.dmem_offset); | ||
294 | |||
295 | if (entry.id == APPINFO_ID_DEVINIT) | ||
296 | gm206_bios_parse_devinit_appinfo(g, entry.dmem_offset); | ||
297 | |||
298 | offset += hdr.entry_size; | ||
299 | } | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | static int gm206_bios_parse_falcon_ucode_desc(struct gk20a *g, | ||
305 | struct nvgpu_bios_ucode *ucode, int offset) | ||
306 | { | ||
307 | struct falcon_ucode_desc_v1 desc; | ||
308 | |||
309 | memcpy(&desc, &g->bios.data[offset], sizeof(desc)); | ||
310 | gk20a_dbg_info("falcon ucode desc stored size %d uncompressed size %d", | ||
311 | desc.hdr_size.stored_size, desc.uncompressed_size); | ||
312 | gk20a_dbg_info("falcon ucode desc virtualEntry %x, interfaceOffset %x", | ||
313 | desc.virtual_entry, desc.interface_offset); | ||
314 | gk20a_dbg_info("falcon ucode IMEM phys base %x, load size %x virt base %x sec base %x sec size %x", | ||
315 | desc.imem_phys_base, desc.imem_load_size, | ||
316 | desc.imem_virt_base, desc.imem_sec_base, | ||
317 | desc.imem_sec_size); | ||
318 | gk20a_dbg_info("falcon ucode DMEM offset %d phys base %x, load size %d", | ||
319 | desc.dmem_offset, desc.dmem_phys_base, | ||
320 | desc.dmem_load_size); | ||
321 | |||
322 | if (desc.hdr_size.stored_size != desc.uncompressed_size) { | ||
323 | gk20a_dbg_info("does not match"); | ||
324 | return -EINVAL; | ||
325 | } | ||
326 | |||
327 | ucode->code_entry_point = desc.virtual_entry; | ||
328 | ucode->bootloader = &g->bios.data[offset] + sizeof(desc); | ||
329 | ucode->bootloader_phys_base = desc.imem_phys_base; | ||
330 | ucode->bootloader_size = desc.imem_load_size - desc.imem_sec_size; | ||
331 | ucode->ucode = ucode->bootloader + ucode->bootloader_size; | ||
332 | ucode->phys_base = ucode->bootloader_phys_base + ucode->bootloader_size; | ||
333 | ucode->size = desc.imem_sec_size; | ||
334 | ucode->dmem = ucode->bootloader + desc.dmem_offset; | ||
335 | ucode->dmem_phys_base = desc.dmem_phys_base; | ||
336 | ucode->dmem_size = desc.dmem_load_size; | ||
337 | |||
338 | return gm206_bios_parse_appinfo_table(g, | ||
339 | offset + sizeof(desc) + | ||
340 | desc.dmem_offset + desc.interface_offset); | ||
341 | } | ||
342 | |||
343 | static int gm206_bios_parse_falcon_ucode_table(struct gk20a *g, int offset) | ||
344 | { | ||
345 | struct falcon_ucode_table_hdr_v1 hdr; | ||
346 | int i; | ||
347 | |||
348 | memcpy(&hdr, &g->bios.data[offset], sizeof(hdr)); | ||
349 | gk20a_dbg_fn("falcon ucode table ver %d size %d entrySize %d entryCount %d descVer %d descSize %d", | ||
350 | hdr.version, hdr.header_size, | ||
351 | hdr.entry_size, hdr.entry_count, | ||
352 | hdr.desc_version, hdr.desc_size); | ||
353 | |||
354 | if (hdr.version != 1) | ||
355 | return -EINVAL; | ||
356 | |||
357 | offset += hdr.header_size; | ||
358 | |||
359 | for (i = 0; i < hdr.entry_count; i++) { | ||
360 | struct falcon_ucode_table_entry_v1 entry; | ||
361 | |||
362 | memcpy(&entry, &g->bios.data[offset], sizeof(entry)); | ||
363 | |||
364 | gk20a_dbg_fn("falcon ucode table entry appid %x targetId %x descPtr %x", | ||
365 | entry.application_id, entry.target_id, | ||
366 | entry.desc_ptr); | ||
367 | |||
368 | if (entry.target_id == TARGET_ID_PMU && | ||
369 | entry.application_id == APPLICATION_ID_DEVINIT) { | ||
370 | int err; | ||
371 | |||
372 | err = gm206_bios_parse_falcon_ucode_desc(g, | ||
373 | &g->bios.devinit, entry.desc_ptr); | ||
374 | if (err) | ||
375 | err = gm206_bios_parse_falcon_ucode_desc(g, | ||
376 | &g->bios.devinit, | ||
377 | entry.desc_ptr + | ||
378 | g->bios.expansion_rom_offset); | ||
379 | |||
380 | if (err) | ||
381 | gk20a_err(dev_from_gk20a(g), | ||
382 | "could not parse devinit ucode desc"); | ||
383 | } else if (entry.target_id == TARGET_ID_PMU && | ||
384 | entry.application_id == APPLICATION_ID_PRE_OS) { | ||
385 | int err; | ||
386 | |||
387 | err = gm206_bios_parse_falcon_ucode_desc(g, | ||
388 | &g->bios.preos, entry.desc_ptr); | ||
389 | if (err) | ||
390 | err = gm206_bios_parse_falcon_ucode_desc(g, | ||
391 | &g->bios.preos, | ||
392 | entry.desc_ptr + | ||
393 | g->bios.expansion_rom_offset); | ||
394 | |||
395 | if (err) | ||
396 | gk20a_err(dev_from_gk20a(g), | ||
397 | "could not parse preos ucode desc"); | ||
398 | } | ||
399 | |||
400 | offset += hdr.entry_size; | ||
401 | } | ||
402 | |||
403 | return 0; | ||
404 | } | ||
405 | |||
406 | static void gm206_bios_parse_falcon_data_v2(struct gk20a *g, int offset) | ||
407 | { | ||
408 | struct falcon_data_v2 falcon_data; | ||
409 | int err; | ||
410 | |||
411 | memcpy(&falcon_data, &g->bios.data[offset], sizeof(falcon_data)); | ||
412 | gk20a_dbg_fn("falcon ucode table ptr %x", | ||
413 | falcon_data.falcon_ucode_table_ptr); | ||
414 | err = gm206_bios_parse_falcon_ucode_table(g, | ||
415 | falcon_data.falcon_ucode_table_ptr); | ||
416 | if (err) | ||
417 | err = gm206_bios_parse_falcon_ucode_table(g, | ||
418 | falcon_data.falcon_ucode_table_ptr + | ||
419 | g->bios.expansion_rom_offset); | ||
420 | |||
421 | if (err) | ||
422 | gk20a_err(dev_from_gk20a(g), | ||
423 | "could not parse falcon ucode table"); | ||
424 | } | ||
425 | |||
426 | static void gm206_bios_parse_bit(struct gk20a *g, int offset) | ||
427 | { | ||
428 | struct bit bit; | ||
429 | struct bit_token bit_token; | ||
430 | int i; | ||
431 | |||
432 | gk20a_dbg_fn(""); | ||
433 | memcpy(&bit, &g->bios.data[offset], sizeof(bit)); | ||
434 | |||
435 | gk20a_dbg_info("BIT header: %04x %08x", bit.id, bit.signature); | ||
436 | gk20a_dbg_info("tokens: %d entries * %d bytes", | ||
437 | bit.token_entries, bit.token_size); | ||
438 | |||
439 | offset += bit.header_size; | ||
440 | for (i = 0; i < bit.token_entries; i++) { | ||
441 | memcpy(&bit_token, &g->bios.data[offset], sizeof(bit_token)); | ||
442 | |||
443 | gk20a_dbg_info("BIT token id %d ptr %d size %d ver %d", | ||
444 | bit_token.token_id, bit_token.data_ptr, | ||
445 | bit_token.data_size, bit_token.data_version); | ||
446 | |||
447 | switch (bit_token.token_id) { | ||
448 | case TOKEN_ID_NVINIT_PTRS: | ||
449 | gm206_bios_parse_nvinit_ptrs(g, bit_token.data_ptr); | ||
450 | break; | ||
451 | case TOKEN_ID_FALCON_DATA: | ||
452 | if (bit_token.data_version == 2) | ||
453 | gm206_bios_parse_falcon_data_v2(g, | ||
454 | bit_token.data_ptr); | ||
455 | break; | ||
456 | default: | ||
457 | break; | ||
458 | } | ||
459 | |||
460 | offset += bit.token_size; | ||
461 | } | ||
462 | gk20a_dbg_fn("done"); | ||
463 | } | ||
464 | |||
465 | static void upload_code(struct gk20a *g, u32 dst, | ||
466 | u8 *src, u32 size, u8 port, bool sec) | ||
467 | { | ||
468 | u32 i, words; | ||
469 | u32 *src_u32 = (u32 *)src; | ||
470 | u32 blk; | ||
471 | u32 tag = 0; | ||
472 | |||
473 | gk20a_dbg_info("upload %d bytes to %x", size, dst); | ||
474 | |||
475 | words = size >> 2; | ||
476 | |||
477 | blk = dst >> 8; | ||
478 | tag = blk; | ||
479 | |||
480 | gk20a_dbg_info("upload %d words to %x block %d", | ||
481 | words, dst, blk); | ||
482 | |||
483 | gk20a_writel(g, pwr_falcon_imemc_r(port), | ||
484 | pwr_falcon_imemc_offs_f(dst >> 2) | | ||
485 | pwr_falcon_imemc_blk_f(blk) | | ||
486 | pwr_falcon_imemc_aincw_f(1) | | ||
487 | sec << 28); | ||
488 | |||
489 | for (i = 0; i < words; i++) { | ||
490 | if (i % 64 == 0) { | ||
491 | gk20a_writel(g, 0x10a188, tag); | ||
492 | tag++; | ||
493 | } | ||
494 | |||
495 | gk20a_writel(g, pwr_falcon_imemd_r(port), src_u32[i]); | ||
496 | } | ||
497 | |||
498 | while (i % 64) { | ||
499 | gk20a_writel(g, pwr_falcon_imemd_r(port), 0); | ||
500 | i++; | ||
501 | } | ||
502 | } | ||
503 | |||
504 | static void upload_data(struct gk20a *g, u32 dst, u8 *src, u32 size, u8 port) | ||
505 | { | ||
506 | u32 i, words; | ||
507 | u32 *src_u32 = (u32 *)src; | ||
508 | u32 blk; | ||
509 | |||
510 | gk20a_dbg_info("upload %d bytes to %x", size, dst); | ||
511 | |||
512 | words = DIV_ROUND_UP(size, 4); | ||
513 | |||
514 | blk = dst >> 8; | ||
515 | |||
516 | gk20a_dbg_info("upload %d words to %x blk %d", | ||
517 | words, dst, blk); | ||
518 | gk20a_writel(g, pwr_falcon_dmemc_r(port), | ||
519 | pwr_falcon_dmemc_offs_f(dst >> 2) | | ||
520 | pwr_falcon_dmemc_blk_f(blk) | | ||
521 | pwr_falcon_dmemc_aincw_f(1)); | ||
522 | |||
523 | for (i = 0; i < words; i++) | ||
524 | gk20a_writel(g, pwr_falcon_dmemd_r(port), src_u32[i]); | ||
525 | } | ||
526 | |||
527 | static int gm206_bios_devinit(struct gk20a *g) | ||
528 | { | ||
529 | int retries = PMU_BOOT_TIMEOUT_MAX / PMU_BOOT_TIMEOUT_DEFAULT; | ||
530 | int err = 0; | ||
531 | int val; | ||
532 | |||
533 | gk20a_dbg_fn(""); | ||
534 | g->ops.pmu.reset(g); | ||
535 | |||
536 | do { | ||
537 | u32 w = gk20a_readl(g, pwr_falcon_dmactl_r()) & | ||
538 | (pwr_falcon_dmactl_dmem_scrubbing_m() | | ||
539 | pwr_falcon_dmactl_imem_scrubbing_m()); | ||
540 | |||
541 | if (!w) { | ||
542 | gk20a_dbg_fn("done"); | ||
543 | break; | ||
544 | } | ||
545 | udelay(PMU_BOOT_TIMEOUT_DEFAULT); | ||
546 | } while (--retries || !tegra_platform_is_silicon()); | ||
547 | |||
548 | /* todo check retries */ | ||
549 | upload_code(g, g->bios.devinit.bootloader_phys_base, | ||
550 | g->bios.devinit.bootloader, | ||
551 | g->bios.devinit.bootloader_size, | ||
552 | 0, 0); | ||
553 | upload_code(g, g->bios.devinit.phys_base, | ||
554 | g->bios.devinit.ucode, | ||
555 | g->bios.devinit.size, | ||
556 | 0, 1); | ||
557 | upload_data(g, g->bios.devinit.dmem_phys_base, | ||
558 | g->bios.devinit.dmem, | ||
559 | g->bios.devinit.dmem_size, | ||
560 | 0); | ||
561 | upload_data(g, g->bios.devinit_tables_phys_base, | ||
562 | g->bios.devinit_tables, | ||
563 | g->bios.devinit_tables_size, | ||
564 | 0); | ||
565 | upload_data(g, g->bios.devinit_script_phys_base, | ||
566 | g->bios.bootscripts, | ||
567 | g->bios.bootscripts_size, | ||
568 | 0); | ||
569 | |||
570 | gk20a_writel(g, pwr_falcon_bootvec_r(), | ||
571 | pwr_falcon_bootvec_vec_f(g->bios.devinit.code_entry_point)); | ||
572 | gk20a_writel(g, pwr_falcon_dmactl_r(), | ||
573 | pwr_falcon_dmactl_require_ctx_f(0)); | ||
574 | gk20a_writel(g, pwr_falcon_cpuctl_r(), | ||
575 | pwr_falcon_cpuctl_startcpu_f(1)); | ||
576 | |||
577 | retries = PMU_BOOT_TIMEOUT_MAX / PMU_BOOT_TIMEOUT_DEFAULT; | ||
578 | do { | ||
579 | val = top_scratch1_devinit_completed_v( | ||
580 | gk20a_readl(g, top_scratch1_r())); | ||
581 | udelay(PMU_BOOT_TIMEOUT_DEFAULT); | ||
582 | } while (!val && retries--); | ||
583 | |||
584 | gk20a_writel(g, pwr_falcon_irqsclr_r(), | ||
585 | pwr_falcon_irqstat_halt_true_f()); | ||
586 | gk20a_readl(g, pwr_falcon_irqsclr_r()); | ||
587 | |||
588 | if (!retries) | ||
589 | err = -EINVAL; | ||
590 | |||
591 | gk20a_dbg_fn("done"); | ||
592 | return err; | ||
593 | } | ||
594 | |||
595 | static int gm206_bios_preos(struct gk20a *g) | ||
596 | { | ||
597 | int retries = GR_IDLE_CHECK_MAX / GR_IDLE_CHECK_DEFAULT; | ||
598 | int err = 0; | ||
599 | int val; | ||
600 | |||
601 | gk20a_dbg_fn(""); | ||
602 | g->ops.pmu.reset(g); | ||
603 | |||
604 | do { | ||
605 | u32 w = gk20a_readl(g, pwr_falcon_dmactl_r()) & | ||
606 | (pwr_falcon_dmactl_dmem_scrubbing_m() | | ||
607 | pwr_falcon_dmactl_imem_scrubbing_m()); | ||
608 | |||
609 | if (!w) { | ||
610 | gk20a_dbg_fn("done"); | ||
611 | break; | ||
612 | } | ||
613 | udelay(GR_IDLE_CHECK_DEFAULT); | ||
614 | } while (--retries || !tegra_platform_is_silicon()); | ||
615 | |||
616 | /* todo check retries */ | ||
617 | upload_code(g, g->bios.preos.bootloader_phys_base, | ||
618 | g->bios.preos.bootloader, | ||
619 | g->bios.preos.bootloader_size, | ||
620 | 0, 0); | ||
621 | upload_code(g, g->bios.preos.phys_base, | ||
622 | g->bios.preos.ucode, | ||
623 | g->bios.preos.size, | ||
624 | 0, 1); | ||
625 | upload_data(g, g->bios.preos.dmem_phys_base, | ||
626 | g->bios.preos.dmem, | ||
627 | g->bios.preos.dmem_size, | ||
628 | 0); | ||
629 | |||
630 | gk20a_writel(g, pwr_falcon_bootvec_r(), | ||
631 | pwr_falcon_bootvec_vec_f(g->bios.preos.code_entry_point)); | ||
632 | gk20a_writel(g, pwr_falcon_dmactl_r(), | ||
633 | pwr_falcon_dmactl_require_ctx_f(0)); | ||
634 | gk20a_writel(g, pwr_falcon_cpuctl_r(), | ||
635 | pwr_falcon_cpuctl_startcpu_f(1)); | ||
636 | |||
637 | retries = PMU_BOOT_TIMEOUT_MAX / PMU_BOOT_TIMEOUT_DEFAULT; | ||
638 | do { | ||
639 | val = pwr_falcon_cpuctl_halt_intr_v( | ||
640 | gk20a_readl(g, pwr_falcon_cpuctl_r())); | ||
641 | udelay(PMU_BOOT_TIMEOUT_DEFAULT); | ||
642 | } while (!val && retries--); | ||
643 | |||
644 | gk20a_writel(g, pwr_falcon_irqsclr_r(), | ||
645 | pwr_falcon_irqstat_halt_true_f()); | ||
646 | gk20a_readl(g, pwr_falcon_irqsclr_r()); | ||
647 | |||
648 | if (!retries) | ||
649 | err = -EINVAL; | ||
650 | |||
651 | gk20a_dbg_fn("done"); | ||
652 | return err; | ||
653 | } | ||
654 | |||
655 | static int gm206_bios_init(struct gk20a *g) | ||
656 | { | ||
657 | int i; | ||
658 | struct gk20a_platform *platform = dev_get_drvdata(g->dev); | ||
659 | struct dentry *d; | ||
660 | int err; | ||
661 | |||
662 | gk20a_dbg_fn(""); | ||
663 | g->bios.data = kzalloc(BIOS_SIZE, GFP_KERNEL); | ||
664 | if (!g->bios.data) | ||
665 | return -ENOMEM; | ||
666 | |||
667 | gk20a_dbg_info("reading bios"); | ||
668 | gk20a_writel(g, NV_PCFG + xve_rom_ctrl_r(), | ||
669 | xve_rom_ctrl_rom_shadow_disabled_f()); | ||
670 | for (i = 0; i < BIOS_SIZE/4; i++) { | ||
671 | u32 val = be32_to_cpu(gk20a_readl(g, 0x300000 + i*4)); | ||
672 | |||
673 | g->bios.data[(i*4)] = (val >> 24) & 0xff; | ||
674 | g->bios.data[(i*4)+1] = (val >> 16) & 0xff; | ||
675 | g->bios.data[(i*4)+2] = (val >> 8) & 0xff; | ||
676 | g->bios.data[(i*4)+3] = val & 0xff; | ||
677 | } | ||
678 | gk20a_writel(g, NV_PCFG + xve_rom_ctrl_r(), | ||
679 | xve_rom_ctrl_rom_shadow_enabled_f()); | ||
680 | |||
681 | gm206_bios_parse_rom(g); | ||
682 | gk20a_dbg_info("read bios"); | ||
683 | for (i = 0; i < BIOS_SIZE; i++) { | ||
684 | if (gm206_bios_rdu16(g, i) == BIT_HEADER_ID && | ||
685 | gm206_bios_rdu32(g, i+2) == BIT_HEADER_SIGNATURE) { | ||
686 | gm206_bios_parse_bit(g, i); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | g->bios_blob.data = g->bios.data; | ||
691 | g->bios_blob.size = BIOS_SIZE; | ||
692 | |||
693 | d = debugfs_create_blob("bios", S_IRUGO, platform->debugfs, | ||
694 | &g->bios_blob); | ||
695 | if (!d) | ||
696 | gk20a_err(g->dev, "No debugfs?"); | ||
697 | |||
698 | gk20a_dbg_fn("done"); | ||
699 | |||
700 | err = gm206_bios_devinit(g); | ||
701 | if (err) { | ||
702 | gk20a_err(g->dev, "devinit failed"); | ||
703 | return err; | ||
704 | } | ||
705 | |||
706 | err = gm206_bios_preos(g); | ||
707 | if (err) { | ||
708 | gk20a_err(g->dev, "pre-os failed"); | ||
709 | return err; | ||
710 | } | ||
711 | |||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | void gm206_init_bios(struct gpu_ops *gops) | ||
716 | { | ||
717 | gops->bios.init = gm206_bios_init; | ||
718 | } | ||