Linux Power Management development
 help / color / mirror / Atom feed
* Re: [PATCH RESEND] cpufreq: Make sure target freq is within limits
From: Rafael J. Wysocki @ 2012-10-31  0:44 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,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <f79c0841be1ae16e1f2bd9084ed9a158eb171eb0.1351254846.git.viresh.kumar-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

On Friday, October 26, 2012 06:05:21 PM Viresh Kumar wrote:
> __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().
> 
> Signed-off-by: Viresh Kumar <viresh.kumar-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
> Hi Rafael,
> 
> Resend doesn't contain any change, but fixed commit log

Applied to the linux-next branch of linux-pm.git as v3.8 material.

Thanks,
Rafael


>  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 28dc134..2f5ac2d 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 (target_freq == policy->cur)
>  		return 0;
> 
-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply

* Re: [PATCH] cpuidle - sysfs : move declaration in the sysfs file
From: Rafael J. Wysocki @ 2012-10-31  0:46 UTC (permalink / raw)
  To: Daniel Lezcano; +Cc: linaro-dev, patches, linux-pm
In-Reply-To: <1351165577-32020-1-git-send-email-daniel.lezcano@linaro.org>

On Thursday, October 25, 2012 01:46:17 PM Daniel Lezcano wrote:
> 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>

Applied to the linux-next branch of linux-pm.git as v3.8 material.

Thanks,
Rafael


> ---
>  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;

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply

* Re: [PATCH -next] PM / OPP: using kfree_rcu() to simplify the code
From: Rafael J. Wysocki @ 2012-10-31  0:43 UTC (permalink / raw)
  To: Wei Yongjun; +Cc: len.brown, pavel, gregkh, yongjun_wei, linux-pm
In-Reply-To: <CAPgLHd_HvceXF4pNhnrKH3px8h59S7zTwToAjAZzjCBm3AEmRw@mail.gmail.com>

On Friday, October 26, 2012 10:57:41 PM Wei Yongjun wrote:
> From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
> 
> The callback function of call_rcu() just calls a kfree(), so we
> can use kfree_rcu() instead of call_rcu() + callback function.
> 
> dpatch engine is used to auto generate this patch.
> (https://github.com/weiyj/dpatch)
> 
> Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>

Applied to the linux-next branch of linux-pm.git as v3.8 material.

Thanks,
Rafael


> ---
>  drivers/base/power/opp.c | 13 +------------
>  1 file changed, 1 insertion(+), 12 deletions(-)
> 
> diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
> index c8a908b..50b2831 100644
> --- a/drivers/base/power/opp.c
> +++ b/drivers/base/power/opp.c
> @@ -461,17 +461,6 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
>  }
>  
>  /**
> - * opp_free_rcu() - helper to clear the struct opp when grace period has
> - * elapsed without blocking the the caller of opp_set_availability
> - */
> -static void opp_free_rcu(struct rcu_head *head)
> -{
> -	struct opp *opp = container_of(head, struct opp, head);
> -
> -	kfree(opp);
> -}
> -
> -/**
>   * opp_set_availability() - helper to set the availability of an opp
>   * @dev:		device for which we do this operation
>   * @freq:		OPP frequency to modify availability
> @@ -542,7 +531,7 @@ static int opp_set_availability(struct device *dev, unsigned long freq,
>  
>  	list_replace_rcu(&opp->node, &new_opp->node);
>  	mutex_unlock(&dev_opp_list_lock);
> -	call_rcu(&opp->head, opp_free_rcu);
> +	kfree_rcu(opp, head);
>  
>  	/* Notify the change of the OPP availability */
>  	if (availability_req)
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply

* Re: [PATCH] cpuidle : fixup device.h header in cpuidle.h
From: Rafael J. Wysocki @ 2012-10-31  0:43 UTC (permalink / raw)
  To: Daniel Lezcano; +Cc: linux-pm, linaro-dev, patches
In-Reply-To: <1351522120-24697-1-git-send-email-daniel.lezcano@linaro.org>

On Monday, October 29, 2012 03:48:40 PM Daniel Lezcano wrote:
> The "struct device" is only used in sysfs.c.
> 
> The other .c files including the private header "cpuidle.h"
> do not need to pull the entire headers tree from there as they
> don't manipulate the "struct device".
> 
> This patch fix this by moving the header inclusion to sysfs.c
> and adding a forward declaration for the struct device.
> 
> The number of lines generated by the preprocesor:
> Without this patch : 17269 loc
> With this patch : 16446 loc
> 
> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>

Applied to the linux-next branch of linux-pm.git as v3.8 material.

Thanks,
Rafael


> ---
>  drivers/cpuidle/cpuidle.h |    5 +++--
>  drivers/cpuidle/sysfs.c   |    1 +
>  2 files changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h
> index 2120d9e..f6b0923 100644
> --- a/drivers/cpuidle/cpuidle.h
> +++ b/drivers/cpuidle/cpuidle.h
> @@ -5,8 +5,6 @@
>  #ifndef __DRIVER_CPUIDLE_H
>  #define __DRIVER_CPUIDLE_H
>  
> -#include <linux/device.h>
> -
>  /* For internal use only */
>  extern struct cpuidle_governor *cpuidle_curr_governor;
>  extern struct list_head cpuidle_governors;
> @@ -25,6 +23,9 @@ extern void cpuidle_uninstall_idle_handler(void);
>  extern int cpuidle_switch_governor(struct cpuidle_governor *gov);
>  
>  /* sysfs */
> +
> +struct device;
> +
>  extern int cpuidle_add_interface(struct device *dev);
>  extern void cpuidle_remove_interface(struct device *dev);
>  extern int cpuidle_add_state_sysfs(struct cpuidle_device *device);
> diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
> index ed87399..860a686 100644
> --- a/drivers/cpuidle/sysfs.c
> +++ b/drivers/cpuidle/sysfs.c
> @@ -12,6 +12,7 @@
>  #include <linux/slab.h>
>  #include <linux/cpu.h>
>  #include <linux/capability.h>
> +#include <linux/device.h>
>  
>  #include "cpuidle.h"
>  
> 
-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply

* Re: [PATCH 0/6] cpufreq: add support for Calxeda ECX-1000 (highbank)
From: Rafael J. Wysocki @ 2012-10-31  0:21 UTC (permalink / raw)
  To: Mark Langsdorf; +Cc: linux-kernel, Linux PM list
In-Reply-To: <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com>

