blob: 47df7d04009fcf9c2da1a358fd7d021d76c44a58 [file] [log] [blame]
Thomas Gleixner1802d0b2019-05-27 08:55:21 +02001// SPDX-License-Identifier: GPL-2.0-only
Yong Wucc8bbe12016-02-23 01:20:49 +08002/*
3 * Copyright (c) 2015-2016 MediaTek Inc.
4 * Author: Yong Wu <yong.wu@mediatek.com>
Yong Wucc8bbe12016-02-23 01:20:49 +08005 */
6#include <linux/clk.h>
7#include <linux/component.h>
8#include <linux/device.h>
9#include <linux/err.h>
10#include <linux/io.h>
Yong Wu4f608d32017-08-21 19:00:21 +080011#include <linux/module.h>
Yong Wucc8bbe12016-02-23 01:20:49 +080012#include <linux/of.h>
13#include <linux/of_platform.h>
14#include <linux/platform_device.h>
15#include <linux/pm_runtime.h>
16#include <soc/mediatek/smi.h>
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +080017#include <dt-bindings/memory/mt2701-larb-port.h>
Yong Wucc8bbe12016-02-23 01:20:49 +080018
Yong Wue6dec922017-08-21 19:00:16 +080019/* mt8173 */
Yong Wucc8bbe12016-02-23 01:20:49 +080020#define SMI_LARB_MMU_EN 0xf00
Yong Wue6dec922017-08-21 19:00:16 +080021
22/* mt2701 */
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +080023#define REG_SMI_SECUR_CON_BASE 0x5c0
24
25/* every register control 8 port, register offset 0x4 */
26#define REG_SMI_SECUR_CON_OFFSET(id) (((id) >> 3) << 2)
27#define REG_SMI_SECUR_CON_ADDR(id) \
28 (REG_SMI_SECUR_CON_BASE + REG_SMI_SECUR_CON_OFFSET(id))
29
30/*
31 * every port have 4 bit to control, bit[port + 3] control virtual or physical,
32 * bit[port + 2 : port + 1] control the domain, bit[port] control the security
33 * or non-security.
34 */
35#define SMI_SECUR_CON_VAL_MSK(id) (~(0xf << (((id) & 0x7) << 2)))
36#define SMI_SECUR_CON_VAL_VIRT(id) BIT((((id) & 0x7) << 2) + 3)
37/* mt2701 domain should be set to 3 */
38#define SMI_SECUR_CON_VAL_DOMAIN(id) (0x3 << ((((id) & 0x7) << 2) + 1))
39
Yong Wue6dec922017-08-21 19:00:16 +080040/* mt2712 */
41#define SMI_LARB_NONSEC_CON(id) (0x380 + ((id) * 4))
42#define F_MMU_EN BIT(0)
43
Yong Wu42d42c72019-08-24 11:01:49 +080044enum mtk_smi_gen {
45 MTK_SMI_GEN1,
46 MTK_SMI_GEN2
47};
48
49struct mtk_smi_common_plat {
50 enum mtk_smi_gen gen;
51};
52
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +080053struct mtk_smi_larb_gen {
Honghui Zhang363932c2017-08-04 09:32:26 +080054 bool need_larbid;
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +080055 int port_in_larb[MTK_LARB_NR_MAX + 1];
56 void (*config_port)(struct device *);
Yong Wu2e9b0902019-08-24 11:01:48 +080057 unsigned int larb_direct_to_common_mask;
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +080058};
Yong Wucc8bbe12016-02-23 01:20:49 +080059
60struct mtk_smi {
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +080061 struct device *dev;
62 struct clk *clk_apb, *clk_smi;
63 struct clk *clk_async; /*only needed by mt2701*/
64 void __iomem *smi_ao_base;
Yong Wu42d42c72019-08-24 11:01:49 +080065
66 const struct mtk_smi_common_plat *plat;
Yong Wucc8bbe12016-02-23 01:20:49 +080067};
68
69struct mtk_smi_larb { /* larb: local arbiter */
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +080070 struct mtk_smi smi;
71 void __iomem *base;
72 struct device *smi_common_dev;
73 const struct mtk_smi_larb_gen *larb_gen;
74 int larbid;
75 u32 *mmu;
76};
77
Yong Wucc8bbe12016-02-23 01:20:49 +080078static int mtk_smi_enable(const struct mtk_smi *smi)
79{
80 int ret;
81
82 ret = pm_runtime_get_sync(smi->dev);
83 if (ret < 0)
84 return ret;
85
86 ret = clk_prepare_enable(smi->clk_apb);
87 if (ret)
88 goto err_put_pm;
89
90 ret = clk_prepare_enable(smi->clk_smi);
91 if (ret)
92 goto err_disable_apb;
93
94 return 0;
95
96err_disable_apb:
97 clk_disable_unprepare(smi->clk_apb);
98err_put_pm:
99 pm_runtime_put_sync(smi->dev);
100 return ret;
101}
102
103static void mtk_smi_disable(const struct mtk_smi *smi)
104{
105 clk_disable_unprepare(smi->clk_smi);
106 clk_disable_unprepare(smi->clk_apb);
107 pm_runtime_put_sync(smi->dev);
108}
109
110int mtk_smi_larb_get(struct device *larbdev)
111{
112 struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800113 const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
Yong Wucc8bbe12016-02-23 01:20:49 +0800114 struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev);
115 int ret;
116
117 /* Enable the smi-common's power and clocks */
118 ret = mtk_smi_enable(common);
119 if (ret)
120 return ret;
121
122 /* Enable the larb's power and clocks */
123 ret = mtk_smi_enable(&larb->smi);
124 if (ret) {
125 mtk_smi_disable(common);
126 return ret;
127 }
128
129 /* Configure the iommu info for this larb */
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800130 larb_gen->config_port(larbdev);
Yong Wucc8bbe12016-02-23 01:20:49 +0800131
132 return 0;
133}
Philipp Zabelcb1b5df2016-04-27 10:48:00 +0200134EXPORT_SYMBOL_GPL(mtk_smi_larb_get);
Yong Wucc8bbe12016-02-23 01:20:49 +0800135
136void mtk_smi_larb_put(struct device *larbdev)
137{
138 struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
139 struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev);
140
141 /*
142 * Don't de-configure the iommu info for this larb since there may be
143 * several modules in this larb.
144 * The iommu info will be reset after power off.
145 */
146
147 mtk_smi_disable(&larb->smi);
148 mtk_smi_disable(common);
149}
Philipp Zabelcb1b5df2016-04-27 10:48:00 +0200150EXPORT_SYMBOL_GPL(mtk_smi_larb_put);
Yong Wucc8bbe12016-02-23 01:20:49 +0800151
152static int
153mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
154{
155 struct mtk_smi_larb *larb = dev_get_drvdata(dev);
156 struct mtk_smi_iommu *smi_iommu = data;
157 unsigned int i;
158
Yong Wue6dec922017-08-21 19:00:16 +0800159 if (larb->larb_gen->need_larbid) {
160 larb->mmu = &smi_iommu->larb_imu[larb->larbid].mmu;
161 return 0;
162 }
163
164 /*
165 * If there is no larbid property, Loop to find the corresponding
166 * iommu information.
167 */
Yong Wucc8bbe12016-02-23 01:20:49 +0800168 for (i = 0; i < smi_iommu->larb_nr; i++) {
169 if (dev == smi_iommu->larb_imu[i].dev) {
170 /* The 'mmu' may be updated in iommu-attach/detach. */
171 larb->mmu = &smi_iommu->larb_imu[i].mmu;
172 return 0;
173 }
174 }
175 return -ENODEV;
176}
177
Yong Wu2e9b0902019-08-24 11:01:48 +0800178static void mtk_smi_larb_config_port_gen2_general(struct device *dev)
Yong Wue6dec922017-08-21 19:00:16 +0800179{
180 struct mtk_smi_larb *larb = dev_get_drvdata(dev);
181 u32 reg;
182 int i;
183
Yong Wu2e9b0902019-08-24 11:01:48 +0800184 if (BIT(larb->larbid) & larb->larb_gen->larb_direct_to_common_mask)
Yong Wue6dec922017-08-21 19:00:16 +0800185 return;
186
187 for_each_set_bit(i, (unsigned long *)larb->mmu, 32) {
188 reg = readl_relaxed(larb->base + SMI_LARB_NONSEC_CON(i));
189 reg |= F_MMU_EN;
190 writel(reg, larb->base + SMI_LARB_NONSEC_CON(i));
191 }
192}
193
194static void mtk_smi_larb_config_port_mt8173(struct device *dev)
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800195{
196 struct mtk_smi_larb *larb = dev_get_drvdata(dev);
197
198 writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN);
199}
200
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800201static void mtk_smi_larb_config_port_gen1(struct device *dev)
202{
203 struct mtk_smi_larb *larb = dev_get_drvdata(dev);
204 const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
205 struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev);
206 int i, m4u_port_id, larb_port_num;
207 u32 sec_con_val, reg_val;
208
209 m4u_port_id = larb_gen->port_in_larb[larb->larbid];
210 larb_port_num = larb_gen->port_in_larb[larb->larbid + 1]
211 - larb_gen->port_in_larb[larb->larbid];
212
213 for (i = 0; i < larb_port_num; i++, m4u_port_id++) {
214 if (*larb->mmu & BIT(i)) {
215 /* bit[port + 3] controls the virtual or physical */
216 sec_con_val = SMI_SECUR_CON_VAL_VIRT(m4u_port_id);
217 } else {
218 /* do not need to enable m4u for this port */
219 continue;
220 }
221 reg_val = readl(common->smi_ao_base
222 + REG_SMI_SECUR_CON_ADDR(m4u_port_id));
223 reg_val &= SMI_SECUR_CON_VAL_MSK(m4u_port_id);
224 reg_val |= sec_con_val;
225 reg_val |= SMI_SECUR_CON_VAL_DOMAIN(m4u_port_id);
226 writel(reg_val,
227 common->smi_ao_base
228 + REG_SMI_SECUR_CON_ADDR(m4u_port_id));
229 }
230}
231
Yong Wucc8bbe12016-02-23 01:20:49 +0800232static void
233mtk_smi_larb_unbind(struct device *dev, struct device *master, void *data)
234{
235 /* Do nothing as the iommu is always enabled. */
236}
237
238static const struct component_ops mtk_smi_larb_component_ops = {
239 .bind = mtk_smi_larb_bind,
240 .unbind = mtk_smi_larb_unbind,
241};
242
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800243static const struct mtk_smi_larb_gen mtk_smi_larb_mt8173 = {
244 /* mt8173 do not need the port in larb */
Yong Wue6dec922017-08-21 19:00:16 +0800245 .config_port = mtk_smi_larb_config_port_mt8173,
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800246};
247
248static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = {
Honghui Zhang363932c2017-08-04 09:32:26 +0800249 .need_larbid = true,
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800250 .port_in_larb = {
251 LARB0_PORT_OFFSET, LARB1_PORT_OFFSET,
252 LARB2_PORT_OFFSET, LARB3_PORT_OFFSET
253 },
254 .config_port = mtk_smi_larb_config_port_gen1,
255};
256
Yong Wue6dec922017-08-21 19:00:16 +0800257static const struct mtk_smi_larb_gen mtk_smi_larb_mt2712 = {
258 .need_larbid = true,
Yong Wu2e9b0902019-08-24 11:01:48 +0800259 .config_port = mtk_smi_larb_config_port_gen2_general,
260 .larb_direct_to_common_mask = BIT(8) | BIT(9), /* bdpsys */
Yong Wue6dec922017-08-21 19:00:16 +0800261};
262
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800263static const struct of_device_id mtk_smi_larb_of_ids[] = {
264 {
265 .compatible = "mediatek,mt8173-smi-larb",
266 .data = &mtk_smi_larb_mt8173
267 },
268 {
269 .compatible = "mediatek,mt2701-smi-larb",
270 .data = &mtk_smi_larb_mt2701
271 },
Yong Wue6dec922017-08-21 19:00:16 +0800272 {
273 .compatible = "mediatek,mt2712-smi-larb",
274 .data = &mtk_smi_larb_mt2712
275 },
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800276 {}
277};
278
Yong Wucc8bbe12016-02-23 01:20:49 +0800279static int mtk_smi_larb_probe(struct platform_device *pdev)
280{
281 struct mtk_smi_larb *larb;
282 struct resource *res;
283 struct device *dev = &pdev->dev;
284 struct device_node *smi_node;
285 struct platform_device *smi_pdev;
Honghui Zhang363932c2017-08-04 09:32:26 +0800286 int err;
Yong Wucc8bbe12016-02-23 01:20:49 +0800287
Yong Wucc8bbe12016-02-23 01:20:49 +0800288 larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL);
289 if (!larb)
290 return -ENOMEM;
291
Honghui Zhang75487862017-08-04 09:32:25 +0800292 larb->larb_gen = of_device_get_match_data(dev);
Yong Wucc8bbe12016-02-23 01:20:49 +0800293 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
294 larb->base = devm_ioremap_resource(dev, res);
295 if (IS_ERR(larb->base))
296 return PTR_ERR(larb->base);
297
298 larb->smi.clk_apb = devm_clk_get(dev, "apb");
299 if (IS_ERR(larb->smi.clk_apb))
300 return PTR_ERR(larb->smi.clk_apb);
301
302 larb->smi.clk_smi = devm_clk_get(dev, "smi");
303 if (IS_ERR(larb->smi.clk_smi))
304 return PTR_ERR(larb->smi.clk_smi);
305 larb->smi.dev = dev;
306
Honghui Zhang363932c2017-08-04 09:32:26 +0800307 if (larb->larb_gen->need_larbid) {
308 err = of_property_read_u32(dev->of_node, "mediatek,larb-id",
309 &larb->larbid);
310 if (err) {
311 dev_err(dev, "missing larbid property\n");
312 return err;
313 }
314 }
315
Yong Wucc8bbe12016-02-23 01:20:49 +0800316 smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0);
317 if (!smi_node)
318 return -EINVAL;
319
320 smi_pdev = of_find_device_by_node(smi_node);
321 of_node_put(smi_node);
322 if (smi_pdev) {
Yong Wu4f608d32017-08-21 19:00:21 +0800323 if (!platform_get_drvdata(smi_pdev))
324 return -EPROBE_DEFER;
Yong Wucc8bbe12016-02-23 01:20:49 +0800325 larb->smi_common_dev = &smi_pdev->dev;
326 } else {
327 dev_err(dev, "Failed to get the smi_common device\n");
328 return -EINVAL;
329 }
330
331 pm_runtime_enable(dev);
332 platform_set_drvdata(pdev, larb);
333 return component_add(dev, &mtk_smi_larb_component_ops);
334}
335
336static int mtk_smi_larb_remove(struct platform_device *pdev)
337{
338 pm_runtime_disable(&pdev->dev);
339 component_del(&pdev->dev, &mtk_smi_larb_component_ops);
340 return 0;
341}
342
Yong Wucc8bbe12016-02-23 01:20:49 +0800343static struct platform_driver mtk_smi_larb_driver = {
344 .probe = mtk_smi_larb_probe,
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800345 .remove = mtk_smi_larb_remove,
Yong Wucc8bbe12016-02-23 01:20:49 +0800346 .driver = {
347 .name = "mtk-smi-larb",
348 .of_match_table = mtk_smi_larb_of_ids,
349 }
350};
351
Yong Wu42d42c72019-08-24 11:01:49 +0800352static const struct mtk_smi_common_plat mtk_smi_common_gen1 = {
353 .gen = MTK_SMI_GEN1,
354};
355
356static const struct mtk_smi_common_plat mtk_smi_common_gen2 = {
357 .gen = MTK_SMI_GEN2,
358};
359
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800360static const struct of_device_id mtk_smi_common_of_ids[] = {
361 {
362 .compatible = "mediatek,mt8173-smi-common",
Yong Wu42d42c72019-08-24 11:01:49 +0800363 .data = &mtk_smi_common_gen2,
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800364 },
365 {
366 .compatible = "mediatek,mt2701-smi-common",
Yong Wu42d42c72019-08-24 11:01:49 +0800367 .data = &mtk_smi_common_gen1,
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800368 },
Yong Wue6dec922017-08-21 19:00:16 +0800369 {
370 .compatible = "mediatek,mt2712-smi-common",
Yong Wu42d42c72019-08-24 11:01:49 +0800371 .data = &mtk_smi_common_gen2,
Yong Wue6dec922017-08-21 19:00:16 +0800372 },
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800373 {}
374};
375
Yong Wucc8bbe12016-02-23 01:20:49 +0800376static int mtk_smi_common_probe(struct platform_device *pdev)
377{
378 struct device *dev = &pdev->dev;
379 struct mtk_smi *common;
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800380 struct resource *res;
Arvind Yadav46cc8152017-08-10 10:47:32 +0530381 int ret;
Yong Wucc8bbe12016-02-23 01:20:49 +0800382
Yong Wucc8bbe12016-02-23 01:20:49 +0800383 common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL);
384 if (!common)
385 return -ENOMEM;
386 common->dev = dev;
Yong Wu42d42c72019-08-24 11:01:49 +0800387 common->plat = of_device_get_match_data(dev);
Yong Wucc8bbe12016-02-23 01:20:49 +0800388
389 common->clk_apb = devm_clk_get(dev, "apb");
390 if (IS_ERR(common->clk_apb))
391 return PTR_ERR(common->clk_apb);
392
393 common->clk_smi = devm_clk_get(dev, "smi");
394 if (IS_ERR(common->clk_smi))
395 return PTR_ERR(common->clk_smi);
396
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800397 /*
398 * for mtk smi gen 1, we need to get the ao(always on) base to config
399 * m4u port, and we need to enable the aync clock for transform the smi
400 * clock into emi clock domain, but for mtk smi gen2, there's no smi ao
401 * base.
402 */
Yong Wu42d42c72019-08-24 11:01:49 +0800403 if (common->plat->gen == MTK_SMI_GEN1) {
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800404 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
405 common->smi_ao_base = devm_ioremap_resource(dev, res);
406 if (IS_ERR(common->smi_ao_base))
407 return PTR_ERR(common->smi_ao_base);
408
409 common->clk_async = devm_clk_get(dev, "async");
410 if (IS_ERR(common->clk_async))
411 return PTR_ERR(common->clk_async);
412
Arvind Yadav46cc8152017-08-10 10:47:32 +0530413 ret = clk_prepare_enable(common->clk_async);
414 if (ret)
415 return ret;
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800416 }
Yong Wucc8bbe12016-02-23 01:20:49 +0800417 pm_runtime_enable(dev);
418 platform_set_drvdata(pdev, common);
419 return 0;
420}
421
422static int mtk_smi_common_remove(struct platform_device *pdev)
423{
424 pm_runtime_disable(&pdev->dev);
425 return 0;
426}
427
Yong Wucc8bbe12016-02-23 01:20:49 +0800428static struct platform_driver mtk_smi_common_driver = {
429 .probe = mtk_smi_common_probe,
430 .remove = mtk_smi_common_remove,
431 .driver = {
432 .name = "mtk-smi-common",
433 .of_match_table = mtk_smi_common_of_ids,
434 }
435};
436
437static int __init mtk_smi_init(void)
438{
439 int ret;
440
441 ret = platform_driver_register(&mtk_smi_common_driver);
442 if (ret != 0) {
443 pr_err("Failed to register SMI driver\n");
444 return ret;
445 }
446
447 ret = platform_driver_register(&mtk_smi_larb_driver);
448 if (ret != 0) {
449 pr_err("Failed to register SMI-LARB driver\n");
450 goto err_unreg_smi;
451 }
452 return ret;
453
454err_unreg_smi:
455 platform_driver_unregister(&mtk_smi_common_driver);
456 return ret;
457}
Honghui Zhang3c8f4ad2016-06-08 17:50:59 +0800458
Yong Wu4f608d32017-08-21 19:00:21 +0800459module_init(mtk_smi_init);