Merge "msm: Add krait scm driver to configure cpu" into msm-3.4
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 702667b..7ccdd0f 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -27,6 +27,7 @@
 obj-$(CONFIG_ARCH_MSM_SCORPION) += pmu.o
 obj-$(CONFIG_ARCH_MSM_SCORPIONMP) += perf_event_msm_l2.o
 obj-$(CONFIG_ARCH_MSM_KRAIT) += msm-krait-l2-accessors.o pmu.o perf_event_msm_krait_l2.o
+obj-$(CONFIG_ARCH_MSM_KRAIT) += krait-scm.o
 obj-$(CONFIG_ARCH_MSM7X27A) += pmu.o
 
 ifndef CONFIG_MSM_SMP
diff --git a/arch/arm/mach-msm/krait-scm.c b/arch/arm/mach-msm/krait-scm.c
new file mode 100644
index 0000000..eb48d35
--- /dev/null
+++ b/arch/arm/mach-msm/krait-scm.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * 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/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/percpu.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/sysfs.h>
+#include <linux/sysdev.h>
+
+#include <mach/scm.h>
+
+#define CPU_CONFIG_CMD 5
+#define CPU_CONFIG_QUERY_CMD 6
+
+static int query_cpu_config(void)
+{
+	struct cpu_config_query_req_resp {
+		u32	id;
+		u32	arg0;
+		u32	arg1;
+		u32	arg2;
+	} request;
+	struct cpu_config_query_resp {
+		u32	ret0;
+		u32	ret1;
+		u32	ret2;
+		u32	ret3;
+	} response = {0};
+	int ret;
+
+	request.id = 1;
+	ret = scm_call(SCM_SVC_BOOT, CPU_CONFIG_QUERY_CMD, &request,
+			sizeof(request), &response, sizeof(response));
+	return ret ? : response.ret0;
+}
+
+static void set_cpu_config(int enable)
+{
+	struct cpu_config_req {
+		u32	id;
+		u32	arg0;
+		u32	arg1;
+		u32	arg2;
+	} request;
+
+	request.id = 1;
+	request.arg0 = enable;
+	scm_call(SCM_SVC_BOOT, CPU_CONFIG_CMD, &request, sizeof(request),
+			NULL, 0);
+}
+
+void enable_cpu_config(struct work_struct *work)
+{
+	set_cpu_config(1);
+}
+
+void disable_cpu_config(struct work_struct *work)
+{
+	set_cpu_config(0);
+}
+
+int cpu_config_on_each_cpu(bool enable)
+{
+	work_func_t func = enable ? enable_cpu_config : disable_cpu_config;
+	return schedule_on_each_cpu(func);
+}
+
+static ssize_t show_cpuctl(struct sysdev_class *class,
+		struct sysdev_class_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", query_cpu_config());
+}
+
+static ssize_t store_cpuctl(struct sysdev_class *class,
+		struct sysdev_class_attribute *attr, const char *buf,
+		size_t count)
+{
+	unsigned val;
+	int ret;
+
+	ret = kstrtouint(buf, 10, &val);
+	if (ret < 0)
+		return ret;
+	ret = cpu_config_on_each_cpu(val);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static SYSDEV_CLASS_ATTR(cpuctl, 0600, show_cpuctl, store_cpuctl);
+
+static int __init init_scm_cpu(void)
+{
+	return sysfs_create_file(&cpu_subsys.dev_root->kobj,
+			&attr_cpuctl.attr);
+}
+module_init(init_scm_cpu);