/* * Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #include #include #include #include #include "gk20a/gk20a.h" #include "gm20b/fifo_gm20b.h" #include "fifo_gm206.h" #include "hw_pwr_gm206.h" #include "hw_mc_gm206.h" #include "hw_xve_gm206.h" #include "hw_top_gm206.h" #include "bios_gm206.h" #include "nvgpu_common.h" #define BIT_HEADER_ID 0xb8ff #define BIT_HEADER_SIGNATURE 0x00544942 #define BIOS_SIZE 0x40000 #define NV_PCFG 0x88000 #define PCI_EXP_ROM_SIG 0xaa55 #define PCI_EXP_ROM_SIG_NV 0x4e56 #define ROM_FILE_PAYLOAD_OFFSET 0xa00 #define PMU_BOOT_TIMEOUT_DEFAULT 100 /* usec */ #define PMU_BOOT_TIMEOUT_MAX 2000000 /* usec */ #define BIOS_OVERLAY_NAME "bios-%04x.rom" #define BIOS_OVERLAY_NAME_FORMATTED "bios-xxxx.rom" static u16 gm206_bios_rdu16(struct gk20a *g, int offset) { u16 val = (g->bios.data[offset+1] << 8) + g->bios.data[offset]; return val; } static u32 gm206_bios_rdu32(struct gk20a *g, int offset) { u32 val = (g->bios.data[offset+3] << 24) + (g->bios.data[offset+2] << 16) + (g->bios.data[offset+1] << 8) + g->bios.data[offset]; return val; } struct bit { u16 id; u32 signature; u16 bcd_version; u8 header_size; u8 token_size; u8 token_entries; u8 header_checksum; } __packed; #define TOKEN_ID_BIOSDATA 0x42 #define TOKEN_ID_NVINIT_PTRS 0x49 #define TOKEN_ID_FALCON_DATA 0x70 #define TOKEN_ID_PERF_PTRS 0x50 #define TOKEN_ID_CLOCK_PTRS 0x43 #define TOKEN_ID_VIRT_PTRS 0x56 struct biosdata { u32 version; u8 oem_version; u8 checksum; u16 int15callbackspost; u16 int16callbackssystem; u16 boardid; u16 framecount; u8 biosmoddate[8]; } __packed; struct nvinit_ptrs { u16 initscript_table_ptr; u16 macro_index_table_ptr; u16 macro_table_ptr; u16 condition_table_ptr; u16 io_condition_table_ptr; u16 io_flag_condition_table_ptr; u16 init_function_table_ptr; u16 vbios_private_table_ptr; u16 data_arrays_table_ptr; u16 pcie_settings_script_ptr; u16 devinit_tables_ptr; u16 devinit_tables_size; u16 bootscripts_ptr; u16 bootscripts_size; u16 nvlink_config_data_ptr; } __packed; struct falcon_data_v2 { u32 falcon_ucode_table_ptr; } __packed; struct falcon_ucode_table_hdr_v1 { u8 version; u8 header_size; u8 entry_size; u8 entry_count; u8 desc_version; u8 desc_size; } __packed; struct falcon_ucode_table_entry_v1 { u8 application_id; u8 target_id; u32 desc_ptr; } __packed; #define TARGET_ID_PMU 0x01 #define APPLICATION_ID_DEVINIT 0x04 #define APPLICATION_ID_PRE_OS 0x01 struct falcon_ucode_desc_v1 { union { u32 v_desc; u32 stored_size; } hdr_size; u32 uncompressed_size; u32 virtual_entry; u32 interface_offset; u32 imem_phys_base; u32 imem_load_size; u32 imem_virt_base; u32 imem_sec_base; u32 imem_sec_size; u32 dmem_offset; u32 dmem_phys_base; u32 dmem_load_size; } __packed; struct application_interface_table_hdr_v1 { u8 version; u8 header_size; u8 entry_size; u8 entry_count; } __packed; struct application_interface_entry_v1 { u32 id; u32 dmem_offset; } __packed; #define APPINFO_ID_DEVINIT 0x01 struct devinit_engine_interface { u32 field0; u32 field1; u32 tables_phys_base; u32 tables_virt_base; u32 script_phys_base; u32 script_virt_base; u32 script_virt_entry; u16 script_size; u8 memory_strap_count; u8 reserved; u32 memory_information_table_virt_base; u32 empty_script_virt_base; u32 cond_table_virt_base; u32 io_cond_table_virt_base; u32 data_arrays_table_virt_base; u32 gpio_assignment_table_virt_base; } __packed; struct pci_exp_rom { u16 sig; u8 reserved[0x16]; u16 pci_data_struct_ptr; u32 size_of_block; } __packed; struct pci_data_struct { u32 sig; u16 vendor_id; u16 device_id; u16 device_list_ptr; u16 pci_data_struct_len; u8 pci_data_struct_rev; u8 class_code[3]; u16 image_len; u16 vendor_rom_rev; u8 code_type; u8 last_image; u16 max_runtime_image_len; } __packed; struct pci_ext_data_struct { u32 sig; u16 nv_pci_data_ext_rev; u16 nv_pci_data_ext_len; u16 sub_image_len; u8 priv_last_image; u8 flags; } __packed; static int gm206_bios_parse_rom(struct gk20a *g) { int offset = 0; int last = 0; while (!last) { struct pci_exp_rom *pci_rom; struct pci_data_struct *pci_data; struct pci_ext_data_struct *pci_ext_data; pci_rom = (struct pci_exp_rom *)&g->bios.data[offset]; gk20a_dbg_fn("pci rom sig %04x ptr %04x block %x", pci_rom->sig, pci_rom->pci_data_struct_ptr, pci_rom->size_of_block); if (pci_rom->sig != PCI_EXP_ROM_SIG && pci_rom->sig != PCI_EXP_ROM_SIG_NV) { gk20a_err(g->dev, "invalid VBIOS signature"); return -EINVAL; } pci_data = (struct pci_data_struct *) &g->bios.data[offset + pci_rom->pci_data_struct_ptr]; gk20a_dbg_fn("pci data sig %08x len %d image len %x type %x last %d max %08x", pci_data->sig, pci_data->pci_data_struct_len, pci_data->image_len, pci_data->code_type, pci_data->last_image, pci_data->max_runtime_image_len); if (pci_data->code_type == 0x3) { pci_ext_data = (struct pci_ext_data_struct *) &g->bios.data[(offset + pci_rom->pci_data_struct_ptr + pci_data->pci_data_struct_len + 0xf) & ~0xf]; gk20a_dbg_fn("pci ext data sig %08x rev %x len %x sub_image_len %x priv_last %d flags %x", pci_ext_data->sig, pci_ext_data->nv_pci_data_ext_rev, pci_ext_data->nv_pci_data_ext_len, pci_ext_data->sub_image_len, pci_ext_data->priv_last_image, pci_ext_data->flags); gk20a_dbg_fn("expansion rom offset %x", pci_data->image_len * 512); g->bios.expansion_rom_offset = pci_data->image_len * 512; offset += pci_ext_data->sub_image_len * 512; last = pci_ext_data->priv_last_image; } else { offset += pci_data->image_len * 512; last = pci_data->last_image; } } return 0; } static void gm206_bios_parse_biosdata(struct gk20a *g, int offset) { struct biosdata biosdata; memcpy(&biosdata, &g->bios.data[offset], sizeof(biosdata)); gk20a_dbg_fn("bios version %x, oem version %x", biosdata.version, biosdata.oem_version); g->gpu_characteristics.vbios_version = biosdata.version; g->gpu_characteristics.vbios_oem_version = biosdata.oem_version; } static void gm206_bios_parse_nvinit_ptrs(struct gk20a *g, int offset) { struct nvinit_ptrs nvinit_ptrs; memcpy(&nvinit_ptrs, &g->bios.data[offset], sizeof(nvinit_ptrs)); gk20a_dbg_fn("devinit ptr %x size %d", nvinit_ptrs.devinit_tables_ptr, nvinit_ptrs.devinit_tables_size); gk20a_dbg_fn("bootscripts ptr %x size %d", nvinit_ptrs.bootscripts_ptr, nvinit_ptrs.bootscripts_size); g->bios.devinit_tables = &g->bios.data[nvinit_ptrs.devinit_tables_ptr]; g->bios.devinit_tables_size = nvinit_ptrs.devinit_tables_size; g->bios.bootscripts = &g->bios.data[nvinit_ptrs.bootscripts_ptr]; g->bios.bootscripts_size = nvinit_ptrs.bootscripts_size; } static void gm206_bios_parse_devinit_appinfo(struct gk20a *g, int dmem_offset) { struct devinit_engine_interface interface; memcpy(&interface, &g->bios.devinit.dmem[dmem_offset], sizeof(interface)); gk20a_dbg_fn("devinit tables phys %x script phys %x size %d", interface.tables_phys_base, interface.script_phys_base, interface.script_size); g->bios.devinit_tables_phys_base = interface.tables_phys_base; g->bios.devinit_script_phys_base = interface.script_phys_base; } static int gm206_bios_parse_appinfo_table(struct gk20a *g, int offset) { struct application_interface_table_hdr_v1 hdr; int i; memcpy(&hdr, &g->bios.data[offset], sizeof(hdr)); gk20a_dbg_fn("appInfoHdr ver %d size %d entrySize %d entryCount %d", hdr.version, hdr.header_size, hdr.entry_size, hdr.entry_count); if (hdr.version != 1) return 0; offset += sizeof(hdr); for (i = 0; i < hdr.entry_count; i++) { struct application_interface_entry_v1 entry; memcpy(&entry, &g->bios.data[offset], sizeof(entry)); gk20a_dbg_fn("appInfo id %d dmem_offset %d", entry.id, entry.dmem_offset); if (entry.id == APPINFO_ID_DEVINIT) gm206_bios_parse_devinit_appinfo(g, entry.dmem_offset); offset += hdr.entry_size; } return 0; } static int gm206_bios_parse_falcon_ucode_desc(struct gk20a *g, struct nvgpu_bios_ucode *ucode, int offset) { struct falcon_ucode_desc_v1 desc; memcpy(&desc, &g->bios.data[offset], sizeof(desc)); gk20a_dbg_info("falcon ucode desc stored size %d uncompressed size %d", desc.hdr_size.stored_size, desc.uncompressed_size); gk20a_dbg_info("falcon ucode desc virtualEntry %x, interfaceOffset %x", desc.virtual_entry, desc.interface_offset); gk20a_dbg_info("falcon ucode IMEM phys base %x, load size %x virt base %x sec base %x sec size %x", desc.imem_phys_base, desc.imem_load_size, desc.imem_virt_base, desc.imem_sec_base, desc.imem_sec_size); gk20a_dbg_info("falcon ucode DMEM offset %d phys base %x, load size %d", desc.dmem_offset, desc.dmem_phys_base, desc.dmem_load_size); if (desc.hdr_size.stored_size != desc.uncompressed_size) { gk20a_dbg_info("does not match"); return -EINVAL; } ucode->code_entry_point = desc.virtual_entry; ucode->bootloader = &g->bios.data[offset] + sizeof(desc); ucode->bootloader_phys_base = desc.imem_phys_base; ucode->bootloader_size = desc.imem_load_size - desc.imem_sec_size; ucode->ucode = ucode->bootloader + ucode->bootloader_size; ucode->phys_base = ucode->bootloader_phys_base + ucode->bootloader_size; ucode->size = desc.imem_sec_size; ucode->dmem = ucode->bootloader + desc.dmem_offset; ucode->dmem_phys_base = desc.dmem_phys_base; ucode->dmem_size = desc.dmem_load_size; return gm206_bios_parse_appinfo_table(g, offset + sizeof(desc) + desc.dmem_offset + desc.interface_offset); } static int gm206_bios_parse_falcon_ucode_table(struct gk20a *g, int offset) { struct falcon_ucode_table_hdr_v1 hdr; int i; memcpy(&hdr, &g->bios.data[offset], sizeof(hdr)); gk20a_dbg_fn("falcon ucode table ver %d size %d entrySize %d entryCount %d descVer %d descSize %d", hdr.version, hdr.header_size, hdr.entry_size, hdr.entry_count, hdr.desc_version, hdr.desc_size); if (hdr.version != 1) return -EINVAL; offset += hdr.header_size; for (i = 0; i < hdr.entry_count; i++) { struct falcon_ucode_table_entry_v1 entry; memcpy(&entry, &g->bios.data[offset], sizeof(entry)); gk20a_dbg_fn("falcon ucode table entry appid %x targetId %x descPtr %x", entry.application_id, entry.target_id, entry.desc_ptr); if (entry.target_id == TARGET_ID_PMU && entry.application_id == APPLICATION_ID_DEVINIT) { int err; err = gm206_bios_parse_falcon_ucode_desc(g, &g->bios.devinit, entry.desc_ptr); if (err) err = gm206_bios_parse_falcon_ucode_desc(g, &g->bios.devinit, entry.desc_ptr + g->bios.expansion_rom_offset); if (err) gk20a_err(dev_from_gk20a(g), "could not parse devinit ucode desc"); } else if (entry.target_id == TARGET_ID_PMU && entry.application_id == APPLICATION_ID_PRE_OS) { int err; err = gm206_bios_parse_falcon_ucode_desc(g, &g->bios.preos, entry.desc_ptr); if (err) err = gm206_bios_parse_falcon_ucode_desc(g, &g->bios.preos, entry.desc_ptr + g->bios.expansion_rom_offset); if (err) gk20a_err(dev_from_gk20a(g), "could not parse preos ucode desc"); } offset += hdr.entry_size; } return 0; } static void gm206_bios_parse_falcon_data_v2(struct gk20a *g, int offset) { struct falcon_data_v2 falcon_data; int err; memcpy(&falcon_data, &g->bios.data[offset], sizeof(falcon_data)); gk20a_dbg_fn("falcon ucode table ptr %x", falcon_data.falcon_ucode_table_ptr); err = gm206_bios_parse_falcon_ucode_table(g, falcon_data.falcon_ucode_table_ptr); if (err) err = gm206_bios_parse_falcon_ucode_table(g, falcon_data.falcon_ucode_table_ptr + g->bios.expansion_rom_offset); if (err) gk20a_err(dev_from_gk20a(g), "could not parse falcon ucode table"); } static void *gm206_bios_get_perf_table_ptrs(struct gk20a *g, struct bit_token *ptoken, u8 table_id) { u32 perf_table_id_offset = 0; u8 *perf_table_ptr = NULL; u8 data_size = 4; if (ptoken != NULL) { if (ptoken->token_id == TOKEN_ID_VIRT_PTRS) { perf_table_id_offset = *((u16 *)&g->bios.data[ ptoken->data_ptr + (table_id * PERF_PTRS_WIDTH_16)]); data_size = PERF_PTRS_WIDTH_16; } else { perf_table_id_offset = *((u32 *)&g->bios.data[ ptoken->data_ptr + (table_id * PERF_PTRS_WIDTH)]); data_size = PERF_PTRS_WIDTH; } } else return (void *)perf_table_ptr; if (table_id < (ptoken->data_size/data_size)) { gk20a_dbg_info("Perf_Tbl_ID-offset 0x%x Tbl_ID_Ptr-offset- 0x%x", (ptoken->data_ptr + (table_id * data_size)), perf_table_id_offset); if (perf_table_id_offset != 0) { /* check is perf_table_id_offset is > 64k */ if (perf_table_id_offset & ~0xFFFF) perf_table_ptr = &g->bios.data[g->bios.expansion_rom_offset + perf_table_id_offset]; else perf_table_ptr = &g->bios.data[perf_table_id_offset]; } else gk20a_warn(g->dev, "PERF TABLE ID %d is NULL", table_id); } else gk20a_warn(g->dev, "INVALID PERF TABLE ID - %d ", table_id); return (void *)perf_table_ptr; } static void gm206_bios_parse_bit(struct gk20a *g, int offset) { struct bit bit; struct bit_token bit_token; int i; gk20a_dbg_fn(""); memcpy(&bit, &g->bios.data[offset], sizeof(bit)); gk20a_dbg_info("BIT header: %04x %08x", bit.id, bit.signature); gk20a_dbg_info("tokens: %d entries * %d bytes", bit.token_entries, bit.token_size); offset += bit.header_size; for (i = 0; i < bit.token_entries; i++) { memcpy(&bit_token, &g->bios.data[offset], sizeof(bit_token)); gk20a_dbg_info("BIT token id %d ptr %d size %d ver %d", bit_token.token_id, bit_token.data_ptr, bit_token.data_size, bit_token.data_version); switch (bit_token.token_id) { case TOKEN_ID_BIOSDATA: gm206_bios_parse_biosdata(g, bit_token.data_ptr); break; case TOKEN_ID_NVINIT_PTRS: gm206_bios_parse_nvinit_ptrs(g, bit_token.data_ptr); break; case TOKEN_ID_FALCON_DATA: if (bit_token.data_version == 2) gm206_bios_parse_falcon_data_v2(g, bit_token.data_ptr); break; case TOKEN_ID_PERF_PTRS: g->bios.perf_token = (struct bit_token *)&g->bios.data[offset]; break; case TOKEN_ID_CLOCK_PTRS: g->bios.clock_token = (struct bit_token *)&g->bios.data[offset]; break; case TOKEN_ID_VIRT_PTRS: g->bios.virt_token = (struct bit_token *)&g->bios.data[offset]; break; default: break; } offset += bit.token_size; } gk20a_dbg_fn("done"); } static void upload_code(struct gk20a *g, u32 dst, u8 *src, u32 size, u8 port, bool sec) { u32 i, words; u32 *src_u32 = (u32 *)src; u32 blk; u32 tag = 0; gk20a_dbg_info("upload %d bytes to %x", size, dst); words = size >> 2; blk = dst >> 8; tag = blk; gk20a_dbg_info("upload %d words to %x block %d", words, dst, blk); gk20a_writel(g, pwr_falcon_imemc_r(port), pwr_falcon_imemc_offs_f(dst >> 2) | pwr_falcon_imemc_blk_f(blk) | pwr_falcon_imemc_aincw_f(1) | sec << 28); for (i = 0; i < words; i++) { if (i % 64 == 0) { gk20a_writel(g, 0x10a188, tag); tag++; } gk20a_writel(g, pwr_falcon_imemd_r(port), src_u32[i]); } while (i % 64) { gk20a_writel(g, pwr_falcon_imemd_r(port), 0); i++; } } static void upload_data(struct gk20a *g, u32 dst, u8 *src, u32 size, u8 port) { u32 i, words; u32 *src_u32 = (u32 *)src; u32 blk; gk20a_dbg_info("upload %d bytes to %x", size, dst); words = DIV_ROUND_UP(size, 4); blk = dst >> 8; gk20a_dbg_info("upload %d words to %x blk %d", words, dst, blk); gk20a_writel(g, pwr_falcon_dmemc_r(port), pwr_falcon_dmemc_offs_f(dst >> 2) | pwr_falcon_dmemc_blk_f(blk) | pwr_falcon_dmemc_aincw_f(1)); for (i = 0; i < words; i++) gk20a_writel(g, pwr_falcon_dmemd_r(port), src_u32[i]); } static int gm206_bios_devinit(struct gk20a *g) { int retries = PMU_BOOT_TIMEOUT_MAX / PMU_BOOT_TIMEOUT_DEFAULT; int err = 0; int val; gk20a_dbg_fn(""); g->ops.pmu.reset(g); do { u32 w = gk20a_readl(g, pwr_falcon_dmactl_r()) & (pwr_falcon_dmactl_dmem_scrubbing_m() | pwr_falcon_dmactl_imem_scrubbing_m()); if (!w) { gk20a_dbg_fn("done"); break; } udelay(PMU_BOOT_TIMEOUT_DEFAULT); } while (--retries || !tegra_platform_is_silicon()); /* todo check retries */ upload_code(g, g->bios.devinit.bootloader_phys_base, g->bios.devinit.bootloader, g->bios.devinit.bootloader_size, 0, 0); upload_code(g, g->bios.devinit.phys_base, g->bios.devinit.ucode, g->bios.devinit.size, 0, 1); upload_data(g, g->bios.devinit.dmem_phys_base, g->bios.devinit.dmem, g->bios.devinit.dmem_size, 0); upload_data(g, g->bios.devinit_tables_phys_base, g->bios.devinit_tables, g->bios.devinit_tables_size, 0); upload_data(g, g->bios.devinit_script_phys_base, g->bios.bootscripts, g->bios.bootscripts_size, 0); gk20a_writel(g, pwr_falcon_bootvec_r(), pwr_falcon_bootvec_vec_f(g->bios.devinit.code_entry_point)); gk20a_writel(g, pwr_falcon_dmactl_r(), pwr_falcon_dmactl_require_ctx_f(0)); gk20a_writel(g, pwr_falcon_cpuctl_r(), pwr_falcon_cpuctl_startcpu_f(1)); retries = PMU_BOOT_TIMEOUT_MAX / PMU_BOOT_TIMEOUT_DEFAULT; do { val = top_scratch1_devinit_completed_v( gk20a_readl(g, top_scratch1_r())); udelay(PMU_BOOT_TIMEOUT_DEFAULT); } while (!val && retries--); gk20a_writel(g, pwr_falcon_irqsclr_r(), pwr_falcon_irqstat_halt_true_f()); gk20a_readl(g, pwr_falcon_irqsclr_r()); if (!retries) err = -EINVAL; gk20a_dbg_fn("done"); return err; } static int gm206_bios_preos(struct gk20a *g) { int retries = PMU_BOOT_TIMEOUT_MAX / PMU_BOOT_TIMEOUT_DEFAULT; int err = 0; int val; gk20a_dbg_fn(""); g->ops.pmu.reset(g); do { u32 w = gk20a_readl(g, pwr_falcon_dmactl_r()) & (pwr_falcon_dmactl_dmem_scrubbing_m() | pwr_falcon_dmactl_imem_scrubbing_m()); if (!w) { gk20a_dbg_fn("done"); break; } udelay(PMU_BOOT_TIMEOUT_DEFAULT); } while (--retries || !tegra_platform_is_silicon()); /* todo check retries */ upload_code(g, g->bios.preos.bootloader_phys_base, g->bios.preos.bootloader, g->bios.preos.bootloader_size, 0, 0); upload_code(g, g->bios.preos.phys_base, g->bios.preos.ucode, g->bios.preos.size, 0, 1); upload_data(g, g->bios.preos.dmem_phys_base, g->bios.preos.dmem, g->bios.preos.dmem_size, 0); gk20a_writel(g, pwr_falcon_bootvec_r(), pwr_falcon_bootvec_vec_f(g->bios.preos.code_entry_point)); gk20a_writel(g, pwr_falcon_dmactl_r(), pwr_falcon_dmactl_require_ctx_f(0)); gk20a_writel(g, pwr_falcon_cpuctl_r(), pwr_falcon_cpuctl_startcpu_f(1)); retries = PMU_BOOT_TIMEOUT_MAX / PMU_BOOT_TIMEOUT_DEFAULT; do { val = pwr_falcon_cpuctl_halt_intr_v( gk20a_readl(g, pwr_falcon_cpuctl_r())); udelay(PMU_BOOT_TIMEOUT_DEFAULT); } while (!val && retries--); gk20a_writel(g, pwr_falcon_irqsclr_r(), pwr_falcon_irqstat_halt_true_f()); gk20a_readl(g, pwr_falcon_irqsclr_r()); if (!retries) err = -EINVAL; gk20a_dbg_fn("done"); return err; } static int gm206_bios_init(struct gk20a *g) { int i; struct gk20a_platform *platform = dev_get_drvdata(g->dev); struct dentry *d; const struct firmware *bios_fw; int err; bool found = 0; struct pci_dev *pdev = to_pci_dev(g->dev); char rom_name[sizeof(BIOS_OVERLAY_NAME_FORMATTED)]; gk20a_dbg_fn(""); snprintf(rom_name, sizeof(rom_name), BIOS_OVERLAY_NAME, pdev->device); gk20a_dbg_info("checking for VBIOS overlay %s", rom_name); bios_fw = nvgpu_request_firmware(g, rom_name, NVGPU_REQUEST_FIRMWARE_NO_WARN | NVGPU_REQUEST_FIRMWARE_NO_SOC); if (bios_fw) { gk20a_dbg_info("using VBIOS overlay"); g->bios.size = bios_fw->size - ROM_FILE_PAYLOAD_OFFSET; g->bios.data = vmalloc(g->bios.size); if (!g->bios.data) return -ENOMEM; memcpy(g->bios.data, &bios_fw->data[ROM_FILE_PAYLOAD_OFFSET], g->bios.size); release_firmware(bios_fw); } else { gk20a_dbg_info("reading bios from EEPROM"); g->bios.size = BIOS_SIZE; g->bios.data = vmalloc(BIOS_SIZE); if (!g->bios.data) return -ENOMEM; gk20a_writel(g, NV_PCFG + xve_rom_ctrl_r(), xve_rom_ctrl_rom_shadow_disabled_f()); for (i = 0; i < g->bios.size/4; i++) { u32 val = be32_to_cpu(gk20a_readl(g, 0x300000 + i*4)); g->bios.data[(i*4)] = (val >> 24) & 0xff; g->bios.data[(i*4)+1] = (val >> 16) & 0xff; g->bios.data[(i*4)+2] = (val >> 8) & 0xff; g->bios.data[(i*4)+3] = val & 0xff; } gk20a_writel(g, NV_PCFG + xve_rom_ctrl_r(), xve_rom_ctrl_rom_shadow_enabled_f()); } err = gm206_bios_parse_rom(g); if (err) return err; gk20a_dbg_info("read bios"); for (i = 0; i < g->bios.size; i++) { if (gm206_bios_rdu16(g, i) == BIT_HEADER_ID && gm206_bios_rdu32(g, i+2) == BIT_HEADER_SIGNATURE) { gm206_bios_parse_bit(g, i); found = true; } } if (!found) { gk20a_err(g->dev, "no valid VBIOS found"); return -EINVAL; } g->bios_blob.data = g->bios.data; g->bios_blob.size = g->bios.size; d = debugfs_create_blob("bios", S_IRUGO, platform->debugfs, &g->bios_blob); if (!d) gk20a_err(g->dev, "No debugfs?"); gk20a_dbg_fn("done"); err = gm206_bios_devinit(g); if (err) { gk20a_err(g->dev, "devinit failed"); return err; } err = gm206_bios_preos(g); if (err) { gk20a_err(g->dev, "pre-os failed"); return err; } return 0; } void gm206_init_bios(struct gpu_ops *gops) { gops->bios.init = gm206_bios_init; gops->bios.get_perf_table_ptrs = gm206_bios_get_perf_table_ptrs; }