From: Nishanth Menon <nm@ti.com>
To: "Rafael J. Wysocki" <rjw@rjwysocki.net>,
Viresh Kumar <viresh.kumar@linaro.org>,
MyungJoo Ham <myungjoo.ham@samsung.com>,
Mark Brown <broonie@kernel.org>,
Mike Turquette <mturquette@linaro.org>
Cc: <devicetree@vger.kernel.org>, <linux-doc@vger.kernel.org>,
<linux-kernel@vger.kernel.org>, <cpufreq@vger.kernel.org>,
<linux-pm@vger.kernel.org>,
<linux-arm-kernel@lists.infradead.org>,
<linux-omap@vger.kernel.org>, Nishanth Menon <nm@ti.com>
Subject: [RFC PATCH 1/6] PM / Voltagedomain: Add generic clk notifier handler for regulator based dynamic voltage scaling
Date: Tue, 18 Feb 2014 14:32:18 -0600 [thread overview]
Message-ID: <1392755543-28335-2-git-send-email-nm@ti.com> (raw)
In-Reply-To: <1392755543-28335-1-git-send-email-nm@ti.com>
From: Mike Turquette <mturquette@linaro.org>
This patch provides helper functions for drivers that wish to scale
voltage through the clock rate-change notifiers. The approach taken
is that the user-driver(cpufreq/devfreq) do not care about the
details of the OPP table, nor does it care about handling the voltage
regulator directly.
By using the clk notifier flags, we are able to sequence the operations
in the right order. The current logic is heavily influenced by
implementation done in cpufreq-cpu0.
[nm@ti.com: Fixes in logic, and broken out from clk to allow building
a generic voltagedomain solution independent of cpufreq]
Signed-off-by: Nishanth Menon <nm@ti.com>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
---
drivers/power/Kconfig | 1 +
drivers/power/Makefile | 1 +
drivers/power/voltdm/Kconfig | 7 ++
drivers/power/voltdm/Makefile | 2 +
drivers/power/voltdm/core.c | 195 +++++++++++++++++++++++++++++++++++++
include/linux/pm_voltage_domain.h | 47 +++++++++
6 files changed, 253 insertions(+)
create mode 100644 drivers/power/voltdm/Kconfig
create mode 100644 drivers/power/voltdm/Makefile
create mode 100644 drivers/power/voltdm/core.c
create mode 100644 include/linux/pm_voltage_domain.h
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index ba69751..5c4fe16 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -394,3 +394,4 @@ source "drivers/power/reset/Kconfig"
endif # POWER_SUPPLY
source "drivers/power/avs/Kconfig"
+source "drivers/power/voltdm/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index ee54a3e..3d47072 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -58,3 +58,4 @@ obj-$(CONFIG_POWER_AVS) += avs/
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
obj-$(CONFIG_POWER_RESET) += reset/
+obj-$(CONFIG_VOLTAGE_DOMAIN) += voltdm/
diff --git a/drivers/power/voltdm/Kconfig b/drivers/power/voltdm/Kconfig
new file mode 100644
index 0000000..c5353bd
--- /dev/null
+++ b/drivers/power/voltdm/Kconfig
@@ -0,0 +1,7 @@
+config VOLTAGE_DOMAIN
+ bool
+ depends on COMMON_CLK && OF && PM_OPP
+ default y if COMMON_CLK
+ ---help---
+ Core voltage domain framework using common clock framework's
+ notifier mechanism.
diff --git a/drivers/power/voltdm/Makefile b/drivers/power/voltdm/Makefile
new file mode 100644
index 0000000..3fa4408
--- /dev/null
+++ b/drivers/power/voltdm/Makefile
@@ -0,0 +1,2 @@
+# Generic voltage domain
+obj-$(CONFIG_VOLTAGE_DOMAIN) += core.o
diff --git a/drivers/power/voltdm/core.c b/drivers/power/voltdm/core.c
new file mode 100644
index 0000000..d0ed27e
--- /dev/null
+++ b/drivers/power/voltdm/core.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2013 Linaro Ltd <mturquette@linaro.org>
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Helper functions for registering clock rate-change notifier handlers
+ * that scale voltage when a clock changes its output frequency.
+ */
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_voltage_domain.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+/**
+ * struct volt_scale_data - Internal structure to maintain notifier information
+ * @dev: device on behalf of which we register the notifier
+ * @clk: clk on which we registered the notifier
+ * @reg: regulator if any which is used for scaling voltage
+ * @tol: voltage tolerance in %
+ * @nb: notifier block pointer
+ */
+struct volt_scale_data {
+ struct device *dev;
+ struct clk *clk;
+ struct regulator *reg;
+ int tol;
+ struct notifier_block nb;
+};
+
+#define to_volt_scale_data(_nb) container_of(_nb, \
+ struct volt_scale_data, nb)
+
+static int clk_volt_notifier_handler(struct notifier_block *nb,
+ unsigned long flags, void *data)
+{
+ struct clk_notifier_data *cnd = data;
+ struct volt_scale_data *vsd = to_volt_scale_data(nb);
+ int ret, volt, tol;
+ struct dev_pm_opp *opp;
+ unsigned long old_rate = cnd->old_rate;
+ unsigned long new_rate = cnd->new_rate;
+
+ if ((new_rate < old_rate && flags == PRE_RATE_CHANGE) ||
+ (new_rate > old_rate && flags == POST_RATE_CHANGE))
+ return NOTIFY_OK;
+
+ rcu_read_lock();
+ if (flags != ABORT_RATE_CHANGE)
+ opp = dev_pm_opp_find_freq_ceil(vsd->dev, &new_rate);
+ else
+ opp = dev_pm_opp_find_freq_ceil(vsd->dev, &old_rate);
+ if (IS_ERR(opp)) {
+ rcu_read_unlock();
+ dev_err(vsd->dev, "%s: Failed to find OPP for %lu\n",
+ __func__, new_rate);
+ return notifier_from_errno(PTR_ERR(opp));
+ }
+
+ volt = dev_pm_opp_get_voltage(opp);
+ rcu_read_unlock();
+
+ tol = volt * vsd->tol / 100;
+
+ dev_dbg(vsd->dev, "%s: %lu -> %lu, V=%d, tol=%d, clk_flag=%lu\n",
+ __func__, old_rate, new_rate, volt, tol, flags);
+
+ ret = regulator_set_voltage_tol(vsd->reg, volt, tol);
+ if (ret) {
+ dev_err(vsd->dev,
+ "%s: Failed to scale voltage(%u): %d\n", __func__,
+ volt, ret);
+ return notifier_from_errno(ret);
+ }
+
+ return NOTIFY_OK;
+}
+
+/**
+ * of_pm_voltdm_notifier_register() - register voltage domain notifier
+ * @dev: device for which to register notifier for
+ * @np: node pointer of the device for which we register
+ * @clk: clk pointer around which the notifier is expected to trigger
+ * @supply: default regulator supply name(regulator id string)
+ * @voltage_latency: returns the latency for the voltage domain
+ *
+ * Return: notifier block which is registered with the common clock framework's
+ * notifier for the clk node requested.
+ */
+struct notifier_block *of_pm_voltdm_notifier_register(struct device *dev,
+ struct device_node *np,
+ struct clk *clk,
+ const char *supply,
+ int *voltage_latency)
+{
+ struct volt_scale_data *vsd;
+ struct dev_pm_opp *opp;
+ unsigned long min, max, freq;
+ int ret;
+
+ vsd = kzalloc(sizeof(*vsd), GFP_KERNEL);
+ if (!vsd)
+ return ERR_PTR(-ENOMEM);
+
+ vsd->dev = dev;
+ vsd->clk = clk;
+ vsd->nb.notifier_call = clk_volt_notifier_handler;
+ vsd->reg = regulator_get_optional(dev, supply);
+ ret = 0;
+ if (IS_ERR(vsd->reg))
+ ret = PTR_ERR(vsd->reg);
+ /* regulator is not mandatory */
+ if (ret != -EPROBE_DEFER) {
+ dev_warn(dev, "%s: Failed to get %s regulator:%d\n",
+ __func__, supply, ret);
+ ret = 0;
+ goto err_free_vsd;
+ }
+ /* For devices that are not ready.... */
+ if (ret)
+ goto err_free_vsd;
+
+ rcu_read_lock();
+ freq = 0;
+ opp = dev_pm_opp_find_freq_ceil(dev, &freq);
+ if (IS_ERR(opp))
+ goto err_bad_opp;
+ min = dev_pm_opp_get_voltage(opp);
+
+ freq = ULONG_MAX;
+ opp = dev_pm_opp_find_freq_floor(dev, &freq);
+ if (IS_ERR(opp))
+ goto err_bad_opp;
+ max = dev_pm_opp_get_voltage(opp);
+ rcu_read_unlock();
+
+ *voltage_latency = regulator_set_voltage_time(vsd->reg, min, max);
+ if (*voltage_latency < 0) {
+ dev_warn(dev,
+ "%s: Fail calculating voltage latency[%ld<->%ld]:%d\n",
+ __func__, min, max, *voltage_latency);
+ }
+
+ of_property_read_u32(np, "voltage-tolerance", &vsd->tol);
+
+ ret = clk_notifier_register(clk, &vsd->nb);
+
+ if (ret) {
+ dev_err(dev, "%s: Failed to Register Notifier, %d\n", __func__,
+ ret);
+ goto err_free_reg;
+ }
+
+ return &vsd->nb;
+
+err_bad_opp:
+ rcu_read_unlock();
+ ret = PTR_ERR(opp);
+ dev_err(dev, "%s: failed to get OPP, %d\n", __func__, ret);
+
+err_free_reg:
+ regulator_put(vsd->reg);
+
+err_free_vsd:
+ kfree(vsd);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(of_pm_voltdm_notifier_register);
+
+/**
+ * of_pm_voltdm_notifier_unregister() - unregister notifier for volt domain
+ * @nb: notifier block returned by of_pm_voltdm_notifier_register
+ */
+void of_pm_voltdm_notifier_unregister(struct notifier_block *nb)
+{
+ struct volt_scale_data *vsd;
+ struct clk *clk;
+
+ /* if caller send us back error value */
+ if (IS_ERR(nb))
+ return;
+
+ vsd = to_volt_scale_data(nb);
+ clk = vsd->clk;
+ clk_notifier_unregister(clk, nb);
+ if (!IS_ERR(vsd->reg))
+ regulator_put(vsd->reg);
+
+ kfree(vsd);
+}
+EXPORT_SYMBOL_GPL(of_pm_voltdm_notifier_unregister);
diff --git a/include/linux/pm_voltage_domain.h b/include/linux/pm_voltage_domain.h
new file mode 100644
index 0000000..1ee7343
--- /dev/null
+++ b/include/linux/pm_voltage_domain.h
@@ -0,0 +1,47 @@
+/*
+ * Voltage Domain header for users of voltage domain interface
+ *
+ * Copyright (C) 2013 Linaro Ltd <mturquette@linaro.org>
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __PM_VOLTAGE_DOMAIN__
+#define __PM_VOLTAGE_DOMAIN__
+
+#include <linux/clk.h>
+
+#if defined(CONFIG_VOLTAGE_DOMAIN)
+struct notifier_block *of_pm_voltdm_notifier_register(struct device *dev,
+ struct device_node *np,
+ struct clk *clk,
+ const char *supply,
+ int *voltage_latency);
+void of_pm_voltdm_notifier_unregister(struct notifier_block *nb);
+
+#else
+static inline struct notifier_block *of_pm_voltdm_notifier_register(
+ struct device *dev,
+ struct device_node *np,
+ struct clk *clk,
+ const char *supply,
+ int *voltage_latency);
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline void of_pm_voltdm_notifier_unregister(struct notifier_block *nb)
+{
+}
+
+#endif /* VOLTAGE_DOMAIN */
+
+#endif /* __PM_VOLTAGE_DOMAIN__ */
--
1.7.9.5
next prev parent reply other threads:[~2014-02-18 20:35 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-02-18 20:32 [RFC PATCH 0/6] PM: introduce voltage domain abstraction Nishanth Menon
2014-02-18 20:32 ` Nishanth Menon [this message]
2014-02-25 5:51 ` [RFC PATCH 1/6] PM / Voltagedomain: Add generic clk notifier handler for regulator based dynamic voltage scaling Mike Turquette
2014-02-25 20:56 ` Nishanth Menon
2014-02-27 2:34 ` Nishanth Menon
2014-02-27 5:00 ` Mike Turquette
2014-02-27 14:42 ` Nishanth Menon
2014-02-27 18:59 ` Felipe Balbi
2014-02-18 20:32 ` [RFC PATCH 2/6] cpufreq: cpufreq-cpu0: use clk rate-change notifiers Nishanth Menon
2014-02-18 20:32 ` [RFC PATCH 3/6] PM / Voltagedomain: introduce voltage domain driver support Nishanth Menon
2014-02-24 1:58 ` Mark Brown
2014-02-24 14:38 ` Nishanth Menon
2014-03-03 3:54 ` Mark Brown
2014-03-10 17:11 ` Nishanth Menon
2014-03-10 17:22 ` Mark Brown
2014-03-10 17:41 ` Nishanth Menon
2014-03-10 18:01 ` Mark Brown
2014-03-10 19:25 ` Nishanth Menon
2014-03-19 22:35 ` Mike Turquette
2014-02-18 20:32 ` [RFC PATCH 4/6] devicetree: bindings: add documentation for voltagedomain Nishanth Menon
2014-02-18 20:32 ` [RFC PATCH 5/6] PM / Voltagedomain: introduce basic voltage domain support for OMAP Nishanth Menon
2014-02-18 20:32 ` [RFC PATCH 6/6] devicetree: bindings: voltagedomain: add bindings for OMAP compatible SoCs Nishanth Menon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1392755543-28335-2-git-send-email-nm@ti.com \
--to=nm@ti.com \
--cc=broonie@kernel.org \
--cc=cpufreq@vger.kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-omap@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=mturquette@linaro.org \
--cc=myungjoo.ham@samsung.com \
--cc=rjw@rjwysocki.net \
--cc=viresh.kumar@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®