blob: 0bf8a16447ba257ddb137e3aa6e465eecf4cd011 [file] [log] [blame]
developer8bd109d2018-11-15 10:08:00 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2018 MediaTek Inc.
4 * Author: Ryder Lee <ryder.lee@mediatek.com>
5 */
6
7#include <clk.h>
8#include <common.h>
9#include <dm.h>
10#include <power-domain-uclass.h>
11#include <regmap.h>
12#include <syscon.h>
13#include <asm/io.h>
14#include <asm/processor.h>
15#include <linux/iopoll.h>
16
developer58898812018-11-15 10:08:01 +080017#include <dt-bindings/power/mt7623-power.h>
developer8bd109d2018-11-15 10:08:00 +080018#include <dt-bindings/power/mt7629-power.h>
19
20#define SPM_EN (0xb16 << 16 | 0x1)
developer58898812018-11-15 10:08:01 +080021#define SPM_VDE_PWR_CON 0x0210
22#define SPM_MFG_PWR_CON 0x0214
23#define SPM_ISP_PWR_CON 0x0238
24#define SPM_DIS_PWR_CON 0x023c
25#define SPM_CONN_PWR_CON 0x0280
26#define SPM_BDP_PWR_CON 0x029c
27#define SPM_ETH_PWR_CON 0x02a0
28#define SPM_HIF_PWR_CON 0x02a4
29#define SPM_IFR_MSC_PWR_CON 0x02a8
developer8bd109d2018-11-15 10:08:00 +080030#define SPM_ETHSYS_PWR_CON 0x2e0
31#define SPM_HIF0_PWR_CON 0x2e4
32#define SPM_HIF1_PWR_CON 0x2e8
33#define SPM_PWR_STATUS 0x60c
34#define SPM_PWR_STATUS_2ND 0x610
35
36#define PWR_RST_B_BIT BIT(0)
37#define PWR_ISO_BIT BIT(1)
38#define PWR_ON_BIT BIT(2)
39#define PWR_ON_2ND_BIT BIT(3)
40#define PWR_CLK_DIS_BIT BIT(4)
41
developer58898812018-11-15 10:08:01 +080042#define PWR_STATUS_CONN BIT(1)
43#define PWR_STATUS_DISP BIT(3)
44#define PWR_STATUS_MFG BIT(4)
45#define PWR_STATUS_ISP BIT(5)
46#define PWR_STATUS_VDEC BIT(7)
47#define PWR_STATUS_BDP BIT(14)
48#define PWR_STATUS_ETH BIT(15)
49#define PWR_STATUS_HIF BIT(16)
50#define PWR_STATUS_IFR_MSC BIT(17)
developer8bd109d2018-11-15 10:08:00 +080051#define PWR_STATUS_ETHSYS BIT(24)
52#define PWR_STATUS_HIF0 BIT(25)
53#define PWR_STATUS_HIF1 BIT(26)
54
55/* Infrasys configuration */
56#define INFRA_TOPDCM_CTRL 0x10
57#define INFRA_TOPAXI_PROT_EN 0x220
58#define INFRA_TOPAXI_PROT_STA1 0x228
59
60#define DCM_TOP_EN BIT(0)
61
62enum scp_domain_type {
developere7cceca2020-01-10 16:30:31 +080063 SCPSYS_MT7622,
developer58898812018-11-15 10:08:01 +080064 SCPSYS_MT7623,
developer8bd109d2018-11-15 10:08:00 +080065 SCPSYS_MT7629,
66};
67
68struct scp_domain;
69
70struct scp_domain_data {
71 struct scp_domain *scpd;
72 u32 sta_mask;
73 int ctl_offs;
74 u32 sram_pdn_bits;
75 u32 sram_pdn_ack_bits;
76 u32 bus_prot_mask;
77};
78
79struct scp_domain {
80 void __iomem *base;
81 void __iomem *infracfg;
82 enum scp_domain_type type;
83 struct scp_domain_data *data;
84};
85
developer58898812018-11-15 10:08:01 +080086static struct scp_domain_data scp_domain_mt7623[] = {
87 [MT7623_POWER_DOMAIN_CONN] = {
88 .sta_mask = PWR_STATUS_CONN,
89 .ctl_offs = SPM_CONN_PWR_CON,
90 .bus_prot_mask = BIT(8) | BIT(2),
91 },
92 [MT7623_POWER_DOMAIN_DISP] = {
93 .sta_mask = PWR_STATUS_DISP,
94 .ctl_offs = SPM_DIS_PWR_CON,
95 .sram_pdn_bits = GENMASK(11, 8),
96 .bus_prot_mask = BIT(2),
97 },
98 [MT7623_POWER_DOMAIN_MFG] = {
99 .sta_mask = PWR_STATUS_MFG,
100 .ctl_offs = SPM_MFG_PWR_CON,
101 .sram_pdn_bits = GENMASK(11, 8),
102 .sram_pdn_ack_bits = GENMASK(12, 12),
103 },
104 [MT7623_POWER_DOMAIN_VDEC] = {
105 .sta_mask = PWR_STATUS_VDEC,
106 .ctl_offs = SPM_VDE_PWR_CON,
107 .sram_pdn_bits = GENMASK(11, 8),
108 .sram_pdn_ack_bits = GENMASK(12, 12),
109 },
110 [MT7623_POWER_DOMAIN_ISP] = {
111 .sta_mask = PWR_STATUS_ISP,
112 .ctl_offs = SPM_ISP_PWR_CON,
113 .sram_pdn_bits = GENMASK(11, 8),
114 .sram_pdn_ack_bits = GENMASK(13, 12),
115 },
116 [MT7623_POWER_DOMAIN_BDP] = {
117 .sta_mask = PWR_STATUS_BDP,
118 .ctl_offs = SPM_BDP_PWR_CON,
119 .sram_pdn_bits = GENMASK(11, 8),
120 },
121 [MT7623_POWER_DOMAIN_ETH] = {
122 .sta_mask = PWR_STATUS_ETH,
123 .ctl_offs = SPM_ETH_PWR_CON,
124 .sram_pdn_bits = GENMASK(11, 8),
125 .sram_pdn_ack_bits = GENMASK(15, 12),
126 },
127 [MT7623_POWER_DOMAIN_HIF] = {
128 .sta_mask = PWR_STATUS_HIF,
129 .ctl_offs = SPM_HIF_PWR_CON,
130 .sram_pdn_bits = GENMASK(11, 8),
131 .sram_pdn_ack_bits = GENMASK(15, 12),
132 },
133 [MT7623_POWER_DOMAIN_IFR_MSC] = {
134 .sta_mask = PWR_STATUS_IFR_MSC,
135 .ctl_offs = SPM_IFR_MSC_PWR_CON,
136 },
137};
138
developer8bd109d2018-11-15 10:08:00 +0800139static struct scp_domain_data scp_domain_mt7629[] = {
140 [MT7629_POWER_DOMAIN_ETHSYS] = {
141 .sta_mask = PWR_STATUS_ETHSYS,
142 .ctl_offs = SPM_ETHSYS_PWR_CON,
143 .sram_pdn_bits = GENMASK(11, 8),
144 .sram_pdn_ack_bits = GENMASK(15, 12),
145 .bus_prot_mask = (BIT(3) | BIT(17)),
146 },
147 [MT7629_POWER_DOMAIN_HIF0] = {
148 .sta_mask = PWR_STATUS_HIF0,
149 .ctl_offs = SPM_HIF0_PWR_CON,
150 .sram_pdn_bits = GENMASK(11, 8),
151 .sram_pdn_ack_bits = GENMASK(15, 12),
152 .bus_prot_mask = GENMASK(25, 24),
153 },
154 [MT7629_POWER_DOMAIN_HIF1] = {
155 .sta_mask = PWR_STATUS_HIF1,
156 .ctl_offs = SPM_HIF1_PWR_CON,
157 .sram_pdn_bits = GENMASK(11, 8),
158 .sram_pdn_ack_bits = GENMASK(15, 12),
159 .bus_prot_mask = GENMASK(28, 26),
160 },
161};
162
163/**
164 * This function enables the bus protection bits for disabled power
165 * domains so that the system does not hang when some unit accesses the
166 * bus while in power down.
167 */
168static int mtk_infracfg_set_bus_protection(void __iomem *infracfg,
169 u32 mask)
170{
171 u32 val;
172
173 clrsetbits_le32(infracfg + INFRA_TOPAXI_PROT_EN, mask, mask);
174
175 return readl_poll_timeout(infracfg + INFRA_TOPAXI_PROT_STA1, val,
176 (val & mask) == mask, 100);
177}
178
179static int mtk_infracfg_clear_bus_protection(void __iomem *infracfg,
180 u32 mask)
181{
182 u32 val;
183
184 clrbits_le32(infracfg + INFRA_TOPAXI_PROT_EN, mask);
185
186 return readl_poll_timeout(infracfg + INFRA_TOPAXI_PROT_STA1, val,
187 !(val & mask), 100);
188}
189
190static int scpsys_domain_is_on(struct scp_domain_data *data)
191{
192 struct scp_domain *scpd = data->scpd;
193 u32 sta = readl(scpd->base + SPM_PWR_STATUS) &
194 data->sta_mask;
195 u32 sta2 = readl(scpd->base + SPM_PWR_STATUS_2ND) &
196 data->sta_mask;
197
198 /*
199 * A domain is on when both status bits are set. If only one is set
200 * return an error. This happens while powering up a domain
201 */
202 if (sta && sta2)
203 return true;
204 if (!sta && !sta2)
205 return false;
206
207 return -EINVAL;
208}
209
210static int scpsys_power_on(struct power_domain *power_domain)
211{
212 struct scp_domain *scpd = dev_get_priv(power_domain->dev);
213 struct scp_domain_data *data = &scpd->data[power_domain->id];
214 void __iomem *ctl_addr = scpd->base + data->ctl_offs;
215 u32 pdn_ack = data->sram_pdn_ack_bits;
216 u32 val;
217 int ret, tmp;
218
219 writel(SPM_EN, scpd->base);
220
221 val = readl(ctl_addr);
222 val |= PWR_ON_BIT;
223 writel(val, ctl_addr);
224
225 val |= PWR_ON_2ND_BIT;
226 writel(val, ctl_addr);
227
228 ret = readx_poll_timeout(scpsys_domain_is_on, data, tmp, tmp > 0,
229 100);
230 if (ret < 0)
231 return ret;
232
233 val &= ~PWR_CLK_DIS_BIT;
234 writel(val, ctl_addr);
235
236 val &= ~PWR_ISO_BIT;
237 writel(val, ctl_addr);
238
239 val |= PWR_RST_B_BIT;
240 writel(val, ctl_addr);
241
242 val &= ~data->sram_pdn_bits;
243 writel(val, ctl_addr);
244
245 ret = readl_poll_timeout(ctl_addr, tmp, !(tmp & pdn_ack), 100);
246 if (ret < 0)
247 return ret;
248
249 if (data->bus_prot_mask) {
250 ret = mtk_infracfg_clear_bus_protection(scpd->infracfg,
251 data->bus_prot_mask);
252 if (ret)
253 return ret;
254 }
255
256 return 0;
257}
258
259static int scpsys_power_off(struct power_domain *power_domain)
260{
261 struct scp_domain *scpd = dev_get_priv(power_domain->dev);
262 struct scp_domain_data *data = &scpd->data[power_domain->id];
263 void __iomem *ctl_addr = scpd->base + data->ctl_offs;
264 u32 pdn_ack = data->sram_pdn_ack_bits;
265 u32 val;
266 int ret, tmp;
267
268 if (data->bus_prot_mask) {
269 ret = mtk_infracfg_set_bus_protection(scpd->infracfg,
270 data->bus_prot_mask);
271 if (ret)
272 return ret;
273 }
274
275 val = readl(ctl_addr);
276 val |= data->sram_pdn_bits;
277 writel(val, ctl_addr);
278
279 ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == pdn_ack,
280 100);
281 if (ret < 0)
282 return ret;
283
284 val |= PWR_ISO_BIT;
285 writel(val, ctl_addr);
286
287 val &= ~PWR_RST_B_BIT;
288 writel(val, ctl_addr);
289
290 val |= PWR_CLK_DIS_BIT;
291 writel(val, ctl_addr);
292
293 val &= ~PWR_ON_BIT;
294 writel(val, ctl_addr);
295
296 val &= ~PWR_ON_2ND_BIT;
297 writel(val, ctl_addr);
298
299 ret = readx_poll_timeout(scpsys_domain_is_on, data, tmp, !tmp, 100);
300 if (ret < 0)
301 return ret;
302
303 return 0;
304}
305
306static int scpsys_power_request(struct power_domain *power_domain)
307{
308 struct scp_domain *scpd = dev_get_priv(power_domain->dev);
309 struct scp_domain_data *data;
310
311 data = &scpd->data[power_domain->id];
312 data->scpd = scpd;
313
314 return 0;
315}
316
317static int scpsys_power_free(struct power_domain *power_domain)
318{
319 return 0;
320}
321
322static int mtk_power_domain_hook(struct udevice *dev)
323{
324 struct scp_domain *scpd = dev_get_priv(dev);
325
326 scpd->type = (enum scp_domain_type)dev_get_driver_data(dev);
327
328 switch (scpd->type) {
developer58898812018-11-15 10:08:01 +0800329 case SCPSYS_MT7623:
330 scpd->data = scp_domain_mt7623;
331 break;
developere7cceca2020-01-10 16:30:31 +0800332 case SCPSYS_MT7622:
developer8bd109d2018-11-15 10:08:00 +0800333 case SCPSYS_MT7629:
334 scpd->data = scp_domain_mt7629;
335 break;
336 default:
337 return -EINVAL;
338 }
339
340 return 0;
341}
342
343static int mtk_power_domain_probe(struct udevice *dev)
344{
345 struct ofnode_phandle_args args;
346 struct scp_domain *scpd = dev_get_priv(dev);
347 struct regmap *regmap;
348 struct clk_bulk bulk;
349 int err;
350
351 scpd->base = dev_read_addr_ptr(dev);
352 if (!scpd->base)
353 return -ENOENT;
354
355 err = mtk_power_domain_hook(dev);
356 if (err)
357 return err;
358
359 /* get corresponding syscon phandle */
360 err = dev_read_phandle_with_args(dev, "infracfg", NULL, 0, 0, &args);
361 if (err)
362 return err;
363
364 regmap = syscon_node_to_regmap(args.node);
365 if (IS_ERR(regmap))
366 return PTR_ERR(regmap);
367
368 scpd->infracfg = regmap_get_range(regmap, 0);
369 if (!scpd->infracfg)
370 return -ENOENT;
371
372 /* enable Infra DCM */
373 setbits_le32(scpd->infracfg + INFRA_TOPDCM_CTRL, DCM_TOP_EN);
374
375 err = clk_get_bulk(dev, &bulk);
376 if (err)
377 return err;
378
379 return clk_enable_bulk(&bulk);
380}
381
382static const struct udevice_id mtk_power_domain_ids[] = {
383 {
developere7cceca2020-01-10 16:30:31 +0800384 .compatible = "mediatek,mt7622-scpsys",
385 .data = SCPSYS_MT7622,
386 },
387 {
developer58898812018-11-15 10:08:01 +0800388 .compatible = "mediatek,mt7623-scpsys",
389 .data = SCPSYS_MT7623,
390 },
391 {
developer8bd109d2018-11-15 10:08:00 +0800392 .compatible = "mediatek,mt7629-scpsys",
393 .data = SCPSYS_MT7629,
394 },
395 { /* sentinel */ }
396};
397
398struct power_domain_ops mtk_power_domain_ops = {
399 .free = scpsys_power_free,
400 .off = scpsys_power_off,
401 .on = scpsys_power_on,
402 .request = scpsys_power_request,
403};
404
405U_BOOT_DRIVER(mtk_power_domain) = {
406 .name = "mtk_power_domain",
407 .id = UCLASS_POWER_DOMAIN,
408 .ops = &mtk_power_domain_ops,
409 .probe = mtk_power_domain_probe,
410 .of_match = mtk_power_domain_ids,
411 .priv_auto_alloc_size = sizeof(struct scp_domain),
412};