18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2015-2016 MediaTek Inc.
48c2ecf20Sopenharmony_ci * Author: Yong Wu <yong.wu@mediatek.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/clk.h>
78c2ecf20Sopenharmony_ci#include <linux/component.h>
88c2ecf20Sopenharmony_ci#include <linux/device.h>
98c2ecf20Sopenharmony_ci#include <linux/err.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
168c2ecf20Sopenharmony_ci#include <soc/mediatek/smi.h>
178c2ecf20Sopenharmony_ci#include <dt-bindings/memory/mt2701-larb-port.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* mt8173 */
208c2ecf20Sopenharmony_ci#define SMI_LARB_MMU_EN		0xf00
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* mt8167 */
238c2ecf20Sopenharmony_ci#define MT8167_SMI_LARB_MMU_EN	0xfc0
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* mt2701 */
268c2ecf20Sopenharmony_ci#define REG_SMI_SECUR_CON_BASE		0x5c0
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* every register control 8 port, register offset 0x4 */
298c2ecf20Sopenharmony_ci#define REG_SMI_SECUR_CON_OFFSET(id)	(((id) >> 3) << 2)
308c2ecf20Sopenharmony_ci#define REG_SMI_SECUR_CON_ADDR(id)	\
318c2ecf20Sopenharmony_ci	(REG_SMI_SECUR_CON_BASE + REG_SMI_SECUR_CON_OFFSET(id))
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * every port have 4 bit to control, bit[port + 3] control virtual or physical,
358c2ecf20Sopenharmony_ci * bit[port + 2 : port + 1] control the domain, bit[port] control the security
368c2ecf20Sopenharmony_ci * or non-security.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci#define SMI_SECUR_CON_VAL_MSK(id)	(~(0xf << (((id) & 0x7) << 2)))
398c2ecf20Sopenharmony_ci#define SMI_SECUR_CON_VAL_VIRT(id)	BIT((((id) & 0x7) << 2) + 3)
408c2ecf20Sopenharmony_ci/* mt2701 domain should be set to 3 */
418c2ecf20Sopenharmony_ci#define SMI_SECUR_CON_VAL_DOMAIN(id)	(0x3 << ((((id) & 0x7) << 2) + 1))
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* mt2712 */
448c2ecf20Sopenharmony_ci#define SMI_LARB_NONSEC_CON(id)	(0x380 + ((id) * 4))
458c2ecf20Sopenharmony_ci#define F_MMU_EN		BIT(0)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* SMI COMMON */
488c2ecf20Sopenharmony_ci#define SMI_BUS_SEL			0x220
498c2ecf20Sopenharmony_ci#define SMI_BUS_LARB_SHIFT(larbid)	((larbid) << 1)
508c2ecf20Sopenharmony_ci/* All are MMU0 defaultly. Only specialize mmu1 here. */
518c2ecf20Sopenharmony_ci#define F_MMU1_LARB(larbid)		(0x1 << SMI_BUS_LARB_SHIFT(larbid))
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cienum mtk_smi_gen {
548c2ecf20Sopenharmony_ci	MTK_SMI_GEN1,
558c2ecf20Sopenharmony_ci	MTK_SMI_GEN2
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistruct mtk_smi_common_plat {
598c2ecf20Sopenharmony_ci	enum mtk_smi_gen gen;
608c2ecf20Sopenharmony_ci	bool             has_gals;
618c2ecf20Sopenharmony_ci	u32              bus_sel; /* Balance some larbs to enter mmu0 or mmu1 */
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistruct mtk_smi_larb_gen {
658c2ecf20Sopenharmony_ci	int port_in_larb[MTK_LARB_NR_MAX + 1];
668c2ecf20Sopenharmony_ci	void (*config_port)(struct device *dev);
678c2ecf20Sopenharmony_ci	unsigned int			larb_direct_to_common_mask;
688c2ecf20Sopenharmony_ci	bool				has_gals;
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistruct mtk_smi {
728c2ecf20Sopenharmony_ci	struct device			*dev;
738c2ecf20Sopenharmony_ci	struct clk			*clk_apb, *clk_smi;
748c2ecf20Sopenharmony_ci	struct clk			*clk_gals0, *clk_gals1;
758c2ecf20Sopenharmony_ci	struct clk			*clk_async; /*only needed by mt2701*/
768c2ecf20Sopenharmony_ci	union {
778c2ecf20Sopenharmony_ci		void __iomem		*smi_ao_base; /* only for gen1 */
788c2ecf20Sopenharmony_ci		void __iomem		*base;	      /* only for gen2 */
798c2ecf20Sopenharmony_ci	};
808c2ecf20Sopenharmony_ci	const struct mtk_smi_common_plat *plat;
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistruct mtk_smi_larb { /* larb: local arbiter */
848c2ecf20Sopenharmony_ci	struct mtk_smi			smi;
858c2ecf20Sopenharmony_ci	void __iomem			*base;
868c2ecf20Sopenharmony_ci	struct device			*smi_common_dev;
878c2ecf20Sopenharmony_ci	const struct mtk_smi_larb_gen	*larb_gen;
888c2ecf20Sopenharmony_ci	int				larbid;
898c2ecf20Sopenharmony_ci	u32				*mmu;
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int mtk_smi_clk_enable(const struct mtk_smi *smi)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	int ret;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(smi->clk_apb);
978c2ecf20Sopenharmony_ci	if (ret)
988c2ecf20Sopenharmony_ci		return ret;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(smi->clk_smi);
1018c2ecf20Sopenharmony_ci	if (ret)
1028c2ecf20Sopenharmony_ci		goto err_disable_apb;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(smi->clk_gals0);
1058c2ecf20Sopenharmony_ci	if (ret)
1068c2ecf20Sopenharmony_ci		goto err_disable_smi;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(smi->clk_gals1);
1098c2ecf20Sopenharmony_ci	if (ret)
1108c2ecf20Sopenharmony_ci		goto err_disable_gals0;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return 0;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cierr_disable_gals0:
1158c2ecf20Sopenharmony_ci	clk_disable_unprepare(smi->clk_gals0);
1168c2ecf20Sopenharmony_cierr_disable_smi:
1178c2ecf20Sopenharmony_ci	clk_disable_unprepare(smi->clk_smi);
1188c2ecf20Sopenharmony_cierr_disable_apb:
1198c2ecf20Sopenharmony_ci	clk_disable_unprepare(smi->clk_apb);
1208c2ecf20Sopenharmony_ci	return ret;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic void mtk_smi_clk_disable(const struct mtk_smi *smi)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	clk_disable_unprepare(smi->clk_gals1);
1268c2ecf20Sopenharmony_ci	clk_disable_unprepare(smi->clk_gals0);
1278c2ecf20Sopenharmony_ci	clk_disable_unprepare(smi->clk_smi);
1288c2ecf20Sopenharmony_ci	clk_disable_unprepare(smi->clk_apb);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ciint mtk_smi_larb_get(struct device *larbdev)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	int ret = pm_runtime_resume_and_get(larbdev);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return (ret < 0) ? ret : 0;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mtk_smi_larb_get);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_civoid mtk_smi_larb_put(struct device *larbdev)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	pm_runtime_put_sync(larbdev);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mtk_smi_larb_put);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int
1468c2ecf20Sopenharmony_cimtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
1498c2ecf20Sopenharmony_ci	struct mtk_smi_larb_iommu *larb_mmu = data;
1508c2ecf20Sopenharmony_ci	unsigned int         i;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	for (i = 0; i < MTK_LARB_NR_MAX; i++) {
1538c2ecf20Sopenharmony_ci		if (dev == larb_mmu[i].dev) {
1548c2ecf20Sopenharmony_ci			larb->larbid = i;
1558c2ecf20Sopenharmony_ci			larb->mmu = &larb_mmu[i].mmu;
1568c2ecf20Sopenharmony_ci			return 0;
1578c2ecf20Sopenharmony_ci		}
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci	return -ENODEV;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic void mtk_smi_larb_config_port_gen2_general(struct device *dev)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
1658c2ecf20Sopenharmony_ci	u32 reg;
1668c2ecf20Sopenharmony_ci	int i;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (BIT(larb->larbid) & larb->larb_gen->larb_direct_to_common_mask)
1698c2ecf20Sopenharmony_ci		return;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	for_each_set_bit(i, (unsigned long *)larb->mmu, 32) {
1728c2ecf20Sopenharmony_ci		reg = readl_relaxed(larb->base + SMI_LARB_NONSEC_CON(i));
1738c2ecf20Sopenharmony_ci		reg |= F_MMU_EN;
1748c2ecf20Sopenharmony_ci		writel(reg, larb->base + SMI_LARB_NONSEC_CON(i));
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic void mtk_smi_larb_config_port_mt8173(struct device *dev)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic void mtk_smi_larb_config_port_mt8167(struct device *dev)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	writel(*larb->mmu, larb->base + MT8167_SMI_LARB_MMU_EN);
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic void mtk_smi_larb_config_port_gen1(struct device *dev)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
1958c2ecf20Sopenharmony_ci	const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
1968c2ecf20Sopenharmony_ci	struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev);
1978c2ecf20Sopenharmony_ci	int i, m4u_port_id, larb_port_num;
1988c2ecf20Sopenharmony_ci	u32 sec_con_val, reg_val;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	m4u_port_id = larb_gen->port_in_larb[larb->larbid];
2018c2ecf20Sopenharmony_ci	larb_port_num = larb_gen->port_in_larb[larb->larbid + 1]
2028c2ecf20Sopenharmony_ci			- larb_gen->port_in_larb[larb->larbid];
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	for (i = 0; i < larb_port_num; i++, m4u_port_id++) {
2058c2ecf20Sopenharmony_ci		if (*larb->mmu & BIT(i)) {
2068c2ecf20Sopenharmony_ci			/* bit[port + 3] controls the virtual or physical */
2078c2ecf20Sopenharmony_ci			sec_con_val = SMI_SECUR_CON_VAL_VIRT(m4u_port_id);
2088c2ecf20Sopenharmony_ci		} else {
2098c2ecf20Sopenharmony_ci			/* do not need to enable m4u for this port */
2108c2ecf20Sopenharmony_ci			continue;
2118c2ecf20Sopenharmony_ci		}
2128c2ecf20Sopenharmony_ci		reg_val = readl(common->smi_ao_base
2138c2ecf20Sopenharmony_ci			+ REG_SMI_SECUR_CON_ADDR(m4u_port_id));
2148c2ecf20Sopenharmony_ci		reg_val &= SMI_SECUR_CON_VAL_MSK(m4u_port_id);
2158c2ecf20Sopenharmony_ci		reg_val |= sec_con_val;
2168c2ecf20Sopenharmony_ci		reg_val |= SMI_SECUR_CON_VAL_DOMAIN(m4u_port_id);
2178c2ecf20Sopenharmony_ci		writel(reg_val,
2188c2ecf20Sopenharmony_ci			common->smi_ao_base
2198c2ecf20Sopenharmony_ci			+ REG_SMI_SECUR_CON_ADDR(m4u_port_id));
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic void
2248c2ecf20Sopenharmony_cimtk_smi_larb_unbind(struct device *dev, struct device *master, void *data)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	/* Do nothing as the iommu is always enabled. */
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic const struct component_ops mtk_smi_larb_component_ops = {
2308c2ecf20Sopenharmony_ci	.bind = mtk_smi_larb_bind,
2318c2ecf20Sopenharmony_ci	.unbind = mtk_smi_larb_unbind,
2328c2ecf20Sopenharmony_ci};
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic const struct mtk_smi_larb_gen mtk_smi_larb_mt8173 = {
2358c2ecf20Sopenharmony_ci	/* mt8173 do not need the port in larb */
2368c2ecf20Sopenharmony_ci	.config_port = mtk_smi_larb_config_port_mt8173,
2378c2ecf20Sopenharmony_ci};
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic const struct mtk_smi_larb_gen mtk_smi_larb_mt8167 = {
2408c2ecf20Sopenharmony_ci	/* mt8167 do not need the port in larb */
2418c2ecf20Sopenharmony_ci	.config_port = mtk_smi_larb_config_port_mt8167,
2428c2ecf20Sopenharmony_ci};
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = {
2458c2ecf20Sopenharmony_ci	.port_in_larb = {
2468c2ecf20Sopenharmony_ci		LARB0_PORT_OFFSET, LARB1_PORT_OFFSET,
2478c2ecf20Sopenharmony_ci		LARB2_PORT_OFFSET, LARB3_PORT_OFFSET
2488c2ecf20Sopenharmony_ci	},
2498c2ecf20Sopenharmony_ci	.config_port = mtk_smi_larb_config_port_gen1,
2508c2ecf20Sopenharmony_ci};
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic const struct mtk_smi_larb_gen mtk_smi_larb_mt2712 = {
2538c2ecf20Sopenharmony_ci	.config_port                = mtk_smi_larb_config_port_gen2_general,
2548c2ecf20Sopenharmony_ci	.larb_direct_to_common_mask = BIT(8) | BIT(9),      /* bdpsys */
2558c2ecf20Sopenharmony_ci};
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic const struct mtk_smi_larb_gen mtk_smi_larb_mt6779 = {
2588c2ecf20Sopenharmony_ci	.config_port  = mtk_smi_larb_config_port_gen2_general,
2598c2ecf20Sopenharmony_ci	.larb_direct_to_common_mask =
2608c2ecf20Sopenharmony_ci		BIT(4) | BIT(6) | BIT(11) | BIT(12) | BIT(13),
2618c2ecf20Sopenharmony_ci		/* DUMMY | IPU0 | IPU1 | CCU | MDLA */
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic const struct mtk_smi_larb_gen mtk_smi_larb_mt8183 = {
2658c2ecf20Sopenharmony_ci	.has_gals                   = true,
2668c2ecf20Sopenharmony_ci	.config_port                = mtk_smi_larb_config_port_gen2_general,
2678c2ecf20Sopenharmony_ci	.larb_direct_to_common_mask = BIT(2) | BIT(3) | BIT(7),
2688c2ecf20Sopenharmony_ci				      /* IPU0 | IPU1 | CCU */
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic const struct of_device_id mtk_smi_larb_of_ids[] = {
2728c2ecf20Sopenharmony_ci	{
2738c2ecf20Sopenharmony_ci		.compatible = "mediatek,mt8167-smi-larb",
2748c2ecf20Sopenharmony_ci		.data = &mtk_smi_larb_mt8167
2758c2ecf20Sopenharmony_ci	},
2768c2ecf20Sopenharmony_ci	{
2778c2ecf20Sopenharmony_ci		.compatible = "mediatek,mt8173-smi-larb",
2788c2ecf20Sopenharmony_ci		.data = &mtk_smi_larb_mt8173
2798c2ecf20Sopenharmony_ci	},
2808c2ecf20Sopenharmony_ci	{
2818c2ecf20Sopenharmony_ci		.compatible = "mediatek,mt2701-smi-larb",
2828c2ecf20Sopenharmony_ci		.data = &mtk_smi_larb_mt2701
2838c2ecf20Sopenharmony_ci	},
2848c2ecf20Sopenharmony_ci	{
2858c2ecf20Sopenharmony_ci		.compatible = "mediatek,mt2712-smi-larb",
2868c2ecf20Sopenharmony_ci		.data = &mtk_smi_larb_mt2712
2878c2ecf20Sopenharmony_ci	},
2888c2ecf20Sopenharmony_ci	{
2898c2ecf20Sopenharmony_ci		.compatible = "mediatek,mt6779-smi-larb",
2908c2ecf20Sopenharmony_ci		.data = &mtk_smi_larb_mt6779
2918c2ecf20Sopenharmony_ci	},
2928c2ecf20Sopenharmony_ci	{
2938c2ecf20Sopenharmony_ci		.compatible = "mediatek,mt8183-smi-larb",
2948c2ecf20Sopenharmony_ci		.data = &mtk_smi_larb_mt8183
2958c2ecf20Sopenharmony_ci	},
2968c2ecf20Sopenharmony_ci	{}
2978c2ecf20Sopenharmony_ci};
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic int mtk_smi_larb_probe(struct platform_device *pdev)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct mtk_smi_larb *larb;
3028c2ecf20Sopenharmony_ci	struct resource *res;
3038c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
3048c2ecf20Sopenharmony_ci	struct device_node *smi_node;
3058c2ecf20Sopenharmony_ci	struct platform_device *smi_pdev;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL);
3088c2ecf20Sopenharmony_ci	if (!larb)
3098c2ecf20Sopenharmony_ci		return -ENOMEM;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	larb->larb_gen = of_device_get_match_data(dev);
3128c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3138c2ecf20Sopenharmony_ci	larb->base = devm_ioremap_resource(dev, res);
3148c2ecf20Sopenharmony_ci	if (IS_ERR(larb->base))
3158c2ecf20Sopenharmony_ci		return PTR_ERR(larb->base);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	larb->smi.clk_apb = devm_clk_get(dev, "apb");
3188c2ecf20Sopenharmony_ci	if (IS_ERR(larb->smi.clk_apb))
3198c2ecf20Sopenharmony_ci		return PTR_ERR(larb->smi.clk_apb);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	larb->smi.clk_smi = devm_clk_get(dev, "smi");
3228c2ecf20Sopenharmony_ci	if (IS_ERR(larb->smi.clk_smi))
3238c2ecf20Sopenharmony_ci		return PTR_ERR(larb->smi.clk_smi);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (larb->larb_gen->has_gals) {
3268c2ecf20Sopenharmony_ci		/* The larbs may still haven't gals even if the SoC support.*/
3278c2ecf20Sopenharmony_ci		larb->smi.clk_gals0 = devm_clk_get(dev, "gals");
3288c2ecf20Sopenharmony_ci		if (PTR_ERR(larb->smi.clk_gals0) == -ENOENT)
3298c2ecf20Sopenharmony_ci			larb->smi.clk_gals0 = NULL;
3308c2ecf20Sopenharmony_ci		else if (IS_ERR(larb->smi.clk_gals0))
3318c2ecf20Sopenharmony_ci			return PTR_ERR(larb->smi.clk_gals0);
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci	larb->smi.dev = dev;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0);
3368c2ecf20Sopenharmony_ci	if (!smi_node)
3378c2ecf20Sopenharmony_ci		return -EINVAL;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	smi_pdev = of_find_device_by_node(smi_node);
3408c2ecf20Sopenharmony_ci	of_node_put(smi_node);
3418c2ecf20Sopenharmony_ci	if (smi_pdev) {
3428c2ecf20Sopenharmony_ci		if (!platform_get_drvdata(smi_pdev))
3438c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
3448c2ecf20Sopenharmony_ci		larb->smi_common_dev = &smi_pdev->dev;
3458c2ecf20Sopenharmony_ci	} else {
3468c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to get the smi_common device\n");
3478c2ecf20Sopenharmony_ci		return -EINVAL;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
3518c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, larb);
3528c2ecf20Sopenharmony_ci	return component_add(dev, &mtk_smi_larb_component_ops);
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic int mtk_smi_larb_remove(struct platform_device *pdev)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
3588c2ecf20Sopenharmony_ci	component_del(&pdev->dev, &mtk_smi_larb_component_ops);
3598c2ecf20Sopenharmony_ci	return 0;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic int __maybe_unused mtk_smi_larb_resume(struct device *dev)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
3658c2ecf20Sopenharmony_ci	const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
3668c2ecf20Sopenharmony_ci	int ret;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* Power on smi-common. */
3698c2ecf20Sopenharmony_ci	ret = pm_runtime_resume_and_get(larb->smi_common_dev);
3708c2ecf20Sopenharmony_ci	if (ret < 0) {
3718c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to pm get for smi-common(%d).\n", ret);
3728c2ecf20Sopenharmony_ci		return ret;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	ret = mtk_smi_clk_enable(&larb->smi);
3768c2ecf20Sopenharmony_ci	if (ret < 0) {
3778c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to enable clock(%d).\n", ret);
3788c2ecf20Sopenharmony_ci		pm_runtime_put_sync(larb->smi_common_dev);
3798c2ecf20Sopenharmony_ci		return ret;
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/* Configure the basic setting for this larb */
3838c2ecf20Sopenharmony_ci	larb_gen->config_port(dev);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return 0;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic int __maybe_unused mtk_smi_larb_suspend(struct device *dev)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	mtk_smi_clk_disable(&larb->smi);
3938c2ecf20Sopenharmony_ci	pm_runtime_put_sync(larb->smi_common_dev);
3948c2ecf20Sopenharmony_ci	return 0;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic const struct dev_pm_ops smi_larb_pm_ops = {
3988c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(mtk_smi_larb_suspend, mtk_smi_larb_resume, NULL)
3998c2ecf20Sopenharmony_ci	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
4008c2ecf20Sopenharmony_ci				     pm_runtime_force_resume)
4018c2ecf20Sopenharmony_ci};
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic struct platform_driver mtk_smi_larb_driver = {
4048c2ecf20Sopenharmony_ci	.probe	= mtk_smi_larb_probe,
4058c2ecf20Sopenharmony_ci	.remove	= mtk_smi_larb_remove,
4068c2ecf20Sopenharmony_ci	.driver	= {
4078c2ecf20Sopenharmony_ci		.name = "mtk-smi-larb",
4088c2ecf20Sopenharmony_ci		.of_match_table = mtk_smi_larb_of_ids,
4098c2ecf20Sopenharmony_ci		.pm             = &smi_larb_pm_ops,
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci};
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic const struct mtk_smi_common_plat mtk_smi_common_gen1 = {
4148c2ecf20Sopenharmony_ci	.gen = MTK_SMI_GEN1,
4158c2ecf20Sopenharmony_ci};
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic const struct mtk_smi_common_plat mtk_smi_common_gen2 = {
4188c2ecf20Sopenharmony_ci	.gen = MTK_SMI_GEN2,
4198c2ecf20Sopenharmony_ci};
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic const struct mtk_smi_common_plat mtk_smi_common_mt6779 = {
4228c2ecf20Sopenharmony_ci	.gen		= MTK_SMI_GEN2,
4238c2ecf20Sopenharmony_ci	.has_gals	= true,
4248c2ecf20Sopenharmony_ci	.bus_sel	= F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(4) |
4258c2ecf20Sopenharmony_ci			  F_MMU1_LARB(5) | F_MMU1_LARB(6) | F_MMU1_LARB(7),
4268c2ecf20Sopenharmony_ci};
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic const struct mtk_smi_common_plat mtk_smi_common_mt8183 = {
4298c2ecf20Sopenharmony_ci	.gen      = MTK_SMI_GEN2,
4308c2ecf20Sopenharmony_ci	.has_gals = true,
4318c2ecf20Sopenharmony_ci	.bus_sel  = F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(5) |
4328c2ecf20Sopenharmony_ci		    F_MMU1_LARB(7),
4338c2ecf20Sopenharmony_ci};
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic const struct of_device_id mtk_smi_common_of_ids[] = {
4368c2ecf20Sopenharmony_ci	{
4378c2ecf20Sopenharmony_ci		.compatible = "mediatek,mt8173-smi-common",
4388c2ecf20Sopenharmony_ci		.data = &mtk_smi_common_gen2,
4398c2ecf20Sopenharmony_ci	},
4408c2ecf20Sopenharmony_ci	{
4418c2ecf20Sopenharmony_ci		.compatible = "mediatek,mt8167-smi-common",
4428c2ecf20Sopenharmony_ci		.data = &mtk_smi_common_gen2,
4438c2ecf20Sopenharmony_ci	},
4448c2ecf20Sopenharmony_ci	{
4458c2ecf20Sopenharmony_ci		.compatible = "mediatek,mt2701-smi-common",
4468c2ecf20Sopenharmony_ci		.data = &mtk_smi_common_gen1,
4478c2ecf20Sopenharmony_ci	},
4488c2ecf20Sopenharmony_ci	{
4498c2ecf20Sopenharmony_ci		.compatible = "mediatek,mt2712-smi-common",
4508c2ecf20Sopenharmony_ci		.data = &mtk_smi_common_gen2,
4518c2ecf20Sopenharmony_ci	},
4528c2ecf20Sopenharmony_ci	{
4538c2ecf20Sopenharmony_ci		.compatible = "mediatek,mt6779-smi-common",
4548c2ecf20Sopenharmony_ci		.data = &mtk_smi_common_mt6779,
4558c2ecf20Sopenharmony_ci	},
4568c2ecf20Sopenharmony_ci	{
4578c2ecf20Sopenharmony_ci		.compatible = "mediatek,mt8183-smi-common",
4588c2ecf20Sopenharmony_ci		.data = &mtk_smi_common_mt8183,
4598c2ecf20Sopenharmony_ci	},
4608c2ecf20Sopenharmony_ci	{}
4618c2ecf20Sopenharmony_ci};
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic int mtk_smi_common_probe(struct platform_device *pdev)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
4668c2ecf20Sopenharmony_ci	struct mtk_smi *common;
4678c2ecf20Sopenharmony_ci	struct resource *res;
4688c2ecf20Sopenharmony_ci	int ret;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL);
4718c2ecf20Sopenharmony_ci	if (!common)
4728c2ecf20Sopenharmony_ci		return -ENOMEM;
4738c2ecf20Sopenharmony_ci	common->dev = dev;
4748c2ecf20Sopenharmony_ci	common->plat = of_device_get_match_data(dev);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	common->clk_apb = devm_clk_get(dev, "apb");
4778c2ecf20Sopenharmony_ci	if (IS_ERR(common->clk_apb))
4788c2ecf20Sopenharmony_ci		return PTR_ERR(common->clk_apb);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	common->clk_smi = devm_clk_get(dev, "smi");
4818c2ecf20Sopenharmony_ci	if (IS_ERR(common->clk_smi))
4828c2ecf20Sopenharmony_ci		return PTR_ERR(common->clk_smi);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if (common->plat->has_gals) {
4858c2ecf20Sopenharmony_ci		common->clk_gals0 = devm_clk_get(dev, "gals0");
4868c2ecf20Sopenharmony_ci		if (IS_ERR(common->clk_gals0))
4878c2ecf20Sopenharmony_ci			return PTR_ERR(common->clk_gals0);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci		common->clk_gals1 = devm_clk_get(dev, "gals1");
4908c2ecf20Sopenharmony_ci		if (IS_ERR(common->clk_gals1))
4918c2ecf20Sopenharmony_ci			return PTR_ERR(common->clk_gals1);
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	/*
4958c2ecf20Sopenharmony_ci	 * for mtk smi gen 1, we need to get the ao(always on) base to config
4968c2ecf20Sopenharmony_ci	 * m4u port, and we need to enable the aync clock for transform the smi
4978c2ecf20Sopenharmony_ci	 * clock into emi clock domain, but for mtk smi gen2, there's no smi ao
4988c2ecf20Sopenharmony_ci	 * base.
4998c2ecf20Sopenharmony_ci	 */
5008c2ecf20Sopenharmony_ci	if (common->plat->gen == MTK_SMI_GEN1) {
5018c2ecf20Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
5028c2ecf20Sopenharmony_ci		common->smi_ao_base = devm_ioremap_resource(dev, res);
5038c2ecf20Sopenharmony_ci		if (IS_ERR(common->smi_ao_base))
5048c2ecf20Sopenharmony_ci			return PTR_ERR(common->smi_ao_base);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci		common->clk_async = devm_clk_get(dev, "async");
5078c2ecf20Sopenharmony_ci		if (IS_ERR(common->clk_async))
5088c2ecf20Sopenharmony_ci			return PTR_ERR(common->clk_async);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(common->clk_async);
5118c2ecf20Sopenharmony_ci		if (ret)
5128c2ecf20Sopenharmony_ci			return ret;
5138c2ecf20Sopenharmony_ci	} else {
5148c2ecf20Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
5158c2ecf20Sopenharmony_ci		common->base = devm_ioremap_resource(dev, res);
5168c2ecf20Sopenharmony_ci		if (IS_ERR(common->base))
5178c2ecf20Sopenharmony_ci			return PTR_ERR(common->base);
5188c2ecf20Sopenharmony_ci	}
5198c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
5208c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, common);
5218c2ecf20Sopenharmony_ci	return 0;
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_cistatic int mtk_smi_common_remove(struct platform_device *pdev)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
5278c2ecf20Sopenharmony_ci	return 0;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic int __maybe_unused mtk_smi_common_resume(struct device *dev)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	struct mtk_smi *common = dev_get_drvdata(dev);
5338c2ecf20Sopenharmony_ci	u32 bus_sel = common->plat->bus_sel;
5348c2ecf20Sopenharmony_ci	int ret;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	ret = mtk_smi_clk_enable(common);
5378c2ecf20Sopenharmony_ci	if (ret) {
5388c2ecf20Sopenharmony_ci		dev_err(common->dev, "Failed to enable clock(%d).\n", ret);
5398c2ecf20Sopenharmony_ci		return ret;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (common->plat->gen == MTK_SMI_GEN2 && bus_sel)
5438c2ecf20Sopenharmony_ci		writel(bus_sel, common->base + SMI_BUS_SEL);
5448c2ecf20Sopenharmony_ci	return 0;
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic int __maybe_unused mtk_smi_common_suspend(struct device *dev)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	struct mtk_smi *common = dev_get_drvdata(dev);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	mtk_smi_clk_disable(common);
5528c2ecf20Sopenharmony_ci	return 0;
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic const struct dev_pm_ops smi_common_pm_ops = {
5568c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(mtk_smi_common_suspend, mtk_smi_common_resume, NULL)
5578c2ecf20Sopenharmony_ci	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
5588c2ecf20Sopenharmony_ci				     pm_runtime_force_resume)
5598c2ecf20Sopenharmony_ci};
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic struct platform_driver mtk_smi_common_driver = {
5628c2ecf20Sopenharmony_ci	.probe	= mtk_smi_common_probe,
5638c2ecf20Sopenharmony_ci	.remove = mtk_smi_common_remove,
5648c2ecf20Sopenharmony_ci	.driver	= {
5658c2ecf20Sopenharmony_ci		.name = "mtk-smi-common",
5668c2ecf20Sopenharmony_ci		.of_match_table = mtk_smi_common_of_ids,
5678c2ecf20Sopenharmony_ci		.pm             = &smi_common_pm_ops,
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci};
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic int __init mtk_smi_init(void)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	int ret;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	ret = platform_driver_register(&mtk_smi_common_driver);
5768c2ecf20Sopenharmony_ci	if (ret != 0) {
5778c2ecf20Sopenharmony_ci		pr_err("Failed to register SMI driver\n");
5788c2ecf20Sopenharmony_ci		return ret;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	ret = platform_driver_register(&mtk_smi_larb_driver);
5828c2ecf20Sopenharmony_ci	if (ret != 0) {
5838c2ecf20Sopenharmony_ci		pr_err("Failed to register SMI-LARB driver\n");
5848c2ecf20Sopenharmony_ci		goto err_unreg_smi;
5858c2ecf20Sopenharmony_ci	}
5868c2ecf20Sopenharmony_ci	return ret;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cierr_unreg_smi:
5898c2ecf20Sopenharmony_ci	platform_driver_unregister(&mtk_smi_common_driver);
5908c2ecf20Sopenharmony_ci	return ret;
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cimodule_init(mtk_smi_init);
594