summaryrefslogtreecommitdiff
path: root/arch/powerpc/sysdev/fsl_rcpm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/sysdev/fsl_rcpm.c')
-rw-r--r--arch/powerpc/sysdev/fsl_rcpm.c150
1 files changed, 150 insertions, 0 deletions
diff --git a/arch/powerpc/sysdev/fsl_rcpm.c b/arch/powerpc/sysdev/fsl_rcpm.c
new file mode 100644
index 0000000..ecf43a2
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_rcpm.c
@@ -0,0 +1,150 @@
+/*
+ * RCPM(Run Control/Power Management) support
+ *
+ * Copyright 2012 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/suspend.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/of_platform.h>
+#include <linux/export.h>
+
+#include <asm/io.h>
+#include <asm/cacheflush.h>
+#include <asm/fsl_guts.h>
+
+struct ccsr_rcpm __iomem *rcpm1_regs;
+struct ccsr_rcpm_v2 __iomem *rcpm2_regs;
+
+static int rcpm_suspend_enter(suspend_state_t state)
+{
+ int ret = 0;
+ int result;
+
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+
+ flush_dcache_L1();
+ flush_backside_L2_cache();
+
+ setbits32(&rcpm1_regs->powmgtcsr, RCPM_POWMGTCSR_SLP);
+ /* At this point, the device is in sleep mode. */
+
+ /* Upon resume, wait for SLP bit to be clear. */
+ result = spin_event_timeout(
+ (in_be32(&rcpm1_regs->powmgtcsr) & RCPM_POWMGTCSR_SLP) == 0,
+ 10000, 10);
+ if (!result) {
+ pr_err("%s: timeout waiting for SLP bit "
+ "to be cleared\n", __func__);
+ ret = -ETIMEDOUT;
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+
+ }
+ return ret;
+}
+
+static int rcpm_v2_suspend_enter(suspend_state_t state)
+{
+ int ret = 0;
+ int result;
+
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+
+ /* clear previous LPM20 status */
+ setbits32(&rcpm2_regs->powmgtcsr, RCPM_POWMGTCSR_P_LPM20_ST);
+ /* enter LPM20 status */
+ setbits32(&rcpm2_regs->powmgtcsr, RCPM_POWMGTCSR_LPM20_RQ);
+
+ /* At this point, the device is in LPM20 status. */
+
+ /* resume ... */
+ result = spin_event_timeout(
+ (in_be32(&rcpm2_regs->powmgtcsr) & RCPM_POWMGTCSR_LPM20_ST)
+ == 0, 10000, 10);
+ if (!result) {
+ pr_err("%s: timeout waiting for LPM20 bit to be cleared\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ }
+
+ break;
+
+ default:
+ ret = -EINVAL;
+
+ }
+
+ return ret;
+
+}
+
+static int rcpm_suspend_valid(suspend_state_t state)
+{
+ if (state == PM_SUSPEND_STANDBY)
+ return 1;
+ else
+ return 0;
+}
+
+static struct platform_suspend_ops rcpm_suspend_ops = {
+ .valid = rcpm_suspend_valid,
+};
+
+static int rcpm_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+
+ if (of_device_is_compatible(np, "fsl,qoriq-rcpm-2.0")) {
+ rcpm2_regs = of_iomap(np, 0);
+ if (!rcpm2_regs)
+ return -ENOMEM;
+
+ rcpm_suspend_ops.enter = rcpm_v2_suspend_enter;
+ } else {
+ rcpm1_regs= of_iomap(np, 0);
+ if (!rcpm1_regs)
+ return -ENOMEM;
+
+ rcpm_suspend_ops.enter = rcpm_suspend_enter;
+ }
+
+ suspend_set_ops(&rcpm_suspend_ops);
+
+ dev_info(&pdev->dev, "Freescale RCPM driver\n");
+ return 0;
+}
+
+static const struct of_device_id rcpm_ids[] = {
+ { .compatible = "fsl,qoriq-rcpm-1.0", },
+ { .compatible = "fsl,qoriq-rcpm-2.0", },
+ { },
+};
+
+static struct platform_driver rcpm_driver = {
+ .driver = {
+ .name = "fsl-rcpm",
+ .owner = THIS_MODULE,
+ .of_match_table = rcpm_ids,
+ },
+ .probe = rcpm_probe,
+};
+
+static int __init rcpm_init(void)
+{
+ return platform_driver_register(&rcpm_driver);
+}
+
+device_initcall(rcpm_init);