blob: ed4a718023b9177970c0f5bee507b17a5714ffa9 [file] [log] [blame]
Ryder Lee2ae7e4d2018-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
17#include <dt-bindings/power/mt7629-power.h>
18
19#define SPM_EN (0xb16 << 16 | 0x1)
20#define SPM_ETHSYS_PWR_CON 0x2e0
21#define SPM_HIF0_PWR_CON 0x2e4
22#define SPM_HIF1_PWR_CON 0x2e8
23#define SPM_PWR_STATUS 0x60c
24#define SPM_PWR_STATUS_2ND 0x610
25
26#define PWR_RST_B_BIT BIT(0)
27#define PWR_ISO_BIT BIT(1)
28#define PWR_ON_BIT BIT(2)
29#define PWR_ON_2ND_BIT BIT(3)
30#define PWR_CLK_DIS_BIT BIT(4)
31
32#define PWR_STATUS_ETHSYS BIT(24)
33#define PWR_STATUS_HIF0 BIT(25)
34#define PWR_STATUS_HIF1 BIT(26)
35
36/* Infrasys configuration */
37#define INFRA_TOPDCM_CTRL 0x10
38#define INFRA_TOPAXI_PROT_EN 0x220
39#define INFRA_TOPAXI_PROT_STA1 0x228
40
41#define DCM_TOP_EN BIT(0)
42
43enum scp_domain_type {
44 SCPSYS_MT7629,
45};
46
47struct scp_domain;
48
49struct scp_domain_data {
50 struct scp_domain *scpd;
51 u32 sta_mask;
52 int ctl_offs;
53 u32 sram_pdn_bits;
54 u32 sram_pdn_ack_bits;
55 u32 bus_prot_mask;
56};
57
58struct scp_domain {
59 void __iomem *base;
60 void __iomem *infracfg;
61 enum scp_domain_type type;
62 struct scp_domain_data *data;
63};
64
65static struct scp_domain_data scp_domain_mt7629[] = {
66 [MT7629_POWER_DOMAIN_ETHSYS] = {
67 .sta_mask = PWR_STATUS_ETHSYS,
68 .ctl_offs = SPM_ETHSYS_PWR_CON,
69 .sram_pdn_bits = GENMASK(11, 8),
70 .sram_pdn_ack_bits = GENMASK(15, 12),
71 .bus_prot_mask = (BIT(3) | BIT(17)),
72 },
73 [MT7629_POWER_DOMAIN_HIF0] = {
74 .sta_mask = PWR_STATUS_HIF0,
75 .ctl_offs = SPM_HIF0_PWR_CON,
76 .sram_pdn_bits = GENMASK(11, 8),
77 .sram_pdn_ack_bits = GENMASK(15, 12),
78 .bus_prot_mask = GENMASK(25, 24),
79 },
80 [MT7629_POWER_DOMAIN_HIF1] = {
81 .sta_mask = PWR_STATUS_HIF1,
82 .ctl_offs = SPM_HIF1_PWR_CON,
83 .sram_pdn_bits = GENMASK(11, 8),
84 .sram_pdn_ack_bits = GENMASK(15, 12),
85 .bus_prot_mask = GENMASK(28, 26),
86 },
87};
88
89/**
90 * This function enables the bus protection bits for disabled power
91 * domains so that the system does not hang when some unit accesses the
92 * bus while in power down.
93 */
94static int mtk_infracfg_set_bus_protection(void __iomem *infracfg,
95 u32 mask)
96{
97 u32 val;
98
99 clrsetbits_le32(infracfg + INFRA_TOPAXI_PROT_EN, mask, mask);
100
101 return readl_poll_timeout(infracfg + INFRA_TOPAXI_PROT_STA1, val,
102 (val & mask) == mask, 100);
103}
104
105static int mtk_infracfg_clear_bus_protection(void __iomem *infracfg,
106 u32 mask)
107{
108 u32 val;
109
110 clrbits_le32(infracfg + INFRA_TOPAXI_PROT_EN, mask);
111
112 return readl_poll_timeout(infracfg + INFRA_TOPAXI_PROT_STA1, val,
113 !(val & mask), 100);
114}
115
116static int scpsys_domain_is_on(struct scp_domain_data *data)
117{
118 struct scp_domain *scpd = data->scpd;
119 u32 sta = readl(scpd->base + SPM_PWR_STATUS) &
120 data->sta_mask;
121 u32 sta2 = readl(scpd->base + SPM_PWR_STATUS_2ND) &
122 data->sta_mask;
123
124 /*
125 * A domain is on when both status bits are set. If only one is set
126 * return an error. This happens while powering up a domain
127 */
128 if (sta && sta2)
129 return true;
130 if (!sta && !sta2)
131 return false;
132
133 return -EINVAL;
134}
135
136static int scpsys_power_on(struct power_domain *power_domain)
137{
138 struct scp_domain *scpd = dev_get_priv(power_domain->dev);
139 struct scp_domain_data *data = &scpd->data[power_domain->id];
140 void __iomem *ctl_addr = scpd->base + data->ctl_offs;
141 u32 pdn_ack = data->sram_pdn_ack_bits;
142 u32 val;
143 int ret, tmp;
144
145 writel(SPM_EN, scpd->base);
146
147 val = readl(ctl_addr);
148 val |= PWR_ON_BIT;
149 writel(val, ctl_addr);
150
151 val |= PWR_ON_2ND_BIT;
152 writel(val, ctl_addr);
153
154 ret = readx_poll_timeout(scpsys_domain_is_on, data, tmp, tmp > 0,
155 100);
156 if (ret < 0)
157 return ret;
158
159 val &= ~PWR_CLK_DIS_BIT;
160 writel(val, ctl_addr);
161
162 val &= ~PWR_ISO_BIT;
163 writel(val, ctl_addr);
164
165 val |= PWR_RST_B_BIT;
166 writel(val, ctl_addr);
167
168 val &= ~data->sram_pdn_bits;
169 writel(val, ctl_addr);
170
171 ret = readl_poll_timeout(ctl_addr, tmp, !(tmp & pdn_ack), 100);
172 if (ret < 0)
173 return ret;
174
175 if (data->bus_prot_mask) {
176 ret = mtk_infracfg_clear_bus_protection(scpd->infracfg,
177 data->bus_prot_mask);
178 if (ret)
179 return ret;
180 }
181
182 return 0;
183}
184
185static int scpsys_power_off(struct power_domain *power_domain)
186{
187 struct scp_domain *scpd = dev_get_priv(power_domain->dev);
188 struct scp_domain_data *data = &scpd->data[power_domain->id];
189 void __iomem *ctl_addr = scpd->base + data->ctl_offs;
190 u32 pdn_ack = data->sram_pdn_ack_bits;
191 u32 val;
192 int ret, tmp;
193
194 if (data->bus_prot_mask) {
195 ret = mtk_infracfg_set_bus_protection(scpd->infracfg,
196 data->bus_prot_mask);
197 if (ret)
198 return ret;
199 }
200
201 val = readl(ctl_addr);
202 val |= data->sram_pdn_bits;
203 writel(val, ctl_addr);
204
205 ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == pdn_ack,
206 100);
207 if (ret < 0)
208 return ret;
209
210 val |= PWR_ISO_BIT;
211 writel(val, ctl_addr);
212
213 val &= ~PWR_RST_B_BIT;
214 writel(val, ctl_addr);
215
216 val |= PWR_CLK_DIS_BIT;
217 writel(val, ctl_addr);
218
219 val &= ~PWR_ON_BIT;
220 writel(val, ctl_addr);
221
222 val &= ~PWR_ON_2ND_BIT;
223 writel(val, ctl_addr);
224
225 ret = readx_poll_timeout(scpsys_domain_is_on, data, tmp, !tmp, 100);
226 if (ret < 0)
227 return ret;
228
229 return 0;
230}
231
232static int scpsys_power_request(struct power_domain *power_domain)
233{
234 struct scp_domain *scpd = dev_get_priv(power_domain->dev);
235 struct scp_domain_data *data;
236
237 data = &scpd->data[power_domain->id];
238 data->scpd = scpd;
239
240 return 0;
241}
242
243static int scpsys_power_free(struct power_domain *power_domain)
244{
245 return 0;
246}
247
248static int mtk_power_domain_hook(struct udevice *dev)
249{
250 struct scp_domain *scpd = dev_get_priv(dev);
251
252 scpd->type = (enum scp_domain_type)dev_get_driver_data(dev);
253
254 switch (scpd->type) {
255 case SCPSYS_MT7629:
256 scpd->data = scp_domain_mt7629;
257 break;
258 default:
259 return -EINVAL;
260 }
261
262 return 0;
263}
264
265static int mtk_power_domain_probe(struct udevice *dev)
266{
267 struct ofnode_phandle_args args;
268 struct scp_domain *scpd = dev_get_priv(dev);
269 struct regmap *regmap;
270 struct clk_bulk bulk;
271 int err;
272
273 scpd->base = dev_read_addr_ptr(dev);
274 if (!scpd->base)
275 return -ENOENT;
276
277 err = mtk_power_domain_hook(dev);
278 if (err)
279 return err;
280
281 /* get corresponding syscon phandle */
282 err = dev_read_phandle_with_args(dev, "infracfg", NULL, 0, 0, &args);
283 if (err)
284 return err;
285
286 regmap = syscon_node_to_regmap(args.node);
287 if (IS_ERR(regmap))
288 return PTR_ERR(regmap);
289
290 scpd->infracfg = regmap_get_range(regmap, 0);
291 if (!scpd->infracfg)
292 return -ENOENT;
293
294 /* enable Infra DCM */
295 setbits_le32(scpd->infracfg + INFRA_TOPDCM_CTRL, DCM_TOP_EN);
296
297 err = clk_get_bulk(dev, &bulk);
298 if (err)
299 return err;
300
301 return clk_enable_bulk(&bulk);
302}
303
304static const struct udevice_id mtk_power_domain_ids[] = {
305 {
306 .compatible = "mediatek,mt7629-scpsys",
307 .data = SCPSYS_MT7629,
308 },
309 { /* sentinel */ }
310};
311
312struct power_domain_ops mtk_power_domain_ops = {
313 .free = scpsys_power_free,
314 .off = scpsys_power_off,
315 .on = scpsys_power_on,
316 .request = scpsys_power_request,
317};
318
319U_BOOT_DRIVER(mtk_power_domain) = {
320 .name = "mtk_power_domain",
321 .id = UCLASS_POWER_DOMAIN,
322 .ops = &mtk_power_domain_ops,
323 .probe = mtk_power_domain_probe,
324 .of_match = mtk_power_domain_ids,
325 .priv_auto_alloc_size = sizeof(struct scp_domain),
326};