/* * Copyright (c) 2018-2020 Atmosphère-NX * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "utils.h" #include "car.h" #include "timers.h" #include "di.h" #include "se.h" #include "fuse.h" #include "pmc.h" #include "mc.h" #include "sysreg.h" #include "tsec.h" #define I2S_BASE 0x702D1000 #define MAKE_I2S_REG(n) MAKE_REG32(I2S_BASE + n) static void setup_exception_vectors(void) { for (unsigned int i = 0; i < 0x20; i += 4) { MAKE_REG32(0x6000F200u + i) = (uint32_t)generic_panic; } } static void mbist_workaround(void) { volatile tegra_car_t *car = car_get_regs(); car->clk_source_sor1 = ((car->clk_source_sor1 | 0x8000) & 0xFFFFBFFF); car->plld_base |= 0x40800000u; car->rst_dev_y_clr = 0x40; car->rst_dev_x_clr = 0x40000; car->rst_dev_l_clr = 0x18000000; udelay(3); /* Setup I2S. */ MAKE_I2S_REG(0x0A0) |= 0x400; MAKE_I2S_REG(0x088) &= 0xFFFFFFFE; MAKE_I2S_REG(0x1A0) |= 0x400; MAKE_I2S_REG(0x188) &= 0xFFFFFFFE; MAKE_I2S_REG(0x2A0) |= 0x400; MAKE_I2S_REG(0x288) &= 0xFFFFFFFE; MAKE_I2S_REG(0x3A0) |= 0x400; MAKE_I2S_REG(0x388) &= 0xFFFFFFFE; MAKE_I2S_REG(0x4A0) |= 0x400; MAKE_I2S_REG(0x488) &= 0xFFFFFFFE; MAKE_DI_REG(DC_COM_DSC_TOP_CTL) |= 4; MAKE_VIC_REG(0x8C) = 0xFFFFFFFF; udelay(3); /* Set devices in reset. */ car->rst_dev_y_set = 0x40; car->rst_dev_l_set = 0x18000000; car->rst_dev_x_set = 0x40000; /* Clock out enables. */ car->clk_out_enb_h = 0xC0; car->clk_out_enb_l = 0x80000130; car->clk_out_enb_u = 0x1F00200; car->clk_out_enb_v = 0x80400808; car->clk_out_enb_w = 0x402000FC; car->clk_out_enb_x = 0x23000780; car->clk_out_enb_y = 0x300; /* LVL2 clock gate overrides. */ car->lvl2_clk_gate_ovra = 0; car->lvl2_clk_gate_ovrb = 0; car->lvl2_clk_gate_ovrc = 0; car->lvl2_clk_gate_ovrd = 0; car->lvl2_clk_gate_ovre = 0; /* Configure clock sources. */ car->plld_base &= 0x1F7FFFFF; car->clk_source_sor1 &= 0xFFFF3FFF; car->clk_source_vi = ((car->clk_source_vi & 0x1FFFFFFF) | 0x80000000); car->clk_source_host1x = ((car->clk_source_host1x & 0x1FFFFFFF) | 0x80000000); car->clk_source_nvenc = ((car->clk_source_nvenc & 0x1FFFFFFF) | 0x80000000); } static int tsec_dma_wait_idle() { volatile tegra_tsec_t *tsec = tsec_get_regs(); uint32_t timeout = (get_time_ms() + 10000); while (!(tsec->TSEC_FALCON_DMATRFCMD & 2)) { if (get_time_ms() > timeout) return 0; } return 1; } static int tsec_dma_phys_to_flcn(bool is_imem, uint32_t flcn_offset, uint32_t phys_offset) { volatile tegra_tsec_t *tsec = tsec_get_regs(); uint32_t cmd = 0; if (!is_imem) cmd = 0x600; else cmd = 0x10; tsec->TSEC_FALCON_DMATRFMOFFS = flcn_offset; tsec->TSEC_FALCON_DMATRFFBOFFS = phys_offset; tsec->TSEC_FALCON_DMATRFCMD = cmd; return tsec_dma_wait_idle(); } static int tsec_kfuse_wait_ready() { uint32_t timeout = (get_time_ms() + 10000); /* Wait for STATE_DONE. */ while (!(KFUSE_STATE & 0x10000)) { if (get_time_ms() > timeout) return 0; } /* Check for STATE_CRCPASS. */ if (!(KFUSE_STATE & 0x20000)) return 0; return 1; } int load_tsec_fw(void) { volatile uint32_t* tsec_fw = (volatile uint32_t*)0x40010F00; const uint32_t tsec_fw_length = MAKE_REG32(0x40010EFC); volatile tegra_tsec_t *tsec = tsec_get_regs(); /* Enable clocks. */ clkrst_reboot(CARDEVICE_HOST1X); clkrst_reboot(CARDEVICE_TSEC); clkrst_reboot(CARDEVICE_SOR_SAFE); clkrst_reboot(CARDEVICE_SOR0); clkrst_reboot(CARDEVICE_SOR1); clkrst_reboot(CARDEVICE_KFUSE); /* Make sure KFUSE is ready. */ if (!tsec_kfuse_wait_ready()) { /* Disable clocks. */ clkrst_disable(CARDEVICE_KFUSE); clkrst_disable(CARDEVICE_SOR1); clkrst_disable(CARDEVICE_SOR0); clkrst_disable(CARDEVICE_SOR_SAFE); clkrst_disable(CARDEVICE_TSEC); clkrst_disable(CARDEVICE_HOST1X); return -1; } /* Configure Falcon. */ tsec->TSEC_FALCON_DMACTL = 0; tsec->TSEC_FALCON_IRQMSET = 0xFFF2; tsec->TSEC_FALCON_IRQDEST = 0xFFF0; tsec->TSEC_FALCON_ITFEN = 3; /* Make sure the DMA block is idle. */ if (!tsec_dma_wait_idle()) { /* Disable clocks. */ clkrst_disable(CARDEVICE_KFUSE); clkrst_disable(CARDEVICE_SOR1); clkrst_disable(CARDEVICE_SOR0); clkrst_disable(CARDEVICE_SOR_SAFE); clkrst_disable(CARDEVICE_TSEC); clkrst_disable(CARDEVICE_HOST1X); return -2; } /* Load firmware. */ tsec->TSEC_FALCON_DMATRFBASE = (uint32_t)tsec_fw >> 8; for (uint32_t addr = 0; addr < tsec_fw_length; addr += 0x100) { if (!tsec_dma_phys_to_flcn(true, addr, addr)) { /* Disable clocks. */ clkrst_disable(CARDEVICE_KFUSE); clkrst_disable(CARDEVICE_SOR1); clkrst_disable(CARDEVICE_SOR0); clkrst_disable(CARDEVICE_SOR_SAFE); clkrst_disable(CARDEVICE_TSEC); clkrst_disable(CARDEVICE_HOST1X); return -3; } } /* Write magic value to HOST1X scratch register. */ MAKE_HOST1X_REG(0x3300) = 0x34C2E1DA; /* Execute firmware. */ tsec->TSEC_FALCON_MAILBOX1 = 0; tsec->TSEC_FALCON_MAILBOX0 = 1; tsec->TSEC_FALCON_BOOTVEC = 0; tsec->TSEC_FALCON_CPUCTL = 2; while (true) { /* Yield to Nintendo's TSEC firmware. */ } } int main(void) { /* Setup vectors */ setup_exception_vectors(); volatile tegra_pmc_t *pmc = pmc_get_regs(); volatile tegra_car_t *car = car_get_regs(); /* Clear the boot reason to avoid problems later */ pmc->scratch200 = 0; pmc->rst_status = 0; //AHB_AHB_SPARE_REG_0 &= 0xFFFFFF9F; //pmc->scratch49 = (((pmc->scratch49 >> 1) << 1) & 0xFFFFFFFD); /* Apply the memory built-in self test workaround. */ mbist_workaround(); /* Reboot SE. */ clkrst_reboot(CARDEVICE_SE); /* Initialize the fuse driver. */ fuse_init(); /* Don't bother checking SKU, fuses, or bootloader version. */ mc_enable_for_tsec(); /* 7.0.0 package1ldr holds I2C5 in reset, clears SYS clock. */ car->clk_source_sys = 0; rst_enable(CARDEVICE_I2C5); load_tsec_fw(); while (true) { } return 0; }