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
vote_count_inc(struct hw_sema_t * sema,enum cmd_source id)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
vote_count_dec(struct hw_sema_t * sema,enum cmd_source id)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
vote_count(struct hw_sema_t * sema)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
mtk_smmu_get_hw_sema_cfg(enum smmu_id id)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
mm_pm_get_if_in_use(struct hw_sema_t * sema,enum cmd_source id)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
mm_pm_put(struct hw_sema_t * sema,enum cmd_source id)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
mtk_smmu_pm_get(enum smmu_id id,enum cmd_source source_id)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
mtk_smmu_pm_put(enum smmu_id id,enum cmd_source source_id)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 */
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)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 return ret;
259*86dd08d8SYong Wu }
260*86dd08d8SYong Wu /* Register MTK SMMU service */
261*86dd08d8SYong Wu DECLARE_SMC_HANDLER(MTK_SIP_IOMMU_CONTROL, mtk_smmu_handler);
262*86dd08d8SYong Wu
263*86dd08d8SYong Wu #if defined(MTK_SMMU_MT8196)
264*86dd08d8SYong Wu static struct hw_sema_t smmu_hw_semas[MTK_SMMU_ID_NUM] = {
265*86dd08d8SYong Wu {
266*86dd08d8SYong Wu .id = MTK_SMMU_ID_MM,
267*86dd08d8SYong Wu .active = true,
268*86dd08d8SYong Wu },
269*86dd08d8SYong Wu {
270*86dd08d8SYong Wu .id = MTK_SMMU_ID_APU,
271*86dd08d8SYong Wu .active = true,
272*86dd08d8SYong Wu },
273*86dd08d8SYong Wu {
274*86dd08d8SYong Wu .id = MTK_SMMU_ID_SOC,
275*86dd08d8SYong Wu .active = false,
276*86dd08d8SYong Wu },
277*86dd08d8SYong Wu {
278*86dd08d8SYong Wu .id = MTK_SMMU_ID_GPU,
279*86dd08d8SYong Wu .active = true,
280*86dd08d8SYong Wu },
281*86dd08d8SYong Wu };
282*86dd08d8SYong Wu #else
283*86dd08d8SYong Wu static struct hw_sema_t *smmu_hw_semas;
284*86dd08d8SYong Wu #endif
285*86dd08d8SYong Wu
286*86dd08d8SYong Wu /* Register MTK SMMU driver setup init function */
mtk_smmu_init(void)287*86dd08d8SYong Wu static int mtk_smmu_init(void)
288*86dd08d8SYong Wu {
289*86dd08d8SYong Wu hw_semas = smmu_hw_semas;
290*86dd08d8SYong Wu
291*86dd08d8SYong Wu if (!hw_semas) {
292*86dd08d8SYong Wu ERROR("%s: failed.\n", __func__);
293*86dd08d8SYong Wu return -ENODEV;
294*86dd08d8SYong Wu }
295*86dd08d8SYong Wu SMMUDBG("%s done.\n", __func__);
296*86dd08d8SYong Wu return 0;
297*86dd08d8SYong Wu }
298*86dd08d8SYong Wu MTK_PLAT_SETUP_0_INIT(mtk_smmu_init);
299