blob: 55ab08ffc01099313545129327149ccee83f3bf5 [file] [log] [blame]
Sascha Hauerc84e3582015-06-24 08:17:04 +02001/*
2 * Copyright (c) 2015 Pengutronix, Sascha Hauer <kernel@pengutronix.de>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13#include <linux/clk.h>
14#include <linux/delay.h>
15#include <linux/io.h>
16#include <linux/kernel.h>
17#include <linux/mfd/syscon.h>
Matthias Brugger9dd068a2015-07-31 17:03:13 +020018#include <linux/module.h>
Sascha Hauerc84e3582015-06-24 08:17:04 +020019#include <linux/of_device.h>
20#include <linux/platform_device.h>
21#include <linux/pm_domain.h>
22#include <linux/regmap.h>
23#include <linux/soc/mediatek/infracfg.h>
24#include <dt-bindings/power/mt8173-power.h>
25
26#define SPM_VDE_PWR_CON 0x0210
27#define SPM_MFG_PWR_CON 0x0214
28#define SPM_VEN_PWR_CON 0x0230
29#define SPM_ISP_PWR_CON 0x0238
30#define SPM_DIS_PWR_CON 0x023c
31#define SPM_VEN2_PWR_CON 0x0298
32#define SPM_AUDIO_PWR_CON 0x029c
33#define SPM_MFG_2D_PWR_CON 0x02c0
34#define SPM_MFG_ASYNC_PWR_CON 0x02c4
35#define SPM_USB_PWR_CON 0x02cc
36#define SPM_PWR_STATUS 0x060c
37#define SPM_PWR_STATUS_2ND 0x0610
38
39#define PWR_RST_B_BIT BIT(0)
40#define PWR_ISO_BIT BIT(1)
41#define PWR_ON_BIT BIT(2)
42#define PWR_ON_2ND_BIT BIT(3)
43#define PWR_CLK_DIS_BIT BIT(4)
44
45#define PWR_STATUS_DISP BIT(3)
46#define PWR_STATUS_MFG BIT(4)
47#define PWR_STATUS_ISP BIT(5)
48#define PWR_STATUS_VDEC BIT(7)
49#define PWR_STATUS_VENC_LT BIT(20)
50#define PWR_STATUS_VENC BIT(21)
51#define PWR_STATUS_MFG_2D BIT(22)
52#define PWR_STATUS_MFG_ASYNC BIT(23)
53#define PWR_STATUS_AUDIO BIT(24)
54#define PWR_STATUS_USB BIT(25)
55
56enum clk_id {
57 MT8173_CLK_MM,
58 MT8173_CLK_MFG,
59 MT8173_CLK_NONE,
60 MT8173_CLK_MAX = MT8173_CLK_NONE,
61};
62
63struct scp_domain_data {
64 const char *name;
65 u32 sta_mask;
66 int ctl_offs;
67 u32 sram_pdn_bits;
68 u32 sram_pdn_ack_bits;
69 u32 bus_prot_mask;
70 enum clk_id clk_id;
Eddie Huang47e90152015-08-26 15:14:41 +080071 bool active_wakeup;
Sascha Hauerc84e3582015-06-24 08:17:04 +020072};
73
74static const struct scp_domain_data scp_domain_data[] __initconst = {
75 [MT8173_POWER_DOMAIN_VDEC] = {
76 .name = "vdec",
77 .sta_mask = PWR_STATUS_VDEC,
78 .ctl_offs = SPM_VDE_PWR_CON,
79 .sram_pdn_bits = GENMASK(11, 8),
80 .sram_pdn_ack_bits = GENMASK(12, 12),
81 .clk_id = MT8173_CLK_MM,
82 },
83 [MT8173_POWER_DOMAIN_VENC] = {
84 .name = "venc",
85 .sta_mask = PWR_STATUS_VENC,
86 .ctl_offs = SPM_VEN_PWR_CON,
87 .sram_pdn_bits = GENMASK(11, 8),
88 .sram_pdn_ack_bits = GENMASK(15, 12),
89 .clk_id = MT8173_CLK_MM,
90 },
91 [MT8173_POWER_DOMAIN_ISP] = {
92 .name = "isp",
93 .sta_mask = PWR_STATUS_ISP,
94 .ctl_offs = SPM_ISP_PWR_CON,
95 .sram_pdn_bits = GENMASK(11, 8),
96 .sram_pdn_ack_bits = GENMASK(13, 12),
97 .clk_id = MT8173_CLK_MM,
98 },
99 [MT8173_POWER_DOMAIN_MM] = {
100 .name = "mm",
101 .sta_mask = PWR_STATUS_DISP,
102 .ctl_offs = SPM_DIS_PWR_CON,
103 .sram_pdn_bits = GENMASK(11, 8),
104 .sram_pdn_ack_bits = GENMASK(12, 12),
105 .clk_id = MT8173_CLK_MM,
106 .bus_prot_mask = MT8173_TOP_AXI_PROT_EN_MM_M0 |
107 MT8173_TOP_AXI_PROT_EN_MM_M1,
108 },
109 [MT8173_POWER_DOMAIN_VENC_LT] = {
110 .name = "venc_lt",
111 .sta_mask = PWR_STATUS_VENC_LT,
112 .ctl_offs = SPM_VEN2_PWR_CON,
113 .sram_pdn_bits = GENMASK(11, 8),
114 .sram_pdn_ack_bits = GENMASK(15, 12),
115 .clk_id = MT8173_CLK_MM,
116 },
117 [MT8173_POWER_DOMAIN_AUDIO] = {
118 .name = "audio",
119 .sta_mask = PWR_STATUS_AUDIO,
120 .ctl_offs = SPM_AUDIO_PWR_CON,
121 .sram_pdn_bits = GENMASK(11, 8),
122 .sram_pdn_ack_bits = GENMASK(15, 12),
123 .clk_id = MT8173_CLK_NONE,
124 },
125 [MT8173_POWER_DOMAIN_USB] = {
126 .name = "usb",
127 .sta_mask = PWR_STATUS_USB,
128 .ctl_offs = SPM_USB_PWR_CON,
129 .sram_pdn_bits = GENMASK(11, 8),
130 .sram_pdn_ack_bits = GENMASK(15, 12),
131 .clk_id = MT8173_CLK_NONE,
Eddie Huang47e90152015-08-26 15:14:41 +0800132 .active_wakeup = true,
Sascha Hauerc84e3582015-06-24 08:17:04 +0200133 },
134 [MT8173_POWER_DOMAIN_MFG_ASYNC] = {
135 .name = "mfg_async",
136 .sta_mask = PWR_STATUS_MFG_ASYNC,
137 .ctl_offs = SPM_MFG_ASYNC_PWR_CON,
138 .sram_pdn_bits = GENMASK(11, 8),
139 .sram_pdn_ack_bits = 0,
140 .clk_id = MT8173_CLK_MFG,
141 },
142 [MT8173_POWER_DOMAIN_MFG_2D] = {
143 .name = "mfg_2d",
144 .sta_mask = PWR_STATUS_MFG_2D,
145 .ctl_offs = SPM_MFG_2D_PWR_CON,
146 .sram_pdn_bits = GENMASK(11, 8),
147 .sram_pdn_ack_bits = GENMASK(13, 12),
148 .clk_id = MT8173_CLK_NONE,
149 },
150 [MT8173_POWER_DOMAIN_MFG] = {
151 .name = "mfg",
152 .sta_mask = PWR_STATUS_MFG,
153 .ctl_offs = SPM_MFG_PWR_CON,
154 .sram_pdn_bits = GENMASK(13, 8),
155 .sram_pdn_ack_bits = GENMASK(21, 16),
156 .clk_id = MT8173_CLK_NONE,
157 .bus_prot_mask = MT8173_TOP_AXI_PROT_EN_MFG_S |
158 MT8173_TOP_AXI_PROT_EN_MFG_M0 |
159 MT8173_TOP_AXI_PROT_EN_MFG_M1 |
160 MT8173_TOP_AXI_PROT_EN_MFG_SNOOP_OUT,
161 },
162};
163
164#define NUM_DOMAINS ARRAY_SIZE(scp_domain_data)
165
166struct scp;
167
168struct scp_domain {
169 struct generic_pm_domain genpd;
170 struct scp *scp;
171 struct clk *clk;
172 u32 sta_mask;
173 void __iomem *ctl_addr;
174 u32 sram_pdn_bits;
175 u32 sram_pdn_ack_bits;
176 u32 bus_prot_mask;
Eddie Huang47e90152015-08-26 15:14:41 +0800177 bool active_wakeup;
Sascha Hauerc84e3582015-06-24 08:17:04 +0200178};
179
180struct scp {
181 struct scp_domain domains[NUM_DOMAINS];
182 struct genpd_onecell_data pd_data;
183 struct device *dev;
184 void __iomem *base;
185 struct regmap *infracfg;
186};
187
188static int scpsys_domain_is_on(struct scp_domain *scpd)
189{
190 struct scp *scp = scpd->scp;
191
192 u32 status = readl(scp->base + SPM_PWR_STATUS) & scpd->sta_mask;
193 u32 status2 = readl(scp->base + SPM_PWR_STATUS_2ND) & scpd->sta_mask;
194
195 /*
196 * A domain is on when both status bits are set. If only one is set
197 * return an error. This happens while powering up a domain
198 */
199
200 if (status && status2)
201 return true;
202 if (!status && !status2)
203 return false;
204
205 return -EINVAL;
206}
207
208static int scpsys_power_on(struct generic_pm_domain *genpd)
209{
210 struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd);
211 struct scp *scp = scpd->scp;
212 unsigned long timeout;
213 bool expired;
214 void __iomem *ctl_addr = scpd->ctl_addr;
215 u32 sram_pdn_ack = scpd->sram_pdn_ack_bits;
216 u32 val;
217 int ret;
218
219 if (scpd->clk) {
220 ret = clk_prepare_enable(scpd->clk);
221 if (ret)
222 goto err_clk;
223 }
224
225 val = readl(ctl_addr);
226 val |= PWR_ON_BIT;
227 writel(val, ctl_addr);
228 val |= PWR_ON_2ND_BIT;
229 writel(val, ctl_addr);
230
231 /* wait until PWR_ACK = 1 */
232 timeout = jiffies + HZ;
233 expired = false;
234 while (1) {
235 ret = scpsys_domain_is_on(scpd);
236 if (ret > 0)
237 break;
238
239 if (expired) {
240 ret = -ETIMEDOUT;
241 goto err_pwr_ack;
242 }
243
244 cpu_relax();
245
246 if (time_after(jiffies, timeout))
247 expired = true;
248 }
249
250 val &= ~PWR_CLK_DIS_BIT;
251 writel(val, ctl_addr);
252
253 val &= ~PWR_ISO_BIT;
254 writel(val, ctl_addr);
255
256 val |= PWR_RST_B_BIT;
257 writel(val, ctl_addr);
258
259 val &= ~scpd->sram_pdn_bits;
260 writel(val, ctl_addr);
261
262 /* wait until SRAM_PDN_ACK all 0 */
263 timeout = jiffies + HZ;
264 expired = false;
265 while (sram_pdn_ack && (readl(ctl_addr) & sram_pdn_ack)) {
266
267 if (expired) {
268 ret = -ETIMEDOUT;
269 goto err_pwr_ack;
270 }
271
272 cpu_relax();
273
274 if (time_after(jiffies, timeout))
275 expired = true;
276 }
277
278 if (scpd->bus_prot_mask) {
279 ret = mtk_infracfg_clear_bus_protection(scp->infracfg,
280 scpd->bus_prot_mask);
281 if (ret)
282 goto err_pwr_ack;
283 }
284
285 return 0;
286
287err_pwr_ack:
288 clk_disable_unprepare(scpd->clk);
289err_clk:
290 dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name);
291
292 return ret;
293}
294
295static int scpsys_power_off(struct generic_pm_domain *genpd)
296{
297 struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd);
298 struct scp *scp = scpd->scp;
299 unsigned long timeout;
300 bool expired;
301 void __iomem *ctl_addr = scpd->ctl_addr;
302 u32 pdn_ack = scpd->sram_pdn_ack_bits;
303 u32 val;
304 int ret;
305
306 if (scpd->bus_prot_mask) {
307 ret = mtk_infracfg_set_bus_protection(scp->infracfg,
308 scpd->bus_prot_mask);
309 if (ret)
310 goto out;
311 }
312
313 val = readl(ctl_addr);
314 val |= scpd->sram_pdn_bits;
315 writel(val, ctl_addr);
316
317 /* wait until SRAM_PDN_ACK all 1 */
318 timeout = jiffies + HZ;
319 expired = false;
320 while (pdn_ack && (readl(ctl_addr) & pdn_ack) != pdn_ack) {
321 if (expired) {
322 ret = -ETIMEDOUT;
323 goto out;
324 }
325
326 cpu_relax();
327
328 if (time_after(jiffies, timeout))
329 expired = true;
330 }
331
332 val |= PWR_ISO_BIT;
333 writel(val, ctl_addr);
334
335 val &= ~PWR_RST_B_BIT;
336 writel(val, ctl_addr);
337
338 val |= PWR_CLK_DIS_BIT;
339 writel(val, ctl_addr);
340
341 val &= ~PWR_ON_BIT;
342 writel(val, ctl_addr);
343
344 val &= ~PWR_ON_2ND_BIT;
345 writel(val, ctl_addr);
346
347 /* wait until PWR_ACK = 0 */
348 timeout = jiffies + HZ;
349 expired = false;
350 while (1) {
351 ret = scpsys_domain_is_on(scpd);
352 if (ret == 0)
353 break;
354
355 if (expired) {
356 ret = -ETIMEDOUT;
357 goto out;
358 }
359
360 cpu_relax();
361
362 if (time_after(jiffies, timeout))
363 expired = true;
364 }
365
366 if (scpd->clk)
367 clk_disable_unprepare(scpd->clk);
368
369 return 0;
370
371out:
372 dev_err(scp->dev, "Failed to power off domain %s\n", genpd->name);
373
374 return ret;
375}
376
Eddie Huang47e90152015-08-26 15:14:41 +0800377static bool scpsys_active_wakeup(struct device *dev)
378{
379 struct generic_pm_domain *genpd;
380 struct scp_domain *scpd;
381
382 genpd = pd_to_genpd(dev->pm_domain);
383 scpd = container_of(genpd, struct scp_domain, genpd);
384
385 return scpd->active_wakeup;
386}
387
Sascha Hauerc84e3582015-06-24 08:17:04 +0200388static int __init scpsys_probe(struct platform_device *pdev)
389{
390 struct genpd_onecell_data *pd_data;
391 struct resource *res;
392 int i, ret;
393 struct scp *scp;
394 struct clk *clk[MT8173_CLK_MAX];
395
396 scp = devm_kzalloc(&pdev->dev, sizeof(*scp), GFP_KERNEL);
397 if (!scp)
398 return -ENOMEM;
399
400 scp->dev = &pdev->dev;
401
402 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
403 scp->base = devm_ioremap_resource(&pdev->dev, res);
404 if (IS_ERR(scp->base))
405 return PTR_ERR(scp->base);
406
407 pd_data = &scp->pd_data;
408
409 pd_data->domains = devm_kzalloc(&pdev->dev,
410 sizeof(*pd_data->domains) * NUM_DOMAINS, GFP_KERNEL);
411 if (!pd_data->domains)
412 return -ENOMEM;
413
414 clk[MT8173_CLK_MM] = devm_clk_get(&pdev->dev, "mm");
415 if (IS_ERR(clk[MT8173_CLK_MM]))
416 return PTR_ERR(clk[MT8173_CLK_MM]);
417
418 clk[MT8173_CLK_MFG] = devm_clk_get(&pdev->dev, "mfg");
419 if (IS_ERR(clk[MT8173_CLK_MFG]))
420 return PTR_ERR(clk[MT8173_CLK_MFG]);
421
422 scp->infracfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
423 "infracfg");
424 if (IS_ERR(scp->infracfg)) {
425 dev_err(&pdev->dev, "Cannot find infracfg controller: %ld\n",
426 PTR_ERR(scp->infracfg));
427 return PTR_ERR(scp->infracfg);
428 }
429
430 pd_data->num_domains = NUM_DOMAINS;
431
432 for (i = 0; i < NUM_DOMAINS; i++) {
433 struct scp_domain *scpd = &scp->domains[i];
434 struct generic_pm_domain *genpd = &scpd->genpd;
435 const struct scp_domain_data *data = &scp_domain_data[i];
436
437 pd_data->domains[i] = genpd;
438 scpd->scp = scp;
439
440 scpd->sta_mask = data->sta_mask;
441 scpd->ctl_addr = scp->base + data->ctl_offs;
442 scpd->sram_pdn_bits = data->sram_pdn_bits;
443 scpd->sram_pdn_ack_bits = data->sram_pdn_ack_bits;
444 scpd->bus_prot_mask = data->bus_prot_mask;
Eddie Huang47e90152015-08-26 15:14:41 +0800445 scpd->active_wakeup = data->active_wakeup;
Sascha Hauerc84e3582015-06-24 08:17:04 +0200446 if (data->clk_id != MT8173_CLK_NONE)
447 scpd->clk = clk[data->clk_id];
448
449 genpd->name = data->name;
450 genpd->power_off = scpsys_power_off;
451 genpd->power_on = scpsys_power_on;
Eddie Huang47e90152015-08-26 15:14:41 +0800452 genpd->dev_ops.active_wakeup = scpsys_active_wakeup;
Sascha Hauerc84e3582015-06-24 08:17:04 +0200453
454 /*
455 * Initially turn on all domains to make the domains usable
456 * with !CONFIG_PM and to get the hardware in sync with the
457 * software. The unused domains will be switched off during
458 * late_init time.
459 */
460 genpd->power_on(genpd);
461
462 pm_genpd_init(genpd, NULL, false);
463 }
464
465 /*
466 * We are not allowed to fail here since there is no way to unregister
467 * a power domain. Once registered above we have to keep the domains
468 * valid.
469 */
470
471 ret = pm_genpd_add_subdomain(pd_data->domains[MT8173_POWER_DOMAIN_MFG_ASYNC],
472 pd_data->domains[MT8173_POWER_DOMAIN_MFG_2D]);
473 if (ret && IS_ENABLED(CONFIG_PM))
474 dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret);
475
476 ret = pm_genpd_add_subdomain(pd_data->domains[MT8173_POWER_DOMAIN_MFG_2D],
477 pd_data->domains[MT8173_POWER_DOMAIN_MFG]);
478 if (ret && IS_ENABLED(CONFIG_PM))
479 dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret);
480
481 ret = of_genpd_add_provider_onecell(pdev->dev.of_node, pd_data);
482 if (ret)
483 dev_err(&pdev->dev, "Failed to add OF provider: %d\n", ret);
484
485 return 0;
486}
487
488static const struct of_device_id of_scpsys_match_tbl[] = {
489 {
490 .compatible = "mediatek,mt8173-scpsys",
491 }, {
492 /* sentinel */
493 }
494};
495
496static struct platform_driver scpsys_drv = {
497 .driver = {
498 .name = "mtk-scpsys",
499 .owner = THIS_MODULE,
500 .of_match_table = of_match_ptr(of_scpsys_match_tbl),
501 },
502};
503
504module_platform_driver_probe(scpsys_drv, scpsys_probe);