xref: /rk3399_ARM-atf/plat/mediatek/drivers/smmu/smmu.c (revision 7d196ded502d49a2c170fc0f30c8f4b94584d5fe)
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