On Tuesday, October 30, 2012 04:04:10 PM Mark Langsdorf wrote:
> This patch series adds cpufreq support for the Calxeda ECX-1000 (highbank)
> SoCs. The driver is based on the cpufreq-cpu0 driver. Because of the 
> unique way that highbank uses the EnergyCore Management Engine to manage
> voltages, it was not possible to use the cpufreq-cpu0 driver.

I've got patches [3-6/6] only for some reason.  Care to resend the other three
with direct CCs to me?

Rafael


-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply

* Re: [PATCH 3/4] cpufreq, powernow-k8: Change email address
From: Rafael J. Wysocki @ 2012-10-30 23:42 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: Andreas Herrmann, Jean Delvare, Guenter Roeck, Ingo Molnar,
	Thomas Gleixner, H Peter Anvin, lm-sensors, cpufreq, linux-pm,
	linux-kernel, oprofile-list, Stephane Eranian, Robert Richter,
	Jörg Rödel
In-Reply-To: <20121029233100.GE8393@liondog.tnic>

On Tuesday, October 30, 2012 12:31:00 AM Borislav Petkov wrote:
> On Tue, Oct 30, 2012 at 12:33:15AM +0100, Rafael J. Wysocki wrote:
> > On Monday, October 29, 2012 06:52:21 PM Andreas Herrmann wrote:
> > > 
> > > Signed-off-by: Andreas Herrmann <herrmann.der.user@googlemail.com>
> > 
> > Am I supposed to take this one?
> 
> Yes please.

OK, I'm going to push it for v3.7.

Thanks,
Rafael


-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply

* Re: [PATCH 3/4] cpufreq, powernow-k8: Change email address
From: Borislav Petkov @ 2012-10-30 23:40 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Andreas Herrmann, Jean Delvare, Guenter Roeck, Ingo Molnar,
	Thomas Gleixner, H Peter Anvin, lm-sensors, cpufreq, linux-pm,
	linux-kernel, oprofile-list, Stephane Eranian, Robert Richter,
	Jörg Rödel
In-Reply-To: <2384721.S5BJ6UP5pX@vostro.rjw.lan>

On Wed, Oct 31, 2012 at 12:42:01AM +0100, Rafael J. Wysocki wrote:
> On Tuesday, October 30, 2012 12:31:00 AM Borislav Petkov wrote:
> > On Tue, Oct 30, 2012 at 12:33:15AM +0100, Rafael J. Wysocki wrote:
> > > On Monday, October 29, 2012 06:52:21 PM Andreas Herrmann wrote:
> > > > 
> > > > Signed-off-by: Andreas Herrmann <herrmann.der.user@googlemail.com>
> > > 
> > > Am I supposed to take this one?
> > 
> > Yes please.
> 
> OK, I'm going to push it for v3.7.

Sure thing.

Thanks.

-- 
Regards/Gruss,
    Boris.

^ permalink raw reply

* Re: [linux-next PATCH] PM / devfreq: documentation cleanups for devfreq header
From: Rafael J. Wysocki @ 2012-10-30 23:43 UTC (permalink / raw)
  To: myungjoo.ham
  Cc: Randy Dunlap, Nishanth Menon, linux-pm@vger.kernel.org,
	Rajagopal Venkat, 박경민, Kevin Hilman,
	linux-kernel@vger.kernel.org, 이종화
In-Reply-To: <31002982.486491351584547433.JavaMail.weblogic@epv6ml03>

On Tuesday, October 30, 2012 08:09:09 AM MyungJoo Ham wrote:
> > On 10/29/2012 06:02 AM, Nishanth Menon wrote:
> > 
> > > struct parameters need to have ':' in documentation for
> > > scripts/kernel-doc to parse appropriately.
> > > 
> > > Fix the errors reported by:
> > > ./scripts/kernel-doc include/linux/devfreq.h >/dev/null
> > > 
> > > Cc: Rajagopal Venkat <rajagopal.venkat@linaro.org>
> > > 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
> > > 
> > > Signed-off-by: Nishanth Menon <nm@ti.com>
> > 
> > 
> > Acked-by: Randy Dunlap <rdunlap@xenotime.net>
> > 
> > Thanks.
> 
> Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com>
> 
> Applying to git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git for-rafael, which is based on rafael's linux-pm.git / linux-next.
> 
> http://git.kernel.org/?p=linux/kernel/git/mzx/devfreq.git;a=shortlog;h=refs/heads/for-rafael
> 
> I'll apply your "Add sysfs node ..." patch after refactoring with Jonghwa's (devfreq trans_stat) as the two patches use the same data (list of available freqs).

May I assume that you'll handle all of the subsequent devfreq patches too?

Rafael


-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply

* [PATCH 5/6] power: export opp cpufreq functions
From: Mark Langsdorf @ 2012-10-30 21:04 UTC (permalink / raw)
  To: linux-kernel; +Cc: Mark Langsdorf, linux-pm
In-Reply-To: <1351631056-25938-1-git-send-email-mark.langsdorf@calxeda.com>

Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com>
Cc: linux-pm@vger.kernel.org

---
 drivers/base/power/opp.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
index d946864..37dc5f4 100644
--- a/drivers/base/power/opp.c
+++ b/drivers/base/power/opp.c
@@ -23,6 +23,7 @@
 #include <linux/rcupdate.h>
 #include <linux/opp.h>
 #include <linux/of.h>
