Add cpuidle driver interface to allow cpus to go into C-States.
Use the cpuidle DT interfacecommon across ARM architectures to provide
the C-State information to the cpuidle framework.

Signed-off-by: Lina Iyer <lina.i...@linaro.org>
---
 drivers/cpuidle/Kconfig.arm    |   7 +++
 drivers/cpuidle/Makefile       |   1 +
 drivers/cpuidle/cpuidle-qcom.c | 119 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 127 insertions(+)
 create mode 100644 drivers/cpuidle/cpuidle-qcom.c

diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
index e339c7f..26e31bd 100644
--- a/drivers/cpuidle/Kconfig.arm
+++ b/drivers/cpuidle/Kconfig.arm
@@ -63,3 +63,10 @@ config ARM_MVEBU_V7_CPUIDLE
        depends on ARCH_MVEBU
        help
          Select this to enable cpuidle on Armada 370, 38x and XP processors.
+
+config ARM_QCOM_CPUIDLE
+       bool "CPU Idle drivers for Qualcomm processors"
+       depends on QCOM_PM
+       select DT_IDLE_STATES
+       help
+         Select this to enable cpuidle for QCOM processors
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index 4d177b9..6c222d5 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_ARM_ZYNQ_CPUIDLE)                += 
cpuidle-zynq.o
 obj-$(CONFIG_ARM_U8500_CPUIDLE)         += cpuidle-ux500.o
 obj-$(CONFIG_ARM_AT91_CPUIDLE)          += cpuidle-at91.o
 obj-$(CONFIG_ARM_EXYNOS_CPUIDLE)        += cpuidle-exynos.o
+obj-$(CONFIG_ARM_QCOM_CPUIDLE)         += cpuidle-qcom.o
 
 ###############################################################################
 # MIPS drivers
diff --git a/drivers/cpuidle/cpuidle-qcom.c b/drivers/cpuidle/cpuidle-qcom.c
new file mode 100644
index 0000000..4aae672
--- /dev/null
+++ b/drivers/cpuidle/cpuidle-qcom.c
@@ -0,0 +1,119 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/cpuidle.h>
+#include <linux/cpumask.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+
+#include <soc/qcom/pm.h>
+#include "dt_idle_states.h"
+
+static enum msm_pm_sleep_mode modes[CPUIDLE_STATE_MAX];
+
+static int qcom_lpm_enter(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv, int index)
+{
+       return msm_cpu_pm_enter_sleep(modes[index], true);
+}
+
+static struct cpuidle_driver qcom_cpuidle_driver = {
+       .name   = "qcom_cpuidle",
+       .owner  = THIS_MODULE,
+};
+
+static void parse_state_translations(struct cpuidle_driver *drv)
+{
+       struct device_node *state_node, *cpu_node;
+       const char *mode_name;
+       int i, j;
+
+       struct name_map {
+               enum msm_pm_sleep_mode mode;
+               char *name;
+       };
+       static struct name_map c_states[] = {
+               { MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
+                                               "standalone-pc" },
+               { MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT, "wfi" },
+       };
+
+       cpu_node = of_cpu_device_node_get(cpumask_first(drv->cpumask));
+       if (!cpu_node)
+               return;
+
+       /**
+        * Get the state description from idle-state node entry-method
+        * First state is always WFI, per spec.
+        */
+       modes[0] = MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT;
+       for (i = 1; i < drv->state_count; i++) {
+               mode_name = NULL;
+               state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+               of_property_read_string(state_node, "entry-method",
+                                               &mode_name);
+               for (j = 0; mode_name && (j < ARRAY_SIZE(c_states)); j++) {
+                       if (!strcmp(mode_name, c_states[j].name)) {
+                               modes[i] = c_states[j].mode;
+                               break;
+                       }
+               }
+       }
+}
+
+static int qcom_cpuidle_init(void)
+{
+       struct cpuidle_driver *drv = &qcom_cpuidle_driver;
+       int ret;
+       int i;
+
+       drv->cpumask = kzalloc(cpumask_size(), GFP_KERNEL);
+       if (!drv->cpumask)
+               return -ENOMEM;
+       cpumask_copy(drv->cpumask, cpu_possible_mask);
+
+       /**
+        * Default to standard for WFI (index = 0)
+        * Probe only for other states
+        */
+       ret = dt_init_idle_driver(drv, 1);
+       if (ret < 0) {
+               pr_err("%s: failed to initialize idle states\n", __func__);
+               goto failed;
+       }
+
+       /* Parse the idle states for C-States on this cpu */
+       parse_state_translations(drv);
+
+       /* Register entry point for all low states */
+       for (i = 0; i < drv->state_count; i++)
+               drv->states[i].enter = qcom_lpm_enter;
+       drv->safe_state_index = 0;
+
+       ret = cpuidle_register(drv, NULL);
+       if (ret) {
+               pr_err("%s: failed to register cpuidle driver\n", __func__);
+               goto failed;
+       }
+
+       return 0;
+
+failed:
+       kfree(drv->cpumask);
+       return ret;
+}
+module_init(qcom_cpuidle_init);
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to