From fb64e1f1b9c2162f521dfe3956655accb911d6a7 Mon Sep 17 00:00:00 2001 From: Terje Bergstrom Date: Wed, 23 Mar 2016 08:43:53 -0700 Subject: 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 Reviewed-on: http://git-master/r/1120467 GVS: Gerrit_Virtual_Submit Reviewed-by: Ken Adams --- drivers/gpu/nvgpu/gm206/bios_gm206.c | 718 +++++++++++++++++++++++++++++++++++ 1 file changed, 718 insertions(+) create mode 100644 drivers/gpu/nvgpu/gm206/bios_gm206.c (limited to 'drivers/gpu/nvgpu/gm206/bios_gm206.c') 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 @@ +/* + * 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 "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" + +#define BIT_HEADER_ID 0xb8ff +#define BIT_HEADER_SIGNATURE 0x00544942 +#define BIOS_SIZE 0x40000 +#define NV_PCFG 0x88000 +#define PMU_BOOT_TIMEOUT_DEFAULT 100 /* usec */ +#define PMU_BOOT_TIMEOUT_MAX 2000000 /* usec */ + +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; + +struct bit_token { + u8 token_id; + u8 data_version; + u16 data_size; + u16 data_ptr; +} __packed; + +#define TOKEN_ID_NVINIT_PTRS 0x49 +#define TOKEN_ID_FALCON_DATA 0x70 + +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 void 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); + + 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; + } + } +} + +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_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_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; + 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 = GR_IDLE_CHECK_MAX / GR_IDLE_CHECK_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(GR_IDLE_CHECK_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; + int err; + + gk20a_dbg_fn(""); + g->bios.data = kzalloc(BIOS_SIZE, GFP_KERNEL); + if (!g->bios.data) + return -ENOMEM; + + gk20a_dbg_info("reading bios"); + gk20a_writel(g, NV_PCFG + xve_rom_ctrl_r(), + xve_rom_ctrl_rom_shadow_disabled_f()); + for (i = 0; i < 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()); + + gm206_bios_parse_rom(g); + gk20a_dbg_info("read bios"); + for (i = 0; i < 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); + } + } + + g->bios_blob.data = g->bios.data; + g->bios_blob.size = 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; +} -- cgit v1.2.2