+#include <linux/module.h>
 
 /*
  * Internal data structure organization with the OPP layer library is as
@@ -643,6 +644,7 @@ int opp_init_cpufreq_table(struct device *dev,
 
 	return 0;
 }
+EXPORT_SYMBOL(opp_init_cpufreq_table);
 
 /**
  * opp_free_cpufreq_table() - free the cpufreq table
@@ -660,6 +662,7 @@ void opp_free_cpufreq_table(struct device *dev,
 	kfree(*table);
 	*table = NULL;
 }
+EXPORT_SYMBOL(opp_free_cpufreq_table);
 #endif		/* CONFIG_CPU_FREQ */
 
 /**
@@ -720,4 +723,5 @@ int of_init_opp_table(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL(of_init_opp_table);
 #endif
-- 
1.7.11.7

^ permalink raw reply related

* Re: [PATCH v3 2/6] PM / Runtime: introduce pm_runtime_set[get]_memalloc_noio()
From: Alan Stern @ 2012-10-30 16:54 UTC (permalink / raw)
  To: Ming Lei
  Cc: linux-kernel, Oliver Neukum, Minchan Kim, Greg Kroah-Hartman,
	Rafael J. Wysocki, Jens Axboe, David S. Miller, Andrew Morton,
	netdev, linux-usb, linux-pm, linux-mm
In-Reply-To: <CACVXFVO5-UPNrWsySzDE5AfOv1TMqbyitQX9ViidSJPM36fqAQ@mail.gmail.com>

On Wed, 31 Oct 2012, Ming Lei wrote:

> On Tue, Oct 30, 2012 at 11:38 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
> 
> >
> > Okay, I see your point.  But acquiring the lock here doesn't solve the
> > problem.  Suppose a thread is about to reset a USB mass-storage device.
> > It acquires the lock and sees that the noio flag is clear.  But before
> > it can issue the reset, another thread sets the noio flag.
> 
> If the USB mass-storage device is being reseted, the flag should be set
> already generally.  If the flag is still unset, that means the disk/network
> device isn't added into system(or removed just now), so memory allocation
> with block I/O should be allowed during the reset. Looks it isn't one problem,
> isn't it?

As Oliver said, it can be a problem.

> > Lastly, pm_runtime_get_memalloc_noio always returns false when
> > CONFIG_PM_RUNTIME is disabled.  But we still need to prevent I/O during
> > usb_reset_device even when there's no runtime PM.  Maybe the simplest
> > answer is always to set noio during resets.  That would also help with
> > the race described above.
> 
> I have thought about this. IMO, pm_runtime_get_memalloc_noio should
> return true always if CONFIG_PM_RUNTIME is unset.

That's okay as long as the only user of pm_runtime_get_memalloc_noio
(apart from the runtime PM core) is usbcore.

Alan Stern

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* [PATCH V3 0/5] Fix thermal bugs and Upstream ST-Ericsson thermal driver
From: hongbo.zhang @ 2012-10-30 16:48 UTC (permalink / raw)
  To: linaro-dev, linux-kernel, linux-pm, rui.zhang, amit.kachhap
  Cc: patches, linaro-kernel, STEricsson_nomadik_linux, kernel,
	hongbo.zhang
In-Reply-To: <1351079900-32236-1-git-send-email-hongbo.zhang@linaro.com>

From: "hongbo.zhang" <hongbo.zhang@linaro.com>

V2->V3 Changes:

1. Moved the previous "[PATCH V2 2/6] Thermal: make sure cpufreq cooling
register after cpufreq driver" from generic cpu cooling layer to ST-Ericsson
driver, thus only 5 patches in total in V3 patch set.

2. Update ST-Ericsson thermal driver due to review comments from Viresh Kumar
and Francesco Lavra.

hongbo.zhang (5):
  Thermal: add indent for code alignment.
  Thermal: fix bug of counting cpu frequencies.
  Thermal: Remove the cooling_cpufreq_list.
  Thermal: Add ST-Ericsson DB8500 thermal driver.
  Thermal: Add ST-Ericsson DB8500 thermal properties and platform data.

 .../devicetree/bindings/thermal/db8500-thermal.txt |  40 ++
 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 +++
 drivers/thermal/Kconfig                            |  20 +
 drivers/thermal/Makefile                           |   2 +
 drivers/thermal/cpu_cooling.c                      | 103 +---
 drivers/thermal/db8500_cpufreq_cooling.c           | 108 +++++
 drivers/thermal/db8500_thermal.c                   | 531 +++++++++++++++++++++
 include/linux/platform_data/db8500_thermal.h       |  38 ++
 11 files changed, 878 insertions(+), 77 deletions(-)
 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

-- 
1.7.11.3


^ permalink raw reply

* [PATCH V3 5/5] Thermal: Add ST-Ericsson DB8500 thermal properties and platform data.
From: hongbo.zhang @ 2012-10-30 16:49 UTC (permalink / raw)
  To: linaro-dev-cunTk1MwBs8s++Sfvej+rw,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pm-u79uwXL29TY76Z2rM5mHXA, rui.zhang-ral2JQCrhuEAvxtiuMwx3w,
	amit.kachhap-QSEj5FYQhm4dnm+yROfE0A
  Cc: STEricsson_nomadik_linux-nkJGhpqTU55BDgjK7y7TUQ,
	kernel-vMlcbD5RyM6HZuj8yyL1ah2eb7JE58TQ,
	linaro-kernel-cunTk1MwBs8s++Sfvej+rw, hongbo.zhang,
	patches-QSEj5FYQhm4dnm+yROfE0A
In-Reply-To: <1351615741-24134-1-git-send-email-hongbo.zhang-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>

From: "hongbo.zhang" <hongbo.zhang-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>

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-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>
---
 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 4b0e0ca..731086b 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";
 
@@ -660,5 +668,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 V3 4/5] Thermal: Add ST-Ericsson DB8500 thermal driver.
From: hongbo.zhang @ 2012-10-30 16:49 UTC (permalink / raw)
  To: linaro-dev-cunTk1MwBs8s++Sfvej+rw,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pm-u79uwXL29TY76Z2rM5mHXA, rui.zhang-ral2JQCrhuEAvxtiuMwx3w,
	amit.kachhap-QSEj5FYQhm4dnm+yROfE0A
  Cc: STEricsson_nomadik_linux-nkJGhpqTU55BDgjK7y7TUQ,
	kernel-vMlcbD5RyM6HZuj8yyL1ah2eb7JE58TQ,
	linaro-kernel-cunTk1MwBs8s++Sfvej+rw, hongbo.zhang,
	patches-QSEj5FYQhm4dnm+yROfE0A
In-Reply-To: <1351615741-24134-1-git-send-email-hongbo.zhang-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>

From: "hongbo.zhang" <hongbo.zhang-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>

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-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>
---
 .../devicetree/bindings/thermal/db8500-thermal.txt |  40 ++
 drivers/thermal/Kconfig                            |  20 +
 drivers/thermal/Makefile                           |   2 +
 drivers/thermal/db8500_cpufreq_cooling.c           | 108 +++++
 drivers/thermal/db8500_thermal.c                   | 531 +++++++++++++++++++++
 include/linux/platform_data/db8500_thermal.h       |  38 ++
 6 files changed, 739 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..cab6916
--- /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 from 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 e1cb6bd..54c8fd0 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -31,6 +31,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..4cf8e72
--- /dev/null
+++ b/drivers/thermal/db8500_cpufreq_cooling.c
@@ -0,0 +1,108 @@
+/*
+ * db8500_cpufreq_cooling.c - DB8500 cpufreq works as cooling device.
+ *
+ * Copyright (C) 2012 ST-Ericsson
+ * Copyright (C) 2012 Linaro Ltd.
+ *
+ * Author: Hongbo Zhang <hongbo.zhang-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>
+ *
+ * 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 int db8500_cpufreq_cooling_probe(struct platform_device *pdev)
+{
+	struct thermal_cooling_device *cdev;
+	struct cpumask mask_val;
+
+	/* make sure cpufreq driver has been initialized */
+	if (!cpufreq_frequency_get_table(0))
+		return -EPROBE_DEFER;
+
+	cpumask_set_cpu(0, &mask_val);
+	cdev = cpufreq_cooling_register(&mask_val);
+
+	if (IS_ERR_OR_NULL(cdev)) {
+		dev_err(&pdev->dev, "Failed to register cooling device\n");
+		return PTR_ERR(cdev);
+	}
+
+	platform_set_drvdata(pdev, cdev);
+
+	dev_info(&pdev->dev, "Cooling device registered: %s\n",	cdev->type);
+
+	return 0;
+}
+
+static int db8500_cpufreq_cooling_remove(struct platform_device *pdev)
+{
+	struct thermal_cooling_device *cdev = platform_get_drvdata(pdev);
+
+	cpufreq_cooling_unregister(cdev);
+
+	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 = 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-0IS4wlFg1OjSUeElwK9/Pw@public.gmane.org>");
+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..023f7b4
--- /dev/null
+++ b/drivers/thermal/db8500_thermal.c
@@ -0,0 +1,531 @@
+/*
+ * db8500_thermal.c - DB8500 Thermal Management Implementation
+ *
+ * Copyright (C) 2012 ST-Ericsson
+ * Copyright (C) 2012 Linaro Ltd.
+ *
+ * Author: Hongbo Zhang <hongbo.zhang-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>
+ *
+ * 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;
+
+	if (tmp_data > THERMAL_MAX_TRIPS)
+		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;
+
+		if (tmp_data > COOLING_DEV_MAX)
+			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;
+
+			if (strlen(tmp_str) > THERMAL_NAME_LENGTH)
+				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 = db8500_thermal_remove,
+};
+
+module_platform_driver(db8500_thermal_driver);
+
+MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang-0IS4wlFg1OjSUeElwK9/Pw@public.gmane.org>");
+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..3bf6090
--- /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 <hongbo.zhang-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>
+ *
+ * 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 V3 3/5] Thermal: Remove the cooling_cpufreq_list.
From: hongbo.zhang @ 2012-10-30 16:48 UTC (permalink / raw)
  To: linaro-dev-cunTk1MwBs8s++Sfvej+rw,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pm-u79uwXL29TY76Z2rM5mHXA, rui.zhang-ral2JQCrhuEAvxtiuMwx3w,
	amit.kachhap-QSEj5FYQhm4dnm+yROfE0A
  Cc: STEricsson_nomadik_linux-nkJGhpqTU55BDgjK7y7TUQ,
	kernel-vMlcbD5RyM6HZuj8yyL1ah2eb7JE58TQ,
	linaro-kernel-cunTk1MwBs8s++Sfvej+rw, hongbo.zhang,
	patches-QSEj5FYQhm4dnm+yROfE0A
In-Reply-To: <1351615741-24134-1-git-send-email-hongbo.zhang-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>

From: "hongbo.zhang" <hongbo.zhang-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>

Problem of using this list is that the cpufreq_get_max_state callback will be
called when register cooling device by thermal_cooling_device_register, but
this list isn't ready at this moment. What's more, there is no need to maintain
such a list, we can get cpufreq_cooling_device instance by the private
thermal_cooling_device.devdata.

Signed-off-by: hongbo.zhang <hongbo.zhang-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>
Reviewed-by: Francesco Lavra <francescolavra.fl-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Reviewed-by: Amit Daniel Kachhap <amit.kachhap-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 drivers/thermal/cpu_cooling.c | 91 +++++++++----------------------------------
 1 file changed, 19 insertions(+), 72 deletions(-)

diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index bfd62b7..392d57d 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -58,8 +58,9 @@ struct cpufreq_cooling_device {
 };
 static LIST_HEAD(cooling_cpufreq_list);
 static DEFINE_IDR(cpufreq_idr);
+static DEFINE_MUTEX(cooling_cpufreq_lock);
 
-static struct mutex cooling_cpufreq_lock;
+static unsigned int cpufreq_dev_count;
 
 /* notify_table passes value to the CPUFREQ_ADJUST callback function. */
 #define NOTIFY_INVALID NULL
@@ -240,28 +241,18 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb,
 static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
 				 unsigned long *state)
 {
-	int ret = -EINVAL, i = 0;
-	struct cpufreq_cooling_device *cpufreq_device;
-	struct cpumask *maskPtr;
+	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
+	struct cpumask *maskPtr = &cpufreq_device->allowed_cpus;
 	unsigned int cpu;
 	struct cpufreq_frequency_table *table;
 	unsigned long count = 0;
+	int i = 0;
 
-	mutex_lock(&cooling_cpufreq_lock);
-	list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) {
-		if (cpufreq_device && cpufreq_device->cool_dev == cdev)
-			break;
-	}
-	if (cpufreq_device == NULL)
-		goto return_get_max_state;
-
-	maskPtr = &cpufreq_device->allowed_cpus;
 	cpu = cpumask_any(maskPtr);
 	table = cpufreq_frequency_get_table(cpu);
 	if (!table) {
 		*state = 0;
-		ret = 0;
-		goto return_get_max_state;
+		return 0;
 	}
 
 	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
