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