]>
Commit | Line | Data |
---|---|---|
c84e3587 SH |
1 | /* |
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> | |
6078c651 | 14 | #include <linux/init.h> |
c84e3587 | 15 | #include <linux/io.h> |
c84e3587 SH |
16 | #include <linux/mfd/syscon.h> |
17 | #include <linux/of_device.h> | |
18 | #include <linux/platform_device.h> | |
19 | #include <linux/pm_domain.h> | |
4688f385 | 20 | #include <linux/regulator/consumer.h> |
6078c651 JL |
21 | #include <linux/soc/mediatek/infracfg.h> |
22 | ||
112ef188 | 23 | #include <dt-bindings/power/mt2701-power.h> |
36c310f5 | 24 | #include <dt-bindings/power/mt6797-power.h> |
c84e3587 SH |
25 | #include <dt-bindings/power/mt8173-power.h> |
26 | ||
27 | #define SPM_VDE_PWR_CON 0x0210 | |
28 | #define SPM_MFG_PWR_CON 0x0214 | |
29 | #define SPM_VEN_PWR_CON 0x0230 | |
30 | #define SPM_ISP_PWR_CON 0x0238 | |
31 | #define SPM_DIS_PWR_CON 0x023c | |
112ef188 | 32 | #define SPM_CONN_PWR_CON 0x0280 |
c84e3587 | 33 | #define SPM_VEN2_PWR_CON 0x0298 |
112ef188 SW |
34 | #define SPM_AUDIO_PWR_CON 0x029c /* MT8173 */ |
35 | #define SPM_BDP_PWR_CON 0x029c /* MT2701 */ | |
36 | #define SPM_ETH_PWR_CON 0x02a0 | |
37 | #define SPM_HIF_PWR_CON 0x02a4 | |
38 | #define SPM_IFR_MSC_PWR_CON 0x02a8 | |
c84e3587 SH |
39 | #define SPM_MFG_2D_PWR_CON 0x02c0 |
40 | #define SPM_MFG_ASYNC_PWR_CON 0x02c4 | |
41 | #define SPM_USB_PWR_CON 0x02cc | |
6078c651 | 42 | |
c84e3587 SH |
43 | #define SPM_PWR_STATUS 0x060c |
44 | #define SPM_PWR_STATUS_2ND 0x0610 | |
45 | ||
46 | #define PWR_RST_B_BIT BIT(0) | |
47 | #define PWR_ISO_BIT BIT(1) | |
48 | #define PWR_ON_BIT BIT(2) | |
49 | #define PWR_ON_2ND_BIT BIT(3) | |
50 | #define PWR_CLK_DIS_BIT BIT(4) | |
51 | ||
112ef188 | 52 | #define PWR_STATUS_CONN BIT(1) |
c84e3587 SH |
53 | #define PWR_STATUS_DISP BIT(3) |
54 | #define PWR_STATUS_MFG BIT(4) | |
55 | #define PWR_STATUS_ISP BIT(5) | |
56 | #define PWR_STATUS_VDEC BIT(7) | |
112ef188 SW |
57 | #define PWR_STATUS_BDP BIT(14) |
58 | #define PWR_STATUS_ETH BIT(15) | |
59 | #define PWR_STATUS_HIF BIT(16) | |
60 | #define PWR_STATUS_IFR_MSC BIT(17) | |
c84e3587 SH |
61 | #define PWR_STATUS_VENC_LT BIT(20) |
62 | #define PWR_STATUS_VENC BIT(21) | |
63 | #define PWR_STATUS_MFG_2D BIT(22) | |
64 | #define PWR_STATUS_MFG_ASYNC BIT(23) | |
65 | #define PWR_STATUS_AUDIO BIT(24) | |
66 | #define PWR_STATUS_USB BIT(25) | |
67 | ||
68 | enum clk_id { | |
6078c651 JL |
69 | CLK_NONE, |
70 | CLK_MM, | |
71 | CLK_MFG, | |
72 | CLK_VENC, | |
73 | CLK_VENC_LT, | |
112ef188 | 74 | CLK_ETHIF, |
a3acbbf4 | 75 | CLK_VDEC, |
6078c651 JL |
76 | CLK_MAX, |
77 | }; | |
78 | ||
79 | static const char * const clk_names[] = { | |
80 | NULL, | |
81 | "mm", | |
82 | "mfg", | |
83 | "venc", | |
84 | "venc_lt", | |
112ef188 | 85 | "ethif", |
a3acbbf4 | 86 | "vdec", |
6078c651 | 87 | NULL, |
c84e3587 SH |
88 | }; |
89 | ||
41b3e0f0 JL |
90 | #define MAX_CLKS 2 |
91 | ||
c84e3587 SH |
92 | struct scp_domain_data { |
93 | const char *name; | |
94 | u32 sta_mask; | |
95 | int ctl_offs; | |
96 | u32 sram_pdn_bits; | |
97 | u32 sram_pdn_ack_bits; | |
98 | u32 bus_prot_mask; | |
41b3e0f0 | 99 | enum clk_id clk_id[MAX_CLKS]; |
47e90154 | 100 | bool active_wakeup; |
c84e3587 SH |
101 | }; |
102 | ||
c84e3587 SH |
103 | struct scp; |
104 | ||
105 | struct scp_domain { | |
106 | struct generic_pm_domain genpd; | |
107 | struct scp *scp; | |
41b3e0f0 | 108 | struct clk *clk[MAX_CLKS]; |
be29523d | 109 | const struct scp_domain_data *data; |
4688f385 | 110 | struct regulator *supply; |
c84e3587 SH |
111 | }; |
112 | ||
f1be4c4e MC |
113 | struct scp_ctrl_reg { |
114 | int pwr_sta_offs; | |
115 | int pwr_sta2nd_offs; | |
116 | }; | |
117 | ||
c84e3587 | 118 | struct scp { |
6078c651 | 119 | struct scp_domain *domains; |
c84e3587 SH |
120 | struct genpd_onecell_data pd_data; |
121 | struct device *dev; | |
122 | void __iomem *base; | |
123 | struct regmap *infracfg; | |
f1be4c4e | 124 | struct scp_ctrl_reg ctrl_reg; |
c84e3587 SH |
125 | }; |
126 | ||
127 | static int scpsys_domain_is_on(struct scp_domain *scpd) | |
128 | { | |
129 | struct scp *scp = scpd->scp; | |
130 | ||
f1be4c4e MC |
131 | u32 status = readl(scp->base + scp->ctrl_reg.pwr_sta_offs) & |
132 | scpd->data->sta_mask; | |
133 | u32 status2 = readl(scp->base + scp->ctrl_reg.pwr_sta2nd_offs) & | |
134 | scpd->data->sta_mask; | |
c84e3587 SH |
135 | |
136 | /* | |
137 | * A domain is on when both status bits are set. If only one is set | |
138 | * return an error. This happens while powering up a domain | |
139 | */ | |
140 | ||
141 | if (status && status2) | |
142 | return true; | |
143 | if (!status && !status2) | |
144 | return false; | |
145 | ||
146 | return -EINVAL; | |
147 | } | |
148 | ||
149 | static int scpsys_power_on(struct generic_pm_domain *genpd) | |
150 | { | |
151 | struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); | |
152 | struct scp *scp = scpd->scp; | |
153 | unsigned long timeout; | |
154 | bool expired; | |
be29523d MB |
155 | void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; |
156 | u32 sram_pdn_ack = scpd->data->sram_pdn_ack_bits; | |
c84e3587 SH |
157 | u32 val; |
158 | int ret; | |
41b3e0f0 JL |
159 | int i; |
160 | ||
4688f385 SH |
161 | if (scpd->supply) { |
162 | ret = regulator_enable(scpd->supply); | |
163 | if (ret) | |
164 | return ret; | |
165 | } | |
166 | ||
41b3e0f0 JL |
167 | for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) { |
168 | ret = clk_prepare_enable(scpd->clk[i]); | |
169 | if (ret) { | |
170 | for (--i; i >= 0; i--) | |
171 | clk_disable_unprepare(scpd->clk[i]); | |
c84e3587 | 172 | |
c84e3587 | 173 | goto err_clk; |
41b3e0f0 | 174 | } |
c84e3587 SH |
175 | } |
176 | ||
177 | val = readl(ctl_addr); | |
178 | val |= PWR_ON_BIT; | |
179 | writel(val, ctl_addr); | |
180 | val |= PWR_ON_2ND_BIT; | |
181 | writel(val, ctl_addr); | |
182 | ||
183 | /* wait until PWR_ACK = 1 */ | |
184 | timeout = jiffies + HZ; | |
185 | expired = false; | |
186 | while (1) { | |
187 | ret = scpsys_domain_is_on(scpd); | |
188 | if (ret > 0) | |
189 | break; | |
190 | ||
191 | if (expired) { | |
192 | ret = -ETIMEDOUT; | |
193 | goto err_pwr_ack; | |
194 | } | |
195 | ||
196 | cpu_relax(); | |
197 | ||
198 | if (time_after(jiffies, timeout)) | |
199 | expired = true; | |
200 | } | |
201 | ||
202 | val &= ~PWR_CLK_DIS_BIT; | |
203 | writel(val, ctl_addr); | |
204 | ||
205 | val &= ~PWR_ISO_BIT; | |
206 | writel(val, ctl_addr); | |
207 | ||
208 | val |= PWR_RST_B_BIT; | |
209 | writel(val, ctl_addr); | |
210 | ||
be29523d | 211 | val &= ~scpd->data->sram_pdn_bits; |
c84e3587 SH |
212 | writel(val, ctl_addr); |
213 | ||
214 | /* wait until SRAM_PDN_ACK all 0 */ | |
215 | timeout = jiffies + HZ; | |
216 | expired = false; | |
217 | while (sram_pdn_ack && (readl(ctl_addr) & sram_pdn_ack)) { | |
218 | ||
219 | if (expired) { | |
220 | ret = -ETIMEDOUT; | |
221 | goto err_pwr_ack; | |
222 | } | |
223 | ||
224 | cpu_relax(); | |
225 | ||
226 | if (time_after(jiffies, timeout)) | |
227 | expired = true; | |
228 | } | |
229 | ||
be29523d | 230 | if (scpd->data->bus_prot_mask) { |
c84e3587 | 231 | ret = mtk_infracfg_clear_bus_protection(scp->infracfg, |
be29523d | 232 | scpd->data->bus_prot_mask); |
c84e3587 SH |
233 | if (ret) |
234 | goto err_pwr_ack; | |
235 | } | |
236 | ||
237 | return 0; | |
238 | ||
239 | err_pwr_ack: | |
41b3e0f0 JL |
240 | for (i = MAX_CLKS - 1; i >= 0; i--) { |
241 | if (scpd->clk[i]) | |
242 | clk_disable_unprepare(scpd->clk[i]); | |
243 | } | |
c84e3587 | 244 | err_clk: |
4688f385 SH |
245 | if (scpd->supply) |
246 | regulator_disable(scpd->supply); | |
247 | ||
c84e3587 SH |
248 | dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name); |
249 | ||
250 | return ret; | |
251 | } | |
252 | ||
253 | static int scpsys_power_off(struct generic_pm_domain *genpd) | |
254 | { | |
255 | struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); | |
256 | struct scp *scp = scpd->scp; | |
257 | unsigned long timeout; | |
258 | bool expired; | |
be29523d MB |
259 | void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; |
260 | u32 pdn_ack = scpd->data->sram_pdn_ack_bits; | |
c84e3587 SH |
261 | u32 val; |
262 | int ret; | |
41b3e0f0 | 263 | int i; |
c84e3587 | 264 | |
be29523d | 265 | if (scpd->data->bus_prot_mask) { |
c84e3587 | 266 | ret = mtk_infracfg_set_bus_protection(scp->infracfg, |
be29523d | 267 | scpd->data->bus_prot_mask); |
c84e3587 SH |
268 | if (ret) |
269 | goto out; | |
270 | } | |
271 | ||
272 | val = readl(ctl_addr); | |
be29523d | 273 | val |= scpd->data->sram_pdn_bits; |
c84e3587 SH |
274 | writel(val, ctl_addr); |
275 | ||
276 | /* wait until SRAM_PDN_ACK all 1 */ | |
277 | timeout = jiffies + HZ; | |
278 | expired = false; | |
279 | while (pdn_ack && (readl(ctl_addr) & pdn_ack) != pdn_ack) { | |
280 | if (expired) { | |
281 | ret = -ETIMEDOUT; | |
282 | goto out; | |
283 | } | |
284 | ||
285 | cpu_relax(); | |
286 | ||
287 | if (time_after(jiffies, timeout)) | |
288 | expired = true; | |
289 | } | |
290 | ||
291 | val |= PWR_ISO_BIT; | |
292 | writel(val, ctl_addr); | |
293 | ||
294 | val &= ~PWR_RST_B_BIT; | |
295 | writel(val, ctl_addr); | |
296 | ||
297 | val |= PWR_CLK_DIS_BIT; | |
298 | writel(val, ctl_addr); | |
299 | ||
300 | val &= ~PWR_ON_BIT; | |
301 | writel(val, ctl_addr); | |
302 | ||
303 | val &= ~PWR_ON_2ND_BIT; | |
304 | writel(val, ctl_addr); | |
305 | ||
306 | /* wait until PWR_ACK = 0 */ | |
307 | timeout = jiffies + HZ; | |
308 | expired = false; | |
309 | while (1) { | |
310 | ret = scpsys_domain_is_on(scpd); | |
311 | if (ret == 0) | |
312 | break; | |
313 | ||
314 | if (expired) { | |
315 | ret = -ETIMEDOUT; | |
316 | goto out; | |
317 | } | |
318 | ||
319 | cpu_relax(); | |
320 | ||
321 | if (time_after(jiffies, timeout)) | |
322 | expired = true; | |
323 | } | |
324 | ||
41b3e0f0 JL |
325 | for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) |
326 | clk_disable_unprepare(scpd->clk[i]); | |
c84e3587 | 327 | |
4688f385 SH |
328 | if (scpd->supply) |
329 | regulator_disable(scpd->supply); | |
330 | ||
c84e3587 SH |
331 | return 0; |
332 | ||
333 | out: | |
334 | dev_err(scp->dev, "Failed to power off domain %s\n", genpd->name); | |
335 | ||
336 | return ret; | |
337 | } | |
338 | ||
47e90154 EH |
339 | static bool scpsys_active_wakeup(struct device *dev) |
340 | { | |
341 | struct generic_pm_domain *genpd; | |
342 | struct scp_domain *scpd; | |
343 | ||
344 | genpd = pd_to_genpd(dev->pm_domain); | |
345 | scpd = container_of(genpd, struct scp_domain, genpd); | |
346 | ||
be29523d | 347 | return scpd->data->active_wakeup; |
47e90154 EH |
348 | } |
349 | ||
6078c651 JL |
350 | static void init_clks(struct platform_device *pdev, struct clk **clk) |
351 | { | |
352 | int i; | |
353 | ||
354 | for (i = CLK_NONE + 1; i < CLK_MAX; i++) | |
355 | clk[i] = devm_clk_get(&pdev->dev, clk_names[i]); | |
356 | } | |
357 | ||
358 | static struct scp *init_scp(struct platform_device *pdev, | |
f1be4c4e MC |
359 | const struct scp_domain_data *scp_domain_data, int num, |
360 | struct scp_ctrl_reg *scp_ctrl_reg) | |
c84e3587 SH |
361 | { |
362 | struct genpd_onecell_data *pd_data; | |
363 | struct resource *res; | |
6078c651 | 364 | int i, j; |
c84e3587 | 365 | struct scp *scp; |
6078c651 | 366 | struct clk *clk[CLK_MAX]; |
c84e3587 SH |
367 | |
368 | scp = devm_kzalloc(&pdev->dev, sizeof(*scp), GFP_KERNEL); | |
369 | if (!scp) | |
6078c651 | 370 | return ERR_PTR(-ENOMEM); |
c84e3587 | 371 | |
f1be4c4e MC |
372 | scp->ctrl_reg.pwr_sta_offs = scp_ctrl_reg->pwr_sta_offs; |
373 | scp->ctrl_reg.pwr_sta2nd_offs = scp_ctrl_reg->pwr_sta2nd_offs; | |
374 | ||
c84e3587 SH |
375 | scp->dev = &pdev->dev; |
376 | ||
377 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
378 | scp->base = devm_ioremap_resource(&pdev->dev, res); | |
379 | if (IS_ERR(scp->base)) | |
6078c651 JL |
380 | return ERR_CAST(scp->base); |
381 | ||
382 | scp->domains = devm_kzalloc(&pdev->dev, | |
383 | sizeof(*scp->domains) * num, GFP_KERNEL); | |
384 | if (!scp->domains) | |
385 | return ERR_PTR(-ENOMEM); | |
c84e3587 SH |
386 | |
387 | pd_data = &scp->pd_data; | |
388 | ||
389 | pd_data->domains = devm_kzalloc(&pdev->dev, | |
6078c651 | 390 | sizeof(*pd_data->domains) * num, GFP_KERNEL); |
c84e3587 | 391 | if (!pd_data->domains) |
6078c651 | 392 | return ERR_PTR(-ENOMEM); |
41b3e0f0 | 393 | |
c84e3587 SH |
394 | scp->infracfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, |
395 | "infracfg"); | |
396 | if (IS_ERR(scp->infracfg)) { | |
397 | dev_err(&pdev->dev, "Cannot find infracfg controller: %ld\n", | |
398 | PTR_ERR(scp->infracfg)); | |
6078c651 | 399 | return ERR_CAST(scp->infracfg); |
c84e3587 SH |
400 | } |
401 | ||
6078c651 | 402 | for (i = 0; i < num; i++) { |
4688f385 SH |
403 | struct scp_domain *scpd = &scp->domains[i]; |
404 | const struct scp_domain_data *data = &scp_domain_data[i]; | |
405 | ||
406 | scpd->supply = devm_regulator_get_optional(&pdev->dev, data->name); | |
407 | if (IS_ERR(scpd->supply)) { | |
408 | if (PTR_ERR(scpd->supply) == -ENODEV) | |
409 | scpd->supply = NULL; | |
410 | else | |
6078c651 | 411 | return ERR_CAST(scpd->supply); |
4688f385 SH |
412 | } |
413 | } | |
414 | ||
6078c651 | 415 | pd_data->num_domains = num; |
c84e3587 | 416 | |
6078c651 JL |
417 | init_clks(pdev, clk); |
418 | ||
419 | for (i = 0; i < num; i++) { | |
c84e3587 SH |
420 | struct scp_domain *scpd = &scp->domains[i]; |
421 | struct generic_pm_domain *genpd = &scpd->genpd; | |
422 | const struct scp_domain_data *data = &scp_domain_data[i]; | |
423 | ||
424 | pd_data->domains[i] = genpd; | |
425 | scpd->scp = scp; | |
426 | ||
be29523d | 427 | scpd->data = data; |
6078c651 JL |
428 | |
429 | for (j = 0; j < MAX_CLKS && data->clk_id[j]; j++) { | |
430 | struct clk *c = clk[data->clk_id[j]]; | |
431 | ||
432 | if (IS_ERR(c)) { | |
433 | dev_err(&pdev->dev, "%s: clk unavailable\n", | |
434 | data->name); | |
435 | return ERR_CAST(c); | |
436 | } | |
437 | ||
438 | scpd->clk[j] = c; | |
439 | } | |
c84e3587 SH |
440 | |
441 | genpd->name = data->name; | |
442 | genpd->power_off = scpsys_power_off; | |
443 | genpd->power_on = scpsys_power_on; | |
47e90154 | 444 | genpd->dev_ops.active_wakeup = scpsys_active_wakeup; |
6078c651 JL |
445 | } |
446 | ||
447 | return scp; | |
448 | } | |
449 | ||
450 | static void mtk_register_power_domains(struct platform_device *pdev, | |
451 | struct scp *scp, int num) | |
452 | { | |
453 | struct genpd_onecell_data *pd_data; | |
454 | int i, ret; | |
455 | ||
456 | for (i = 0; i < num; i++) { | |
457 | struct scp_domain *scpd = &scp->domains[i]; | |
458 | struct generic_pm_domain *genpd = &scpd->genpd; | |
c84e3587 SH |
459 | |
460 | /* | |
d9c9f3b8 JL |
461 | * Initially turn on all domains to make the domains usable |
462 | * with !CONFIG_PM and to get the hardware in sync with the | |
463 | * software. The unused domains will be switched off during | |
464 | * late_init time. | |
c84e3587 | 465 | */ |
d9c9f3b8 | 466 | genpd->power_on(genpd); |
c84e3587 | 467 | |
d9c9f3b8 | 468 | pm_genpd_init(genpd, NULL, false); |
c84e3587 SH |
469 | } |
470 | ||
471 | /* | |
472 | * We are not allowed to fail here since there is no way to unregister | |
473 | * a power domain. Once registered above we have to keep the domains | |
474 | * valid. | |
475 | */ | |
476 | ||
6078c651 JL |
477 | pd_data = &scp->pd_data; |
478 | ||
479 | ret = of_genpd_add_provider_onecell(pdev->dev.of_node, pd_data); | |
480 | if (ret) | |
481 | dev_err(&pdev->dev, "Failed to add OF provider: %d\n", ret); | |
482 | } | |
483 | ||
112ef188 SW |
484 | /* |
485 | * MT2701 power domain support | |
486 | */ | |
487 | ||
488 | static const struct scp_domain_data scp_domain_data_mt2701[] = { | |
489 | [MT2701_POWER_DOMAIN_CONN] = { | |
490 | .name = "conn", | |
491 | .sta_mask = PWR_STATUS_CONN, | |
492 | .ctl_offs = SPM_CONN_PWR_CON, | |
493 | .bus_prot_mask = 0x0104, | |
494 | .clk_id = {CLK_NONE}, | |
495 | .active_wakeup = true, | |
496 | }, | |
497 | [MT2701_POWER_DOMAIN_DISP] = { | |
498 | .name = "disp", | |
499 | .sta_mask = PWR_STATUS_DISP, | |
500 | .ctl_offs = SPM_DIS_PWR_CON, | |
501 | .sram_pdn_bits = GENMASK(11, 8), | |
502 | .clk_id = {CLK_MM}, | |
503 | .bus_prot_mask = 0x0002, | |
504 | .active_wakeup = true, | |
505 | }, | |
506 | [MT2701_POWER_DOMAIN_MFG] = { | |
507 | .name = "mfg", | |
508 | .sta_mask = PWR_STATUS_MFG, | |
509 | .ctl_offs = SPM_MFG_PWR_CON, | |
510 | .sram_pdn_bits = GENMASK(11, 8), | |
511 | .sram_pdn_ack_bits = GENMASK(12, 12), | |
512 | .clk_id = {CLK_MFG}, | |
513 | .active_wakeup = true, | |
514 | }, | |
515 | [MT2701_POWER_DOMAIN_VDEC] = { | |
516 | .name = "vdec", | |
517 | .sta_mask = PWR_STATUS_VDEC, | |
518 | .ctl_offs = SPM_VDE_PWR_CON, | |
519 | .sram_pdn_bits = GENMASK(11, 8), | |
520 | .sram_pdn_ack_bits = GENMASK(12, 12), | |
521 | .clk_id = {CLK_MM}, | |
522 | .active_wakeup = true, | |
523 | }, | |
524 | [MT2701_POWER_DOMAIN_ISP] = { | |
525 | .name = "isp", | |
526 | .sta_mask = PWR_STATUS_ISP, | |
527 | .ctl_offs = SPM_ISP_PWR_CON, | |
528 | .sram_pdn_bits = GENMASK(11, 8), | |
529 | .sram_pdn_ack_bits = GENMASK(13, 12), | |
530 | .clk_id = {CLK_MM}, | |
531 | .active_wakeup = true, | |
532 | }, | |
533 | [MT2701_POWER_DOMAIN_BDP] = { | |
534 | .name = "bdp", | |
535 | .sta_mask = PWR_STATUS_BDP, | |
536 | .ctl_offs = SPM_BDP_PWR_CON, | |
537 | .sram_pdn_bits = GENMASK(11, 8), | |
538 | .clk_id = {CLK_NONE}, | |
539 | .active_wakeup = true, | |
540 | }, | |
541 | [MT2701_POWER_DOMAIN_ETH] = { | |
542 | .name = "eth", | |
543 | .sta_mask = PWR_STATUS_ETH, | |
544 | .ctl_offs = SPM_ETH_PWR_CON, | |
545 | .sram_pdn_bits = GENMASK(11, 8), | |
546 | .sram_pdn_ack_bits = GENMASK(15, 12), | |
547 | .clk_id = {CLK_ETHIF}, | |
548 | .active_wakeup = true, | |
549 | }, | |
550 | [MT2701_POWER_DOMAIN_HIF] = { | |
551 | .name = "hif", | |
552 | .sta_mask = PWR_STATUS_HIF, | |
553 | .ctl_offs = SPM_HIF_PWR_CON, | |
554 | .sram_pdn_bits = GENMASK(11, 8), | |
555 | .sram_pdn_ack_bits = GENMASK(15, 12), | |
556 | .clk_id = {CLK_ETHIF}, | |
557 | .active_wakeup = true, | |
558 | }, | |
559 | [MT2701_POWER_DOMAIN_IFR_MSC] = { | |
560 | .name = "ifr_msc", | |
561 | .sta_mask = PWR_STATUS_IFR_MSC, | |
562 | .ctl_offs = SPM_IFR_MSC_PWR_CON, | |
563 | .clk_id = {CLK_NONE}, | |
564 | .active_wakeup = true, | |
565 | }, | |
566 | }; | |
567 | ||
568 | #define NUM_DOMAINS_MT2701 ARRAY_SIZE(scp_domain_data_mt2701) | |
569 | ||
570 | static int __init scpsys_probe_mt2701(struct platform_device *pdev) | |
571 | { | |
572 | struct scp *scp; | |
f1be4c4e | 573 | struct scp_ctrl_reg scp_reg; |
112ef188 | 574 | |
f1be4c4e MC |
575 | scp_reg.pwr_sta_offs = SPM_PWR_STATUS; |
576 | scp_reg.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND; | |
577 | ||
578 | scp = init_scp(pdev, scp_domain_data_mt2701, NUM_DOMAINS_MT2701, | |
579 | &scp_reg); | |
112ef188 SW |
580 | if (IS_ERR(scp)) |
581 | return PTR_ERR(scp); | |
582 | ||
583 | mtk_register_power_domains(pdev, scp, NUM_DOMAINS_MT2701); | |
584 | ||
585 | return 0; | |
586 | } | |
587 | ||
36c310f5 MC |
588 | /* |
589 | * MT6797 power domain support | |
590 | */ | |
591 | ||
592 | static const struct scp_domain_data scp_domain_data_mt6797[] = { | |
593 | [MT6797_POWER_DOMAIN_VDEC] = { | |
594 | .name = "vdec", | |
595 | .sta_mask = BIT(7), | |
596 | .ctl_offs = 0x300, | |
597 | .sram_pdn_bits = GENMASK(8, 8), | |
598 | .sram_pdn_ack_bits = GENMASK(12, 12), | |
599 | .clk_id = {CLK_VDEC}, | |
600 | }, | |
601 | [MT6797_POWER_DOMAIN_VENC] = { | |
602 | .name = "venc", | |
603 | .sta_mask = BIT(21), | |
604 | .ctl_offs = 0x304, | |
605 | .sram_pdn_bits = GENMASK(11, 8), | |
606 | .sram_pdn_ack_bits = GENMASK(15, 12), | |
607 | .clk_id = {CLK_NONE}, | |
608 | }, | |
609 | [MT6797_POWER_DOMAIN_ISP] = { | |
610 | .name = "isp", | |
611 | .sta_mask = BIT(5), | |
612 | .ctl_offs = 0x308, | |
613 | .sram_pdn_bits = GENMASK(9, 8), | |
614 | .sram_pdn_ack_bits = GENMASK(13, 12), | |
615 | .clk_id = {CLK_NONE}, | |
616 | }, | |
617 | [MT6797_POWER_DOMAIN_MM] = { | |
618 | .name = "mm", | |
619 | .sta_mask = BIT(3), | |
620 | .ctl_offs = 0x30C, | |
621 | .sram_pdn_bits = GENMASK(8, 8), | |
622 | .sram_pdn_ack_bits = GENMASK(12, 12), | |
623 | .clk_id = {CLK_MM}, | |
624 | .bus_prot_mask = (BIT(1) | BIT(2)), | |
625 | }, | |
626 | [MT6797_POWER_DOMAIN_AUDIO] = { | |
627 | .name = "audio", | |
628 | .sta_mask = BIT(24), | |
629 | .ctl_offs = 0x314, | |
630 | .sram_pdn_bits = GENMASK(11, 8), | |
631 | .sram_pdn_ack_bits = GENMASK(15, 12), | |
632 | .clk_id = {CLK_NONE}, | |
633 | }, | |
634 | [MT6797_POWER_DOMAIN_MFG_ASYNC] = { | |
635 | .name = "mfg_async", | |
636 | .sta_mask = BIT(13), | |
637 | .ctl_offs = 0x334, | |
638 | .sram_pdn_bits = 0, | |
639 | .sram_pdn_ack_bits = 0, | |
640 | .clk_id = {CLK_MFG}, | |
641 | }, | |
642 | [MT6797_POWER_DOMAIN_MJC] = { | |
643 | .name = "mjc", | |
644 | .sta_mask = BIT(20), | |
645 | .ctl_offs = 0x310, | |
646 | .sram_pdn_bits = GENMASK(8, 8), | |
647 | .sram_pdn_ack_bits = GENMASK(12, 12), | |
648 | .clk_id = {CLK_NONE}, | |
649 | }, | |
650 | }; | |
651 | ||
652 | #define NUM_DOMAINS_MT6797 ARRAY_SIZE(scp_domain_data_mt6797) | |
653 | #define SPM_PWR_STATUS_MT6797 0x0180 | |
654 | #define SPM_PWR_STATUS_2ND_MT6797 0x0184 | |
655 | ||
656 | static int __init scpsys_probe_mt6797(struct platform_device *pdev) | |
657 | { | |
658 | struct scp *scp; | |
659 | struct genpd_onecell_data *pd_data; | |
660 | int ret; | |
661 | struct scp_ctrl_reg scp_reg; | |
662 | ||
663 | scp_reg.pwr_sta_offs = SPM_PWR_STATUS_MT6797; | |
664 | scp_reg.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND_MT6797; | |
665 | ||
666 | scp = init_scp(pdev, scp_domain_data_mt6797, NUM_DOMAINS_MT6797, | |
667 | &scp_reg); | |
668 | if (IS_ERR(scp)) | |
669 | return PTR_ERR(scp); | |
670 | ||
671 | mtk_register_power_domains(pdev, scp, NUM_DOMAINS_MT6797); | |
672 | ||
673 | pd_data = &scp->pd_data; | |
674 | ||
675 | ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM], | |
676 | pd_data->domains[MT6797_POWER_DOMAIN_VDEC]); | |
677 | if (ret && IS_ENABLED(CONFIG_PM)) | |
678 | dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); | |
679 | ||
680 | ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM], | |
681 | pd_data->domains[MT6797_POWER_DOMAIN_ISP]); | |
682 | if (ret && IS_ENABLED(CONFIG_PM)) | |
683 | dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); | |
684 | ||
685 | ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM], | |
686 | pd_data->domains[MT6797_POWER_DOMAIN_VENC]); | |
687 | if (ret && IS_ENABLED(CONFIG_PM)) | |
688 | dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); | |
689 | ||
690 | ret = pm_genpd_add_subdomain(pd_data->domains[MT6797_POWER_DOMAIN_MM], | |
691 | pd_data->domains[MT6797_POWER_DOMAIN_MJC]); | |
692 | if (ret && IS_ENABLED(CONFIG_PM)) | |
693 | dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); | |
694 | ||
695 | return 0; | |
696 | } | |
697 | ||
6078c651 JL |
698 | /* |
699 | * MT8173 power domain support | |
700 | */ | |
701 | ||
702 | static const struct scp_domain_data scp_domain_data_mt8173[] = { | |
703 | [MT8173_POWER_DOMAIN_VDEC] = { | |
704 | .name = "vdec", | |
705 | .sta_mask = PWR_STATUS_VDEC, | |
706 | .ctl_offs = SPM_VDE_PWR_CON, | |
707 | .sram_pdn_bits = GENMASK(11, 8), | |
708 | .sram_pdn_ack_bits = GENMASK(12, 12), | |
709 | .clk_id = {CLK_MM}, | |
710 | }, | |
711 | [MT8173_POWER_DOMAIN_VENC] = { | |
712 | .name = "venc", | |
713 | .sta_mask = PWR_STATUS_VENC, | |
714 | .ctl_offs = SPM_VEN_PWR_CON, | |
715 | .sram_pdn_bits = GENMASK(11, 8), | |
716 | .sram_pdn_ack_bits = GENMASK(15, 12), | |
717 | .clk_id = {CLK_MM, CLK_VENC}, | |
718 | }, | |
719 | [MT8173_POWER_DOMAIN_ISP] = { | |
720 | .name = "isp", | |
721 | .sta_mask = PWR_STATUS_ISP, | |
722 | .ctl_offs = SPM_ISP_PWR_CON, | |
723 | .sram_pdn_bits = GENMASK(11, 8), | |
724 | .sram_pdn_ack_bits = GENMASK(13, 12), | |
725 | .clk_id = {CLK_MM}, | |
726 | }, | |
727 | [MT8173_POWER_DOMAIN_MM] = { | |
728 | .name = "mm", | |
729 | .sta_mask = PWR_STATUS_DISP, | |
730 | .ctl_offs = SPM_DIS_PWR_CON, | |
731 | .sram_pdn_bits = GENMASK(11, 8), | |
732 | .sram_pdn_ack_bits = GENMASK(12, 12), | |
733 | .clk_id = {CLK_MM}, | |
734 | .bus_prot_mask = MT8173_TOP_AXI_PROT_EN_MM_M0 | | |
735 | MT8173_TOP_AXI_PROT_EN_MM_M1, | |
736 | }, | |
737 | [MT8173_POWER_DOMAIN_VENC_LT] = { | |
738 | .name = "venc_lt", | |
739 | .sta_mask = PWR_STATUS_VENC_LT, | |
740 | .ctl_offs = SPM_VEN2_PWR_CON, | |
741 | .sram_pdn_bits = GENMASK(11, 8), | |
742 | .sram_pdn_ack_bits = GENMASK(15, 12), | |
743 | .clk_id = {CLK_MM, CLK_VENC_LT}, | |
744 | }, | |
745 | [MT8173_POWER_DOMAIN_AUDIO] = { | |
746 | .name = "audio", | |
747 | .sta_mask = PWR_STATUS_AUDIO, | |
748 | .ctl_offs = SPM_AUDIO_PWR_CON, | |
749 | .sram_pdn_bits = GENMASK(11, 8), | |
750 | .sram_pdn_ack_bits = GENMASK(15, 12), | |
751 | .clk_id = {CLK_NONE}, | |
752 | }, | |
753 | [MT8173_POWER_DOMAIN_USB] = { | |
754 | .name = "usb", | |
755 | .sta_mask = PWR_STATUS_USB, | |
756 | .ctl_offs = SPM_USB_PWR_CON, | |
757 | .sram_pdn_bits = GENMASK(11, 8), | |
758 | .sram_pdn_ack_bits = GENMASK(15, 12), | |
759 | .clk_id = {CLK_NONE}, | |
760 | .active_wakeup = true, | |
761 | }, | |
762 | [MT8173_POWER_DOMAIN_MFG_ASYNC] = { | |
763 | .name = "mfg_async", | |
764 | .sta_mask = PWR_STATUS_MFG_ASYNC, | |
765 | .ctl_offs = SPM_MFG_ASYNC_PWR_CON, | |
766 | .sram_pdn_bits = GENMASK(11, 8), | |
767 | .sram_pdn_ack_bits = 0, | |
768 | .clk_id = {CLK_MFG}, | |
769 | }, | |
770 | [MT8173_POWER_DOMAIN_MFG_2D] = { | |
771 | .name = "mfg_2d", | |
772 | .sta_mask = PWR_STATUS_MFG_2D, | |
773 | .ctl_offs = SPM_MFG_2D_PWR_CON, | |
774 | .sram_pdn_bits = GENMASK(11, 8), | |
775 | .sram_pdn_ack_bits = GENMASK(13, 12), | |
776 | .clk_id = {CLK_NONE}, | |
777 | }, | |
778 | [MT8173_POWER_DOMAIN_MFG] = { | |
779 | .name = "mfg", | |
780 | .sta_mask = PWR_STATUS_MFG, | |
781 | .ctl_offs = SPM_MFG_PWR_CON, | |
782 | .sram_pdn_bits = GENMASK(13, 8), | |
783 | .sram_pdn_ack_bits = GENMASK(21, 16), | |
784 | .clk_id = {CLK_NONE}, | |
785 | .bus_prot_mask = MT8173_TOP_AXI_PROT_EN_MFG_S | | |
786 | MT8173_TOP_AXI_PROT_EN_MFG_M0 | | |
787 | MT8173_TOP_AXI_PROT_EN_MFG_M1 | | |
788 | MT8173_TOP_AXI_PROT_EN_MFG_SNOOP_OUT, | |
789 | }, | |
790 | }; | |
791 | ||
792 | #define NUM_DOMAINS_MT8173 ARRAY_SIZE(scp_domain_data_mt8173) | |
793 | ||
794 | static int __init scpsys_probe_mt8173(struct platform_device *pdev) | |
795 | { | |
796 | struct scp *scp; | |
797 | struct genpd_onecell_data *pd_data; | |
798 | int ret; | |
f1be4c4e MC |
799 | struct scp_ctrl_reg scp_reg; |
800 | ||
801 | scp_reg.pwr_sta_offs = SPM_PWR_STATUS; | |
802 | scp_reg.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND; | |
6078c651 | 803 | |
f1be4c4e MC |
804 | scp = init_scp(pdev, scp_domain_data_mt8173, NUM_DOMAINS_MT8173, |
805 | &scp_reg); | |
6078c651 JL |
806 | if (IS_ERR(scp)) |
807 | return PTR_ERR(scp); | |
808 | ||
809 | mtk_register_power_domains(pdev, scp, NUM_DOMAINS_MT8173); | |
810 | ||
811 | pd_data = &scp->pd_data; | |
812 | ||
c84e3587 SH |
813 | ret = pm_genpd_add_subdomain(pd_data->domains[MT8173_POWER_DOMAIN_MFG_ASYNC], |
814 | pd_data->domains[MT8173_POWER_DOMAIN_MFG_2D]); | |
815 | if (ret && IS_ENABLED(CONFIG_PM)) | |
816 | dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); | |
817 | ||
818 | ret = pm_genpd_add_subdomain(pd_data->domains[MT8173_POWER_DOMAIN_MFG_2D], | |
819 | pd_data->domains[MT8173_POWER_DOMAIN_MFG]); | |
820 | if (ret && IS_ENABLED(CONFIG_PM)) | |
821 | dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); | |
822 | ||
c84e3587 SH |
823 | return 0; |
824 | } | |
825 | ||
6078c651 JL |
826 | /* |
827 | * scpsys driver init | |
828 | */ | |
829 | ||
c84e3587 SH |
830 | static const struct of_device_id of_scpsys_match_tbl[] = { |
831 | { | |
112ef188 SW |
832 | .compatible = "mediatek,mt2701-scpsys", |
833 | .data = scpsys_probe_mt2701, | |
36c310f5 MC |
834 | }, { |
835 | .compatible = "mediatek,mt6797-scpsys", | |
836 | .data = scpsys_probe_mt6797, | |
112ef188 | 837 | }, { |
c84e3587 | 838 | .compatible = "mediatek,mt8173-scpsys", |
6078c651 | 839 | .data = scpsys_probe_mt8173, |
c84e3587 SH |
840 | }, { |
841 | /* sentinel */ | |
842 | } | |
843 | }; | |
844 | ||
6078c651 JL |
845 | static int scpsys_probe(struct platform_device *pdev) |
846 | { | |
847 | int (*probe)(struct platform_device *); | |
848 | const struct of_device_id *of_id; | |
849 | ||
850 | of_id = of_match_node(of_scpsys_match_tbl, pdev->dev.of_node); | |
851 | if (!of_id || !of_id->data) | |
852 | return -EINVAL; | |
853 | ||
854 | probe = of_id->data; | |
855 | ||
856 | return probe(pdev); | |
857 | } | |
858 | ||
c84e3587 | 859 | static struct platform_driver scpsys_drv = { |
be29523d | 860 | .probe = scpsys_probe, |
c84e3587 SH |
861 | .driver = { |
862 | .name = "mtk-scpsys", | |
be29523d | 863 | .suppress_bind_attrs = true, |
c84e3587 SH |
864 | .owner = THIS_MODULE, |
865 | .of_match_table = of_match_ptr(of_scpsys_match_tbl), | |
866 | }, | |
867 | }; | |
be29523d | 868 | builtin_platform_driver(scpsys_drv); |