@@ -272,12 +263,10 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
 
 	if (count > 0) {
 		*state = --count;
-		ret = 0;
+		return 0;
 	}
 
-return_get_max_state:
-	mutex_unlock(&cooling_cpufreq_lock);
-	return ret;
+	return -EINVAL;
 }
 
 /**
@@ -288,20 +277,10 @@ return_get_max_state:
 static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
 				 unsigned long *state)
 {
-	int ret = -EINVAL;
-	struct cpufreq_cooling_device *cpufreq_device;
+	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
 
-	mutex_lock(&cooling_cpufreq_lock);
-	list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) {
-		if (cpufreq_device && cpufreq_device->cool_dev == cdev) {
-			*state = cpufreq_device->cpufreq_state;
-			ret = 0;
-			break;
-		}
-	}
-	mutex_unlock(&cooling_cpufreq_lock);
-
-	return ret;
+	*state = cpufreq_device->cpufreq_state;
+	return 0;
 }
 
 /**
@@ -312,22 +291,9 @@ static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
 static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
 				 unsigned long state)
 {
-	int ret = -EINVAL;
-	struct cpufreq_cooling_device *cpufreq_device;
+	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
 
-	mutex_lock(&cooling_cpufreq_lock);
-	list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) {
-		if (cpufreq_device && cpufreq_device->cool_dev == cdev) {
-			ret = 0;
-			break;
-		}
-	}
-	if (!ret)
-		ret = cpufreq_apply_cooling(cpufreq_device, state);
-
-	mutex_unlock(&cooling_cpufreq_lock);
-
-	return ret;
+	return cpufreq_apply_cooling(cpufreq_device, state);
 }
 
 /* Bind cpufreq callbacks to thermal cooling device ops */
