EDP: add sysedp_reactive_capping
Timo Alho [Sun, 22 Dec 2013 11:04:40 +0000 (13:04 +0200)]
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>

drivers/edp/Makefile
drivers/edp/sysedp_reactive_capping.c [new file with mode: 0644]
include/linux/sysedp.h

index 7a6f2c2..867adbb 100644 (file)
@@ -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 (file)
index 0000000..9a0cdd3
--- /dev/null
@@ -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);
index f237b27..3820826 100644 (file)
@@ -119,6 +119,27 @@ struct sysedp_batmon_calc_platform_data {
        unsigned int update_interval;
 };
 
+/* Sysedp reactive capping
+ * @max_capping_mw: maximum capping in mW
+ * @step_alarm_mw: amount of mW to cap on each interrupt
+ * @step_relax_mw: amount ow mW to relax after relax_ms
+ * @relax_ms: back-off period to relax capping (in ms)
+ */
+struct sysedp_reactive_capping_platform_data {
+       int max_capping_mw;
+       int step_alarm_mw;
+       int step_relax_mw;
+       int relax_ms;
+       int irq;
+       int irq_flags;
+       struct sysedp_consumer sysedpc;
+
+       /* internal */
+       int cur_capping_mw;
+       struct mutex mutex;
+       struct delayed_work work;
+};
+
 #ifdef CONFIG_SYSEDP_FRAMEWORK
 extern struct dentry *edp_debugfs_dir;