diff --git a/src/mainboard/nintendo/switch/mainboard.c b/src/mainboard/nintendo/switch/mainboard.c index 3be1a3a137..e1d6fa81f0 100644 --- a/src/mainboard/nintendo/switch/mainboard.c +++ b/src/mainboard/nintendo/switch/mainboard.c @@ -220,3 +220,8 @@ struct chip_operations mainboard_ops = { .name = "switch", .enable_dev = mainboard_enable, }; + +void lb_board(struct lb_header *header) +{ + soc_add_mtc(header); +} diff --git a/src/mainboard/nintendo/switch/sdram_configs.c b/src/mainboard/nintendo/switch/sdram_configs.c index 4d4b9591c3..68c3db508d 100644 --- a/src/mainboard/nintendo/switch/sdram_configs.c +++ b/src/mainboard/nintendo/switch/sdram_configs.c @@ -30,7 +30,7 @@ static const struct sdram_params sdram_configs[] = { #define FUSE_BASE ((void *)TEGRA_FUSE_BASE) #define FUSE_RESERVED_ODM4 0x1d8 -static uint32_t switch_sdram_get_id(void) +uint32_t switch_sdram_get_id(void) { return (read32(FUSE_BASE + FUSE_RESERVED_ODM4) & 0x38) >> 3; } @@ -48,3 +48,8 @@ const struct sdram_params *get_sdram_config() return &sdram_configs[id]; } + +uint32_t ram_code(void) +{ + return switch_sdram_get_id(); +} diff --git a/src/soc/nvidia/tegra210/Kconfig b/src/soc/nvidia/tegra210/Kconfig index 8789570235..fe8991068e 100644 --- a/src/soc/nvidia/tegra210/Kconfig +++ b/src/soc/nvidia/tegra210/Kconfig @@ -142,6 +142,24 @@ config MTC_ADDRESS The DRAM location where MTC firmware to be loaded in. This location needs to be consistent with the location defined in tegra_mtc.ld +config HAVE_MTC_TABLES + bool "Add external Memory controller Training Code tables" + default n + depends on USE_BLOBS + help + Select this option to add external emc training tables + +if HAVE_MTC_TABLES + +config MTC_TABLES_DIRECTORY + string "Directory where MTC table files are located" + default "mtc_tables" + help + Path to directory where MTC tables files are located. They should be + named tegra_mtc_table_.bin. + +endif # HAVE_MTC_TABLES + endif # HAVE_MTC endif # SOC_NVIDIA_TEGRA210 diff --git a/src/soc/nvidia/tegra210/Makefile.inc b/src/soc/nvidia/tegra210/Makefile.inc index 8eb097764c..81a47d25a6 100644 --- a/src/soc/nvidia/tegra210/Makefile.inc +++ b/src/soc/nvidia/tegra210/Makefile.inc @@ -162,4 +162,18 @@ cbfs-files-$(CONFIG_HAVE_MTC) += $(MTC_FILE_CBFS) $(MTC_FILE_CBFS)-file := $(MTC_FILE) $(MTC_FILE_CBFS)-type := raw +ifeq ($(CONFIG_HAVE_MTC_TABLES),y) + MTC_TABLES_DIRECTORY = $(call strip_quotes,$(CONFIG_MTC_TABLES_DIRECTORY)) + + define MTC_CBFS_template = + cbfs-files-y += $(1) + $(1)-file := $(MTC_TABLES_DIRECTORY)/$(1) + $(1)-type := raw + endef + + MTC_TABLE_NAMES = $(notdir $(wildcard $(MTC_TABLES_DIRECTORY)/tegra_mtc_table_*.bin)) + + $(foreach table,$(MTC_TABLE_NAMES),$(eval $(call MTC_CBFS_template,$(table)))) +endif + endif diff --git a/src/soc/nvidia/tegra210/include/soc/mtc.h b/src/soc/nvidia/tegra210/include/soc/mtc.h index ca369ad3cf..0dd376d077 100644 --- a/src/soc/nvidia/tegra210/include/soc/mtc.h +++ b/src/soc/nvidia/tegra210/include/soc/mtc.h @@ -20,6 +20,89 @@ #if IS_ENABLED(CONFIG_HAVE_MTC) +typedef struct { + unsigned int rev; + char dvfs_ver[60]; + unsigned int rate; + unsigned int min_volt; + unsigned int gpu_min_volt; + char clock_src[32]; + unsigned int clk_src_emc; + unsigned int needs_training; + unsigned int training_pattern; + unsigned int trained; + unsigned int periodic_training; + unsigned int trained_dram_clktree_c0d0u0; + unsigned int trained_dram_clktree_c0d0u1; + unsigned int trained_dram_clktree_c0d1u0; + unsigned int trained_dram_clktree_c0d1u1; + unsigned int trained_dram_clktree_c1d0u0; + unsigned int trained_dram_clktree_c1d0u1; + unsigned int trained_dram_clktree_c1d1u0; + unsigned int trained_dram_clktree_c1d1u1; + unsigned int current_dram_clktree_c0d0u0; + unsigned int current_dram_clktree_c0d0u1; + unsigned int current_dram_clktree_c0d1u0; + unsigned int current_dram_clktree_c0d1u1; + unsigned int current_dram_clktree_c1d0u0; + unsigned int current_dram_clktree_c1d0u1; + unsigned int current_dram_clktree_c1d1u0; + unsigned int current_dram_clktree_c1d1u1; + unsigned int run_clocks; + unsigned int tree_margin; + unsigned int num_burst; + unsigned int num_burst_per_ch; + unsigned int num_trim; + unsigned int num_trim_per_ch; + unsigned int num_mc_regs; + unsigned int num_up_down; + unsigned int vref_num; + unsigned int training_mod_num; + unsigned int dram_timing_num; + + unsigned int burst_regs[221]; + unsigned int burst_reg_per_ch[8]; + unsigned int shadow_regs_ca_train[221]; + unsigned int shadow_regs_quse_train[221]; + unsigned int shadow_regs_rdwr_train[221]; + unsigned int trim_regs[138]; + unsigned int trim_perch_regs[10]; + + /* Vref regs, impacted by training. */ + unsigned int vref_perch_regs[4]; + + /* DRAM timing parameters are required in some calculations. */ + unsigned int dram_timings[5]; + unsigned int training_mod_regs[20]; + unsigned int save_restore_mod_regs[12]; + unsigned int burst_mc_regs[33]; + unsigned int la_scale_regs[24]; + + unsigned int min_mrs_wait; + unsigned int emc_mrw; + unsigned int emc_mrw2; + unsigned int emc_mrw3; + unsigned int emc_mrw4; + unsigned int emc_mrw9; + unsigned int emc_mrs; + unsigned int emc_emrs; + unsigned int emc_emrs2; + unsigned int emc_auto_cal_config; + unsigned int emc_auto_cal_config2; + unsigned int emc_auto_cal_config3; + unsigned int emc_auto_cal_config4; + unsigned int emc_auto_cal_config5; + unsigned int emc_auto_cal_config6; + unsigned int emc_auto_cal_config7; + unsigned int emc_auto_cal_config8; + unsigned int emc_cfg_2; + unsigned int emc_sel_dpd_ctrl; + unsigned int emc_fdpd_ctrl_cmd_no_ramp; + unsigned int dll_clk_src; + unsigned int clk_out_enb_x_0_clk_enb_emc_dll; + unsigned int latency; +} tegra210_emc_table; + int tegra210_run_mtc(void); void soc_add_mtc(struct lb_header *header); diff --git a/src/soc/nvidia/tegra210/mtc.c b/src/soc/nvidia/tegra210/mtc.c index f71e5e8f85..7cbd0f4049 100644 --- a/src/soc/nvidia/tegra210/mtc.c +++ b/src/soc/nvidia/tegra210/mtc.c @@ -13,10 +13,14 @@ * GNU General Public License for more details. */ +#include #include #include #include #include +#include +#include +#include #include static size_t mtc_table_size; @@ -25,6 +29,111 @@ static size_t mtc_table_size; #define MTC_TABLE_ENTRY_SIZE 4880 #define MTC_TABLE_MAX_SIZE (MAX_MTC_TABLE_ENTRIES * MTC_TABLE_ENTRY_SIZE) +#define TRAIN_FUNC 0x5100 + +#define OP_SWITCH 0 +#define OP_TRAIN 1 + +static int train_all(void *mtc) +{ + void * const mtc_entry = (void *)(((uintptr_t)mtc) + TRAIN_FUNC); + int (*train_one)(int z, unsigned int to, unsigned int from, + void *table, int count, int mode) = (void *)(mtc_entry); + char filename[32]; + int entries; + struct region_device fh; + struct cbfsf mtc_file; + int ret = 0; + + snprintf(filename, sizeof(filename), "tegra_mtc_table_%d.bin", ram_code()); + + if (cbfs_boot_locate(&mtc_file, filename, NULL)) { + printk(BIOS_ERR, "MTC: Table file not found: %s\n", filename); + return -1; + } + + cbfs_file_data(&fh, &mtc_file); + mtc_table_size = region_device_sz(&fh); + + entries = mtc_table_size / sizeof(tegra210_emc_table); + + printk(BIOS_ERR, "MTC: Using MTC table %s (size: 0x%lx / %d entries)\n", + filename, mtc_table_size, entries); + + void *cbmem_tab = cbmem_add(CBMEM_ID_MTC, mtc_table_size); + if (cbmem_tab == NULL) { + printk(BIOS_ERR, "MTC table allocation in cbmem failed!\n"); + return -1; + } + + printk(BIOS_INFO, "MTC: table is at %p\n", cbmem_tab); + printk(BIOS_INFO, "MTC: entry is at %p\n", train_one); + + ssize_t nread = rdev_readat(&fh, cbmem_tab, 0, mtc_table_size); + if (nread != region_device_sz(&fh)) { + printk(BIOS_ERR, "MTC bytes read (%zu) != file length(%zu)!\n", + nread, region_device_sz(&fh)); + goto cleanup; + } + + tegra210_emc_table *table = cbmem_tab; + + int boot_index = 0; + u32 reg = read32(CLK_RST_REG(clk_src_emc)); + printk(BIOS_INFO, "MTC: clk_src_emc=0x%08x\n", reg); + for (boot_index = 0; boot_index < entries; boot_index++) { + if (reg == table[boot_index].clk_src_emc) + break; + } + + if (boot_index >= entries) { + printk(BIOS_ERR, "MTC: failed to find boot entry\n"); + goto cleanup; + } + + printk(BIOS_INFO, "MTC: booted using entry #%d (%d kHz): %s\n", boot_index, + table[boot_index].rate, table[boot_index].dvfs_ver); + + printk(BIOS_INFO, "MTC: running training\n"); + + for (int i = 0; i < entries; i++) { + printk(BIOS_INFO, "MTC: Training %d kHz -> %d kHz\n", + table[boot_index].rate, table[i].rate); + ret = train_one(0, table[i].rate, table[boot_index].rate, + table, entries, OP_TRAIN); + if (ret) { + printk(BIOS_ERR, "MTC: Training failed (%d)\n", ret); + goto cleanup; + } else { + printk(BIOS_ERR, "MTC: Training OK\n"); + } + } + + printk(BIOS_INFO, "MTC: increasing memory clocks\n"); + + for (int i = boot_index + 1; i < entries; i++) { + if (table[i].periodic_training) + break; + + printk(BIOS_INFO, "MTC: Switching %d kHz -> %d kHz\n", + table[i - 1].rate, table[i].rate); + ret = train_one(0, table[i].rate, table[i - 1].rate, + table, entries, OP_SWITCH); + if (ret) { + printk(BIOS_ERR, "MTC: Switch failed (%d)\n", ret); + goto cleanup; + } + } + + printk(BIOS_INFO, "MTC: successful\n"); + return 0; + +cleanup: + cbmem_entry_remove(cbmem_entry_find(CBMEM_ID_MTC)); + mtc_table_size = 0; + return -1; +} + int tegra210_run_mtc(void) { ssize_t nread; @@ -53,6 +162,7 @@ int tegra210_run_mtc(void) printk(BIOS_INFO, "MTC: %zu bytes loaded @ %p\n", nread, mtc); +#if !IS_ENABLED(CONFIG_HAVE_MTC_TABLES) mtc_table_size = (*mtc_fw)(&dvfs_table); if ((mtc_table_size == 0) || (mtc_table_size > MTC_TABLE_MAX_SIZE)) { @@ -72,6 +182,9 @@ int tegra210_run_mtc(void) memcpy(cbmem_tab, dvfs_table, mtc_table_size); printk(BIOS_INFO, "MTC: Copied 0x%zx bytes from %p to %p\n", mtc_table_size, dvfs_table, cbmem_tab); +#else + return train_all(mtc); +#endif return 0; } diff --git a/src/soc/nvidia/tegra210/ramstage.c b/src/soc/nvidia/tegra210/ramstage.c index 18fdded800..42f0191853 100644 --- a/src/soc/nvidia/tegra210/ramstage.c +++ b/src/soc/nvidia/tegra210/ramstage.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,9 @@ void ramstage_entry(void) clock_init_arm_generic_timer(); + /* Needed to store MTC data */ + cbmem_initialize(); + if (tegra210_run_mtc() != 0) printk(BIOS_ERR, "MTC: No training data.\n");