diff options
-rw-r--r-- | arch/arm/mach-imx/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-imx/gpc.c | 213 |
2 files changed, 214 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index b8d3ccdfe127..178f0d001011 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig | |||
@@ -50,6 +50,7 @@ config HAVE_IMX_ANATOP | |||
50 | 50 | ||
51 | config HAVE_IMX_GPC | 51 | config HAVE_IMX_GPC |
52 | bool | 52 | bool |
53 | select PM_GENERIC_DOMAINS if PM | ||
53 | 54 | ||
54 | config HAVE_IMX_MMDC | 55 | config HAVE_IMX_MMDC |
55 | bool | 56 | bool |
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 | |||
45 | struct 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 | |||
30 | static void __iomem *gpc_base; | 52 | static void __iomem *gpc_base; |
31 | static u32 gpc_wake_irqs[IMR_NUM]; | 53 | static u32 gpc_wake_irqs[IMR_NUM]; |
32 | static u32 gpc_saved_imrs[IMR_NUM]; | 54 | static 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 | |||
198 | static 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 | |||
220 | static 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 | |||
232 | static 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 | |||
272 | static struct generic_pm_domain imx6q_arm_domain = { | ||
273 | .name = "ARM", | ||
274 | }; | ||
275 | |||
276 | static 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 | |||
286 | static struct generic_pm_domain imx6sl_display_domain = { | ||
287 | .name = "DISPLAY", | ||
288 | }; | ||
289 | |||
290 | static struct generic_pm_domain *imx_gpc_domains[] = { | ||
291 | &imx6q_arm_domain, | ||
292 | &imx6q_pu_domain.base, | ||
293 | &imx6sl_display_domain, | ||
294 | }; | ||
295 | |||
296 | static struct genpd_onecell_data imx_gpc_onecell_data = { | ||
297 | .domains = imx_gpc_domains, | ||
298 | .num_domains = ARRAY_SIZE(imx_gpc_domains), | ||
299 | }; | ||
300 | |||
301 | static 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 | |||
336 | clk_err: | ||
337 | while (i--) | ||
338 | clk_put(imx6q_pu_domain.clk[i]); | ||
339 | return -EINVAL; | ||
340 | } | ||
341 | |||
342 | #else | ||
343 | static inline int imx_gpc_genpd_init(struct device *dev, struct regulator *reg) | ||
344 | { | ||
345 | return 0; | ||
346 | } | ||
347 | #endif /* CONFIG_PM_GENERIC_DOMAINS */ | ||
348 | |||
349 | static 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 | |||
366 | static const struct of_device_id imx_gpc_dt_ids[] = { | ||
367 | { .compatible = "fsl,imx6q-gpc" }, | ||
368 | { .compatible = "fsl,imx6sl-gpc" }, | ||
369 | { } | ||
370 | }; | ||
371 | |||
372 | static 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 | |||
381 | static int __init imx_pgc_init(void) | ||
382 | { | ||
383 | return platform_driver_register(&imx_gpc_driver); | ||
384 | } | ||
385 | subsys_initcall(imx_pgc_init); | ||