@@ -351,14 +317,11 @@ struct thermal_cooling_device *cpufreq_cooling_register(
 {
 	struct thermal_cooling_device *cool_dev;
 	struct cpufreq_cooling_device *cpufreq_dev = NULL;
-	unsigned int cpufreq_dev_count = 0, min = 0, max = 0;
+	unsigned int min = 0, max = 0;
 	char dev_name[THERMAL_NAME_LENGTH];
 	int ret = 0, i;
 	struct cpufreq_policy policy;
 
-	list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node)
-		cpufreq_dev_count++;
-
 	/*Verify that all the clip cpus have same freq_min, freq_max limit*/
 	for_each_cpu(i, clip_cpus) {
 		/*continue if cpufreq policy not found and not return error*/
@@ -380,9 +343,6 @@ struct thermal_cooling_device *cpufreq_cooling_register(
 
 	cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
 
-	if (cpufreq_dev_count == 0)
-		mutex_init(&cooling_cpufreq_lock);
-
 	ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
 	if (ret) {
 		kfree(cpufreq_dev);
@@ -401,12 +361,12 @@ struct thermal_cooling_device *cpufreq_cooling_register(
 	cpufreq_dev->cool_dev = cool_dev;
 	cpufreq_dev->cpufreq_state = 0;
 	mutex_lock(&cooling_cpufreq_lock);
-	list_add_tail(&cpufreq_dev->node, &cooling_cpufreq_list);
 
 	/* Register the notifier for first cpufreq cooling device */
 	if (cpufreq_dev_count == 0)
 		cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
 						CPUFREQ_POLICY_NOTIFIER);
+	cpufreq_dev_count++;
 
 	mutex_unlock(&cooling_cpufreq_lock);
 	return cool_dev;
@@ -419,33 +379,20 @@ EXPORT_SYMBOL(cpufreq_cooling_register);
  */
 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
 {
-	struct cpufreq_cooling_device *cpufreq_dev = NULL;
-	unsigned int cpufreq_dev_count = 0;
+	struct cpufreq_cooling_device *cpufreq_dev = cdev->devdata;
 
 	mutex_lock(&cooling_cpufreq_lock);
-	list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node) {
-		if (cpufreq_dev && cpufreq_dev->cool_dev == cdev)
-			break;
-		cpufreq_dev_count++;
-	}
-
-	if (!cpufreq_dev || cpufreq_dev->cool_dev != cdev) {
-		mutex_unlock(&cooling_cpufreq_lock);
-		return;
-	}
-
-	list_del(&cpufreq_dev->node);
+	cpufreq_dev_count--;
 
 	/* Unregister the notifier for the last cpufreq cooling device */
-	if (cpufreq_dev_count == 1) {
+	if (cpufreq_dev_count == 0) {
 		cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
 					CPUFREQ_POLICY_NOTIFIER);
 	}
 	mutex_unlock(&cooling_cpufreq_lock);
+
 	thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
 	release_idr(&cpufreq_idr, cpufreq_dev->id);
-	if (cpufreq_dev_count == 1)
-		mutex_destroy(&cooling_cpufreq_lock);
 	kfree(cpufreq_dev);
 }
 EXPORT_SYMBOL(cpufreq_cooling_unregister);
-- 
1.7.11.3

^ permalink raw reply related

* [PATCH V3 2/5] Thermal: fix bug of counting cpu frequencies.
From: hongbo.zhang @ 2012-10-30 16:48 UTC (permalink / raw)
  To: linaro-dev-cunTk1MwBs8s++Sfvej+rw,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-pm-u79uwXL29TY76Z2rM5mHXA, rui.zhang-ral2JQCrhuEAvxtiuMwx3w,
	amit.kachhap-QSEj5FYQhm4dnm+yROfE0A
  Cc: STEricsson_nomadik_linux-nkJGhpqTU55BDgjK7y7TUQ,
	kernel-vMlcbD5RyM6HZuj8yyL1ah2eb7JE58TQ,
	linaro-kernel-cunTk1MwBs8s++Sfvej+rw, hongbo.zhang,
	patches-QSEj5FYQhm4dnm+yROfE0A
In-Reply-To: <1351615741-24134-1-git-send-email-hongbo.zhang-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>

From: "hongbo.zhang" <hongbo.zhang-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>

In the while loop for counting cpu frequencies, if table[i].frequency equals
CPUFREQ_ENTRY_INVALID, index i won't be increased, so this leads to an endless
loop, what's more the index i cannot be referred as cpu frequencies number if
there is CPUFREQ_ENTRY_INVALID case.

Signed-off-by: hongbo.zhang <hongbo.zhang-68IGFXMjmZ7QT0dZR+AlfA@public.gmane.org>
Reviewed-by: Viresh Kumar <viresh.kumar-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Reviewed-by: Amit Daniel Kachhap <amit.kachhap-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 drivers/thermal/cpu_cooling.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index b6b4c2a..bfd62b7 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -245,6 +245,7 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
 	struct cpumask *maskPtr;
 	unsigned int cpu;
 	struct cpufreq_frequency_table *table;
+	unsigned long count = 0;
 
 	mutex_lock(&cooling_cpufreq_lock);
 	list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) {
@@ -263,13 +264,14 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
 		goto return_get_max_state;
 	}
 
-	while (table[i].frequency != CPUFREQ_TABLE_END) {
+	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
 		if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
 			continue;
-		i++;
+		count++;
 	}
-	if (i > 0) {
-		*state = --i;
+
+	if (count > 0) {
+		*state = --count;
 		ret = 0;
 	}
 
-- 
1.7.11.3

^ permalink raw reply related

* [PATCH V3 1/5] Thermal: add indent for code alignment.
From: hongbo.zhang @ 2012-10-30 16:48 UTC (permalink / raw)
  To: linaro-dev, linux-kernel, linux-pm, rui.zhang, amit.kachhap
  Cc: patches, linaro-kernel, STEricsson_nomadik_linux, kernel,
	hongbo.zhang
In-Reply-To: <1351615741-24134-1-git-send-email-hongbo.zhang@linaro.com>

From: "hongbo.zhang" <hongbo.zhang@linaro.com>

The curly bracket should be aligned with corresponding if else statements.

Signed-off-by: hongbo.zhang <hongbo.zhang@linaro.com>
Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 drivers/thermal/cpu_cooling.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index cc1c930..b6b4c2a 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -369,7 +369,7 @@ struct thermal_cooling_device *cpufreq_cooling_register(
 			if (min != policy.cpuinfo.min_freq ||
 				max != policy.cpuinfo.max_freq)
 				return ERR_PTR(-EINVAL);
-}
+		}
 	}
 	cpufreq_dev = kzalloc(sizeof(struct cpufreq_cooling_device),
 			GFP_KERNEL);
-- 
1.7.11.3

^ permalink raw reply related

* Re: [PATCH v3 2/6] PM / Runtime: introduce pm_runtime_set[get]_memalloc_noio()
From: Oliver Neukum @ 2012-10-30 16:30 UTC (permalink / raw)
  To: Ming Lei
  Cc: Alan Stern, linux-kernel, Minchan Kim, Greg Kroah-Hartman,
	Rafael J. Wysocki, Jens Axboe, David S. Miller, Andrew Morton,
	netdev, linux-usb, linux-pm, linux-mm
In-Reply-To: <CACVXFVO5-UPNrWsySzDE5AfOv1TMqbyitQX9ViidSJPM36fqAQ@mail.gmail.com>

On Wednesday 31 October 2012 00:00:56 Ming Lei wrote:
> On Tue, Oct 30, 2012 at 11:38 PM, Alan Stern <stern@rowland.harvard.edu> wrote:
> 
> >
> > Okay, I see your point.  But acquiring the lock here doesn't solve the
> > problem.  Suppose a thread is about to reset a USB mass-storage device.
> > It acquires the lock and sees that the noio flag is clear.  But before
> > it can issue the reset, another thread sets the noio flag.
> 
> If the USB mass-storage device is being reseted, the flag should be set
> already generally.  If the flag is still unset, that means the disk/network
> device isn't added into system(or removed just now), so memory allocation
> with block I/O should be allowed during the reset. Looks it isn't one problem,
> isn't it?

I am afraid it is, because a disk may just have been probed as the deviceis being reset.

	Regards
		Oliver


^ permalink raw reply

* Re: [PATCH v3 2/6] PM / Runtime: introduce pm_runtime_set[get]_memalloc_noio()
From: Ming Lei @ 2012-10-30 16:15 UTC (permalink / raw)
  To: Alan Stern
  Cc: linux-kernel, Oliver Neukum, Minchan Kim, Greg Kroah-Hartman,
	Rafael J. Wysocki, Jens Axboe, David S. Miller, Andrew Morton,
	netdev, linux-usb, linux-pm, linux-mm
In-Reply-To: <CACVXFVO5-UPNrWsySzDE5AfOv1TMqbyitQX9ViidSJPM36fqAQ@mail.gmail.com>

On Wed, Oct 31, 2012 at 12:00 AM, Ming Lei <ming.lei@canonical.com> wrote:
>
> Looks the simplest approach is to handle the noio flag thing at the start and
> end of rpm_resume.

Sorry, that doesn't work, runtime_suspend need that too because memory
allocation with block I/O might deadlock when doing I/O on the same device.

Thanks,
--
Ming Lei

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH v3 2/6] PM / Runtime: introduce pm_runtime_set[get]_memalloc_noio()
From: Ming Lei @ 2012-10-30 16:00 UTC (permalink / raw)
  To: Alan Stern
  Cc: linux-kernel, Oliver Neukum, Minchan Kim, Greg Kroah-Hartman,
	Rafael J. Wysocki, Jens Axboe, David S. Miller, Andrew Morton,
	netdev, linux-usb, linux-pm, linux-mm
