blob: e2e41cf5fee1fc92d0914edb745f858717bd6354 [file] [log] [blame]
Patrick Wildtd08a1942019-10-03 15:51:50 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright 2017 NXP
4 */
5
6#include <common.h>
Marek Vasut4eb82c22022-04-13 00:42:51 +02007#include <clk.h>
Patrick Wildtd08a1942019-10-03 15:51:50 +02008#include <dm.h>
Simon Glass336d4612020-02-03 07:36:16 -07009#include <malloc.h>
Patrick Wildtd08a1942019-10-03 15:51:50 +020010#include <power-domain-uclass.h>
Simon Glass401d1c42020-10-30 21:38:53 -060011#include <asm/global_data.h>
Patrick Wildtd08a1942019-10-03 15:51:50 +020012#include <asm/io.h>
Patrick Wildtd08a1942019-10-03 15:51:50 +020013#include <asm/mach-imx/sys_proto.h>
14#include <dm/device-internal.h>
15#include <dm/device.h>
Marek Vasut4eb82c22022-04-13 00:42:51 +020016#include <dm/device_compat.h>
Patrick Wildtd08a1942019-10-03 15:51:50 +020017#include <imx_sip.h>
Marek Vasut4eb82c22022-04-13 00:42:51 +020018#include <linux/bitmap.h>
19#include <wait_bit.h>
20
21#include <dt-bindings/power/imx8mm-power.h>
22#include <dt-bindings/power/imx8mn-power.h>
23#include <dt-bindings/power/imx8mq-power.h>
Patrick Wildtd08a1942019-10-03 15:51:50 +020024
25DECLARE_GLOBAL_DATA_PTR;
26
Marek Vasut4eb82c22022-04-13 00:42:51 +020027#define GPC_PGC_CPU_MAPPING 0x0ec
28
29#define IMX8M_PCIE2_A53_DOMAIN BIT(15)
30#define IMX8M_OTG2_A53_DOMAIN BIT(5)
31#define IMX8M_OTG1_A53_DOMAIN BIT(4)
32#define IMX8M_PCIE1_A53_DOMAIN BIT(3)
33
34#define IMX8MM_OTG2_A53_DOMAIN BIT(5)
35#define IMX8MM_OTG1_A53_DOMAIN BIT(4)
36#define IMX8MM_PCIE_A53_DOMAIN BIT(3)
37
38#define IMX8MN_OTG1_A53_DOMAIN BIT(4)
39#define IMX8MN_MIPI_A53_DOMAIN BIT(2)
40
41#define GPC_PU_PGC_SW_PUP_REQ 0x0f8
42#define GPC_PU_PGC_SW_PDN_REQ 0x104
43
44#define IMX8M_PCIE2_SW_Pxx_REQ BIT(13)
45#define IMX8M_OTG2_SW_Pxx_REQ BIT(3)
46#define IMX8M_OTG1_SW_Pxx_REQ BIT(2)
47#define IMX8M_PCIE1_SW_Pxx_REQ BIT(1)
48
49#define IMX8MM_OTG2_SW_Pxx_REQ BIT(3)
50#define IMX8MM_OTG1_SW_Pxx_REQ BIT(2)
51#define IMX8MM_PCIE_SW_Pxx_REQ BIT(1)
52
53#define IMX8MN_OTG1_SW_Pxx_REQ BIT(2)
54#define IMX8MN_MIPI_SW_Pxx_REQ BIT(0)
55
56#define GPC_M4_PU_PDN_FLG 0x1bc
57
58#define GPC_PU_PWRHSK 0x1fc
59
60#define IMX8MM_HSIO_HSK_PWRDNACKN (BIT(23) | BIT(24))
61#define IMX8MM_HSIO_HSK_PWRDNREQN (BIT(5) | BIT(6))
62
63#define IMX8MN_HSIO_HSK_PWRDNACKN BIT(23)
64#define IMX8MN_HSIO_HSK_PWRDNREQN BIT(5)
65
66/*
67 * The PGC offset values in Reference Manual
68 * (Rev. 1, 01/2018 and the older ones) GPC chapter's
69 * GPC_PGC memory map are incorrect, below offset
70 * values are from design RTL.
71 */
72#define IMX8M_PGC_PCIE1 17
73#define IMX8M_PGC_OTG1 18
74#define IMX8M_PGC_OTG2 19
75#define IMX8M_PGC_PCIE2 29
76
77#define IMX8MM_PGC_PCIE 17
78#define IMX8MM_PGC_OTG1 18
79#define IMX8MM_PGC_OTG2 19
80
81#define IMX8MN_PGC_OTG1 18
82
83#define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40)
84#define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc)
85
86#define GPC_PGC_CTRL_PCR BIT(0)
87
88struct imx_pgc_regs {
89 u16 map;
90 u16 pup;
91 u16 pdn;
92 u16 hsk;
93};
94
95struct imx_pgc_domain {
96 unsigned long pgc;
97
98 const struct {
99 u32 pxx;
100 u32 map;
101 u32 hskreq;
102 u32 hskack;
103 } bits;
104
105 const bool keep_clocks;
106};
107
108struct imx_pgc_domain_data {
109 const struct imx_pgc_domain *domains;
110 size_t domains_num;
111 const struct imx_pgc_regs *pgc_regs;
112};
113
Marek Vasut19842b62022-04-13 00:42:50 +0200114struct imx8m_power_domain_plat {
Marek Vasut4eb82c22022-04-13 00:42:51 +0200115 struct power_domain pd;
116 const struct imx_pgc_domain *domain;
117 const struct imx_pgc_regs *regs;
118 struct clk_bulk clk;
119 void __iomem *base;
Marek Vasut19842b62022-04-13 00:42:50 +0200120 int resource_id;
121 int has_pd;
Marek Vasut19842b62022-04-13 00:42:50 +0200122};
123
Marek Vasut4eb82c22022-04-13 00:42:51 +0200124#if defined(CONFIG_IMX8MM) || defined(CONFIG_IMX8MN) || defined(CONFIG_IMX8MQ)
125static const struct imx_pgc_regs imx7_pgc_regs = {
126 .map = GPC_PGC_CPU_MAPPING,
127 .pup = GPC_PU_PGC_SW_PUP_REQ,
128 .pdn = GPC_PU_PGC_SW_PDN_REQ,
129 .hsk = GPC_PU_PWRHSK,
130};
131#endif
132
133#ifdef CONFIG_IMX8MQ
134static const struct imx_pgc_domain imx8m_pgc_domains[] = {
135 [IMX8M_POWER_DOMAIN_PCIE1] = {
136 .bits = {
137 .pxx = IMX8M_PCIE1_SW_Pxx_REQ,
138 .map = IMX8M_PCIE1_A53_DOMAIN,
139 },
140 .pgc = BIT(IMX8M_PGC_PCIE1),
141 },
142
143 [IMX8M_POWER_DOMAIN_USB_OTG1] = {
144 .bits = {
145 .pxx = IMX8M_OTG1_SW_Pxx_REQ,
146 .map = IMX8M_OTG1_A53_DOMAIN,
147 },
148 .pgc = BIT(IMX8M_PGC_OTG1),
149 },
150
151 [IMX8M_POWER_DOMAIN_USB_OTG2] = {
152 .bits = {
153 .pxx = IMX8M_OTG2_SW_Pxx_REQ,
154 .map = IMX8M_OTG2_A53_DOMAIN,
155 },
156 .pgc = BIT(IMX8M_PGC_OTG2),
157 },
158
159 [IMX8M_POWER_DOMAIN_PCIE2] = {
160 .bits = {
161 .pxx = IMX8M_PCIE2_SW_Pxx_REQ,
162 .map = IMX8M_PCIE2_A53_DOMAIN,
163 },
164 .pgc = BIT(IMX8M_PGC_PCIE2),
165 },
166};
167
168static const struct imx_pgc_domain_data imx8m_pgc_domain_data = {
169 .domains = imx8m_pgc_domains,
170 .domains_num = ARRAY_SIZE(imx8m_pgc_domains),
171 .pgc_regs = &imx7_pgc_regs,
172};
173#endif
174
175#ifdef CONFIG_IMX8MM
176static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
177 [IMX8MM_POWER_DOMAIN_HSIOMIX] = {
178 .bits = {
179 .pxx = 0, /* no power sequence control */
180 .map = 0, /* no power sequence control */
181 .hskreq = IMX8MM_HSIO_HSK_PWRDNREQN,
182 .hskack = IMX8MM_HSIO_HSK_PWRDNACKN,
183 },
184 .keep_clocks = true,
185 },
186
187 [IMX8MM_POWER_DOMAIN_PCIE] = {
188 .bits = {
189 .pxx = IMX8MM_PCIE_SW_Pxx_REQ,
190 .map = IMX8MM_PCIE_A53_DOMAIN,
191 },
192 .pgc = BIT(IMX8MM_PGC_PCIE),
193 },
194
195 [IMX8MM_POWER_DOMAIN_OTG1] = {
196 .bits = {
197 .pxx = IMX8MM_OTG1_SW_Pxx_REQ,
198 .map = IMX8MM_OTG1_A53_DOMAIN,
199 },
200 .pgc = BIT(IMX8MM_PGC_OTG1),
201 },
202
203 [IMX8MM_POWER_DOMAIN_OTG2] = {
204 .bits = {
205 .pxx = IMX8MM_OTG2_SW_Pxx_REQ,
206 .map = IMX8MM_OTG2_A53_DOMAIN,
207 },
208 .pgc = BIT(IMX8MM_PGC_OTG2),
209 },
210};
211
212static const struct imx_pgc_domain_data imx8mm_pgc_domain_data = {
213 .domains = imx8mm_pgc_domains,
214 .domains_num = ARRAY_SIZE(imx8mm_pgc_domains),
215 .pgc_regs = &imx7_pgc_regs,
216};
217#endif
218
219#ifdef CONFIG_IMX8MN
220static const struct imx_pgc_domain imx8mn_pgc_domains[] = {
221 [IMX8MN_POWER_DOMAIN_HSIOMIX] = {
222 .bits = {
223 .pxx = 0, /* no power sequence control */
224 .map = 0, /* no power sequence control */
225 .hskreq = IMX8MN_HSIO_HSK_PWRDNREQN,
226 .hskack = IMX8MN_HSIO_HSK_PWRDNACKN,
227 },
228 .keep_clocks = true,
229 },
230
231 [IMX8MN_POWER_DOMAIN_OTG1] = {
232 .bits = {
233 .pxx = IMX8MN_OTG1_SW_Pxx_REQ,
234 .map = IMX8MN_OTG1_A53_DOMAIN,
235 },
236 .pgc = BIT(IMX8MN_PGC_OTG1),
237 },
238};
239
240static const struct imx_pgc_domain_data imx8mn_pgc_domain_data = {
241 .domains = imx8mn_pgc_domains,
242 .domains_num = ARRAY_SIZE(imx8mn_pgc_domains),
243 .pgc_regs = &imx7_pgc_regs,
244};
245#endif
246
Patrick Wildtd08a1942019-10-03 15:51:50 +0200247static int imx8m_power_domain_on(struct power_domain *power_domain)
248{
249 struct udevice *dev = power_domain->dev;
Marek Vasut4eb82c22022-04-13 00:42:51 +0200250 struct imx8m_power_domain_plat *pdata = dev_get_plat(dev);
251 const struct imx_pgc_domain *domain = pdata->domain;
252 const struct imx_pgc_regs *regs = pdata->regs;
253 void __iomem *base = pdata->base;
254 u32 pgc;
255 int ret;
Peng Fan4b8f22d2020-05-11 15:16:37 +0800256
Marek Vasut4eb82c22022-04-13 00:42:51 +0200257 if (pdata->clk.count) {
258 ret = clk_enable_bulk(&pdata->clk);
259 if (ret) {
260 dev_err(dev, "failed to enable reset clocks\n");
261 return ret;
262 }
263 }
Patrick Wildtd08a1942019-10-03 15:51:50 +0200264
Marek Vasut4eb82c22022-04-13 00:42:51 +0200265 if (domain->bits.pxx) {
266 /* request the domain to power up */
267 setbits_le32(base + regs->pup, domain->bits.pxx);
Patrick Wildtd08a1942019-10-03 15:51:50 +0200268
Marek Vasut4eb82c22022-04-13 00:42:51 +0200269 /*
270 * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
271 * for PUP_REQ/PDN_REQ bit to be cleared
272 */
273 ret = wait_for_bit_le32(base + regs->pup, domain->bits.pxx,
274 false, 1000, false);
275 if (ret) {
276 dev_err(dev, "failed to command PGC\n");
277 goto out_clk_disable;
278 }
Patrick Wildtd08a1942019-10-03 15:51:50 +0200279
Marek Vasut4eb82c22022-04-13 00:42:51 +0200280 /* disable power control */
281 for_each_set_bit(pgc, &domain->pgc, 32) {
282 clrbits_le32(base + GPC_PGC_CTRL(pgc),
283 GPC_PGC_CTRL_PCR);
284 }
285 }
286
287 /* delay for reset to propagate */
288 udelay(5);
289
290 /* request the ADB400 to power up */
291 if (domain->bits.hskreq)
292 setbits_le32(base + regs->hsk, domain->bits.hskreq);
293
294 /* Disable reset clocks for all devices in the domain */
295 if (!domain->keep_clocks && pdata->clk.count)
296 clk_disable_bulk(&pdata->clk);
Patrick Wildtd08a1942019-10-03 15:51:50 +0200297
298 return 0;
Marek Vasut4eb82c22022-04-13 00:42:51 +0200299
300out_clk_disable:
301 if (pdata->clk.count)
302 clk_disable_bulk(&pdata->clk);
303 return ret;
Patrick Wildtd08a1942019-10-03 15:51:50 +0200304}
305
306static int imx8m_power_domain_off(struct power_domain *power_domain)
307{
308 struct udevice *dev = power_domain->dev;
Marek Vasut4eb82c22022-04-13 00:42:51 +0200309 struct imx8m_power_domain_plat *pdata = dev_get_plat(dev);
310 const struct imx_pgc_domain *domain = pdata->domain;
311 const struct imx_pgc_regs *regs = pdata->regs;
312 void __iomem *base = pdata->base;
313 u32 pgc;
314 int ret;
Patrick Wildtd08a1942019-10-03 15:51:50 +0200315
Marek Vasut4eb82c22022-04-13 00:42:51 +0200316 /* Enable reset clocks for all devices in the domain */
317 if (!domain->keep_clocks && pdata->clk.count) {
318 ret = clk_enable_bulk(&pdata->clk);
319 if (ret)
320 return ret;
321 }
Patrick Wildtd08a1942019-10-03 15:51:50 +0200322
Marek Vasut4eb82c22022-04-13 00:42:51 +0200323 /* request the ADB400 to power down */
324 if (domain->bits.hskreq) {
325 clrbits_le32(base + regs->hsk, domain->bits.hskreq);
326
327 ret = wait_for_bit_le32(base + regs->hsk, domain->bits.hskack,
328 false, 1000, false);
329 if (ret) {
330 dev_err(dev, "failed to power down ADB400\n");
331 goto out_clk_disable;
332 }
333 }
334
335 if (domain->bits.pxx) {
336 /* enable power control */
337 for_each_set_bit(pgc, &domain->pgc, 32) {
338 setbits_le32(base + GPC_PGC_CTRL(pgc),
339 GPC_PGC_CTRL_PCR);
340 }
341
342 /* request the domain to power down */
343 setbits_le32(base + regs->pdn, domain->bits.pxx);
344
345 /*
346 * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
347 * for PUP_REQ/PDN_REQ bit to be cleared
348 */
349 ret = wait_for_bit_le32(base + regs->pdn, domain->bits.pxx,
350 false, 1000, false);
351 if (ret) {
352 dev_err(dev, "failed to command PGC\n");
353 goto out_clk_disable;
354 }
355 }
356
357 /* Disable reset clocks for all devices in the domain */
358 if (pdata->clk.count)
359 clk_disable_bulk(&pdata->clk);
Patrick Wildtd08a1942019-10-03 15:51:50 +0200360
361 if (pdata->has_pd)
362 power_domain_off(&pdata->pd);
363
364 return 0;
Marek Vasut4eb82c22022-04-13 00:42:51 +0200365
366out_clk_disable:
367 if (!domain->keep_clocks && pdata->clk.count)
368 clk_disable_bulk(&pdata->clk);
369
370 return ret;
Patrick Wildtd08a1942019-10-03 15:51:50 +0200371}
372
373static int imx8m_power_domain_of_xlate(struct power_domain *power_domain,
374 struct ofnode_phandle_args *args)
375{
376 return 0;
377}
378
379static int imx8m_power_domain_bind(struct udevice *dev)
380{
381 int offset;
382 const char *name;
383 int ret = 0;
384
385 offset = dev_of_offset(dev);
386 for (offset = fdt_first_subnode(gd->fdt_blob, offset); offset > 0;
387 offset = fdt_next_subnode(gd->fdt_blob, offset)) {
388 /* Bind the subnode to this driver */
389 name = fdt_get_name(gd->fdt_blob, offset, NULL);
390
Marek Vasut8741d922022-04-13 00:42:49 +0200391 /* Descend into 'pgc' subnode */
392 if (!strstr(name, "power-domain")) {
393 offset = fdt_first_subnode(gd->fdt_blob, offset);
394 name = fdt_get_name(gd->fdt_blob, offset, NULL);
395 }
396
Patrick Wildtd08a1942019-10-03 15:51:50 +0200397 ret = device_bind_with_driver_data(dev, dev->driver, name,
398 dev->driver_data,
399 offset_to_ofnode(offset),
400 NULL);
401
402 if (ret == -ENODEV)
403 printf("Driver '%s' refuses to bind\n",
404 dev->driver->name);
405
406 if (ret)
407 printf("Error binding driver '%s': %d\n",
408 dev->driver->name, ret);
409 }
410
411 return 0;
412}
413
Marek Vasut4eb82c22022-04-13 00:42:51 +0200414static int imx8m_power_domain_probe(struct udevice *dev)
415{
416 struct imx8m_power_domain_plat *pdata = dev_get_plat(dev);
417 int ret;
418
419 /* Nothing to do for non-"power-domain" driver instances. */
420 if (!strstr(dev->name, "power-domain"))
421 return 0;
422
423 /* Grab optional power domain clock. */
424 ret = clk_get_bulk(dev, &pdata->clk);
425 if (ret && ret != -ENOENT) {
426 dev_err(dev, "Failed to get domain clock (%d)\n", ret);
427 return ret;
428 }
429
430 return 0;
431}
432
Simon Glassd1998a92020-12-03 16:55:21 -0700433static int imx8m_power_domain_of_to_plat(struct udevice *dev)
Patrick Wildtd08a1942019-10-03 15:51:50 +0200434{
Simon Glass8a8d24b2020-12-03 16:55:23 -0700435 struct imx8m_power_domain_plat *pdata = dev_get_plat(dev);
Marek Vasut4eb82c22022-04-13 00:42:51 +0200436 struct imx_pgc_domain_data *domain_data =
437 (struct imx_pgc_domain_data *)dev_get_driver_data(dev);
Patrick Wildtd08a1942019-10-03 15:51:50 +0200438
439 pdata->resource_id = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
440 "reg", -1);
Marek Vasut4eb82c22022-04-13 00:42:51 +0200441 pdata->domain = &domain_data->domains[pdata->resource_id];
442 pdata->regs = domain_data->pgc_regs;
443 pdata->base = dev_read_addr_ptr(dev->parent);
Patrick Wildtd08a1942019-10-03 15:51:50 +0200444
445 if (!power_domain_get(dev, &pdata->pd))
446 pdata->has_pd = 1;
447
448 return 0;
449}
450
451static const struct udevice_id imx8m_power_domain_ids[] = {
Marek Vasut4eb82c22022-04-13 00:42:51 +0200452#ifdef CONFIG_IMX8MQ
453 { .compatible = "fsl,imx8mq-gpc", .data = (long)&imx8m_pgc_domain_data },
454#endif
455#ifdef CONFIG_IMX8MM
456 { .compatible = "fsl,imx8mm-gpc", .data = (long)&imx8mm_pgc_domain_data },
457#endif
458#ifdef CONFIG_IMX8MN
459 { .compatible = "fsl,imx8mn-gpc", .data = (long)&imx8mn_pgc_domain_data },
460#endif
Patrick Wildtd08a1942019-10-03 15:51:50 +0200461 { }
462};
463
464struct power_domain_ops imx8m_power_domain_ops = {
Patrick Wildtd08a1942019-10-03 15:51:50 +0200465 .on = imx8m_power_domain_on,
466 .off = imx8m_power_domain_off,
467 .of_xlate = imx8m_power_domain_of_xlate,
468};
469
470U_BOOT_DRIVER(imx8m_power_domain) = {
471 .name = "imx8m_power_domain",
472 .id = UCLASS_POWER_DOMAIN,
473 .of_match = imx8m_power_domain_ids,
474 .bind = imx8m_power_domain_bind,
Marek Vasut4eb82c22022-04-13 00:42:51 +0200475 .probe = imx8m_power_domain_probe,
Simon Glassd1998a92020-12-03 16:55:21 -0700476 .of_to_plat = imx8m_power_domain_of_to_plat,
Simon Glass8a8d24b2020-12-03 16:55:23 -0700477 .plat_auto = sizeof(struct imx8m_power_domain_plat),
Patrick Wildtd08a1942019-10-03 15:51:50 +0200478 .ops = &imx8m_power_domain_ops,
479};