blob: d9c390a924137f6dff2ebc30117264d059934061 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Structures and helpers for managing GXP MicroController Unit.
*
* Copyright (C) 2022 Google LLC
*/
#include <linux/delay.h>
#include <linux/sizes.h>
#include <gcip/gcip-mem-pool.h>
#include "gxp-config.h"
#include "gxp-internal.h"
#include "gxp-kci.h"
#include "gxp-lpm.h"
#include "gxp-mcu-firmware.h"
#include "gxp-mcu-platform.h"
#include "gxp-mcu.h"
#include "gxp-uci.h"
/* Allocates the MCU <-> cores shared buffer region. */
static int gxp_alloc_shared_buffer(struct gxp_dev *gxp, struct gxp_mcu *mcu)
{
const size_t size = GXP_SHARED_BUFFER_SIZE;
phys_addr_t paddr;
struct gxp_mapped_resource *res = &mcu->gxp->shared_buf;
size_t offset;
void *vaddr;
paddr = gcip_mem_pool_alloc(&mcu->remap_data_pool, size);
if (!paddr)
return -ENOMEM;
res->paddr = paddr;
res->size = size;
res->daddr = GXP_IOVA_SHARED_BUFFER;
/* clear shared buffer to make sure it's clean */
offset = gcip_mem_pool_offset(&mcu->remap_data_pool, paddr);
vaddr = offset + (mcu->fw.image_buf.vaddr + GXP_IREMAP_DATA_OFFSET);
memset(vaddr, 0, size);
res->vaddr = vaddr;
return 0;
}
static void gxp_free_shared_buffer(struct gxp_mcu *mcu)
{
struct gxp_mapped_resource *res = &mcu->gxp->shared_buf;
gcip_mem_pool_free(&mcu->remap_data_pool, res->paddr, res->size);
}
/*
* Initializes memory pools, must be called after @mcu->fw has been initialized
* to have a valid image_buf.
*/
static int gxp_mcu_mem_pools_init(struct gxp_dev *gxp, struct gxp_mcu *mcu)
{
phys_addr_t iremap_paddr = mcu->fw.image_buf.paddr;
int ret;
ret = gcip_mem_pool_init(&mcu->remap_data_pool, gxp->dev,
iremap_paddr + GXP_IREMAP_DATA_OFFSET,
GXP_IREMAP_DATA_SIZE, SZ_4K);
if (ret)
return ret;
ret = gcip_mem_pool_init(&mcu->remap_secure_pool, gxp->dev,
iremap_paddr + GXP_IREMAP_SECURE_OFFSET,
GXP_IREMAP_SECURE_SIZE, SZ_4K);
if (ret) {
gcip_mem_pool_exit(&mcu->remap_data_pool);
return ret;
}
return 0;
}
static void gxp_mcu_mem_pools_exit(struct gxp_mcu *mcu)
{
gcip_mem_pool_exit(&mcu->remap_secure_pool);
gcip_mem_pool_exit(&mcu->remap_data_pool);
}
int gxp_mcu_mem_alloc_data(struct gxp_mcu *mcu, struct gxp_mapped_resource *mem, size_t size)
{
size_t offset;
phys_addr_t paddr;
paddr = gcip_mem_pool_alloc(&mcu->remap_data_pool, size);
if (!paddr)
return -ENOMEM;
offset = gcip_mem_pool_offset(&mcu->remap_data_pool, paddr);
mem->size = size;
mem->paddr = paddr;
mem->vaddr = offset + (mcu->fw.image_buf.vaddr + GXP_IREMAP_DATA_OFFSET);
mem->daddr = offset + (mcu->fw.image_buf.daddr + GXP_IREMAP_DATA_OFFSET);
return 0;
}
void gxp_mcu_mem_free_data(struct gxp_mcu *mcu, struct gxp_mapped_resource *mem)
{
gcip_mem_pool_free(&mcu->remap_data_pool, mem->paddr, mem->size);
mem->size = 0;
mem->paddr = 0;
mem->vaddr = NULL;
mem->daddr = 0;
}
int gxp_mcu_init(struct gxp_dev *gxp, struct gxp_mcu *mcu)
{
int ret;
mcu->gxp = gxp;
ret = gxp_mcu_firmware_init(gxp, &mcu->fw);
if (ret)
return ret;
ret = gxp_mcu_mem_pools_init(gxp, mcu);
if (ret)
goto err_fw_exit;
ret = gxp_alloc_shared_buffer(gxp, mcu);
if (ret)
goto err_pools_exit;
/*
* MCU telemetry must be initialized before UCI and KCI to match the
* .log_buffer address in the firmware linker.ld.
*/
ret = gxp_mcu_telemetry_init(mcu);
if (ret)
goto err_free_shared_buffer;
ret = gxp_uci_init(mcu);
if (ret)
goto err_telemetry_exit;
ret = gxp_kci_init(mcu);
if (ret)
goto err_uci_exit;
return 0;
err_uci_exit:
gxp_uci_exit(&mcu->uci);
err_telemetry_exit:
gxp_mcu_telemetry_exit(mcu);
err_free_shared_buffer:
gxp_free_shared_buffer(mcu);
err_pools_exit:
gxp_mcu_mem_pools_exit(mcu);
err_fw_exit:
gxp_mcu_firmware_exit(&mcu->fw);
return ret;
}
void gxp_mcu_exit(struct gxp_mcu *mcu)
{
gxp_kci_exit(&mcu->kci);
gxp_uci_exit(&mcu->uci);
gxp_mcu_telemetry_exit(mcu);
gxp_free_shared_buffer(mcu);
gxp_mcu_mem_pools_exit(mcu);
gxp_mcu_firmware_exit(&mcu->fw);
}
void gxp_mcu_reset_mailbox(struct gxp_mcu *mcu)
{
gxp_uci_reinit(&mcu->uci);
gxp_kci_reinit(&mcu->kci);
}