In-Reply-To: <Pine.LNX.4.44L0.1210301112270.1363-100000@iolanthe.rowland.org>

On Tue, Oct 30, 2012 at 11:38 PM, Alan Stern <stern@rowland.harvard.edu> wrote:

>
> Okay, I see your point.  But acquiring the lock here doesn't solve the
> problem.  Suppose a thread is about to reset a USB mass-storage device.
> It acquires the lock and sees that the noio flag is clear.  But before
> it can issue the reset, another thread sets the noio flag.

If the USB mass-storage device is being reseted, the flag should be set
already generally.  If the flag is still unset, that means the disk/network
device isn't added into system(or removed just now), so memory allocation
with block I/O should be allowed during the reset. Looks it isn't one problem,
isn't it?

> I'm not sure what the best solution is.
>
>> The lock needn't to be held when the function is called inside
>> pm_runtime_set_memalloc_noio(),  so the bitfield flag should
>> be checked directly without holding power lock in dev_memalloc_noio().
>
> Yes.
>
> A couple of other things...  Runtime resume can be blocked by runtime
> suspend, if a resume is requested while the suspend is in progress.
> Therefore the runtime suspend code also needs to save-set-restore the
> noio flag.

Looks the simplest approach is to handle the noio flag thing at the start and
end of rpm_resume.

> Also, we should set the noio flag at the start of
> usb_stor_control_thread, because everything that thread does can
> potentially block an I/O operation.

Yes, it should be done, and all GFP_NOIO in usbcore should be converted
into GFP_KERNEL together. And the work shouldn't be started until
the patchset is merged.

> Lastly, pm_runtime_get_memalloc_noio always returns false when
> CONFIG_PM_RUNTIME is disabled.  But we still need to prevent I/O during
> usb_reset_device even when there's no runtime PM.  Maybe the simplest
> answer is always to set noio during resets.  That would also help with
> the race described above.

I have thought about this. IMO, pm_runtime_get_memalloc_noio should
return true always if CONFIG_PM_RUNTIME is unset.

Thanks,
--
Ming Lei

^ permalink raw reply

* Re: [PATCH v3 2/6] PM / Runtime: introduce pm_runtime_set[get]_memalloc_noio()
From: Alan Stern @ 2012-10-30 15:38 UTC (permalink / raw)
  To: Ming Lei
  Cc: linux-kernel, Oliver Neukum, Minchan Kim, Greg Kroah-Hartman,
	Rafael J. Wysocki, Jens Axboe, David S. Miller, Andrew Morton,
	netdev, linux-usb, linux-pm, linux-mm
