* Re: [PATCH] PM / OPP: Export symbols for module usage.
From: Rafael J. Wysocki @ 2012-10-24 21:55 UTC (permalink / raw)
To: Kevin Hilman; +Cc: Nishanth Menon, linux-pm, Liam Girdwood, linux-kernel
In-Reply-To: <87k3uwagby.fsf@deeprootsystems.com>
On Thursday 11 of October 2012 13:45:05 Kevin Hilman wrote:
> Nishanth Menon <nm@ti.com> writes:
>
> > From: Liam Girdwood <lrg@ti.com>
> >
> > Export the OPP functions for use by driver modules.
> >
> > Cc: "Rafael J. Wysocki" <rjw@sisk.pl>
> > Cc: Kevin Hilman <khilman@ti.com>
> > Cc: linux-pm@vger.kernel.org
> > Cc: linux-kernel@vger.kernel.org
> >
> > [nm@ti.com: expansion of functions exported]
> > Signed-off-by: Nishanth Menon <nm@ti.com>
> > Signed-off-by: Liam Girdwood <lrg@ti.com>
>
> Acked-by: Kevin Hilman <khilman@ti.com>
Applied to linux-pm.git/linux-next as v3.8 material.
Thanks,
Rafael
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
^ permalink raw reply
* Re: [PATCH RESEND] PM/Hibernate: use rb_entry
From: Rafael J. Wysocki @ 2012-10-24 21:57 UTC (permalink / raw)
To: dave; +Cc: Pavel Machek, Len Brown, linux-pm, lkml
In-Reply-To: <1349086372.2721.1.camel@offbook>
On Monday 01 of October 2012 12:12:52 Davidlohr Bueso wrote:
> Since the software suspend extents are organized in an rbtree, use rb_entry
> instead of container_of, as it is semantically more appropriate in order to
> get a node as it is iterated.
>
> Signed-off-by: Davidlohr Bueso <dave@gnu.org>
Applied to linux-pm.git/linux-next as v3.8 material.
Thanks,
Rafael
> ---
> kernel/power/swap.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/kernel/power/swap.c b/kernel/power/swap.c
> index 3c9d764..7c33ed2 100644
> --- a/kernel/power/swap.c
> +++ b/kernel/power/swap.c
> @@ -126,7 +126,7 @@ static int swsusp_extents_insert(unsigned long swap_offset)
>
> /* Figure out where to put the new node */
> while (*new) {
> - ext = container_of(*new, struct swsusp_extent, node);
> + ext = rb_entry(*new, struct swsusp_extent, node);
> parent = *new;
> if (swap_offset < ext->start) {
> /* Try to merge */
>
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
^ permalink raw reply
* Re: [PATCH 1/2] cpufreq: Improve debug prints
From: Rafael J. Wysocki @ 2012-10-24 22:00 UTC (permalink / raw)
To: Viresh Kumar
Cc: linaro-dev-cunTk1MwBs8s++Sfvej+rw, patches-QSEj5FYQhm4dnm+yROfE0A,
linux-pm-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
cpufreq-u79uwXL29TY76Z2rM5mHXA, pdsw-power-team-5wv7dgnIgG8,
Joe Perches
In-Reply-To: <CAKohpo=fgz98F+1SBG9TgO0EsPd_XLznJ0Z__0ehsgm14EZK7A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Wednesday 17 of October 2012 12:03:31 Viresh Kumar wrote:
> On 17 October 2012 11:56, Joe Perches <joe-6d6DIl74uiNBDgjK7y7TUQ@public.gmane.org> wrote:
> > Oh good, but please use a space between KBUILD_MODNAME
> > and the quoted ": ".
>
> Anything technical behind it or just for code formatting? As output is
> same in both cases :)
>
> ------------------------8<----------------------8<-------------------
>
> From: Viresh Kumar <viresh.kumar-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Date: Wed, 17 Oct 2012 10:38:31 +0530
> Subject: [PATCH] cpufreq: Improve debug prints
>
> With debug options on, it is difficult to locate cpufreq core's debug prints.
> Fix this by prefixing debug prints with KBUILD_MODNAME.
>
> Signed-off-by: Viresh Kumar <viresh.kumar-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Applied to linux-pm.git/linux-next as v3.8 material.
Thanks,
Rafael
> ---
> drivers/cpufreq/cpufreq.c | 2 ++
> drivers/cpufreq/cpufreq_performance.c | 2 ++
> drivers/cpufreq/cpufreq_powersave.c | 2 ++
> drivers/cpufreq/cpufreq_userspace.c | 2 ++
> drivers/cpufreq/freq_table.c | 2 ++
> 5 files changed, 10 insertions(+)
>
> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
> index db6e337..85df538 100644
> --- a/drivers/cpufreq/cpufreq.c
> +++ b/drivers/cpufreq/cpufreq.c
> @@ -15,6 +15,8 @@
> *
> */
>
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/init.h>
> diff --git a/drivers/cpufreq/cpufreq_performance.c
> b/drivers/cpufreq/cpufreq_performance.c
> index f13a8a9..ceee068 100644
> --- a/drivers/cpufreq/cpufreq_performance.c
> +++ b/drivers/cpufreq/cpufreq_performance.c
> @@ -10,6 +10,8 @@
> *
> */
>
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/cpufreq.h>
> diff --git a/drivers/cpufreq/cpufreq_powersave.c
> b/drivers/cpufreq/cpufreq_powersave.c
> index 4c2eb51..2d948a1 100644
> --- a/drivers/cpufreq/cpufreq_powersave.c
> +++ b/drivers/cpufreq/cpufreq_powersave.c
> @@ -10,6 +10,8 @@
> *
> */
>
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/cpufreq.h>
> diff --git a/drivers/cpufreq/cpufreq_userspace.c
> b/drivers/cpufreq/cpufreq_userspace.c
> index bedac1a..c8c3d29 100644
> --- a/drivers/cpufreq/cpufreq_userspace.c
> +++ b/drivers/cpufreq/cpufreq_userspace.c
> @@ -11,6 +11,8 @@
> *
> */
>
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/smp.h>
> diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c
> index 90431cb..49cda25 100644
> --- a/drivers/cpufreq/freq_table.c
> +++ b/drivers/cpufreq/freq_table.c
> @@ -9,6 +9,8 @@
> *
> */
>
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/init.h>
>
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
^ permalink raw reply
* Re: [PATCH V2] PM / OPP: predictable fail results for opp_find* functions
From: Rafael J. Wysocki @ 2012-10-24 22:01 UTC (permalink / raw)
To: Nishanth Menon
Cc: linux-pm, MyungJoo Ham, Kyungmin Park, Kevin Hilman, linux-kernel
In-Reply-To: <1350912799-17323-1-git-send-email-nm@ti.com>
On Monday 22 of October 2012 08:33:19 Nishanth Menon wrote:
> Currently the opp_find* functions return -ENODEV when:
> a) it cant find a device (e.g. request for an OPP search on device
> which was not registered)
> b) When it cant find a match for the search strategy used
>
> This makes life a little in-efficient for users such as devfreq
> to make reasonable judgement before switching search strategies.
>
> So, standardize the return results as following:
> -EINVAL for bad pointer parameters
> -ENODEV when device cannot be found
> -ERANGE when search fails
>
> This has the following benefit for devfreq implementation:
> The search fails when an unregistered device pointer is provided.
> This is a trigger to change the search direction and search for
> a better fit, however, if we cannot differentiate between a valid
> search range failure Vs an unregistered device, second search goes
> through the same fail return condition. This can be avoided by
> appropriate handling of error return code.
>
> With this change, we also fix devfreq for the improved search
> strategy with updated error code.
>
> Cc: MyungJoo Ham <myungjoo.ham@samsung.com>
> Cc: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: "Rafael J. Wysocki" <rjw@sisk.pl>
> Cc: Kevin Hilman <khilman@ti.com>
> Cc: linux-pm@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
>
> Reviewed-by: Kevin Hilman <khilman@ti.com>
> Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com>
> Signed-off-by: Nishanth Menon <nm@ti.com>
Applied to linux-pm.git/linux-next as v3.8 material.
Thanks,
Rafael
> ---
> V2:
> * commit message change - updated frome example code, to verbose description
> * picked up Acked-by and Reviewed-by tags from V1.
> * rebased to v3.7-rc2
> V1: https://patchwork.kernel.org/patch/1582111/
>
> drivers/base/power/opp.c | 27 +++++++++++++++++++--------
> drivers/devfreq/devfreq.c | 4 ++--
> 2 files changed, 21 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
> index 4730a58..99d0573 100644
> --- a/drivers/base/power/opp.c
> +++ b/drivers/base/power/opp.c
> @@ -234,7 +234,10 @@ EXPORT_SYMBOL(opp_get_opp_count);
> *
> * Searches for exact match in the opp list and returns pointer to the matching
> * opp if found, else returns ERR_PTR in case of error and should be handled
> - * using IS_ERR.
> + * using IS_ERR. Error return values can be:
> + * EINVAL: for bad pointer
> + * ERANGE: no match found for search
> + * ENODEV: if device not found in list of registered devices
> *
> * Note: available is a modifier for the search. if available=true, then the
> * match is for exact matching frequency and is available in the stored OPP
> @@ -253,7 +256,7 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq,
> bool available)
> {
> struct device_opp *dev_opp;
> - struct opp *temp_opp, *opp = ERR_PTR(-ENODEV);
> + struct opp *temp_opp, *opp = ERR_PTR(-ERANGE);
>
> dev_opp = find_device_opp(dev);
> if (IS_ERR(dev_opp)) {
> @@ -283,7 +286,11 @@ EXPORT_SYMBOL(opp_find_freq_exact);
> * for a device.
> *
> * Returns matching *opp and refreshes *freq accordingly, else returns
> - * ERR_PTR in case of error and should be handled using IS_ERR.
> + * ERR_PTR in case of error and should be handled using IS_ERR. Error return
> + * values can be:
> + * EINVAL: for bad pointer
> + * ERANGE: no match found for search
> + * ENODEV: if device not found in list of registered devices
> *
> * Locking: This function must be called under rcu_read_lock(). opp is a rcu
> * protected pointer. The reason for the same is that the opp pointer which is
> @@ -294,7 +301,7 @@ EXPORT_SYMBOL(opp_find_freq_exact);
> struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)
> {
> struct device_opp *dev_opp;
> - struct opp *temp_opp, *opp = ERR_PTR(-ENODEV);
> + struct opp *temp_opp, *opp = ERR_PTR(-ERANGE);
>
> if (!dev || !freq) {
> dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
> @@ -303,7 +310,7 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)
>
> dev_opp = find_device_opp(dev);
> if (IS_ERR(dev_opp))
> - return opp;
> + return ERR_CAST(dev_opp);
>
> list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {
> if (temp_opp->available && temp_opp->rate >= *freq) {
> @@ -326,7 +333,11 @@ EXPORT_SYMBOL(opp_find_freq_ceil);
> * for a device.
> *
> * Returns matching *opp and refreshes *freq accordingly, else returns
> - * ERR_PTR in case of error and should be handled using IS_ERR.
> + * ERR_PTR in case of error and should be handled using IS_ERR. Error return
> + * values can be:
> + * EINVAL: for bad pointer
> + * ERANGE: no match found for search
> + * ENODEV: if device not found in list of registered devices
> *
> * Locking: This function must be called under rcu_read_lock(). opp is a rcu
> * protected pointer. The reason for the same is that the opp pointer which is
> @@ -337,7 +348,7 @@ EXPORT_SYMBOL(opp_find_freq_ceil);
> struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq)
> {
> struct device_opp *dev_opp;
> - struct opp *temp_opp, *opp = ERR_PTR(-ENODEV);
> + struct opp *temp_opp, *opp = ERR_PTR(-ERANGE);
>
> if (!dev || !freq) {
> dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
> @@ -346,7 +357,7 @@ struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq)
>
> dev_opp = find_device_opp(dev);
> if (IS_ERR(dev_opp))
> - return opp;
> + return ERR_CAST(dev_opp);
>
> list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {
> if (temp_opp->available) {
> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
> index 0920d75..bee1c29 100644
> --- a/drivers/devfreq/devfreq.c
> +++ b/drivers/devfreq/devfreq.c
> @@ -656,14 +656,14 @@ struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq,
> opp = opp_find_freq_floor(dev, freq);
>
> /* If not available, use the closest opp */
> - if (opp == ERR_PTR(-ENODEV))
> + if (opp == ERR_PTR(-ERANGE))
> opp = opp_find_freq_ceil(dev, freq);
> } else {
> /* The freq is an lower bound. opp should be higher */
> opp = opp_find_freq_ceil(dev, freq);
>
> /* If not available, use the closest opp */
> - if (opp == ERR_PTR(-ENODEV))
> + if (opp == ERR_PTR(-ERANGE))
> opp = opp_find_freq_floor(dev, freq);
> }
>
>
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
^ permalink raw reply
* Re: [PATCH 1/2] cpufreq: return early from __cpufreq_driver_getavg()
From: Rafael J. Wysocki @ 2012-10-24 22:02 UTC (permalink / raw)
To: Viresh Kumar
Cc: linaro-dev-cunTk1MwBs8s++Sfvej+rw,
linux-pm-u79uwXL29TY76Z2rM5mHXA, patches-QSEj5FYQhm4dnm+yROfE0A,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
cpufreq-u79uwXL29TY76Z2rM5mHXA, pdsw-power-team-5wv7dgnIgG8,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <CAKohponGwhC_zmk1Whq56vmZMvjcxXKiSpdvMk4ZU8z0Wa2TBw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Saturday 20 of October 2012 10:12:07 Viresh Kumar wrote:
> On Oct 20, 2012 3:37 AM, "Rafael J. Wysocki" <rjw-KKrjLPT3xs0@public.gmane.org> wrote:
> >
> > On Saturday 20 of October 2012 01:42:05 Viresh Kumar wrote:
> > > There is no need to do cpufreq_get_cpu() and cpufreq_put_cpu() for
> drivers that
> > > don't support getavg() routine.
> > >
> > > Signed-off-by: Viresh Kumar <viresh.kumar-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> >
> > The patch doesn't seem to follow the changelog or the other way around.
>
> Sorry if my log isn't clear enough.
> But i could still see it matching the code :)
>
> I have moved the check for drivers capabilities at the top
> of routine, so that there is no need to call mentioned routines.
OK
Applied to linux-pm.git/linux-next as v3.8 material.
Thanks,
Rafael
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
^ permalink raw reply
* [BUGFIX] PCI/PM: Fix proc config reg access for D3cold and bridge suspending
From: Huang Ying @ 2012-10-25 1:36 UTC (permalink / raw)
To: Bjorn Helgaas
Cc: linux-kernel, linux-pci, linux-pm, Rafael J. Wysocki, Huang Ying,
stable
In
https://bugzilla.kernel.org/show_bug.cgi?id=48981
Peter reported that /proc/bus/pci/??/??.? does not works for 3.6.
This is This is because the device configuration space registers will
be not accessible if the corresponding parent bridge is suspended or
the device is put into D3cold state.
This is the same as /sys/bus/pci/devices/0000:??:??.?/config access
issue. So the function used to solve sysfs issue is used to solve
this issue.
Cc: stable@vger.kernel.org
Reported-by: Peter <lekensteyn@gmail.com>
Signed-off-by: Huang Ying <ying.huang@intel.com>
---
drivers/pci/pci-sysfs.c | 34 ----------------------------------
drivers/pci/pci.c | 32 ++++++++++++++++++++++++++++++++
drivers/pci/pci.h | 2 ++
drivers/pci/proc.c | 8 ++++++++
4 files changed, 42 insertions(+), 34 deletions(-)
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -458,40 +458,6 @@ boot_vga_show(struct device *dev, struct
}
struct device_attribute vga_attr = __ATTR_RO(boot_vga);
-static void
-pci_config_pm_runtime_get(struct pci_dev *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device *parent = dev->parent;
-
- if (parent)
- pm_runtime_get_sync(parent);
- pm_runtime_get_noresume(dev);
- /*
- * pdev->current_state is set to PCI_D3cold during suspending,
- * so wait until suspending completes
- */
- pm_runtime_barrier(dev);
- /*
- * Only need to resume devices in D3cold, because config
- * registers are still accessible for devices suspended but
- * not in D3cold.
- */
- if (pdev->current_state == PCI_D3cold)
- pm_runtime_resume(dev);
-}
-
-static void
-pci_config_pm_runtime_put(struct pci_dev *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device *parent = dev->parent;
-
- pm_runtime_put(dev);
- if (parent)
- pm_runtime_put_sync(parent);
-}
-
static ssize_t
pci_read_config(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1858,6 +1858,38 @@ bool pci_dev_run_wake(struct pci_dev *de
}
EXPORT_SYMBOL_GPL(pci_dev_run_wake);
+void pci_config_pm_runtime_get(struct pci_dev *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *parent = dev->parent;
+
+ if (parent)
+ pm_runtime_get_sync(parent);
+ pm_runtime_get_noresume(dev);
+ /*
+ * pdev->current_state is set to PCI_D3cold during suspending,
+ * so wait until suspending completes
+ */
+ pm_runtime_barrier(dev);
+ /*
+ * Only need to resume devices in D3cold, because config
+ * registers are still accessible for devices suspended but
+ * not in D3cold.
+ */
+ if (pdev->current_state == PCI_D3cold)
+ pm_runtime_resume(dev);
+}
+
+void pci_config_pm_runtime_put(struct pci_dev *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *parent = dev->parent;
+
+ pm_runtime_put(dev);
+ if (parent)
+ pm_runtime_put_sync(parent);
+}
+
/**
* pci_pm_init - Initialize PM functions of given PCI device
* @dev: PCI device to handle.
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -72,6 +72,8 @@ extern void pci_disable_enabled_device(s
extern int pci_finish_runtime_suspend(struct pci_dev *dev);
extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign);
extern void pci_wakeup_bus(struct pci_bus *bus);
+extern void pci_config_pm_runtime_get(struct pci_dev *dev);
+extern void pci_config_pm_runtime_put(struct pci_dev *dev);
extern void pci_pm_init(struct pci_dev *dev);
extern void platform_pci_wakeup_init(struct pci_dev *dev);
extern void pci_allocate_cap_save_buffers(struct pci_dev *dev);
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -76,6 +76,8 @@ proc_bus_pci_read(struct file *file, cha
if (!access_ok(VERIFY_WRITE, buf, cnt))
return -EINVAL;
+ pci_config_pm_runtime_get(dev);
+
if ((pos & 1) && cnt) {
unsigned char val;
pci_user_read_config_byte(dev, pos, &val);
@@ -121,6 +123,8 @@ proc_bus_pci_read(struct file *file, cha
cnt--;
}
+ pci_config_pm_runtime_put(dev);
+
*ppos = pos;
return nbytes;
}
@@ -146,6 +150,8 @@ proc_bus_pci_write(struct file *file, co
if (!access_ok(VERIFY_READ, buf, cnt))
return -EINVAL;
+ pci_config_pm_runtime_get(dev);
+
if ((pos & 1) && cnt) {
unsigned char val;
__get_user(val, buf);
@@ -191,6 +197,8 @@ proc_bus_pci_write(struct file *file, co
cnt--;
}
+ pci_config_pm_runtime_put(dev);
+
*ppos = pos;
i_size_write(ino, dp->size);
return nbytes;
^ permalink raw reply
* Re: [PATCH 2/2] cpufreq: governors: remove redundant code
From: Viresh Kumar @ 2012-10-25 3:29 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: cpufreq, linux-pm, linux-kernel, linux-arm-kernel, linaro-dev,
patches, pdsw-power-team, arvind.chauhan
In-Reply-To: <3245066.cWfgP9Ikey@vostro.rjw.lan>
[-- Attachment #1: Type: text/plain, Size: 2493 bytes --]
On 25 October 2012 02:42, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Wednesday 24 of October 2012 21:43:46 Rafael J. Wysocki wrote:
>> On Wednesday 24 of October 2012 11:37:13 Viresh Kumar wrote:
>> > On 22 October 2012 14:16, Viresh Kumar <viresh.kumar@linaro.org> wrote:
>> > > On 20 October 2012 01:42, Viresh Kumar <viresh.kumar@linaro.org> wrote:
>> > >> Initially ondemand governor was written and then using its code conservative
>> > >> governor is written. It used a lot of code from ondemand governor, but copy of
>> > >> code was created instead of using the same routines from both governors. Which
>> > >> increased code redundancy, which is difficult to manage.
>> > >>
>> > >> This patch is an attempt to move common part of both the governors to
>> > >> cpufreq_governor.c file to come over above mentioned issues.
>> > >>
>> > >> This shouldn't change anything from functionality point of view.
>> > >>
>> > >> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
>> >
>> > For everybody else, this patch is already pushed by Rafael in his linux-next
>> > branch.
>>
>> Well, not yet, although I'm going to do that.
>
> Or I would if it still applied. Unfortunately, though, it doesn't apply any
> more to my linux-next branch due to some previous changes in it.
>
> Care to rebase?
Ahh.. I got confused by the following patch:
commit 83a73f712f2275033b2dc7f5c664988a1823ebc7
Author: viresh kumar <viresh.kumar@linaro.org>
Date: Tue Oct 23 01:28:05 2012 +0200
cpufreq: Move common part from governors to separate file, v2
Multiple cpufreq governers have defined similar get_cpu_idle_time_***()
routines. These routines must be moved to some common place, so that all
governors can use them.
So moving them to cpufreq_governor.c, which seems to be a better place for
keeping these routines.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Actually, i should i have replied on this patch (and i forgot). I
wanted you to skip
this patch, as the latest patch already had this change.
But now i see commits from others on cpufreq_governor.c file.
Hmm... So you can keep your tree as it is and apply the attached
patch. It is the
same patch getting discussed in this thread. Just rebased over your latest next.
You must also apply my other patches fixing sparse warnings (they can be applied
over this patch, tested), to fix sparse warnings from cpufreq.
--
viresh
[-- Attachment #2: 0001-cpufreq-governors-remove-redundant-code.patch --]
[-- Type: application/octet-stream, Size: 67140 bytes --]
From e89da420892e5144d438345c29144b6bc9a1f438 Mon Sep 17 00:00:00 2001
Message-Id: <e89da420892e5144d438345c29144b6bc9a1f438.1351135447.git.viresh.kumar@linaro.org>
From: Viresh Kumar <viresh.kumar@linaro.org>
Date: Mon, 15 Oct 2012 11:28:34 +0530
Subject: [PATCH] cpufreq: governors: remove redundant code
Initially ondemand governor was written and then using its code conservative
governor is written. It used a lot of code from ondemand governor, but copy of
code was created instead of using the same routines from both governors. Which
increased code redundancy, which is difficult to manage.
This patch is an attempt to move common part of both the governors to
cpufreq_governor.c file to come over above mentioned issues.
This shouldn't change anything from functionality point of view.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/cpufreq/cpufreq_conservative.c | 548 ++++++++------------------
drivers/cpufreq/cpufreq_governor.c | 276 ++++++++++++-
drivers/cpufreq/cpufreq_governor.h | 177 +++++++++
drivers/cpufreq/cpufreq_ondemand.c | 698 +++++++++++----------------------
include/linux/cpufreq.h | 6 -
5 files changed, 830 insertions(+), 875 deletions(-)
create mode 100644 drivers/cpufreq/cpufreq_governor.h
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 181abad..64ef737 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -11,83 +11,30 @@
* published by the Free Software Foundation.
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
#include <linux/cpufreq.h>
-#include <linux/cpu.h>
-#include <linux/jiffies.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
#include <linux/kernel_stat.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/hrtimer.h>
-#include <linux/tick.h>
-#include <linux/ktime.h>
-#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/percpu-defs.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
-/*
- * dbs is used in this file as a shortform for demandbased switching
- * It helps to keep variable names smaller, simpler
- */
+#include "cpufreq_governor.h"
+/* Conservative governor macors */
#define DEF_FREQUENCY_UP_THRESHOLD (80)
#define DEF_FREQUENCY_DOWN_THRESHOLD (20)
-
-/*
- * The polling frequency of this governor depends on the capability of
- * the processor. Default polling frequency is 1000 times the transition
- * latency of the processor. The governor will work on any processor with
- * transition latency <= 10mS, using appropriate sampling
- * rate.
- * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL)
- * this governor will not work.
- * All times here are in uS.
- */
-#define MIN_SAMPLING_RATE_RATIO (2)
-
-static unsigned int min_sampling_rate;
-
-#define LATENCY_MULTIPLIER (1000)
-#define MIN_LATENCY_MULTIPLIER (100)
#define DEF_SAMPLING_DOWN_FACTOR (1)
#define MAX_SAMPLING_DOWN_FACTOR (10)
-#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000)
-
-static void do_dbs_timer(struct work_struct *work);
-
-struct cpu_dbs_info_s {
- cputime64_t prev_cpu_idle;
- cputime64_t prev_cpu_wall;
- cputime64_t prev_cpu_nice;
- struct cpufreq_policy *cur_policy;
- struct delayed_work work;
- unsigned int down_skip;
- unsigned int requested_freq;
- int cpu;
- unsigned int enable:1;
- /*
- * percpu mutex that serializes governor limit change with
- * do_dbs_timer invocation. We do not want do_dbs_timer to run
- * when user is changing the governor or limits.
- */
- struct mutex timer_mutex;
-};
-static DEFINE_PER_CPU(struct cpu_dbs_info_s, cs_cpu_dbs_info);
-static unsigned int dbs_enable; /* number of CPUs using this policy */
+static struct dbs_data cs_dbs_data;
+static DEFINE_PER_CPU(struct cs_cpu_dbs_info_s, cs_cpu_dbs_info);
-/*
- * dbs_mutex protects dbs_enable in governor start/stop.
- */
-static DEFINE_MUTEX(dbs_mutex);
-
-static struct dbs_tuners {
- unsigned int sampling_rate;
- unsigned int sampling_down_factor;
- unsigned int up_threshold;
- unsigned int down_threshold;
- unsigned int ignore_nice;
- unsigned int freq_step;
-} dbs_tuners_ins = {
+static struct cs_dbs_tuners cs_tuners = {
.up_threshold = DEF_FREQUENCY_UP_THRESHOLD,
.down_threshold = DEF_FREQUENCY_DOWN_THRESHOLD,
.sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR,
@@ -95,61 +42,121 @@ static struct dbs_tuners {
.freq_step = 5,
};
-/* keep track of frequency transitions */
-static int
-dbs_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
- void *data)
+/*
+ * Every sampling_rate, we check, if current idle time is less than 20%
+ * (default), then we try to increase frequency Every sampling_rate *
+ * sampling_down_factor, we check, if current idle time is more than 80%, then
+ * we try to decrease frequency
+ *
+ * Any frequency increase takes it to the maximum frequency. Frequency reduction
+ * happens at minimum steps of 5% (default) of maximum frequency
+ */
+static void cs_check_cpu(int cpu, unsigned int load)
{
- struct cpufreq_freqs *freq = data;
- struct cpu_dbs_info_s *this_dbs_info = &per_cpu(cs_cpu_dbs_info,
- freq->cpu);
+ struct cs_cpu_dbs_info_s *dbs_info = &per_cpu(cs_cpu_dbs_info, cpu);
+ struct cpufreq_policy *policy = dbs_info->cdbs.cur_policy;
+ unsigned int freq_target;
+
+ /*
+ * break out if we 'cannot' reduce the speed as the user might
+ * want freq_step to be zero
+ */
+ if (cs_tuners.freq_step == 0)
+ return;
+
+ /* Check for frequency increase */
+ if (load > cs_tuners.up_threshold) {
+ dbs_info->down_skip = 0;
+
+ /* if we are already at full speed then break out early */
+ if (dbs_info->requested_freq == policy->max)
+ return;
+
+ freq_target = (cs_tuners.freq_step * policy->max) / 100;
+
+ /* max freq cannot be less than 100. But who knows.... */
+ if (unlikely(freq_target == 0))
+ freq_target = 5;
+
+ dbs_info->requested_freq += freq_target;
+ if (dbs_info->requested_freq > policy->max)
+ dbs_info->requested_freq = policy->max;
+ __cpufreq_driver_target(policy, dbs_info->requested_freq,
+ CPUFREQ_RELATION_H);
+ return;
+ }
+
+ /*
+ * The optimal frequency is the frequency that is the lowest that can
+ * support the current CPU usage without triggering the up policy. To be
+ * safe, we focus 10 points under the threshold.
+ */
+ if (load < (cs_tuners.down_threshold - 10)) {
+ freq_target = (cs_tuners.freq_step * policy->max) / 100;
+
+ dbs_info->requested_freq -= freq_target;
+ if (dbs_info->requested_freq < policy->min)
+ dbs_info->requested_freq = policy->min;
+
+ /*
+ * if we cannot reduce the frequency anymore, break out early
+ */
+ if (policy->cur == policy->min)
+ return;
+
+ __cpufreq_driver_target(policy, dbs_info->requested_freq,
+ CPUFREQ_RELATION_H);
+ return;
+ }
+}
+
+static void cs_dbs_timer(struct work_struct *work)
+{
+ struct cs_cpu_dbs_info_s *dbs_info = container_of(work,
+ struct cs_cpu_dbs_info_s, cdbs.work.work);
+ unsigned int cpu = dbs_info->cdbs.cpu;
+ int delay = delay_for_sampling_rate(cs_tuners.sampling_rate);
+
+ mutex_lock(&dbs_info->cdbs.timer_mutex);
+
+ dbs_check_cpu(&cs_dbs_data, cpu);
+
+ schedule_delayed_work_on(cpu, &dbs_info->cdbs.work, delay);
+ mutex_unlock(&dbs_info->cdbs.timer_mutex);
+}
+
+static int dbs_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ struct cpufreq_freqs *freq = data;
+ struct cs_cpu_dbs_info_s *dbs_info =
+ &per_cpu(cs_cpu_dbs_info, freq->cpu);
struct cpufreq_policy *policy;
- if (!this_dbs_info->enable)
+ if (!dbs_info->enable)
return 0;
- policy = this_dbs_info->cur_policy;
+ policy = dbs_info->cdbs.cur_policy;
/*
- * we only care if our internally tracked freq moves outside
- * the 'valid' ranges of freqency available to us otherwise
- * we do not change it
+ * we only care if our internally tracked freq moves outside the 'valid'
+ * ranges of freqency available to us otherwise we do not change it
*/
- if (this_dbs_info->requested_freq > policy->max
- || this_dbs_info->requested_freq < policy->min)
- this_dbs_info->requested_freq = freq->new;
+ if (dbs_info->requested_freq > policy->max
+ || dbs_info->requested_freq < policy->min)
+ dbs_info->requested_freq = freq->new;
return 0;
}
-static struct notifier_block dbs_cpufreq_notifier_block = {
- .notifier_call = dbs_cpufreq_notifier
-};
-
/************************** sysfs interface ************************/
static ssize_t show_sampling_rate_min(struct kobject *kobj,
struct attribute *attr, char *buf)
{
- return sprintf(buf, "%u\n", min_sampling_rate);
+ return sprintf(buf, "%u\n", cs_dbs_data.min_sampling_rate);
}
-define_one_global_ro(sampling_rate_min);
-
-/* cpufreq_conservative Governor Tunables */
-#define show_one(file_name, object) \
-static ssize_t show_##file_name \
-(struct kobject *kobj, struct attribute *attr, char *buf) \
-{ \
- return sprintf(buf, "%u\n", dbs_tuners_ins.object); \
-}
-show_one(sampling_rate, sampling_rate);
-show_one(sampling_down_factor, sampling_down_factor);
-show_one(up_threshold, up_threshold);
-show_one(down_threshold, down_threshold);
-show_one(ignore_nice_load, ignore_nice);
-show_one(freq_step, freq_step);
-
static ssize_t store_sampling_down_factor(struct kobject *a,
struct attribute *b,
const char *buf, size_t count)
@@ -161,7 +168,7 @@ static ssize_t store_sampling_down_factor(struct kobject *a,
if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1)
return -EINVAL;
- dbs_tuners_ins.sampling_down_factor = input;
+ cs_tuners.sampling_down_factor = input;
return count;
}
@@ -175,7 +182,7 @@ static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b,
if (ret != 1)
return -EINVAL;
- dbs_tuners_ins.sampling_rate = max(input, min_sampling_rate);
+ cs_tuners.sampling_rate = max(input, cs_dbs_data.min_sampling_rate);
return count;
}
@@ -186,11 +193,10 @@ static ssize_t store_up_threshold(struct kobject *a, struct attribute *b,
int ret;
ret = sscanf(buf, "%u", &input);
- if (ret != 1 || input > 100 ||
- input <= dbs_tuners_ins.down_threshold)
+ if (ret != 1 || input > 100 || input <= cs_tuners.down_threshold)
return -EINVAL;
- dbs_tuners_ins.up_threshold = input;
+ cs_tuners.up_threshold = input;
return count;
}
@@ -203,21 +209,19 @@ static ssize_t store_down_threshold(struct kobject *a, struct attribute *b,
/* cannot be lower than 11 otherwise freq will not fall */
if (ret != 1 || input < 11 || input > 100 ||
- input >= dbs_tuners_ins.up_threshold)
+ input >= cs_tuners.up_threshold)
return -EINVAL;
- dbs_tuners_ins.down_threshold = input;
+ cs_tuners.down_threshold = input;
return count;
}
static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
{
- unsigned int input;
+ unsigned int input, j;
int ret;
- unsigned int j;
-
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
@@ -225,19 +229,20 @@ static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b,
if (input > 1)
input = 1;
- if (input == dbs_tuners_ins.ignore_nice) /* nothing to do */
+ if (input == cs_tuners.ignore_nice) /* nothing to do */
return count;
- dbs_tuners_ins.ignore_nice = input;
+ cs_tuners.ignore_nice = input;
/* we need to re-evaluate prev_cpu_idle */
for_each_online_cpu(j) {
- struct cpu_dbs_info_s *dbs_info;
+ struct cs_cpu_dbs_info_s *dbs_info;
dbs_info = &per_cpu(cs_cpu_dbs_info, j);
- dbs_info->prev_cpu_idle = get_cpu_idle_time(j,
- &dbs_info->prev_cpu_wall);
- if (dbs_tuners_ins.ignore_nice)
- dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE];
+ dbs_info->cdbs.prev_cpu_idle = get_cpu_idle_time(j,
+ &dbs_info->cdbs.prev_cpu_wall);
+ if (cs_tuners.ignore_nice)
+ dbs_info->cdbs.prev_cpu_nice =
+ kcpustat_cpu(j).cpustat[CPUTIME_NICE];
}
return count;
}
@@ -255,18 +260,28 @@ static ssize_t store_freq_step(struct kobject *a, struct attribute *b,
if (input > 100)
input = 100;
- /* no need to test here if freq_step is zero as the user might actually
- * want this, they would be crazy though :) */
- dbs_tuners_ins.freq_step = input;
+ /*
+ * no need to test here if freq_step is zero as the user might actually
+ * want this, they would be crazy though :)
+ */
+ cs_tuners.freq_step = input;
return count;
}
+show_one(cs, sampling_rate, sampling_rate);
+show_one(cs, sampling_down_factor, sampling_down_factor);
+show_one(cs, up_threshold, up_threshold);
+show_one(cs, down_threshold, down_threshold);
+show_one(cs, ignore_nice_load, ignore_nice);
+show_one(cs, freq_step, freq_step);
+
define_one_global_rw(sampling_rate);
define_one_global_rw(sampling_down_factor);
define_one_global_rw(up_threshold);
define_one_global_rw(down_threshold);
define_one_global_rw(ignore_nice_load);
define_one_global_rw(freq_step);
+define_one_global_ro(sampling_rate_min);
static struct attribute *dbs_attributes[] = {
&sampling_rate_min.attr,
@@ -279,283 +294,38 @@ static struct attribute *dbs_attributes[] = {
NULL
};
-static struct attribute_group dbs_attr_group = {
+static struct attribute_group cs_attr_group = {
.attrs = dbs_attributes,
.name = "conservative",
};
/************************** sysfs end ************************/
-static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
-{
- unsigned int load = 0;
- unsigned int max_load = 0;
- unsigned int freq_target;
-
- struct cpufreq_policy *policy;
- unsigned int j;
-
- policy = this_dbs_info->cur_policy;
-
- /*
- * Every sampling_rate, we check, if current idle time is less
- * than 20% (default), then we try to increase frequency
- * Every sampling_rate*sampling_down_factor, we check, if current
- * idle time is more than 80%, then we try to decrease frequency
- *
- * Any frequency increase takes it to the maximum frequency.
- * Frequency reduction happens at minimum steps of
- * 5% (default) of maximum frequency
- */
-
- /* Get Absolute Load */
- for_each_cpu(j, policy->cpus) {
- struct cpu_dbs_info_s *j_dbs_info;
- cputime64_t cur_wall_time, cur_idle_time;
- unsigned int idle_time, wall_time;
-
- j_dbs_info = &per_cpu(cs_cpu_dbs_info, j);
-
- cur_idle_time = get_cpu_idle_time(j, &cur_wall_time);
-
- wall_time = (unsigned int)
- (cur_wall_time - j_dbs_info->prev_cpu_wall);
- j_dbs_info->prev_cpu_wall = cur_wall_time;
-
- idle_time = (unsigned int)
- (cur_idle_time - j_dbs_info->prev_cpu_idle);
- j_dbs_info->prev_cpu_idle = cur_idle_time;
-
- if (dbs_tuners_ins.ignore_nice) {
- u64 cur_nice;
- unsigned long cur_nice_jiffies;
-
- cur_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE] -
- j_dbs_info->prev_cpu_nice;
- /*
- * Assumption: nice time between sampling periods will
- * be less than 2^32 jiffies for 32 bit sys
- */
- cur_nice_jiffies = (unsigned long)
- cputime64_to_jiffies64(cur_nice);
-
- j_dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE];
- idle_time += jiffies_to_usecs(cur_nice_jiffies);
- }
-
- if (unlikely(!wall_time || wall_time < idle_time))
- continue;
-
- load = 100 * (wall_time - idle_time) / wall_time;
-
- if (load > max_load)
- max_load = load;
- }
+define_get_cpu_dbs_routines(cs_cpu_dbs_info);
- /*
- * break out if we 'cannot' reduce the speed as the user might
- * want freq_step to be zero
- */
- if (dbs_tuners_ins.freq_step == 0)
- return;
-
- /* Check for frequency increase */
- if (max_load > dbs_tuners_ins.up_threshold) {
- this_dbs_info->down_skip = 0;
-
- /* if we are already at full speed then break out early */
- if (this_dbs_info->requested_freq == policy->max)
- return;
-
- freq_target = (dbs_tuners_ins.freq_step * policy->max) / 100;
-
- /* max freq cannot be less than 100. But who knows.... */
- if (unlikely(freq_target == 0))
- freq_target = 5;
-
- this_dbs_info->requested_freq += freq_target;
- if (this_dbs_info->requested_freq > policy->max)
- this_dbs_info->requested_freq = policy->max;
-
- __cpufreq_driver_target(policy, this_dbs_info->requested_freq,
- CPUFREQ_RELATION_H);
- return;
- }
-
- /*
- * The optimal frequency is the frequency that is the lowest that
- * can support the current CPU usage without triggering the up
- * policy. To be safe, we focus 10 points under the threshold.
- */
- if (max_load < (dbs_tuners_ins.down_threshold - 10)) {
- freq_target = (dbs_tuners_ins.freq_step * policy->max) / 100;
-
- this_dbs_info->requested_freq -= freq_target;
- if (this_dbs_info->requested_freq < policy->min)
- this_dbs_info->requested_freq = policy->min;
-
- /*
- * if we cannot reduce the frequency anymore, break out early
- */
- if (policy->cur == policy->min)
- return;
-
- __cpufreq_driver_target(policy, this_dbs_info->requested_freq,
- CPUFREQ_RELATION_H);
- return;
- }
-}
-
-static void do_dbs_timer(struct work_struct *work)
-{
- struct cpu_dbs_info_s *dbs_info =
- container_of(work, struct cpu_dbs_info_s, work.work);
- unsigned int cpu = dbs_info->cpu;
-
- /* We want all CPUs to do sampling nearly on same jiffy */
- int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
-
- delay -= jiffies % delay;
-
- mutex_lock(&dbs_info->timer_mutex);
-
- dbs_check_cpu(dbs_info);
-
- schedule_delayed_work_on(cpu, &dbs_info->work, delay);
- mutex_unlock(&dbs_info->timer_mutex);
-}
-
-static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
-{
- /* We want all CPUs to do sampling nearly on same jiffy */
- int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
- delay -= jiffies % delay;
+static struct notifier_block cs_cpufreq_notifier_block = {
+ .notifier_call = dbs_cpufreq_notifier,
+};
- dbs_info->enable = 1;
- INIT_DEFERRABLE_WORK(&dbs_info->work, do_dbs_timer);
- schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, delay);
-}
+static struct cs_ops cs_ops = {
+ .notifier_block = &cs_cpufreq_notifier_block,
+};
-static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
-{
- dbs_info->enable = 0;
- cancel_delayed_work_sync(&dbs_info->work);
-}
+static struct dbs_data cs_dbs_data = {
+ .governor = GOV_CONSERVATIVE,
+ .attr_group = &cs_attr_group,
+ .tuners = &cs_tuners,
+ .get_cpu_cdbs = get_cpu_cdbs,
+ .get_cpu_dbs_info_s = get_cpu_dbs_info_s,
+ .gov_dbs_timer = cs_dbs_timer,
+ .gov_check_cpu = cs_check_cpu,
+ .gov_ops = &cs_ops,
+};
-static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
+static int cs_cpufreq_governor_dbs(struct cpufreq_policy *policy,
unsigned int event)
{
- unsigned int cpu = policy->cpu;
- struct cpu_dbs_info_s *this_dbs_info;
- unsigned int j;
- int rc;
-
- this_dbs_info = &per_cpu(cs_cpu_dbs_info, cpu);
-
- switch (event) {
- case CPUFREQ_GOV_START:
- if ((!cpu_online(cpu)) || (!policy->cur))
- return -EINVAL;
-
- mutex_lock(&dbs_mutex);
-
- for_each_cpu(j, policy->cpus) {
- struct cpu_dbs_info_s *j_dbs_info;
- j_dbs_info = &per_cpu(cs_cpu_dbs_info, j);
- j_dbs_info->cur_policy = policy;
-
- j_dbs_info->prev_cpu_idle = get_cpu_idle_time(j,
- &j_dbs_info->prev_cpu_wall);
- if (dbs_tuners_ins.ignore_nice)
- j_dbs_info->prev_cpu_nice =
- kcpustat_cpu(j).cpustat[CPUTIME_NICE];
- }
- this_dbs_info->cpu = cpu;
- this_dbs_info->down_skip = 0;
- this_dbs_info->requested_freq = policy->cur;
-
- mutex_init(&this_dbs_info->timer_mutex);
- dbs_enable++;
- /*
- * Start the timerschedule work, when this governor
- * is used for first time
- */
- if (dbs_enable == 1) {
- unsigned int latency;
- /* policy latency is in nS. Convert it to uS first */
- latency = policy->cpuinfo.transition_latency / 1000;
- if (latency == 0)
- latency = 1;
-
- rc = sysfs_create_group(cpufreq_global_kobject,
- &dbs_attr_group);
- if (rc) {
- mutex_unlock(&dbs_mutex);
- return rc;
- }
-
- /*
- * conservative does not implement micro like ondemand
- * governor, thus we are bound to jiffes/HZ
- */
- min_sampling_rate =
- MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10);
- /* Bring kernel and HW constraints together */
- min_sampling_rate = max(min_sampling_rate,
- MIN_LATENCY_MULTIPLIER * latency);
- dbs_tuners_ins.sampling_rate =
- max(min_sampling_rate,
- latency * LATENCY_MULTIPLIER);
-
- cpufreq_register_notifier(
- &dbs_cpufreq_notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
- }
- mutex_unlock(&dbs_mutex);
-
- dbs_timer_init(this_dbs_info);
-
- break;
-
- case CPUFREQ_GOV_STOP:
- dbs_timer_exit(this_dbs_info);
-
- mutex_lock(&dbs_mutex);
- dbs_enable--;
- mutex_destroy(&this_dbs_info->timer_mutex);
-
- /*
- * Stop the timerschedule work, when this governor
- * is used for first time
- */
- if (dbs_enable == 0)
- cpufreq_unregister_notifier(
- &dbs_cpufreq_notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
-
- mutex_unlock(&dbs_mutex);
- if (!dbs_enable)
- sysfs_remove_group(cpufreq_global_kobject,
- &dbs_attr_group);
-
- break;
-
- case CPUFREQ_GOV_LIMITS:
- mutex_lock(&this_dbs_info->timer_mutex);
- if (policy->max < this_dbs_info->cur_policy->cur)
- __cpufreq_driver_target(
- this_dbs_info->cur_policy,
- policy->max, CPUFREQ_RELATION_H);
- else if (policy->min > this_dbs_info->cur_policy->cur)
- __cpufreq_driver_target(
- this_dbs_info->cur_policy,
- policy->min, CPUFREQ_RELATION_L);
- dbs_check_cpu(this_dbs_info);
- mutex_unlock(&this_dbs_info->timer_mutex);
-
- break;
- }
- return 0;
+ return cpufreq_governor_dbs(&cs_dbs_data, policy, event);
}
#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE
@@ -563,13 +333,14 @@ static
#endif
struct cpufreq_governor cpufreq_gov_conservative = {
.name = "conservative",
- .governor = cpufreq_governor_dbs,
+ .governor = cs_cpufreq_governor_dbs,
.max_transition_latency = TRANSITION_LATENCY_LIMIT,
.owner = THIS_MODULE,
};
static int __init cpufreq_gov_dbs_init(void)
{
+ mutex_init(&cs_dbs_data.mutex);
return cpufreq_register_governor(&cpufreq_gov_conservative);
}
@@ -578,7 +349,6 @@ static void __exit cpufreq_gov_dbs_exit(void)
cpufreq_unregister_governor(&cpufreq_gov_conservative);
}
-
MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>");
MODULE_DESCRIPTION("'cpufreq_conservative' - A dynamic cpufreq governor for "
"Low Latency Frequency Transition capable processors "
diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c
index 679842a..cd5fe57 100644
--- a/drivers/cpufreq/cpufreq_governor.c
+++ b/drivers/cpufreq/cpufreq_governor.c
@@ -3,19 +3,30 @@
*
* CPUFREQ governors common code
*
+ * Copyright (C) 2001 Russell King
+ * (C) 2003 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
+ * (C) 2003 Jun Nakajima <jun.nakajima@intel.com>
+ * (C) 2009 Alexander Clouter <alex@digriz.org.uk>
+ * (c) 2012 Viresh Kumar <viresh.kumar@linaro.org>
+ *
* 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.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <asm/cputime.h>
-#include <linux/export.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
#include <linux/kernel_stat.h>
+#include <linux/mutex.h>
#include <linux/tick.h>
#include <linux/types.h>
-/*
- * Code picked from earlier governer implementations
- */
+#include <linux/workqueue.h>
+
+#include "cpufreq_governor.h"
+
static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall)
{
u64 idle_time;
@@ -33,9 +44,9 @@ static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall)
idle_time = cur_wall_time - busy_time;
if (wall)
- *wall = cputime_to_usecs(cur_wall_time);
+ *wall = jiffies_to_usecs(cur_wall_time);
- return cputime_to_usecs(idle_time);
+ return jiffies_to_usecs(idle_time);
}
cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall)
@@ -49,4 +60,255 @@ cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall)
return idle_time;
}
-EXPORT_SYMBOL_GPL(get_cpu_idle_time);
+
+void dbs_check_cpu(struct dbs_data *dbs_data, int cpu)
+{
+ struct cpu_dbs_common_info *cdbs = dbs_data->get_cpu_cdbs(cpu);
+ struct od_dbs_tuners *od_tuners = dbs_data->tuners;
+ struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
+ struct cpufreq_policy *policy;
+ unsigned int max_load = 0;
+ unsigned int ignore_nice;
+ unsigned int j;
+
+ if (dbs_data->governor == GOV_ONDEMAND)
+ ignore_nice = od_tuners->ignore_nice;
+ else
+ ignore_nice = cs_tuners->ignore_nice;
+
+ policy = cdbs->cur_policy;
+
+ /* Get Absolute Load (in terms of freq for ondemand gov) */
+ for_each_cpu(j, policy->cpus) {
+ struct cpu_dbs_common_info *j_cdbs;
+ cputime64_t cur_wall_time, cur_idle_time, cur_iowait_time;
+ unsigned int idle_time, wall_time, iowait_time;
+ unsigned int load;
+
+ j_cdbs = dbs_data->get_cpu_cdbs(j);
+
+ cur_idle_time = get_cpu_idle_time(j, &cur_wall_time);
+
+ wall_time = (unsigned int)
+ (cur_wall_time - j_cdbs->prev_cpu_wall);
+ j_cdbs->prev_cpu_wall = cur_wall_time;
+
+ idle_time = (unsigned int)
+ (cur_idle_time - j_cdbs->prev_cpu_idle);
+ j_cdbs->prev_cpu_idle = cur_idle_time;
+
+ if (ignore_nice) {
+ u64 cur_nice;
+ unsigned long cur_nice_jiffies;
+
+ cur_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE] -
+ cdbs->prev_cpu_nice;
+ /*
+ * Assumption: nice time between sampling periods will
+ * be less than 2^32 jiffies for 32 bit sys
+ */
+ cur_nice_jiffies = (unsigned long)
+ cputime64_to_jiffies64(cur_nice);
+
+ cdbs->prev_cpu_nice =
+ kcpustat_cpu(j).cpustat[CPUTIME_NICE];
+ idle_time += jiffies_to_usecs(cur_nice_jiffies);
+ }
+
+ if (dbs_data->governor == GOV_ONDEMAND) {
+ struct od_cpu_dbs_info_s *od_j_dbs_info =
+ dbs_data->get_cpu_dbs_info_s(cpu);
+
+ cur_iowait_time = get_cpu_iowait_time_us(j,
+ &cur_wall_time);
+ if (cur_iowait_time == -1ULL)
+ cur_iowait_time = 0;
+
+ iowait_time = (unsigned int) (cur_iowait_time -
+ od_j_dbs_info->prev_cpu_iowait);
+ od_j_dbs_info->prev_cpu_iowait = cur_iowait_time;
+
+ /*
+ * For the purpose of ondemand, waiting for disk IO is
+ * an indication that you're performance critical, and
+ * not that the system is actually idle. So subtract the
+ * iowait time from the cpu idle time.
+ */
+ if (od_tuners->io_is_busy && idle_time >= iowait_time)
+ idle_time -= iowait_time;
+ }
+
+ if (unlikely(!wall_time || wall_time < idle_time))
+ continue;
+
+ load = 100 * (wall_time - idle_time) / wall_time;
+
+ if (dbs_data->governor == GOV_ONDEMAND) {
+ int freq_avg = __cpufreq_driver_getavg(policy, j);
+ if (freq_avg <= 0)
+ freq_avg = policy->cur;
+
+ load *= freq_avg;
+ }
+
+ if (load > max_load)
+ max_load = load;
+ }
+
+ dbs_data->gov_check_cpu(cpu, max_load);
+}
+
+static inline void dbs_timer_init(struct dbs_data *dbs_data,
+ struct cpu_dbs_common_info *cdbs, unsigned int sampling_rate)
+{
+ int delay = delay_for_sampling_rate(sampling_rate);
+
+ INIT_DEFERRABLE_WORK(&cdbs->work, dbs_data->gov_dbs_timer);
+ schedule_delayed_work_on(cdbs->cpu, &cdbs->work, delay);
+}
+
+static inline void dbs_timer_exit(struct cpu_dbs_common_info *cdbs)
+{
+ cancel_delayed_work_sync(&cdbs->work);
+}
+
+int cpufreq_governor_dbs(struct dbs_data *dbs_data,
+ struct cpufreq_policy *policy, unsigned int event)
+{
+ struct od_cpu_dbs_info_s *od_dbs_info = NULL;
+ struct cs_cpu_dbs_info_s *cs_dbs_info = NULL;
+ struct od_dbs_tuners *od_tuners = dbs_data->tuners;
+ struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
+ struct cpu_dbs_common_info *cpu_cdbs;
+ unsigned int *sampling_rate, latency, ignore_nice, j, cpu = policy->cpu;
+ int rc;
+
+ cpu_cdbs = dbs_data->get_cpu_cdbs(cpu);
+
+ if (dbs_data->governor == GOV_CONSERVATIVE) {
+ cs_dbs_info = dbs_data->get_cpu_dbs_info_s(cpu);
+ sampling_rate = &cs_tuners->sampling_rate;
+ ignore_nice = cs_tuners->ignore_nice;
+ } else {
+ od_dbs_info = dbs_data->get_cpu_dbs_info_s(cpu);
+ sampling_rate = &od_tuners->sampling_rate;
+ ignore_nice = od_tuners->ignore_nice;
+ }
+
+ switch (event) {
+ case CPUFREQ_GOV_START:
+ if ((!cpu_online(cpu)) || (!policy->cur))
+ return -EINVAL;
+
+ mutex_lock(&dbs_data->mutex);
+
+ dbs_data->enable++;
+ cpu_cdbs->cpu = cpu;
+ for_each_cpu(j, policy->cpus) {
+ struct cpu_dbs_common_info *j_cdbs;
+ j_cdbs = dbs_data->get_cpu_cdbs(j);
+
+ j_cdbs->cur_policy = policy;
+ j_cdbs->prev_cpu_idle = get_cpu_idle_time(j,
+ &j_cdbs->prev_cpu_wall);
+ if (ignore_nice)
+ j_cdbs->prev_cpu_nice =
+ kcpustat_cpu(j).cpustat[CPUTIME_NICE];
+ }
+
+ /*
+ * Start the timerschedule work, when this governor is used for
+ * first time
+ */
+ if (dbs_data->enable != 1)
+ goto second_time;
+
+ rc = sysfs_create_group(cpufreq_global_kobject,
+ dbs_data->attr_group);
+ if (rc) {
+ mutex_unlock(&dbs_data->mutex);
+ return rc;
+ }
+
+ /* policy latency is in nS. Convert it to uS first */
+ latency = policy->cpuinfo.transition_latency / 1000;
+ if (latency == 0)
+ latency = 1;
+
+ /*
+ * conservative does not implement micro like ondemand
+ * governor, thus we are bound to jiffes/HZ
+ */
+ if (dbs_data->governor == GOV_CONSERVATIVE) {
+ struct cs_ops *ops = dbs_data->gov_ops;
+
+ cpufreq_register_notifier(ops->notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
+ dbs_data->min_sampling_rate = MIN_SAMPLING_RATE_RATIO *
+ jiffies_to_usecs(10);
+ } else {
+ struct od_ops *ops = dbs_data->gov_ops;
+
+ od_tuners->io_is_busy = ops->io_busy();
+ }
+
+ /* Bring kernel and HW constraints together */
+ dbs_data->min_sampling_rate = max(dbs_data->min_sampling_rate,
+ MIN_LATENCY_MULTIPLIER * latency);
+ *sampling_rate = max(dbs_data->min_sampling_rate, latency *
+ LATENCY_MULTIPLIER);
+
+second_time:
+ if (dbs_data->governor == GOV_CONSERVATIVE) {
+ cs_dbs_info->down_skip = 0;
+ cs_dbs_info->enable = 1;
+ cs_dbs_info->requested_freq = policy->cur;
+ } else {
+ struct od_ops *ops = dbs_data->gov_ops;
+ od_dbs_info->rate_mult = 1;
+ od_dbs_info->sample_type = OD_NORMAL_SAMPLE;
+ ops->powersave_bias_init_cpu(cpu);
+ }
+ mutex_unlock(&dbs_data->mutex);
+
+ mutex_init(&cpu_cdbs->timer_mutex);
+ dbs_timer_init(dbs_data, cpu_cdbs, *sampling_rate);
+ break;
+
+ case CPUFREQ_GOV_STOP:
+ if (dbs_data->governor == GOV_CONSERVATIVE)
+ cs_dbs_info->enable = 0;
+
+ dbs_timer_exit(cpu_cdbs);
+
+ mutex_lock(&dbs_data->mutex);
+ mutex_destroy(&cpu_cdbs->timer_mutex);
+ dbs_data->enable--;
+ if (!dbs_data->enable) {
+ struct cs_ops *ops = dbs_data->gov_ops;
+
+ sysfs_remove_group(cpufreq_global_kobject,
+ dbs_data->attr_group);
+ if (dbs_data->governor == GOV_CONSERVATIVE)
+ cpufreq_unregister_notifier(ops->notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+ }
+ mutex_unlock(&dbs_data->mutex);
+
+ break;
+
+ case CPUFREQ_GOV_LIMITS:
+ mutex_lock(&cpu_cdbs->timer_mutex);
+ if (policy->max < cpu_cdbs->cur_policy->cur)
+ __cpufreq_driver_target(cpu_cdbs->cur_policy,
+ policy->max, CPUFREQ_RELATION_H);
+ else if (policy->min > cpu_cdbs->cur_policy->cur)
+ __cpufreq_driver_target(cpu_cdbs->cur_policy,
+ policy->min, CPUFREQ_RELATION_L);
+ dbs_check_cpu(dbs_data, cpu);
+ mutex_unlock(&cpu_cdbs->timer_mutex);
+ break;
+ }
+ return 0;
+}
diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h
new file mode 100644
index 0000000..34e14ad
--- /dev/null
+++ b/drivers/cpufreq/cpufreq_governor.h
@@ -0,0 +1,177 @@
+/*
+ * drivers/cpufreq/cpufreq_governor.h
+ *
+ * Header file for CPUFreq governors common code
+ *
+ * Copyright (C) 2001 Russell King
+ * (C) 2003 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
+ * (C) 2003 Jun Nakajima <jun.nakajima@intel.com>
+ * (C) 2009 Alexander Clouter <alex@digriz.org.uk>
+ * (c) 2012 Viresh Kumar <viresh.kumar@linaro.org>
+ *
+ * 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.
+ */
+
+#ifndef _CPUFREQ_GOVERNER_H
+#define _CPUFREQ_GOVERNER_H
+
+#include <asm/cputime.h>
+#include <linux/cpufreq.h>
+#include <linux/kobject.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/sysfs.h>
+
+/*
+ * The polling frequency depends on the capability of the processor. Default
+ * polling frequency is 1000 times the transition latency of the processor. The
+ * governor will work on any processor with transition latency <= 10mS, using
+ * appropriate sampling rate.
+ *
+ * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL)
+ * this governor will not work. All times here are in uS.
+ */
+#define MIN_SAMPLING_RATE_RATIO (2)
+#define LATENCY_MULTIPLIER (1000)
+#define MIN_LATENCY_MULTIPLIER (100)
+#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000)
+
+/* Ondemand Sampling types */
+enum {OD_NORMAL_SAMPLE, OD_SUB_SAMPLE};
+
+/* Macro creating sysfs show routines */
+#define show_one(_gov, file_name, object) \
+static ssize_t show_##file_name \
+(struct kobject *kobj, struct attribute *attr, char *buf) \
+{ \
+ return sprintf(buf, "%u\n", _gov##_tuners.object); \
+}
+
+#define define_get_cpu_dbs_routines(_dbs_info) \
+static struct cpu_dbs_common_info *get_cpu_cdbs(int cpu) \
+{ \
+ return &per_cpu(_dbs_info, cpu).cdbs; \
+} \
+ \
+static void *get_cpu_dbs_info_s(int cpu) \
+{ \
+ return &per_cpu(_dbs_info, cpu); \
+}
+
+/*
+ * Abbreviations:
+ * dbs: used as a shortform for demand based switching It helps to keep variable
+ * names smaller, simpler
+ * cdbs: common dbs
+ * on_*: On-demand governor
+ * cs_*: Conservative governor
+ */
+
+/* Per cpu structures */
+struct cpu_dbs_common_info {
+ int cpu;
+ cputime64_t prev_cpu_idle;
+ cputime64_t prev_cpu_wall;
+ cputime64_t prev_cpu_nice;
+ struct cpufreq_policy *cur_policy;
+ struct delayed_work work;
+ /*
+ * percpu mutex that serializes governor limit change with gov_dbs_timer
+ * invocation. We do not want gov_dbs_timer to run when user is changing
+ * the governor or limits.
+ */
+ struct mutex timer_mutex;
+};
+
+struct od_cpu_dbs_info_s {
+ struct cpu_dbs_common_info cdbs;
+ cputime64_t prev_cpu_iowait;
+ struct cpufreq_frequency_table *freq_table;
+ unsigned int freq_lo;
+ unsigned int freq_lo_jiffies;
+ unsigned int freq_hi_jiffies;
+ unsigned int rate_mult;
+ unsigned int sample_type:1;
+};
+
+struct cs_cpu_dbs_info_s {
+ struct cpu_dbs_common_info cdbs;
+ unsigned int down_skip;
+ unsigned int requested_freq;
+ unsigned int enable:1;
+};
+
+/* Governers sysfs tunables */
+struct od_dbs_tuners {
+ unsigned int ignore_nice;
+ unsigned int sampling_rate;
+ unsigned int sampling_down_factor;
+ unsigned int up_threshold;
+ unsigned int down_differential;
+ unsigned int powersave_bias;
+ unsigned int io_is_busy;
+};
+
+struct cs_dbs_tuners {
+ unsigned int ignore_nice;
+ unsigned int sampling_rate;
+ unsigned int sampling_down_factor;
+ unsigned int up_threshold;
+ unsigned int down_threshold;
+ unsigned int freq_step;
+};
+
+/* Per Governer data */
+struct dbs_data {
+ /* Common across governors */
+ #define GOV_ONDEMAND 0
+ #define GOV_CONSERVATIVE 1
+ int governor;
+ unsigned int min_sampling_rate;
+ unsigned int enable; /* number of CPUs using this policy */
+ struct attribute_group *attr_group;
+ void *tuners;
+
+ /* dbs_mutex protects dbs_enable in governor start/stop */
+ struct mutex mutex;
+
+ struct cpu_dbs_common_info *(*get_cpu_cdbs)(int cpu);
+ void *(*get_cpu_dbs_info_s)(int cpu);
+ void (*gov_dbs_timer)(struct work_struct *work);
+ void (*gov_check_cpu)(int cpu, unsigned int load);
+
+ /* Governor specific ops, see below */
+ void *gov_ops;
+};
+
+/* Governor specific ops, will be passed to dbs_data->gov_ops */
+struct od_ops {
+ int (*io_busy)(void);
+ void (*powersave_bias_init_cpu)(int cpu);
+ unsigned int (*powersave_bias_target)(struct cpufreq_policy *policy,
+ unsigned int freq_next, unsigned int relation);
+ void (*freq_increase)(struct cpufreq_policy *p, unsigned int freq);
+};
+
+struct cs_ops {
+ struct notifier_block *notifier_block;
+};
+
+static inline int delay_for_sampling_rate(unsigned int sampling_rate)
+{
+ int delay = usecs_to_jiffies(sampling_rate);
+
+ /* We want all CPUs to do sampling nearly on same jiffy */
+ if (num_online_cpus() > 1)
+ delay -= jiffies % delay;
+
+ return delay;
+}
+
+cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall);
+void dbs_check_cpu(struct dbs_data *dbs_data, int cpu);
+int cpufreq_governor_dbs(struct dbs_data *dbs_data,
+ struct cpufreq_policy *policy, unsigned int event);
+#endif /* _CPUFREQ_GOVERNER_H */
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index d7f774b..bdaab92 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -10,24 +10,23 @@
* published by the Free Software Foundation.
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/cpufreq.h>
-#include <linux/cpu.h>
-#include <linux/jiffies.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
#include <linux/kernel_stat.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/hrtimer.h>
+#include <linux/percpu-defs.h>
+#include <linux/sysfs.h>
#include <linux/tick.h>
-#include <linux/ktime.h>
-#include <linux/sched.h>
+#include <linux/types.h>
-/*
- * dbs is used in this file as a shortform for demandbased switching
- * It helps to keep variable names smaller, simpler
- */
+#include "cpufreq_governor.h"
+/* On-demand governor macors */
#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10)
#define DEF_FREQUENCY_UP_THRESHOLD (80)
#define DEF_SAMPLING_DOWN_FACTOR (1)
@@ -38,80 +37,10 @@
#define MIN_FREQUENCY_UP_THRESHOLD (11)
#define MAX_FREQUENCY_UP_THRESHOLD (100)
-/*
- * The polling frequency of this governor depends on the capability of
- * the processor. Default polling frequency is 1000 times the transition
- * latency of the processor. The governor will work on any processor with
- * transition latency <= 10mS, using appropriate sampling
- * rate.
- * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL)
- * this governor will not work.
- * All times here are in uS.
- */
-#define MIN_SAMPLING_RATE_RATIO (2)
-
-static unsigned int min_sampling_rate;
-
-#define LATENCY_MULTIPLIER (1000)
-#define MIN_LATENCY_MULTIPLIER (100)
-#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000)
-
-static void do_dbs_timer(struct work_struct *work);
-static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
- unsigned int event);
-
-#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND
-static
-#endif
-struct cpufreq_governor cpufreq_gov_ondemand = {
- .name = "ondemand",
- .governor = cpufreq_governor_dbs,
- .max_transition_latency = TRANSITION_LATENCY_LIMIT,
- .owner = THIS_MODULE,
-};
-
-/* Sampling types */
-enum {DBS_NORMAL_SAMPLE, DBS_SUB_SAMPLE};
-
-struct cpu_dbs_info_s {
- cputime64_t prev_cpu_idle;
- cputime64_t prev_cpu_iowait;
- cputime64_t prev_cpu_wall;
- cputime64_t prev_cpu_nice;
- struct cpufreq_policy *cur_policy;
- struct delayed_work work;
- struct cpufreq_frequency_table *freq_table;
- unsigned int freq_lo;
- unsigned int freq_lo_jiffies;
- unsigned int freq_hi_jiffies;
- unsigned int rate_mult;
- int cpu;
- unsigned int sample_type:1;
- /*
- * percpu mutex that serializes governor limit change with
- * do_dbs_timer invocation. We do not want do_dbs_timer to run
- * when user is changing the governor or limits.
- */
- struct mutex timer_mutex;
-};
-static DEFINE_PER_CPU(struct cpu_dbs_info_s, od_cpu_dbs_info);
-
-static unsigned int dbs_enable; /* number of CPUs using this policy */
+static struct dbs_data od_dbs_data;
+static DEFINE_PER_CPU(struct od_cpu_dbs_info_s, od_cpu_dbs_info);
-/*
- * dbs_mutex protects dbs_enable in governor start/stop.
- */
-static DEFINE_MUTEX(dbs_mutex);
-
-static struct dbs_tuners {
- unsigned int sampling_rate;
- unsigned int up_threshold;
- unsigned int down_differential;
- unsigned int ignore_nice;
- unsigned int sampling_down_factor;
- unsigned int powersave_bias;
- unsigned int io_is_busy;
-} dbs_tuners_ins = {
+static struct od_dbs_tuners od_tuners = {
.up_threshold = DEF_FREQUENCY_UP_THRESHOLD,
.sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR,
.down_differential = DEF_FREQUENCY_DOWN_DIFFERENTIAL,
@@ -119,14 +48,35 @@ static struct dbs_tuners {
.powersave_bias = 0,
};
-static inline cputime64_t get_cpu_iowait_time(unsigned int cpu, cputime64_t *wall)
+static void ondemand_powersave_bias_init_cpu(int cpu)
{
- u64 iowait_time = get_cpu_iowait_time_us(cpu, wall);
+ struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
- if (iowait_time == -1ULL)
- return 0;
+ dbs_info->freq_table = cpufreq_frequency_get_table(cpu);
+ dbs_info->freq_lo = 0;
+}
- return iowait_time;
+/*
+ * Not all CPUs want IO time to be accounted as busy; this depends on how
+ * efficient idling at a higher frequency/voltage is.
+ * Pavel Machek says this is not so for various generations of AMD and old
+ * Intel systems.
+ * Mike Chan (androidlcom) calis this is also not true for ARM.
+ * Because of this, whitelist specific known (series) of CPUs by default, and
+ * leave all others up to the user.
+ */
+static int should_io_be_busy(void)
+{
+#if defined(CONFIG_X86)
+ /*
+ * For Intel, Core 2 (model 15) andl later have an efficient idle.
+ */
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
+ boot_cpu_data.x86 == 6 &&
+ boot_cpu_data.x86_model >= 15)
+ return 1;
+#endif
+ return 0;
}
/*
@@ -135,14 +85,13 @@ static inline cputime64_t get_cpu_iowait_time(unsigned int cpu, cputime64_t *wal
* freq_lo, and freq_lo_jiffies in percpu area for averaging freqs.
*/
static unsigned int powersave_bias_target(struct cpufreq_policy *policy,
- unsigned int freq_next,
- unsigned int relation)
+ unsigned int freq_next, unsigned int relation)
{
unsigned int freq_req, freq_reduc, freq_avg;
unsigned int freq_hi, freq_lo;
unsigned int index = 0;
unsigned int jiffies_total, jiffies_hi, jiffies_lo;
- struct cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info,
+ struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info,
policy->cpu);
if (!dbs_info->freq_table) {
@@ -154,7 +103,7 @@ static unsigned int powersave_bias_target(struct cpufreq_policy *policy,
cpufreq_frequency_table_target(policy, dbs_info->freq_table, freq_next,
relation, &index);
freq_req = dbs_info->freq_table[index].frequency;
- freq_reduc = freq_req * dbs_tuners_ins.powersave_bias / 1000;
+ freq_reduc = freq_req * od_tuners.powersave_bias / 1000;
freq_avg = freq_req - freq_reduc;
/* Find freq bounds for freq_avg in freq_table */
@@ -173,7 +122,7 @@ static unsigned int powersave_bias_target(struct cpufreq_policy *policy,
dbs_info->freq_lo_jiffies = 0;
return freq_lo;
}
- jiffies_total = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+ jiffies_total = usecs_to_jiffies(od_tuners.sampling_rate);
jiffies_hi = (freq_avg - freq_lo) * jiffies_total;
jiffies_hi += ((freq_hi - freq_lo) / 2);
jiffies_hi /= (freq_hi - freq_lo);
@@ -184,13 +133,6 @@ static unsigned int powersave_bias_target(struct cpufreq_policy *policy,
return freq_hi;
}
-static void ondemand_powersave_bias_init_cpu(int cpu)
-{
- struct cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
- dbs_info->freq_table = cpufreq_frequency_get_table(cpu);
- dbs_info->freq_lo = 0;
-}
-
static void ondemand_powersave_bias_init(void)
{
int i;
@@ -199,53 +141,138 @@ static void ondemand_powersave_bias_init(void)
}
}
-/************************** sysfs interface ************************/
+static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq)
+{
+ if (od_tuners.powersave_bias)
+ freq = powersave_bias_target(p, freq, CPUFREQ_RELATION_H);
+ else if (p->cur == p->max)
+ return;
-static ssize_t show_sampling_rate_min(struct kobject *kobj,
- struct attribute *attr, char *buf)
+ __cpufreq_driver_target(p, freq, od_tuners.powersave_bias ?
+ CPUFREQ_RELATION_L : CPUFREQ_RELATION_H);
+}
+
+/*
+ * Every sampling_rate, we check, if current idle time is less than 20%
+ * (default), then we try to increase frequency Every sampling_rate, we look for
+ * a the lowest frequency which can sustain the load while keeping idle time
+ * over 30%. If such a frequency exist, we try to decrease to this frequency.
+ *
+ * Any frequency increase takes it to the maximum frequency. Frequency reduction
+ * happens at minimum steps of 5% (default) of current frequency
+ */
+static void od_check_cpu(int cpu, unsigned int load_freq)
{
- return sprintf(buf, "%u\n", min_sampling_rate);
+ struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
+ struct cpufreq_policy *policy = dbs_info->cdbs.cur_policy;
+
+ dbs_info->freq_lo = 0;
+
+ /* Check for frequency increase */
+ if (load_freq > od_tuners.up_threshold * policy->cur) {
+ /* If switching to max speed, apply sampling_down_factor */
+ if (policy->cur < policy->max)
+ dbs_info->rate_mult =
+ od_tuners.sampling_down_factor;
+ dbs_freq_increase(policy, policy->max);
+ return;
+ }
+
+ /* Check for frequency decrease */
+ /* if we cannot reduce the frequency anymore, break out early */
+ if (policy->cur == policy->min)
+ return;
+
+ /*
+ * The optimal frequency is the frequency that is the lowest that can
+ * support the current CPU usage without triggering the up policy. To be
+ * safe, we focus 10 points under the threshold.
+ */
+ if (load_freq < (od_tuners.up_threshold - od_tuners.down_differential) *
+ policy->cur) {
+ unsigned int freq_next;
+ freq_next = load_freq / (od_tuners.up_threshold -
+ od_tuners.down_differential);
+
+ /* No longer fully busy, reset rate_mult */
+ dbs_info->rate_mult = 1;
+
+ if (freq_next < policy->min)
+ freq_next = policy->min;
+
+ if (!od_tuners.powersave_bias) {
+ __cpufreq_driver_target(policy, freq_next,
+ CPUFREQ_RELATION_L);
+ } else {
+ int freq = powersave_bias_target(policy, freq_next,
+ CPUFREQ_RELATION_L);
+ __cpufreq_driver_target(policy, freq,
+ CPUFREQ_RELATION_L);
+ }
+ }
}
-define_one_global_ro(sampling_rate_min);
+static void od_dbs_timer(struct work_struct *work)
+{
+ struct od_cpu_dbs_info_s *dbs_info =
+ container_of(work, struct od_cpu_dbs_info_s, cdbs.work.work);
+ unsigned int cpu = dbs_info->cdbs.cpu;
+ int delay, sample_type = dbs_info->sample_type;
-/* cpufreq_ondemand Governor Tunables */
-#define show_one(file_name, object) \
-static ssize_t show_##file_name \
-(struct kobject *kobj, struct attribute *attr, char *buf) \
-{ \
- return sprintf(buf, "%u\n", dbs_tuners_ins.object); \
+ mutex_lock(&dbs_info->cdbs.timer_mutex);
+
+ /* Common NORMAL_SAMPLE setup */
+ dbs_info->sample_type = OD_NORMAL_SAMPLE;
+ if (sample_type == OD_SUB_SAMPLE) {
+ delay = dbs_info->freq_lo_jiffies;
+ __cpufreq_driver_target(dbs_info->cdbs.cur_policy,
+ dbs_info->freq_lo, CPUFREQ_RELATION_H);
+ } else {
+ dbs_check_cpu(&od_dbs_data, cpu);
+ if (dbs_info->freq_lo) {
+ /* Setup timer for SUB_SAMPLE */
+ dbs_info->sample_type = OD_SUB_SAMPLE;
+ delay = dbs_info->freq_hi_jiffies;
+ } else {
+ delay = delay_for_sampling_rate(dbs_info->rate_mult);
+ }
+ }
+
+ schedule_delayed_work_on(cpu, &dbs_info->cdbs.work, delay);
+ mutex_unlock(&dbs_info->cdbs.timer_mutex);
+}
+
+/************************** sysfs interface ************************/
+
+static ssize_t show_sampling_rate_min(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", od_dbs_data.min_sampling_rate);
}
-show_one(sampling_rate, sampling_rate);
-show_one(io_is_busy, io_is_busy);
-show_one(up_threshold, up_threshold);
-show_one(sampling_down_factor, sampling_down_factor);
-show_one(ignore_nice_load, ignore_nice);
-show_one(powersave_bias, powersave_bias);
/**
* update_sampling_rate - update sampling rate effective immediately if needed.
* @new_rate: new sampling rate
*
* If new rate is smaller than the old, simply updaing
- * dbs_tuners_int.sampling_rate might not be appropriate. For example,
- * if the original sampling_rate was 1 second and the requested new sampling
- * rate is 10 ms because the user needs immediate reaction from ondemand
- * governor, but not sure if higher frequency will be required or not,
- * then, the governor may change the sampling rate too late; up to 1 second
- * later. Thus, if we are reducing the sampling rate, we need to make the
- * new value effective immediately.
+ * dbs_tuners_int.sampling_rate might not be appropriate. For example, if the
+ * original sampling_rate was 1 second and the requested new sampling rate is 10
+ * ms because the user needs immediate reaction from ondemand governor, but not
+ * sure if higher frequency will be required or not, then, the governor may
+ * change the sampling rate too late; up to 1 second later. Thus, if we are
+ * reducing the sampling rate, we need to make the new value effective
+ * immediately.
*/
static void update_sampling_rate(unsigned int new_rate)
{
int cpu;
- dbs_tuners_ins.sampling_rate = new_rate
- = max(new_rate, min_sampling_rate);
+ od_tuners.sampling_rate = new_rate = max(new_rate,
+ od_dbs_data.min_sampling_rate);
for_each_online_cpu(cpu) {
struct cpufreq_policy *policy;
- struct cpu_dbs_info_s *dbs_info;
+ struct od_cpu_dbs_info_s *dbs_info;
unsigned long next_sampling, appointed_at;
policy = cpufreq_cpu_get(cpu);
@@ -254,28 +281,28 @@ static void update_sampling_rate(unsigned int new_rate)
dbs_info = &per_cpu(od_cpu_dbs_info, policy->cpu);
cpufreq_cpu_put(policy);
- mutex_lock(&dbs_info->timer_mutex);
+ mutex_lock(&dbs_info->cdbs.timer_mutex);
- if (!delayed_work_pending(&dbs_info->work)) {
- mutex_unlock(&dbs_info->timer_mutex);
+ if (!delayed_work_pending(&dbs_info->cdbs.work)) {
+ mutex_unlock(&dbs_info->cdbs.timer_mutex);
continue;
}
- next_sampling = jiffies + usecs_to_jiffies(new_rate);
- appointed_at = dbs_info->work.timer.expires;
-
+ next_sampling = jiffies + usecs_to_jiffies(new_rate);
+ appointed_at = dbs_info->cdbs.work.timer.expires;
if (time_before(next_sampling, appointed_at)) {
- mutex_unlock(&dbs_info->timer_mutex);
- cancel_delayed_work_sync(&dbs_info->work);
- mutex_lock(&dbs_info->timer_mutex);
+ mutex_unlock(&dbs_info->cdbs.timer_mutex);
+ cancel_delayed_work_sync(&dbs_info->cdbs.work);
+ mutex_lock(&dbs_info->cdbs.timer_mutex);
- schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work,
- usecs_to_jiffies(new_rate));
+ schedule_delayed_work_on(dbs_info->cdbs.cpu,
+ &dbs_info->cdbs.work,
+ usecs_to_jiffies(new_rate));
}
- mutex_unlock(&dbs_info->timer_mutex);
+ mutex_unlock(&dbs_info->cdbs.timer_mutex);
}
}
@@ -300,7 +327,7 @@ static ssize_t store_io_is_busy(struct kobject *a, struct attribute *b,
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
- dbs_tuners_ins.io_is_busy = !!input;
+ od_tuners.io_is_busy = !!input;
return count;
}
@@ -315,7 +342,7 @@ static ssize_t store_up_threshold(struct kobject *a, struct attribute *b,
input < MIN_FREQUENCY_UP_THRESHOLD) {
return -EINVAL;
}
- dbs_tuners_ins.up_threshold = input;
+ od_tuners.up_threshold = input;
return count;
}
@@ -328,12 +355,12 @@ static ssize_t store_sampling_down_factor(struct kobject *a,
if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1)
return -EINVAL;
- dbs_tuners_ins.sampling_down_factor = input;
+ od_tuners.sampling_down_factor = input;
/* Reset down sampling multiplier in case it was active */
for_each_online_cpu(j) {
- struct cpu_dbs_info_s *dbs_info;
- dbs_info = &per_cpu(od_cpu_dbs_info, j);
+ struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info,
+ j);
dbs_info->rate_mult = 1;
}
return count;
@@ -354,19 +381,20 @@ static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b,
if (input > 1)
input = 1;
- if (input == dbs_tuners_ins.ignore_nice) { /* nothing to do */
+ if (input == od_tuners.ignore_nice) { /* nothing to do */
return count;
}
- dbs_tuners_ins.ignore_nice = input;
+ od_tuners.ignore_nice = input;
/* we need to re-evaluate prev_cpu_idle */
for_each_online_cpu(j) {
- struct cpu_dbs_info_s *dbs_info;
+ struct od_cpu_dbs_info_s *dbs_info;
dbs_info = &per_cpu(od_cpu_dbs_info, j);
- dbs_info->prev_cpu_idle = get_cpu_idle_time(j,
- &dbs_info->prev_cpu_wall);
- if (dbs_tuners_ins.ignore_nice)
- dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE];
+ dbs_info->cdbs.prev_cpu_idle = get_cpu_idle_time(j,
+ &dbs_info->cdbs.prev_cpu_wall);
+ if (od_tuners.ignore_nice)
+ dbs_info->cdbs.prev_cpu_nice =
+ kcpustat_cpu(j).cpustat[CPUTIME_NICE];
}
return count;
@@ -385,17 +413,25 @@ static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b,
if (input > 1000)
input = 1000;
- dbs_tuners_ins.powersave_bias = input;
+ od_tuners.powersave_bias = input;
ondemand_powersave_bias_init();
return count;
}
+show_one(od, sampling_rate, sampling_rate);
+show_one(od, io_is_busy, io_is_busy);
+show_one(od, up_threshold, up_threshold);
+show_one(od, sampling_down_factor, sampling_down_factor);
+show_one(od, ignore_nice_load, ignore_nice);
+show_one(od, powersave_bias, powersave_bias);
+
define_one_global_rw(sampling_rate);
define_one_global_rw(io_is_busy);
define_one_global_rw(up_threshold);
define_one_global_rw(sampling_down_factor);
define_one_global_rw(ignore_nice_load);
define_one_global_rw(powersave_bias);
+define_one_global_ro(sampling_rate_min);
static struct attribute *dbs_attributes[] = {
&sampling_rate_min.attr,
@@ -408,354 +444,71 @@ static struct attribute *dbs_attributes[] = {
NULL
};
-static struct attribute_group dbs_attr_group = {
+static struct attribute_group od_attr_group = {
.attrs = dbs_attributes,
.name = "ondemand",
};
/************************** sysfs end ************************/
-static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq)
-{
- if (dbs_tuners_ins.powersave_bias)
- freq = powersave_bias_target(p, freq, CPUFREQ_RELATION_H);
- else if (p->cur == p->max)
- return;
-
- __cpufreq_driver_target(p, freq, dbs_tuners_ins.powersave_bias ?
- CPUFREQ_RELATION_L : CPUFREQ_RELATION_H);
-}
-
-static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
-{
- unsigned int max_load_freq;
-
- struct cpufreq_policy *policy;
- unsigned int j;
-
- this_dbs_info->freq_lo = 0;
- policy = this_dbs_info->cur_policy;
-
- /*
- * Every sampling_rate, we check, if current idle time is less
- * than 20% (default), then we try to increase frequency
- * Every sampling_rate, we look for a the lowest
- * frequency which can sustain the load while keeping idle time over
- * 30%. If such a frequency exist, we try to decrease to this frequency.
- *
- * Any frequency increase takes it to the maximum frequency.
- * Frequency reduction happens at minimum steps of
- * 5% (default) of current frequency
- */
-
- /* Get Absolute Load - in terms of freq */
- max_load_freq = 0;
-
- for_each_cpu(j, policy->cpus) {
- struct cpu_dbs_info_s *j_dbs_info;
- cputime64_t cur_wall_time, cur_idle_time, cur_iowait_time;
- unsigned int idle_time, wall_time, iowait_time;
- unsigned int load, load_freq;
- int freq_avg;
-
- j_dbs_info = &per_cpu(od_cpu_dbs_info, j);
-
- cur_idle_time = get_cpu_idle_time(j, &cur_wall_time);
- cur_iowait_time = get_cpu_iowait_time(j, &cur_wall_time);
-
- wall_time = (unsigned int)
- (cur_wall_time - j_dbs_info->prev_cpu_wall);
- j_dbs_info->prev_cpu_wall = cur_wall_time;
-
- idle_time = (unsigned int)
- (cur_idle_time - j_dbs_info->prev_cpu_idle);
- j_dbs_info->prev_cpu_idle = cur_idle_time;
-
- iowait_time = (unsigned int)
- (cur_iowait_time - j_dbs_info->prev_cpu_iowait);
- j_dbs_info->prev_cpu_iowait = cur_iowait_time;
-
- if (dbs_tuners_ins.ignore_nice) {
- u64 cur_nice;
- unsigned long cur_nice_jiffies;
-
- cur_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE] -
- j_dbs_info->prev_cpu_nice;
- /*
- * Assumption: nice time between sampling periods will
- * be less than 2^32 jiffies for 32 bit sys
- */
- cur_nice_jiffies = (unsigned long)
- cputime64_to_jiffies64(cur_nice);
-
- j_dbs_info->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE];
- idle_time += jiffies_to_usecs(cur_nice_jiffies);
- }
-
- /*
- * For the purpose of ondemand, waiting for disk IO is an
- * indication that you're performance critical, and not that
- * the system is actually idle. So subtract the iowait time
- * from the cpu idle time.
- */
-
- if (dbs_tuners_ins.io_is_busy && idle_time >= iowait_time)
- idle_time -= iowait_time;
+define_get_cpu_dbs_routines(od_cpu_dbs_info);
- if (unlikely(!wall_time || wall_time < idle_time))
- continue;
-
- load = 100 * (wall_time - idle_time) / wall_time;
-
- freq_avg = __cpufreq_driver_getavg(policy, j);
- if (freq_avg <= 0)
- freq_avg = policy->cur;
-
- load_freq = load * freq_avg;
- if (load_freq > max_load_freq)
- max_load_freq = load_freq;
- }
-
- /* Check for frequency increase */
- if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) {
- /* If switching to max speed, apply sampling_down_factor */
- if (policy->cur < policy->max)
- this_dbs_info->rate_mult =
- dbs_tuners_ins.sampling_down_factor;
- dbs_freq_increase(policy, policy->max);
- return;
- }
-
- /* Check for frequency decrease */
- /* if we cannot reduce the frequency anymore, break out early */
- if (policy->cur == policy->min)
- return;
-
- /*
- * The optimal frequency is the frequency that is the lowest that
- * can support the current CPU usage without triggering the up
- * policy. To be safe, we focus 10 points under the threshold.
- */
- if (max_load_freq <
- (dbs_tuners_ins.up_threshold - dbs_tuners_ins.down_differential) *
- policy->cur) {
- unsigned int freq_next;
- freq_next = max_load_freq /
- (dbs_tuners_ins.up_threshold -
- dbs_tuners_ins.down_differential);
-
- /* No longer fully busy, reset rate_mult */
- this_dbs_info->rate_mult = 1;
-
- if (freq_next < policy->min)
- freq_next = policy->min;
-
- if (!dbs_tuners_ins.powersave_bias) {
- __cpufreq_driver_target(policy, freq_next,
- CPUFREQ_RELATION_L);
- } else {
- int freq = powersave_bias_target(policy, freq_next,
- CPUFREQ_RELATION_L);
- __cpufreq_driver_target(policy, freq,
- CPUFREQ_RELATION_L);
- }
- }
-}
-
-static void do_dbs_timer(struct work_struct *work)
-{
- struct cpu_dbs_info_s *dbs_info =
- container_of(work, struct cpu_dbs_info_s, work.work);
- unsigned int cpu = dbs_info->cpu;
- int sample_type = dbs_info->sample_type;
-
- int delay;
-
- mutex_lock(&dbs_info->timer_mutex);
-
- /* Common NORMAL_SAMPLE setup */
- dbs_info->sample_type = DBS_NORMAL_SAMPLE;
- if (!dbs_tuners_ins.powersave_bias ||
- sample_type == DBS_NORMAL_SAMPLE) {
- dbs_check_cpu(dbs_info);
- if (dbs_info->freq_lo) {
- /* Setup timer for SUB_SAMPLE */
- dbs_info->sample_type = DBS_SUB_SAMPLE;
- delay = dbs_info->freq_hi_jiffies;
- } else {
- /* We want all CPUs to do sampling nearly on
- * same jiffy
- */
- delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate
- * dbs_info->rate_mult);
-
- if (num_online_cpus() > 1)
- delay -= jiffies % delay;
- }
- } else {
- __cpufreq_driver_target(dbs_info->cur_policy,
- dbs_info->freq_lo, CPUFREQ_RELATION_H);
- delay = dbs_info->freq_lo_jiffies;
- }
- schedule_delayed_work_on(cpu, &dbs_info->work, delay);
- mutex_unlock(&dbs_info->timer_mutex);
-}
-
-static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
-{
- /* We want all CPUs to do sampling nearly on same jiffy */
- int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
-
- if (num_online_cpus() > 1)
- delay -= jiffies % delay;
+static struct od_ops od_ops = {
+ .io_busy = should_io_be_busy,
+ .powersave_bias_init_cpu = ondemand_powersave_bias_init_cpu,
+ .powersave_bias_target = powersave_bias_target,
+ .freq_increase = dbs_freq_increase,
+};
- dbs_info->sample_type = DBS_NORMAL_SAMPLE;
- INIT_DEFERRABLE_WORK(&dbs_info->work, do_dbs_timer);
- schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, delay);
-}
+static struct dbs_data od_dbs_data = {
+ .governor = GOV_ONDEMAND,
+ .attr_group = &od_attr_group,
+ .tuners = &od_tuners,
+ .get_cpu_cdbs = get_cpu_cdbs,
+ .get_cpu_dbs_info_s = get_cpu_dbs_info_s,
+ .gov_dbs_timer = od_dbs_timer,
+ .gov_check_cpu = od_check_cpu,
+ .gov_ops = &od_ops,
+};
-static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
+static int od_cpufreq_governor_dbs(struct cpufreq_policy *policy,
+ unsigned int event)
{
- cancel_delayed_work_sync(&dbs_info->work);
+ return cpufreq_governor_dbs(&od_dbs_data, policy, event);
}
-/*
- * Not all CPUs want IO time to be accounted as busy; this dependson how
- * efficient idling at a higher frequency/voltage is.
- * Pavel Machek says this is not so for various generations of AMD and old
- * Intel systems.
- * Mike Chan (androidlcom) calis this is also not true for ARM.
- * Because of this, whitelist specific known (series) of CPUs by default, and
- * leave all others up to the user.
- */
-static int should_io_be_busy(void)
-{
-#if defined(CONFIG_X86)
- /*
- * For Intel, Core 2 (model 15) andl later have an efficient idle.
- */
- if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
- boot_cpu_data.x86 == 6 &&
- boot_cpu_data.x86_model >= 15)
- return 1;
+#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND
+static
#endif
- return 0;
-}
-
-static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
- unsigned int event)
-{
- unsigned int cpu = policy->cpu;
- struct cpu_dbs_info_s *this_dbs_info;
- unsigned int j;
- int rc;
-
- this_dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
-
- switch (event) {
- case CPUFREQ_GOV_START:
- if ((!cpu_online(cpu)) || (!policy->cur))
- return -EINVAL;
-
- mutex_lock(&dbs_mutex);
-
- dbs_enable++;
- for_each_cpu(j, policy->cpus) {
- struct cpu_dbs_info_s *j_dbs_info;
- j_dbs_info = &per_cpu(od_cpu_dbs_info, j);
- j_dbs_info->cur_policy = policy;
-
- j_dbs_info->prev_cpu_idle = get_cpu_idle_time(j,
- &j_dbs_info->prev_cpu_wall);
- if (dbs_tuners_ins.ignore_nice)
- j_dbs_info->prev_cpu_nice =
- kcpustat_cpu(j).cpustat[CPUTIME_NICE];
- }
- this_dbs_info->cpu = cpu;
- this_dbs_info->rate_mult = 1;
- ondemand_powersave_bias_init_cpu(cpu);
- /*
- * Start the timerschedule work, when this governor
- * is used for first time
- */
- if (dbs_enable == 1) {
- unsigned int latency;
-
- rc = sysfs_create_group(cpufreq_global_kobject,
- &dbs_attr_group);
- if (rc) {
- mutex_unlock(&dbs_mutex);
- return rc;
- }
-
- /* policy latency is in nS. Convert it to uS first */
- latency = policy->cpuinfo.transition_latency / 1000;
- if (latency == 0)
- latency = 1;
- /* Bring kernel and HW constraints together */
- min_sampling_rate = max(min_sampling_rate,
- MIN_LATENCY_MULTIPLIER * latency);
- dbs_tuners_ins.sampling_rate =
- max(min_sampling_rate,
- latency * LATENCY_MULTIPLIER);
- dbs_tuners_ins.io_is_busy = should_io_be_busy();
- }
- mutex_unlock(&dbs_mutex);
-
- mutex_init(&this_dbs_info->timer_mutex);
- dbs_timer_init(this_dbs_info);
- break;
-
- case CPUFREQ_GOV_STOP:
- dbs_timer_exit(this_dbs_info);
-
- mutex_lock(&dbs_mutex);
- mutex_destroy(&this_dbs_info->timer_mutex);
- dbs_enable--;
- mutex_unlock(&dbs_mutex);
- if (!dbs_enable)
- sysfs_remove_group(cpufreq_global_kobject,
- &dbs_attr_group);
-
- break;
-
- case CPUFREQ_GOV_LIMITS:
- mutex_lock(&this_dbs_info->timer_mutex);
- if (policy->max < this_dbs_info->cur_policy->cur)
- __cpufreq_driver_target(this_dbs_info->cur_policy,
- policy->max, CPUFREQ_RELATION_H);
- else if (policy->min > this_dbs_info->cur_policy->cur)
- __cpufreq_driver_target(this_dbs_info->cur_policy,
- policy->min, CPUFREQ_RELATION_L);
- dbs_check_cpu(this_dbs_info);
- mutex_unlock(&this_dbs_info->timer_mutex);
- break;
- }
- return 0;
-}
+struct cpufreq_governor cpufreq_gov_ondemand = {
+ .name = "ondemand",
+ .governor = od_cpufreq_governor_dbs,
+ .max_transition_latency = TRANSITION_LATENCY_LIMIT,
+ .owner = THIS_MODULE,
+};
static int __init cpufreq_gov_dbs_init(void)
{
u64 idle_time;
int cpu = get_cpu();
+ mutex_init(&od_dbs_data.mutex);
idle_time = get_cpu_idle_time_us(cpu, NULL);
put_cpu();
if (idle_time != -1ULL) {
/* Idle micro accounting is supported. Use finer thresholds */
- dbs_tuners_ins.up_threshold = MICRO_FREQUENCY_UP_THRESHOLD;
- dbs_tuners_ins.down_differential =
- MICRO_FREQUENCY_DOWN_DIFFERENTIAL;
+ od_tuners.up_threshold = MICRO_FREQUENCY_UP_THRESHOLD;
+ od_tuners.down_differential = MICRO_FREQUENCY_DOWN_DIFFERENTIAL;
/*
* In nohz/micro accounting case we set the minimum frequency
* not depending on HZ, but fixed (very low). The deferred
* timer might skip some samples if idle/sleeping as needed.
*/
- min_sampling_rate = MICRO_FREQUENCY_MIN_SAMPLE_RATE;
+ od_dbs_data.min_sampling_rate = MICRO_FREQUENCY_MIN_SAMPLE_RATE;
} else {
/* For correct statistics, we need 10 ticks for each measure */
- min_sampling_rate =
- MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10);
+ od_dbs_data.min_sampling_rate = MIN_SAMPLING_RATE_RATIO *
+ jiffies_to_usecs(10);
}
return cpufreq_register_governor(&cpufreq_gov_ondemand);
@@ -766,7 +519,6 @@ static void __exit cpufreq_gov_dbs_exit(void)
cpufreq_unregister_governor(&cpufreq_gov_ondemand);
}
-
MODULE_AUTHOR("Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>");
MODULE_AUTHOR("Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>");
MODULE_DESCRIPTION("'cpufreq_ondemand' - A dynamic cpufreq governor for "
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index d03c219..a55b88e 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -407,10 +407,4 @@ void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table,
unsigned int cpu);
void cpufreq_frequency_table_put_attr(unsigned int cpu);
-
-/*********************************************************************
- * Governor Helpers *
- *********************************************************************/
-cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall);
-
#endif /* _LINUX_CPUFREQ_H */
--
1.7.12.rc2.18.g61b472e
^ permalink raw reply related
* Re: [PATCH 1/2] cpufreq: Fix sparse warnings by updating cputime64_t to u64
From: Viresh Kumar @ 2012-10-25 3:30 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: cpufreq, linux-pm, linaro-dev, patches, pdsw-power-team,
arvind.chauhan, fengguang.wu
In-Reply-To: <1829614.cr6YHfW4bW@vostro.rjw.lan>
On 25 October 2012 02:44, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Wednesday 24 of October 2012 13:15:58 Viresh Kumar wrote:
>> There were few sparse warnings due to mismatch of type on function arguments.
>> Two types were used u64 and cputime64_t. Both are actually u64, so use u64 only.
>>
>> Reported-by: Fengguang Wu <fengguang.wu@intel.com>
>> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
>
> This series appears to be based on your "cpufreq: governors: remove
> redundant code" patch that hasn't been applied yet.
>
> Please rebase it on top of linux-pm/linux-next or on top of v3.7-rc2,
> whichever is more convenient, and resend.
Please apply it after applying the latest cpufreq: governors patch i have sent.
--
viresh
^ permalink raw reply
* Re: [PATCH V2 6/6] Thermal: Add ST-Ericsson DB8500 thermal properties and platform data.
From: Hongbo Zhang @ 2012-10-25 3:45 UTC (permalink / raw)
To: Joe Perches
Cc: linaro-kernel-cunTk1MwBs8s++Sfvej+rw,
linaro-dev-cunTk1MwBs8s++Sfvej+rw, patches-QSEj5FYQhm4dnm+yROfE0A,
linux-pm-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
STEricsson_nomadik_linux-nkJGhpqTU55BDgjK7y7TUQ,
kernel-vMlcbD5RyM6HZuj8yyL1ah2eb7JE58TQ, hongbo.zhang
In-Reply-To: <1351089124.18179.12.camel@joe-AO722>
On 24 October 2012 22:32, Joe Perches <joe-6d6DIl74uiNBDgjK7y7TUQ@public.gmane.org> wrote:
> On Wed, 2012-10-24 at 19:58 +0800, hongbo.zhang wrote:
>> This patch adds device tree properties for ST-Ericsson DB8500 thermal driver,
>> also adds the platform data to support the old fashion.
>
> Just a trivial note:
>
>> diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
>
>> @@ -229,6 +230,67 @@ static struct ab8500_platform_data ab8500_platdata = {
>> };
>>
>> /*
>> + * Thermal Sensor
>> + */
>> +
>> +static struct resource db8500_thsens_resources[] = {
>
> should there be a const in any of these?
There will be warnings from gcc:
warning: initialization discards 'const' qualifier from pointer target
type [enabled by default]
>
>> +static struct db8500_thsens_platform_data db8500_thsens_data = {
> []
>> +static struct platform_device u8500_thsens_device = {
> []
>> +static struct platform_device u8500_cpufreq_cooling_device = {
>
>
^ permalink raw reply
* Re: [PATCH V2 6/6] Thermal: Add ST-Ericsson DB8500 thermal properties and platform data.
From: Hongbo Zhang @ 2012-10-25 3:49 UTC (permalink / raw)
To: Viresh Kumar
Cc: linaro-kernel-cunTk1MwBs8s++Sfvej+rw,
linaro-dev-cunTk1MwBs8s++Sfvej+rw,
linux-pm-u79uwXL29TY76Z2rM5mHXA, patches-QSEj5FYQhm4dnm+yROfE0A,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
STEricsson_nomadik_linux-nkJGhpqTU55BDgjK7y7TUQ,
kernel-vMlcbD5RyM6HZuj8yyL1ah2eb7JE58TQ, hongbo.zhang
In-Reply-To: <CAKohpom0EOAuahLQoNr1ODbTT-Trv3eE0-oBEmbbdbiKBJPCng-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
[...]
>> /*
>> + * Thermal Sensor
>> + */
>> +
>> +static struct resource db8500_thsens_resources[] = {
>> + {
>> + .name = "IRQ_HOTMON_LOW",
>> + .start = IRQ_PRCMU_HOTMON_LOW,
>> + .end = IRQ_PRCMU_HOTMON_LOW,
>> + .flags = IORESOURCE_IRQ,
>> + },
>> + {
>
> I prefer }, {
I just follow the style of all the other definitions, let's keep a
uniform style.
>
>> + .name = "IRQ_HOTMON_HIGH",
>> + .start = IRQ_PRCMU_HOTMON_HIGH,
>> + .end = IRQ_PRCMU_HOTMON_HIGH,
>> + .flags = IORESOURCE_IRQ,
>> + },
>> +};
[...]
>>
>>
>> _______________________________________________
>> linaro-dev mailing list
>> linaro-dev-cunTk1MwBs8s++Sfvej+rw@public.gmane.org
>> http://lists.linaro.org/mailman/listinfo/linaro-dev
^ permalink raw reply
* [RFC] cpufreq: Make sure target freq is within limits
From: Viresh Kumar @ 2012-10-25 6:33 UTC (permalink / raw)
To: rjw-KKrjLPT3xs0
Cc: linaro-dev-cunTk1MwBs8s++Sfvej+rw, patches-QSEj5FYQhm4dnm+yROfE0A,
Viresh Kumar, linux-pm-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
cpufreq-u79uwXL29TY76Z2rM5mHXA, pdsw-power-team-5wv7dgnIgG8,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Hi Rafael,
__cpufreq_driver_target() must not pass target frequency beyond the limits of
current policy.
Today most of cpufreq platform drivers are doing this check in their target
routines. Why not move it to __cpufreq_driver_target().
I wanted to get your opinion on this before making changes in all driver files.
That's why this is an RFC.
Signed-off-by: Viresh Kumar <viresh.kumar-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
drivers/cpufreq/cpufreq.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index f552d5f..59264f1 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1470,12 +1470,19 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
unsigned int relation)
{
int retval = -EINVAL;
+ unsigned int old_target_freq = target_freq;
if (cpufreq_disabled())
return -ENODEV;
- pr_debug("target for CPU %u: %u kHz, relation %u\n", policy->cpu,
- target_freq, relation);
+ /* Make sure that target_freq is within supported range */
+ if (target_freq > policy->max)
+ target_freq = policy->max;
+ if (target_freq < policy->min)
+ target_freq = policy->min;
+
+ pr_debug("target for CPU %u: %u kHz, relation %u, requested %u kHz\n",
+ policy->cpu, target_freq, relation, old_target_freq);
if (cpu_online(policy->cpu) && cpufreq_driver->target)
retval = cpufreq_driver->target(policy, target_freq, relation);
--
1.7.12.rc2.18.g61b472e
^ permalink raw reply related
* Re: [PATCH V2 5/6] Thermal: Add ST-Ericsson DB8500 thermal dirver.
From: Hongbo Zhang @ 2012-10-25 8:26 UTC (permalink / raw)
To: Viresh Kumar
Cc: linaro-dev, linux-kernel, linux-pm, STEricsson_nomadik_linux,
kernel, linaro-kernel, hongbo.zhang, patches
In-Reply-To: <CAKohpo=L-Xz90h90Q78gr7enOHK3u1TwHSs-QPQNE=0896SjRA@mail.gmail.com>
[...]
>> +/* Callback to get temperature changing trend */
>> +static int db8500_sys_get_trend(struct thermal_zone_device *thermal,
>> + int trip, enum thermal_trend *trend)
>> +{
>> + struct db8500_thermal_zone *pzone = thermal->devdata;
>> +
>> + *trend = pzone->trend;
>> +
>> + return 0;
>
> Can make it return void.
No, it is callback of thermal layer, prototype it to return int.
[...]
>> +static int __devinit db8500_thermal_probe(struct platform_device *pdev)
>> +{
>> + struct db8500_thermal_zone *pzone = NULL;
>> + struct db8500_thsens_platform_data *ptrips = NULL;
>> + int low_irq, high_irq, ret = 0;
>> + unsigned long dft_low, dft_high;
>> +
>> + pzone = devm_kzalloc(&pdev->dev, sizeof(*pzone), GFP_KERNEL);
>> + if (!pzone)
>> + return -ENOMEM;
>> +
>> + ptrips = db8500_thermal_parse_dt(pdev);
>
> This is what u have in this routine at the very first line:
>
> if (!np) {
> dev_err(&pdev->dev, "Missing device tree data\n");
>
> So, you will end up printing this line for every non-DT case. Not good.
> What u can do is, give preference to normal pdata here.
I moved this if(!np) into parse_dt function, no problem again.
(in fact have already done this, but it is missed in this sending)
>
>> + if (!ptrips)
>> + ptrips = dev_get_platdata(&pdev->dev);
>> +
>> + if (!ptrips)
>
[...]
>> + }
>> +
>> + ret = devm_request_threaded_irq(&pdev->dev, low_irq, NULL,
>
> why threaded irq?
In fact PRCMU firmware is polling the thermal sensor, and if it meets
threshold, the PRCMU will write this event into share memory (shared
between PRCMU and ARM) and trigger an interrupt to ARM.
There may be other events passed via share memory, so it is better to
handle this kind of irq as fast as possible(it is always the policy),
and threaded irq satisfies this case better then the traditional one.
>
>> + prcmu_low_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
>> + "dbx500_temp_low", pzone);
>> + if (ret < 0) {
>> + dev_err(&pdev->dev, "Failed to allocate temp low irq.\n");
>> + return ret;
>> + }
>> +
>> + high_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_HIGH");
>> + if (high_irq < 0) {
>> + dev_err(&pdev->dev, "Get IRQ_HOTMON_HIGH failed.\n");
>> + return high_irq;
>
> don't want to free resources?
devm_* is used to allocate resources, so no need to free them manually.
>
>> + }
>> +
>> + ret = devm_request_threaded_irq(&pdev->dev, high_irq, NULL,
>> + prcmu_high_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
>> + "dbx500_temp_high", pzone);
>> + if (ret < 0) {
>> + dev_err(&pdev->dev, "Failed to allocate temp high irq.\n");
>> + return ret;
>> + }
>> +
[...]
^ permalink raw reply
* Re: [PATCH V2 5/6] Thermal: Add ST-Ericsson DB8500 thermal dirver.
From: Viresh Kumar @ 2012-10-25 8:41 UTC (permalink / raw)
To: Hongbo Zhang, Amit Kucheria
Cc: linaro-dev, linux-kernel, linux-pm, STEricsson_nomadik_linux,
kernel, linaro-kernel, hongbo.zhang, patches
In-Reply-To: <CAJLyvQxEAeP__drUAAz6D_UeeDRvdqKdmim3xAO7sXP8cCM8=g@mail.gmail.com>
On 25 October 2012 13:56, Hongbo Zhang <hongbo.zhang@linaro.org> wrote:
While replying to mails, don't remove lines like above. They help
identifying who
wrote what.
> [...]
>>> +/* Callback to get temperature changing trend */
>>> +static int db8500_sys_get_trend(struct thermal_zone_device *thermal,
For example, you can't tell who wrote this line...
>>> +static int __devinit db8500_thermal_probe(struct platform_device *pdev)
>>> +{
>>> + struct db8500_thermal_zone *pzone = NULL;
>>> + struct db8500_thsens_platform_data *ptrips = NULL;
>>> + int low_irq, high_irq, ret = 0;
>>> + unsigned long dft_low, dft_high;
>>> +
>>> + pzone = devm_kzalloc(&pdev->dev, sizeof(*pzone), GFP_KERNEL);
>>> + if (!pzone)
>>> + return -ENOMEM;
>>> +
>>> + ptrips = db8500_thermal_parse_dt(pdev);
>>
>> This is what u have in this routine at the very first line:
>>
>> if (!np) {
>> dev_err(&pdev->dev, "Missing device tree data\n");
>>
>> So, you will end up printing this line for every non-DT case. Not good.
>> What u can do is, give preference to normal pdata here.
> I moved this if(!np) into parse_dt function, no problem again.
> (in fact have already done this, but it is missed in this sending)
Sorry couldn't get your point. :(
Can you share diff of latest code in the same mail thread?
>>> + ret = devm_request_threaded_irq(&pdev->dev, low_irq, NULL,
>>
>> why threaded irq?
> In fact PRCMU firmware is polling the thermal sensor, and if it meets
> threshold, the PRCMU will write this event into share memory (shared
> between PRCMU and ARM) and trigger an interrupt to ARM.
>
> There may be other events passed via share memory, so it is better to
> handle this kind of irq as fast as possible(it is always the policy),
> and threaded irq satisfies this case better then the traditional one.
Its been long that i prepared for an interview, but i believe purpose
of threaded
irq is something else.
There can be two use cases of request_irq()
- We don't want to sleep from interrupt handler, because we don't need to sleep
for reading hardware's register. And so handler must be called from interrupt
context. We use normal request_irq() here. This is the fastest one.
- We have to sleep from some part of interrupt handler, because we don't have
peripherals register on AMBA bus. But we have it on SPI or I2C bus,
where read/
write to SPI/I2C can potentially sleep. So, we want handler to execute from
process context and so use request_threaded_irq(), i.e. handler will
be called
from a thread. This will surely be slow.
Now in threaded irq case, we can give two handlers, one that must be called
from interrupt context and other that must be called from process context.
Both will be called one by one.
Sorry if i am wrong in my theory :(
@Amit: Am i correct??
Now, the same question again. Are you sure you want threaded irq here.
>>> + prcmu_low_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
>>> + "dbx500_temp_low", pzone);
^ permalink raw reply
* Re: [PATCH V2 5/6] Thermal: Add ST-Ericsson DB8500 thermal dirver.
From: Hongbo Zhang @ 2012-10-25 9:33 UTC (permalink / raw)
To: Viresh Kumar
Cc: Amit Kucheria, linaro-dev, linux-kernel, linux-pm,
STEricsson_nomadik_linux, kernel, linaro-kernel, hongbo.zhang,
patches
In-Reply-To: <CAKohpomZZiXnnkUC=9ZQoux1BVh+uJZw_oM1Ahndua2PZaKiDw@mail.gmail.com>
On 25 October 2012 16:41, Viresh Kumar <viresh.kumar@linaro.org> wrote:
> On 25 October 2012 13:56, Hongbo Zhang <hongbo.zhang@linaro.org> wrote:
>
> While replying to mails, don't remove lines like above. They help
> identifying who
> wrote what.
>
>> [...]
>>>> +/* Callback to get temperature changing trend */
>>>> +static int db8500_sys_get_trend(struct thermal_zone_device *thermal,
>
> For example, you can't tell who wrote this line...
>
>>>> +static int __devinit db8500_thermal_probe(struct platform_device *pdev)
>>>> +{
>>>> + struct db8500_thermal_zone *pzone = NULL;
>>>> + struct db8500_thsens_platform_data *ptrips = NULL;
>>>> + int low_irq, high_irq, ret = 0;
>>>> + unsigned long dft_low, dft_high;
>>>> +
>>>> + pzone = devm_kzalloc(&pdev->dev, sizeof(*pzone), GFP_KERNEL);
>>>> + if (!pzone)
>>>> + return -ENOMEM;
>>>> +
>>>> + ptrips = db8500_thermal_parse_dt(pdev);
>>>
>>> This is what u have in this routine at the very first line:
>>>
>>> if (!np) {
>>> dev_err(&pdev->dev, "Missing device tree data\n");
>>>
>>> So, you will end up printing this line for every non-DT case. Not good.
>>> What u can do is, give preference to normal pdata here.
>> I moved this if(!np) into parse_dt function, no problem again.
>> (in fact have already done this, but it is missed in this sending)
>
> Sorry couldn't get your point. :(
> Can you share diff of latest code in the same mail thread?
Just paste my current pieces of codes here:
static struct db8500_thsens_platform_data*
db8500_thermal_parse_dt(struct platform_device *pdev)
{
struct db8500_thsens_platform_data *ptrips;
struct device_node *np = pdev->dev.of_node;
char prop_name[32];
const char *tmp_str;
u32 tmp_data;
int i, j;
if (!np) {
dev_err(&pdev->dev, "Missing device tree data\n");
return NULL;
}
......
}
static int db8500_thermal_probe(struct platform_device *pdev)
{
struct db8500_thermal_zone *pzone = NULL;
struct db8500_thsens_platform_data *ptrips = NULL;
int low_irq, high_irq, ret = 0;
unsigned long dft_low, dft_high;
ptrips = db8500_thermal_parse_dt(pdev);
if (!ptrips)
ptrips = dev_get_platdata(&pdev->dev);
if (!ptrips)
return -EINVAL;
pzone = devm_kzalloc(&pdev->dev, sizeof(*pzone), GFP_KERNEL);
if (!pzone)
return -ENOMEM;
......
}
>
>>>> + ret = devm_request_threaded_irq(&pdev->dev, low_irq, NULL,
>>>
>>> why threaded irq?
>
>> In fact PRCMU firmware is polling the thermal sensor, and if it meets
>> threshold, the PRCMU will write this event into share memory (shared
>> between PRCMU and ARM) and trigger an interrupt to ARM.
>>
>> There may be other events passed via share memory, so it is better to
>> handle this kind of irq as fast as possible(it is always the policy),
>> and threaded irq satisfies this case better then the traditional one.
>
> Its been long that i prepared for an interview, but i believe purpose
> of threaded
> irq is something else.
>
> There can be two use cases of request_irq()
> - We don't want to sleep from interrupt handler, because we don't need to sleep
> for reading hardware's register. And so handler must be called from interrupt
> context. We use normal request_irq() here. This is the fastest one.
>
> - We have to sleep from some part of interrupt handler, because we don't have
> peripherals register on AMBA bus. But we have it on SPI or I2C bus,
> where read/
> write to SPI/I2C can potentially sleep. So, we want handler to execute from
> process context and so use request_threaded_irq(), i.e. handler will
> be called
> from a thread. This will surely be slow.
>
> Now in threaded irq case, we can give two handlers, one that must be called
> from interrupt context and other that must be called from process context.
> Both will be called one by one.
>
Understand your points.
> Sorry if i am wrong in my theory :(
> @Amit: Am i correct??
>
> Now, the same question again. Are you sure you want threaded irq here.
I just saw that all the PRCMU and ab8500 related irqs use request_threaded_irq
only difference is that I use devm_request_threaded_irq
>
>>>> + prcmu_low_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
>>>> + "dbx500_temp_low", pzone);
^ permalink raw reply
* Re: [PATCH V2 5/6] Thermal: Add ST-Ericsson DB8500 thermal dirver.
From: Viresh Kumar @ 2012-10-25 9:42 UTC (permalink / raw)
To: Hongbo Zhang
Cc: Amit Kucheria, linaro-dev, linux-kernel, linux-pm,
STEricsson_nomadik_linux, kernel, linaro-kernel, hongbo.zhang,
patches
In-Reply-To: <CAJLyvQw7=ucSTXH8YyPrm6LS8uDyxJkWGEVP2jQ3FL=cYN7frg@mail.gmail.com>
On 25 October 2012 15:03, Hongbo Zhang <hongbo.zhang@linaro.org> wrote:
> On 25 October 2012 16:41, Viresh Kumar <viresh.kumar@linaro.org> wrote:
> Just paste my current pieces of codes here:
>
> static struct db8500_thsens_platform_data*
> db8500_thermal_parse_dt(struct platform_device *pdev)
> {
> if (!np) {
> dev_err(&pdev->dev, "Missing device tree data\n");
> }
>
> static int db8500_thermal_probe(struct platform_device *pdev)
> {
> ptrips = db8500_thermal_parse_dt(pdev);
> if (!ptrips)
> ptrips = dev_get_platdata(&pdev->dev);
So, the above code still has the flaw i pointed out. It will print
"Missing device tree data", while booting for non-DT case.
What i would suggest you is:
static int db8500_thermal_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
if (np)
ptrips = db8500_thermal_parse_dt(pdev);
else
ptrips = dev_get_platdata(&pdev->dev);
if (!ptrips)
explode!!
>>>>> + ret = devm_request_threaded_irq(&pdev->dev, low_irq, NULL,
> I just saw that all the PRCMU and ab8500 related irqs use request_threaded_irq
> only difference is that I use devm_request_threaded_irq
See, i started this threaded_irq thread is to make sure you know
exactly what you
are doing. Others are doing it doesn't mean you should do it too.. :)
You must dig in a bit to see why is it required for your case? If
earlier code related
to PRCMU and db8500 is correct, then i am sure you need to sleep from your
handler.
--
viresh
^ permalink raw reply
* Re: [PATCH V2 5/6] Thermal: Add ST-Ericsson DB8500 thermal dirver.
From: Hongbo Zhang @ 2012-10-25 9:56 UTC (permalink / raw)
To: Viresh Kumar
Cc: linaro-kernel-cunTk1MwBs8s++Sfvej+rw,
linaro-dev-cunTk1MwBs8s++Sfvej+rw,
linux-pm-u79uwXL29TY76Z2rM5mHXA, patches-QSEj5FYQhm4dnm+yROfE0A,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
STEricsson_nomadik_linux-nkJGhpqTU55BDgjK7y7TUQ,
kernel-vMlcbD5RyM6HZuj8yyL1ah2eb7JE58TQ, hongbo.zhang
In-Reply-To: <CAJLyvQw7=ucSTXH8YyPrm6LS8uDyxJkWGEVP2jQ3FL=cYN7frg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 25 October 2012 17:33, Hongbo Zhang <hongbo.zhang-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> On 25 October 2012 16:41, Viresh Kumar <viresh.kumar-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>> On 25 October 2012 13:56, Hongbo Zhang <hongbo.zhang-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>>
>> While replying to mails, don't remove lines like above. They help
>> identifying who
>> wrote what.
>>
>>> [...]
>>>>> +/* Callback to get temperature changing trend */
>>>>> +static int db8500_sys_get_trend(struct thermal_zone_device *thermal,
>>
>> For example, you can't tell who wrote this line...
>>
>>>>> +static int __devinit db8500_thermal_probe(struct platform_device *pdev)
>>>>> +{
>>>>> + struct db8500_thermal_zone *pzone = NULL;
>>>>> + struct db8500_thsens_platform_data *ptrips = NULL;
>>>>> + int low_irq, high_irq, ret = 0;
>>>>> + unsigned long dft_low, dft_high;
>>>>> +
>>>>> + pzone = devm_kzalloc(&pdev->dev, sizeof(*pzone), GFP_KERNEL);
>>>>> + if (!pzone)
>>>>> + return -ENOMEM;
>>>>> +
>>>>> + ptrips = db8500_thermal_parse_dt(pdev);
>>>>
>>>> This is what u have in this routine at the very first line:
>>>>
>>>> if (!np) {
>>>> dev_err(&pdev->dev, "Missing device tree data\n");
>>>>
>>>> So, you will end up printing this line for every non-DT case. Not good.
>>>> What u can do is, give preference to normal pdata here.
>>> I moved this if(!np) into parse_dt function, no problem again.
>>> (in fact have already done this, but it is missed in this sending)
>>
>> Sorry couldn't get your point. :(
>> Can you share diff of latest code in the same mail thread?
> Just paste my current pieces of codes here:
>
> static struct db8500_thsens_platform_data*
> db8500_thermal_parse_dt(struct platform_device *pdev)
> {
> struct db8500_thsens_platform_data *ptrips;
> struct device_node *np = pdev->dev.of_node;
> char prop_name[32];
> const char *tmp_str;
> u32 tmp_data;
> int i, j;
>
> if (!np) {
> dev_err(&pdev->dev, "Missing device tree data\n");
> return NULL;
> }
> ......
> }
>
> static int db8500_thermal_probe(struct platform_device *pdev)
> {
> struct db8500_thermal_zone *pzone = NULL;
> struct db8500_thsens_platform_data *ptrips = NULL;
> int low_irq, high_irq, ret = 0;
> unsigned long dft_low, dft_high;
>
> ptrips = db8500_thermal_parse_dt(pdev);
> if (!ptrips)
> ptrips = dev_get_platdata(&pdev->dev);
>
> if (!ptrips)
> return -EINVAL;
>
> pzone = devm_kzalloc(&pdev->dev, sizeof(*pzone), GFP_KERNEL);
> if (!pzone)
> return -ENOMEM;
> ......
> }
>
>>
>>>>> + ret = devm_request_threaded_irq(&pdev->dev, low_irq, NULL,
>>>>
>>>> why threaded irq?
>>
>>> In fact PRCMU firmware is polling the thermal sensor, and if it meets
>>> threshold, the PRCMU will write this event into share memory (shared
>>> between PRCMU and ARM) and trigger an interrupt to ARM.
>>>
>>> There may be other events passed via share memory, so it is better to
>>> handle this kind of irq as fast as possible(it is always the policy),
>>> and threaded irq satisfies this case better then the traditional one.
>>
>> Its been long that i prepared for an interview, but i believe purpose
>> of threaded
>> irq is something else.
>>
>> There can be two use cases of request_irq()
>> - We don't want to sleep from interrupt handler, because we don't need to sleep
>> for reading hardware's register. And so handler must be called from interrupt
>> context. We use normal request_irq() here. This is the fastest one.
>>
>> - We have to sleep from some part of interrupt handler, because we don't have
>> peripherals register on AMBA bus. But we have it on SPI or I2C bus,
>> where read/
>> write to SPI/I2C can potentially sleep. So, we want handler to execute from
>> process context and so use request_threaded_irq(), i.e. handler will
>> be called
>> from a thread. This will surely be slow.
>>
>> Now in threaded irq case, we can give two handlers, one that must be called
>> from interrupt context and other that must be called from process context.
>> Both will be called one by one.
>>
> Understand your points.
Verish, see the codes in include/linux/interrupt.h:
static inline int __must_check
devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
{
return devm_request_threaded_irq(dev, irq, handler, NULL, irqflags,
devname, dev_id);
}
devm_request_irq is devm_request_threaded_irq
>
>> Sorry if i am wrong in my theory :(
>> @Amit: Am i correct??
>>
>> Now, the same question again. Are you sure you want threaded irq here.
> I just saw that all the PRCMU and ab8500 related irqs use request_threaded_irq
> only difference is that I use devm_request_threaded_irq
>
>>
>>>>> + prcmu_low_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
>>>>> + "dbx500_temp_low", pzone);
^ permalink raw reply
* Re: [PATCH V2 5/6] Thermal: Add ST-Ericsson DB8500 thermal dirver.
From: Viresh Kumar @ 2012-10-25 10:04 UTC (permalink / raw)
To: Hongbo Zhang
Cc: Amit Kucheria, linaro-dev, linux-kernel, linux-pm,
STEricsson_nomadik_linux, kernel, linaro-kernel, hongbo.zhang,
patches
In-Reply-To: <CAJLyvQzpo19hhwG9a4GEv0HCdJtfSJz_ASRnE-WKbwbMa58Akw@mail.gmail.com>
On 25 October 2012 15:26, Hongbo Zhang <hongbo.zhang@linaro.org> wrote:
> Verish, see the codes in include/linux/interrupt.h:
s/verish/viresh :)
> static inline int __must_check
> devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,
> unsigned long irqflags, const char *devname, void *dev_id)
> {
> return devm_request_threaded_irq(dev, irq, handler, NULL, irqflags,
> devname, dev_id);
> }
> devm_request_irq is devm_request_threaded_irq
See carefully what's happening here.
All interrupt types have a common irq_desc type in kernel. This has
few pointers for every
interrupt line:
- List of handlers to call from interrupt context
- handlers to call from process context via a thread.
So, the internal implementation is exactly same... The only difference
is which pointer
should be called in.
the devm_request_threaded_irq() called from devm_request_irq() has
following params
handler: For irq to be called from interrupt context (param 3)
NULL: For irq to be called from process context. (param 4)
So, that means normal request_irq type only.
In your case, you have passed interrupt context pointer as NULL and
process context
one as handler. So that's an issue.
--
viresh
^ permalink raw reply
* Re: [PATCH V2 5/6] Thermal: Add ST-Ericsson DB8500 thermal dirver.
From: Viresh Kumar @ 2012-10-25 10:11 UTC (permalink / raw)
To: Hongbo Zhang
Cc: Amit Kucheria, linaro-dev, linux-kernel, linux-pm,
STEricsson_nomadik_linux, kernel, linaro-kernel, hongbo.zhang,
patches
In-Reply-To: <CAKohpo=grLyatiDEvSi_P+atRJ=JBVs_eWKbtumEkWenPr3EDg@mail.gmail.com>
On 25 October 2012 15:34, Viresh Kumar <viresh.kumar@linaro.org> wrote:
> On 25 October 2012 15:26, Hongbo Zhang <hongbo.zhang@linaro.org> wrote:
This is what your prcmu driver's routines are doing:
int db8500_prcmu_config_hotmon(u8 low, u8 high)
{
...
wait_for_completion(&mb4_transfer.work);
...
return 0;
}
This is why others in STE have used threaded_irqs... Because the
routine you guys call from interrupt handlers actually sleeps.
So, they can't be called from interrupt context.
I wanted you to knew this :)
Its okay now, you need to use threaded irq only and you can't use
normal request_irq(). Its not that you want to make things fast that's why
you used threaded irqs... If you try to sleep from interrupt context
(i.e. if you
have registered your handler with request_irq()), you will see a kernel crash.
--
viresh
^ permalink raw reply
* Re: [PATCH V2 5/6] Thermal: Add ST-Ericsson DB8500 thermal dirver.
From: Hongbo Zhang @ 2012-10-25 10:43 UTC (permalink / raw)
To: Viresh Kumar
Cc: Amit Kucheria, linaro-dev, linux-kernel, linux-pm,
STEricsson_nomadik_linux, kernel, linaro-kernel, hongbo.zhang,
patches
In-Reply-To: <CAKohpokVR3wmung6SY=e2G3mdHnvbZRGvB8JYNxz1vFXTAma7w@mail.gmail.com>
On 25 October 2012 17:42, Viresh Kumar <viresh.kumar@linaro.org> wrote:
> On 25 October 2012 15:03, Hongbo Zhang <hongbo.zhang@linaro.org> wrote:
>> On 25 October 2012 16:41, Viresh Kumar <viresh.kumar@linaro.org> wrote:
>
>> Just paste my current pieces of codes here:
>>
>> static struct db8500_thsens_platform_data*
>> db8500_thermal_parse_dt(struct platform_device *pdev)
>> {
>
>> if (!np) {
>> dev_err(&pdev->dev, "Missing device tree data\n");
>
>> }
>>
>> static int db8500_thermal_probe(struct platform_device *pdev)
>> {
>
>> ptrips = db8500_thermal_parse_dt(pdev);
>> if (!ptrips)
>> ptrips = dev_get_platdata(&pdev->dev);
>
> So, the above code still has the flaw i pointed out. It will print
> "Missing device tree data", while booting for non-DT case.
>
> What i would suggest you is:
>
> static int db8500_thermal_probe(struct platform_device *pdev)
> {
> struct device_node *np = pdev->dev.of_node;
>
> if (np)
> ptrips = db8500_thermal_parse_dt(pdev);
> else
> ptrips = dev_get_platdata(&pdev->dev);
>
> if (!ptrips)
> explode!!
>
This seems neat.
>
>>>>>> + ret = devm_request_threaded_irq(&pdev->dev, low_irq, NULL,
>
>> I just saw that all the PRCMU and ab8500 related irqs use request_threaded_irq
>> only difference is that I use devm_request_threaded_irq
>
> See, i started this threaded_irq thread is to make sure you know
> exactly what you
> are doing. Others are doing it doesn't mean you should do it too.. :)
>
> You must dig in a bit to see why is it required for your case? If
> earlier code related
> to PRCMU and db8500 is correct, then i am sure you need to sleep from your
> handler.
>
> --
> viresh
^ permalink raw reply
* Re: [PATCH V2 5/6] Thermal: Add ST-Ericsson DB8500 thermal dirver.
From: Hongbo Zhang @ 2012-10-25 10:45 UTC (permalink / raw)
To: Viresh Kumar
Cc: Amit Kucheria, linaro-dev, linux-kernel, linux-pm,
STEricsson_nomadik_linux, kernel, linaro-kernel, hongbo.zhang,
patches
In-Reply-To: <CAKohpo=nET=Y+wdpOuBeuXj9gbVUOvg9NChLpbDrBS3QgMrn_Q@mail.gmail.com>
On 25 October 2012 18:11, Viresh Kumar <viresh.kumar@linaro.org> wrote:
> On 25 October 2012 15:34, Viresh Kumar <viresh.kumar@linaro.org> wrote:
>> On 25 October 2012 15:26, Hongbo Zhang <hongbo.zhang@linaro.org> wrote:
>
> This is what your prcmu driver's routines are doing:
>
> int db8500_prcmu_config_hotmon(u8 low, u8 high)
> {
> ...
> wait_for_completion(&mb4_transfer.work);
> ...
> return 0;
> }
>
> This is why others in STE have used threaded_irqs... Because the
> routine you guys call from interrupt handlers actually sleeps.
>
I should find this for you, but you find it for me :(
Thanks a lot.
> So, they can't be called from interrupt context.
>
> I wanted you to knew this :)
>
> Its okay now, you need to use threaded irq only and you can't use
> normal request_irq(). Its not that you want to make things fast that's why
> you used threaded irqs... If you try to sleep from interrupt context
> (i.e. if you
> have registered your handler with request_irq()), you will see a kernel crash.
>
> --
> viresh
^ permalink raw reply
* [PATCH V2 5/6] Thermal: Add ST-Ericsson DB8500 thermal driver.
From: hongbo.zhang @ 2012-10-25 11:13 UTC (permalink / raw)
To: linaro-dev, linux-kernel, linux-pm
Cc: patches, linaro-kernel, STEricsson_nomadik_linux, kernel,
hongbo.zhang
In-Reply-To: <1351079900-32236-6-git-send-email-hongbo.zhang@linaro.com>
From: "hongbo.zhang" <hongbo.zhang@linaro.com>
This diver is based on the thermal management framework in thermal_sys.c. A
thermal zone device is created with the trip points to which cooling devices
can be bound, the current cooling device is cpufreq, e.g. CPU frequency is
clipped down to cool the CPU, and other cooling devices can be added and bound
to the trip points dynamically. The platform specific PRCMU interrupts are
used to active thermal update when trip points are reached.
Signed-off-by: hongbo.zhang <hongbo.zhang@linaro.com>
---
.../devicetree/bindings/thermal/db8500-thermal.txt | 40 ++
drivers/thermal/Kconfig | 20 +
drivers/thermal/Makefile | 2 +
drivers/thermal/db8500_cpufreq_cooling.c | 121 +++++
drivers/thermal/db8500_thermal.c | 523 +++++++++++++++++++++
include/linux/platform_data/db8500_thermal.h | 38 ++
6 files changed, 744 insertions(+)
create mode 100644 Documentation/devicetree/bindings/thermal/db8500-thermal.txt
create mode 100644 drivers/thermal/db8500_cpufreq_cooling.c
create mode 100644 drivers/thermal/db8500_thermal.c
create mode 100644 include/linux/platform_data/db8500_thermal.h
diff --git a/Documentation/devicetree/bindings/thermal/db8500-thermal.txt b/Documentation/devicetree/bindings/thermal/db8500-thermal.txt
new file mode 100644
index 0000000..abe08d7
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/db8500-thermal.txt
@@ -0,0 +1,40 @@
+* ST-Ericsson DB8500 Thermal
+
+** Thermal node properties:
+
+- compatible : "stericsson,db8500-thermal";
+- reg : address range of the thermal sensor registers;
+- interrupts : interrupts generated form PRCMU;
+- interrupt-names : "IRQ_HOTMON_LOW" and "IRQ_HOTMON_HIGH";
+- num-trips : number of total trip points;
+- tripN-temp : temperature of trip point N, should be in ascending order;
+- tripN-type : type of trip point N, should be one of "active" "passive" "hot" "critical";
+- tripN-cdev-num : number of the cooling devices which can be bound to trip point N;
+- tripN-cdev-nameM : name of the No. M cooling device of trip point N;
+
+Usually the num-trips and tripN-*** are separated in board related dts files.
+
+Example:
+thermal@801573c0 {
+ compatible = "stericsson,db8500-thermal";
+ reg = <0x801573c0 0x40>;
+ interrupts = <21 0x4>, <22 0x4>;
+ interrupt-names = "IRQ_HOTMON_LOW", "IRQ_HOTMON_HIGH";
+
+ num-trips = <3>;
+
+ trip0-temp = <70000>;
+ trip0-type = "active";
+ trip0-cdev-num = <1>;
+ trip0-cdev-name0 = "thermal-cpufreq-0";
+
+ trip1-temp = <75000>;
+ trip1-type = "active";
+ trip1-cdev-num = <2>;
+ trip1-cdev-name0 = "thermal-cpufreq-0";
+ trip1-cdev-name1 = "thermal-fan";
+
+ trip2-temp = <85000>;
+ trip2-type = "critical";
+ trip2-cdev-num = <0>;
+}
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index edfd67d..6607cba 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -30,6 +30,26 @@ config CPU_THERMAL
and not the ACPI interface.
If you want this support, you should say Y here.
+config DB8500_THERMAL
+ bool "DB8500 thermal management"
+ depends on THERMAL
+ default y
+ help
+ Adds DB8500 thermal management implementation according to the thermal
+ management framework. A thermal zone with several trip points will be
+ created. Cooling devices can be bound to the trip points to cool this
+ thermal zone if trip points reached.
+
+config DB8500_CPUFREQ_COOLING
+ tristate "DB8500 cpufreq cooling"
+ depends on CPU_THERMAL
+ default y
+ help
+ Adds DB8500 cpufreq cooling devices, and these cooling devices can be
+ bound to thermal zone trip points. When a trip point reached, the
+ bound cpufreq cooling device turns active to set CPU frequency low to
+ cool down the CPU.
+
config SPEAR_THERMAL
bool "SPEAr thermal sensor driver"
depends on THERMAL
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 885550d..c7a8dab 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -7,3 +7,5 @@ obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
+obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
+obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
diff --git a/drivers/thermal/db8500_cpufreq_cooling.c b/drivers/thermal/db8500_cpufreq_cooling.c
new file mode 100644
index 0000000..f6a8d24
--- /dev/null
+++ b/drivers/thermal/db8500_cpufreq_cooling.c
@@ -0,0 +1,121 @@
+/*
+ * db8500_cpufreq_cooling.c - db8500 cpufreq works as cooling device.
+ *
+ * Copyright (C) 2012 ST-Ericsson
+ * Copyright (C) 2012 Linaro Ltd.
+ *
+ * Author: Hongbo Zhang <hognbo.zhang@linaro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/cpu_cooling.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+static LIST_HEAD(db8500_cpufreq_cdev_list);
+
+struct db8500_cpufreq_cdev {
+ struct thermal_cooling_device *cdev;
+ struct list_head node;
+};
+
+static int db8500_cpufreq_cooling_probe(struct platform_device *pdev)
+{
+ struct db8500_cpufreq_cdev *cooling_dev;
+ struct cpumask mask_val;
+
+ cooling_dev = devm_kzalloc(&pdev->dev, sizeof(*cooling_dev),
+ GFP_KERNEL);
+ if (!cooling_dev)
+ return -ENOMEM;
+
+ cpumask_set_cpu(0, &mask_val);
+ cooling_dev->cdev = cpufreq_cooling_register(&mask_val);
+
+ if (IS_ERR_OR_NULL(cooling_dev->cdev)) {
+ dev_err(&pdev->dev, "Failed to register cooling device\n");
+ return PTR_ERR(cooling_dev->cdev);
+ }
+
+ platform_set_drvdata(pdev, cooling_dev->cdev);
+ list_add_tail(&cooling_dev->node, &db8500_cpufreq_cdev_list);
+ dev_info(&pdev->dev, "Cooling device registered: %s\n",
+ cooling_dev->cdev->type);
+
+ return 0;
+}
+
+static int db8500_cpufreq_cooling_remove(struct platform_device *pdev)
+{
+ struct db8500_cpufreq_cdev *cooling_dev;
+
+ list_for_each_entry(cooling_dev, &db8500_cpufreq_cdev_list, node)
+ if (cooling_dev->cdev == platform_get_drvdata(pdev)) {
+ cpufreq_cooling_unregister(cooling_dev->cdev);
+ list_del(&cooling_dev->node);
+ }
+
+ return 0;
+}
+
+static int db8500_cpufreq_cooling_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return -ENOSYS;
+}
+
+static int db8500_cpufreq_cooling_resume(struct platform_device *pdev)
+{
+ return -ENOSYS;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id db8500_cpufreq_cooling_match[] = {
+ { .compatible = "stericsson,db8500-cpufreq-cooling" },
+ {},
+};
+#else
+#define db8500_cpufreq_cooling_match NULL
+#endif
+
+static struct platform_driver db8500_cpufreq_cooling_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "db8500-cpufreq-cooling",
+ .of_match_table = db8500_cpufreq_cooling_match,
+ },
+ .probe = db8500_cpufreq_cooling_probe,
+ .suspend = db8500_cpufreq_cooling_suspend,
+ .resume = db8500_cpufreq_cooling_resume,
+ .remove = __devexit_p(db8500_cpufreq_cooling_remove),
+};
+
+static int __init db8500_cpufreq_cooling_init(void)
+{
+ return platform_driver_register(&db8500_cpufreq_cooling_driver);
+}
+
+static void __exit db8500_cpufreq_cooling_exit(void)
+{
+ platform_driver_unregister(&db8500_cpufreq_cooling_driver);
+}
+
+/* Should be later than db8500_cpufreq_register */
+late_initcall(db8500_cpufreq_cooling_init);
+module_exit(db8500_cpufreq_cooling_exit);
+
+MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@stericsson.com>");
+MODULE_DESCRIPTION("DB8500 cpufreq cooling driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c
new file mode 100644
index 0000000..d12bf9b
--- /dev/null
+++ b/drivers/thermal/db8500_thermal.c
@@ -0,0 +1,523 @@
+/*
+ * db8500_thermal.c - db8500 Thermal Management Implementation
+ *
+ * Copyright (C) 2012 ST-Ericsson
+ * Copyright (C) 2012 Linaro Ltd.
+ *
+ * Author: Hongbo Zhang <hognbo.zhang@linaro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/cpu_cooling.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_data/db8500_thermal.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#define PRCMU_DEFAULT_MEASURE_TIME 0xFFF
+#define PRCMU_DEFAULT_LOW_TEMP 0
+
+struct db8500_thermal_zone {
+ struct thermal_zone_device *therm_dev;
+ struct mutex th_lock;
+ struct work_struct therm_work;
+ struct db8500_thsens_platform_data *trip_tab;
+ enum thermal_device_mode mode;
+ enum thermal_trend trend;
+ unsigned long cur_temp_pseudo;
+ unsigned int cur_index;
+};
+
+/* Local function to check if thermal zone matches cooling devices */
+static int db8500_thermal_match_cdev(struct thermal_cooling_device *cdev,
+ struct db8500_trip_point *trip_points)
+{
+ int i;
+ char *cdev_name;
+
+ if (!strlen(cdev->type))
+ return -EINVAL;
+
+ for (i = 0; i < COOLING_DEV_MAX; i++) {
+ cdev_name = trip_points->cdev_name[i];
+ if (!strcmp(cdev_name, cdev->type))
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+/* Callback to bind cooling device to thermal zone */
+static int db8500_cdev_bind(struct thermal_zone_device *thermal,
+ struct thermal_cooling_device *cdev)
+{
+ struct db8500_thermal_zone *pzone = thermal->devdata;
+ struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
+ unsigned long max_state, upper, lower;
+ int i, ret = -EINVAL;
+
+ for (i = 0; i < ptrips->num_trips; i++) {
+ if (db8500_thermal_match_cdev(cdev, &ptrips->trip_points[i]))
+ continue;
+
+ cdev->ops->get_max_state(cdev, &max_state);
+ lower = upper = (i > max_state) ? max_state : i;
+
+ ret = thermal_zone_bind_cooling_device(thermal, i, cdev,
+ upper, lower);
+
+ dev_info(&cdev->device, "%s bind to %d: %d-%s\n",
+ cdev->type, i, ret, ret ? "fail" : "succeed");
+ }
+
+ return ret;
+}
+
+/* Callback to unbind cooling device from thermal zone */
+static int db8500_cdev_unbind(struct thermal_zone_device *thermal,
+ struct thermal_cooling_device *cdev)
+{
+ struct db8500_thermal_zone *pzone = thermal->devdata;
+ struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
+ int i, ret = -EINVAL;
+
+ for (i = 0; i < ptrips->num_trips; i++) {
+ if (db8500_thermal_match_cdev(cdev, &ptrips->trip_points[i]))
+ continue;
+
+ ret = thermal_zone_unbind_cooling_device(thermal, i, cdev);
+
+ dev_info(&cdev->device, "%s unbind: %s\n",
+ cdev->type, ret ? "fail" : "succeed");
+ }
+
+ return ret;
+}
+
+/* Callback to get current temperature */
+static int db8500_sys_get_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp)
+{
+ struct db8500_thermal_zone *pzone = thermal->devdata;
+
+ /*
+ * TODO: There is no PRCMU interface to get temperature data currently,
+ * so a pseudo temperature is returned , it works for thermal framework
+ * and this will be fixed when the PRCMU interface is available.
+ */
+ *temp = pzone->cur_temp_pseudo;
+
+ return 0;
+}
+
+/* Callback to get temperature changing trend */
+static int db8500_sys_get_trend(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trend *trend)
+{
+ struct db8500_thermal_zone *pzone = thermal->devdata;
+
+ *trend = pzone->trend;
+
+ return 0;
+}
+
+/* Callback to get thermal zone mode */
+static int db8500_sys_get_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode *mode)
+{
+ struct db8500_thermal_zone *pzone = thermal->devdata;
+
+ mutex_lock(&pzone->th_lock);
+ *mode = pzone->mode;
+ mutex_unlock(&pzone->th_lock);
+
+ return 0;
+}
+
+/* Callback to set thermal zone mode */
+static int db8500_sys_set_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode mode)
+{
+ struct db8500_thermal_zone *pzone = thermal->devdata;
+
+ mutex_lock(&pzone->th_lock);
+
+ pzone->mode = mode;
+ if (mode == THERMAL_DEVICE_ENABLED)
+ schedule_work(&pzone->therm_work);
+
+ mutex_unlock(&pzone->th_lock);
+
+ return 0;
+}
+
+/* Callback to get trip point type */
+static int db8500_sys_get_trip_type(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trip_type *type)
+{
+ struct db8500_thermal_zone *pzone = thermal->devdata;
+ struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
+
+ if (trip >= ptrips->num_trips)
+ return -EINVAL;
+
+ *type = ptrips->trip_points[trip].type;
+
+ return 0;
+}
+
+/* Callback to get trip point temperature */
+static int db8500_sys_get_trip_temp(struct thermal_zone_device *thermal,
+ int trip, unsigned long *temp)
+{
+ struct db8500_thermal_zone *pzone = thermal->devdata;
+ struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
+
+ if (trip >= ptrips->num_trips)
+ return -EINVAL;
+
+ *temp = ptrips->trip_points[trip].temp;
+
+ return 0;
+}
+
+/* Callback to get critical trip point temperature */
+static int db8500_sys_get_crit_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp)
+{
+ struct db8500_thermal_zone *pzone = thermal->devdata;
+ struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
+ int i;
+
+ for (i = ptrips->num_trips - 1; i > 0; i--) {
+ if (ptrips->trip_points[i].type == THERMAL_TRIP_CRITICAL) {
+ *temp = ptrips->trip_points[i].temp;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static struct thermal_zone_device_ops thdev_ops = {
+ .bind = db8500_cdev_bind,
+ .unbind = db8500_cdev_unbind,
+ .get_temp = db8500_sys_get_temp,
+ .get_trend = db8500_sys_get_trend,
+ .get_mode = db8500_sys_get_mode,
+ .set_mode = db8500_sys_set_mode,
+ .get_trip_type = db8500_sys_get_trip_type,
+ .get_trip_temp = db8500_sys_get_trip_temp,
+ .get_crit_temp = db8500_sys_get_crit_temp,
+};
+
+static void db8500_thermal_update_config(struct db8500_thermal_zone *pzone,
+ unsigned int idx, enum thermal_trend trend,
+ unsigned long next_low, unsigned long next_high)
+{
+ pzone->cur_index = idx;
+ pzone->cur_temp_pseudo = (next_low + next_high)/2;
+ pzone->trend = trend;
+
+ prcmu_stop_temp_sense();
+ prcmu_config_hotmon((u8)(next_low/1000), (u8)(next_high/1000));
+ prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME);
+}
+
+static irqreturn_t prcmu_low_irq_handler(int irq, void *irq_data)
+{
+ struct db8500_thermal_zone *pzone = irq_data;
+ struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
+ unsigned int idx = pzone->cur_index;
+ unsigned long next_low, next_high;
+
+ if (unlikely(idx == 0))
+ /* Meaningless for thermal management, ignoring it */
+ return IRQ_HANDLED;
+
+ if (idx == 1) {
+ next_high = ptrips->trip_points[0].temp;
+ next_low = PRCMU_DEFAULT_LOW_TEMP;
+ } else {
+ next_high = ptrips->trip_points[idx-1].temp;
+ next_low = ptrips->trip_points[idx-2].temp;
+ }
+ idx -= 1;
+
+ db8500_thermal_update_config(pzone, idx, THERMAL_TREND_DROPPING,
+ next_low, next_high);
+
+ dev_dbg(&pzone->therm_dev->device,
+ "PRCMU set max %ld, min %ld\n", next_high, next_low);
+
+ schedule_work(&pzone->therm_work);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t prcmu_high_irq_handler(int irq, void *irq_data)
+{
+ struct db8500_thermal_zone *pzone = irq_data;
+ struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
+ unsigned int idx = pzone->cur_index;
+ unsigned long next_low, next_high;
+
+ if (idx < ptrips->num_trips - 1) {
+ next_high = ptrips->trip_points[idx+1].temp;
+ next_low = ptrips->trip_points[idx].temp;
+ idx += 1;
+
+ db8500_thermal_update_config(pzone, idx, THERMAL_TREND_RAISING,
+ next_low, next_high);
+
+ dev_dbg(&pzone->therm_dev->device,
+ "PRCMU set max %ld, min %ld\n", next_high, next_low);
+ }
+
+ else if (idx == ptrips->num_trips - 1)
+ pzone->cur_temp_pseudo = ptrips->trip_points[idx].temp + 1;
+
+ schedule_work(&pzone->therm_work);
+
+ return IRQ_HANDLED;
+}
+
+static void db8500_thermal_work(struct work_struct *work)
+{
+ enum thermal_device_mode cur_mode;
+ struct db8500_thermal_zone *pzone;
+
+ pzone = container_of(work, struct db8500_thermal_zone, therm_work);
+
+ mutex_lock(&pzone->th_lock);
+ cur_mode = pzone->mode;
+ mutex_unlock(&pzone->th_lock);
+
+ if (cur_mode == THERMAL_DEVICE_DISABLED)
+ return;
+
+ thermal_zone_device_update(pzone->therm_dev);
+ dev_dbg(&pzone->therm_dev->device, "thermal work finished.\n");
+}
+
+#ifdef CONFIG_OF
+static struct db8500_thsens_platform_data*
+ db8500_thermal_parse_dt(struct platform_device *pdev)
+{
+ struct db8500_thsens_platform_data *ptrips;
+ struct device_node *np = pdev->dev.of_node;
+ char prop_name[32];
+ const char *tmp_str;
+ u32 tmp_data;
+ int i, j;
+
+ ptrips = devm_kzalloc(&pdev->dev, sizeof(*ptrips), GFP_KERNEL);
+ if (!ptrips)
+ return NULL;
+
+ if (of_property_read_u32(np, "num-trips", &tmp_data))
+ goto err_parse_dt;
+
+ ptrips->num_trips = tmp_data;
+
+ for (i = 0; i < ptrips->num_trips; i++) {
+ sprintf(prop_name, "trip%d-temp", i);
+ if (of_property_read_u32(np, prop_name, &tmp_data))
+ goto err_parse_dt;
+
+ ptrips->trip_points[i].temp = tmp_data;
+
+ sprintf(prop_name, "trip%d-type", i);
+ if (of_property_read_string(np, prop_name, &tmp_str))
+ goto err_parse_dt;
+
+ if (!strcmp(tmp_str, "active"))
+ ptrips->trip_points[i].type = THERMAL_TRIP_ACTIVE;
+ else if (!strcmp(tmp_str, "passive"))
+ ptrips->trip_points[i].type = THERMAL_TRIP_PASSIVE;
+ else if (!strcmp(tmp_str, "hot"))
+ ptrips->trip_points[i].type = THERMAL_TRIP_HOT;
+ else if (!strcmp(tmp_str, "critical"))
+ ptrips->trip_points[i].type = THERMAL_TRIP_CRITICAL;
+ else
+ goto err_parse_dt;
+
+ sprintf(prop_name, "trip%d-cdev-num", i);
+ if (of_property_read_u32(np, prop_name, &tmp_data))
+ goto err_parse_dt;
+
+ for (j = 0; j < tmp_data; j++) {
+ sprintf(prop_name, "trip%d-cdev-name%d", i, j);
+ if (of_property_read_string(np, prop_name, &tmp_str))
+ goto err_parse_dt;
+ strcpy(ptrips->trip_points[i].cdev_name[j], tmp_str);
+ }
+ }
+ return ptrips;
+
+err_parse_dt:
+ dev_err(&pdev->dev, "Parsing device tree data error.\n");
+ return NULL;
+}
+#else
+static inline struct db8500_thsens_platform_data*
+ db8500_thermal_parse_dt(struct platform_device *pdev)
+{
+ return NULL;
+}
+#endif
+
+static int db8500_thermal_probe(struct platform_device *pdev)
+{
+ struct db8500_thermal_zone *pzone = NULL;
+ struct db8500_thsens_platform_data *ptrips = NULL;
+ struct device_node *np = pdev->dev.of_node;
+ int low_irq, high_irq, ret = 0;
+ unsigned long dft_low, dft_high;
+
+ if (np)
+ ptrips = db8500_thermal_parse_dt(pdev);
+ else
+ ptrips = dev_get_platdata(&pdev->dev);
+
+ if (!ptrips)
+ return -EINVAL;
+
+ pzone = devm_kzalloc(&pdev->dev, sizeof(*pzone), GFP_KERNEL);
+ if (!pzone)
+ return -ENOMEM;
+
+ mutex_init(&pzone->th_lock);
+ mutex_lock(&pzone->th_lock);
+
+ pzone->mode = THERMAL_DEVICE_DISABLED;
+ pzone->trip_tab = ptrips;
+
+ dft_low = PRCMU_DEFAULT_LOW_TEMP;
+ dft_high = ptrips->trip_points[0].temp;
+
+ db8500_thermal_update_config(pzone, 0, THERMAL_TREND_STABLE,
+ dft_low, dft_high);
+
+ INIT_WORK(&pzone->therm_work, db8500_thermal_work);
+
+ low_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_LOW");
+ if (low_irq < 0) {
+ dev_err(&pdev->dev, "Get IRQ_HOTMON_LOW failed.\n");
+ return low_irq;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, low_irq, NULL,
+ prcmu_low_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
+ "dbx500_temp_low", pzone);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to allocate temp low irq.\n");
+ return ret;
+ }
+
+ high_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_HIGH");
+ if (high_irq < 0) {
+ dev_err(&pdev->dev, "Get IRQ_HOTMON_HIGH failed.\n");
+ return high_irq;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, high_irq, NULL,
+ prcmu_high_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
+ "dbx500_temp_high", pzone);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to allocate temp high irq.\n");
+ return ret;
+ }
+
+ pzone->therm_dev = thermal_zone_device_register("db8500_thermal_zone",
+ ptrips->num_trips, 0, pzone, &thdev_ops, 0, 0);
+
+ if (IS_ERR_OR_NULL(pzone->therm_dev)) {
+ dev_err(&pdev->dev, "Register thermal zone device failed.\n");
+ return PTR_ERR(pzone->therm_dev);
+ }
+ dev_info(&pdev->dev, "Thermal zone device registered.\n");
+
+ platform_set_drvdata(pdev, pzone);
+ pzone->mode = THERMAL_DEVICE_ENABLED;
+ mutex_unlock(&pzone->th_lock);
+
+ return 0;
+}
+
+static int db8500_thermal_remove(struct platform_device *pdev)
+{
+ struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev);
+
+ thermal_zone_device_unregister(pzone->therm_dev);
+ cancel_work_sync(&pzone->therm_work);
+ mutex_destroy(&pzone->th_lock);
+
+ return 0;
+}
+
+static int db8500_thermal_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev);
+
+ flush_work_sync(&pzone->therm_work);
+ prcmu_stop_temp_sense();
+
+ return 0;
+}
+
+static int db8500_thermal_resume(struct platform_device *pdev)
+{
+ struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev);
+ struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
+ unsigned long dft_low, dft_high;
+
+ dft_low = PRCMU_DEFAULT_LOW_TEMP;
+ dft_high = ptrips->trip_points[0].temp;
+
+ db8500_thermal_update_config(pzone, 0, THERMAL_TREND_STABLE,
+ dft_low, dft_high);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id db8500_thermal_match[] = {
+ { .compatible = "stericsson,db8500-thermal" },
+ {},
+};
+#else
+#define db8500_thermal_match NULL
+#endif
+
+static struct platform_driver db8500_thermal_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "db8500-thermal",
+ .of_match_table = db8500_thermal_match,
+ },
+ .probe = db8500_thermal_probe,
+ .suspend = db8500_thermal_suspend,
+ .resume = db8500_thermal_resume,
+ .remove = __devexit_p(db8500_thermal_remove),
+};
+
+module_platform_driver(db8500_thermal_driver);
+
+MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@stericsson.com>");
+MODULE_DESCRIPTION("DB8500 thermal driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/platform_data/db8500_thermal.h b/include/linux/platform_data/db8500_thermal.h
new file mode 100644
index 0000000..aad98f8
--- /dev/null
+++ b/include/linux/platform_data/db8500_thermal.h
@@ -0,0 +1,38 @@
+/*
+ * db8500_thermal.h - db8500 Thermal Management Implementation
+ *
+ * Copyright (C) 2012 ST-Ericsson
+ * Copyright (C) 2012 Linaro Ltd.
+ *
+ * Author: Hongbo Zhang <hognbo.zhang@linaro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 _DB8500_THERMAL_H_
+#define _DB8500_THERMAL_H_
+
+#include <linux/thermal.h>
+
+#define COOLING_DEV_MAX 8
+
+struct db8500_trip_point {
+ unsigned long temp;
+ enum thermal_trip_type type;
+ char cdev_name[COOLING_DEV_MAX][THERMAL_NAME_LENGTH];
+};
+
+struct db8500_thsens_platform_data {
+ struct db8500_trip_point trip_points[THERMAL_MAX_TRIPS];
+ int num_trips;
+};
+
+#endif /* _DB8500_THERMAL_H_ */
--
1.7.11.3
^ permalink raw reply related
* [PATCH V2 6/6] Thermal: Add ST-Ericsson DB8500 thermal properties and platform data.
From: hongbo.zhang @ 2012-10-25 11:15 UTC (permalink / raw)
To: linaro-dev, linux-kernel, linux-pm
Cc: patches, linaro-kernel, STEricsson_nomadik_linux, kernel,
hongbo.zhang
In-Reply-To: <1351079900-32236-7-git-send-email-hongbo.zhang@linaro.com>
From: "hongbo.zhang" <hongbo.zhang@linaro.com>
This patch adds device tree properties for ST-Ericsson DB8500 thermal driver,
also adds the platform data to support the old fashion.
Signed-off-by: hongbo.zhang <hongbo.zhang@linaro.com>
---
arch/arm/boot/dts/dbx5x0.dtsi | 14 +++++++++
arch/arm/boot/dts/snowball.dts | 31 ++++++++++++++++++
arch/arm/configs/u8500_defconfig | 4 +++
arch/arm/mach-ux500/board-mop500.c | 64 ++++++++++++++++++++++++++++++++++++++
4 files changed, 113 insertions(+)
diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi
index 748ba7a..949edc2 100644
--- a/arch/arm/boot/dts/dbx5x0.dtsi
+++ b/arch/arm/boot/dts/dbx5x0.dtsi
@@ -203,6 +203,14 @@
reg = <0x80157450 0xC>;
};
+ thermal@801573c0 {
+ compatible = "stericsson,db8500-thermal";
+ reg = <0x801573c0 0x40>;
+ interrupts = <21 0x4>, <22 0x4>;
+ interrupt-names = "IRQ_HOTMON_LOW", "IRQ_HOTMON_HIGH";
+ status = "disabled";
+ };
+
db8500-prcmu-regulators {
compatible = "stericsson,db8500-prcmu-regulator";
@@ -645,5 +653,11 @@
ranges = <0 0x50000000 0x4000000>;
status = "disabled";
};
+
+ cpufreq-cooling {
+ compatible = "stericsson,db8500-cpufreq-cooling";
+ status = "disabled";
+ };
+
};
};
diff --git a/arch/arm/boot/dts/snowball.dts b/arch/arm/boot/dts/snowball.dts
index fce4a00..787d992 100644
--- a/arch/arm/boot/dts/snowball.dts
+++ b/arch/arm/boot/dts/snowball.dts
@@ -91,6 +91,33 @@
status = "okay";
};
+ prcmu@80157000 {
+ thermal@801573c0 {
+ num-trips = <4>;
+
+ trip0-temp = <70000>;
+ trip0-type = "active";
+ trip0-cdev-num = <1>;
+ trip0-cdev-name0 = "thermal-cpufreq-0";
+
+ trip1-temp = <75000>;
+ trip1-type = "active";
+ trip1-cdev-num = <1>;
+ trip1-cdev-name0 = "thermal-cpufreq-0";
+
+ trip2-temp = <80000>;
+ trip2-type = "active";
+ trip2-cdev-num = <1>;
+ trip2-cdev-name0 = "thermal-cpufreq-0";
+
+ trip3-temp = <85000>;
+ trip3-type = "critical";
+ trip3-cdev-num = <0>;
+
+ status = "okay";
+ };
+ };
+
external-bus@50000000 {
status = "okay";
@@ -176,5 +203,9 @@
reg = <0x33>;
};
};
+
+ cpufreq-cooling {
+ status = "okay";
+ };
};
};
diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig
index cc5e7a8..34918c4 100644
--- a/arch/arm/configs/u8500_defconfig
+++ b/arch/arm/configs/u8500_defconfig
@@ -118,3 +118,7 @@ CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
# CONFIG_FTRACE is not set
CONFIG_DEBUG_USER=y
+CONFIG_THERMAL=y
+CONFIG_CPU_THERMAL=y
+CONFIG_DB8500_THERMAL=y
+CONFIG_DB8500_CPUFREQ_COOLING=y
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index a534d88..71f2a9c 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -16,6 +16,7 @@
#include <linux/io.h>
#include <linux/i2c.h>
#include <linux/platform_data/i2c-nomadik.h>
+#include <linux/platform_data/db8500_thermal.h>
#include <linux/gpio.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl022.h>
@@ -212,6 +213,67 @@ static struct ab8500_platform_data ab8500_platdata = {
};
/*
+ * Thermal Sensor
+ */
+
+static struct resource db8500_thsens_resources[] = {
+ {
+ .name = "IRQ_HOTMON_LOW",
+ .start = IRQ_PRCMU_HOTMON_LOW,
+ .end = IRQ_PRCMU_HOTMON_LOW,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "IRQ_HOTMON_HIGH",
+ .start = IRQ_PRCMU_HOTMON_HIGH,
+ .end = IRQ_PRCMU_HOTMON_HIGH,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct db8500_thsens_platform_data db8500_thsens_data = {
+ .trip_points[0] = {
+ .temp = 70000,
+ .type = THERMAL_TRIP_ACTIVE,
+ .cdev_name = {
+ [0] = "thermal-cpufreq-0",
+ },
+ },
+ .trip_points[1] = {
+ .temp = 75000,
+ .type = THERMAL_TRIP_ACTIVE,
+ .cdev_name = {
+ [0] = "thermal-cpufreq-0",
+ },
+ },
+ .trip_points[2] = {
+ .temp = 80000,
+ .type = THERMAL_TRIP_ACTIVE,
+ .cdev_name = {
+ [0] = "thermal-cpufreq-0",
+ },
+ },
+ .trip_points[3] = {
+ .temp = 85000,
+ .type = THERMAL_TRIP_CRITICAL,
+ },
+ .num_trips = 4,
+};
+
+static struct platform_device u8500_thsens_device = {
+ .name = "db8500-thermal",
+ .resource = db8500_thsens_resources,
+ .num_resources = ARRAY_SIZE(db8500_thsens_resources),
+ .dev = {
+ .platform_data = &db8500_thsens_data,
+ },
+};
+
+static struct platform_device u8500_cpufreq_cooling_device = {
+ .name = "db8500-cpufreq-cooling",
+};
+
+/*
* TPS61052
*/
@@ -586,6 +648,8 @@ static struct platform_device *snowball_platform_devs[] __initdata = {
&snowball_led_dev,
&snowball_key_dev,
&snowball_sbnet_dev,
+ &u8500_thsens_device,
+ &u8500_cpufreq_cooling_device,
};
static void __init mop500_init_machine(void)
--
1.7.11.3
^ permalink raw reply related
* [PATCH V2 6/6] Thermal: Add ST-Ericsson DB8500 thermal properties and platform data.
From: hongbo.zhang @ 2012-10-25 11:39 UTC (permalink / raw)
To: linaro-dev, linux-kernel, linux-pm
Cc: patches, linaro-kernel, STEricsson_nomadik_linux, kernel,
hongbo.zhang
In-Reply-To: <1351163752-29389-1-git-send-email-hongbo.zhang@linaro.com>
From: "hongbo.zhang" <hongbo.zhang@linaro.com>
This patch adds device tree properties for ST-Ericsson DB8500 thermal driver,
also adds the platform data to support the old fashion.
Signed-off-by: hongbo.zhang <hongbo.zhang@linaro.com>
---
arch/arm/boot/dts/dbx5x0.dtsi | 14 +++++++++
arch/arm/boot/dts/snowball.dts | 31 ++++++++++++++++++
arch/arm/configs/u8500_defconfig | 4 +++
arch/arm/mach-ux500/board-mop500.c | 64 ++++++++++++++++++++++++++++++++++++++
4 files changed, 113 insertions(+)
diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi
index 748ba7a..949edc2 100644
--- a/arch/arm/boot/dts/dbx5x0.dtsi
+++ b/arch/arm/boot/dts/dbx5x0.dtsi
@@ -203,6 +203,14 @@
reg = <0x80157450 0xC>;
};
+ thermal@801573c0 {
+ compatible = "stericsson,db8500-thermal";
+ reg = <0x801573c0 0x40>;
+ interrupts = <21 0x4>, <22 0x4>;
+ interrupt-names = "IRQ_HOTMON_LOW", "IRQ_HOTMON_HIGH";
+ status = "disabled";
+ };
+
db8500-prcmu-regulators {
compatible = "stericsson,db8500-prcmu-regulator";
@@ -645,5 +653,11 @@
ranges = <0 0x50000000 0x4000000>;
status = "disabled";
};
+
+ cpufreq-cooling {
+ compatible = "stericsson,db8500-cpufreq-cooling";
+ status = "disabled";
+ };
+
};
};
diff --git a/arch/arm/boot/dts/snowball.dts b/arch/arm/boot/dts/snowball.dts
index 702c0ba..c6f85f0 100644
--- a/arch/arm/boot/dts/snowball.dts
+++ b/arch/arm/boot/dts/snowball.dts
@@ -99,6 +99,33 @@
status = "okay";
};
+ prcmu@80157000 {
+ thermal@801573c0 {
+ num-trips = <4>;
+
+ trip0-temp = <70000>;
+ trip0-type = "active";
+ trip0-cdev-num = <1>;
+ trip0-cdev-name0 = "thermal-cpufreq-0";
+
+ trip1-temp = <75000>;
+ trip1-type = "active";
+ trip1-cdev-num = <1>;
+ trip1-cdev-name0 = "thermal-cpufreq-0";
+
+ trip2-temp = <80000>;
+ trip2-type = "active";
+ trip2-cdev-num = <1>;
+ trip2-cdev-name0 = "thermal-cpufreq-0";
+
+ trip3-temp = <85000>;
+ trip3-type = "critical";
+ trip3-cdev-num = <0>;
+
+ status = "okay";
+ };
+ };
+
external-bus@50000000 {
status = "okay";
@@ -183,5 +210,9 @@
reg = <0x33>;
};
};
+
+ cpufreq-cooling {
+ status = "okay";
+ };
};
};
diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig
index cc5e7a8..34918c4 100644
--- a/arch/arm/configs/u8500_defconfig
+++ b/arch/arm/configs/u8500_defconfig
@@ -118,3 +118,7 @@ CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
# CONFIG_FTRACE is not set
CONFIG_DEBUG_USER=y
+CONFIG_THERMAL=y
+CONFIG_CPU_THERMAL=y
+CONFIG_DB8500_THERMAL=y
+CONFIG_DB8500_CPUFREQ_COOLING=y
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index 416d436..b03216b 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -16,6 +16,7 @@
#include <linux/io.h>
#include <linux/i2c.h>
#include <linux/platform_data/i2c-nomadik.h>
+#include <linux/platform_data/db8500_thermal.h>
#include <linux/gpio.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl022.h>
@@ -229,6 +230,67 @@ static struct ab8500_platform_data ab8500_platdata = {
};
/*
+ * Thermal Sensor
+ */
+
+static struct resource db8500_thsens_resources[] = {
+ {
+ .name = "IRQ_HOTMON_LOW",
+ .start = IRQ_PRCMU_HOTMON_LOW,
+ .end = IRQ_PRCMU_HOTMON_LOW,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "IRQ_HOTMON_HIGH",
+ .start = IRQ_PRCMU_HOTMON_HIGH,
+ .end = IRQ_PRCMU_HOTMON_HIGH,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct db8500_thsens_platform_data db8500_thsens_data = {
+ .trip_points[0] = {
+ .temp = 70000,
+ .type = THERMAL_TRIP_ACTIVE,
+ .cdev_name = {
+ [0] = "thermal-cpufreq-0",
+ },
+ },
+ .trip_points[1] = {
+ .temp = 75000,
+ .type = THERMAL_TRIP_ACTIVE,
+ .cdev_name = {
+ [0] = "thermal-cpufreq-0",
+ },
+ },
+ .trip_points[2] = {
+ .temp = 80000,
+ .type = THERMAL_TRIP_ACTIVE,
+ .cdev_name = {
+ [0] = "thermal-cpufreq-0",
+ },
+ },
+ .trip_points[3] = {
+ .temp = 85000,
+ .type = THERMAL_TRIP_CRITICAL,
+ },
+ .num_trips = 4,
+};
+
+static struct platform_device u8500_thsens_device = {
+ .name = "db8500-thermal",
+ .resource = db8500_thsens_resources,
+ .num_resources = ARRAY_SIZE(db8500_thsens_resources),
+ .dev = {
+ .platform_data = &db8500_thsens_data,
+ },
+};
+
+static struct platform_device u8500_cpufreq_cooling_device = {
+ .name = "db8500-cpufreq-cooling",
+};
+
+/*
* TPS61052
*/
@@ -583,6 +645,8 @@ static struct platform_device *snowball_platform_devs[] __initdata = {
&snowball_key_dev,
&snowball_sbnet_dev,
&snowball_gpio_en_3v3_regulator_dev,
+ &u8500_thsens_device,
+ &u8500_cpufreq_cooling_device,
};
static void __init mop500_init_machine(void)
--
1.7.11.3
^ permalink raw reply related
* [PATCH] cpuidle - sysfs : move declaration in the sysfs file
From: Daniel Lezcano @ 2012-10-25 11:46 UTC (permalink / raw)
To: rjw; +Cc: linaro-dev, patches, linux-pm
The structure cpuidle_state_kobj is not used anywhere except
in the sysfs.c file. The definition of this structure is not
needed in the cpuidle header file. This patch moves it to the
sysfs.c file in order to encapsulate the code a bit more.
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
drivers/cpuidle/sysfs.c | 7 +++++++
include/linux/cpuidle.h | 7 -------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
index ed87399..f15c1e5 100644
--- a/drivers/cpuidle/sysfs.c
+++ b/drivers/cpuidle/sysfs.c
@@ -297,6 +297,13 @@ static struct attribute *cpuidle_state_default_attrs[] = {
NULL
};
+struct cpuidle_state_kobj {
+ struct cpuidle_state *state;
+ struct cpuidle_state_usage *state_usage;
+ struct completion kobj_unregister;
+ struct kobject kobj;
+};
+
#define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj)
#define kobj_to_state(k) (kobj_to_state_obj(k)->state)
#define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage)
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 279b1ea..7daf0e3 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -82,13 +82,6 @@ cpuidle_set_statedata(struct cpuidle_state_usage *st_usage, void *data)
st_usage->driver_data = data;
}
-struct cpuidle_state_kobj {
- struct cpuidle_state *state;
- struct cpuidle_state_usage *state_usage;
- struct completion kobj_unregister;
- struct kobject kobj;
-};
-
struct cpuidle_device {
unsigned int registered:1;
unsigned int enabled:1;
--
1.7.5.4
^ permalink raw reply related
* Re: [PATCH 0/4][V2] cpuidle : multiple drivers support
From: Peter De Schrijver @ 2012-10-25 13:49 UTC (permalink / raw)
To: Daniel Lezcano
Cc: rjw@sisk.pl, linux-pm@vger.kernel.org, lorenzo.pieralisi@arm.com,
patches@linaro.org, linaro-dev@lists.linaro.org
In-Reply-To: <1350641449-22863-1-git-send-email-daniel.lezcano@linaro.org>
On Fri, Oct 19, 2012 at 12:10:45PM +0200, Daniel Lezcano wrote:
> The discussion about having different cpus on the system with
> different latencies bring us to a first attemp by adding a
> pointer in the cpuidle_device to the states array.
>
> But as Rafael suggested, it would make more sense to create a
> driver per cpu [1].
>
> This patch adds support for multiple cpuidle drivers.
>
> It creates a per cpu cpuidle driver pointer.
>
> In order to not break the different drivers, the function cpuidle_register_driver
> assign for each cpu, the driver.
>
> The multiple driver support is optional and if it is not set, the cpuide driver
> core code remains the same (except some code reorganisation).
>
> I did the following tests compiled, booted, tested without/with CONFIG_CPU_IDLE,
> with/without CONFIG_CPU_IDLE_MULTIPLE_DRIVERS.
>
> Tested on Core2 Duo T9500 with acpi_idle [and intel_idle]
> Tested on ARM Dual Cortex-A9 U8500 (aka Snowball)
>
> V1 tested on Tegra3 and Vexpress TC2
>
V2 tested on Tegra3.
Cheers,
Peter.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox