diff --git a/plat/mediatek/drivers/smmu/rules.mk b/plat/mediatek/drivers/smmu/rules.mk new file mode 100644 index 000000000..76086d966 --- /dev/null +++ b/plat/mediatek/drivers/smmu/rules.mk @@ -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))) diff --git a/plat/mediatek/drivers/smmu/smmu.c b/plat/mediatek/drivers/smmu/smmu.c new file mode 100644 index 000000000..4d3107106 --- /dev/null +++ b/plat/mediatek/drivers/smmu/smmu.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2025, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#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); diff --git a/plat/mediatek/mt8196/platform.mk b/plat/mediatek/mt8196/platform.mk index 3a459c204..50508093a 100644 --- a/plat/mediatek/mt8196/platform.mk +++ b/plat/mediatek/mt8196/platform.mk @@ -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