aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/gpc.c
diff options
context:
space:
mode:
authorPhilipp Zabel <p.zabel@pengutronix.de>2015-02-23 12:40:12 -0500
committerShawn Guo <shawn.guo@linaro.org>2015-03-10 20:59:00 -0400
commit00eb60a8b4f7a4aa00fd8abd68c2dc7aec55df19 (patch)
tree40f2096629c67bf883f92432661ac49069210c62 /arch/arm/mach-imx/gpc.c
parent58e4a668be82362c32577ef08e06c8a292e3a457 (diff)
ARM: imx6: gpc: Add PU power domain for GPU/VPU
When generic pm domain support is enabled, the PGC can be used to completely gate power to the PU power domain containing GPU3D, GPU2D, and VPU cores. This code triggers the PGC powerdown sequence to disable the GPU/VPU isolation cells and gate power and then disables the PU regulator. To reenable, the reverse powerup sequence is triggered after the PU regulator is enabled again. The GPU and VPU devices in the PU power domain temporarily need to be clocked during powerup, so that the reset machinery can work. [Avoid explicit regulator enabling in probe, unless !PM] Signed-off-by: Markus Pargmann <mpa@pengutronix.de> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Diffstat (limited to 'arch/arm/mach-imx/gpc.c')
-rw-r--r--arch/arm/mach-imx/gpc.c213
1 files changed, 213 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
index 745caa18ab2c..029f59ce2712 100644
--- a/arch/arm/mach-imx/gpc.c
+++ b/arch/arm/mach-imx/gpc.c
@@ -10,15 +10,25 @@
10 * http://www.gnu.org/copyleft/gpl.html 10 * http://www.gnu.org/copyleft/gpl.html
11 */ 11 */
12 12
13#include <linux/clk.h>
14#include <linux/delay.h>
13#include <linux/io.h> 15#include <linux/io.h>
14#include <linux/irq.h> 16#include <linux/irq.h>
15#include <linux/of.h> 17#include <linux/of.h>
16#include <linux/of_address.h> 18#include <linux/of_address.h>
17#include <linux/of_irq.h> 19#include <linux/of_irq.h>
20#include <linux/platform_device.h>
21#include <linux/pm_domain.h>
22#include <linux/regulator/consumer.h>
18#include <linux/irqchip/arm-gic.h> 23#include <linux/irqchip/arm-gic.h>
19#include "common.h" 24#include "common.h"
25#include "hardware.h"
20 26
27#define GPC_CNTR 0x000
21#define GPC_IMR1 0x008 28#define GPC_IMR1 0x008
29#define GPC_PGC_GPU_PDN 0x260
30#define GPC_PGC_GPU_PUPSCR 0x264
31#define GPC_PGC_GPU_PDNSCR 0x268
22#define GPC_PGC_CPU_PDN 0x2a0 32#define GPC_PGC_CPU_PDN 0x2a0
23#define GPC_PGC_CPU_PUPSCR 0x2a4 33#define GPC_PGC_CPU_PUPSCR 0x2a4
24#define GPC_PGC_CPU_PDNSCR 0x2a8 34#define GPC_PGC_CPU_PDNSCR 0x2a8
@@ -27,6 +37,18 @@
27 37
28#define IMR_NUM 4 38#define IMR_NUM 4
29 39
40#define GPU_VPU_PUP_REQ BIT(1)
41#define GPU_VPU_PDN_REQ BIT(0)
42
43#define GPC_CLK_MAX 6
44
45struct pu_domain {
46 struct generic_pm_domain base;
47 struct regulator *reg;
48 struct clk *clk[GPC_CLK_MAX];
49 int num_clks;
50};
51
30static void __iomem *gpc_base; 52static void __iomem *gpc_base;
31static u32 gpc_wake_irqs[IMR_NUM]; 53static u32 gpc_wake_irqs[IMR_NUM];
32static u32 gpc_saved_imrs[IMR_NUM]; 54static u32 gpc_saved_imrs[IMR_NUM];
@@ -170,3 +192,194 @@ void __init imx_gpc_init(void)
170 gic_arch_extn.irq_unmask = imx_gpc_irq_unmask; 192 gic_arch_extn.irq_unmask = imx_gpc_irq_unmask;
171 gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake; 193 gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake;
172} 194}
195
196#ifdef CONFIG_PM_GENERIC_DOMAINS
197
198static void _imx6q_pm_pu_power_off(struct generic_pm_domain *genpd)
199{
200 int iso, iso2sw;
201 u32 val;
202
203 /* Read ISO and ISO2SW power down delays */
204 val = readl_relaxed(gpc_base + GPC_PGC_GPU_PDNSCR);
205 iso = val & 0x3f;
206 iso2sw = (val >> 8) & 0x3f;
207
208 /* Gate off PU domain when GPU/VPU when powered down */
209 writel_relaxed(0x1, gpc_base + GPC_PGC_GPU_PDN);
210
211 /* Request GPC to power down GPU/VPU */
212 val = readl_relaxed(gpc_base + GPC_CNTR);
213 val |= GPU_VPU_PDN_REQ;
214 writel_relaxed(val, gpc_base + GPC_CNTR);
215
216 /* Wait ISO + ISO2SW IPG clock cycles */
217 ndelay((iso + iso2sw) * 1000 / 66);
218}
219
220static int imx6q_pm_pu_power_off(struct generic_pm_domain *genpd)
221{
222 struct pu_domain *pu = container_of(genpd, struct pu_domain, base);
223
224 _imx6q_pm_pu_power_off(genpd);
225
226 if (pu->reg)
227 regulator_disable(pu->reg);
228
229 return 0;
230}
231
232static int imx6q_pm_pu_power_on(struct generic_pm_domain *genpd)
233{
234 struct pu_domain *pu = container_of(genpd, struct pu_domain, base);
235 int i, ret, sw, sw2iso;
236 u32 val;
237
238 if (pu->reg)
239 ret = regulator_enable(pu->reg);
240 if (pu->reg && ret) {
241 pr_err("%s: failed to enable regulator: %d\n", __func__, ret);
242 return ret;
243 }
244
245 /* Enable reset clocks for all devices in the PU domain */
246 for (i = 0; i < pu->num_clks; i++)
247 clk_prepare_enable(pu->clk[i]);
248
249 /* Gate off PU domain when GPU/VPU when powered down */
250 writel_relaxed(0x1, gpc_base + GPC_PGC_GPU_PDN);
251
252 /* Read ISO and ISO2SW power down delays */
253 val = readl_relaxed(gpc_base + GPC_PGC_GPU_PUPSCR);
254 sw = val & 0x3f;
255 sw2iso = (val >> 8) & 0x3f;
256
257 /* Request GPC to power up GPU/VPU */
258 val = readl_relaxed(gpc_base + GPC_CNTR);
259 val |= GPU_VPU_PUP_REQ;
260 writel_relaxed(val, gpc_base + GPC_CNTR);
261
262 /* Wait ISO + ISO2SW IPG clock cycles */
263 ndelay((sw + sw2iso) * 1000 / 66);
264
265 /* Disable reset clocks for all devices in the PU domain */
266 for (i = 0; i < pu->num_clks; i++)
267 clk_disable_unprepare(pu->clk[i]);
268
269 return 0;
270}
271
272static struct generic_pm_domain imx6q_arm_domain = {
273 .name = "ARM",
274};
275
276static struct pu_domain imx6q_pu_domain = {
277 .base = {
278 .name = "PU",
279 .power_off = imx6q_pm_pu_power_off,
280 .power_on = imx6q_pm_pu_power_on,
281 .power_off_latency_ns = 25000,
282 .power_on_latency_ns = 2000000,
283 },
284};
285
286static struct generic_pm_domain imx6sl_display_domain = {
287 .name = "DISPLAY",
288};
289
290static struct generic_pm_domain *imx_gpc_domains[] = {
291 &imx6q_arm_domain,
292 &imx6q_pu_domain.base,
293 &imx6sl_display_domain,
294};
295
296static struct genpd_onecell_data imx_gpc_onecell_data = {
297 .domains = imx_gpc_domains,
298 .num_domains = ARRAY_SIZE(imx_gpc_domains),
299};
300
301static int imx_gpc_genpd_init(struct device *dev, struct regulator *pu_reg)
302{
303 struct clk *clk;
304 bool is_off;
305 int i;
306
307 imx6q_pu_domain.reg = pu_reg;
308
309 for (i = 0; ; i++) {
310 clk = of_clk_get(dev->of_node, i);
311 if (IS_ERR(clk))
312 break;
313 if (i >= GPC_CLK_MAX) {
314 dev_err(dev, "more than %d clocks\n", GPC_CLK_MAX);
315 goto clk_err;
316 }
317 imx6q_pu_domain.clk[i] = clk;
318 }
319 imx6q_pu_domain.num_clks = i;
320
321 is_off = IS_ENABLED(CONFIG_PM);
322 if (is_off) {
323 _imx6q_pm_pu_power_off(&imx6q_pu_domain.base);
324 } else {
325 /*
326 * Enable power if compiled without CONFIG_PM in case the
327 * bootloader disabled it.
328 */
329 imx6q_pm_pu_power_on(&imx6q_pu_domain.base);
330 }
331
332 pm_genpd_init(&imx6q_pu_domain.base, NULL, is_off);
333 return of_genpd_add_provider_onecell(dev->of_node,
334 &imx_gpc_onecell_data);
335
336clk_err:
337 while (i--)
338 clk_put(imx6q_pu_domain.clk[i]);
339 return -EINVAL;
340}
341
342#else
343static inline int imx_gpc_genpd_init(struct device *dev, struct regulator *reg)
344{
345 return 0;
346}
347#endif /* CONFIG_PM_GENERIC_DOMAINS */
348
349static int imx_gpc_probe(struct platform_device *pdev)
350{
351 struct regulator *pu_reg;
352 int ret;
353
354 pu_reg = devm_regulator_get_optional(&pdev->dev, "pu");
355 if (PTR_ERR(pu_reg) == -ENODEV)
356 pu_reg = NULL;
357 if (IS_ERR(pu_reg)) {
358 ret = PTR_ERR(pu_reg);
359 dev_err(&pdev->dev, "failed to get pu regulator: %d\n", ret);
360 return ret;
361 }
362
363 return imx_gpc_genpd_init(&pdev->dev, pu_reg);
364}
365
366static const struct of_device_id imx_gpc_dt_ids[] = {
367 { .compatible = "fsl,imx6q-gpc" },
368 { .compatible = "fsl,imx6sl-gpc" },
369 { }
370};
371
372static struct platform_driver imx_gpc_driver = {
373 .driver = {
374 .name = "imx-gpc",
375 .owner = THIS_MODULE,
376 .of_match_table = imx_gpc_dt_ids,
377 },
378 .probe = imx_gpc_probe,
379};
380
381static int __init imx_pgc_init(void)
382{
383 return platform_driver_register(&imx_gpc_driver);
384}
385subsys_initcall(imx_pgc_init);