Merge "isa1200: Add support for pwm clk" into msm-3.4
diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt b/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt
new file mode 100644
index 0000000..9ff43a1
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/lpm-resources.txt
@@ -0,0 +1,31 @@
+* Low Power Management Resources
+
+The application processor in the MSM can enter several different low power
+states depending on the sleep time and on the required system resources. The
+MSM cannot enter a given low power state if that state involves turning off
+some shared resources which are required by some components of the
+system.The lpm-resources device tree node represents the shared resources
+that need to be monitored for usage requirement to check if a given low power
+state can be entered.Each resource is identified by a combination of the name,
+id,type and key which is also used by the RPM to identify a shared resource.
+
+The required nodes for lpm-resources are:
+
+- compatible: "qcom,lpm-resources"
+- reg: The numeric level id
+- qcom,name: The name of the low power resource.
+- qcom,type: The string represeting the type of resource used
+ like smps or pxo.
+- qcom,id: The id representing a device within a resource type.
+- qcom,key: The key is the specific attribute of the resource being
+ monitored.
+
+Example:
+ qcom,lpm-resources@0 {
+ reg = <0x0>;
+ qcom,name = "vdd-dig";
+ qcom,type = "smpb\0";
+ qcom,id = <0x02>;
+ qcom,key = "uv\0\0";
+ };
+
diff --git a/arch/arm/boot/dts/msmcopper_pm.dtsi b/arch/arm/boot/dts/msmcopper_pm.dtsi
index 79cb95c..6f12e31c 100644
--- a/arch/arm/boot/dts/msmcopper_pm.dtsi
+++ b/arch/arm/boot/dts/msmcopper_pm.dtsi
@@ -132,6 +132,36 @@
3b 60 02 32 a0 50 0f];
};
+ qcom,lpm-resources {
+ compatible = "qcom,lpm-resources";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,lpm-resources@0 {
+ reg = <0x0>;
+ qcom,name = "vdd-dig";
+ qcom,type = "smpb\0";
+ qcom,id = <0x02>;
+ qcom,key = "uv\0\0";
+ };
+
+ qcom,lpm-resources@1 {
+ reg = <0x1>;
+ qcom,name = "vdd-mem";
+ qcom,type = "smpb\0";
+ qcom,id = <0x01>;
+ qcom,key = "uv\0\0";
+ };
+
+ qcom,lpm-resources@2 {
+ reg = <0x2>;
+ qcom,name = "pxo";
+ qcom,type = "clk0\0";
+ qcom,id = <0x00>;
+ qcom,key = "Enab";
+ };
+ };
+
qcom,lpm-levels {
compatible = "qcom,lpm-levels";
#address-cells = <1>;
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index c3b13ec..8315d70 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -317,7 +317,7 @@
obj-$(CONFIG_ARCH_MSM9615) += rpm_resources.o
endif
ifdef CONFIG_MSM_RPM_SMD
- obj-$(CONFIG_ARCH_MSMCOPPER) += lpm_levels.o
+ obj-$(CONFIG_ARCH_MSMCOPPER) += lpm_levels.o lpm_resources.o
endif
obj-$(CONFIG_MSM_MPM) += mpm.o
obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o
diff --git a/arch/arm/mach-msm/board-copper.c b/arch/arm/mach-msm/board-copper.c
index 94a73c6..85241a4 100644
--- a/arch/arm/mach-msm/board-copper.c
+++ b/arch/arm/mach-msm/board-copper.c
@@ -47,6 +47,7 @@
#include "devices.h"
#include "spm.h"
#include "modem_notifier.h"
+#include "lpm_resources.h"
#define MSM_KERNEL_EBI1_MEM_SIZE 0x280000
#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
@@ -467,6 +468,7 @@
msm_init_modem_notifier_list();
msm_smd_init();
msm_rpm_driver_init();
+ msm_lpmrs_module_init();
rpm_regulator_smd_driver_init();
msm_spm_device_init();
regulator_stub_init();
diff --git a/arch/arm/mach-msm/board-msm7627a-io.c b/arch/arm/mach-msm/board-msm7627a-io.c
index ec168f9..22095cd 100644
--- a/arch/arm/mach-msm/board-msm7627a-io.c
+++ b/arch/arm/mach-msm/board-msm7627a-io.c
@@ -229,7 +229,7 @@
static int mxt_vkey_setup(void)
{
- int retval;
+ int retval = 0;
mxt_virtual_key_properties_kobj =
kobject_create_and_add("board_properties", NULL);
diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
index a1f5ff5..e65f71c 100644
--- a/arch/arm/mach-msm/lpm_levels.c
+++ b/arch/arm/mach-msm/lpm_levels.c
@@ -18,17 +18,32 @@
#include <linux/platform_device.h>
#include <linux/of.h>
#include <mach/mpm.h>
-#include "rpm_resources.h"
+#include "lpm_resources.h"
#include "pm.h"
static struct msm_rpmrs_level *msm_lpm_levels;
static int msm_lpm_level_count;
-static int msm_lpm_enter_sleep(uint32_t sclk_count, void *limits,
+static void msm_lpm_level_update(void)
+{
+ unsigned int lpm_level;
+ struct msm_rpmrs_level *level = NULL;
+
+ for (lpm_level = 0; lpm_level < msm_lpm_level_count; lpm_level++) {
+ level = &msm_lpm_levels[lpm_level];
+ level->available =
+ !msm_lpm_level_beyond_limit(&level->rs_limits);
+ }
+}
+
+int msm_lpm_enter_sleep(uint32_t sclk_count, void *limits,
bool from_idle, bool notify_rpm)
{
- /* TODO */
- return 0;
+ int ret = 0;
+
+ ret = msm_lpmrs_enter_sleep((struct msm_rpmrs_limits *)limits,
+ from_idle, notify_rpm);
+ return ret;
}
static void msm_lpm_exit_sleep(void *limits, bool from_idle,
@@ -38,14 +53,7 @@
return;
}
-static bool msm_rpmrs_irqs_detectable(struct msm_rpmrs_limits *limits,
- bool irqs_detect, bool gpio_detect)
-{
- /* TODO */
- return true;
-}
-
-void msm_rpmrs_show_resources(void)
+void msm_lpm_show_resources(void)
{
/* TODO */
return;
@@ -80,18 +88,13 @@
{
unsigned int cpu = smp_processor_id();
struct msm_rpmrs_level *best_level = NULL;
- bool irqs_detectable = false;
- bool gpio_detectable = false;
uint32_t pwr;
int i;
if (!msm_lpm_levels)
return NULL;
- if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
- irqs_detectable = msm_mpm_irqs_detectable(from_idle);
- gpio_detectable = msm_mpm_gpio_irqs_detectable(from_idle);
- }
+ msm_lpm_level_update();
for (i = 0; i < msm_lpm_level_count; i++) {
struct msm_rpmrs_level *level = &msm_lpm_levels[i];
@@ -105,10 +108,6 @@
if (latency_us < level->latency_us)
continue;
- if (!msm_rpmrs_irqs_detectable(&level->rs_limits,
- irqs_detectable, gpio_detectable))
- continue;
-
if (sleep_us <= 1) {
pwr = level->energy_overhead;
} else if (sleep_us <= level->time_overhead_us) {
@@ -192,7 +191,7 @@
ret = of_property_read_u32(node, key, &val);
if (ret)
goto fail;
- level->rs_limits.vdd_dig = val;
+ level->rs_limits.vdd_dig_lower_bound = val;
key = "qcom,vdd-mem-upper-bound";
ret = of_property_read_u32(node, key, &val);
@@ -204,7 +203,7 @@
ret = of_property_read_u32(node, key, &val);
if (ret)
goto fail;
- level->rs_limits.vdd_mem = val;
+ level->rs_limits.vdd_mem_lower_bound = val;
key = "qcom,latency-us";
ret = of_property_read_u32(node, key, &val);
diff --git a/arch/arm/mach-msm/lpm_resources.c b/arch/arm/mach-msm/lpm_resources.c
new file mode 100644
index 0000000..f57f974
--- /dev/null
+++ b/arch/arm/mach-msm/lpm_resources.c
@@ -0,0 +1,865 @@
+/* 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/cpu.h>
+#include <mach/mpm.h>
+#include <linux/notifier.h>
+#include <linux/hrtimer.h>
+#include <linux/tick.h>
+#include "spm.h"
+#include "lpm_resources.h"
+#include "rpm-notifier.h"
+#include <mach/rpm-smd.h>
+#include "idle.h"
+
+/*Debug Definitions*/
+enum {
+ MSM_LPMRS_DEBUG_RPM = BIT(0),
+ MSM_LPMRS_DEBUG_PXO = BIT(1),
+ MSM_LPMRS_DEBUG_VDD_DIG = BIT(2),
+ MSM_LPMRS_DEBUG_VDD_MEM = BIT(3),
+ MSM_LPMRS_DEBUG_L2 = BIT(4),
+ MSM_LPMRS_DEBUG_LVLS = BIT(5),
+};
+
+static int msm_lpm_debug_mask;
+module_param_named(
+ debug_mask, msm_lpm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
+);
+
+static bool msm_lpm_get_rpm_notif = true;
+
+/*Macros*/
+#define VDD_DIG_ACTIVE (950000)
+#define VDD_MEM_ACTIVE (1050000)
+#define MAX_RS_NAME (16)
+#define MAX_RS_SIZE (4)
+#define IS_RPM_CTL(rs) \
+ (!strncmp(rs->name, "rpm_ctl", MAX_RS_NAME))
+
+static bool msm_lpm_beyond_limits_vdd_dig(struct msm_rpmrs_limits *limits);
+static void msm_lpm_aggregate_vdd_dig(struct msm_rpmrs_limits *limits);
+static void msm_lpm_flush_vdd_dig(int notify_rpm);
+static void msm_lpm_notify_vdd_dig(struct msm_rpm_notifier_data
+ *rpm_notifier_cb);
+
+static bool msm_lpm_beyond_limits_vdd_mem(struct msm_rpmrs_limits *limits);
+static void msm_lpm_aggregate_vdd_mem(struct msm_rpmrs_limits *limits);
+static void msm_lpm_flush_vdd_mem(int notify_rpm);
+static void msm_lpm_notify_vdd_mem(struct msm_rpm_notifier_data
+ *rpm_notifier_cb);
+
+static bool msm_lpm_beyond_limits_pxo(struct msm_rpmrs_limits *limits);
+static void msm_lpm_aggregate_pxo(struct msm_rpmrs_limits *limits);
+static void msm_lpm_flush_pxo(int notify_rpm);
+static void msm_lpm_notify_pxo(struct msm_rpm_notifier_data
+ *rpm_notifier_cb);
+
+
+static bool msm_lpm_beyond_limits_l2(struct msm_rpmrs_limits *limits);
+static void msm_lpm_flush_l2(int notify_rpm);
+static void msm_lpm_aggregate_l2(struct msm_rpmrs_limits *limits);
+
+static void msm_lpm_flush_rpm_ctl(int notify_rpm);
+
+static int msm_lpm_rpm_callback(struct notifier_block *rpm_nb,
+ unsigned long action, void *rpm_notif);
+
+static int msm_lpm_cpu_callback(struct notifier_block *cpu_nb,
+ unsigned long action, void *hcpu);
+
+static ssize_t msm_lpm_resource_attr_show(
+ struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+static ssize_t msm_lpm_resource_attr_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count);
+
+
+#define RPMRS_ATTR(_name) \
+ __ATTR(_name, S_IRUGO|S_IWUSR, \
+ msm_lpm_resource_attr_show, msm_lpm_resource_attr_store)
+
+/*Data structures*/
+struct msm_lpm_rs_data {
+ uint32_t type;
+ uint32_t id;
+ uint32_t key;
+ uint32_t value;
+ uint32_t default_value;
+ struct msm_rpm_request *handle;
+};
+
+struct msm_lpm_resource {
+ struct msm_lpm_rs_data rs_data;
+ uint32_t sleep_value;
+ char name[MAX_RS_NAME];
+
+ uint32_t enable_low_power;
+ bool valid;
+
+ bool (*beyond_limits)(struct msm_rpmrs_limits *limits);
+ void (*aggregate)(struct msm_rpmrs_limits *limits);
+ void (*flush)(int notify_rpm);
+ void (*notify)(struct msm_rpm_notifier_data *rpm_notifier_cb);
+ struct kobj_attribute ko_attr;
+};
+
+
+static struct msm_lpm_resource msm_lpm_l2 = {
+ .name = "l2",
+ .beyond_limits = msm_lpm_beyond_limits_l2,
+ .aggregate = msm_lpm_aggregate_l2,
+ .flush = msm_lpm_flush_l2,
+ .notify = NULL,
+ .valid = true,
+ .rs_data = {
+ .value = MSM_LPM_L2_CACHE_ACTIVE,
+ .default_value = MSM_LPM_L2_CACHE_ACTIVE,
+ },
+ .ko_attr = RPMRS_ATTR(l2),
+};
+
+static struct msm_lpm_resource msm_lpm_vdd_dig = {
+ .name = "vdd-dig",
+ .beyond_limits = msm_lpm_beyond_limits_vdd_dig,
+ .aggregate = msm_lpm_aggregate_vdd_dig,
+ .flush = msm_lpm_flush_vdd_dig,
+ .notify = msm_lpm_notify_vdd_dig,
+ .valid = false,
+ .rs_data = {
+ .value = VDD_DIG_ACTIVE,
+ .default_value = VDD_DIG_ACTIVE,
+ },
+ .ko_attr = RPMRS_ATTR(vdd_dig),
+};
+
+static struct msm_lpm_resource msm_lpm_vdd_mem = {
+ .name = "vdd-mem",
+ .beyond_limits = msm_lpm_beyond_limits_vdd_mem,
+ .aggregate = msm_lpm_aggregate_vdd_mem,
+ .flush = msm_lpm_flush_vdd_mem,
+ .notify = msm_lpm_notify_vdd_mem,
+ .valid = false,
+ .rs_data = {
+ .value = VDD_MEM_ACTIVE,
+ .default_value = VDD_MEM_ACTIVE,
+ },
+ .ko_attr = RPMRS_ATTR(vdd_mem),
+};
+
+static struct msm_lpm_resource msm_lpm_pxo = {
+ .name = "pxo",
+ .beyond_limits = msm_lpm_beyond_limits_pxo,
+ .aggregate = msm_lpm_aggregate_pxo,
+ .flush = msm_lpm_flush_pxo,
+ .notify = msm_lpm_notify_pxo,
+ .valid = false,
+ .rs_data = {
+ .value = MSM_LPM_PXO_ON,
+ .default_value = MSM_LPM_PXO_ON,
+ },
+ .ko_attr = RPMRS_ATTR(pxo),
+};
+
+static struct msm_lpm_resource *msm_lpm_resources[] = {
+ &msm_lpm_vdd_dig,
+ &msm_lpm_vdd_mem,
+ &msm_lpm_pxo,
+ &msm_lpm_l2,
+};
+
+static struct msm_lpm_resource msm_lpm_rpm_ctl = {
+ .name = "rpm_ctl",
+ .beyond_limits = NULL,
+ .aggregate = NULL,
+ .flush = msm_lpm_flush_rpm_ctl,
+ .valid = true,
+ .ko_attr = RPMRS_ATTR(rpm_ctl),
+};
+
+static struct notifier_block msm_lpm_rpm_nblk = {
+ .notifier_call = msm_lpm_rpm_callback,
+};
+
+static struct notifier_block __refdata msm_lpm_cpu_nblk = {
+ .notifier_call = msm_lpm_cpu_callback,
+};
+
+static DEFINE_SPINLOCK(msm_lpm_sysfs_lock);
+
+/* Attribute Definitions */
+static struct attribute *msm_lpm_attributes[] = {
+ &msm_lpm_vdd_dig.ko_attr.attr,
+ &msm_lpm_vdd_mem.ko_attr.attr,
+ &msm_lpm_pxo.ko_attr.attr,
+ &msm_lpm_l2.ko_attr.attr,
+ NULL,
+};
+
+static struct attribute_group msm_lpm_attribute_group = {
+ .attrs = msm_lpm_attributes,
+};
+
+static struct attribute *msm_lpm_rpm_ctl_attribute[] = {
+ &msm_lpm_rpm_ctl.ko_attr.attr,
+ NULL,
+};
+
+static struct attribute_group msm_lpm_rpm_ctl_attr_group = {
+ .attrs = msm_lpm_rpm_ctl_attribute,
+};
+
+#define GET_RS_FROM_ATTR(attr) \
+ (container_of(attr, struct msm_lpm_resource, ko_attr))
+
+/* RPM */
+static struct msm_rpm_request *msm_lpm_create_rpm_request
+ (uint32_t rsc_type, uint32_t rsc_id)
+{
+ struct msm_rpm_request *handle = NULL;
+
+ handle = msm_rpm_create_request(MSM_RPM_CTX_SLEEP_SET,
+ rsc_type,
+ rsc_id, 1);
+ return handle;
+}
+
+static int msm_lpm_send_sleep_data(struct msm_rpm_request *handle,
+ uint32_t key, uint8_t *value)
+{
+ int ret = 0;
+
+ if (!handle)
+ return ret;
+
+ ret = msm_rpm_add_kvp_data_noirq(handle, key, value, MAX_RS_SIZE);
+
+ if (ret < 0) {
+ pr_err("%s: Error adding kvp data key %u, size %d\n",
+ __func__, key, MAX_RS_SIZE);
+ return ret;
+ }
+
+ ret = msm_rpm_send_request_noirq(handle);
+ if (ret < 0) {
+ pr_err("%s: Error sending RPM request key %u, handle 0x%x\n",
+ __func__, key, (unsigned int)handle);
+ return ret;
+ }
+ if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_RPM)
+ pr_info("Rs key %u, value %u, size %d\n", key,
+ *(unsigned int *)value, MAX_RS_SIZE);
+ return ret;
+}
+
+/* RPM Notifier */
+static int msm_lpm_rpm_callback(struct notifier_block *rpm_nb,
+ unsigned long action,
+ void *rpm_notif)
+{
+ int i;
+ struct msm_lpm_resource *rs = NULL;
+ struct msm_rpm_notifier_data *rpm_notifier_cb =
+ (struct msm_rpm_notifier_data *)rpm_notif;
+
+ if (!msm_lpm_get_rpm_notif)
+ return NOTIFY_DONE;
+
+ if (!(rpm_nb && rpm_notif))
+ return NOTIFY_BAD;
+
+ for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
+ rs = msm_lpm_resources[i];
+ if (rs && rs->valid && rs->notify)
+ rs->notify(rpm_notifier_cb);
+ }
+
+ return NOTIFY_OK;
+}
+
+/* SYSFS */
+static ssize_t msm_lpm_resource_attr_show(
+ struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+ struct kernel_param kp;
+ unsigned long flags;
+ unsigned int temp;
+ int rc;
+
+ spin_lock_irqsave(&msm_lpm_sysfs_lock, flags);
+ temp = GET_RS_FROM_ATTR(attr)->enable_low_power;
+ spin_unlock_irqrestore(&msm_lpm_sysfs_lock, flags);
+
+ kp.arg = &temp;
+ rc = param_get_uint(buf, &kp);
+
+ if (rc > 0) {
+ strlcat(buf, "\n", PAGE_SIZE);
+ rc++;
+ }
+
+ return rc;
+}
+
+static ssize_t msm_lpm_resource_attr_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ struct kernel_param kp;
+ unsigned long flags;
+ unsigned int temp;
+ int rc;
+
+ kp.arg = &temp;
+ rc = param_set_uint(buf, &kp);
+ if (rc)
+ return rc;
+
+ spin_lock_irqsave(&msm_lpm_sysfs_lock, flags);
+ GET_RS_FROM_ATTR(attr)->enable_low_power = temp;
+
+ if (IS_RPM_CTL(GET_RS_FROM_ATTR(attr))) {
+ struct msm_lpm_resource *rs = GET_RS_FROM_ATTR(attr);
+ rs->flush(false);
+ }
+
+ spin_unlock_irqrestore(&msm_lpm_sysfs_lock, flags);
+
+ return count;
+}
+
+/* lpm resource handling functions */
+/* Common */
+static void msm_lpm_notify_common(struct msm_rpm_notifier_data *rpm_notifier_cb,
+ struct msm_lpm_resource *rs)
+{
+ if ((rpm_notifier_cb->rsc_type == rs->rs_data.type) &&
+ (rpm_notifier_cb->rsc_id == rs->rs_data.id) &&
+ (rpm_notifier_cb->key == rs->rs_data.key)) {
+ BUG_ON(rpm_notifier_cb->size > MAX_RS_SIZE);
+
+ if (rs->valid) {
+ if (rpm_notifier_cb->value)
+ memcpy(&rs->rs_data.value,
+ rpm_notifier_cb->value, rpm_notifier_cb->size);
+ else
+ rs->rs_data.value = rs->rs_data.default_value;
+
+ if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_RPM)
+ pr_info("Notification received Rs %s value %u\n",
+ rs->name, rs->rs_data.value);
+ }
+ }
+}
+
+/* L2 */
+static bool msm_lpm_beyond_limits_l2(struct msm_rpmrs_limits *limits)
+{
+ uint32_t l2;
+ bool ret = true;
+ struct msm_lpm_resource *rs = &msm_lpm_l2;
+
+ if (rs->valid) {
+ uint32_t l2_buf = rs->rs_data.value;
+
+ if (rs->enable_low_power == 1)
+ l2 = MSM_LPM_L2_CACHE_GDHS;
+ else if (rs->enable_low_power == 2)
+ l2 = MSM_LPM_L2_CACHE_HSFS_OPEN;
+ else
+ l2 = MSM_LPM_L2_CACHE_ACTIVE ;
+
+ if (l2_buf > l2)
+ l2 = l2_buf;
+ ret = (l2 > limits->l2_cache);
+
+ if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_L2)
+ pr_info("%s: l2 buf %u, l2 %u, limits %u\n",
+ __func__, l2_buf, l2, limits->l2_cache);
+ }
+ return ret;
+}
+
+static void msm_lpm_aggregate_l2(struct msm_rpmrs_limits *limits)
+{
+ struct msm_lpm_resource *rs = &msm_lpm_l2;
+
+ if (rs->valid)
+ rs->sleep_value = limits->l2_cache;
+}
+
+static void msm_lpm_flush_l2(int notify_rpm)
+{
+ struct msm_lpm_resource *rs = &msm_lpm_l2;
+ int lpm;
+ int rc;
+
+ switch (rs->sleep_value) {
+ case MSM_LPM_L2_CACHE_HSFS_OPEN:
+ lpm = MSM_SPM_L2_MODE_POWER_COLLAPSE;
+ msm_pm_set_l2_flush_flag(1);
+ break;
+ case MSM_LPM_L2_CACHE_GDHS:
+ lpm = MSM_SPM_L2_MODE_GDHS;
+ break;
+ case MSM_LPM_L2_CACHE_RETENTION:
+ lpm = MSM_SPM_L2_MODE_RETENTION;
+ break;
+ default:
+ case MSM_LPM_L2_CACHE_ACTIVE:
+ lpm = MSM_SPM_L2_MODE_DISABLED;
+ break;
+ }
+
+ rc = msm_spm_l2_set_low_power_mode(lpm, notify_rpm);
+
+ if (rc < 0)
+ pr_err("%s: Failed to set L2 low power mode %d",
+ __func__, lpm);
+
+ if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_L2)
+ pr_info("%s: Requesting low power mode %d\n",
+ __func__, lpm);
+}
+
+/* RPM CTL */
+static void msm_lpm_flush_rpm_ctl(int notify_rpm)
+{
+ struct msm_lpm_resource *rs = &msm_lpm_rpm_ctl;
+ msm_lpm_send_sleep_data(rs->rs_data.handle,
+ rs->rs_data.key,
+ (uint8_t *)&rs->sleep_value);
+}
+
+/*VDD Dig*/
+static bool msm_lpm_beyond_limits_vdd_dig(struct msm_rpmrs_limits *limits)
+{
+ bool ret = true;
+ struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
+
+ if (rs->valid) {
+ uint32_t vdd_buf = rs->rs_data.value;
+ uint32_t vdd_dig = rs->enable_low_power ? rs->enable_low_power :
+ rs->rs_data.default_value;
+
+ if (vdd_buf > vdd_dig)
+ vdd_dig = vdd_buf;
+
+ ret = (vdd_dig > limits->vdd_dig_upper_bound);
+
+ if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_VDD_DIG)
+ pr_info("%s:buf %d vdd dig %d limits%d\n",
+ __func__, vdd_buf, vdd_dig,
+ limits->vdd_dig_upper_bound);
+ }
+ return ret;
+}
+
+static void msm_lpm_aggregate_vdd_dig(struct msm_rpmrs_limits *limits)
+{
+ struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
+
+ if (rs->valid) {
+ uint32_t vdd_buf = rs->rs_data.value;
+ if (limits->vdd_dig_lower_bound > vdd_buf)
+ rs->sleep_value = limits->vdd_dig_lower_bound;
+ else
+ rs->sleep_value = vdd_buf;
+ }
+}
+
+static void msm_lpm_flush_vdd_dig(int notify_rpm)
+{
+ if (notify_rpm) {
+ struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
+ msm_lpm_send_sleep_data(rs->rs_data.handle,
+ rs->rs_data.key,
+ (uint8_t *)&rs->sleep_value);
+ }
+}
+
+static void msm_lpm_notify_vdd_dig(struct msm_rpm_notifier_data
+ *rpm_notifier_cb)
+{
+ struct msm_lpm_resource *rs = &msm_lpm_vdd_dig;
+ msm_lpm_notify_common(rpm_notifier_cb, rs);
+}
+
+/*VDD Mem*/
+static bool msm_lpm_beyond_limits_vdd_mem(struct msm_rpmrs_limits *limits)
+{
+ bool ret = true;
+ struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
+
+ if (rs->valid) {
+ uint32_t vdd_buf = rs->rs_data.value;
+ uint32_t vdd_mem = rs->enable_low_power ? rs->enable_low_power :
+ rs->rs_data.default_value;
+
+ if (vdd_buf > vdd_mem)
+ vdd_mem = vdd_buf;
+
+ ret = (vdd_mem > limits->vdd_mem_upper_bound);
+
+ if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_VDD_MEM)
+ pr_info("%s:buf %d vdd mem %d limits%d\n",
+ __func__, vdd_buf, vdd_mem,
+ limits->vdd_mem_upper_bound);
+ }
+ return ret;
+}
+
+static void msm_lpm_aggregate_vdd_mem(struct msm_rpmrs_limits *limits)
+{
+ struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
+
+ if (rs->valid) {
+ uint32_t vdd_buf = rs->rs_data.value;
+ if (limits->vdd_mem_lower_bound > vdd_buf)
+ rs->sleep_value = limits->vdd_mem_lower_bound;
+ else
+ rs->sleep_value = vdd_buf;
+ }
+}
+
+static void msm_lpm_flush_vdd_mem(int notify_rpm)
+{
+ if (notify_rpm) {
+ struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
+ msm_lpm_send_sleep_data(rs->rs_data.handle,
+ rs->rs_data.key,
+ (uint8_t *)&rs->sleep_value);
+ }
+}
+
+static void msm_lpm_notify_vdd_mem(struct msm_rpm_notifier_data
+ *rpm_notifier_cb)
+{
+ struct msm_lpm_resource *rs = &msm_lpm_vdd_mem;
+ msm_lpm_notify_common(rpm_notifier_cb, rs);
+}
+
+/*PXO*/
+static bool msm_lpm_beyond_limits_pxo(struct msm_rpmrs_limits *limits)
+{
+ bool ret = true;
+ struct msm_lpm_resource *rs = &msm_lpm_pxo;
+
+ if (rs->valid) {
+ uint32_t pxo_buf = rs->rs_data.value;
+ uint32_t pxo = rs->enable_low_power ? MSM_LPM_PXO_OFF :
+ rs->rs_data.default_value;
+
+ if (pxo_buf > pxo)
+ pxo = pxo_buf;
+
+ ret = (pxo > limits->pxo);
+
+ if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_PXO)
+ pr_info("%s:pxo buf %d pxo %d limits pxo %d\n",
+ __func__, pxo_buf, pxo, limits->pxo);
+ }
+ return ret;
+}
+
+static void msm_lpm_aggregate_pxo(struct msm_rpmrs_limits *limits)
+{
+ struct msm_lpm_resource *rs = &msm_lpm_pxo;
+
+ if (rs->valid) {
+ uint32_t pxo_buf = rs->rs_data.value;
+ if (limits->pxo > pxo_buf)
+ rs->sleep_value = limits->pxo;
+ else
+ rs->sleep_value = pxo_buf;
+
+ if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_PXO)
+ pr_info("%s: pxo buf %d sleep value %d\n",
+ __func__, pxo_buf, rs->sleep_value);
+ }
+}
+
+static void msm_lpm_flush_pxo(int notify_rpm)
+{
+ if (notify_rpm) {
+ struct msm_lpm_resource *rs = &msm_lpm_pxo;
+ msm_lpm_send_sleep_data(rs->rs_data.handle,
+ rs->rs_data.key,
+ (uint8_t *)&rs->sleep_value);
+ }
+}
+
+static void msm_lpm_notify_pxo(struct msm_rpm_notifier_data
+ *rpm_notifier_cb)
+{
+ struct msm_lpm_resource *rs = &msm_lpm_pxo;
+ msm_lpm_notify_common(rpm_notifier_cb, rs);
+}
+
+/* MPM
+static bool msm_lpm_use_mpm(struct msm_rpmrs_limits *limits)
+{
+ return ((limits->pxo == MSM_LPM_PXO_OFF) ||
+ (limits->vdd_dig_lower_bound <= VDD_DIG_RET_HIGH));
+}*/
+
+/* LPM levels interface */
+bool msm_lpm_level_beyond_limit(struct msm_rpmrs_limits *limits)
+{
+ int i;
+ struct msm_lpm_resource *rs;
+ bool beyond_limit = false;
+
+ for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
+ rs = msm_lpm_resources[i];
+ if (rs->beyond_limits && rs->beyond_limits(limits)) {
+ beyond_limit = true;
+ if (msm_lpm_debug_mask & MSM_LPMRS_DEBUG_LVLS)
+ pr_info("%s: %s beyond limit", __func__,
+ rs->name);
+ break;
+ }
+ }
+
+ return beyond_limit;
+}
+
+int msm_lpmrs_enter_sleep(struct msm_rpmrs_limits *limits,
+ bool from_idle, bool notify_rpm)
+{
+ int ret = 0;
+ int i;
+ struct msm_lpm_resource *rs = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
+ rs = msm_lpm_resources[i];
+ if (rs->aggregate)
+ rs->aggregate(limits);
+ }
+
+ msm_lpm_get_rpm_notif = false;
+ for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
+ rs = msm_lpm_resources[i];
+ if (rs->flush)
+ rs->flush(notify_rpm);
+ }
+ msm_lpm_get_rpm_notif = true;
+
+ /* MPM Enter sleep
+ if (msm_lpm_use_mpm(limits))
+ msm_mpm_enter_sleep(from_idle);*/
+
+ return ret;
+}
+
+void msm_lpmrs_exit_sleep(uint32_t sclk_count, struct msm_rpmrs_limits *limits,
+ bool from_idle, bool notify_rpm)
+{
+ /* MPM exit sleep
+ if (msm_lpm_use_mpm(limits))
+ msm_mpm_exit_sleep(from_idle);*/
+}
+
+static int msm_lpm_cpu_callback(struct notifier_block *cpu_nb,
+ unsigned long action, void *hcpu)
+{
+ struct msm_lpm_resource *rs = &msm_lpm_l2;
+ switch (action) {
+ case CPU_ONLINE_FROZEN:
+ case CPU_ONLINE:
+ if (num_online_cpus() > 1)
+ rs->rs_data.value = MSM_LPM_L2_CACHE_ACTIVE;
+ break;
+ case CPU_DEAD_FROZEN:
+ case CPU_DEAD:
+ if (num_online_cpus() == 1)
+ rs->rs_data.value = MSM_LPM_L2_CACHE_GDHS;
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+/* RPM CTL */
+static int __devinit msm_lpm_init_rpm_ctl(void)
+{
+ struct msm_lpm_resource *rs = &msm_lpm_rpm_ctl;
+
+ rs->rs_data.handle = msm_rpm_create_request(
+ MSM_RPM_CTX_ACTIVE_SET,
+ rs->rs_data.type,
+ rs->rs_data.id, 1);
+ if (!rs->rs_data.handle)
+ return -EIO;
+
+ rs->valid = true;
+ return 0;
+}
+
+static int __devinit msm_lpm_resource_sysfs_add(void)
+{
+ struct kobject *module_kobj = NULL;
+ struct kobject *low_power_kobj = NULL;
+ struct kobject *mode_kobj = NULL;
+ int rc = 0;
+
+ module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
+ if (!module_kobj) {
+ pr_err("%s: cannot find kobject for module %s\n",
+ __func__, KBUILD_MODNAME);
+ rc = -ENOENT;
+ goto resource_sysfs_add_exit;
+ }
+
+ low_power_kobj = kobject_create_and_add(
+ "enable_low_power", module_kobj);
+ if (!low_power_kobj) {
+ pr_err("%s: cannot create kobject\n", __func__);
+ rc = -ENOMEM;
+ goto resource_sysfs_add_exit;
+ }
+
+ mode_kobj = kobject_create_and_add(
+ "mode", module_kobj);
+ if (!mode_kobj) {
+ pr_err("%s: cannot create kobject\n", __func__);
+ rc = -ENOMEM;
+ goto resource_sysfs_add_exit;
+ }
+
+ rc = sysfs_create_group(low_power_kobj, &msm_lpm_attribute_group);
+ if (rc) {
+ pr_err("%s: cannot create kobject attribute group\n", __func__);
+ goto resource_sysfs_add_exit;
+ }
+
+ rc = sysfs_create_group(mode_kobj, &msm_lpm_rpm_ctl_attr_group);
+ if (rc) {
+ pr_err("%s: cannot create kobject attribute group\n", __func__);
+ goto resource_sysfs_add_exit;
+ }
+
+resource_sysfs_add_exit:
+ if (rc) {
+ if (low_power_kobj)
+ sysfs_remove_group(low_power_kobj,
+ &msm_lpm_attribute_group);
+ kobject_del(low_power_kobj);
+ kobject_del(mode_kobj);
+ }
+
+ return rc;
+}
+
+late_initcall(msm_lpm_resource_sysfs_add);
+
+static int __devinit msm_lpmrs_probe(struct platform_device *pdev)
+{
+ struct device_node *node = NULL;
+ char *key = NULL;
+ int ret = 0;
+
+ for_each_child_of_node(pdev->dev.of_node, node) {
+ struct msm_lpm_resource *rs = NULL;
+ const char *val;
+ int i;
+
+ key = "qcom,name";
+ ret = of_property_read_string(node, key, &val);
+ if (ret) {
+ pr_err("Cannot read string\n");
+ goto fail;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(msm_lpm_resources); i++) {
+ char *lpmrs_name = msm_lpm_resources[i]->name;
+ if (!msm_lpm_resources[i]->valid &&
+ !strncmp(val, lpmrs_name, strnlen(lpmrs_name,
+ MAX_RS_NAME))) {
+ rs = msm_lpm_resources[i];
+ break;
+ }
+ }
+
+ if (!rs) {
+ pr_err("LPM resource not found\n");
+ continue;
+ }
+
+ key = "qcom,type";
+ ret = of_property_read_u32(node, key, &rs->rs_data.type);
+ if (ret) {
+ pr_err("Failed to read type\n");
+ goto fail;
+ }
+
+ key = "qcom,id";
+ ret = of_property_read_u32(node, key, &rs->rs_data.id);
+ if (ret) {
+ pr_err("Failed to read id\n");
+ goto fail;
+ }
+
+ key = "qcom,key";
+ ret = of_property_read_u32(node, key, &rs->rs_data.key);
+ if (ret) {
+ pr_err("Failed to read key\n");
+ goto fail;
+ }
+
+ rs->rs_data.handle = msm_lpm_create_rpm_request(
+ rs->rs_data.type, rs->rs_data.id);
+
+ if (!rs->rs_data.handle) {
+ pr_err("%s: Failed to allocate handle for %s\n",
+ __func__, rs->name);
+ ret = -1;
+ goto fail;
+ }
+
+ rs->valid = true;
+ }
+ msm_rpm_register_notifier(&msm_lpm_rpm_nblk);
+ msm_lpm_init_rpm_ctl();
+ register_hotcpu_notifier(&msm_lpm_cpu_nblk);
+ /* For UP mode, set the default to HSFS OPEN*/
+ if (num_possible_cpus() == 1) {
+ msm_lpm_l2.rs_data.default_value = MSM_LPM_L2_CACHE_HSFS_OPEN;
+ msm_lpm_l2.rs_data.value = MSM_LPM_L2_CACHE_HSFS_OPEN;
+ }
+ return 0;
+fail:
+ return ret;
+}
+
+static struct of_device_id msm_lpmrs_match_table[] = {
+ {.compatible = "qcom,lpm-resources"},
+ {},
+};
+
+static struct platform_driver msm_lpmrs_driver = {
+ .probe = msm_lpmrs_probe,
+ .driver = {
+ .name = "lpm-resources",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_lpmrs_match_table,
+ },
+};
+
+int __init msm_lpmrs_module_init(void)
+{
+ return platform_driver_register(&msm_lpmrs_driver);
+}
diff --git a/arch/arm/mach-msm/lpm_resources.h b/arch/arm/mach-msm/lpm_resources.h
new file mode 100644
index 0000000..9973fbf
--- /dev/null
+++ b/arch/arm/mach-msm/lpm_resources.h
@@ -0,0 +1,128 @@
+/* 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_LPM_RESOURCES_H
+#define __ARCH_ARM_MACH_MSM_LPM_RESOURCES_H
+
+#include "pm.h"
+
+enum {
+ MSM_LPM_PXO_OFF = 0,
+ MSM_LPM_PXO_ON = 1,
+};
+
+enum {
+ MSM_LPM_L2_CACHE_HSFS_OPEN = 0,
+ MSM_LPM_L2_CACHE_GDHS = 1,
+ MSM_LPM_L2_CACHE_RETENTION = 2,
+ MSM_LPM_L2_CACHE_ACTIVE = 3,
+};
+
+struct msm_rpmrs_limits {
+ uint32_t pxo;
+ uint32_t l2_cache;
+ uint32_t vdd_mem_upper_bound;
+ uint32_t vdd_mem_lower_bound;
+ uint32_t vdd_dig_upper_bound;
+ uint32_t vdd_dig_lower_bound;
+
+ uint32_t latency_us[NR_CPUS];
+ uint32_t power[NR_CPUS];
+};
+
+struct msm_rpmrs_level {
+ enum msm_pm_sleep_mode sleep_mode;
+ struct msm_rpmrs_limits rs_limits;
+ bool available;
+ uint32_t latency_us;
+ uint32_t steady_state_power;
+ uint32_t energy_overhead;
+ uint32_t time_overhead_us;
+};
+
+#ifdef CONFIG_MSM_RPM_SMD
+
+/**
+ * msm_lpm_level_beyond_limit() - Check if the resources in a low power level
+ * is beyond the limits of the driver votes received for those resources.This
+ * function is used by lpm_levels to eliminate any low power level that cannot
+ * be entered.
+ *
+ * @limits: pointer to the resource limits of a low power level.
+ *
+ * returns true if the resource limits are beyond driver resource votes.
+ * false otherwise.
+ */
+bool msm_lpm_level_beyond_limit(struct msm_rpmrs_limits *limits);
+
+/**
+ * msm_lpmrs_enter_sleep() - Enter sleep flushes the sleep votes of low power
+ * resources to the RPM driver, also configure the MPM if needed depending
+ * on the low power mode being entered. L2 low power mode is also set in
+ * this function.
+
+ * @limits: pointer to the resource limits of the low power mode being entered.
+ * @from_idle: bool to determine if this call being made as a part of
+ * idle power collapse.
+ * @notify_rpm: bool that informs if this is an RPM notified power collapse.
+ *
+ * returns 0 on success.
+ */
+int msm_lpmrs_enter_sleep(struct msm_rpmrs_limits *limits,
+ bool from_idle, bool notify_rpm);
+
+/**
+ * msm_lpmrs_exit_sleep() - Exit sleep, reset the MPM and L2 mode.
+ * @ sclk_count - Sleep Clock count.
+ * @ limits: pointer to resource limits of the most recent low power mode.
+ * @from_idle: bool to determine if this call being made as a part of
+ * idle power collapse.
+ * @notify_rpm: bool that informs if this is an RPM notified power collapse.
+ */
+void msm_lpmrs_exit_sleep(uint32_t sclk_count, struct msm_rpmrs_limits *limits,
+ bool from_idle, bool notify_rpm);
+/**
+ * msm_lpmrs_module_init() - Init function that parses the device tree to
+ * get the low power resource attributes and registers with RPM driver for
+ * callback notification.
+ *
+ * returns 0 on success.
+ */
+int __init msm_lpmrs_module_init(void);
+
+#else
+static inline bool msm_lpm_level_beyond_limit(struct msm_rpmrs_limits *limits)
+{
+ return true;
+}
+
+static inline int msm_lpmrs_enter_sleep(struct msm_rpmrs_limits *limits,
+ bool from_idle, bool notify_rpm)
+{
+ return 0;
+}
+
+static inline void msm_lpmrs_exit_sleep(uint32_t sclk_count,
+ struct msm_rpmrs_limits *limits, bool from_idle,
+ bool notify_rpm)
+{
+ return;
+}
+
+static inline int __init msm_lpmrs_module_init(void)
+{
+ return 0;
+}
+#endif /* CONFIG_MSM_RPM_SMD */
+
+#endif
diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c
index ac48990..6052918 100644
--- a/arch/arm/mach-msm/scm.c
+++ b/arch/arm/mach-msm/scm.c
@@ -297,6 +297,9 @@
__asmeq("%1", "r0")
__asmeq("%2", "r1")
__asmeq("%3", "r2")
+#ifdef REQUIRES_SEC
+ ".arch_extension sec\n"
+#endif
"smc #0 @ switch to secure world\n"
: "=r" (r0)
: "r" (r0), "r" (r1), "r" (r2)
@@ -329,6 +332,9 @@
__asmeq("%2", "r1")
__asmeq("%3", "r2")
__asmeq("%4", "r3")
+#ifdef REQUIRES_SEC
+ ".arch_extension sec\n"
+#endif
"smc #0 @ switch to secure world\n"
: "=r" (r0)
: "r" (r0), "r" (r1), "r" (r2), "r" (r3));
@@ -356,6 +362,9 @@
__asmeq("%4", "r1")
__asmeq("%5", "r2")
__asmeq("%6", "r3")
+#ifdef REQUIRES_SEC
+ ".arch_extension sec\n"
+#endif
"smc #0 @ switch to secure world\n"
: "=r" (r0), "=r" (r1), "=r" (r2)
: "r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4), "r" (r5));
@@ -388,6 +397,9 @@
__asmeq("%1", "r1")
__asmeq("%2", "r0")
__asmeq("%3", "r1")
+#ifdef REQUIRES_SEC
+ ".arch_extension sec\n"
+#endif
"smc #0 @ switch to secure world\n"
: "=r" (r0), "=r" (r1)
: "r" (r0), "r" (r1)
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 58a0963..56696c4 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -2558,11 +2558,6 @@
{
struct kgsl_device *device = &adreno_dev->dev;
- /* Reset the core */
- adreno_regwrite(device, A3XX_RBBM_SW_RESET_CMD,
- 0x00000001);
- msleep(20);
-
/* Set up 16 deep read/write request queues */
adreno_regwrite(device, A3XX_VBIF_IN_RD_LIM_CONF0, 0x10101010);
diff --git a/drivers/media/radio/radio-tavarua.c b/drivers/media/radio/radio-tavarua.c
index 971cf10..6085871 100644
--- a/drivers/media/radio/radio-tavarua.c
+++ b/drivers/media/radio/radio-tavarua.c
@@ -1214,7 +1214,7 @@
unsigned char adie_type_bahma;
int retval = 0;
unsigned int rdsMask = 0;
- unsigned char value;
+ unsigned char value = 0;
adie_type_bahma = is_bahama();