xref: /openbmc/linux/drivers/pmdomain/imx/gpc.c (revision b97d6790d03b763eca08847a9a5869a4291b9f9a)
1e2ad626fSUlf Hansson // SPDX-License-Identifier: GPL-2.0+
2e2ad626fSUlf Hansson /*
3e2ad626fSUlf Hansson  * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
4e2ad626fSUlf Hansson  * Copyright 2011-2013 Freescale Semiconductor, Inc.
5e2ad626fSUlf Hansson  */
6e2ad626fSUlf Hansson 
7e2ad626fSUlf Hansson #include <linux/clk.h>
8e2ad626fSUlf Hansson #include <linux/delay.h>
9e2ad626fSUlf Hansson #include <linux/io.h>
10e2ad626fSUlf Hansson #include <linux/of_device.h>
11e2ad626fSUlf Hansson #include <linux/platform_device.h>
12e2ad626fSUlf Hansson #include <linux/pm_domain.h>
13e2ad626fSUlf Hansson #include <linux/regmap.h>
14e2ad626fSUlf Hansson #include <linux/regulator/consumer.h>
15e2ad626fSUlf Hansson 
16e2ad626fSUlf Hansson #define GPC_CNTR		0x000
17e2ad626fSUlf Hansson 
18e2ad626fSUlf Hansson #define GPC_PGC_CTRL_OFFS	0x0
19e2ad626fSUlf Hansson #define GPC_PGC_PUPSCR_OFFS	0x4
20e2ad626fSUlf Hansson #define GPC_PGC_PDNSCR_OFFS	0x8
21e2ad626fSUlf Hansson #define GPC_PGC_SW2ISO_SHIFT	0x8
22e2ad626fSUlf Hansson #define GPC_PGC_SW_SHIFT	0x0
23e2ad626fSUlf Hansson 
24e2ad626fSUlf Hansson #define GPC_PGC_PCI_PDN		0x200
25e2ad626fSUlf Hansson #define GPC_PGC_PCI_SR		0x20c
26e2ad626fSUlf Hansson 
27e2ad626fSUlf Hansson #define GPC_PGC_GPU_PDN		0x260
28e2ad626fSUlf Hansson #define GPC_PGC_GPU_PUPSCR	0x264
29e2ad626fSUlf Hansson #define GPC_PGC_GPU_PDNSCR	0x268
30e2ad626fSUlf Hansson #define GPC_PGC_GPU_SR		0x26c
31e2ad626fSUlf Hansson 
32e2ad626fSUlf Hansson #define GPC_PGC_DISP_PDN	0x240
33e2ad626fSUlf Hansson #define GPC_PGC_DISP_SR		0x24c
34e2ad626fSUlf Hansson 
35e2ad626fSUlf Hansson #define GPU_VPU_PUP_REQ		BIT(1)
36e2ad626fSUlf Hansson #define GPU_VPU_PDN_REQ		BIT(0)
37e2ad626fSUlf Hansson 
38e2ad626fSUlf Hansson #define GPC_CLK_MAX		7
39e2ad626fSUlf Hansson 
40e2ad626fSUlf Hansson #define PGC_DOMAIN_FLAG_NO_PD		BIT(0)
41e2ad626fSUlf Hansson 
42e2ad626fSUlf Hansson struct imx_pm_domain {
43e2ad626fSUlf Hansson 	struct generic_pm_domain base;
44e2ad626fSUlf Hansson 	struct regmap *regmap;
45e2ad626fSUlf Hansson 	struct regulator *supply;
46e2ad626fSUlf Hansson 	struct clk *clk[GPC_CLK_MAX];
47e2ad626fSUlf Hansson 	int num_clks;
48e2ad626fSUlf Hansson 	unsigned int reg_offs;
49e2ad626fSUlf Hansson 	signed char cntr_pdn_bit;
50e2ad626fSUlf Hansson 	unsigned int ipg_rate_mhz;
51e2ad626fSUlf Hansson };
52e2ad626fSUlf Hansson 
53e2ad626fSUlf Hansson static inline struct imx_pm_domain *
to_imx_pm_domain(struct generic_pm_domain * genpd)54e2ad626fSUlf Hansson to_imx_pm_domain(struct generic_pm_domain *genpd)
55e2ad626fSUlf Hansson {
56e2ad626fSUlf Hansson 	return container_of(genpd, struct imx_pm_domain, base);
57e2ad626fSUlf Hansson }
58e2ad626fSUlf Hansson 
imx6_pm_domain_power_off(struct generic_pm_domain * genpd)59e2ad626fSUlf Hansson static int imx6_pm_domain_power_off(struct generic_pm_domain *genpd)
60e2ad626fSUlf Hansson {
61e2ad626fSUlf Hansson 	struct imx_pm_domain *pd = to_imx_pm_domain(genpd);
62e2ad626fSUlf Hansson 	int iso, iso2sw;
63e2ad626fSUlf Hansson 	u32 val;
64e2ad626fSUlf Hansson 
65e2ad626fSUlf Hansson 	/* Read ISO and ISO2SW power down delays */
66e2ad626fSUlf Hansson 	regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PDNSCR_OFFS, &val);
67e2ad626fSUlf Hansson 	iso = val & 0x3f;
68e2ad626fSUlf Hansson 	iso2sw = (val >> 8) & 0x3f;
69e2ad626fSUlf Hansson 
70e2ad626fSUlf Hansson 	/* Gate off domain when powered down */
71e2ad626fSUlf Hansson 	regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS,
72e2ad626fSUlf Hansson 			   0x1, 0x1);
73e2ad626fSUlf Hansson 
74e2ad626fSUlf Hansson 	/* Request GPC to power down domain */
75e2ad626fSUlf Hansson 	val = BIT(pd->cntr_pdn_bit);
76e2ad626fSUlf Hansson 	regmap_update_bits(pd->regmap, GPC_CNTR, val, val);
77e2ad626fSUlf Hansson 
78e2ad626fSUlf Hansson 	/* Wait ISO + ISO2SW IPG clock cycles */
79e2ad626fSUlf Hansson 	udelay(DIV_ROUND_UP(iso + iso2sw, pd->ipg_rate_mhz));
80e2ad626fSUlf Hansson 
81e2ad626fSUlf Hansson 	if (pd->supply)
82e2ad626fSUlf Hansson 		regulator_disable(pd->supply);
83e2ad626fSUlf Hansson 
84e2ad626fSUlf Hansson 	return 0;
85e2ad626fSUlf Hansson }
86e2ad626fSUlf Hansson 
imx6_pm_domain_power_on(struct generic_pm_domain * genpd)87e2ad626fSUlf Hansson static int imx6_pm_domain_power_on(struct generic_pm_domain *genpd)
88e2ad626fSUlf Hansson {
89e2ad626fSUlf Hansson 	struct imx_pm_domain *pd = to_imx_pm_domain(genpd);
90e2ad626fSUlf Hansson 	int i, ret;
91e2ad626fSUlf Hansson 	u32 val, req;
92e2ad626fSUlf Hansson 
93e2ad626fSUlf Hansson 	if (pd->supply) {
94e2ad626fSUlf Hansson 		ret = regulator_enable(pd->supply);
95e2ad626fSUlf Hansson 		if (ret) {
96e2ad626fSUlf Hansson 			pr_err("%s: failed to enable regulator: %d\n",
97e2ad626fSUlf Hansson 			       __func__, ret);
98e2ad626fSUlf Hansson 			return ret;
99e2ad626fSUlf Hansson 		}
100e2ad626fSUlf Hansson 	}
101e2ad626fSUlf Hansson 
102e2ad626fSUlf Hansson 	/* Enable reset clocks for all devices in the domain */
103e2ad626fSUlf Hansson 	for (i = 0; i < pd->num_clks; i++)
104e2ad626fSUlf Hansson 		clk_prepare_enable(pd->clk[i]);
105e2ad626fSUlf Hansson 
106e2ad626fSUlf Hansson 	/* Gate off domain when powered down */
107e2ad626fSUlf Hansson 	regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS,
108e2ad626fSUlf Hansson 			   0x1, 0x1);
109e2ad626fSUlf Hansson 
110e2ad626fSUlf Hansson 	/* Request GPC to power up domain */
111e2ad626fSUlf Hansson 	req = BIT(pd->cntr_pdn_bit + 1);
112e2ad626fSUlf Hansson 	regmap_update_bits(pd->regmap, GPC_CNTR, req, req);
113e2ad626fSUlf Hansson 
114e2ad626fSUlf Hansson 	/* Wait for the PGC to handle the request */
115e2ad626fSUlf Hansson 	ret = regmap_read_poll_timeout(pd->regmap, GPC_CNTR, val, !(val & req),
116e2ad626fSUlf Hansson 				       1, 50);
117e2ad626fSUlf Hansson 	if (ret)
118e2ad626fSUlf Hansson 		pr_err("powerup request on domain %s timed out\n", genpd->name);
119e2ad626fSUlf Hansson 
120e2ad626fSUlf Hansson 	/* Wait for reset to propagate through peripherals */
121e2ad626fSUlf Hansson 	usleep_range(5, 10);
122e2ad626fSUlf Hansson 
123e2ad626fSUlf Hansson 	/* Disable reset clocks for all devices in the domain */
124e2ad626fSUlf Hansson 	for (i = 0; i < pd->num_clks; i++)
125e2ad626fSUlf Hansson 		clk_disable_unprepare(pd->clk[i]);
126e2ad626fSUlf Hansson 
127e2ad626fSUlf Hansson 	return 0;
128e2ad626fSUlf Hansson }
129e2ad626fSUlf Hansson 
imx_pgc_get_clocks(struct device * dev,struct imx_pm_domain * domain)130e2ad626fSUlf Hansson static int imx_pgc_get_clocks(struct device *dev, struct imx_pm_domain *domain)
131e2ad626fSUlf Hansson {
132e2ad626fSUlf Hansson 	int i, ret;
133e2ad626fSUlf Hansson 
134e2ad626fSUlf Hansson 	for (i = 0; ; i++) {
135e2ad626fSUlf Hansson 		struct clk *clk = of_clk_get(dev->of_node, i);
136e2ad626fSUlf Hansson 		if (IS_ERR(clk))
137e2ad626fSUlf Hansson 			break;
138e2ad626fSUlf Hansson 		if (i >= GPC_CLK_MAX) {
139e2ad626fSUlf Hansson 			dev_err(dev, "more than %d clocks\n", GPC_CLK_MAX);
140e2ad626fSUlf Hansson 			ret = -EINVAL;
141e2ad626fSUlf Hansson 			goto clk_err;
142e2ad626fSUlf Hansson 		}
143e2ad626fSUlf Hansson 		domain->clk[i] = clk;
144e2ad626fSUlf Hansson 	}
145e2ad626fSUlf Hansson 	domain->num_clks = i;
146e2ad626fSUlf Hansson 
147e2ad626fSUlf Hansson 	return 0;
148e2ad626fSUlf Hansson 
149e2ad626fSUlf Hansson clk_err:
150e2ad626fSUlf Hansson 	while (i--)
151e2ad626fSUlf Hansson 		clk_put(domain->clk[i]);
152e2ad626fSUlf Hansson 
153e2ad626fSUlf Hansson 	return ret;
154e2ad626fSUlf Hansson }
155e2ad626fSUlf Hansson 
imx_pgc_put_clocks(struct imx_pm_domain * domain)156e2ad626fSUlf Hansson static void imx_pgc_put_clocks(struct imx_pm_domain *domain)
157e2ad626fSUlf Hansson {
158e2ad626fSUlf Hansson 	int i;
159e2ad626fSUlf Hansson 
160e2ad626fSUlf Hansson 	for (i = domain->num_clks - 1; i >= 0; i--)
161e2ad626fSUlf Hansson 		clk_put(domain->clk[i]);
162e2ad626fSUlf Hansson }
163e2ad626fSUlf Hansson 
imx_pgc_parse_dt(struct device * dev,struct imx_pm_domain * domain)164e2ad626fSUlf Hansson static int imx_pgc_parse_dt(struct device *dev, struct imx_pm_domain *domain)
165e2ad626fSUlf Hansson {
166e2ad626fSUlf Hansson 	/* try to get the domain supply regulator */
167e2ad626fSUlf Hansson 	domain->supply = devm_regulator_get_optional(dev, "power");
168e2ad626fSUlf Hansson 	if (IS_ERR(domain->supply)) {
169e2ad626fSUlf Hansson 		if (PTR_ERR(domain->supply) == -ENODEV)
170e2ad626fSUlf Hansson 			domain->supply = NULL;
171e2ad626fSUlf Hansson 		else
172e2ad626fSUlf Hansson 			return PTR_ERR(domain->supply);
173e2ad626fSUlf Hansson 	}
174e2ad626fSUlf Hansson 
175e2ad626fSUlf Hansson 	/* try to get all clocks needed for reset propagation */
176e2ad626fSUlf Hansson 	return imx_pgc_get_clocks(dev, domain);
177e2ad626fSUlf Hansson }
178e2ad626fSUlf Hansson 
imx_pgc_power_domain_probe(struct platform_device * pdev)179e2ad626fSUlf Hansson static int imx_pgc_power_domain_probe(struct platform_device *pdev)
180e2ad626fSUlf Hansson {
181e2ad626fSUlf Hansson 	struct imx_pm_domain *domain = pdev->dev.platform_data;
182e2ad626fSUlf Hansson 	struct device *dev = &pdev->dev;
183e2ad626fSUlf Hansson 	int ret;
184e2ad626fSUlf Hansson 
185e2ad626fSUlf Hansson 	/* if this PD is associated with a DT node try to parse it */
186e2ad626fSUlf Hansson 	if (dev->of_node) {
187e2ad626fSUlf Hansson 		ret = imx_pgc_parse_dt(dev, domain);
188e2ad626fSUlf Hansson 		if (ret)
189e2ad626fSUlf Hansson 			return ret;
190e2ad626fSUlf Hansson 	}
191e2ad626fSUlf Hansson 
192e2ad626fSUlf Hansson 	/* initially power on the domain */
193e2ad626fSUlf Hansson 	if (domain->base.power_on)
194e2ad626fSUlf Hansson 		domain->base.power_on(&domain->base);
195e2ad626fSUlf Hansson 
196e2ad626fSUlf Hansson 	if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
197e2ad626fSUlf Hansson 		pm_genpd_init(&domain->base, NULL, false);
198e2ad626fSUlf Hansson 		ret = of_genpd_add_provider_simple(dev->of_node, &domain->base);
199e2ad626fSUlf Hansson 		if (ret)
200e2ad626fSUlf Hansson 			goto genpd_err;
201e2ad626fSUlf Hansson 	}
202e2ad626fSUlf Hansson 
203e2ad626fSUlf Hansson 	device_link_add(dev, dev->parent, DL_FLAG_AUTOREMOVE_CONSUMER);
204e2ad626fSUlf Hansson 
205e2ad626fSUlf Hansson 	return 0;
206e2ad626fSUlf Hansson 
207e2ad626fSUlf Hansson genpd_err:
208e2ad626fSUlf Hansson 	pm_genpd_remove(&domain->base);
209e2ad626fSUlf Hansson 	imx_pgc_put_clocks(domain);
210e2ad626fSUlf Hansson 
211e2ad626fSUlf Hansson 	return ret;
212e2ad626fSUlf Hansson }
213e2ad626fSUlf Hansson 
imx_pgc_power_domain_remove(struct platform_device * pdev)214e2ad626fSUlf Hansson static int imx_pgc_power_domain_remove(struct platform_device *pdev)
215e2ad626fSUlf Hansson {
216e2ad626fSUlf Hansson 	struct imx_pm_domain *domain = pdev->dev.platform_data;
217e2ad626fSUlf Hansson 
218e2ad626fSUlf Hansson 	if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
219e2ad626fSUlf Hansson 		of_genpd_del_provider(pdev->dev.of_node);
220e2ad626fSUlf Hansson 		pm_genpd_remove(&domain->base);
221e2ad626fSUlf Hansson 		imx_pgc_put_clocks(domain);
222e2ad626fSUlf Hansson 	}
223e2ad626fSUlf Hansson 
224e2ad626fSUlf Hansson 	return 0;
225e2ad626fSUlf Hansson }
226e2ad626fSUlf Hansson 
227e2ad626fSUlf Hansson static const struct platform_device_id imx_pgc_power_domain_id[] = {
228e2ad626fSUlf Hansson 	{ "imx-pgc-power-domain"},
229e2ad626fSUlf Hansson 	{ },
230e2ad626fSUlf Hansson };
231e2ad626fSUlf Hansson 
232e2ad626fSUlf Hansson static struct platform_driver imx_pgc_power_domain_driver = {
233e2ad626fSUlf Hansson 	.driver = {
234e2ad626fSUlf Hansson 		.name = "imx-pgc-pd",
235e2ad626fSUlf Hansson 	},
236e2ad626fSUlf Hansson 	.probe = imx_pgc_power_domain_probe,
237e2ad626fSUlf Hansson 	.remove = imx_pgc_power_domain_remove,
238e2ad626fSUlf Hansson 	.id_table = imx_pgc_power_domain_id,
239e2ad626fSUlf Hansson };
240e2ad626fSUlf Hansson builtin_platform_driver(imx_pgc_power_domain_driver)
241e2ad626fSUlf Hansson 
242e2ad626fSUlf Hansson #define GPC_PGC_DOMAIN_ARM	0
243e2ad626fSUlf Hansson #define GPC_PGC_DOMAIN_PU	1
244e2ad626fSUlf Hansson #define GPC_PGC_DOMAIN_DISPLAY	2
245e2ad626fSUlf Hansson #define GPC_PGC_DOMAIN_PCI	3
246e2ad626fSUlf Hansson 
247e2ad626fSUlf Hansson static struct genpd_power_state imx6_pm_domain_pu_state = {
248e2ad626fSUlf Hansson 	.power_off_latency_ns = 25000,
249e2ad626fSUlf Hansson 	.power_on_latency_ns = 2000000,
250e2ad626fSUlf Hansson };
251e2ad626fSUlf Hansson 
252e2ad626fSUlf Hansson static struct imx_pm_domain imx_gpc_domains[] = {
253e2ad626fSUlf Hansson 	[GPC_PGC_DOMAIN_ARM] = {
254e2ad626fSUlf Hansson 		.base = {
255e2ad626fSUlf Hansson 			.name = "ARM",
256e2ad626fSUlf Hansson 			.flags = GENPD_FLAG_ALWAYS_ON,
257e2ad626fSUlf Hansson 		},
258e2ad626fSUlf Hansson 	},
259e2ad626fSUlf Hansson 	[GPC_PGC_DOMAIN_PU] = {
260e2ad626fSUlf Hansson 		.base = {
261e2ad626fSUlf Hansson 			.name = "PU",
262e2ad626fSUlf Hansson 			.power_off = imx6_pm_domain_power_off,
263e2ad626fSUlf Hansson 			.power_on = imx6_pm_domain_power_on,
264e2ad626fSUlf Hansson 			.states = &imx6_pm_domain_pu_state,
265e2ad626fSUlf Hansson 			.state_count = 1,
266e2ad626fSUlf Hansson 		},
267e2ad626fSUlf Hansson 		.reg_offs = 0x260,
268e2ad626fSUlf Hansson 		.cntr_pdn_bit = 0,
269e2ad626fSUlf Hansson 	},
270e2ad626fSUlf Hansson 	[GPC_PGC_DOMAIN_DISPLAY] = {
271e2ad626fSUlf Hansson 		.base = {
272e2ad626fSUlf Hansson 			.name = "DISPLAY",
273e2ad626fSUlf Hansson 			.power_off = imx6_pm_domain_power_off,
274e2ad626fSUlf Hansson 			.power_on = imx6_pm_domain_power_on,
275e2ad626fSUlf Hansson 		},
276e2ad626fSUlf Hansson 		.reg_offs = 0x240,
277e2ad626fSUlf Hansson 		.cntr_pdn_bit = 4,
278e2ad626fSUlf Hansson 	},
279e2ad626fSUlf Hansson 	[GPC_PGC_DOMAIN_PCI] = {
280e2ad626fSUlf Hansson 		.base = {
281e2ad626fSUlf Hansson 			.name = "PCI",
282e2ad626fSUlf Hansson 			.power_off = imx6_pm_domain_power_off,
283e2ad626fSUlf Hansson 			.power_on = imx6_pm_domain_power_on,
284e2ad626fSUlf Hansson 		},
285e2ad626fSUlf Hansson 		.reg_offs = 0x200,
286e2ad626fSUlf Hansson 		.cntr_pdn_bit = 6,
287e2ad626fSUlf Hansson 	},
288e2ad626fSUlf Hansson };
289e2ad626fSUlf Hansson 
290e2ad626fSUlf Hansson struct imx_gpc_dt_data {
291e2ad626fSUlf Hansson 	int num_domains;
292e2ad626fSUlf Hansson 	bool err009619_present;
293e2ad626fSUlf Hansson 	bool err006287_present;
294e2ad626fSUlf Hansson };
295e2ad626fSUlf Hansson 
296e2ad626fSUlf Hansson static const struct imx_gpc_dt_data imx6q_dt_data = {
297e2ad626fSUlf Hansson 	.num_domains = 2,
298e2ad626fSUlf Hansson 	.err009619_present = false,
299e2ad626fSUlf Hansson 	.err006287_present = false,
300e2ad626fSUlf Hansson };
301e2ad626fSUlf Hansson 
302e2ad626fSUlf Hansson static const struct imx_gpc_dt_data imx6qp_dt_data = {
303e2ad626fSUlf Hansson 	.num_domains = 2,
304e2ad626fSUlf Hansson 	.err009619_present = true,
305e2ad626fSUlf Hansson 	.err006287_present = false,
306e2ad626fSUlf Hansson };
307e2ad626fSUlf Hansson 
308e2ad626fSUlf Hansson static const struct imx_gpc_dt_data imx6sl_dt_data = {
309e2ad626fSUlf Hansson 	.num_domains = 3,
310e2ad626fSUlf Hansson 	.err009619_present = false,
311e2ad626fSUlf Hansson 	.err006287_present = true,
312e2ad626fSUlf Hansson };
313e2ad626fSUlf Hansson 
314e2ad626fSUlf Hansson static const struct imx_gpc_dt_data imx6sx_dt_data = {
315e2ad626fSUlf Hansson 	.num_domains = 4,
316e2ad626fSUlf Hansson 	.err009619_present = false,
317e2ad626fSUlf Hansson 	.err006287_present = false,
318e2ad626fSUlf Hansson };
319e2ad626fSUlf Hansson 
320e2ad626fSUlf Hansson static const struct of_device_id imx_gpc_dt_ids[] = {
321e2ad626fSUlf Hansson 	{ .compatible = "fsl,imx6q-gpc", .data = &imx6q_dt_data },
322e2ad626fSUlf Hansson 	{ .compatible = "fsl,imx6qp-gpc", .data = &imx6qp_dt_data },
323e2ad626fSUlf Hansson 	{ .compatible = "fsl,imx6sl-gpc", .data = &imx6sl_dt_data },
324e2ad626fSUlf Hansson 	{ .compatible = "fsl,imx6sx-gpc", .data = &imx6sx_dt_data },
325e2ad626fSUlf Hansson 	{ }
326e2ad626fSUlf Hansson };
327e2ad626fSUlf Hansson 
328e2ad626fSUlf Hansson static const struct regmap_range yes_ranges[] = {
329e2ad626fSUlf Hansson 	regmap_reg_range(GPC_CNTR, GPC_CNTR),
330e2ad626fSUlf Hansson 	regmap_reg_range(GPC_PGC_PCI_PDN, GPC_PGC_PCI_SR),
331e2ad626fSUlf Hansson 	regmap_reg_range(GPC_PGC_GPU_PDN, GPC_PGC_GPU_SR),
332e2ad626fSUlf Hansson 	regmap_reg_range(GPC_PGC_DISP_PDN, GPC_PGC_DISP_SR),
333e2ad626fSUlf Hansson };
334e2ad626fSUlf Hansson 
335e2ad626fSUlf Hansson static const struct regmap_access_table access_table = {
336e2ad626fSUlf Hansson 	.yes_ranges	= yes_ranges,
337e2ad626fSUlf Hansson 	.n_yes_ranges	= ARRAY_SIZE(yes_ranges),
338e2ad626fSUlf Hansson };
339e2ad626fSUlf Hansson 
340e2ad626fSUlf Hansson static const struct regmap_config imx_gpc_regmap_config = {
341e2ad626fSUlf Hansson 	.reg_bits = 32,
342e2ad626fSUlf Hansson 	.val_bits = 32,
343e2ad626fSUlf Hansson 	.reg_stride = 4,
344e2ad626fSUlf Hansson 	.rd_table = &access_table,
345e2ad626fSUlf Hansson 	.wr_table = &access_table,
346e2ad626fSUlf Hansson 	.max_register = 0x2ac,
347e2ad626fSUlf Hansson 	.fast_io = true,
348e2ad626fSUlf Hansson };
349e2ad626fSUlf Hansson 
350e2ad626fSUlf Hansson static struct generic_pm_domain *imx_gpc_onecell_domains[] = {
351e2ad626fSUlf Hansson 	&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base,
352e2ad626fSUlf Hansson 	&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base,
353e2ad626fSUlf Hansson };
354e2ad626fSUlf Hansson 
355e2ad626fSUlf Hansson static struct genpd_onecell_data imx_gpc_onecell_data = {
356e2ad626fSUlf Hansson 	.domains = imx_gpc_onecell_domains,
357e2ad626fSUlf Hansson 	.num_domains = 2,
358e2ad626fSUlf Hansson };
359e2ad626fSUlf Hansson 
imx_gpc_old_dt_init(struct device * dev,struct regmap * regmap,unsigned int num_domains)360e2ad626fSUlf Hansson static int imx_gpc_old_dt_init(struct device *dev, struct regmap *regmap,
361e2ad626fSUlf Hansson 			       unsigned int num_domains)
362e2ad626fSUlf Hansson {
363e2ad626fSUlf Hansson 	struct imx_pm_domain *domain;
364e2ad626fSUlf Hansson 	int i, ret;
365e2ad626fSUlf Hansson 
366e2ad626fSUlf Hansson 	for (i = 0; i < num_domains; i++) {
367e2ad626fSUlf Hansson 		domain = &imx_gpc_domains[i];
368e2ad626fSUlf Hansson 		domain->regmap = regmap;
369e2ad626fSUlf Hansson 		domain->ipg_rate_mhz = 66;
370e2ad626fSUlf Hansson 
371e2ad626fSUlf Hansson 		if (i == 1) {
372e2ad626fSUlf Hansson 			domain->supply = devm_regulator_get(dev, "pu");
373e2ad626fSUlf Hansson 			if (IS_ERR(domain->supply))
374e2ad626fSUlf Hansson 				return PTR_ERR(domain->supply);
375e2ad626fSUlf Hansson 
376e2ad626fSUlf Hansson 			ret = imx_pgc_get_clocks(dev, domain);
377e2ad626fSUlf Hansson 			if (ret)
378e2ad626fSUlf Hansson 				goto clk_err;
379e2ad626fSUlf Hansson 
380e2ad626fSUlf Hansson 			domain->base.power_on(&domain->base);
381e2ad626fSUlf Hansson 		}
382e2ad626fSUlf Hansson 	}
383e2ad626fSUlf Hansson 
384e2ad626fSUlf Hansson 	for (i = 0; i < num_domains; i++)
385e2ad626fSUlf Hansson 		pm_genpd_init(&imx_gpc_domains[i].base, NULL, false);
386e2ad626fSUlf Hansson 
387e2ad626fSUlf Hansson 	if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) {
388e2ad626fSUlf Hansson 		ret = of_genpd_add_provider_onecell(dev->of_node,
389e2ad626fSUlf Hansson 						    &imx_gpc_onecell_data);
390e2ad626fSUlf Hansson 		if (ret)
391e2ad626fSUlf Hansson 			goto genpd_err;
392e2ad626fSUlf Hansson 	}
393e2ad626fSUlf Hansson 
394e2ad626fSUlf Hansson 	return 0;
395e2ad626fSUlf Hansson 
396e2ad626fSUlf Hansson genpd_err:
397e2ad626fSUlf Hansson 	for (i = 0; i < num_domains; i++)
398e2ad626fSUlf Hansson 		pm_genpd_remove(&imx_gpc_domains[i].base);
399e2ad626fSUlf Hansson 	imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]);
400e2ad626fSUlf Hansson clk_err:
401e2ad626fSUlf Hansson 	return ret;
402e2ad626fSUlf Hansson }
403e2ad626fSUlf Hansson 
imx_gpc_probe(struct platform_device * pdev)404e2ad626fSUlf Hansson static int imx_gpc_probe(struct platform_device *pdev)
405e2ad626fSUlf Hansson {
406e2ad626fSUlf Hansson 	const struct of_device_id *of_id =
407e2ad626fSUlf Hansson 			of_match_device(imx_gpc_dt_ids, &pdev->dev);
408e2ad626fSUlf Hansson 	const struct imx_gpc_dt_data *of_id_data = of_id->data;
409e2ad626fSUlf Hansson 	struct device_node *pgc_node;
410e2ad626fSUlf Hansson 	struct regmap *regmap;
411e2ad626fSUlf Hansson 	void __iomem *base;
412e2ad626fSUlf Hansson 	int ret;
413e2ad626fSUlf Hansson 
414e2ad626fSUlf Hansson 	pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc");
415e2ad626fSUlf Hansson 
416e2ad626fSUlf Hansson 	/* bail out if DT too old and doesn't provide the necessary info */
417e2ad626fSUlf Hansson 	if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") &&
418e2ad626fSUlf Hansson 	    !pgc_node)
419e2ad626fSUlf Hansson 		return 0;
420e2ad626fSUlf Hansson 
421e2ad626fSUlf Hansson 	base = devm_platform_ioremap_resource(pdev, 0);
422e2ad626fSUlf Hansson 	if (IS_ERR(base))
423e2ad626fSUlf Hansson 		return PTR_ERR(base);
424e2ad626fSUlf Hansson 
425e2ad626fSUlf Hansson 	regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
426e2ad626fSUlf Hansson 					   &imx_gpc_regmap_config);
427e2ad626fSUlf Hansson 	if (IS_ERR(regmap)) {
428e2ad626fSUlf Hansson 		ret = PTR_ERR(regmap);
429e2ad626fSUlf Hansson 		dev_err(&pdev->dev, "failed to init regmap: %d\n",
430e2ad626fSUlf Hansson 			ret);
431e2ad626fSUlf Hansson 		return ret;
432e2ad626fSUlf Hansson 	}
433e2ad626fSUlf Hansson 
434e2ad626fSUlf Hansson 	/*
435e2ad626fSUlf Hansson 	 * Disable PU power down by runtime PM if ERR009619 is present.
436e2ad626fSUlf Hansson 	 *
437e2ad626fSUlf Hansson 	 * The PRE clock will be paused for several cycles when turning on the
438e2ad626fSUlf Hansson 	 * PU domain LDO from power down state. If PRE is in use at that time,
439e2ad626fSUlf Hansson 	 * the IPU/PRG cannot get the correct display data from the PRE.
440e2ad626fSUlf Hansson 	 *
441e2ad626fSUlf Hansson 	 * This is not a concern when the whole system enters suspend state, so
442e2ad626fSUlf Hansson 	 * it's safe to power down PU in this case.
443e2ad626fSUlf Hansson 	 */
444e2ad626fSUlf Hansson 	if (of_id_data->err009619_present)
445e2ad626fSUlf Hansson 		imx_gpc_domains[GPC_PGC_DOMAIN_PU].base.flags |=
446e2ad626fSUlf Hansson 				GENPD_FLAG_RPM_ALWAYS_ON;
447e2ad626fSUlf Hansson 
448e2ad626fSUlf Hansson 	/* Keep DISP always on if ERR006287 is present */
449e2ad626fSUlf Hansson 	if (of_id_data->err006287_present)
450e2ad626fSUlf Hansson 		imx_gpc_domains[GPC_PGC_DOMAIN_DISPLAY].base.flags |=
451e2ad626fSUlf Hansson 				GENPD_FLAG_ALWAYS_ON;
452e2ad626fSUlf Hansson 
453e2ad626fSUlf Hansson 	if (!pgc_node) {
454e2ad626fSUlf Hansson 		ret = imx_gpc_old_dt_init(&pdev->dev, regmap,
455e2ad626fSUlf Hansson 					  of_id_data->num_domains);
456e2ad626fSUlf Hansson 		if (ret)
457e2ad626fSUlf Hansson 			return ret;
458e2ad626fSUlf Hansson 	} else {
459e2ad626fSUlf Hansson 		struct imx_pm_domain *domain;
460e2ad626fSUlf Hansson 		struct platform_device *pd_pdev;
461e2ad626fSUlf Hansson 		struct device_node *np;
462e2ad626fSUlf Hansson 		struct clk *ipg_clk;
463e2ad626fSUlf Hansson 		unsigned int ipg_rate_mhz;
464e2ad626fSUlf Hansson 		int domain_index;
465e2ad626fSUlf Hansson 
466e2ad626fSUlf Hansson 		ipg_clk = devm_clk_get(&pdev->dev, "ipg");
467e2ad626fSUlf Hansson 		if (IS_ERR(ipg_clk))
468e2ad626fSUlf Hansson 			return PTR_ERR(ipg_clk);
469e2ad626fSUlf Hansson 		ipg_rate_mhz = clk_get_rate(ipg_clk) / 1000000;
470e2ad626fSUlf Hansson 
471e2ad626fSUlf Hansson 		for_each_child_of_node(pgc_node, np) {
472e2ad626fSUlf Hansson 			ret = of_property_read_u32(np, "reg", &domain_index);
473e2ad626fSUlf Hansson 			if (ret) {
474e2ad626fSUlf Hansson 				of_node_put(np);
475e2ad626fSUlf Hansson 				return ret;
476e2ad626fSUlf Hansson 			}
477e2ad626fSUlf Hansson 			if (domain_index >= of_id_data->num_domains)
478e2ad626fSUlf Hansson 				continue;
479e2ad626fSUlf Hansson 
480e2ad626fSUlf Hansson 			pd_pdev = platform_device_alloc("imx-pgc-power-domain",
481e2ad626fSUlf Hansson 							domain_index);
482e2ad626fSUlf Hansson 			if (!pd_pdev) {
483e2ad626fSUlf Hansson 				of_node_put(np);
484e2ad626fSUlf Hansson 				return -ENOMEM;
485e2ad626fSUlf Hansson 			}
486e2ad626fSUlf Hansson 
487e2ad626fSUlf Hansson 			ret = platform_device_add_data(pd_pdev,
488e2ad626fSUlf Hansson 						       &imx_gpc_domains[domain_index],
489e2ad626fSUlf Hansson 						       sizeof(imx_gpc_domains[domain_index]));
490e2ad626fSUlf Hansson 			if (ret) {
491e2ad626fSUlf Hansson 				platform_device_put(pd_pdev);
492e2ad626fSUlf Hansson 				of_node_put(np);
493e2ad626fSUlf Hansson 				return ret;
494e2ad626fSUlf Hansson 			}
495e2ad626fSUlf Hansson 			domain = pd_pdev->dev.platform_data;
496e2ad626fSUlf Hansson 			domain->regmap = regmap;
497e2ad626fSUlf Hansson 			domain->ipg_rate_mhz = ipg_rate_mhz;
498e2ad626fSUlf Hansson 
499e2ad626fSUlf Hansson 			pd_pdev->dev.parent = &pdev->dev;
500e2ad626fSUlf Hansson 			pd_pdev->dev.of_node = np;
501*2e9ae219SPengfei Li 			pd_pdev->dev.fwnode = of_fwnode_handle(np);
502e2ad626fSUlf Hansson 
503e2ad626fSUlf Hansson 			ret = platform_device_add(pd_pdev);
504e2ad626fSUlf Hansson 			if (ret) {
505e2ad626fSUlf Hansson 				platform_device_put(pd_pdev);
506e2ad626fSUlf Hansson 				of_node_put(np);
507e2ad626fSUlf Hansson 				return ret;
508e2ad626fSUlf Hansson 			}
509e2ad626fSUlf Hansson 		}
510e2ad626fSUlf Hansson 	}
511e2ad626fSUlf Hansson 
512e2ad626fSUlf Hansson 	return 0;
513e2ad626fSUlf Hansson }
514e2ad626fSUlf Hansson 
imx_gpc_remove(struct platform_device * pdev)515e2ad626fSUlf Hansson static int imx_gpc_remove(struct platform_device *pdev)
516e2ad626fSUlf Hansson {
517e2ad626fSUlf Hansson 	struct device_node *pgc_node;
518e2ad626fSUlf Hansson 	int ret;
519e2ad626fSUlf Hansson 
520e2ad626fSUlf Hansson 	pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc");
521e2ad626fSUlf Hansson 
522e2ad626fSUlf Hansson 	/* bail out if DT too old and doesn't provide the necessary info */
523e2ad626fSUlf Hansson 	if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") &&
524e2ad626fSUlf Hansson 	    !pgc_node)
525e2ad626fSUlf Hansson 		return 0;
526e2ad626fSUlf Hansson 
527e2ad626fSUlf Hansson 	/*
528e2ad626fSUlf Hansson 	 * If the old DT binding is used the toplevel driver needs to
529e2ad626fSUlf Hansson 	 * de-register the power domains
530e2ad626fSUlf Hansson 	 */
531e2ad626fSUlf Hansson 	if (!pgc_node) {
532e2ad626fSUlf Hansson 		of_genpd_del_provider(pdev->dev.of_node);
533e2ad626fSUlf Hansson 
534e2ad626fSUlf Hansson 		ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base);
535e2ad626fSUlf Hansson 		if (ret)
536e2ad626fSUlf Hansson 			return ret;
537e2ad626fSUlf Hansson 		imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]);
538e2ad626fSUlf Hansson 
539e2ad626fSUlf Hansson 		ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base);
540e2ad626fSUlf Hansson 		if (ret)
541e2ad626fSUlf Hansson 			return ret;
542e2ad626fSUlf Hansson 	}
543e2ad626fSUlf Hansson 
544e2ad626fSUlf Hansson 	return 0;
545e2ad626fSUlf Hansson }
546e2ad626fSUlf Hansson 
547e2ad626fSUlf Hansson static struct platform_driver imx_gpc_driver = {
548e2ad626fSUlf Hansson 	.driver = {
549e2ad626fSUlf Hansson 		.name = "imx-gpc",
550e2ad626fSUlf Hansson 		.of_match_table = imx_gpc_dt_ids,
551e2ad626fSUlf Hansson 	},
552e2ad626fSUlf Hansson 	.probe = imx_gpc_probe,
553e2ad626fSUlf Hansson 	.remove = imx_gpc_remove,
554e2ad626fSUlf Hansson };
555e2ad626fSUlf Hansson builtin_platform_driver(imx_gpc_driver)
556