blob: eb31f235822139bf3103c20af1c76cc680067108 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* GXP firmware loader.
*
* Copyright (C) 2021 Google LLC
*/
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/elf.h>
#include <linux/gsa/gsa_image_auth.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/types.h>
#include "gxp-bpm.h"
#include "gxp-debug-dump.h"
#include "gxp-doorbell.h"
#include "gxp-firmware.h"
#include "gxp-host-device-structs.h"
#include "gxp-internal.h"
#include "gxp-lpm.h"
#include "gxp-mailbox.h"
#include "gxp-notification.h"
#include "gxp-pm.h"
#include "gxp-telemetry.h"
#include "gxp-vd.h"
/* Files need to be copied to /lib/firmware */
#define DSP_FIRMWARE_DEFAULT_PREFIX "gxp_fw_core"
#define FW_HEADER_SIZE (0x1000)
#define FW_IMAGE_TYPE_OFFSET (0x400)
static int gxp_dsp_fw_auth_disable;
module_param_named(dsp_fw_auth_disable, gxp_dsp_fw_auth_disable, int, 0660);
static int
request_dsp_firmware(struct gxp_dev *gxp, char *name_prefix,
const struct firmware *out_firmwares[GXP_NUM_CORES])
{
char *name_buf;
/* 1 for NULL-terminator and up to 4 for core number */
size_t name_len = strlen(name_prefix) + 5;
int core;
int ret = 0;
name_buf = kzalloc(name_len, GFP_KERNEL);
if (!name_buf)
return -ENOMEM;
for (core = 0; core < GXP_NUM_CORES; core++) {
ret = snprintf(name_buf, name_len, "%s%d", name_prefix, core);
if (ret <= 0 || ret >= name_len) {
ret = -EINVAL;
goto err;
}
dev_notice(gxp->dev, "Requesting dsp core %d firmware file: %s\n",
core, name_buf);
ret = request_firmware(&out_firmwares[core], name_buf, NULL);
if (ret < 0) {
dev_err(gxp->dev,
"Requesting dsp core %d firmware failed (ret=%d)\n",
core, ret);
goto err;
}
dev_dbg(gxp->dev, "dsp core %d firmware file obtained\n", core);
}
kfree(name_buf);
return ret;
err:
for (core -= 1; core >= 0; core--)
release_firmware(out_firmwares[core]);
kfree(name_buf);
return ret;
}
static int elf_load_segments(struct gxp_dev *gxp, const u8 *elf_data,
size_t size,
const struct gxp_mapped_resource *buffer)
{
struct elf32_hdr *ehdr;
struct elf32_phdr *phdr;
int i, ret = 0;
ehdr = (struct elf32_hdr *)elf_data;
phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
if ((ehdr->e_ident[EI_MAG0] != ELFMAG0) ||
(ehdr->e_ident[EI_MAG1] != ELFMAG1) ||
(ehdr->e_ident[EI_MAG2] != ELFMAG2) ||
(ehdr->e_ident[EI_MAG3] != ELFMAG3)) {
dev_err(gxp->dev, "Cannot load FW! Invalid ELF format.\n");
return -EINVAL;
}
/* go through the available ELF segments */
for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
u64 da = phdr->p_paddr;
u32 memsz = phdr->p_memsz;
u32 filesz = phdr->p_filesz;
u32 offset = phdr->p_offset;
void *ptr;
if (phdr->p_type != PT_LOAD)
continue;
if (!phdr->p_flags)
continue;
if (!memsz)
continue;
if (!((da >= (u32)buffer->daddr) &&
((da + memsz) <= ((u32)buffer->daddr +
(u32)buffer->size)))) {
/*
* Some BSS data may be referenced from TCM, and can be
* skipped while loading
*/
dev_err(gxp->dev, "Segment out of bounds: da 0x%llx mem 0x%x. Skipping...\n",
da, memsz);
continue;
}
dev_notice(gxp->dev, "phdr: type %d da 0x%llx memsz 0x%x filesz 0x%x\n",
phdr->p_type, da, memsz, filesz);
if (filesz > memsz) {
dev_err(gxp->dev, "Bad phdr filesz 0x%x memsz 0x%x\n",
filesz, memsz);
ret = -EINVAL;
break;
}
if (offset + filesz > size) {
dev_err(gxp->dev, "Truncated fw: need 0x%x avail 0x%zx\n",
offset + filesz, size);
ret = -EINVAL;
break;
}
/* grab the kernel address for this device address */
ptr = buffer->vaddr + (da - buffer->daddr);
if (!ptr) {
dev_err(gxp->dev, "Bad phdr: da 0x%llx mem 0x%x\n",
da, memsz);
ret = -EINVAL;
break;
}
/* put the segment where the remote processor expects it */
if (phdr->p_filesz)
memcpy_toio(ptr, elf_data + phdr->p_offset, filesz);
/*
* Zero out remaining memory for this segment.
*/
if (memsz > filesz)
memset(ptr + filesz, 0, memsz - filesz);
}
return ret;
}
static int
gxp_firmware_authenticate(struct gxp_dev *gxp,
const struct firmware *firmwares[GXP_NUM_CORES])
{
const u8 *data;
size_t size;
void *header_vaddr;
struct gxp_mapped_resource *buffer;
dma_addr_t header_dma_addr;
int core;
int ret;
if (gxp_dsp_fw_auth_disable) {
dev_warn(gxp->dev,
"DSP FW authentication disabled, skipping\n");
return 0;
}
if (!gxp->gsa_dev) {
dev_warn(
gxp->dev,
"No GSA device available, skipping firmware authentication\n");
return 0;
}
for (core = 0; core < GXP_NUM_CORES; core++) {
data = firmwares[core]->data;
size = firmwares[core]->size;
buffer = &gxp->fwbufs[core];
if ((size - FW_HEADER_SIZE) > buffer->size) {
dev_err(gxp->dev,
"Firmware image does not fit (%zu > %llu)\n",
size - FW_HEADER_SIZE, buffer->size);
ret = -EINVAL;
goto error;
}
dev_dbg(gxp->dev, "Authenticating firmware of core%u\n", core);
/* Allocate coherent memory for the image header */
header_vaddr = dma_alloc_coherent(gxp->gsa_dev, FW_HEADER_SIZE,
&header_dma_addr, GFP_KERNEL);
if (!header_vaddr) {
dev_err(gxp->dev,
"Failed to allocate coherent memory for header\n");
ret = -ENOMEM;
goto error;
}
/* Copy the header to GSA coherent memory */
memcpy(header_vaddr, data, FW_HEADER_SIZE);
/* Copy the firmware image to the carveout location, skipping the header */
memcpy_toio(buffer->vaddr, data + FW_HEADER_SIZE,
size - FW_HEADER_SIZE);
dev_dbg(gxp->dev,
"Requesting GSA authentication. meta = %pad payload = %pap",
&header_dma_addr, &buffer->paddr);
ret = gsa_authenticate_image(gxp->gsa_dev, header_dma_addr,
buffer->paddr);
dma_free_coherent(gxp->gsa_dev, FW_HEADER_SIZE, header_vaddr,
header_dma_addr);
if (ret) {
dev_err(gxp->dev, "GSA authentication failed: %d\n",
ret);
memset_io(buffer->vaddr, 0, buffer->size);
goto error;
}
}
return 0;
error:
/*
* Zero out firmware buffers if we got a authentication failure on any
* core.
*/
for (core -= 1; core >= 0; core--) {
buffer = &gxp->fwbufs[core];
memset_io(buffer->vaddr, 0, buffer->size);
}
return ret;
}
/* Forward declaration for usage inside gxp_firmware_load(..). */
static void gxp_firmware_unload(struct gxp_dev *gxp, uint core);
static void gxp_program_reset_vector(struct gxp_dev *gxp, uint core, bool verbose)
{
u32 reset_vec;
reset_vec = gxp_read_32_core(gxp, core,
GXP_REG_ALT_RESET_VECTOR);
if (verbose)
dev_notice(gxp->dev,
"Current Aurora reset vector for core %u: 0x%x\n",
core, reset_vec);
gxp_write_32_core(gxp, core, GXP_REG_ALT_RESET_VECTOR,
gxp->fwbufs[core].daddr);
if (verbose)
dev_notice(gxp->dev,
"New Aurora reset vector for core %u: 0x%llx\n",
core, gxp->fwbufs[core].daddr);
}
static int gxp_firmware_load(struct gxp_dev *gxp, uint core)
{
u32 offset;
void __iomem *core_scratchpad_base;
int ret;
if (!gxp->firmwares[core])
return -ENODEV;
/* Load firmware to System RAM */
ret = elf_load_segments(gxp,
gxp->firmwares[core]->data + FW_HEADER_SIZE,
gxp->firmwares[core]->size - FW_HEADER_SIZE,
&gxp->fwbufs[core]);
if (ret) {
dev_err(gxp->dev, "Unable to load elf file\n");
goto out_firmware_unload;
}
memset(gxp->fwbufs[core].vaddr + AURORA_SCRATCHPAD_OFF, 0,
AURORA_SCRATCHPAD_LEN);
core_scratchpad_base = gxp->fwbufs[core].vaddr + AURORA_SCRATCHPAD_OFF;
offset = SCRATCHPAD_MSG_OFFSET(MSG_CORE_ALIVE);
writel(0, core_scratchpad_base + offset);
offset = SCRATCHPAD_MSG_OFFSET(MSG_TOP_ACCESS_OK);
writel(0, core_scratchpad_base + offset);
/* TODO(b/188970444): Cleanup logging of addresses */
dev_notice(gxp->dev,
"ELF loaded at virtual: %pK and physical: 0x%llx\n",
gxp->fwbufs[core].vaddr, gxp->fwbufs[core].paddr);
/* Configure bus performance monitors */
gxp_bpm_configure(gxp, core, INST_BPM_OFFSET, BPM_EVENT_READ_XFER);
gxp_bpm_configure(gxp, core, DATA_BPM_OFFSET, BPM_EVENT_WRITE_XFER);
return 0;
out_firmware_unload:
gxp_firmware_unload(gxp, core);
return ret;
}
static int gxp_firmware_handshake(struct gxp_dev *gxp, uint core)
{
u32 offset;
u32 expected_top_value;
void __iomem *core_scratchpad_base;
int ctr;
/* Wait for core to come up */
dev_notice(gxp->dev, "Waiting for core %u to power up...\n", core);
ctr = 1000;
while (ctr) {
if (gxp_lpm_is_powered(gxp, core))
break;
udelay(1 * GXP_TIME_DELAY_FACTOR);
ctr--;
}
if (!ctr) {
dev_notice(gxp->dev, "Failed!\n");
return -ETIMEDOUT;
}
dev_notice(gxp->dev, "Powered up!\n");
/* Wait for 500ms. Then check if Q7 core is alive */
dev_notice(gxp->dev, "Waiting for core %u to respond...\n",
core);
core_scratchpad_base = gxp->fwbufs[core].vaddr + AURORA_SCRATCHPAD_OFF;
/*
* Currently, the hello_world FW writes a magic number
* (Q7_ALIVE_MAGIC) to offset MSG_CORE_ALIVE in the scratchpad
* space as an alive message
*/
ctr = 5000;
offset = SCRATCHPAD_MSG_OFFSET(MSG_CORE_ALIVE);
usleep_range(50 * GXP_TIME_DELAY_FACTOR, 60 * GXP_TIME_DELAY_FACTOR);
while (ctr--) {
if (readl(core_scratchpad_base + offset) == Q7_ALIVE_MAGIC)
break;
usleep_range(1 * GXP_TIME_DELAY_FACTOR,
10 * GXP_TIME_DELAY_FACTOR);
}
if (readl(core_scratchpad_base + offset) != Q7_ALIVE_MAGIC) {
dev_err(gxp->dev, "Core %u did not respond!\n", core);
return -EIO;
}
dev_notice(gxp->dev, "Core %u is alive!\n", core);
#if !IS_ENABLED(CONFIG_GXP_GEM5)
/*
* FW reads the INT_MASK0 register (written by the driver) to
* validate TOP access. The value read is echoed back by the FW to
* offset MSG_TOP_ACCESS_OK in the scratchpad space, which must be
* compared to the value written in the INT_MASK0 register by the
* driver for confirmation.
* On Gem5, FW will start early when lpm is up. This behavior will
* affect the order of reading/writing INT_MASK0, so ignore these
* handshakes in Gem5.
*/
ctr = 1000;
offset = SCRATCHPAD_MSG_OFFSET(MSG_TOP_ACCESS_OK);
expected_top_value = BIT(CORE_WAKEUP_DOORBELL(core));
while (ctr--) {
if (readl(core_scratchpad_base + offset) == expected_top_value)
break;
udelay(1 * GXP_TIME_DELAY_FACTOR);
}
if (readl(core_scratchpad_base + offset) != expected_top_value) {
dev_err(gxp->dev, "TOP access from core %u failed!\n", core);
return -EIO;
}
dev_notice(gxp->dev, "TOP access from core %u successful!\n", core);
#endif
/* Stop bus performance monitors */
gxp_bpm_stop(gxp, core);
dev_notice(gxp->dev, "Core%u Instruction read transactions: 0x%x\n",
core, gxp_bpm_read_counter(gxp, core, INST_BPM_OFFSET));
dev_notice(gxp->dev, "Core%u Data write transactions: 0x%x\n", core,
gxp_bpm_read_counter(gxp, core, DATA_BPM_OFFSET));
return 0;
}
static void gxp_firmware_unload(struct gxp_dev *gxp, uint core)
{
/* NO-OP for now. */
}
/* Helper function to parse name written to sysfs "load_dsp_firmware" node */
static char *fw_name_from_attr_buf(const char *buf)
{
size_t len;
char *name;
len = strlen(buf);
if (len == 0 || buf[len - 1] != '\n')
return ERR_PTR(-EINVAL);
name = kstrdup(buf, GFP_KERNEL);
if (!name)
return ERR_PTR(-ENOMEM);
name[len - 1] = '\0';
return name;
}
/* sysfs node for loading custom firmware */
static ssize_t load_dsp_firmware_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gxp_dev *gxp = dev_get_drvdata(dev);
ssize_t ret;
mutex_lock(&gxp->dsp_firmware_lock);
ret = scnprintf(buf, PAGE_SIZE, "%s\n",
gxp->firmware_name ? gxp->firmware_name :
DSP_FIRMWARE_DEFAULT_PREFIX);
mutex_unlock(&gxp->dsp_firmware_lock);
return ret;
}
static ssize_t load_dsp_firmware_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct gxp_dev *gxp = dev_get_drvdata(dev);
const struct firmware *firmwares[GXP_NUM_CORES];
char *name_buf = NULL;
int ret;
int core;
/*
* Lock the VD semaphore to ensure no core is executing the firmware
* while requesting new firmware.
*/
down_read(&gxp->vd_semaphore);
if (gxp->firmware_running) {
dev_warn(dev, "Cannot update firmware when any core is running\n");
ret = -EBUSY;
goto out;
}
name_buf = fw_name_from_attr_buf(buf);
if (IS_ERR(name_buf)) {
dev_err(gxp->dev, "Invalid firmware prefix requested: %s\n",
buf);
ret = PTR_ERR(name_buf);
goto out;
}
mutex_lock(&gxp->dsp_firmware_lock);
dev_notice(gxp->dev, "Requesting firmware be reloaded: %s\n", name_buf);
ret = request_dsp_firmware(gxp, name_buf, firmwares);
if (ret) {
dev_err(gxp->dev,
"Failed to request firmwares with names \"%sX\" (ret=%d)\n",
name_buf, ret);
goto err_request_firmware;
}
ret = gxp_firmware_authenticate(gxp, firmwares);
if (ret)
goto err_authenticate_firmware;
for (core = 0; core < GXP_NUM_CORES; core++) {
if (gxp->firmwares[core])
release_firmware(gxp->firmwares[core]);
gxp->firmwares[core] = firmwares[core];
}
kfree(gxp->firmware_name);
gxp->firmware_name = name_buf;
mutex_unlock(&gxp->dsp_firmware_lock);
out:
up_read(&gxp->vd_semaphore);
return count;
err_authenticate_firmware:
for (core = 0; core < GXP_NUM_CORES; core++)
release_firmware(firmwares[core]);
err_request_firmware:
kfree(name_buf);
mutex_unlock(&gxp->dsp_firmware_lock);
up_read(&gxp->vd_semaphore);
return ret;
}
static DEVICE_ATTR_RW(load_dsp_firmware);
static struct attribute *dev_attrs[] = {
&dev_attr_load_dsp_firmware.attr,
NULL,
};
static const struct attribute_group gxp_firmware_attr_group = {
.attrs = dev_attrs,
};
int gxp_fw_init(struct gxp_dev *gxp)
{
u32 ver, proc_id;
uint core;
struct resource r;
int ret;
/* Power on BLK_AUR to read the revision and processor ID registers */
gxp_pm_blk_on(gxp);
ver = gxp_read_32(gxp, GXP_REG_AURORA_REVISION);
dev_notice(gxp->dev, "Aurora version: 0x%x\n", ver);
for (core = 0; core < GXP_NUM_CORES; core++) {
proc_id = gxp_read_32_core(gxp, core, GXP_REG_PROCESSOR_ID);
dev_notice(gxp->dev, "Aurora core %u processor ID: 0x%x\n",
core, proc_id);
}
/* Shut BLK_AUR down again to avoid interfering with power management */
gxp_pm_blk_off(gxp);
ret = gxp_acquire_rmem_resource(gxp, &r, "gxp-fw-region");
if (ret) {
dev_err(gxp->dev,
"Unable to acquire firmware reserved memory\n");
return ret;
}
for (core = 0; core < GXP_NUM_CORES; core++) {
gxp->fwbufs[core].size =
(resource_size(&r) / GXP_NUM_CORES) & PAGE_MASK;
gxp->fwbufs[core].paddr =
r.start + (core * gxp->fwbufs[core].size);
/*
* Firmware buffers are not mapped into kernel VA space until
* firmware is ready to be loaded.
*/
}
ret = gxp_acquire_rmem_resource(gxp, &r, "gxp-scratchpad-region");
if (ret) {
dev_err(gxp->dev,
"Unable to acquire shared FW data reserved memory\n");
return ret;
}
gxp->fwdatabuf.size = resource_size(&r);
gxp->fwdatabuf.paddr = r.start;
/*
* Scratchpad region is not mapped until the firmware data is
* initialized.
*/
for (core = 0; core < GXP_NUM_CORES; core++) {
/*
* Currently, the Q7 FW needs to be statically linked to a base
* address where it would be loaded in memory. This requires the
* address (where the FW is to be loaded in DRAM) to be
* pre-defined, and hence not allocate-able dynamically (using
* the kernel's memory management system). Therefore, we are
* memremapping a static address and loading the FW there, while
* also having compiled the FW with this as the base address
* (used by the linker).
*/
gxp->fwbufs[core].vaddr =
memremap(gxp->fwbufs[core].paddr,
gxp->fwbufs[core].size, MEMREMAP_WC);
if (!(gxp->fwbufs[core].vaddr)) {
dev_err(gxp->dev, "FW buf %d memremap failed\n", core);
ret = -EINVAL;
goto out_fw_destroy;
}
}
ret = device_add_group(gxp->dev, &gxp_firmware_attr_group);
if (ret)
goto out_fw_destroy;
gxp->firmware_running = 0;
return 0;
out_fw_destroy:
gxp_fw_destroy(gxp);
return ret;
}
void gxp_fw_destroy(struct gxp_dev *gxp)
{
uint core;
device_remove_group(gxp->dev, &gxp_firmware_attr_group);
for (core = 0; core < GXP_NUM_CORES; core++) {
if (gxp->fwbufs[core].vaddr) {
memunmap(gxp->fwbufs[core].vaddr);
gxp->fwbufs[core].vaddr = NULL;
}
if (gxp->firmwares[core]) {
release_firmware(gxp->firmwares[core]);
gxp->firmwares[core] = NULL;
}
}
kfree(gxp->firmware_name);
}
int gxp_firmware_request_if_needed(struct gxp_dev *gxp)
{
int ret = 0;
uint core;
mutex_lock(&gxp->dsp_firmware_lock);
if (gxp->is_firmware_requested)
goto out;
ret = request_dsp_firmware(gxp, DSP_FIRMWARE_DEFAULT_PREFIX,
gxp->firmwares);
if (ret)
goto out;
ret = gxp_firmware_authenticate(gxp, gxp->firmwares);
if (ret)
goto err_authenticate_firmware;
gxp->is_firmware_requested = true;
out:
mutex_unlock(&gxp->dsp_firmware_lock);
return ret;
err_authenticate_firmware:
for (core = 0; core < GXP_NUM_CORES; core++) {
release_firmware(gxp->firmwares[core]);
gxp->firmwares[core] = NULL;
}
mutex_unlock(&gxp->dsp_firmware_lock);
return ret;
}
static int gxp_firmware_setup(struct gxp_dev *gxp, uint core)
{
int ret = 0;
if (gxp->firmware_running & BIT(core)) {
dev_err(gxp->dev, "Firmware is already running on core %u\n",
core);
return -EBUSY;
}
ret = gxp_firmware_load(gxp, core);
if (ret) {
dev_err(gxp->dev, "Failed to load firmware on core %u\n", core);
return ret;
}
/* Mark this as a cold boot */
gxp_firmware_set_boot_mode(gxp, core, GXP_BOOT_MODE_REQUEST_COLD_BOOT);
ret = gxp_firmware_setup_hw_after_block_off(gxp, core,
/*verbose=*/true);
if (ret) {
dev_err(gxp->dev, "Failed to power up core %u\n", core);
gxp_firmware_unload(gxp, core);
}
return ret;
}
static void gxp_firmware_wakeup_cores(struct gxp_dev *gxp, uint core_list)
{
uint core;
/* Raise wakeup doorbell */
for (core = 0; core < GXP_NUM_CORES; core++) {
if (!(core_list & BIT(core)))
continue;
#if !IS_ENABLED(CONFIG_GXP_GEM5)
gxp_doorbell_enable_for_core(gxp, CORE_WAKEUP_DOORBELL(core),
core);
#endif
gxp_doorbell_set(gxp, CORE_WAKEUP_DOORBELL(core));
}
}
static int gxp_firmware_finish_startup(struct gxp_dev *gxp,
struct gxp_virtual_device *vd,
uint virt_core, uint core)
{
int ret;
struct work_struct *work;
ret = gxp_firmware_handshake(gxp, core);
if (ret) {
dev_err(gxp->dev, "Firmware handshake failed on core %u\n",
core);
gxp_pm_core_off(gxp, core);
goto out_firmware_unload;
}
/* Initialize mailbox */
gxp->mailbox_mgr->mailboxes[core] =
gxp_mailbox_alloc(gxp->mailbox_mgr, vd, virt_core, core);
if (IS_ERR(gxp->mailbox_mgr->mailboxes[core])) {
dev_err(gxp->dev,
"Unable to allocate mailbox (core=%u, ret=%ld)\n", core,
PTR_ERR(gxp->mailbox_mgr->mailboxes[core]));
ret = PTR_ERR(gxp->mailbox_mgr->mailboxes[core]);
gxp->mailbox_mgr->mailboxes[core] = NULL;
goto out_firmware_unload;
}
work = gxp_debug_dump_get_notification_handler(gxp, core);
if (work)
gxp_notification_register_handler(
gxp, core, HOST_NOTIF_DEBUG_DUMP_READY, work);
work = gxp_telemetry_get_notification_handler(gxp, core);
if (work)
gxp_notification_register_handler(
gxp, core, HOST_NOTIF_TELEMETRY_STATUS, work);
gxp->firmware_running |= BIT(core);
return ret;
out_firmware_unload:
gxp_firmware_unload(gxp, core);
return ret;
}
static void gxp_firmware_stop_core(struct gxp_dev *gxp,
struct gxp_virtual_device *vd,
uint virt_core, uint core)
{
if (!(gxp->firmware_running & BIT(core)))
dev_err(gxp->dev, "Firmware is not running on core %u\n", core);
gxp->firmware_running &= ~BIT(core);
gxp_notification_unregister_handler(gxp, core,
HOST_NOTIF_DEBUG_DUMP_READY);
gxp_notification_unregister_handler(gxp, core,
HOST_NOTIF_TELEMETRY_STATUS);
gxp_mailbox_release(gxp->mailbox_mgr, vd, virt_core,
gxp->mailbox_mgr->mailboxes[core]);
dev_notice(gxp->dev, "Mailbox %u released\n", core);
if (vd->state == GXP_VD_RUNNING)
gxp_pm_core_off(gxp, core);
gxp_firmware_unload(gxp, core);
}
int gxp_firmware_run(struct gxp_dev *gxp, struct gxp_virtual_device *vd,
uint core_list)
{
int ret;
uint core, virt_core;
uint failed_cores = 0;
int failed_ret;
for (core = 0; core < GXP_NUM_CORES; core++) {
if (core_list & BIT(core)) {
ret = gxp_firmware_setup(gxp, core);
if (ret) {
failed_cores |= BIT(core);
failed_ret = ret;
dev_err(gxp->dev, "Failed to run firmware on core %u\n",
core);
}
}
}
if (failed_cores != 0) {
/*
* Shut down the cores which call `gxp_firmware_setup`
* successfully
*/
for (core = 0; core < GXP_NUM_CORES; core++) {
if (core_list & BIT(core)) {
if (!(failed_cores & BIT(core))) {
gxp_pm_core_off(gxp, core);
gxp_firmware_unload(gxp, core);
}
}
}
return failed_ret;
}
#if IS_ENABLED(CONFIG_GXP_GEM5)
/*
* GEM5 starts firmware after LPM is programmed, so we need to call
* gxp_doorbell_enable_for_core here to set GXP_REG_COMMON_INT_MASK_0
* first to enable the firmware handshakes.
*/
for (core = 0; core < GXP_NUM_CORES; core++) {
if (!(core_list & BIT(core)))
continue;
gxp_doorbell_enable_for_core(gxp, CORE_WAKEUP_DOORBELL(core),
core);
}
#endif
/* Switch clock mux to the normal state to guarantee LPM works */
gxp_pm_force_clkmux_normal(gxp);
gxp_firmware_wakeup_cores(gxp, core_list);
virt_core = 0;
for (core = 0; core < GXP_NUM_CORES; core++) {
if (core_list & BIT(core)) {
ret = gxp_firmware_finish_startup(gxp, vd, virt_core,
core);
if (ret) {
failed_cores |= BIT(core);
dev_err(gxp->dev,
"Failed to run firmware on core %u\n",
core);
}
virt_core++;
}
}
if (failed_cores != 0) {
virt_core = 0;
for (core = 0; core < GXP_NUM_CORES; core++) {
if (core_list & BIT(core)) {
if (!(failed_cores & BIT(core))) {
gxp_firmware_stop_core(gxp, vd,
virt_core, core);
}
virt_core++;
}
}
}
/* Check if we need to set clock mux to low state as requested */
gxp_pm_resume_clkmux(gxp);
return ret;
}
int gxp_firmware_setup_hw_after_block_off(struct gxp_dev *gxp, uint core,
bool verbose)
{
gxp_program_reset_vector(gxp, core, verbose);
return gxp_pm_core_on(gxp, core, verbose);
}
void gxp_firmware_stop(struct gxp_dev *gxp, struct gxp_virtual_device *vd,
uint core_list)
{
uint core, virt_core = 0;
for (core = 0; core < GXP_NUM_CORES; core++) {
if (core_list & BIT(core)) {
gxp_firmware_stop_core(gxp, vd, virt_core, core);
virt_core++;
}
}
}
void gxp_firmware_set_boot_mode(struct gxp_dev *gxp, uint core, u32 mode)
{
void __iomem *boot_mode_addr;
/* Callers shouldn't call the function under this condition. */
if (!gxp->fwbufs[core].vaddr)
return;
boot_mode_addr = gxp->fwbufs[core].vaddr + AURORA_SCRATCHPAD_OFF +
SCRATCHPAD_MSG_OFFSET(MSG_BOOT_MODE);
writel(mode, boot_mode_addr);
}
u32 gxp_firmware_get_boot_mode(struct gxp_dev *gxp, uint core)
{
void __iomem *boot_mode_addr;
/* Callers shouldn't call the function under this condition. */
if (!gxp->fwbufs[core].vaddr)
return 0;
boot_mode_addr = gxp->fwbufs[core].vaddr + AURORA_SCRATCHPAD_OFF +
SCRATCHPAD_MSG_OFFSET(MSG_BOOT_MODE);
return readl(boot_mode_addr);
}