In-Reply-To: <CACVXFVOPDu6wVgPmvtTkokn7VV41x3XVvL4g_E0pz0mikUbvUg@mail.gmail.com>

On Tue, 30 Oct 2012, Ming Lei wrote:

> >> +bool pm_runtime_get_memalloc_noio(struct device *dev)
> >> +{
> >> +     bool ret;
> >> +     spin_lock_irq(&dev->power.lock);
> >> +     ret = dev->power.memalloc_noio_resume;
> >> +     spin_unlock_irq(&dev->power.lock);
> >> +     return ret;
> >> +}
> >
> > You don't need to acquire and release a spinlock just to read the
> > value.  Reading bitfields _is_ SMP-safe; writing them is not.
> 
> Thanks for your review.
> 
> As you pointed out before, the flag need to be checked before
> resetting usb devices, so the lock should be held to make another
> context(CPU) see the updated value suppose one context(CPU)
> call pm_runtime_set_memalloc_noio() to change the flag at the
> same time.

Okay, I see your point.  But acquiring the lock here doesn't solve the 
problem.  Suppose a thread is about to reset a USB mass-storage device.  
It acquires the lock and sees that the noio flag is clear.  But before 
it can issue the reset, another thread sets the noio flag.

I'm not sure what the best solution is.

> The lock needn't to be held when the function is called inside
> pm_runtime_set_memalloc_noio(),  so the bitfield flag should
> be checked directly without holding power lock in dev_memalloc_noio().

Yes.

A couple of other things...  Runtime resume can be blocked by runtime
suspend, if a resume is requested while the suspend is in progress.  
Therefore the runtime suspend code also needs to save-set-restore the
noio flag.

Also, we should set the noio flag at the start of 
usb_stor_control_thread, because everything that thread does can 
potentially block an I/O operation.

Lastly, pm_runtime_get_memalloc_noio always returns false when 
CONFIG_PM_RUNTIME is disabled.  But we still need to prevent I/O during 
usb_reset_device even when there's no runtime PM.  Maybe the simplest 
answer is always to set noio during resets.  That would also help with 
the race described above.

Alan Stern


^ permalink raw reply

