mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-15 17:14:21 +00:00
Merge "feat(mt8196): add SMMU driver for PM" into integration
This commit is contained in:
commit
0c370e2d59
3 changed files with 319 additions and 0 deletions
17
plat/mediatek/drivers/smmu/rules.mk
Normal file
17
plat/mediatek/drivers/smmu/rules.mk
Normal file
|
@ -0,0 +1,17 @@
|
|||
#
|
||||
# Copyright (c) 2025, MediaTek Inc. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(call GET_LOCAL_DIR)
|
||||
|
||||
MODULE := smmu
|
||||
|
||||
LOCAL_SRCS-y := ${LOCAL_DIR}/smmu.c
|
||||
|
||||
ifeq ($(MTK_SOC), mt8196)
|
||||
CFLAGS += -DMTK_SMMU_MT8196
|
||||
endif
|
||||
|
||||
$(eval $(call MAKE_MODULE,$(MODULE),$(LOCAL_SRCS-y),$(MTK_BL)))
|
301
plat/mediatek/drivers/smmu/smmu.c
Normal file
301
plat/mediatek/drivers/smmu/smmu.c
Normal file
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* Copyright (c) 2025, MediaTek Inc. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <common/debug.h>
|
||||
#include <lib/mmio.h>
|
||||
#include <lib/spinlock.h>
|
||||
|
||||
#include <drivers/apusys_rv_public.h>
|
||||
#include <drivers/mminfra_public.h>
|
||||
#include <lib/mtk_init/mtk_init.h>
|
||||
#include <mtk_sip_svc.h>
|
||||
|
||||
#define TAG "[MTK_SMMU]"
|
||||
|
||||
#ifdef SMMU_DBG
|
||||
#define SMMUDBG(fmt, args...) INFO(TAG fmt, ##args)
|
||||
#else
|
||||
#define SMMUDBG(fmt, args...) VERBOSE(TAG fmt, ##args)
|
||||
#endif
|
||||
|
||||
#define SMMU_SUCCESS 0
|
||||
#define SMMU_ID_ERR 1
|
||||
#define SMMU_CMD_ERR 2
|
||||
|
||||
#define F_MSK_SHIFT(val, h, l) (((val) & GENMASK(h, l)) >> (l))
|
||||
|
||||
#define SMMU_SMC_ID_H (10)
|
||||
#define SMMU_SMC_ID_L (8)
|
||||
#define SMMU_SMC_CMD_H (7)
|
||||
#define SMMU_SMC_CMD_L (0)
|
||||
|
||||
/* SMMU CMD Definition From Rich OS */
|
||||
enum smc_cmd {
|
||||
SMMU_SECURE_PM_GET,
|
||||
SMMU_SECURE_PM_PUT,
|
||||
SMMU_CMD_NUM
|
||||
};
|
||||
|
||||
enum smmu_id {
|
||||
MTK_SMMU_ID_MM,
|
||||
MTK_SMMU_ID_APU,
|
||||
MTK_SMMU_ID_SOC,
|
||||
MTK_SMMU_ID_GPU,
|
||||
MTK_SMMU_ID_NUM,
|
||||
};
|
||||
|
||||
enum cmd_source {
|
||||
SMMU_CMD_SOURCE_KERNEL = 0, /* Command comes from kernel */
|
||||
SMMU_CMD_SOURCE_TFA,
|
||||
SMMU_CMD_SOURCE_HYP, /* Command comes from hypervisor */
|
||||
SMMU_CMD_SOURCE_NUM
|
||||
};
|
||||
|
||||
struct hw_sema_t {
|
||||
enum smmu_id id;
|
||||
uint32_t vote[SMMU_CMD_SOURCE_NUM]; /* SW vote count */
|
||||
spinlock_t lock;
|
||||
bool active;
|
||||
};
|
||||
|
||||
static struct hw_sema_t *hw_semas;
|
||||
|
||||
static inline uint32_t vote_count_inc(struct hw_sema_t *sema, enum cmd_source id)
|
||||
{
|
||||
if (sema->vote[id] < UINT32_MAX) {
|
||||
sema->vote[id]++;
|
||||
return sema->vote[id];
|
||||
}
|
||||
|
||||
ERROR(TAG "%s:id:%u:source_id:%u overflow\n", __func__, sema->id, id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint32_t vote_count_dec(struct hw_sema_t *sema, enum cmd_source id)
|
||||
{
|
||||
if (sema->vote[id] > 0) {
|
||||
sema->vote[id]--;
|
||||
return sema->vote[id];
|
||||
}
|
||||
|
||||
ERROR(TAG "%s:id:%u:source_id:%u underflow\n", __func__, sema->id, id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint32_t vote_count(struct hw_sema_t *sema)
|
||||
{
|
||||
uint32_t i, count = 0;
|
||||
|
||||
for (i = 0; i < SMMU_CMD_SOURCE_NUM; i++)
|
||||
count += sema->vote[i];
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct hw_sema_t *mtk_smmu_get_hw_sema_cfg(enum smmu_id id)
|
||||
{
|
||||
if (hw_semas == NULL) {
|
||||
ERROR(TAG "%s failed, hw_sema config not ready\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (id >= MTK_SMMU_ID_NUM) {
|
||||
ERROR(TAG "%s id:%u not support\n", __func__, id);
|
||||
return NULL;
|
||||
}
|
||||
return &hw_semas[id];
|
||||
}
|
||||
|
||||
static int mm_pm_get_if_in_use(struct hw_sema_t *sema, enum cmd_source id)
|
||||
{
|
||||
uint32_t count;
|
||||
int ret;
|
||||
|
||||
ret = mminfra_get_if_in_use();
|
||||
if (ret != MMINFRA_RET_POWER_ON) {
|
||||
count = vote_count(sema);
|
||||
VERBOSE(TAG "%s:id:%u:source_id:%u:vote:%u:vote_count:%u ret:%d\n",
|
||||
__func__, sema->id, id, sema->vote[id], count, ret);
|
||||
return SMMU_CMD_ERR;
|
||||
}
|
||||
return SMMU_SUCCESS;
|
||||
}
|
||||
|
||||
static int mm_pm_put(struct hw_sema_t *sema, enum cmd_source id)
|
||||
{
|
||||
uint32_t count;
|
||||
int ret;
|
||||
|
||||
ret = mminfra_put();
|
||||
if (ret < 0) {
|
||||
count = vote_count(sema);
|
||||
VERBOSE(TAG "%s:id:%u:source_id:%u:vote:%u:vote_count:%u ret:%d\n",
|
||||
__func__, sema->id, id, sema->vote[id], count, ret);
|
||||
return SMMU_CMD_ERR;
|
||||
}
|
||||
return SMMU_SUCCESS;
|
||||
}
|
||||
|
||||
static int mtk_smmu_pm_get(enum smmu_id id, enum cmd_source source_id)
|
||||
{
|
||||
struct hw_sema_t *hw_sema = mtk_smmu_get_hw_sema_cfg(id);
|
||||
uint32_t count;
|
||||
int ret = SMMU_SUCCESS;
|
||||
|
||||
if (!hw_sema || !hw_sema->active)
|
||||
return 0; /* hw_sema not ready or support, bypass */
|
||||
|
||||
spin_lock(&hw_sema->lock);
|
||||
count = vote_count(hw_sema);
|
||||
|
||||
SMMUDBG("%s:id:%u:source_id:%u:vote:%u:vote_count:%u start\n",
|
||||
__func__, id, source_id, hw_sema->vote[source_id], count);
|
||||
|
||||
if (count > 0) {
|
||||
/* hw_sem was already got */
|
||||
vote_count_inc(hw_sema, source_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (id == MTK_SMMU_ID_APU) {
|
||||
ret = apusys_rv_iommu_hw_sem_trylock();
|
||||
} else if (id == MTK_SMMU_ID_MM) {
|
||||
ret = mm_pm_get_if_in_use(hw_sema, source_id);
|
||||
}
|
||||
|
||||
if (ret == SMMU_SUCCESS)
|
||||
vote_count_inc(hw_sema, source_id);
|
||||
|
||||
out:
|
||||
count = vote_count(hw_sema);
|
||||
SMMUDBG("%s:id:%u:source_id:%u:vote:%u:vote_count:%u end ret:%d\n",
|
||||
__func__, id, source_id, hw_sema->vote[source_id], count, ret);
|
||||
|
||||
spin_unlock(&hw_sema->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_smmu_pm_put(enum smmu_id id, enum cmd_source source_id)
|
||||
{
|
||||
struct hw_sema_t *hw_sema = mtk_smmu_get_hw_sema_cfg(id);
|
||||
uint32_t count;
|
||||
int ret = SMMU_SUCCESS;
|
||||
|
||||
if (!hw_sema || !hw_sema->active)
|
||||
return 0; /* hw_sema not ready or support, bypass */
|
||||
|
||||
spin_lock(&hw_sema->lock);
|
||||
count = vote_count(hw_sema);
|
||||
|
||||
SMMUDBG("%s:id:%u:source_id:%u:vote:%u:vote_count:%u start\n",
|
||||
__func__, id, source_id, hw_sema->vote[source_id], count);
|
||||
|
||||
if (count == 0) {
|
||||
/* hw_sem was already released */
|
||||
ERROR(TAG "%s:id:%u, hw_sem already released\n", __func__, id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (hw_sema->vote[source_id] == 0) {
|
||||
/* hw_sem was already released */
|
||||
ERROR(TAG "%s:id:%u:source_id:%u, hw_sem already released\n",
|
||||
__func__, id, source_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
vote_count_dec(hw_sema, source_id);
|
||||
count = vote_count(hw_sema);
|
||||
if (count > 0)
|
||||
goto out; /* hw_sem only vote */
|
||||
|
||||
if (id == MTK_SMMU_ID_APU) {
|
||||
ret = apusys_rv_iommu_hw_sem_unlock();
|
||||
} else if (id == MTK_SMMU_ID_MM) {
|
||||
ret = mm_pm_put(hw_sema, source_id);
|
||||
}
|
||||
out:
|
||||
SMMUDBG("%s:id:%u:source_id:%u:vote:%u:vote_count:%u end ret:%d\n",
|
||||
__func__, id, source_id, hw_sema->vote[source_id], count, ret);
|
||||
|
||||
spin_unlock(&hw_sema->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The function is used handle some request from Rich OS.
|
||||
* x1: TF-A cmd (format: sec[11:11] + smmu_id[10:8] + cmd_id[7:0])
|
||||
* x2: other parameters
|
||||
*/
|
||||
static u_register_t mtk_smmu_handler(u_register_t x1, u_register_t x2,
|
||||
u_register_t x3, u_register_t x4,
|
||||
void *handle, struct smccc_res *smccc_ret)
|
||||
{
|
||||
uint32_t ret = SMMU_CMD_ERR;
|
||||
uint32_t cmd_id = F_MSK_SHIFT(x1, SMMU_SMC_CMD_H, SMMU_SMC_CMD_L);
|
||||
enum smmu_id smmu_id = F_MSK_SHIFT(x1, SMMU_SMC_ID_H, SMMU_SMC_ID_L);
|
||||
enum cmd_source source_id = (enum cmd_source)x2;
|
||||
|
||||
if (smmu_id >= MTK_SMMU_ID_NUM || source_id >= SMMU_CMD_SOURCE_NUM)
|
||||
return SMMU_ID_ERR;
|
||||
|
||||
switch (cmd_id) {
|
||||
case SMMU_SECURE_PM_GET:
|
||||
ret = mtk_smmu_pm_get(smmu_id, source_id);
|
||||
break;
|
||||
case SMMU_SECURE_PM_PUT:
|
||||
ret = mtk_smmu_pm_put(smmu_id, source_id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
ERROR(TAG "%s, smmu_%u cmd:%u fail:%u\n", __func__, smmu_id, cmd_id, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
/* Register MTK SMMU service */
|
||||
DECLARE_SMC_HANDLER(MTK_SIP_IOMMU_CONTROL, mtk_smmu_handler);
|
||||
|
||||
#if defined(MTK_SMMU_MT8196)
|
||||
static struct hw_sema_t smmu_hw_semas[MTK_SMMU_ID_NUM] = {
|
||||
{
|
||||
.id = MTK_SMMU_ID_MM,
|
||||
.active = true,
|
||||
},
|
||||
{
|
||||
.id = MTK_SMMU_ID_APU,
|
||||
.active = true,
|
||||
},
|
||||
{
|
||||
.id = MTK_SMMU_ID_SOC,
|
||||
.active = false,
|
||||
},
|
||||
{
|
||||
.id = MTK_SMMU_ID_GPU,
|
||||
.active = true,
|
||||
},
|
||||
};
|
||||
#else
|
||||
static struct hw_sema_t *smmu_hw_semas;
|
||||
#endif
|
||||
|
||||
/* Register MTK SMMU driver setup init function */
|
||||
static int mtk_smmu_init(void)
|
||||
{
|
||||
hw_semas = smmu_hw_semas;
|
||||
|
||||
if (!hw_semas) {
|
||||
ERROR("%s: failed.\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
SMMUDBG("%s done.\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
MTK_PLAT_SETUP_0_INIT(mtk_smmu_init);
|
|
@ -48,6 +48,7 @@ MODULES-y += $(MTK_PLAT)/drivers/emi
|
|||
MODULES-y += $(MTK_PLAT)/drivers/gicv3
|
||||
MODULES-y += $(MTK_PLAT)/drivers/mcusys
|
||||
MODULES-y += $(MTK_PLAT)/drivers/mminfra
|
||||
MODULES-y += $(MTK_PLAT)/drivers/smmu
|
||||
MODULES-y += $(MTK_PLAT)/drivers/spm
|
||||
MODULES-y += $(MTK_PLAT)/drivers/timer
|
||||
MODULES-y += $(MTK_PLAT)/drivers/vcp
|
||||
|
|
Loading…
Add table
Reference in a new issue