1cc8bbe1aSYong Wu /* 2cc8bbe1aSYong Wu * Copyright (c) 2015-2016 MediaTek Inc. 3cc8bbe1aSYong Wu * Author: Yong Wu <yong.wu@mediatek.com> 4cc8bbe1aSYong Wu * 5cc8bbe1aSYong Wu * This program is free software; you can redistribute it and/or modify 6cc8bbe1aSYong Wu * it under the terms of the GNU General Public License version 2 as 7cc8bbe1aSYong Wu * published by the Free Software Foundation. 8cc8bbe1aSYong Wu * 9cc8bbe1aSYong Wu * This program is distributed in the hope that it will be useful, 10cc8bbe1aSYong Wu * but WITHOUT ANY WARRANTY; without even the implied warranty of 11cc8bbe1aSYong Wu * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12cc8bbe1aSYong Wu * GNU General Public License for more details. 13cc8bbe1aSYong Wu */ 14cc8bbe1aSYong Wu #include <linux/clk.h> 15cc8bbe1aSYong Wu #include <linux/component.h> 16cc8bbe1aSYong Wu #include <linux/device.h> 17cc8bbe1aSYong Wu #include <linux/err.h> 18cc8bbe1aSYong Wu #include <linux/io.h> 19cc8bbe1aSYong Wu #include <linux/of.h> 20cc8bbe1aSYong Wu #include <linux/of_platform.h> 21cc8bbe1aSYong Wu #include <linux/platform_device.h> 22cc8bbe1aSYong Wu #include <linux/pm_runtime.h> 23cc8bbe1aSYong Wu #include <soc/mediatek/smi.h> 24cc8bbe1aSYong Wu 25cc8bbe1aSYong Wu #define SMI_LARB_MMU_EN 0xf00 26cc8bbe1aSYong Wu 27cc8bbe1aSYong Wu struct mtk_smi { 28cc8bbe1aSYong Wu struct device *dev; 29cc8bbe1aSYong Wu struct clk *clk_apb, *clk_smi; 30cc8bbe1aSYong Wu }; 31cc8bbe1aSYong Wu 32cc8bbe1aSYong Wu struct mtk_smi_larb { /* larb: local arbiter */ 33cc8bbe1aSYong Wu struct mtk_smi smi; 34cc8bbe1aSYong Wu void __iomem *base; 35cc8bbe1aSYong Wu struct device *smi_common_dev; 36cc8bbe1aSYong Wu u32 *mmu; 37cc8bbe1aSYong Wu }; 38cc8bbe1aSYong Wu 39cc8bbe1aSYong Wu static int mtk_smi_enable(const struct mtk_smi *smi) 40cc8bbe1aSYong Wu { 41cc8bbe1aSYong Wu int ret; 42cc8bbe1aSYong Wu 43cc8bbe1aSYong Wu ret = pm_runtime_get_sync(smi->dev); 44cc8bbe1aSYong Wu if (ret < 0) 45cc8bbe1aSYong Wu return ret; 46cc8bbe1aSYong Wu 47cc8bbe1aSYong Wu ret = clk_prepare_enable(smi->clk_apb); 48cc8bbe1aSYong Wu if (ret) 49cc8bbe1aSYong Wu goto err_put_pm; 50cc8bbe1aSYong Wu 51cc8bbe1aSYong Wu ret = clk_prepare_enable(smi->clk_smi); 52cc8bbe1aSYong Wu if (ret) 53cc8bbe1aSYong Wu goto err_disable_apb; 54cc8bbe1aSYong Wu 55cc8bbe1aSYong Wu return 0; 56cc8bbe1aSYong Wu 57cc8bbe1aSYong Wu err_disable_apb: 58cc8bbe1aSYong Wu clk_disable_unprepare(smi->clk_apb); 59cc8bbe1aSYong Wu err_put_pm: 60cc8bbe1aSYong Wu pm_runtime_put_sync(smi->dev); 61cc8bbe1aSYong Wu return ret; 62cc8bbe1aSYong Wu } 63cc8bbe1aSYong Wu 64cc8bbe1aSYong Wu static void mtk_smi_disable(const struct mtk_smi *smi) 65cc8bbe1aSYong Wu { 66cc8bbe1aSYong Wu clk_disable_unprepare(smi->clk_smi); 67cc8bbe1aSYong Wu clk_disable_unprepare(smi->clk_apb); 68cc8bbe1aSYong Wu pm_runtime_put_sync(smi->dev); 69cc8bbe1aSYong Wu } 70cc8bbe1aSYong Wu 71cc8bbe1aSYong Wu int mtk_smi_larb_get(struct device *larbdev) 72cc8bbe1aSYong Wu { 73cc8bbe1aSYong Wu struct mtk_smi_larb *larb = dev_get_drvdata(larbdev); 74cc8bbe1aSYong Wu struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev); 75cc8bbe1aSYong Wu int ret; 76cc8bbe1aSYong Wu 77cc8bbe1aSYong Wu /* Enable the smi-common's power and clocks */ 78cc8bbe1aSYong Wu ret = mtk_smi_enable(common); 79cc8bbe1aSYong Wu if (ret) 80cc8bbe1aSYong Wu return ret; 81cc8bbe1aSYong Wu 82cc8bbe1aSYong Wu /* Enable the larb's power and clocks */ 83cc8bbe1aSYong Wu ret = mtk_smi_enable(&larb->smi); 84cc8bbe1aSYong Wu if (ret) { 85cc8bbe1aSYong Wu mtk_smi_disable(common); 86cc8bbe1aSYong Wu return ret; 87cc8bbe1aSYong Wu } 88cc8bbe1aSYong Wu 89cc8bbe1aSYong Wu /* Configure the iommu info for this larb */ 90cc8bbe1aSYong Wu writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN); 91cc8bbe1aSYong Wu 92cc8bbe1aSYong Wu return 0; 93cc8bbe1aSYong Wu } 94cb1b5dffSPhilipp Zabel EXPORT_SYMBOL_GPL(mtk_smi_larb_get); 95cc8bbe1aSYong Wu 96cc8bbe1aSYong Wu void mtk_smi_larb_put(struct device *larbdev) 97cc8bbe1aSYong Wu { 98cc8bbe1aSYong Wu struct mtk_smi_larb *larb = dev_get_drvdata(larbdev); 99cc8bbe1aSYong Wu struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev); 100cc8bbe1aSYong Wu 101cc8bbe1aSYong Wu /* 102cc8bbe1aSYong Wu * Don't de-configure the iommu info for this larb since there may be 103cc8bbe1aSYong Wu * several modules in this larb. 104cc8bbe1aSYong Wu * The iommu info will be reset after power off. 105cc8bbe1aSYong Wu */ 106cc8bbe1aSYong Wu 107cc8bbe1aSYong Wu mtk_smi_disable(&larb->smi); 108cc8bbe1aSYong Wu mtk_smi_disable(common); 109cc8bbe1aSYong Wu } 110cb1b5dffSPhilipp Zabel EXPORT_SYMBOL_GPL(mtk_smi_larb_put); 111cc8bbe1aSYong Wu 112cc8bbe1aSYong Wu static int 113cc8bbe1aSYong Wu mtk_smi_larb_bind(struct device *dev, struct device *master, void *data) 114cc8bbe1aSYong Wu { 115cc8bbe1aSYong Wu struct mtk_smi_larb *larb = dev_get_drvdata(dev); 116cc8bbe1aSYong Wu struct mtk_smi_iommu *smi_iommu = data; 117cc8bbe1aSYong Wu unsigned int i; 118cc8bbe1aSYong Wu 119cc8bbe1aSYong Wu for (i = 0; i < smi_iommu->larb_nr; i++) { 120cc8bbe1aSYong Wu if (dev == smi_iommu->larb_imu[i].dev) { 121cc8bbe1aSYong Wu /* The 'mmu' may be updated in iommu-attach/detach. */ 122cc8bbe1aSYong Wu larb->mmu = &smi_iommu->larb_imu[i].mmu; 123cc8bbe1aSYong Wu return 0; 124cc8bbe1aSYong Wu } 125cc8bbe1aSYong Wu } 126cc8bbe1aSYong Wu return -ENODEV; 127cc8bbe1aSYong Wu } 128cc8bbe1aSYong Wu 129cc8bbe1aSYong Wu static void 130cc8bbe1aSYong Wu mtk_smi_larb_unbind(struct device *dev, struct device *master, void *data) 131cc8bbe1aSYong Wu { 132cc8bbe1aSYong Wu /* Do nothing as the iommu is always enabled. */ 133cc8bbe1aSYong Wu } 134cc8bbe1aSYong Wu 135cc8bbe1aSYong Wu static const struct component_ops mtk_smi_larb_component_ops = { 136cc8bbe1aSYong Wu .bind = mtk_smi_larb_bind, 137cc8bbe1aSYong Wu .unbind = mtk_smi_larb_unbind, 138cc8bbe1aSYong Wu }; 139cc8bbe1aSYong Wu 140cc8bbe1aSYong Wu static int mtk_smi_larb_probe(struct platform_device *pdev) 141cc8bbe1aSYong Wu { 142cc8bbe1aSYong Wu struct mtk_smi_larb *larb; 143cc8bbe1aSYong Wu struct resource *res; 144cc8bbe1aSYong Wu struct device *dev = &pdev->dev; 145cc8bbe1aSYong Wu struct device_node *smi_node; 146cc8bbe1aSYong Wu struct platform_device *smi_pdev; 147cc8bbe1aSYong Wu 148cc8bbe1aSYong Wu if (!dev->pm_domain) 149cc8bbe1aSYong Wu return -EPROBE_DEFER; 150cc8bbe1aSYong Wu 151cc8bbe1aSYong Wu larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL); 152cc8bbe1aSYong Wu if (!larb) 153cc8bbe1aSYong Wu return -ENOMEM; 154cc8bbe1aSYong Wu 155cc8bbe1aSYong Wu res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 156cc8bbe1aSYong Wu larb->base = devm_ioremap_resource(dev, res); 157cc8bbe1aSYong Wu if (IS_ERR(larb->base)) 158cc8bbe1aSYong Wu return PTR_ERR(larb->base); 159cc8bbe1aSYong Wu 160cc8bbe1aSYong Wu larb->smi.clk_apb = devm_clk_get(dev, "apb"); 161cc8bbe1aSYong Wu if (IS_ERR(larb->smi.clk_apb)) 162cc8bbe1aSYong Wu return PTR_ERR(larb->smi.clk_apb); 163cc8bbe1aSYong Wu 164cc8bbe1aSYong Wu larb->smi.clk_smi = devm_clk_get(dev, "smi"); 165cc8bbe1aSYong Wu if (IS_ERR(larb->smi.clk_smi)) 166cc8bbe1aSYong Wu return PTR_ERR(larb->smi.clk_smi); 167cc8bbe1aSYong Wu larb->smi.dev = dev; 168cc8bbe1aSYong Wu 169cc8bbe1aSYong Wu smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0); 170cc8bbe1aSYong Wu if (!smi_node) 171cc8bbe1aSYong Wu return -EINVAL; 172cc8bbe1aSYong Wu 173cc8bbe1aSYong Wu smi_pdev = of_find_device_by_node(smi_node); 174cc8bbe1aSYong Wu of_node_put(smi_node); 175cc8bbe1aSYong Wu if (smi_pdev) { 176cc8bbe1aSYong Wu larb->smi_common_dev = &smi_pdev->dev; 177cc8bbe1aSYong Wu } else { 178cc8bbe1aSYong Wu dev_err(dev, "Failed to get the smi_common device\n"); 179cc8bbe1aSYong Wu return -EINVAL; 180cc8bbe1aSYong Wu } 181cc8bbe1aSYong Wu 182cc8bbe1aSYong Wu pm_runtime_enable(dev); 183cc8bbe1aSYong Wu platform_set_drvdata(pdev, larb); 184cc8bbe1aSYong Wu return component_add(dev, &mtk_smi_larb_component_ops); 185cc8bbe1aSYong Wu } 186cc8bbe1aSYong Wu 187cc8bbe1aSYong Wu static int mtk_smi_larb_remove(struct platform_device *pdev) 188cc8bbe1aSYong Wu { 189cc8bbe1aSYong Wu pm_runtime_disable(&pdev->dev); 190cc8bbe1aSYong Wu component_del(&pdev->dev, &mtk_smi_larb_component_ops); 191cc8bbe1aSYong Wu return 0; 192cc8bbe1aSYong Wu } 193cc8bbe1aSYong Wu 194cc8bbe1aSYong Wu static const struct of_device_id mtk_smi_larb_of_ids[] = { 195cc8bbe1aSYong Wu { .compatible = "mediatek,mt8173-smi-larb",}, 196cc8bbe1aSYong Wu {} 197cc8bbe1aSYong Wu }; 198cc8bbe1aSYong Wu 199cc8bbe1aSYong Wu static struct platform_driver mtk_smi_larb_driver = { 200cc8bbe1aSYong Wu .probe = mtk_smi_larb_probe, 201cc8bbe1aSYong Wu .remove = mtk_smi_larb_remove, 202cc8bbe1aSYong Wu .driver = { 203cc8bbe1aSYong Wu .name = "mtk-smi-larb", 204cc8bbe1aSYong Wu .of_match_table = mtk_smi_larb_of_ids, 205cc8bbe1aSYong Wu } 206cc8bbe1aSYong Wu }; 207cc8bbe1aSYong Wu 208cc8bbe1aSYong Wu static int mtk_smi_common_probe(struct platform_device *pdev) 209cc8bbe1aSYong Wu { 210cc8bbe1aSYong Wu struct device *dev = &pdev->dev; 211cc8bbe1aSYong Wu struct mtk_smi *common; 212cc8bbe1aSYong Wu 213cc8bbe1aSYong Wu if (!dev->pm_domain) 214cc8bbe1aSYong Wu return -EPROBE_DEFER; 215cc8bbe1aSYong Wu 216cc8bbe1aSYong Wu common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); 217cc8bbe1aSYong Wu if (!common) 218cc8bbe1aSYong Wu return -ENOMEM; 219cc8bbe1aSYong Wu common->dev = dev; 220cc8bbe1aSYong Wu 221cc8bbe1aSYong Wu common->clk_apb = devm_clk_get(dev, "apb"); 222cc8bbe1aSYong Wu if (IS_ERR(common->clk_apb)) 223cc8bbe1aSYong Wu return PTR_ERR(common->clk_apb); 224cc8bbe1aSYong Wu 225cc8bbe1aSYong Wu common->clk_smi = devm_clk_get(dev, "smi"); 226cc8bbe1aSYong Wu if (IS_ERR(common->clk_smi)) 227cc8bbe1aSYong Wu return PTR_ERR(common->clk_smi); 228cc8bbe1aSYong Wu 229cc8bbe1aSYong Wu pm_runtime_enable(dev); 230cc8bbe1aSYong Wu platform_set_drvdata(pdev, common); 231cc8bbe1aSYong Wu return 0; 232cc8bbe1aSYong Wu } 233cc8bbe1aSYong Wu 234cc8bbe1aSYong Wu static int mtk_smi_common_remove(struct platform_device *pdev) 235cc8bbe1aSYong Wu { 236cc8bbe1aSYong Wu pm_runtime_disable(&pdev->dev); 237cc8bbe1aSYong Wu return 0; 238cc8bbe1aSYong Wu } 239cc8bbe1aSYong Wu 240cc8bbe1aSYong Wu static const struct of_device_id mtk_smi_common_of_ids[] = { 241cc8bbe1aSYong Wu { .compatible = "mediatek,mt8173-smi-common", }, 242cc8bbe1aSYong Wu {} 243cc8bbe1aSYong Wu }; 244cc8bbe1aSYong Wu 245cc8bbe1aSYong Wu static struct platform_driver mtk_smi_common_driver = { 246cc8bbe1aSYong Wu .probe = mtk_smi_common_probe, 247cc8bbe1aSYong Wu .remove = mtk_smi_common_remove, 248cc8bbe1aSYong Wu .driver = { 249cc8bbe1aSYong Wu .name = "mtk-smi-common", 250cc8bbe1aSYong Wu .of_match_table = mtk_smi_common_of_ids, 251cc8bbe1aSYong Wu } 252cc8bbe1aSYong Wu }; 253cc8bbe1aSYong Wu 254cc8bbe1aSYong Wu static int __init mtk_smi_init(void) 255cc8bbe1aSYong Wu { 256cc8bbe1aSYong Wu int ret; 257cc8bbe1aSYong Wu 258cc8bbe1aSYong Wu ret = platform_driver_register(&mtk_smi_common_driver); 259cc8bbe1aSYong Wu if (ret != 0) { 260cc8bbe1aSYong Wu pr_err("Failed to register SMI driver\n"); 261cc8bbe1aSYong Wu return ret; 262cc8bbe1aSYong Wu } 263cc8bbe1aSYong Wu 264cc8bbe1aSYong Wu ret = platform_driver_register(&mtk_smi_larb_driver); 265cc8bbe1aSYong Wu if (ret != 0) { 266cc8bbe1aSYong Wu pr_err("Failed to register SMI-LARB driver\n"); 267cc8bbe1aSYong Wu goto err_unreg_smi; 268cc8bbe1aSYong Wu } 269cc8bbe1aSYong Wu return ret; 270cc8bbe1aSYong Wu 271cc8bbe1aSYong Wu err_unreg_smi: 272cc8bbe1aSYong Wu platform_driver_unregister(&mtk_smi_common_driver); 273cc8bbe1aSYong Wu return ret; 274cc8bbe1aSYong Wu } 275cc8bbe1aSYong Wu subsys_initcall(mtk_smi_init); 276