* [k.org requests #104] Add 2 categories to linux-pm bugzilla
From: Konstantin Ryabitsev via RT @ 2012-10-30 15:35 UTC (permalink / raw)
  To: lenb; +Cc: linux-pm
In-Reply-To: <rt-3.8.13-17378-1351610911-1063.104-6-0@kernel.org>

On Tue Oct 30 15:28:31 2012, rjw@sisk.pl wrote:
> Can we arrange things so that the _first_ message from every bug in
> these categories goes to linux-pm@vger.kernel.org and then they are
> assigned to a generic power management entity?

I don't believe bugzilla allows us such flexibility, so someone would
have to manually change the default assignee. However, you could set up
a whine script to send bug summaries to linux-pm, if you wanted to.

Best,
-- 
Konstantin Ryabitsev
Systems Administrator
Linux Foundation, kernel.org
Montréal, Québec

^ permalink raw reply

* Re: [k.org requests #104] Please add 2 categories
From: Rafael J. Wysocki via RT @ 2012-10-30 15:28 UTC (permalink / raw)
  To: lenb; +Cc: linux-pm
In-Reply-To: <1451306.UP2oztoM1Z@vostro.rjw.lan>

On Tuesday, October 30, 2012 03:24:00 PM Konstantin Ryabitsev via RT wrote:
> On Tue Oct 30 15:10:36 2012, lenb@kernel.org wrote:
> > Dear Bugzilla Admins,
> > 
> > Please add 2 additional Categories under Product "Power Management":
> > 
> > Run-Time-PM - Issues related to Linux Device Run Time Power
> > Management.
> > 
> > Thermal - Issues related to OS-visible Thermal Management, such as
> > throttling.
> 
> Sure, but I'll need "default assignee" for both of these.
> 
> Speaking of default assignees, kernel-bugs.osdl.org doesn't appear to
> exist any more either, so the following will need to be adjusted as well:
> 
> cpuidle             power-management_cpuidle@kernel-bugs.osdl.org 
> Hibernation/Suspend power-management_other@kernel-bugs.osdl.org 	
> intel_idle          power-management_intel_idle@kernel-bugs.osdl.org 	
> Other               power-management_other@kernel-bugs.osdl.org

Can we arrange things so that the _first_ message from every bug in
these categories goes to linux-pm@vger.kernel.org and then they are
assigned to a generic power management entity?

Rafael


-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.


^ permalink raw reply

* Re: [k.org requests #104] Please add 2 categories
From: Rafael J. Wysocki @ 2012-10-30 15:32 UTC (permalink / raw)
  To: rt; +Cc: lenb, linux-pm
In-Reply-To: <rt-3.8.13-793-1351610640-515.104-6-0@kernel.org>

On Tuesday, October 30, 2012 03:24:00 PM Konstantin Ryabitsev via RT wrote:
> On Tue Oct 30 15:10:36 2012, lenb@kernel.org wrote:
> > Dear Bugzilla Admins,
> > 
> > Please add 2 additional Categories under Product "Power Management":
> > 
> > Run-Time-PM - Issues related to Linux Device Run Time Power
> > Management.
> > 
> > Thermal - Issues related to OS-visible Thermal Management, such as
> > throttling.
> 
> Sure, but I'll need "default assignee" for both of these.
> 
> Speaking of default assignees, kernel-bugs.osdl.org doesn't appear to
> exist any more either, so the following will need to be adjusted as well:
> 
> cpuidle             power-management_cpuidle@kernel-bugs.osdl.org 
> Hibernation/Suspend power-management_other@kernel-bugs.osdl.org 	
> intel_idle          power-management_intel_idle@kernel-bugs.osdl.org 	
> Other               power-management_other@kernel-bugs.osdl.org

Can we arrange things so that the _first_ message from every bug in
these categories goes to linux-pm@vger.kernel.org and then they are
assigned to a generic power management entity?

Rafael


-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply

* Re: [RFD][PATCH 5/7] PM / QoS: Make it possible to expose PM QoS device flags to user space
From: Rafael J. Wysocki @ 2012-10-30 15:29 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Linux PM list, ACPI Devel Mailing List, Alan Stern, Huang Ying,
	Sarah Sharp, Lan Tianyu, Jean Pihet, linux-pci,
	Greg Kroah-Hartman, mark gross
In-Reply-To: <508F9588.5040105@intel.com>

On Tuesday, October 30, 2012 04:53:28 PM Aaron Lu wrote:
> On 09/29/2012 05:55 AM, Rafael J. Wysocki wrote:
> > Define two device PM QoS flags, PM_QOS_FLAG_NO_POWER_OFF
> > and PM_QOS_FLAG_REMOTE_WAKEUP, and introduce routines
> > dev_pm_qos_expose_flags() and dev_pm_qos_hide_flags() allowing the
> > caller to expose those two flags to user space or to hide them
> > from it, respectively.
> > 
> > After the flags have been exposed, user space will see two
> > additional sysfs attributes, pm_qos_no_power_off and
> > pm_qos_remote_wakeup, under the device's /sys/devices/.../power/
> > directory.  Then, writing 1 to one of them will update the
> > PM QoS flags request owned by user space so that the corresponding
> > flag is requested to be set.  In turn, writing 0 to one of them
> > will cause the corresponding flag in the user space's request to
> > be cleared (however, the owners of the other PM QoS flags requests
> > for the same device may still request the flag to be set and it
> > may be effectively set even if user space doesn't request that).
> > 
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > ---
> >  Documentation/ABI/testing/sysfs-devices-power |   31 ++++
> >  drivers/base/power/power.h                    |    6 
> >  drivers/base/power/qos.c                      |  169 ++++++++++++++++++++------
> >  drivers/base/power/sysfs.c                    |   95 +++++++++++++-
> >  include/linux/pm.h                            |    1 
> >  include/linux/pm_qos.h                        |   26 ++++
> >  6 files changed, 280 insertions(+), 48 deletions(-)
> > 
> > Index: linux/drivers/base/power/qos.c
> > ===================================================================
> > --- linux.orig/drivers/base/power/qos.c
> > +++ linux/drivers/base/power/qos.c
> > @@ -40,6 +40,7 @@
> >  #include <linux/device.h>
> >  #include <linux/mutex.h>
> >  #include <linux/export.h>
> > +#include <linux/pm_runtime.h>
> >  
> >  #include "power.h"
> >  
> > @@ -322,6 +323,38 @@ int dev_pm_qos_add_request(struct device
> >  EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
> >  
> >  /**
> > + * __dev_pm_qos_update_request - Modify an existing device PM QoS request.
> > + * @req : PM QoS request to modify.
> > + * @new_value: New value to request.
> > + */
> > +int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value)
> > +{
> > +	s32 curr_value;
> > +	int ret = 0;
> > +
> > +	if (!req->dev->power.qos) {
> > +		ret = -ENODEV;
> > +		goto out;
> > +	}
> > +
> > +	switch(req->type) {
> > +	case DEV_PM_QOS_LATENCY:
> > +		curr_value = req->data.pnode.prio;
> > +		break;
> > +	case DEV_PM_QOS_FLAGS:
> > +		curr_value = req->data.flr.flags;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (curr_value != new_value)
> > +		ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value);
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> >   * dev_pm_qos_update_request - modifies an existing qos request
> >   * @req : handle to list element holding a dev_pm_qos request to use
> >   * @new_value: defines the qos request
> > @@ -336,11 +369,9 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request
> >   * -EINVAL in case of wrong parameters, -ENODEV if the device has been
> >   * removed from the system
> >   */
> > -int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
> > -			      s32 new_value)
> > +int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value)
> >  {
> > -	s32 curr_value;
> > -	int ret = 0;
> > +	int ret;
> >  
> >  	if (!req) /*guard against callers passing in null */
> >  		return -EINVAL;
> > @@ -350,29 +381,9 @@ int dev_pm_qos_update_request(struct dev
> >  		return -EINVAL;
> >  
> >  	mutex_lock(&dev_pm_qos_mtx);
> > -
> > -	if (!req->dev->power.qos) {
> > -		ret = -ENODEV;
> > -		goto out;
> > -	}
> > -
> > -	switch(req->type) {
> > -	case DEV_PM_QOS_LATENCY:
> > -		curr_value = req->data.pnode.prio;
> > -		break;
> > -	case DEV_PM_QOS_FLAGS:
> > -		curr_value = req->data.flr.flags;
> > -		break;
> > -	default:
> > -		ret = -EINVAL;
> > -		goto out;
> > -	}
> > -
> > -	if (curr_value != new_value)
> > -		ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value);
> > -
> > - out:
> > +	__dev_pm_qos_update_request(req, new_value);
> 
> You forgot to assign the return value here.

Quite obviously.  I'm going to apply the appended patch for that.

Thanks for spotting it!

Rafael


---
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Subject: PM / QoS: Fix the return value of dev_pm_qos_update_request()

Commit e39473d (PM / QoS: Make it possible to expose PM QoS device)
flags to user space introduced __dev_pm_qos_update_request() to be
called internally by dev_pm_qos_update_request(), but forgot to make
the latter actually use the return value of the former.  Fix this
mistake.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/base/power/qos.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux/drivers/base/power/qos.c
===================================================================
--- linux.orig/drivers/base/power/qos.c
+++ linux/drivers/base/power/qos.c
@@ -380,7 +380,7 @@ int dev_pm_qos_update_request(struct dev
 		return -EINVAL;
 
 	mutex_lock(&dev_pm_qos_mtx);
-	__dev_pm_qos_update_request(req, new_value);
+	ret = __dev_pm_qos_update_request(req, new_value);
 	mutex_unlock(&dev_pm_qos_mtx);
 
 	return ret;

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply

* [k.org requests #104] Please add 2 categories
From: Konstantin Ryabitsev via RT @ 2012-10-30 15:24 UTC (permalink / raw)
  To: lenb; +Cc: linux-pm
In-Reply-To: <508F40D5.2010109@kernel.org>

On Tue Oct 30 15:10:36 2012, lenb@kernel.org wrote:
> Dear Bugzilla Admins,
> 
> Please add 2 additional Categories under Product "Power Management":
> 
> Run-Time-PM - Issues related to Linux Device Run Time Power
> Management.
> 
> Thermal - Issues related to OS-visible Thermal Management, such as
> throttling.

Sure, but I'll need "default assignee" for both of these.

Speaking of default assignees, kernel-bugs.osdl.org doesn't appear to
exist any more either, so the following will need to be adjusted as well:

cpuidle             power-management_cpuidle@kernel-bugs.osdl.org 
Hibernation/Suspend power-management_other@kernel-bugs.osdl.org 	
intel_idle          power-management_intel_idle@kernel-bugs.osdl.org 	
Other               power-management_other@kernel-bugs.osdl.org

Best,
-- 
Konstantin Ryabitsev
Systems Administrator
Linux Foundation, kernel.org
Montréal, Québec

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox