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