blob: 3735f7673636187034d530857450c1b66181fee5 [file]
/*
* Copyright (c) 2016, NVIDIA CORPORATION.
* Copright 2019, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
* See "LICENSE_GPLv2.txt" for details.
*
* @TAG(DATA61_GPL)
*/
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <platsupport/pmem.h>
#include <tx2bpmp/hsp.h>
#include <utils/util.h>
/* Register holds information about the number of
* shared mailboxes, shared semaphores etc. */
#define HSP_INT_DIMENSION_OFFSET 0x380
#define HSP_INT_DIMENSION_SM_SHIFT 0
#define HSP_INT_DIMENSION_SS_SHIFT 4
#define HSP_INT_DIMENSION_AS_SHIFT 8
#define HSP_INT_DIMENSION_NUM_MASK 0xf
#define HSP_DOORBELL_BLOCK_STRIDE 0x100
#define HSP_BITMAP_TZ_SECURE_SHFIT 0
#define HSP_BITMAP_TZ_NONSECURE_SHIFT 16
enum dbell_reg_offset {
DBELL_TRIGGER = 0x0,
DBELL_ENABLE = 0x4,
DBELL_RAW = 0x8,
DBELL_PENDING = 0xc
};
enum dbell_bitmap_offset {
CCPLEX_BIT = BIT(1),
DPMU_BIT = BIT(2),
BPMP_BIT = BIT(3),
SPE_BIT = BIT(4),
CPE_BIT = BIT(5),
SCE_BIT = CPE_BIT,
DMA_BIT = BIT(6),
TSECA_BIT = BIT(7),
TSECB_BIT = BIT(8),
JTAGM_BIT = BIT(9),
CSITE_BIT = BIT(10),
APE_BIT = BIT(11)
};
static pmem_region_t tx2_hsp_region = {
.type = PMEM_TYPE_DEVICE,
.base_addr = TX2_HSP_PADDR,
.length = TX2_HSP_SIZE
};
static bool check_doorbell_id_is_valid(enum tx2_doorbell_id db_id)
{
if (CCPLEX_PM_DBELL <= db_id && db_id <= APE_DBELL) {
return true;
}
return false;
}
static uint32_t *tx2_hsp_get_doorbell_register(tx2_hsp_t *hsp, enum tx2_doorbell_id db_id,
enum dbell_reg_offset offset)
{
assert(hsp);
assert(DBELL_TRIGGER <= offset && offset <= DBELL_PENDING);
return hsp->doorbell_base + db_id * HSP_DOORBELL_BLOCK_STRIDE + offset;
}
int tx2_hsp_init(ps_io_ops_t *io_ops, tx2_hsp_t *hsp)
{
if (!io_ops || !hsp) {
ZF_LOGE("Arguments are NULL!");
return -EINVAL;
}
hsp->hsp_base = ps_pmem_map(io_ops, tx2_hsp_region, false, PS_MEM_NORMAL);
if (!hsp->hsp_base) {
ZF_LOGE("Failed to map tx2 HSP module");
return -ENOMEM;
}
/* Get the base addr of the doorbell
* Section 14.8.5: All doorbell registers are in a single page, doorbell
* {db} has a register range starting at DB{db}_BASE = HSP_{inst}_BASE +
* (1+ nSM/2 + nSS + nAS) * 64 KiB + {db} * 0x100. */
int num_sm = 0, num_ss = 0, num_as = 0;
uint32_t *int_dim_reg = hsp->hsp_base + HSP_INT_DIMENSION_OFFSET;
num_sm = (*int_dim_reg >> HSP_INT_DIMENSION_SM_SHIFT) & HSP_INT_DIMENSION_NUM_MASK;
num_ss = (*int_dim_reg >> HSP_INT_DIMENSION_SS_SHIFT) & HSP_INT_DIMENSION_NUM_MASK;
num_as = (*int_dim_reg >> HSP_INT_DIMENSION_AS_SHIFT) & HSP_INT_DIMENSION_NUM_MASK;
hsp->doorbell_base = hsp->hsp_base + (1 + (num_sm / 2) + num_ss + num_as) * 0x10000;
return 0;
}
int tx2_hsp_destroy(ps_io_ops_t *io_ops, tx2_hsp_t *hsp)
{
if (!io_ops || !hsp) {
ZF_LOGE("Arguments are NULL!");
return -EINVAL;
}
if (hsp->hsp_base) {
ps_io_unmap(&io_ops->io_mapper, hsp->hsp_base, tx2_hsp_region.length);
}
return 0;
}
int tx2_hsp_doorbell_ring(tx2_hsp_t *hsp, enum tx2_doorbell_id db_id)
{
if (!hsp) {
ZF_LOGE("Arguments are NULL!");
return -EINVAL;
}
if (!check_doorbell_id_is_valid(db_id)) {
ZF_LOGE("Invalid doorbell ID!");
return -EINVAL;
}
/* Write any value to the trigger register to 'ring' the doorbell */
uint32_t *trigger_reg = tx2_hsp_get_doorbell_register(hsp, db_id, DBELL_TRIGGER);
assert(trigger_reg);
*trigger_reg = 1;
return 0;
}
int tx2_hsp_doorbell_check(tx2_hsp_t *hsp, enum tx2_doorbell_id db_id)
{
if (!hsp) {
ZF_LOGE("Arguments are NULL!");
return -EINVAL;
}
if (!check_doorbell_id_is_valid(db_id)) {
ZF_LOGE("Invalid doorbell ID!");
return -EINVAL;
}
/* Checking if the doorbell has been 'rung' requires checking for proper
* bit in the bitfield. The bitfield is also split into TrustZone secure
* and TZ non-secure. Refer to Figure 75 in Section 14.8.5 for further details. */
uint32_t *pending_reg = tx2_hsp_get_doorbell_register(hsp, db_id, DBELL_PENDING);
enum dbell_bitmap_offset bitmap_offset;
switch (db_id) {
case CCPLEX_PM_DBELL:
case CCPLEX_TZ_UNSECURE_DBELL:
case CCPLEX_TZ_SECURE_DBELL:
bitmap_offset = CCPLEX_BIT;
break;
case BPMP_DBELL:
bitmap_offset = BPMP_BIT;
break;
case SPE_DBELL:
bitmap_offset = SPE_BIT;
break;
case SCE_DBELL:
bitmap_offset = SCE_BIT;
break;
case APE_DBELL:
bitmap_offset = APE_BIT;
break;
default:
ZF_LOGF("We shouldn't get here, doorbell ID is %d", db_id);
}
/* Usermode isn't in TrustZone secure, so we just default to TZ non-secure */
int is_pending = *pending_reg & (bitmap_offset << HSP_BITMAP_TZ_NONSECURE_SHIFT);
if (is_pending) {
*pending_reg &= ~(bitmap_offset << HSP_BITMAP_TZ_NONSECURE_SHIFT);
}
return (is_pending != 0);
}