summaryrefslogtreecommitdiff
path: root/drivers/edp
diff options
context:
space:
mode:
authorTimo Alho <talho@nvidia.com>2013-12-22 13:04:40 +0200
committerJuha Tukkinen <jtukkinen@nvidia.com>2013-12-31 04:40:23 -0800
commit84c869e957d0b616487d2331b0a0afad7eb2ec6b (patch)
treebc3ec83042ff42ef25d7a9fb4e40047cc26733a6 /drivers/edp
parent138b68226845b781e82710f65d96a3d927d9847c (diff)
EDP: add sysedp_reactive_capping
This patch adds a sysedp_reactive_capping component. This component monitors the rate of over current (OC) interrupts from soc_therm hardware. During frequent events, sysedp_reactive_capping increases it's power state to reduce the budget available for AP+DRAM. sysedp_reactive_capping hooks into interrupts provided by soc_therm. Towards system EDP framework, sysedp_reactive_capping acts as sysedp consumer. Change-Id: I53918c7cf63cbfd689b78abd17a77d97f5ed985a Signed-off-by: Timo Alho <talho@nvidia.com> Reviewed-on: http://git-master/r/348416 Reviewed-by: Juha Tukkinen <jtukkinen@nvidia.com> Tested-by: Juha Tukkinen <jtukkinen@nvidia.com>
Diffstat (limited to 'drivers/edp')
-rw-r--r--drivers/edp/Makefile3
-rw-r--r--drivers/edp/sysedp_reactive_capping.c136
2 files changed, 138 insertions, 1 deletions
diff --git a/drivers/edp/Makefile b/drivers/edp/Makefile
index 7a6f2c26b99f..867adbb65068 100644
--- a/drivers/edp/Makefile
+++ b/drivers/edp/Makefile
@@ -3,7 +3,8 @@ GCOV_PROFILE := y
obj-$(CONFIG_SYSEDP_FRAMEWORK) += sysedp.o
obj-$(CONFIG_SYSEDP_FRAMEWORK) += sysedp_sysfs.o
obj-$(CONFIG_SYSEDP_FRAMEWORK) += sysedp_dynamic_capping.o
+obj-$(CONFIG_SYSEDP_FRAMEWORK) += sysedp_reactive_capping.o
obj-$(CONFIG_SYSEDP_FRAMEWORK) += sysedp_batmon_calc.o
ifdef CONFIG_DEBUG_FS
obj-$(CONFIG_SYSEDP_FRAMEWORK) += sysedp_debug.o
-endif \ No newline at end of file
+endif
diff --git a/drivers/edp/sysedp_reactive_capping.c b/drivers/edp/sysedp_reactive_capping.c
new file mode 100644
index 000000000000..9a0cdd30fcd3
--- /dev/null
+++ b/drivers/edp/sysedp_reactive_capping.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/sysedp.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+
+#define STATE_MAX_MW 20000
+#define STATE_STEP_MW 500
+#define NSTATES (STATE_MAX_MW / STATE_STEP_MW + 1)
+
+inline unsigned int count_state(int mw)
+{
+ int state;
+ state = mw > 0 ? mw / STATE_STEP_MW + 1 : 0;
+ return min(state, NSTATES - 1);
+}
+
+static void oc_throttle_alarm(struct sysedp_reactive_capping_platform_data *h)
+{
+ mutex_lock(&h->mutex);
+
+ h->cur_capping_mw += h->step_alarm_mw;
+ h->cur_capping_mw = min(h->cur_capping_mw, h->max_capping_mw);
+
+ cancel_delayed_work(&h->work);
+
+ sysedp_set_state(&h->sysedpc, count_state(h->cur_capping_mw));
+
+ schedule_delayed_work(&h->work, msecs_to_jiffies(h->relax_ms));
+
+ mutex_unlock(&h->mutex);
+}
+
+static void oc_throttle_work(struct work_struct *work)
+{
+ struct sysedp_reactive_capping_platform_data *h;
+ h = container_of(to_delayed_work(work),
+ struct sysedp_reactive_capping_platform_data,
+ work);
+ mutex_lock(&h->mutex);
+ h->cur_capping_mw -= h->step_relax_mw;
+ h->cur_capping_mw = max(h->cur_capping_mw, 0);
+
+ sysedp_set_state(&h->sysedpc, count_state(h->cur_capping_mw));
+
+ if (h->cur_capping_mw)
+ schedule_delayed_work(&h->work, msecs_to_jiffies(h->relax_ms));
+
+ mutex_unlock(&h->mutex);
+}
+
+static irqreturn_t sysedp_reactive_capping_irq_handler(int irq, void *data)
+{
+ if (!data)
+ return IRQ_NONE;
+
+ oc_throttle_alarm(data);
+ return IRQ_HANDLED;
+}
+
+
+static unsigned int capping_states[NSTATES];
+
+static int sysedp_reactive_capping_probe(struct platform_device *pdev)
+{
+ int ret, i;
+ struct sysedp_reactive_capping_platform_data *pdata;
+
+ pdata = pdev->dev.platform_data;
+
+ if (!pdata)
+ return -EINVAL;
+
+ /* update static capping_states table */
+ for (i = 0; i < NSTATES; i++)
+ capping_states[i] = i * STATE_STEP_MW;
+
+ /* sysedpc consumer name must be initialized */
+ if (pdata->sysedpc.name[0] == '\0')
+ return -EINVAL;
+ pdata->sysedpc.states = capping_states;
+ pdata->sysedpc.num_states = ARRAY_SIZE(capping_states);
+ ret = sysedp_register_consumer(&pdata->sysedpc);
+ if (ret) {
+ pr_err("sysedp_reactive_capping_probe: consumer register failed (%d)\n",
+ ret);
+ return ret;
+ }
+ mutex_init(&pdata->mutex);
+ INIT_DELAYED_WORK(&pdata->work, oc_throttle_work);
+
+ ret = request_threaded_irq(pdata->irq,
+ NULL,
+ sysedp_reactive_capping_irq_handler,
+ pdata->irq_flags,
+ pdata->sysedpc.name,
+ pdata);
+ if (ret) {
+ pr_err("sysedp_reactive_capping_probe: request_threaded_irq failed (%d)\n",
+ ret);
+ sysedp_unregister_consumer(&pdata->sysedpc);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver sysedp_reactive_capping_driver = {
+ .probe = sysedp_reactive_capping_probe,
+ .driver = {
+ .name = "sysedp_reactive_capping",
+ .owner = THIS_MODULE
+ }
+};
+
+static __init int sysedp_reactive_capping_init(void)
+{
+ return platform_driver_register(&sysedp_reactive_capping_driver);
+}
+late_initcall(sysedp_reactive_capping_init);