blob: 97b6e2d4f7cf978c4dda6b75af2f200a7fac436c [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
#include <assert.h>
#include <arch/mmu.h>
#include <cbfs.h>
#include <cbmem.h>
#include <commonlib/bsd/mem_chip_info.h>
#include <console/cbmem_console.h>
#include <console/console.h>
#include <fmap.h>
#include <mrc_cache.h>
#include <reset.h>
#include <security/vboot/misc.h>
#include <soc/mmu.h>
#include <soc/mmu_common.h>
#include <soc/qclib_common.h>
#include <soc/symbols_common.h>
#include <string.h>
#include <vb2_api.h>
#define QCLIB_VERSION 0
/* store QcLib return data until CBMEM_CREATION_HOOK runs */
static struct mem_chip_info *mem_chip_info;
static void write_mem_chip_information(struct qclib_cb_if_table_entry *te)
{
struct mem_chip_info *info = (void *)te->blob_address;
if (te->size > sizeof(struct mem_chip_info) &&
te->size == mem_chip_info_size(info->num_entries)) {
/* Save mem_chip_info in global variable ahead of hook running */
mem_chip_info = info;
}
}
static void add_mem_chip_info(int unused)
{
void *mem_region_base = NULL;
size_t size;
if (!mem_chip_info || !mem_chip_info->num_entries ||
mem_chip_info->struct_version != MEM_CHIP_STRUCT_VERSION) {
printk(BIOS_ERR, "Did not receive valid mem_chip_info from QcLib!\n");
return;
}
size = mem_chip_info_size(mem_chip_info->num_entries);
/* Add cbmem table */
mem_region_base = cbmem_add(CBMEM_ID_MEM_CHIP_INFO, size);
ASSERT(mem_region_base != NULL);
/* Migrate the data into CBMEM */
memcpy(mem_region_base, mem_chip_info, size);
}
CBMEM_CREATION_HOOK(add_mem_chip_info);
struct qclib_cb_if_table qclib_cb_if_table = {
.magic = QCLIB_MAGIC_NUMBER,
.version = QCLIB_INTERFACE_VERSION,
.num_entries = 0,
.max_entries = QCLIB_MAX_NUMBER_OF_ENTRIES,
.global_attributes = 0,
.reserved = 0,
};
const char *qclib_file_default(enum qclib_cbfs_file file)
{
switch (file) {
case QCLIB_CBFS_PMICCFG:
return CONFIG_CBFS_PREFIX "/pmiccfg";
case QCLIB_CBFS_QCSDI:
return CONFIG_CBFS_PREFIX "/qcsdi";
case QCLIB_CBFS_QCLIB:
return CONFIG_CBFS_PREFIX "/qclib";
case QCLIB_CBFS_DCB:
return CONFIG_CBFS_PREFIX "/dcb";
default:
die("unknown QcLib file %d", file);
}
}
const char *qclib_file(enum qclib_cbfs_file file)
__attribute__((weak, alias("qclib_file_default")));
void qclib_add_if_table_entry(const char *name, void *base,
uint32_t size, uint32_t attrs)
{
struct qclib_cb_if_table_entry *te =
&qclib_cb_if_table.te[qclib_cb_if_table.num_entries++];
assert(qclib_cb_if_table.num_entries <= qclib_cb_if_table.max_entries);
strncpy(te->name, name, sizeof(te->name) - 1);
te->blob_address = (uintptr_t)base;
te->size = size;
te->blob_attributes = attrs;
}
static void write_ddr_information(struct qclib_cb_if_table_entry *te)
{
uint64_t ddr_size;
/* Save DDR info in SRAM region to share with ramstage */
ddr_region->offset = te->blob_address;
ddr_size = te->size;
ddr_region->size = ddr_size * MiB;
/* Use DDR info to configure MMU */
qc_mmu_dram_config_post_dram_init(
(void *)(uintptr_t)region_offset(ddr_region), region_sz(ddr_region));
}
static void write_qclib_log_to_cbmemc(struct qclib_cb_if_table_entry *te)
{
int i;
char *ptr = (char *)te->blob_address;
for (i = 0; i < te->size; i++) {
char c = *ptr++;
if (c != '\r')
__cbmemc_tx_byte(c);
}
}
static void write_table_entry(struct qclib_cb_if_table_entry *te)
{
if (!strncmp(QCLIB_TE_DDR_INFORMATION, te->name,
sizeof(te->name))) {
write_ddr_information(te);
} else if (!strncmp(QCLIB_TE_DDR_TRAINING_DATA, te->name,
sizeof(te->name))) {
assert(!mrc_cache_stash_data(MRC_TRAINING_DATA, QCLIB_VERSION,
(const void *)te->blob_address, te->size));
} else if (!strncmp(QCLIB_TE_LIMITS_CFG_DATA, te->name,
sizeof(te->name))) {
assert(fmap_overwrite_area(QCLIB_FR_LIMITS_CFG_DATA,
(const void *)te->blob_address, te->size));
} else if (!strncmp(QCLIB_TE_QCLIB_LOG_BUFFER, te->name,
sizeof(te->name))) {
write_qclib_log_to_cbmemc(te);
} else if (!strncmp(QCLIB_TE_MEM_CHIP_INFO, te->name,
sizeof(te->name))) {
write_mem_chip_information(te);
} else {
printk(BIOS_WARNING, "%s write not implemented\n", te->name);
printk(BIOS_WARNING, " blob_address[%llx]..size[%x]\n",
te->blob_address, te->size);
}
}
static void dump_te_table(void)
{
struct qclib_cb_if_table_entry *te;
int i;
for (i = 0; i < qclib_cb_if_table.num_entries; i++) {
te = &qclib_cb_if_table.te[i];
printk(BIOS_DEBUG, "[%s][%llx][%x][%x]\n",
te->name, te->blob_address,
te->size, te->blob_attributes);
}
}
__weak int qclib_soc_override(struct qclib_cb_if_table *table) { return 0; }
void qclib_load_and_run(void)
{
int i;
ssize_t data_size;
struct mmu_context pre_qclib_mmu_context;
/* zero ddr_information SRAM region, needs new data each boot */
memset(ddr_region, 0, sizeof(struct region));
/* output area, QCLib copies console log buffer out */
if (CONFIG(CONSOLE_CBMEM))
qclib_add_if_table_entry(QCLIB_TE_QCLIB_LOG_BUFFER,
_qclib_serial_log,
REGION_SIZE(qclib_serial_log), 0);
/* output area, QCLib fills in DDR details */
qclib_add_if_table_entry(QCLIB_TE_DDR_INFORMATION, NULL, 0, 0);
/* Attempt to load DDR Training Blob */
data_size = mrc_cache_load_current(MRC_TRAINING_DATA, QCLIB_VERSION,
_ddr_training, REGION_SIZE(ddr_training));
if (data_size < 0) {
printk(BIOS_ERR, "Unable to load previous training data.\n");
memset(_ddr_training, 0, REGION_SIZE(ddr_training));
}
qclib_add_if_table_entry(QCLIB_TE_DDR_TRAINING_DATA,
_ddr_training, REGION_SIZE(ddr_training), 0);
/* Address and size of this entry will be filled in by QcLib. */
qclib_add_if_table_entry(QCLIB_TE_MEM_CHIP_INFO, NULL, 0, 0);
/* Attempt to load PMICCFG Blob */
data_size = cbfs_load(qclib_file(QCLIB_CBFS_PMICCFG),
_pmic, REGION_SIZE(pmic));
if (!data_size) {
printk(BIOS_ERR, "[%s] /pmiccfg failed\n", __func__);
goto fail;
}
qclib_add_if_table_entry(QCLIB_TE_PMIC_SETTINGS, _pmic, data_size, 0);
/* Attempt to load DCB Blob */
data_size = cbfs_load(qclib_file(QCLIB_CBFS_DCB),
_dcb, REGION_SIZE(dcb));
if (!data_size) {
printk(BIOS_ERR, "[%s] /dcb failed\n", __func__);
goto fail;
}
qclib_add_if_table_entry(QCLIB_TE_DCB_SETTINGS, _dcb, data_size, 0);
/* Enable QCLib serial output, based on Kconfig */
if (CONFIG(CONSOLE_SERIAL))
qclib_cb_if_table.global_attributes =
QCLIB_GA_ENABLE_UART_LOGGING;
if (CONFIG(QC_SDI_ENABLE) && (!CONFIG(VBOOT) ||
!vboot_is_gbb_flag_set(VB2_GBB_FLAG_RUNNING_FAFT))) {
struct prog qcsdi =
PROG_INIT(PROG_REFCODE,
qclib_file(QCLIB_CBFS_QCSDI));
/* Attempt to load QCSDI elf */
if (cbfs_prog_stage_load(&qcsdi))
goto fail;
qclib_add_if_table_entry(QCLIB_TE_QCSDI,
prog_entry(&qcsdi), prog_size(&qcsdi), 0);
printk(BIOS_INFO, "qcsdi.entry[%p]\n", qcsdi.entry);
}
/* hook for SoC specific binary blob loads */
if (qclib_soc_override(&qclib_cb_if_table)) {
printk(BIOS_ERR, "qclib_soc_override failed\n");
goto fail;
}
dump_te_table();
/* Attempt to load QCLib elf */
struct prog qclib =
PROG_INIT(PROG_REFCODE, qclib_file(QCLIB_CBFS_QCLIB));
if (cbfs_prog_stage_load(&qclib))
goto fail;
prog_set_entry(&qclib, prog_entry(&qclib), &qclib_cb_if_table);
printk(BIOS_DEBUG, "\n\n\nQCLib is about to Initialize DDR\n");
printk(BIOS_DEBUG, "Global Attributes[%#x]..Table Entries Count[%d]\n",
qclib_cb_if_table.global_attributes,
qclib_cb_if_table.num_entries);
printk(BIOS_DEBUG, "Jumping to QCLib code at %p(%p)\n",
prog_entry(&qclib), prog_entry_arg(&qclib));
/* back-up mmu context before disabling mmu and executing qclib */
mmu_save_context(&pre_qclib_mmu_context);
/* disable mmu before jumping to qclib. mmu_disable also
flushes and invalidates caches before disabling mmu. */
mmu_disable();
prog_run(&qclib);
/* Before returning, QCLib flushes cache and disables mmu.
Explicitly disable mmu (flush, invalidate and disable mmu)
before re-enabling mmu with backed-up mmu context */
mmu_disable();
mmu_restore_context(&pre_qclib_mmu_context);
mmu_enable();
if (qclib_cb_if_table.global_attributes & QCLIB_GA_FORCE_COLD_REBOOT) {
printk(BIOS_NOTICE, "QcLib requested cold reboot\n");
board_reset();
}
/* step through I/F table, handling return values */
for (i = 0; i < qclib_cb_if_table.num_entries; i++)
if (qclib_cb_if_table.te[i].blob_attributes &
QCLIB_BA_SAVE_TO_STORAGE)
write_table_entry(&qclib_cb_if_table.te[i]);
/* confirm that we received valid ddr information from QCLib */
assert((uintptr_t)_dram == region_offset(ddr_region) &&
region_sz(ddr_region) >= (u8 *)cbmem_top() - _dram);
printk(BIOS_DEBUG, "QCLib completed\n\n\n");
return;
fail:
die("Couldn't run QCLib.\n");
}