blob: 0c2c5aeede3a4db2e0b67347e0cf41f31499c9b1 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* GXP firmware data manager.
*
* Copyright (C) 2021 Google LLC
*/
#include <linux/slab.h>
#include "gxp-config.h"
#include "gxp-debug-dump.h"
#include "gxp-firmware-data.h"
#include "gxp-firmware.h" /* gxp_core_boot */
#include "gxp-host-device-structs.h"
#include "gxp-internal.h"
#include "gxp-vd.h"
#include "gxp.h"
/* A byte pattern to pre-populate the FW region with */
#define FW_DATA_DEBUG_PATTERN 0x66
/* Default application parameters */
#define DEFAULT_APP_ID 1
/*
* Holds information about system-wide HW and memory resources given to the FWs
* of GXP devices.
*/
struct gxp_fw_data_manager {
/* Cached core telemetry descriptors. */
struct gxp_core_telemetry_descriptor core_telemetry_desc;
/*
* A host-view of the System configuration descriptor. This same desc
* is provided to all VDs and all cores. This is the R/O section.
*/
struct gxp_system_descriptor_ro *sys_desc_ro;
/*
* A host-view of the System configuration descriptor. This same desc
* is provided to all VDs and all cores. This is the R/W section.
*/
struct gxp_system_descriptor_rw *sys_desc_rw;
};
/*
* Here assumes sys_cfg contains gxp_system_descriptor_ro in the first page and
* gxp_system_descriptor_rw in the second page.
*/
static void set_system_cfg_region(struct gxp_dev *gxp, void *sys_cfg)
{
struct gxp_system_descriptor_ro *des_ro = sys_cfg;
struct gxp_system_descriptor_rw *des_rw = sys_cfg + PAGE_SIZE;
struct gxp_core_telemetry_descriptor *descriptor =
&gxp->data_mgr->core_telemetry_desc;
struct telemetry_descriptor_ro *tel_ro;
struct telemetry_descriptor_rw *tel_rw;
struct core_telemetry_descriptor *tel_des;
int i;
if (gxp->debug_dump_mgr)
des_ro->debug_dump_dev_addr = gxp->debug_dump_mgr->buf.dsp_addr;
else
des_ro->debug_dump_dev_addr = 0;
#define COPY_FIELDS(des, ro, rw) \
do { \
ro->host_status = des->host_status; \
ro->buffer_addr = des->buffer_addr; \
ro->buffer_size = des->buffer_size; \
rw->device_status = des->device_status; \
rw->data_available = des->watermark_level; \
} while (0)
for (i = 0; i < GXP_NUM_CORES; i++) {
tel_ro = &des_ro->telemetry_desc.per_core_loggers[i];
tel_rw = &des_rw->telemetry_desc.per_core_loggers[i];
tel_des = &descriptor->per_core_loggers[i];
COPY_FIELDS(tel_des, tel_ro, tel_rw);
tel_ro = &des_ro->telemetry_desc.per_core_tracers[i];
tel_rw = &des_rw->telemetry_desc.per_core_tracers[i];
tel_des = &descriptor->per_core_tracers[i];
COPY_FIELDS(tel_des, tel_ro, tel_rw);
}
#undef COPY_FIELDS
/* Update the global descriptors. */
gxp->data_mgr->sys_desc_ro = des_ro;
gxp->data_mgr->sys_desc_rw = des_rw;
}
static void _gxp_fw_data_populate_vd_cfg(struct gxp_dev *gxp,
struct gxp_virtual_device *vd)
{
struct gxp_host_control_region *core_cfg;
struct gxp_job_descriptor job;
struct gxp_vd_descriptor *vd_desc;
int i;
if (!gxp_core_boot(gxp))
return;
if (!vd->vd_cfg.vaddr || !vd->core_cfg.vaddr) {
dev_warn(
gxp->dev,
"Missing VD and core CFG in image config, firmware is not bootable\n");
return;
}
/* Set up VD config region. */
vd_desc = vd->vd_cfg.vaddr;
vd_desc->application_id = DEFAULT_APP_ID;
vd_desc->vd_is_initialized = 0;
/* Set up core config region. */
job.workers_count = vd->num_cores;
for (i = 0; i < ARRAY_SIZE(job.worker_to_fw); i++) {
/*
* Kernel-initiated workloads always act like the entire VD is
* one giant N-core job where N is the number of cores allocated
* to that VD.
* The MCU, on the other hand, can have multiple jobs dispatched
* to the same VD at the same time.
*/
if (i < job.workers_count)
job.worker_to_fw[i] = i;
else
job.worker_to_fw[i] = -1;
}
/* Give each VD a unique HW resources slot. */
job.hardware_resources_slot = gxp_vd_hw_slot_id(vd);
/* Assign the same job descriptor to all cores in this VD */
for (i = 0; i < GXP_NUM_CORES; i++) {
core_cfg = vd->core_cfg.vaddr +
vd->core_cfg.size / GXP_NUM_CORES * i;
core_cfg->job_descriptor = job;
}
}
static struct core_telemetry_descriptor *
gxp_fw_data_get_core_telemetry_descriptor(struct gxp_dev *gxp, u8 type)
{
struct gxp_core_telemetry_descriptor *descriptor =
&gxp->data_mgr->core_telemetry_desc;
if (type == GXP_TELEMETRY_TYPE_LOGGING)
return descriptor->per_core_loggers;
else if (type == GXP_TELEMETRY_TYPE_TRACING)
return descriptor->per_core_tracers;
else
return ERR_PTR(-EINVAL);
}
int gxp_fw_data_init(struct gxp_dev *gxp)
{
struct gxp_fw_data_manager *mgr;
void *virt;
mgr = devm_kzalloc(gxp->dev, sizeof(*mgr), GFP_KERNEL);
if (!mgr)
return -ENOMEM;
virt = memremap(gxp->fwdatabuf.paddr, gxp->fwdatabuf.size, MEMREMAP_WC);
if (IS_ERR_OR_NULL(virt)) {
dev_err(gxp->dev, "Failed to map fw data region\n");
return -ENODEV;
}
gxp->fwdatabuf.vaddr = virt;
/* Populate the region with a pre-defined pattern. */
memset(virt, FW_DATA_DEBUG_PATTERN, gxp->fwdatabuf.size);
gxp->data_mgr = mgr;
return 0;
}
void gxp_fw_data_destroy(struct gxp_dev *gxp)
{
struct gxp_fw_data_manager *mgr = gxp->data_mgr;
if (gxp->fwdatabuf.vaddr)
memunmap(gxp->fwdatabuf.vaddr);
devm_kfree(gxp->dev, mgr);
gxp->data_mgr = NULL;
}
void gxp_fw_data_populate_vd_cfg(struct gxp_dev *gxp, struct gxp_virtual_device *vd)
{
_gxp_fw_data_populate_vd_cfg(gxp, vd);
}
int gxp_fw_data_set_core_telemetry_descriptors(struct gxp_dev *gxp, u8 type,
u32 host_status,
struct gxp_coherent_buf *buffers,
u32 per_buffer_size)
{
struct core_telemetry_descriptor *core_descriptors;
uint core;
bool enable;
core_descriptors = gxp_fw_data_get_core_telemetry_descriptor(gxp, type);
if (IS_ERR(core_descriptors))
return PTR_ERR(core_descriptors);
enable = (host_status & GXP_CORE_TELEMETRY_HOST_STATUS_ENABLED);
if (enable) {
/* Validate that the provided IOVAs are addressable (i.e. 32-bit) */
for (core = 0; core < GXP_NUM_CORES; core++) {
if (buffers && buffers[core].dsp_addr > U32_MAX &&
buffers[core].size == per_buffer_size)
return -EINVAL;
}
for (core = 0; core < GXP_NUM_CORES; core++) {
core_descriptors[core].host_status = host_status;
core_descriptors[core].buffer_addr =
(u32)buffers[core].dsp_addr;
core_descriptors[core].buffer_size = per_buffer_size;
}
} else {
for (core = 0; core < GXP_NUM_CORES; core++) {
core_descriptors[core].host_status = host_status;
core_descriptors[core].buffer_addr = 0;
core_descriptors[core].buffer_size = 0;
}
}
return 0;
}
u32 gxp_fw_data_get_core_telemetry_device_status(struct gxp_dev *gxp, uint core,
u8 type)
{
struct gxp_system_descriptor_rw *des_rw = gxp->data_mgr->sys_desc_rw;
if (core >= GXP_NUM_CORES)
return 0;
switch (type) {
case GXP_TELEMETRY_TYPE_LOGGING:
return des_rw->telemetry_desc.per_core_loggers[core]
.device_status;
case GXP_TELEMETRY_TYPE_TRACING:
return des_rw->telemetry_desc.per_core_tracers[core]
.device_status;
default:
return 0;
}
}
struct gxp_mapped_resource gxp_fw_data_resource(struct gxp_dev *gxp)
{
/*
* For direct mode, the config regions are programmed by host (us); for
* MCU mode, the config regions are programmed by MCU.
*/
if (gxp_is_direct_mode(gxp)) {
struct gxp_mapped_resource tmp = gxp->fwdatabuf;
/* Leave the first piece be used for gxp_fw_data_init() */
tmp.vaddr += tmp.size / 2;
tmp.paddr += tmp.size / 2;
return tmp;
} else {
return gxp->shared_buf;
}
}
void *gxp_fw_data_system_cfg(struct gxp_dev *gxp)
{
/* Use the end of the shared region for system cfg. */
return gxp_fw_data_resource(gxp).vaddr + GXP_SHARED_BUFFER_SIZE -
GXP_FW_DATA_SYSCFG_SIZE;
}
void gxp_fw_data_populate_system_config(struct gxp_dev *gxp)
{
set_system_cfg_region(gxp, gxp_fw_data_system_cfg(gxp));
}