xref: /openbmc/linux/drivers/memory/mtk-smi.c (revision 8d2c749e)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cc8bbe1aSYong Wu /*
3cc8bbe1aSYong Wu  * Copyright (c) 2015-2016 MediaTek Inc.
4cc8bbe1aSYong Wu  * Author: Yong Wu <yong.wu@mediatek.com>
5cc8bbe1aSYong Wu  */
6cc8bbe1aSYong Wu #include <linux/clk.h>
7cc8bbe1aSYong Wu #include <linux/component.h>
8cc8bbe1aSYong Wu #include <linux/device.h>
9cc8bbe1aSYong Wu #include <linux/err.h>
10cc8bbe1aSYong Wu #include <linux/io.h>
114f608d38SYong Wu #include <linux/module.h>
12cc8bbe1aSYong Wu #include <linux/of.h>
13cc8bbe1aSYong Wu #include <linux/of_platform.h>
14cc8bbe1aSYong Wu #include <linux/platform_device.h>
15cc8bbe1aSYong Wu #include <linux/pm_runtime.h>
16cc8bbe1aSYong Wu #include <soc/mediatek/smi.h>
173c8f4ad8SHonghui Zhang #include <dt-bindings/memory/mt2701-larb-port.h>
1866a28915SYong Wu #include <dt-bindings/memory/mtk-memory-port.h>
19cc8bbe1aSYong Wu 
20e6dec923SYong Wu /* mt8173 */
21cc8bbe1aSYong Wu #define SMI_LARB_MMU_EN		0xf00
22e6dec923SYong Wu 
23a8529f3bSFabien Parent /* mt8167 */
24a8529f3bSFabien Parent #define MT8167_SMI_LARB_MMU_EN	0xfc0
25a8529f3bSFabien Parent 
26e6dec923SYong Wu /* mt2701 */
273c8f4ad8SHonghui Zhang #define REG_SMI_SECUR_CON_BASE		0x5c0
283c8f4ad8SHonghui Zhang 
293c8f4ad8SHonghui Zhang /* every register control 8 port, register offset 0x4 */
303c8f4ad8SHonghui Zhang #define REG_SMI_SECUR_CON_OFFSET(id)	(((id) >> 3) << 2)
313c8f4ad8SHonghui Zhang #define REG_SMI_SECUR_CON_ADDR(id)	\
323c8f4ad8SHonghui Zhang 	(REG_SMI_SECUR_CON_BASE + REG_SMI_SECUR_CON_OFFSET(id))
333c8f4ad8SHonghui Zhang 
343c8f4ad8SHonghui Zhang /*
353c8f4ad8SHonghui Zhang  * every port have 4 bit to control, bit[port + 3] control virtual or physical,
363c8f4ad8SHonghui Zhang  * bit[port + 2 : port + 1] control the domain, bit[port] control the security
373c8f4ad8SHonghui Zhang  * or non-security.
383c8f4ad8SHonghui Zhang  */
393c8f4ad8SHonghui Zhang #define SMI_SECUR_CON_VAL_MSK(id)	(~(0xf << (((id) & 0x7) << 2)))
403c8f4ad8SHonghui Zhang #define SMI_SECUR_CON_VAL_VIRT(id)	BIT((((id) & 0x7) << 2) + 3)
413c8f4ad8SHonghui Zhang /* mt2701 domain should be set to 3 */
423c8f4ad8SHonghui Zhang #define SMI_SECUR_CON_VAL_DOMAIN(id)	(0x3 << ((((id) & 0x7) << 2) + 1))
433c8f4ad8SHonghui Zhang 
44e6dec923SYong Wu /* mt2712 */
45e6dec923SYong Wu #define SMI_LARB_NONSEC_CON(id)	(0x380 + ((id) * 4))
46e6dec923SYong Wu #define F_MMU_EN		BIT(0)
47*8d2c749eSYong Wu #define BANK_SEL(id)		({			\
48*8d2c749eSYong Wu 	u32 _id = (id) & 0x3;				\
49*8d2c749eSYong Wu 	(_id << 8 | _id << 10 | _id << 12 | _id << 14);	\
50*8d2c749eSYong Wu })
51e6dec923SYong Wu 
52567e58cfSYong Wu /* SMI COMMON */
53567e58cfSYong Wu #define SMI_BUS_SEL			0x220
54567e58cfSYong Wu #define SMI_BUS_LARB_SHIFT(larbid)	((larbid) << 1)
55567e58cfSYong Wu /* All are MMU0 defaultly. Only specialize mmu1 here. */
56567e58cfSYong Wu #define F_MMU1_LARB(larbid)		(0x1 << SMI_BUS_LARB_SHIFT(larbid))
57567e58cfSYong Wu 
5842d42c76SYong Wu enum mtk_smi_gen {
5942d42c76SYong Wu 	MTK_SMI_GEN1,
6042d42c76SYong Wu 	MTK_SMI_GEN2
6142d42c76SYong Wu };
6242d42c76SYong Wu 
6342d42c76SYong Wu struct mtk_smi_common_plat {
6442d42c76SYong Wu 	enum mtk_smi_gen gen;
6564fea74aSYong Wu 	bool             has_gals;
66567e58cfSYong Wu 	u32              bus_sel; /* Balance some larbs to enter mmu0 or mmu1 */
6742d42c76SYong Wu };
6842d42c76SYong Wu 
693c8f4ad8SHonghui Zhang struct mtk_smi_larb_gen {
703c8f4ad8SHonghui Zhang 	int port_in_larb[MTK_LARB_NR_MAX + 1];
713aa5a6c2SKrzysztof Kozlowski 	void (*config_port)(struct device *dev);
722e9b0908SYong Wu 	unsigned int			larb_direct_to_common_mask;
7364fea74aSYong Wu 	bool				has_gals;
743c8f4ad8SHonghui Zhang };
75cc8bbe1aSYong Wu 
76cc8bbe1aSYong Wu struct mtk_smi {
77cc8bbe1aSYong Wu 	struct device			*dev;
78cc8bbe1aSYong Wu 	struct clk			*clk_apb, *clk_smi;
7964fea74aSYong Wu 	struct clk			*clk_gals0, *clk_gals1;
803c8f4ad8SHonghui Zhang 	struct clk			*clk_async; /*only needed by mt2701*/
81567e58cfSYong Wu 	union {
82567e58cfSYong Wu 		void __iomem		*smi_ao_base; /* only for gen1 */
83567e58cfSYong Wu 		void __iomem		*base;	      /* only for gen2 */
84567e58cfSYong Wu 	};
8542d42c76SYong Wu 	const struct mtk_smi_common_plat *plat;
86cc8bbe1aSYong Wu };
87cc8bbe1aSYong Wu 
88cc8bbe1aSYong Wu struct mtk_smi_larb { /* larb: local arbiter */
89cc8bbe1aSYong Wu 	struct mtk_smi			smi;
90cc8bbe1aSYong Wu 	void __iomem			*base;
91cc8bbe1aSYong Wu 	struct device			*smi_common_dev;
923c8f4ad8SHonghui Zhang 	const struct mtk_smi_larb_gen	*larb_gen;
933c8f4ad8SHonghui Zhang 	int				larbid;
94cc8bbe1aSYong Wu 	u32				*mmu;
95*8d2c749eSYong Wu 	unsigned char			*bank;
96cc8bbe1aSYong Wu };
97cc8bbe1aSYong Wu 
984f0a1a1aSYong Wu static int mtk_smi_clk_enable(const struct mtk_smi *smi)
99cc8bbe1aSYong Wu {
100cc8bbe1aSYong Wu 	int ret;
101cc8bbe1aSYong Wu 
102cc8bbe1aSYong Wu 	ret = clk_prepare_enable(smi->clk_apb);
103cc8bbe1aSYong Wu 	if (ret)
1044f0a1a1aSYong Wu 		return ret;
105cc8bbe1aSYong Wu 
106cc8bbe1aSYong Wu 	ret = clk_prepare_enable(smi->clk_smi);
107cc8bbe1aSYong Wu 	if (ret)
108cc8bbe1aSYong Wu 		goto err_disable_apb;
109cc8bbe1aSYong Wu 
11064fea74aSYong Wu 	ret = clk_prepare_enable(smi->clk_gals0);
11164fea74aSYong Wu 	if (ret)
11264fea74aSYong Wu 		goto err_disable_smi;
11364fea74aSYong Wu 
11464fea74aSYong Wu 	ret = clk_prepare_enable(smi->clk_gals1);
11564fea74aSYong Wu 	if (ret)
11664fea74aSYong Wu 		goto err_disable_gals0;
11764fea74aSYong Wu 
118cc8bbe1aSYong Wu 	return 0;
119cc8bbe1aSYong Wu 
12064fea74aSYong Wu err_disable_gals0:
12164fea74aSYong Wu 	clk_disable_unprepare(smi->clk_gals0);
12264fea74aSYong Wu err_disable_smi:
12364fea74aSYong Wu 	clk_disable_unprepare(smi->clk_smi);
124cc8bbe1aSYong Wu err_disable_apb:
125cc8bbe1aSYong Wu 	clk_disable_unprepare(smi->clk_apb);
126cc8bbe1aSYong Wu 	return ret;
127cc8bbe1aSYong Wu }
128cc8bbe1aSYong Wu 
1294f0a1a1aSYong Wu static void mtk_smi_clk_disable(const struct mtk_smi *smi)
130cc8bbe1aSYong Wu {
13164fea74aSYong Wu 	clk_disable_unprepare(smi->clk_gals1);
13264fea74aSYong Wu 	clk_disable_unprepare(smi->clk_gals0);
133cc8bbe1aSYong Wu 	clk_disable_unprepare(smi->clk_smi);
134cc8bbe1aSYong Wu 	clk_disable_unprepare(smi->clk_apb);
135cc8bbe1aSYong Wu }
136cc8bbe1aSYong Wu 
137cc8bbe1aSYong Wu int mtk_smi_larb_get(struct device *larbdev)
138cc8bbe1aSYong Wu {
1394f0a1a1aSYong Wu 	int ret = pm_runtime_get_sync(larbdev);
140cc8bbe1aSYong Wu 
1414f0a1a1aSYong Wu 	return (ret < 0) ? ret : 0;
142cc8bbe1aSYong Wu }
143cb1b5dffSPhilipp Zabel EXPORT_SYMBOL_GPL(mtk_smi_larb_get);
144cc8bbe1aSYong Wu 
145cc8bbe1aSYong Wu void mtk_smi_larb_put(struct device *larbdev)
146cc8bbe1aSYong Wu {
1474f0a1a1aSYong Wu 	pm_runtime_put_sync(larbdev);
148cc8bbe1aSYong Wu }
149cb1b5dffSPhilipp Zabel EXPORT_SYMBOL_GPL(mtk_smi_larb_put);
150cc8bbe1aSYong Wu 
151cc8bbe1aSYong Wu static int
152cc8bbe1aSYong Wu mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
153cc8bbe1aSYong Wu {
154cc8bbe1aSYong Wu 	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
1551ee9feb2SYong Wu 	struct mtk_smi_larb_iommu *larb_mmu = data;
156cc8bbe1aSYong Wu 	unsigned int         i;
157cc8bbe1aSYong Wu 
158ec2da07cSYong Wu 	for (i = 0; i < MTK_LARB_NR_MAX; i++) {
1591ee9feb2SYong Wu 		if (dev == larb_mmu[i].dev) {
160ec2da07cSYong Wu 			larb->larbid = i;
1611ee9feb2SYong Wu 			larb->mmu = &larb_mmu[i].mmu;
162*8d2c749eSYong Wu 			larb->bank = larb_mmu[i].bank;
163cc8bbe1aSYong Wu 			return 0;
164cc8bbe1aSYong Wu 		}
165cc8bbe1aSYong Wu 	}
166cc8bbe1aSYong Wu 	return -ENODEV;
167cc8bbe1aSYong Wu }
168cc8bbe1aSYong Wu 
1692e9b0908SYong Wu static void mtk_smi_larb_config_port_gen2_general(struct device *dev)
170e6dec923SYong Wu {
171e6dec923SYong Wu 	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
172e6dec923SYong Wu 	u32 reg;
173e6dec923SYong Wu 	int i;
174e6dec923SYong Wu 
1752e9b0908SYong Wu 	if (BIT(larb->larbid) & larb->larb_gen->larb_direct_to_common_mask)
176e6dec923SYong Wu 		return;
177e6dec923SYong Wu 
178e6dec923SYong Wu 	for_each_set_bit(i, (unsigned long *)larb->mmu, 32) {
179e6dec923SYong Wu 		reg = readl_relaxed(larb->base + SMI_LARB_NONSEC_CON(i));
180e6dec923SYong Wu 		reg |= F_MMU_EN;
181*8d2c749eSYong Wu 		reg |= BANK_SEL(larb->bank[i]);
182e6dec923SYong Wu 		writel(reg, larb->base + SMI_LARB_NONSEC_CON(i));
183e6dec923SYong Wu 	}
184e6dec923SYong Wu }
185e6dec923SYong Wu 
186e6dec923SYong Wu static void mtk_smi_larb_config_port_mt8173(struct device *dev)
1873c8f4ad8SHonghui Zhang {
1883c8f4ad8SHonghui Zhang 	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
1893c8f4ad8SHonghui Zhang 
1903c8f4ad8SHonghui Zhang 	writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN);
1913c8f4ad8SHonghui Zhang }
1923c8f4ad8SHonghui Zhang 
193a8529f3bSFabien Parent static void mtk_smi_larb_config_port_mt8167(struct device *dev)
194a8529f3bSFabien Parent {
195a8529f3bSFabien Parent 	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
196a8529f3bSFabien Parent 
197a8529f3bSFabien Parent 	writel(*larb->mmu, larb->base + MT8167_SMI_LARB_MMU_EN);
198a8529f3bSFabien Parent }
199a8529f3bSFabien Parent 
2003c8f4ad8SHonghui Zhang static void mtk_smi_larb_config_port_gen1(struct device *dev)
2013c8f4ad8SHonghui Zhang {
2023c8f4ad8SHonghui Zhang 	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
2033c8f4ad8SHonghui Zhang 	const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
2043c8f4ad8SHonghui Zhang 	struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev);
2053c8f4ad8SHonghui Zhang 	int i, m4u_port_id, larb_port_num;
2063c8f4ad8SHonghui Zhang 	u32 sec_con_val, reg_val;
2073c8f4ad8SHonghui Zhang 
2083c8f4ad8SHonghui Zhang 	m4u_port_id = larb_gen->port_in_larb[larb->larbid];
2093c8f4ad8SHonghui Zhang 	larb_port_num = larb_gen->port_in_larb[larb->larbid + 1]
2103c8f4ad8SHonghui Zhang 			- larb_gen->port_in_larb[larb->larbid];
2113c8f4ad8SHonghui Zhang 
2123c8f4ad8SHonghui Zhang 	for (i = 0; i < larb_port_num; i++, m4u_port_id++) {
2133c8f4ad8SHonghui Zhang 		if (*larb->mmu & BIT(i)) {
2143c8f4ad8SHonghui Zhang 			/* bit[port + 3] controls the virtual or physical */
2153c8f4ad8SHonghui Zhang 			sec_con_val = SMI_SECUR_CON_VAL_VIRT(m4u_port_id);
2163c8f4ad8SHonghui Zhang 		} else {
2173c8f4ad8SHonghui Zhang 			/* do not need to enable m4u for this port */
2183c8f4ad8SHonghui Zhang 			continue;
2193c8f4ad8SHonghui Zhang 		}
2203c8f4ad8SHonghui Zhang 		reg_val = readl(common->smi_ao_base
2213c8f4ad8SHonghui Zhang 			+ REG_SMI_SECUR_CON_ADDR(m4u_port_id));
2223c8f4ad8SHonghui Zhang 		reg_val &= SMI_SECUR_CON_VAL_MSK(m4u_port_id);
2233c8f4ad8SHonghui Zhang 		reg_val |= sec_con_val;
2243c8f4ad8SHonghui Zhang 		reg_val |= SMI_SECUR_CON_VAL_DOMAIN(m4u_port_id);
2253c8f4ad8SHonghui Zhang 		writel(reg_val,
2263c8f4ad8SHonghui Zhang 			common->smi_ao_base
2273c8f4ad8SHonghui Zhang 			+ REG_SMI_SECUR_CON_ADDR(m4u_port_id));
2283c8f4ad8SHonghui Zhang 	}
2293c8f4ad8SHonghui Zhang }
2303c8f4ad8SHonghui Zhang 
231cc8bbe1aSYong Wu static void
232cc8bbe1aSYong Wu mtk_smi_larb_unbind(struct device *dev, struct device *master, void *data)
233cc8bbe1aSYong Wu {
234cc8bbe1aSYong Wu 	/* Do nothing as the iommu is always enabled. */
235cc8bbe1aSYong Wu }
236cc8bbe1aSYong Wu 
237cc8bbe1aSYong Wu static const struct component_ops mtk_smi_larb_component_ops = {
238cc8bbe1aSYong Wu 	.bind = mtk_smi_larb_bind,
239cc8bbe1aSYong Wu 	.unbind = mtk_smi_larb_unbind,
240cc8bbe1aSYong Wu };
241cc8bbe1aSYong Wu 
2423c8f4ad8SHonghui Zhang static const struct mtk_smi_larb_gen mtk_smi_larb_mt8173 = {
2433c8f4ad8SHonghui Zhang 	/* mt8173 do not need the port in larb */
244e6dec923SYong Wu 	.config_port = mtk_smi_larb_config_port_mt8173,
2453c8f4ad8SHonghui Zhang };
2463c8f4ad8SHonghui Zhang 
247a8529f3bSFabien Parent static const struct mtk_smi_larb_gen mtk_smi_larb_mt8167 = {
248a8529f3bSFabien Parent 	/* mt8167 do not need the port in larb */
249a8529f3bSFabien Parent 	.config_port = mtk_smi_larb_config_port_mt8167,
250a8529f3bSFabien Parent };
251a8529f3bSFabien Parent 
2523c8f4ad8SHonghui Zhang static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = {
2533c8f4ad8SHonghui Zhang 	.port_in_larb = {
2543c8f4ad8SHonghui Zhang 		LARB0_PORT_OFFSET, LARB1_PORT_OFFSET,
2553c8f4ad8SHonghui Zhang 		LARB2_PORT_OFFSET, LARB3_PORT_OFFSET
2563c8f4ad8SHonghui Zhang 	},
2573c8f4ad8SHonghui Zhang 	.config_port = mtk_smi_larb_config_port_gen1,
2583c8f4ad8SHonghui Zhang };
2593c8f4ad8SHonghui Zhang 
260e6dec923SYong Wu static const struct mtk_smi_larb_gen mtk_smi_larb_mt2712 = {
2612e9b0908SYong Wu 	.config_port                = mtk_smi_larb_config_port_gen2_general,
2622e9b0908SYong Wu 	.larb_direct_to_common_mask = BIT(8) | BIT(9),      /* bdpsys */
263e6dec923SYong Wu };
264e6dec923SYong Wu 
265fc492f33SMing-Fan Chen static const struct mtk_smi_larb_gen mtk_smi_larb_mt6779 = {
266fc492f33SMing-Fan Chen 	.config_port  = mtk_smi_larb_config_port_gen2_general,
267fc492f33SMing-Fan Chen 	.larb_direct_to_common_mask =
268fc492f33SMing-Fan Chen 		BIT(4) | BIT(6) | BIT(11) | BIT(12) | BIT(13),
269fc492f33SMing-Fan Chen 		/* DUMMY | IPU0 | IPU1 | CCU | MDLA */
270fc492f33SMing-Fan Chen };
271fc492f33SMing-Fan Chen 
272907ba6a1SYong Wu static const struct mtk_smi_larb_gen mtk_smi_larb_mt8183 = {
273907ba6a1SYong Wu 	.has_gals                   = true,
274907ba6a1SYong Wu 	.config_port                = mtk_smi_larb_config_port_gen2_general,
275907ba6a1SYong Wu 	.larb_direct_to_common_mask = BIT(2) | BIT(3) | BIT(7),
276907ba6a1SYong Wu 				      /* IPU0 | IPU1 | CCU */
277907ba6a1SYong Wu };
278907ba6a1SYong Wu 
27902c02ddcSYong Wu static const struct mtk_smi_larb_gen mtk_smi_larb_mt8192 = {
28002c02ddcSYong Wu 	.config_port                = mtk_smi_larb_config_port_gen2_general,
28102c02ddcSYong Wu };
28202c02ddcSYong Wu 
2833c8f4ad8SHonghui Zhang static const struct of_device_id mtk_smi_larb_of_ids[] = {
2843c8f4ad8SHonghui Zhang 	{
285a8529f3bSFabien Parent 		.compatible = "mediatek,mt8167-smi-larb",
286a8529f3bSFabien Parent 		.data = &mtk_smi_larb_mt8167
287a8529f3bSFabien Parent 	},
288a8529f3bSFabien Parent 	{
2893c8f4ad8SHonghui Zhang 		.compatible = "mediatek,mt8173-smi-larb",
2903c8f4ad8SHonghui Zhang 		.data = &mtk_smi_larb_mt8173
2913c8f4ad8SHonghui Zhang 	},
2923c8f4ad8SHonghui Zhang 	{
2933c8f4ad8SHonghui Zhang 		.compatible = "mediatek,mt2701-smi-larb",
2943c8f4ad8SHonghui Zhang 		.data = &mtk_smi_larb_mt2701
2953c8f4ad8SHonghui Zhang 	},
296e6dec923SYong Wu 	{
297e6dec923SYong Wu 		.compatible = "mediatek,mt2712-smi-larb",
298e6dec923SYong Wu 		.data = &mtk_smi_larb_mt2712
299e6dec923SYong Wu 	},
300907ba6a1SYong Wu 	{
301fc492f33SMing-Fan Chen 		.compatible = "mediatek,mt6779-smi-larb",
302fc492f33SMing-Fan Chen 		.data = &mtk_smi_larb_mt6779
303fc492f33SMing-Fan Chen 	},
304fc492f33SMing-Fan Chen 	{
305907ba6a1SYong Wu 		.compatible = "mediatek,mt8183-smi-larb",
306907ba6a1SYong Wu 		.data = &mtk_smi_larb_mt8183
307907ba6a1SYong Wu 	},
30802c02ddcSYong Wu 	{
30902c02ddcSYong Wu 		.compatible = "mediatek,mt8192-smi-larb",
31002c02ddcSYong Wu 		.data = &mtk_smi_larb_mt8192
31102c02ddcSYong Wu 	},
3123c8f4ad8SHonghui Zhang 	{}
3133c8f4ad8SHonghui Zhang };
3143c8f4ad8SHonghui Zhang 
315cc8bbe1aSYong Wu static int mtk_smi_larb_probe(struct platform_device *pdev)
316cc8bbe1aSYong Wu {
317cc8bbe1aSYong Wu 	struct mtk_smi_larb *larb;
318cc8bbe1aSYong Wu 	struct resource *res;
319cc8bbe1aSYong Wu 	struct device *dev = &pdev->dev;
320cc8bbe1aSYong Wu 	struct device_node *smi_node;
321cc8bbe1aSYong Wu 	struct platform_device *smi_pdev;
322cc8bbe1aSYong Wu 
323cc8bbe1aSYong Wu 	larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL);
324cc8bbe1aSYong Wu 	if (!larb)
325cc8bbe1aSYong Wu 		return -ENOMEM;
326cc8bbe1aSYong Wu 
32775487860SHonghui Zhang 	larb->larb_gen = of_device_get_match_data(dev);
328cc8bbe1aSYong Wu 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
329cc8bbe1aSYong Wu 	larb->base = devm_ioremap_resource(dev, res);
330cc8bbe1aSYong Wu 	if (IS_ERR(larb->base))
331cc8bbe1aSYong Wu 		return PTR_ERR(larb->base);
332cc8bbe1aSYong Wu 
333cc8bbe1aSYong Wu 	larb->smi.clk_apb = devm_clk_get(dev, "apb");
334cc8bbe1aSYong Wu 	if (IS_ERR(larb->smi.clk_apb))
335cc8bbe1aSYong Wu 		return PTR_ERR(larb->smi.clk_apb);
336cc8bbe1aSYong Wu 
337cc8bbe1aSYong Wu 	larb->smi.clk_smi = devm_clk_get(dev, "smi");
338cc8bbe1aSYong Wu 	if (IS_ERR(larb->smi.clk_smi))
339cc8bbe1aSYong Wu 		return PTR_ERR(larb->smi.clk_smi);
34064fea74aSYong Wu 
34164fea74aSYong Wu 	if (larb->larb_gen->has_gals) {
34264fea74aSYong Wu 		/* The larbs may still haven't gals even if the SoC support.*/
34364fea74aSYong Wu 		larb->smi.clk_gals0 = devm_clk_get(dev, "gals");
34464fea74aSYong Wu 		if (PTR_ERR(larb->smi.clk_gals0) == -ENOENT)
34564fea74aSYong Wu 			larb->smi.clk_gals0 = NULL;
34664fea74aSYong Wu 		else if (IS_ERR(larb->smi.clk_gals0))
34764fea74aSYong Wu 			return PTR_ERR(larb->smi.clk_gals0);
34864fea74aSYong Wu 	}
349cc8bbe1aSYong Wu 	larb->smi.dev = dev;
350cc8bbe1aSYong Wu 
351cc8bbe1aSYong Wu 	smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0);
352cc8bbe1aSYong Wu 	if (!smi_node)
353cc8bbe1aSYong Wu 		return -EINVAL;
354cc8bbe1aSYong Wu 
355cc8bbe1aSYong Wu 	smi_pdev = of_find_device_by_node(smi_node);
356cc8bbe1aSYong Wu 	of_node_put(smi_node);
357cc8bbe1aSYong Wu 	if (smi_pdev) {
3584f608d38SYong Wu 		if (!platform_get_drvdata(smi_pdev))
3594f608d38SYong Wu 			return -EPROBE_DEFER;
360cc8bbe1aSYong Wu 		larb->smi_common_dev = &smi_pdev->dev;
361cc8bbe1aSYong Wu 	} else {
362cc8bbe1aSYong Wu 		dev_err(dev, "Failed to get the smi_common device\n");
363cc8bbe1aSYong Wu 		return -EINVAL;
364cc8bbe1aSYong Wu 	}
365cc8bbe1aSYong Wu 
366cc8bbe1aSYong Wu 	pm_runtime_enable(dev);
367cc8bbe1aSYong Wu 	platform_set_drvdata(pdev, larb);
368cc8bbe1aSYong Wu 	return component_add(dev, &mtk_smi_larb_component_ops);
369cc8bbe1aSYong Wu }
370cc8bbe1aSYong Wu 
371cc8bbe1aSYong Wu static int mtk_smi_larb_remove(struct platform_device *pdev)
372cc8bbe1aSYong Wu {
373cc8bbe1aSYong Wu 	pm_runtime_disable(&pdev->dev);
374cc8bbe1aSYong Wu 	component_del(&pdev->dev, &mtk_smi_larb_component_ops);
375cc8bbe1aSYong Wu 	return 0;
376cc8bbe1aSYong Wu }
377cc8bbe1aSYong Wu 
3784f0a1a1aSYong Wu static int __maybe_unused mtk_smi_larb_resume(struct device *dev)
3794f0a1a1aSYong Wu {
3804f0a1a1aSYong Wu 	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
3814f0a1a1aSYong Wu 	const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
3824f0a1a1aSYong Wu 	int ret;
3834f0a1a1aSYong Wu 
3844f0a1a1aSYong Wu 	/* Power on smi-common. */
3854f0a1a1aSYong Wu 	ret = pm_runtime_get_sync(larb->smi_common_dev);
3864f0a1a1aSYong Wu 	if (ret < 0) {
3874f0a1a1aSYong Wu 		dev_err(dev, "Failed to pm get for smi-common(%d).\n", ret);
3884f0a1a1aSYong Wu 		return ret;
3894f0a1a1aSYong Wu 	}
3904f0a1a1aSYong Wu 
3914f0a1a1aSYong Wu 	ret = mtk_smi_clk_enable(&larb->smi);
3924f0a1a1aSYong Wu 	if (ret < 0) {
3934f0a1a1aSYong Wu 		dev_err(dev, "Failed to enable clock(%d).\n", ret);
3944f0a1a1aSYong Wu 		pm_runtime_put_sync(larb->smi_common_dev);
3954f0a1a1aSYong Wu 		return ret;
3964f0a1a1aSYong Wu 	}
3974f0a1a1aSYong Wu 
3984f0a1a1aSYong Wu 	/* Configure the basic setting for this larb */
3994f0a1a1aSYong Wu 	larb_gen->config_port(dev);
4004f0a1a1aSYong Wu 
4014f0a1a1aSYong Wu 	return 0;
4024f0a1a1aSYong Wu }
4034f0a1a1aSYong Wu 
4044f0a1a1aSYong Wu static int __maybe_unused mtk_smi_larb_suspend(struct device *dev)
4054f0a1a1aSYong Wu {
4064f0a1a1aSYong Wu 	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
4074f0a1a1aSYong Wu 
4084f0a1a1aSYong Wu 	mtk_smi_clk_disable(&larb->smi);
4094f0a1a1aSYong Wu 	pm_runtime_put_sync(larb->smi_common_dev);
4104f0a1a1aSYong Wu 	return 0;
4114f0a1a1aSYong Wu }
4124f0a1a1aSYong Wu 
4134f0a1a1aSYong Wu static const struct dev_pm_ops smi_larb_pm_ops = {
4144f0a1a1aSYong Wu 	SET_RUNTIME_PM_OPS(mtk_smi_larb_suspend, mtk_smi_larb_resume, NULL)
415fb03082aSYong Wu 	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
416fb03082aSYong Wu 				     pm_runtime_force_resume)
4174f0a1a1aSYong Wu };
4184f0a1a1aSYong Wu 
419cc8bbe1aSYong Wu static struct platform_driver mtk_smi_larb_driver = {
420cc8bbe1aSYong Wu 	.probe	= mtk_smi_larb_probe,
421cc8bbe1aSYong Wu 	.remove	= mtk_smi_larb_remove,
422cc8bbe1aSYong Wu 	.driver	= {
423cc8bbe1aSYong Wu 		.name = "mtk-smi-larb",
424cc8bbe1aSYong Wu 		.of_match_table = mtk_smi_larb_of_ids,
4254f0a1a1aSYong Wu 		.pm             = &smi_larb_pm_ops,
426cc8bbe1aSYong Wu 	}
427cc8bbe1aSYong Wu };
428cc8bbe1aSYong Wu 
42942d42c76SYong Wu static const struct mtk_smi_common_plat mtk_smi_common_gen1 = {
43042d42c76SYong Wu 	.gen = MTK_SMI_GEN1,
43142d42c76SYong Wu };
43242d42c76SYong Wu 
43342d42c76SYong Wu static const struct mtk_smi_common_plat mtk_smi_common_gen2 = {
43442d42c76SYong Wu 	.gen = MTK_SMI_GEN2,
43542d42c76SYong Wu };
43642d42c76SYong Wu 
437fc492f33SMing-Fan Chen static const struct mtk_smi_common_plat mtk_smi_common_mt6779 = {
438fc492f33SMing-Fan Chen 	.gen		= MTK_SMI_GEN2,
439fc492f33SMing-Fan Chen 	.has_gals	= true,
440fc492f33SMing-Fan Chen 	.bus_sel	= F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(4) |
441fc492f33SMing-Fan Chen 			  F_MMU1_LARB(5) | F_MMU1_LARB(6) | F_MMU1_LARB(7),
442fc492f33SMing-Fan Chen };
443fc492f33SMing-Fan Chen 
444907ba6a1SYong Wu static const struct mtk_smi_common_plat mtk_smi_common_mt8183 = {
445907ba6a1SYong Wu 	.gen      = MTK_SMI_GEN2,
446907ba6a1SYong Wu 	.has_gals = true,
447567e58cfSYong Wu 	.bus_sel  = F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(5) |
448567e58cfSYong Wu 		    F_MMU1_LARB(7),
449907ba6a1SYong Wu };
450907ba6a1SYong Wu 
45102c02ddcSYong Wu static const struct mtk_smi_common_plat mtk_smi_common_mt8192 = {
45202c02ddcSYong Wu 	.gen      = MTK_SMI_GEN2,
45302c02ddcSYong Wu 	.has_gals = true,
45402c02ddcSYong Wu 	.bus_sel  = F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(5) |
45502c02ddcSYong Wu 		    F_MMU1_LARB(6),
45602c02ddcSYong Wu };
45702c02ddcSYong Wu 
4583c8f4ad8SHonghui Zhang static const struct of_device_id mtk_smi_common_of_ids[] = {
4593c8f4ad8SHonghui Zhang 	{
4603c8f4ad8SHonghui Zhang 		.compatible = "mediatek,mt8173-smi-common",
46142d42c76SYong Wu 		.data = &mtk_smi_common_gen2,
4623c8f4ad8SHonghui Zhang 	},
4633c8f4ad8SHonghui Zhang 	{
464a8529f3bSFabien Parent 		.compatible = "mediatek,mt8167-smi-common",
465a8529f3bSFabien Parent 		.data = &mtk_smi_common_gen2,
466a8529f3bSFabien Parent 	},
467a8529f3bSFabien Parent 	{
4683c8f4ad8SHonghui Zhang 		.compatible = "mediatek,mt2701-smi-common",
46942d42c76SYong Wu 		.data = &mtk_smi_common_gen1,
4703c8f4ad8SHonghui Zhang 	},
471e6dec923SYong Wu 	{
472e6dec923SYong Wu 		.compatible = "mediatek,mt2712-smi-common",
47342d42c76SYong Wu 		.data = &mtk_smi_common_gen2,
474e6dec923SYong Wu 	},
475907ba6a1SYong Wu 	{
476fc492f33SMing-Fan Chen 		.compatible = "mediatek,mt6779-smi-common",
477fc492f33SMing-Fan Chen 		.data = &mtk_smi_common_mt6779,
478fc492f33SMing-Fan Chen 	},
479fc492f33SMing-Fan Chen 	{
480907ba6a1SYong Wu 		.compatible = "mediatek,mt8183-smi-common",
481907ba6a1SYong Wu 		.data = &mtk_smi_common_mt8183,
482907ba6a1SYong Wu 	},
48302c02ddcSYong Wu 	{
48402c02ddcSYong Wu 		.compatible = "mediatek,mt8192-smi-common",
48502c02ddcSYong Wu 		.data = &mtk_smi_common_mt8192,
48602c02ddcSYong Wu 	},
4873c8f4ad8SHonghui Zhang 	{}
4883c8f4ad8SHonghui Zhang };
4893c8f4ad8SHonghui Zhang 
490cc8bbe1aSYong Wu static int mtk_smi_common_probe(struct platform_device *pdev)
491cc8bbe1aSYong Wu {
492cc8bbe1aSYong Wu 	struct device *dev = &pdev->dev;
493cc8bbe1aSYong Wu 	struct mtk_smi *common;
4943c8f4ad8SHonghui Zhang 	struct resource *res;
49546cc815dSArvind Yadav 	int ret;
496cc8bbe1aSYong Wu 
497cc8bbe1aSYong Wu 	common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL);
498cc8bbe1aSYong Wu 	if (!common)
499cc8bbe1aSYong Wu 		return -ENOMEM;
500cc8bbe1aSYong Wu 	common->dev = dev;
50142d42c76SYong Wu 	common->plat = of_device_get_match_data(dev);
502cc8bbe1aSYong Wu 
503cc8bbe1aSYong Wu 	common->clk_apb = devm_clk_get(dev, "apb");
504cc8bbe1aSYong Wu 	if (IS_ERR(common->clk_apb))
505cc8bbe1aSYong Wu 		return PTR_ERR(common->clk_apb);
506cc8bbe1aSYong Wu 
507cc8bbe1aSYong Wu 	common->clk_smi = devm_clk_get(dev, "smi");
508cc8bbe1aSYong Wu 	if (IS_ERR(common->clk_smi))
509cc8bbe1aSYong Wu 		return PTR_ERR(common->clk_smi);
510cc8bbe1aSYong Wu 
51164fea74aSYong Wu 	if (common->plat->has_gals) {
51264fea74aSYong Wu 		common->clk_gals0 = devm_clk_get(dev, "gals0");
51364fea74aSYong Wu 		if (IS_ERR(common->clk_gals0))
51464fea74aSYong Wu 			return PTR_ERR(common->clk_gals0);
51564fea74aSYong Wu 
51664fea74aSYong Wu 		common->clk_gals1 = devm_clk_get(dev, "gals1");
51764fea74aSYong Wu 		if (IS_ERR(common->clk_gals1))
51864fea74aSYong Wu 			return PTR_ERR(common->clk_gals1);
51964fea74aSYong Wu 	}
52064fea74aSYong Wu 
5213c8f4ad8SHonghui Zhang 	/*
5223c8f4ad8SHonghui Zhang 	 * for mtk smi gen 1, we need to get the ao(always on) base to config
5233c8f4ad8SHonghui Zhang 	 * m4u port, and we need to enable the aync clock for transform the smi
5243c8f4ad8SHonghui Zhang 	 * clock into emi clock domain, but for mtk smi gen2, there's no smi ao
5253c8f4ad8SHonghui Zhang 	 * base.
5263c8f4ad8SHonghui Zhang 	 */
52742d42c76SYong Wu 	if (common->plat->gen == MTK_SMI_GEN1) {
5283c8f4ad8SHonghui Zhang 		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
5293c8f4ad8SHonghui Zhang 		common->smi_ao_base = devm_ioremap_resource(dev, res);
5303c8f4ad8SHonghui Zhang 		if (IS_ERR(common->smi_ao_base))
5313c8f4ad8SHonghui Zhang 			return PTR_ERR(common->smi_ao_base);
5323c8f4ad8SHonghui Zhang 
5333c8f4ad8SHonghui Zhang 		common->clk_async = devm_clk_get(dev, "async");
5343c8f4ad8SHonghui Zhang 		if (IS_ERR(common->clk_async))
5353c8f4ad8SHonghui Zhang 			return PTR_ERR(common->clk_async);
5363c8f4ad8SHonghui Zhang 
53746cc815dSArvind Yadav 		ret = clk_prepare_enable(common->clk_async);
53846cc815dSArvind Yadav 		if (ret)
53946cc815dSArvind Yadav 			return ret;
540567e58cfSYong Wu 	} else {
541567e58cfSYong Wu 		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
542567e58cfSYong Wu 		common->base = devm_ioremap_resource(dev, res);
543567e58cfSYong Wu 		if (IS_ERR(common->base))
544567e58cfSYong Wu 			return PTR_ERR(common->base);
5453c8f4ad8SHonghui Zhang 	}
546cc8bbe1aSYong Wu 	pm_runtime_enable(dev);
547cc8bbe1aSYong Wu 	platform_set_drvdata(pdev, common);
548cc8bbe1aSYong Wu 	return 0;
549cc8bbe1aSYong Wu }
550cc8bbe1aSYong Wu 
551cc8bbe1aSYong Wu static int mtk_smi_common_remove(struct platform_device *pdev)
552cc8bbe1aSYong Wu {
553cc8bbe1aSYong Wu 	pm_runtime_disable(&pdev->dev);
554cc8bbe1aSYong Wu 	return 0;
555cc8bbe1aSYong Wu }
556cc8bbe1aSYong Wu 
5574f0a1a1aSYong Wu static int __maybe_unused mtk_smi_common_resume(struct device *dev)
5584f0a1a1aSYong Wu {
5594f0a1a1aSYong Wu 	struct mtk_smi *common = dev_get_drvdata(dev);
560567e58cfSYong Wu 	u32 bus_sel = common->plat->bus_sel;
5614f0a1a1aSYong Wu 	int ret;
5624f0a1a1aSYong Wu 
5634f0a1a1aSYong Wu 	ret = mtk_smi_clk_enable(common);
5644f0a1a1aSYong Wu 	if (ret) {
5654f0a1a1aSYong Wu 		dev_err(common->dev, "Failed to enable clock(%d).\n", ret);
5664f0a1a1aSYong Wu 		return ret;
5674f0a1a1aSYong Wu 	}
568567e58cfSYong Wu 
569567e58cfSYong Wu 	if (common->plat->gen == MTK_SMI_GEN2 && bus_sel)
570567e58cfSYong Wu 		writel(bus_sel, common->base + SMI_BUS_SEL);
5714f0a1a1aSYong Wu 	return 0;
5724f0a1a1aSYong Wu }
5734f0a1a1aSYong Wu 
5744f0a1a1aSYong Wu static int __maybe_unused mtk_smi_common_suspend(struct device *dev)
5754f0a1a1aSYong Wu {
5764f0a1a1aSYong Wu 	struct mtk_smi *common = dev_get_drvdata(dev);
5774f0a1a1aSYong Wu 
5784f0a1a1aSYong Wu 	mtk_smi_clk_disable(common);
5794f0a1a1aSYong Wu 	return 0;
5804f0a1a1aSYong Wu }
5814f0a1a1aSYong Wu 
5824f0a1a1aSYong Wu static const struct dev_pm_ops smi_common_pm_ops = {
5834f0a1a1aSYong Wu 	SET_RUNTIME_PM_OPS(mtk_smi_common_suspend, mtk_smi_common_resume, NULL)
584fb03082aSYong Wu 	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
585fb03082aSYong Wu 				     pm_runtime_force_resume)
5864f0a1a1aSYong Wu };
5874f0a1a1aSYong Wu 
588cc8bbe1aSYong Wu static struct platform_driver mtk_smi_common_driver = {
589cc8bbe1aSYong Wu 	.probe	= mtk_smi_common_probe,
590cc8bbe1aSYong Wu 	.remove = mtk_smi_common_remove,
591cc8bbe1aSYong Wu 	.driver	= {
592cc8bbe1aSYong Wu 		.name = "mtk-smi-common",
593cc8bbe1aSYong Wu 		.of_match_table = mtk_smi_common_of_ids,
5944f0a1a1aSYong Wu 		.pm             = &smi_common_pm_ops,
595cc8bbe1aSYong Wu 	}
596cc8bbe1aSYong Wu };
597cc8bbe1aSYong Wu 
598cc8bbe1aSYong Wu static int __init mtk_smi_init(void)
599cc8bbe1aSYong Wu {
600cc8bbe1aSYong Wu 	int ret;
601cc8bbe1aSYong Wu 
602cc8bbe1aSYong Wu 	ret = platform_driver_register(&mtk_smi_common_driver);
603cc8bbe1aSYong Wu 	if (ret != 0) {
604cc8bbe1aSYong Wu 		pr_err("Failed to register SMI driver\n");
605cc8bbe1aSYong Wu 		return ret;
606cc8bbe1aSYong Wu 	}
607cc8bbe1aSYong Wu 
608cc8bbe1aSYong Wu 	ret = platform_driver_register(&mtk_smi_larb_driver);
609cc8bbe1aSYong Wu 	if (ret != 0) {
610cc8bbe1aSYong Wu 		pr_err("Failed to register SMI-LARB driver\n");
611cc8bbe1aSYong Wu 		goto err_unreg_smi;
612cc8bbe1aSYong Wu 	}
613cc8bbe1aSYong Wu 	return ret;
614cc8bbe1aSYong Wu 
615cc8bbe1aSYong Wu err_unreg_smi:
616cc8bbe1aSYong Wu 	platform_driver_unregister(&mtk_smi_common_driver);
617cc8bbe1aSYong Wu 	return ret;
618cc8bbe1aSYong Wu }
6193c8f4ad8SHonghui Zhang 
6204f608d38SYong Wu module_init(mtk_smi_init);
621