1*86dd08d8SYong Wu /* 2*86dd08d8SYong Wu * Copyright (c) 2025, MediaTek Inc. All rights reserved. 3*86dd08d8SYong Wu * 4*86dd08d8SYong Wu * SPDX-License-Identifier: BSD-3-Clause 5*86dd08d8SYong Wu */ 6*86dd08d8SYong Wu 7*86dd08d8SYong Wu #include <assert.h> 8*86dd08d8SYong Wu #include <errno.h> 9*86dd08d8SYong Wu 10*86dd08d8SYong Wu #include <common/debug.h> 11*86dd08d8SYong Wu #include <lib/mmio.h> 12*86dd08d8SYong Wu #include <lib/spinlock.h> 13*86dd08d8SYong Wu 14*86dd08d8SYong Wu #include <drivers/apusys_rv_public.h> 15*86dd08d8SYong Wu #include <drivers/mminfra_public.h> 16*86dd08d8SYong Wu #include <lib/mtk_init/mtk_init.h> 17*86dd08d8SYong Wu #include <mtk_sip_svc.h> 18*86dd08d8SYong Wu 19*86dd08d8SYong Wu #define TAG "[MTK_SMMU]" 20*86dd08d8SYong Wu 21*86dd08d8SYong Wu #ifdef SMMU_DBG 22*86dd08d8SYong Wu #define SMMUDBG(fmt, args...) INFO(TAG fmt, ##args) 23*86dd08d8SYong Wu #else 24*86dd08d8SYong Wu #define SMMUDBG(fmt, args...) VERBOSE(TAG fmt, ##args) 25*86dd08d8SYong Wu #endif 26*86dd08d8SYong Wu 27*86dd08d8SYong Wu #define SMMU_SUCCESS 0 28*86dd08d8SYong Wu #define SMMU_ID_ERR 1 29*86dd08d8SYong Wu #define SMMU_CMD_ERR 2 30*86dd08d8SYong Wu 31*86dd08d8SYong Wu #define F_MSK_SHIFT(val, h, l) (((val) & GENMASK(h, l)) >> (l)) 32*86dd08d8SYong Wu 33*86dd08d8SYong Wu #define SMMU_SMC_ID_H (10) 34*86dd08d8SYong Wu #define SMMU_SMC_ID_L (8) 35*86dd08d8SYong Wu #define SMMU_SMC_CMD_H (7) 36*86dd08d8SYong Wu #define SMMU_SMC_CMD_L (0) 37*86dd08d8SYong Wu 38*86dd08d8SYong Wu /* SMMU CMD Definition From Rich OS */ 39*86dd08d8SYong Wu enum smc_cmd { 40*86dd08d8SYong Wu SMMU_SECURE_PM_GET, 41*86dd08d8SYong Wu SMMU_SECURE_PM_PUT, 42*86dd08d8SYong Wu SMMU_CMD_NUM 43*86dd08d8SYong Wu }; 44*86dd08d8SYong Wu 45*86dd08d8SYong Wu enum smmu_id { 46*86dd08d8SYong Wu MTK_SMMU_ID_MM, 47*86dd08d8SYong Wu MTK_SMMU_ID_APU, 48*86dd08d8SYong Wu MTK_SMMU_ID_SOC, 49*86dd08d8SYong Wu MTK_SMMU_ID_GPU, 50*86dd08d8SYong Wu MTK_SMMU_ID_NUM, 51*86dd08d8SYong Wu }; 52*86dd08d8SYong Wu 53*86dd08d8SYong Wu enum cmd_source { 54*86dd08d8SYong Wu SMMU_CMD_SOURCE_KERNEL = 0, /* Command comes from kernel */ 55*86dd08d8SYong Wu SMMU_CMD_SOURCE_TFA, 56*86dd08d8SYong Wu SMMU_CMD_SOURCE_HYP, /* Command comes from hypervisor */ 57*86dd08d8SYong Wu SMMU_CMD_SOURCE_NUM 58*86dd08d8SYong Wu }; 59*86dd08d8SYong Wu 60*86dd08d8SYong Wu struct hw_sema_t { 61*86dd08d8SYong Wu enum smmu_id id; 62*86dd08d8SYong Wu uint32_t vote[SMMU_CMD_SOURCE_NUM]; /* SW vote count */ 63*86dd08d8SYong Wu spinlock_t lock; 64*86dd08d8SYong Wu bool active; 65*86dd08d8SYong Wu }; 66*86dd08d8SYong Wu 67*86dd08d8SYong Wu static struct hw_sema_t *hw_semas; 68*86dd08d8SYong Wu 69*86dd08d8SYong Wu static inline uint32_t vote_count_inc(struct hw_sema_t *sema, enum cmd_source id) 70*86dd08d8SYong Wu { 71*86dd08d8SYong Wu if (sema->vote[id] < UINT32_MAX) { 72*86dd08d8SYong Wu sema->vote[id]++; 73*86dd08d8SYong Wu return sema->vote[id]; 74*86dd08d8SYong Wu } 75*86dd08d8SYong Wu 76*86dd08d8SYong Wu ERROR(TAG "%s:id:%u:source_id:%u overflow\n", __func__, sema->id, id); 77*86dd08d8SYong Wu return 0; 78*86dd08d8SYong Wu } 79*86dd08d8SYong Wu 80*86dd08d8SYong Wu static inline uint32_t vote_count_dec(struct hw_sema_t *sema, enum cmd_source id) 81*86dd08d8SYong Wu { 82*86dd08d8SYong Wu if (sema->vote[id] > 0) { 83*86dd08d8SYong Wu sema->vote[id]--; 84*86dd08d8SYong Wu return sema->vote[id]; 85*86dd08d8SYong Wu } 86*86dd08d8SYong Wu 87*86dd08d8SYong Wu ERROR(TAG "%s:id:%u:source_id:%u underflow\n", __func__, sema->id, id); 88*86dd08d8SYong Wu return 0; 89*86dd08d8SYong Wu } 90*86dd08d8SYong Wu 91*86dd08d8SYong Wu static inline uint32_t vote_count(struct hw_sema_t *sema) 92*86dd08d8SYong Wu { 93*86dd08d8SYong Wu uint32_t i, count = 0; 94*86dd08d8SYong Wu 95*86dd08d8SYong Wu for (i = 0; i < SMMU_CMD_SOURCE_NUM; i++) 96*86dd08d8SYong Wu count += sema->vote[i]; 97*86dd08d8SYong Wu 98*86dd08d8SYong Wu return count; 99*86dd08d8SYong Wu } 100*86dd08d8SYong Wu 101*86dd08d8SYong Wu static struct hw_sema_t *mtk_smmu_get_hw_sema_cfg(enum smmu_id id) 102*86dd08d8SYong Wu { 103*86dd08d8SYong Wu if (hw_semas == NULL) { 104*86dd08d8SYong Wu ERROR(TAG "%s failed, hw_sema config not ready\n", __func__); 105*86dd08d8SYong Wu return NULL; 106*86dd08d8SYong Wu } 107*86dd08d8SYong Wu 108*86dd08d8SYong Wu if (id >= MTK_SMMU_ID_NUM) { 109*86dd08d8SYong Wu ERROR(TAG "%s id:%u not support\n", __func__, id); 110*86dd08d8SYong Wu return NULL; 111*86dd08d8SYong Wu } 112*86dd08d8SYong Wu return &hw_semas[id]; 113*86dd08d8SYong Wu } 114*86dd08d8SYong Wu 115*86dd08d8SYong Wu static int mm_pm_get_if_in_use(struct hw_sema_t *sema, enum cmd_source id) 116*86dd08d8SYong Wu { 117*86dd08d8SYong Wu uint32_t count; 118*86dd08d8SYong Wu int ret; 119*86dd08d8SYong Wu 120*86dd08d8SYong Wu ret = mminfra_get_if_in_use(); 121*86dd08d8SYong Wu if (ret != MMINFRA_RET_POWER_ON) { 122*86dd08d8SYong Wu count = vote_count(sema); 123*86dd08d8SYong Wu VERBOSE(TAG "%s:id:%u:source_id:%u:vote:%u:vote_count:%u ret:%d\n", 124*86dd08d8SYong Wu __func__, sema->id, id, sema->vote[id], count, ret); 125*86dd08d8SYong Wu return SMMU_CMD_ERR; 126*86dd08d8SYong Wu } 127*86dd08d8SYong Wu return SMMU_SUCCESS; 128*86dd08d8SYong Wu } 129*86dd08d8SYong Wu 130*86dd08d8SYong Wu static int mm_pm_put(struct hw_sema_t *sema, enum cmd_source id) 131*86dd08d8SYong Wu { 132*86dd08d8SYong Wu uint32_t count; 133*86dd08d8SYong Wu int ret; 134*86dd08d8SYong Wu 135*86dd08d8SYong Wu ret = mminfra_put(); 136*86dd08d8SYong Wu if (ret < 0) { 137*86dd08d8SYong Wu count = vote_count(sema); 138*86dd08d8SYong Wu VERBOSE(TAG "%s:id:%u:source_id:%u:vote:%u:vote_count:%u ret:%d\n", 139*86dd08d8SYong Wu __func__, sema->id, id, sema->vote[id], count, ret); 140*86dd08d8SYong Wu return SMMU_CMD_ERR; 141*86dd08d8SYong Wu } 142*86dd08d8SYong Wu return SMMU_SUCCESS; 143*86dd08d8SYong Wu } 144*86dd08d8SYong Wu 145*86dd08d8SYong Wu static int mtk_smmu_pm_get(enum smmu_id id, enum cmd_source source_id) 146*86dd08d8SYong Wu { 147*86dd08d8SYong Wu struct hw_sema_t *hw_sema = mtk_smmu_get_hw_sema_cfg(id); 148*86dd08d8SYong Wu uint32_t count; 149*86dd08d8SYong Wu int ret = SMMU_SUCCESS; 150*86dd08d8SYong Wu 151*86dd08d8SYong Wu if (!hw_sema || !hw_sema->active) 152*86dd08d8SYong Wu return 0; /* hw_sema not ready or support, bypass */ 153*86dd08d8SYong Wu 154*86dd08d8SYong Wu spin_lock(&hw_sema->lock); 155*86dd08d8SYong Wu count = vote_count(hw_sema); 156*86dd08d8SYong Wu 157*86dd08d8SYong Wu SMMUDBG("%s:id:%u:source_id:%u:vote:%u:vote_count:%u start\n", 158*86dd08d8SYong Wu __func__, id, source_id, hw_sema->vote[source_id], count); 159*86dd08d8SYong Wu 160*86dd08d8SYong Wu if (count > 0) { 161*86dd08d8SYong Wu /* hw_sem was already got */ 162*86dd08d8SYong Wu vote_count_inc(hw_sema, source_id); 163*86dd08d8SYong Wu goto out; 164*86dd08d8SYong Wu } 165*86dd08d8SYong Wu 166*86dd08d8SYong Wu if (id == MTK_SMMU_ID_APU) { 167*86dd08d8SYong Wu ret = apusys_rv_iommu_hw_sem_trylock(); 168*86dd08d8SYong Wu } else if (id == MTK_SMMU_ID_MM) { 169*86dd08d8SYong Wu ret = mm_pm_get_if_in_use(hw_sema, source_id); 170*86dd08d8SYong Wu } 171*86dd08d8SYong Wu 172*86dd08d8SYong Wu if (ret == SMMU_SUCCESS) 173*86dd08d8SYong Wu vote_count_inc(hw_sema, source_id); 174*86dd08d8SYong Wu 175*86dd08d8SYong Wu out: 176*86dd08d8SYong Wu count = vote_count(hw_sema); 177*86dd08d8SYong Wu SMMUDBG("%s:id:%u:source_id:%u:vote:%u:vote_count:%u end ret:%d\n", 178*86dd08d8SYong Wu __func__, id, source_id, hw_sema->vote[source_id], count, ret); 179*86dd08d8SYong Wu 180*86dd08d8SYong Wu spin_unlock(&hw_sema->lock); 181*86dd08d8SYong Wu return ret; 182*86dd08d8SYong Wu } 183*86dd08d8SYong Wu 184*86dd08d8SYong Wu static int mtk_smmu_pm_put(enum smmu_id id, enum cmd_source source_id) 185*86dd08d8SYong Wu { 186*86dd08d8SYong Wu struct hw_sema_t *hw_sema = mtk_smmu_get_hw_sema_cfg(id); 187*86dd08d8SYong Wu uint32_t count; 188*86dd08d8SYong Wu int ret = SMMU_SUCCESS; 189*86dd08d8SYong Wu 190*86dd08d8SYong Wu if (!hw_sema || !hw_sema->active) 191*86dd08d8SYong Wu return 0; /* hw_sema not ready or support, bypass */ 192*86dd08d8SYong Wu 193*86dd08d8SYong Wu spin_lock(&hw_sema->lock); 194*86dd08d8SYong Wu count = vote_count(hw_sema); 195*86dd08d8SYong Wu 196*86dd08d8SYong Wu SMMUDBG("%s:id:%u:source_id:%u:vote:%u:vote_count:%u start\n", 197*86dd08d8SYong Wu __func__, id, source_id, hw_sema->vote[source_id], count); 198*86dd08d8SYong Wu 199*86dd08d8SYong Wu if (count == 0) { 200*86dd08d8SYong Wu /* hw_sem was already released */ 201*86dd08d8SYong Wu ERROR(TAG "%s:id:%u, hw_sem already released\n", __func__, id); 202*86dd08d8SYong Wu goto out; 203*86dd08d8SYong Wu } 204*86dd08d8SYong Wu 205*86dd08d8SYong Wu if (hw_sema->vote[source_id] == 0) { 206*86dd08d8SYong Wu /* hw_sem was already released */ 207*86dd08d8SYong Wu ERROR(TAG "%s:id:%u:source_id:%u, hw_sem already released\n", 208*86dd08d8SYong Wu __func__, id, source_id); 209*86dd08d8SYong Wu goto out; 210*86dd08d8SYong Wu } 211*86dd08d8SYong Wu 212*86dd08d8SYong Wu vote_count_dec(hw_sema, source_id); 213*86dd08d8SYong Wu count = vote_count(hw_sema); 214*86dd08d8SYong Wu if (count > 0) 215*86dd08d8SYong Wu goto out; /* hw_sem only vote */ 216*86dd08d8SYong Wu 217*86dd08d8SYong Wu if (id == MTK_SMMU_ID_APU) { 218*86dd08d8SYong Wu ret = apusys_rv_iommu_hw_sem_unlock(); 219*86dd08d8SYong Wu } else if (id == MTK_SMMU_ID_MM) { 220*86dd08d8SYong Wu ret = mm_pm_put(hw_sema, source_id); 221*86dd08d8SYong Wu } 222*86dd08d8SYong Wu out: 223*86dd08d8SYong Wu SMMUDBG("%s:id:%u:source_id:%u:vote:%u:vote_count:%u end ret:%d\n", 224*86dd08d8SYong Wu __func__, id, source_id, hw_sema->vote[source_id], count, ret); 225*86dd08d8SYong Wu 226*86dd08d8SYong Wu spin_unlock(&hw_sema->lock); 227*86dd08d8SYong Wu return ret; 228*86dd08d8SYong Wu } 229*86dd08d8SYong Wu 230*86dd08d8SYong Wu /* 231*86dd08d8SYong Wu * The function is used handle some request from Rich OS. 232*86dd08d8SYong Wu * x1: TF-A cmd (format: sec[11:11] + smmu_id[10:8] + cmd_id[7:0]) 233*86dd08d8SYong Wu * x2: other parameters 234*86dd08d8SYong Wu */ 235*86dd08d8SYong Wu static u_register_t mtk_smmu_handler(u_register_t x1, u_register_t x2, 236*86dd08d8SYong Wu u_register_t x3, u_register_t x4, 237*86dd08d8SYong Wu void *handle, struct smccc_res *smccc_ret) 238*86dd08d8SYong Wu { 239*86dd08d8SYong Wu uint32_t ret = SMMU_CMD_ERR; 240*86dd08d8SYong Wu uint32_t cmd_id = F_MSK_SHIFT(x1, SMMU_SMC_CMD_H, SMMU_SMC_CMD_L); 241*86dd08d8SYong Wu enum smmu_id smmu_id = F_MSK_SHIFT(x1, SMMU_SMC_ID_H, SMMU_SMC_ID_L); 242*86dd08d8SYong Wu enum cmd_source source_id = (enum cmd_source)x2; 243*86dd08d8SYong Wu 244*86dd08d8SYong Wu if (smmu_id >= MTK_SMMU_ID_NUM || source_id >= SMMU_CMD_SOURCE_NUM) 245*86dd08d8SYong Wu return SMMU_ID_ERR; 246*86dd08d8SYong Wu 247*86dd08d8SYong Wu switch (cmd_id) { 248*86dd08d8SYong Wu case SMMU_SECURE_PM_GET: 249*86dd08d8SYong Wu ret = mtk_smmu_pm_get(smmu_id, source_id); 250*86dd08d8SYong Wu break; 251*86dd08d8SYong Wu case SMMU_SECURE_PM_PUT: 252*86dd08d8SYong Wu ret = mtk_smmu_pm_put(smmu_id, source_id); 253*86dd08d8SYong Wu break; 254*86dd08d8SYong Wu default: 255*86dd08d8SYong Wu break; 256*86dd08d8SYong Wu } 257*86dd08d8SYong Wu 258*86dd08d8SYong Wu if (ret) 259*86dd08d8SYong Wu ERROR(TAG "%s, smmu_%u cmd:%u fail:%u\n", __func__, smmu_id, cmd_id, ret); 260*86dd08d8SYong Wu 261*86dd08d8SYong Wu return ret; 262*86dd08d8SYong Wu } 263*86dd08d8SYong Wu /* Register MTK SMMU service */ 264*86dd08d8SYong Wu DECLARE_SMC_HANDLER(MTK_SIP_IOMMU_CONTROL, mtk_smmu_handler); 265*86dd08d8SYong Wu 266*86dd08d8SYong Wu #if defined(MTK_SMMU_MT8196) 267*86dd08d8SYong Wu static struct hw_sema_t smmu_hw_semas[MTK_SMMU_ID_NUM] = { 268*86dd08d8SYong Wu { 269*86dd08d8SYong Wu .id = MTK_SMMU_ID_MM, 270*86dd08d8SYong Wu .active = true, 271*86dd08d8SYong Wu }, 272*86dd08d8SYong Wu { 273*86dd08d8SYong Wu .id = MTK_SMMU_ID_APU, 274*86dd08d8SYong Wu .active = true, 275*86dd08d8SYong Wu }, 276*86dd08d8SYong Wu { 277*86dd08d8SYong Wu .id = MTK_SMMU_ID_SOC, 278*86dd08d8SYong Wu .active = false, 279*86dd08d8SYong Wu }, 280*86dd08d8SYong Wu { 281*86dd08d8SYong Wu .id = MTK_SMMU_ID_GPU, 282*86dd08d8SYong Wu .active = true, 283*86dd08d8SYong Wu }, 284*86dd08d8SYong Wu }; 285*86dd08d8SYong Wu #else 286*86dd08d8SYong Wu static struct hw_sema_t *smmu_hw_semas; 287*86dd08d8SYong Wu #endif 288*86dd08d8SYong Wu 289*86dd08d8SYong Wu /* Register MTK SMMU driver setup init function */ 290*86dd08d8SYong Wu static int mtk_smmu_init(void) 291*86dd08d8SYong Wu { 292*86dd08d8SYong Wu hw_semas = smmu_hw_semas; 293*86dd08d8SYong Wu 294*86dd08d8SYong Wu if (!hw_semas) { 295*86dd08d8SYong Wu ERROR("%s: failed.\n", __func__); 296*86dd08d8SYong Wu return -ENODEV; 297*86dd08d8SYong Wu } 298*86dd08d8SYong Wu SMMUDBG("%s done.\n", __func__); 299*86dd08d8SYong Wu return 0; 300*86dd08d8SYong Wu } 301*86dd08d8SYong Wu MTK_PLAT_SETUP_0_INIT(mtk_smmu_init); 302