* Re: Subject: [PATCH] cpuidle: Add a sysfs entry to disable specific C state for debug purpose.
From: Deepthi Dharwar @ 2012-03-05 6:16 UTC (permalink / raw)
Cc: Andrew Morton, yanmin_zhang@linux.intel.com,
linux-kernel@vger.kernel.org, Brown, Len, Ingo Molnar,
Thomas Gleixner, H. Peter Anvin, linux-pm
In-Reply-To: <C6CB66606345BC4B80CBE69D22ABFBB10220E1@SHSMSX101.ccr.corp.intel.com>
Hi,
On 03/05/2012 08:52 AM, Liu, ShuoX wrote:
>>> It's useful to help developers debug some issues.
>>
>> It would help developers more if it was documented a bit. As it
>> stands, they'd be lucky if they even knew it existed.
>>
>> Looky:
>>
>> akpm:/usr/src/linux-3.3-rc5> grep -ril cpuidle Documentation
>> Documentation/trace/events-power.txt
>> Documentation/ABI/testing/sysfs-devices-system-cpu
>> Documentation/kernel-parameters.txt
>> Documentation/00-INDEX
>> Documentation/cpuidle/core.txt
>> Documentation/cpuidle/sysfs.txt
>> Documentation/cpuidle/driver.txt
>> Documentation/cpuidle/governor.txt
>
> Thanks Andrew's advice and Yanmin's review. Here is the new patch.
>
> From: ShuoX Liu <shuox.liu@intel.com>
>
> Some C states of new CPU might be not good. One reason is BIOS might configure
> them incorrectly. To help developers root cause it quickly, the patch adds a
> new sysfs entry, so developers could disable specific C state manually.
>
> In addition, C state might have much impact on performance tuning, as it takes
> much time to enter/exit C states, which might delay interrupt processing. With
> the new debug option, developers could check if a deep C state could impact
> performance and how much impact it could cause.
>
> Also add this option in Documentation/cpuidle/sysfs.txt.
Enabling/Disabling particular C-states from sysfs entry is really
helpful for cpuidle developers for general debugging and performance
tuning for not just x86 but for all platforms that support cpuidle,
like arm, powerpc etc .
>
> Signed-off-by: ShuoX Liu <shuox.liu@intel.com>
> Reviewed-by: Yanmin Zhang <yanmin_zhang@linux.intel.com>
> ---
> Documentation/cpuidle/sysfs.txt | 5 +++++
> drivers/cpuidle/cpuidle.c | 1 +
> drivers/cpuidle/governors/menu.c | 5 ++++-
> drivers/cpuidle/sysfs.c | 34 ++++++++++++++++++++++++++++++++++
> include/linux/cpuidle.h | 1 +
> 5 files changed, 45 insertions(+), 1 deletions(-)
>
> diff --git a/Documentation/cpuidle/sysfs.txt b/Documentation/cpuidle/sysfs.txt
> index 50d7b16..635424f 100644
> --- a/Documentation/cpuidle/sysfs.txt
> +++ b/Documentation/cpuidle/sysfs.txt
> @@ -36,6 +36,7 @@ drwxr-xr-x 2 root root 0 Feb 8 10:42 state3
> /sys/devices/system/cpu/cpu0/cpuidle/state0:
> total 0
> -r--r--r-- 1 root root 4096 Feb 8 10:42 desc
> +-r--r--r-- 1 root root 4096 Feb 8 10:42 disable
> -r--r--r-- 1 root root 4096 Feb 8 10:42 latency
> -r--r--r-- 1 root root 4096 Feb 8 10:42 name
> -r--r--r-- 1 root root 4096 Feb 8 10:42 power
Please update the documentation, rw fields are valid
for disable flag
/sys/devices/system/cpu/cpu2/cpuidle/state1 # ls -l
total 0
-r--r--r-- 1 root root 65536 Mar 5 11:28 desc
-rw-r--r-- 1 root root 65536 Mar 5 11:28 disable
-r--r--r-- 1 root root 65536 Mar 5 11:28 latency
-r--r--r-- 1 root root 65536 Mar 5 11:28 name
-r--r--r-- 1 root root 65536 Mar 5 11:28 power
-r--r--r-- 1 root root 65536 Mar 5 11:28 time
-r--r--r-- 1 root root 65536 Mar 5 11:28 usage
> @@ -45,6 +46,7 @@ total 0
> /sys/devices/system/cpu/cpu0/cpuidle/state1:
> total 0
> -r--r--r-- 1 root root 4096 Feb 8 10:42 desc
> +-r--r--r-- 1 root root 4096 Feb 8 10:42 disable
> -r--r--r-- 1 root root 4096 Feb 8 10:42 latency
> -r--r--r-- 1 root root 4096 Feb 8 10:42 name
> -r--r--r-- 1 root root 4096 Feb 8 10:42 power
> @@ -54,6 +56,7 @@ total 0
> /sys/devices/system/cpu/cpu0/cpuidle/state2:
> total 0
> -r--r--r-- 1 root root 4096 Feb 8 10:42 desc
> +-r--r--r-- 1 root root 4096 Feb 8 10:42 disable
> -r--r--r-- 1 root root 4096 Feb 8 10:42 latency
> -r--r--r-- 1 root root 4096 Feb 8 10:42 name
> -r--r--r-- 1 root root 4096 Feb 8 10:42 power
> @@ -63,6 +66,7 @@ total 0
> /sys/devices/system/cpu/cpu0/cpuidle/state3:
> total 0
> -r--r--r-- 1 root root 4096 Feb 8 10:42 desc
> +-r--r--r-- 1 root root 4096 Feb 8 10:42 disable
> -r--r--r-- 1 root root 4096 Feb 8 10:42 latency
> -r--r--r-- 1 root root 4096 Feb 8 10:42 name
> -r--r--r-- 1 root root 4096 Feb 8 10:42 power
> @@ -72,6 +76,7 @@ total 0
>
>
> * desc : Small description about the idle state (string)
> +* disable : Option to disable this idle state (bool)
> * latency : Latency to exit out of this idle state (in microseconds)
> * name : Name of the idle state (string)
> * power : Power consumed while in this idle state (in milliwatts)
> diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
> index 59f4261..7d66d3e 100644
> --- a/drivers/cpuidle/cpuidle.c
> +++ b/drivers/cpuidle/cpuidle.c
> @@ -197,6 +197,7 @@ static void poll_idle_init(struct cpuidle_driver *drv)
> state->power_usage = -1;
> state->flags = 0;
> state->enter = poll_idle;
> + state->disable = 0;
> }
> #else
> static void poll_idle_init(struct cpuidle_driver *drv) {}
> diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
> index ad09526..5c17ca1 100644
> --- a/drivers/cpuidle/governors/menu.c
> +++ b/drivers/cpuidle/governors/menu.c
> @@ -280,7 +280,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
> * We want to default to C1 (hlt), not to busy polling
> * unless the timer is happening really really soon.
> */
> - if (data->expected_us > 5)
> + if (data->expected_us > 5 &&
> + drv->states[CPUIDLE_DRIVER_STATE_START].disable == 0)
> data->last_state_idx = CPUIDLE_DRIVER_STATE_START;
>
> /*
> @@ -290,6 +291,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
> for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
> struct cpuidle_state *s = &drv->states[i];
>
> + if (s->disable)
> + continue;
> if (s->target_residency > data->predicted_us)
> continue;
> if (s->exit_latency > latency_req)
> diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
> index 3fe41fe..1eae29a 100644
> --- a/drivers/cpuidle/sysfs.c
> +++ b/drivers/cpuidle/sysfs.c
> @@ -222,6 +222,9 @@ struct cpuidle_state_attr {
> #define define_one_state_ro(_name, show) \
> static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL)
>
> +#define define_one_state_rw(_name, show, store) \
> +static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0644, show, store)
> +
> #define define_show_state_function(_name) \
> static ssize_t show_state_##_name(struct cpuidle_state *state, \
> struct cpuidle_state_usage *state_usage, char *buf) \
> @@ -229,6 +232,19 @@ static ssize_t show_state_##_name(struct cpuidle_state *state, \
> return sprintf(buf, "%u\n", state->_name);\
> }
>
> +#define define_store_state_function(_name) \
> +static ssize_t store_state_##_name(struct cpuidle_state *state, \
> + const char *buf, size_t size) \
> +{ \
> + int value; \
> + sscanf(buf, "%d", &value); \
> + if (value) \
> + state->disable = 1; \
> + else \
> + state->disable = 0; \
> + return size; \
> +}
> +
> #define define_show_state_ull_function(_name) \
> static ssize_t show_state_##_name(struct cpuidle_state *state, \
> struct cpuidle_state_usage *state_usage, char *buf) \
> @@ -251,6 +267,8 @@ define_show_state_ull_function(usage)
> define_show_state_ull_function(time)
> define_show_state_str_function(name)
> define_show_state_str_function(desc)
> +define_show_state_function(disable)
> +define_store_state_function(disable)
>
> define_one_state_ro(name, show_state_name);
> define_one_state_ro(desc, show_state_desc);
> @@ -258,6 +276,7 @@ define_one_state_ro(latency, show_state_exit_latency);
> define_one_state_ro(power, show_state_power_usage);
> define_one_state_ro(usage, show_state_usage);
> define_one_state_ro(time, show_state_time);
> +define_one_state_rw(disable, show_state_disable, store_state_disable);
>
> static struct attribute *cpuidle_state_default_attrs[] = {
> &attr_name.attr,
> @@ -266,6 +285,7 @@ static struct attribute *cpuidle_state_default_attrs[] = {
> &attr_power.attr,
> &attr_usage.attr,
> &attr_time.attr,
> + &attr_disable.attr,
> NULL
> };
>
> @@ -287,8 +307,22 @@ static ssize_t cpuidle_state_show(struct kobject * kobj,
> return ret;
> }
>
> +static ssize_t cpuidle_state_store(struct kobject *kobj,
> + struct attribute *attr, const char *buf, size_t size)
> +{
> + int ret = -EIO;
> + struct cpuidle_state *state = kobj_to_state(kobj);
> + struct cpuidle_state_attr *cattr = attr_to_stateattr(attr);
> +
> + if (cattr->store)
> + ret = cattr->store(state, buf, size);
> +
> + return ret;
> +}
> +
> static const struct sysfs_ops cpuidle_state_sysfs_ops = {
> .show = cpuidle_state_show,
> + .store = cpuidle_state_store,
> };
>
> static void cpuidle_state_sysfs_release(struct kobject *kobj)
> diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
> index 712abcc..eb150a5 100644
> --- a/include/linux/cpuidle.h
> +++ b/include/linux/cpuidle.h
> @@ -45,6 +45,7 @@ struct cpuidle_state {
> unsigned int exit_latency; /* in US */
> unsigned int power_usage; /* in mW */
> unsigned int target_residency; /* in US */
> + unsigned int disable;
>
> int (*enter) (struct cpuidle_device *dev,
> struct cpuidle_driver *drv,
>
Reviewed-and-Tested-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Cheers,
Deepthi
^ permalink raw reply
* Re: [linux-pm] [Patch] JBD and JBD2 missing set_freezable()
From: Rafael J. Wysocki @ 2012-03-04 22:51 UTC (permalink / raw)
To: linux-pm; +Cc: Nigel Cunningham, Jan Kara, linux-fsdevel, LKML, linux-ext4
In-Reply-To: <4F2BA1FD.3030302@tuxonice.net>
On Friday, February 03, 2012, Nigel Cunningham wrote:
> Hi all.
>
> With the latest and greatest changes to the freezer, I started seeing
> panics that were caused by jbd2 running post-process freezing and
> hitting the canary BUG_ON for non-TuxOnIce I/O submission. I've traced
> this back to a lack of set_freezable calls in both jbd and jbd2. Since
> they're clearly meant to be frozen (there are tests for freezing()), I
> submit the following patch to add the missing calls.
>
> Signed-off-by: Nigel Cunningham <nigel@tuxonice.net>
Well, I wonder what the filesystems people think about that.
Thanks,
Rafael
> diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c
> index 59c09f9..89cd985 100644
> --- a/fs/jbd/journal.c
> +++ b/fs/jbd/journal.c
> @@ -129,6 +129,8 @@ static int kjournald(void *arg)
> setup_timer(&journal->j_commit_timer, commit_timeout,
> (unsigned long)current);
>
> + set_freezable();
> +
> /* Record that the journal thread is running */
> journal->j_task = current;
> wake_up(&journal->j_wait_done_commit);
> diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
> index c0a5f9f..663e47c 100644
> --- a/fs/jbd2/journal.c
> +++ b/fs/jbd2/journal.c
> @@ -139,6 +139,8 @@ static int kjournald2(void *arg)
> setup_timer(&journal->j_commit_timer, commit_timeout,
> (unsigned long)current);
>
> + set_freezable();
> +
> /* Record that the journal thread is running */
> journal->j_task = current;
> wake_up(&journal->j_wait_done_commit);
>
^ permalink raw reply
* 3.3-rc6: Reported regressions 3.1 -> 3.2
From: Rafael J. Wysocki @ 2012-03-04 21:15 UTC (permalink / raw)
To: Linux Kernel Mailing List
Cc: Maciej Rutecki, Florian Mickler, Andrew Morton, Linus Torvalds,
Kernel Testers List, Network Development, Linux ACPI,
Linux PM List, Linux SCSI List, Linux Wireless List, DRI
This message contains a list of some post-3.1 regressions introduced before
3.2, for which there are no fixes in the mainline known to the tracking team.
If any of them have been fixed already, please let us know.
If you know of any other unresolved post-3.1 regressions, please let us know
either and we'll add them to the list. Also, please let us know if any
of the entries below are invalid.
Each entry from the list will be sent additionally in an automatic reply to
this message with CCs to the people involved in reporting and handling the
issue.
Listed regressions statistics:
Date Total Pending Unresolved
----------------------------------------
2012-03-04 9 8 7
2012-02-24 9 8 7
Unresolved regressions
----------------------
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42714
Subject : 3.2.x Regression for SAS Controller on Dell C6100
Submitter : Herbert Poetzl <herbert-dBHVzrDq9nF4Lj/PQRBjDg@public.gmane.org>
Date : 2012-01-31 18:09 (34 days old)
Message-ID : <20120131180949.GT29272-ZD0Mn47LIGX0Pe/G4T7+5F6hYfS7NtTn@public.gmane.org>
References : http://marc.info/?l=linux-kernel&m=132803360019481&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42685
Subject : multi-media key "mute" no longer recognized
Submitter : Toralf Förster <toralf.foerster-Mmb7MZpHnFY@public.gmane.org>
Date : 2012-01-23 10:52 (42 days old)
Message-ID : <201201231152.25344.toralf.foerster-Mmb7MZpHnFY@public.gmane.org>
References : http://marc.info/?l=linux-kernel&m=132731613606121&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42684
Subject : FYIO: usb/video 3.2 breaks logitech 9000pro webcam focus control
Submitter : M G Berberich <berberic-vmG2aq7wDA3mFSIgyKwhw7NAH6kLmebB@public.gmane.org>
Date : 2012-01-23 21:49 (42 days old)
Message-ID : <20120123214934.GA4240@invalid>
References : http://marc.info/?l=linux-kernel&m=132735609521443&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42631
Subject : Sound broken in 3.2.0 and 3.2.1
Submitter : Joseph Parmelee <jparmele-0t/HR9YC43JWk0Htik3J/w@public.gmane.org>
Date : 2012-01-13 19:44 (52 days old)
Message-ID : <alpine.LNX.2.00.1201131241090.1239@bruno>
References : http://marc.info/?l=linux-kernel&m=132648462806235&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42629
Subject : recvmsg sleeping from invalid context
Submitter : Dave Jones <davej-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Date : 2012-01-13 18:24 (52 days old)
Message-ID : <20120113182401.GA25885-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
References : http://marc.info/?l=linux-kernel&m=132647921304184&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42611
Subject : kernel 3.2 crashes too early on HP dc7700 and HP t5000 series
Submitter : Dmitry D. Khlebnikov <galaxy-cxoSlKxDwOJWk0Htik3J/w@public.gmane.org>
Date : 2012-01-10 11:49 (55 days old)
Message-ID : <20120110114905.GC13535-cxoSlKxDwOJWk0Htik3J/w@public.gmane.org>
References : http://marc.info/?l=linux-kernel&m=132619793012702&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42610
Subject : 3.2.0 reboot powers off machine
Submitter : Tom Weber <l_linux-kernel-w8lRr17kQ6puIcIf6OMhTAC/G2K4zDHf@public.gmane.org>
Date : 2012-01-11 11:03 (54 days old)
Message-ID : <1326279818.24835.12.camel@utumno>
References : http://marc.info/?l=linux-kernel&m=132628059308023&w=2
Regressions with patches
------------------------
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42571
Subject : BUG in ofcs2_change_file_space
Submitter : Bret Towe <magnade-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Date : 2012-01-05 22:23 (60 days old)
Message-ID : <CALjC5haTNDMOTDPL1m1uBTc2a-Qb8cvR44SCaVDFMzGc+Gw3+w@mail.gmail.com>
References : http://marc.info/?l=linux-kernel&m=132580236211209&w=2
Patch : http://oss.oracle.com/pipermail/ocfs2-devel/2012-January/008464.html
For details, please visit the bug entries and follow the links given in
references.
As you can see, there is a Bugzilla entry for each of the listed regressions.
There also is a Bugzilla entry used for tracking the regressions introduced
between 3.1 and 3.2, unresolved as well as resolved, at:
https://bugzilla.kernel.org/show_bug.cgi?id=42566
Please let the tracking team know if there are any Bugzilla entries that
should be added to the list in there.
Thanks!
^ permalink raw reply
* 3.3-rc6: Reported regressions from 3.2
From: Rafael J. Wysocki @ 2012-03-04 20:29 UTC (permalink / raw)
To: Linux Kernel Mailing List
Cc: Maciej Rutecki, Florian Mickler, Andrew Morton, Linus Torvalds,
Kernel Testers List, Network Development, Linux ACPI,
Linux PM List, Linux SCSI List, Linux Wireless List, DRI
Hi all,
It looks like many people have switched away from the kernel Bugzilla
after the kernel.org outage and they are using various means of reporting
(and tracking) kernel bugs. For this reason, it's becoming increasingly
difficult for us to track all of the possible sources of reports and we
are not aware of a number of them as a result. To tackle this issue we
kindly request that regression reports be CCed or forwarded to the LKML
as stated below.
Thanks,
Rafael
-----------------------------------------------------------------------------
This message contains a list of some regressions from 3.2,
for which there are no fixes in the mainline known to the tracking team.
If any of them have been fixed already, please let us know.
If you know of any other unresolved regressions from 3.2, please let us
know either and we'll add them to the list. Moreover, when you are
reporting a kernel regression, please send a CC of your report to the LKML
(or forward it to the LKML if you are not reporting the regression by e-mail)
with the word "regression" in the subject, as that will make it much
easier for us to find it. Also, please let us know if any of the entries
below are invalid.
Each entry from the list will be sent additionally in an automatic reply
to this message with CCs to the people involved in reporting and handling
the issue.
Listed regressions statistics:
Date Total Pending Unresolved
----------------------------------------
2012-03-04 23 15 15
2012-02-23 15 13 13
Unresolved regressions
----------------------
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42860
Subject : 3.3.0-rc5: OOps in dql_completed (Broadcom tg3 driver)
Submitter : Christoph Lameter <cl@gentwo.org>
Date : 2012-03-01 21:13 (4 days old)
Message-ID : <alpine.DEB.2.00.1203011505510.6685@router.home>
References : http://marc.info/?l=linux-kernel&m=133063645224373&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42850
Subject : [BUG] Kernel Bug at fs/btrfs/volumes.c:3638
Submitter : Nageswara R Sastry <rnsastry@linux.vnet.ibm.com>
Date : 2012-02-24 10:53 (10 days old)
Message-ID : <4F476959.2010400@linux.vnet.ibm.com>
References : http://marc.info/?l=linux-kernel&m=133008013332251&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42823
Subject : WARNING: at block/genhd.c:1465
Submitter : Christian Kujau <lists@nerdbynature.de>
Date : 2012-02-21 11:00 (13 days old)
Message-ID : <alpine.DEB.2.01.1202210246400.4930@trent.utfs.org>
References : http://marc.info/?l=linux-kernel&m=132982259213941&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42822
Subject : [3.3.0-rc3][uvcvideo][regression] oops - uvc_video_clock_update
Submitter : Shawn Starr <shawn.starr@rogers.com>
Date : 2012-02-20 1:08 (14 days old)
Message-ID : <5887142.ZMTDAjc4qf@segfault.sh0n.net>
References : http://marc.info/?l=linux-kernel&m=132970057611642&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42821
Subject : alpha: futex regression bisected
Submitter : Michael Cree <mcree@orcon.net.nz>
Date : 2012-02-20 8:20 (14 days old)
First-Bad-Commit: http://git.kernel.org/linus/8d7718aa082aaf30a0b4989e1f04858952f941bc
Message-ID : <4F420256.2090600@orcon.net.nz>
References : http://marc.info/?l=linux-kernel&m=132972606917069&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42776
Subject : OF-related boot crash in 3.3.0-rc3-00188-g3ec1e88
Submitter : Meelis Roos <mroos@linux.ee>
Date : 2012-02-13 7:45 (21 days old)
Message-ID : <alpine.SOC.1.00.1202130942030.1488@math.ut.ee>
References : http://marc.info/?l=linux-kernel&m=132911916331615&w=2
http://marc.info/?l=linux-kernel&m=132993294018762&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42761
Subject : Possible circular locking dependency (3.3-rc2)
Submitter : Felipe Balbi <balbi@ti.com>
Date : 2012-02-08 12:41 (26 days old)
Message-ID : <20120208124147.GF16334@legolas.emea.dhcp.ti.com>
References : http://marc.info/?l=linux-kernel&m=132870492311858&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42746
Subject : 3.3-rc2 snd_pcm lockdep backtrace
Submitter : Josh Boyer <jwboyer@redhat.com>
Date : 2012-02-06 14:56 (28 days old)
Message-ID : <20120206145621.GA4771@zod.bos.redhat.com>
References : http://marc.info/?l=linux-kernel&m=132854040916172&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42733
Subject : Regression 3.2 -> 3.3-rc1 10 sec hang at boot and resume, COMRESET failed
Submitter : Norbert Preining <preining@logic.at>
Date : 2012-02-02 5:12 (32 days old)
Message-ID : <20120202051258.GA15550@gamma.logic.tuwien.ac.at>
References : http://marc.info/?l=linux-kernel&m=132815978615112&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42713
Subject : Regression in skge that started around acb42a3 (so past v3.3-rc1)
Submitter : Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Date : 2012-01-30 15:58 (35 days old)
Message-ID : <20120130155816.GA1400@phenom.dumpdata.com>
References : http://marc.info/?l=linux-kernel&m=132793944220262&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42707
Subject : Hang deconfiguring network interface (in shutdown) on 3.3-rc1
Submitter : James Bottomley <James.Bottomley@hansenpartnership.com>
Date : 2012-01-28 19:56 (37 days old)
First-Bad-Commit: http://git.kernel.org/linus/92feeabf3f673767c6ee4cfc7fc224098446c1c1
Message-ID : <1327780565.2924.24.camel@dabdike.int.hansenpartnership.com>
References : http://marc.info/?l=linux-kernel&m=132778076214873&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42686
Subject : iwlagn is getting even worse with 3.3-rc1
Submitter : Norbert Preining <preining@logic.at>
Date : 2012-01-24 1:36 (41 days old)
Message-ID : <20120124013625.GB18436@gamma.logic.tuwien.ac.at>
References : http://marc.info/?l=linux-kernel&m=132736918325378&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42683
Subject : WARN... Device 'cpu1' does not have a release() function, it is broken and must be fixed. when doing 'xl vcpu-set <guest_id> 1'
Submitter : Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Date : 2012-01-23 18:06 (42 days old)
Message-ID : <20120123180601.GA24553@phenom.dumpdata.com>
References : http://marc.info/?l=linux-kernel&m=132734233916154&w=2
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42678
Subject : [3.3-rc1] radeon stuck in kernel after lockup
Submitter : Torsten Kaiser <just.for.lkml@googlemail.com>
Date : 2012-01-21 19:03 (44 days old)
Message-ID : <CAPVoSvSXMvRb=1itu9DjF+s=6zfAChvUxS-x=b8EV9kOZinNpA@mail.gmail.com>
References : http://marc.info/?l=linux-kernel&m=132717279606670&w=2
Handled-By : Jérôme Glisse <glisse@freedesktop.org>
Bug-Entry : https://bugzilla.kernel.org/show_bug.cgi?id=42649
Subject : Regression 3.2 -> 3.3-rc1 commit b4b583d4 inet/media keyboard: stuck "extra" keys
Submitter : Duncan <1i5t5.duncan@cox.net>
Date : 2012-01-24 18:18 (41 days old)
First-Bad-Commit: http://git.kernel.org/linus/b4b583d4e9a5ff28c4a150bb25a4fff5cd4dfbbd
For details, please visit the bug entries and follow the links given in
references.
As you can see, there is a Bugzilla entry for each of the listed regressions.
There also is a Bugzilla entry used for tracking the regressions from 3.2,
unresolved as well as resolved, at:
http://bugzilla.kernel.org/show_bug.cgi?id=42644
Please let the tracking team know if there are any Bugzilla entries that
should be added to the list in there.
Thanks!
^ permalink raw reply
* Re: [lm-sensors] [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
From: Guenter Roeck @ 2012-03-03 18:04 UTC (permalink / raw)
To: Mark Brown
Cc: Amit Daniel Kachhap, linux-samsung-soc@vger.kernel.org,
linaro-dev@lists.linaro.org, patches@linaro.org,
linux-kernel@vger.kernel.org, lm-sensors@lm-sensors.org,
linux-acpi@vger.kernel.org, linux-pm@lists.linux-foundation.org
In-Reply-To: <20120303164410.GB1495@sirena.org.uk>
On Sat, Mar 03, 2012 at 11:44:10AM -0500, Mark Brown wrote:
> On Sat, Mar 03, 2012 at 04:36:05PM +0530, Amit Daniel Kachhap wrote:
> > This movement is needed because the hwmon entries and corresponding
> > sysfs interface is a duplicate of utilities already provided by
> > driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> > and add necessary calls to get the temperature information.
>
> > --- a/Documentation/hwmon/exynos4_tmu
> > +++ /dev/null
>
> Moving this seems to be a failure, the device is exposing a hwmon
> interface even if you've moved the code to mfd (though it doesn't
> actually look like a multi-function device at all as far as I can see -
> usually a MFD would have a bunch of unrelated functionality while this
> has one function used by two subsystems).
>
> If anything it looks like the ADC driver ought to be moved into IIO with
> either generic or Exynos specific function drivers layered on top of it
> in hwmon and thermal making use of the values that are read.
>
I would agree. Or maybe move it all to thermal, since thermal devices register
the hwmon subsystem.
Guenter
^ permalink raw reply
* Re: [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
From: Mark Brown @ 2012-03-03 16:44 UTC (permalink / raw)
To: Amit Daniel Kachhap
Cc: linux-samsung-soc, linaro-dev, patches, linux-kernel, lm-sensors,
linux-acpi, linux-pm
In-Reply-To: <1330772767-16120-3-git-send-email-amit.kachhap@linaro.org>
On Sat, Mar 03, 2012 at 04:36:05PM +0530, Amit Daniel Kachhap wrote:
> This movement is needed because the hwmon entries and corresponding
> sysfs interface is a duplicate of utilities already provided by
> driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> and add necessary calls to get the temperature information.
> --- a/Documentation/hwmon/exynos4_tmu
> +++ /dev/null
Moving this seems to be a failure, the device is exposing a hwmon
interface even if you've moved the code to mfd (though it doesn't
actually look like a multi-function device at all as far as I can see -
usually a MFD would have a bunch of unrelated functionality while this
has one function used by two subsystems).
If anything it looks like the ADC driver ought to be moved into IIO with
either generic or Exynos specific function drivers layered on top of it
in hwmon and thermal making use of the values that are read.
^ permalink raw reply
* Re: [lm-sensors] [PATCH 3/4] thermal: exynos4: Register the tmu sensor with the thermal interface layer
From: Guenter Roeck @ 2012-03-03 14:55 UTC (permalink / raw)
To: Amit Daniel Kachhap
Cc: linux-pm@lists.linux-foundation.org,
linux-samsung-soc@vger.kernel.org, linaro-dev@lists.linaro.org,
patches@linaro.org, linux-kernel@vger.kernel.org,
lm-sensors@lm-sensors.org, eduardo.valentin@ti.com,
linux-acpi@vger.kernel.org, lenb@kernel.org
In-Reply-To: <1330772767-16120-4-git-send-email-amit.kachhap@linaro.org>
On Sat, Mar 03, 2012 at 06:06:06AM -0500, Amit Daniel Kachhap wrote:
> Export and register information from the tmu temperature sensor to the samsung
> exynos kernel thermal framework where different cooling devices and thermal
> zone are binded. The exported information is based according to the data
> structure thermal_sensor_conf present in exynos_thermal.h. HWMON sysfs
> functions are removed as all of them are present in generic linux thermal layer.
>
> Also the platform data structure is modified to pass frequency cooling
> in percentages for each thermal level.
>
> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
Ah, that is what I meant. I don't think it makes sense to have this as separate patch.
Guenter
^ permalink raw reply
* Re: [lm-sensors] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
From: Guenter Roeck @ 2012-03-03 14:52 UTC (permalink / raw)
To: Amit Daniel Kachhap
Cc: linux-pm@lists.linux-foundation.org,
linux-samsung-soc@vger.kernel.org, linaro-dev@lists.linaro.org,
patches@linaro.org, linux-kernel@vger.kernel.org,
lm-sensors@lm-sensors.org, eduardo.valentin@ti.com,
linux-acpi@vger.kernel.org, lenb@kernel.org
In-Reply-To: <1330772767-16120-3-git-send-email-amit.kachhap@linaro.org>
On Sat, Mar 03, 2012 at 06:06:05AM -0500, Amit Daniel Kachhap wrote:
> This movement is needed because the hwmon entries and corresponding
> sysfs interface is a duplicate of utilities already provided by
> driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> and add necessary calls to get the temperature information.
>
> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
> Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
> ---
> Documentation/hwmon/exynos4_tmu | 81 ------
> Documentation/mfd/exynos4_tmu | 81 ++++++
> drivers/hwmon/Kconfig | 10 -
> drivers/hwmon/Makefile | 1 -
> drivers/hwmon/exynos4_tmu.c | 514 ---------------------------------------
> drivers/mfd/Kconfig | 10 +
> drivers/mfd/Makefile | 1 +
> drivers/mfd/exynos4_tmu.c | 514 +++++++++++++++++++++++++++++++++++++++
> 8 files changed, 606 insertions(+), 606 deletions(-)
> delete mode 100644 Documentation/hwmon/exynos4_tmu
> create mode 100644 Documentation/mfd/exynos4_tmu
> delete mode 100644 drivers/hwmon/exynos4_tmu.c
> create mode 100644 drivers/mfd/exynos4_tmu.c
>
You are just moving the driver from hwmon to mfd. It is still a hwmon driver
and registers itself as hwmon driver. That does not make sense. If you want it
as mfd driver, it should not register itself as hwmon driver. It might have sub-devices
which are thermal and/or hwmon devices in the respective trees, or it might just be
a thermal driver (which then registers itself as hwmon device automatically).
Guenter
^ permalink raw reply
* Re: [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
From: Sylwester Nawrocki @ 2012-03-03 12:21 UTC (permalink / raw)
To: Amit Daniel Kachhap
Cc: linux-pm, linux-samsung-soc, linux-kernel, mjg59, linux-acpi,
lenb, linaro-dev, lm-sensors, eduardo.valentin, durgadoss.r,
patches
In-Reply-To: <1330772767-16120-3-git-send-email-amit.kachhap@linaro.org>
On 03/03/2012 12:06 PM, Amit Daniel Kachhap wrote:
> This movement is needed because the hwmon entries and corresponding
> sysfs interface is a duplicate of utilities already provided by
> driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> and add necessary calls to get the temperature information.
>
> Signed-off-by: Amit Daniel Kachhap<amit.kachhap@linaro.org>
> Signed-off-by: Donggeun Kim<dg77.kim@samsung.com>
> ---
> Documentation/hwmon/exynos4_tmu | 81 ------
> Documentation/mfd/exynos4_tmu | 81 ++++++
> drivers/hwmon/Kconfig | 10 -
> drivers/hwmon/Makefile | 1 -
> drivers/hwmon/exynos4_tmu.c | 514 ---------------------------------------
> drivers/mfd/Kconfig | 10 +
> drivers/mfd/Makefile | 1 +
> drivers/mfd/exynos4_tmu.c | 514 +++++++++++++++++++++++++++++++++++++++
> 8 files changed, 606 insertions(+), 606 deletions(-)
> delete mode 100644 Documentation/hwmon/exynos4_tmu
> create mode 100644 Documentation/mfd/exynos4_tmu
> delete mode 100644 drivers/hwmon/exynos4_tmu.c
> create mode 100644 drivers/mfd/exynos4_tmu.c
Please consider adding -M option to git format-patch next time, which
would make the patch smaller and would let to see clearly what's moved
and what has changed.
--
Thanks,
Sylwester
^ permalink raw reply
* [PATCH 4/4] ARM: exynos4: Add thermal sensor driver platform device support
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
To: linux-pm, linux-samsung-soc
Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi
In-Reply-To: <1330772767-16120-1-git-send-email-amit.kachhap@linaro.org>
This patch adds necessary source definations needed for TMU driver and
the platform device support.
Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
arch/arm/mach-exynos/Kconfig | 11 +++++
arch/arm/mach-exynos/Makefile | 1 +
arch/arm/mach-exynos/clock.c | 4 ++
arch/arm/mach-exynos/dev-tmu.c | 64 +++++++++++++++++++++++++++++
arch/arm/mach-exynos/include/mach/irqs.h | 2 +
arch/arm/mach-exynos/include/mach/map.h | 1 +
arch/arm/mach-exynos/mach-origen.c | 1 +
arch/arm/plat-samsung/include/plat/devs.h | 1 +
8 files changed, 85 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-exynos/dev-tmu.c
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index 5d602f6..03968a6 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -160,6 +160,16 @@ config EXYNOS4_SETUP_SPI
help
Common setup code for SPI GPIO configurations.
+config EXYNOS4_DEV_TMU
+ bool "Exynos4 tmu device support"
+ default n
+ depends on ARCH_EXYNOS4
+ ---help---
+ Compile in platform device definitions for TMU. This macro also
+ enables compilation hwmon base TMU driver and also allows compilation
+ of the platform device files. The platform data in this case is trip
+ temperature and some tmu h/w configurations related parameter.
+
# machine support
if ARCH_EXYNOS4
@@ -199,6 +209,7 @@ config MACH_SMDKV310
select SAMSUNG_DEV_PWM
select EXYNOS4_DEV_USB_OHCI
select EXYNOS4_DEV_SYSMMU
+ select EXYNOS4_DEV_TMU
select EXYNOS4_SETUP_FIMD0
select EXYNOS4_SETUP_I2C1
select EXYNOS4_SETUP_KEYPAD
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile
index 5fc202c..9b62e69 100644
--- a/arch/arm/mach-exynos/Makefile
+++ b/arch/arm/mach-exynos/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_EXYNOS4_DEV_SYSMMU) += dev-sysmmu.o
obj-$(CONFIG_EXYNOS4_DEV_DWMCI) += dev-dwmci.o
obj-$(CONFIG_EXYNOS4_DEV_DMA) += dma.o
obj-$(CONFIG_EXYNOS4_DEV_USB_OHCI) += dev-ohci.o
+obj-$(CONFIG_EXYNOS4_DEV_TMU) += dev-tmu.o
obj-$(CONFIG_ARCH_EXYNOS4) += setup-i2c0.o
obj-$(CONFIG_EXYNOS4_SETUP_FIMC) += setup-fimc.o
diff --git a/arch/arm/mach-exynos/clock.c b/arch/arm/mach-exynos/clock.c
index 187287a..3b15397 100644
--- a/arch/arm/mach-exynos/clock.c
+++ b/arch/arm/mach-exynos/clock.c
@@ -560,6 +560,10 @@ static struct clk init_clocks_off[] = {
.enable = exynos4_clk_ip_peril_ctrl,
.ctrlbit = (1 << 15),
}, {
+ .name = "tmu_apbif",
+ .enable = exynos4_clk_ip_perir_ctrl,
+ .ctrlbit = (1 << 17),
+ }, {
.name = "keypad",
.enable = exynos4_clk_ip_perir_ctrl,
.ctrlbit = (1 << 16),
diff --git a/arch/arm/mach-exynos/dev-tmu.c b/arch/arm/mach-exynos/dev-tmu.c
new file mode 100644
index 0000000..317b321
--- /dev/null
+++ b/arch/arm/mach-exynos/dev-tmu.c
@@ -0,0 +1,64 @@
+/* linux/arch/arm/mach-exynos4/dev-tmu.c
+ *
+ * Copyright 2011 by SAMSUNG
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/platform_data/exynos4_tmu.h>
+#include <asm/irq.h>
+
+#include <mach/irqs.h>
+#include <mach/map.h>
+#include <plat/devs.h>
+
+static struct resource exynos4_tmu_resource[] = {
+ [0] = {
+ .start = EXYNOS4_PA_TMU,
+ .end = EXYNOS4_PA_TMU + 0xFFFF - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_TMU_TRIG0,
+ .end = IRQ_TMU_TRIG0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct exynos4_tmu_platform_data default_tmu_data = {
+ .threshold = 80,
+ .trigger_levels[0] = 2,
+ .trigger_levels[1] = 5,
+ .trigger_levels[2] = 20,
+ .trigger_levels[3] = 30,
+ .trigger_level0_en = 1,
+ .trigger_level1_en = 1,
+ .trigger_level2_en = 1,
+ .trigger_level3_en = 1,
+ .gain = 15,
+ .reference_voltage = 7,
+ .cal_type = TYPE_ONE_POINT_TRIMMING,
+ .freq_tab[0] = {
+ .freq_clip_pctg = 30,
+ },
+ .freq_tab[1] = {
+ .freq_clip_pctg = 99,
+ },
+ .freq_tab_count = 2,
+};
+
+struct platform_device exynos4_device_tmu = {
+ .name = "exynos4-tmu",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(exynos4_tmu_resource),
+ .resource = exynos4_tmu_resource,
+ .dev = {
+ .platform_data = &default_tmu_data,
+ },
+};
diff --git a/arch/arm/mach-exynos/include/mach/irqs.h b/arch/arm/mach-exynos/include/mach/irqs.h
index f77bce0..f98d2e4 100644
--- a/arch/arm/mach-exynos/include/mach/irqs.h
+++ b/arch/arm/mach-exynos/include/mach/irqs.h
@@ -128,6 +128,8 @@
#define COMBINER_GROUP(x) ((x) * MAX_IRQ_IN_COMBINER + IRQ_SPI(128))
#define COMBINER_IRQ(x, y) (COMBINER_GROUP(x) + y)
+#define IRQ_TMU_TRIG0 COMBINER_IRQ(2, 4)
+#define IRQ_TMU_TRIG1 COMBINER_IRQ(3, 4)
#define IRQ_SYSMMU_MDMA0_0 COMBINER_IRQ(4, 0)
#define IRQ_SYSMMU_SSS_0 COMBINER_IRQ(4, 1)
#define IRQ_SYSMMU_FIMC0_0 COMBINER_IRQ(4, 2)
diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
index c754a22..bc11f1f 100644
--- a/arch/arm/mach-exynos/include/mach/map.h
+++ b/arch/arm/mach-exynos/include/mach/map.h
@@ -66,6 +66,7 @@
#define EXYNOS4_PA_COREPERI 0x10500000
#define EXYNOS4_PA_TWD 0x10500600
#define EXYNOS4_PA_L2CC 0x10502000
+#define EXYNOS4_PA_TMU 0x100C0000
#define EXYNOS4_PA_MDMA 0x10810000
#define EXYNOS4_PA_PDMA0 0x12680000
diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c
index 0679b8a..5d56e53 100644
--- a/arch/arm/mach-exynos/mach-origen.c
+++ b/arch/arm/mach-exynos/mach-origen.c
@@ -630,6 +630,7 @@ static struct platform_device *origen_devices[] __initdata = {
&exynos4_device_pd[PD_MFC],
&origen_device_gpiokeys,
&origen_lcd_hv070wsa,
+ &exynos4_device_tmu,
};
/* LCD Backlight data */
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index 4214ea0..0960405 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/arch/arm/plat-samsung/include/plat/devs.h
@@ -130,6 +130,7 @@ extern struct platform_device exynos4_device_pcm2;
extern struct platform_device exynos4_device_pd[];
extern struct platform_device exynos4_device_spdif;
extern struct platform_device exynos4_device_sysmmu;
+extern struct platform_device exynos4_device_tmu;
extern struct platform_device samsung_asoc_dma;
extern struct platform_device samsung_asoc_idma;
--
1.7.1
^ permalink raw reply related
* [PATCH 3/4] thermal: exynos4: Register the tmu sensor with the thermal interface layer
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
To: linux-pm, linux-samsung-soc
Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi
In-Reply-To: <1330772767-16120-1-git-send-email-amit.kachhap@linaro.org>
Export and register information from the tmu temperature sensor to the samsung
exynos kernel thermal framework where different cooling devices and thermal
zone are binded. The exported information is based according to the data
structure thermal_sensor_conf present in exynos_thermal.h. HWMON sysfs
functions are removed as all of them are present in generic linux thermal layer.
Also the platform data structure is modified to pass frequency cooling
in percentages for each thermal level.
Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
Documentation/mfd/exynos4_tmu | 35 +-------
drivers/mfd/exynos4_tmu.c | 139 +++++++----------------------
include/linux/platform_data/exynos4_tmu.h | 7 ++
3 files changed, 44 insertions(+), 137 deletions(-)
diff --git a/Documentation/mfd/exynos4_tmu b/Documentation/mfd/exynos4_tmu
index c3c6b41..2b46f67 100644
--- a/Documentation/mfd/exynos4_tmu
+++ b/Documentation/mfd/exynos4_tmu
@@ -46,36 +46,7 @@ The threshold levels are defined as follows:
The threshold and each trigger_level are set
through the corresponding registers.
-When an interrupt occurs, this driver notify user space of
-one of four threshold levels for the interrupt
-through kobject_uevent_env and sysfs_notify functions.
+When an interrupt occurs, this driver notify kernel thermal framework
+with the function exynos4_report_trigger.
Although an interrupt condition for level_0 can be set,
-it is not notified to user space through sysfs_notify function.
-
-Sysfs Interface
----------------
-name name of the temperature sensor
- RO
-
-temp1_input temperature
- RO
-
-temp1_max temperature for level_1 interrupt
- RO
-
-temp1_crit temperature for level_2 interrupt
- RO
-
-temp1_emergency temperature for level_3 interrupt
- RO
-
-temp1_max_alarm alarm for level_1 interrupt
- RO
-
-temp1_crit_alarm
- alarm for level_2 interrupt
- RO
-
-temp1_emergency_alarm
- alarm for level_3 interrupt
- RO
+it can be used to synchronize the cooling action.
diff --git a/drivers/mfd/exynos4_tmu.c b/drivers/mfd/exynos4_tmu.c
index f2359a0..e343923 100644
--- a/drivers/mfd/exynos4_tmu.c
+++ b/drivers/mfd/exynos4_tmu.c
@@ -33,10 +33,10 @@
#include <linux/io.h>
#include <linux/mutex.h>
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-
#include <linux/platform_data/exynos4_tmu.h>
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+#include <linux/exynos_thermal.h>
+#endif
#define EXYNOS4_TMU_REG_TRIMINFO 0x0
#define EXYNOS4_TMU_REG_CONTROL 0x20
@@ -70,7 +70,6 @@
struct exynos4_tmu_data {
struct exynos4_tmu_platform_data *pdata;
- struct device *hwmon_dev;
struct resource *mem;
void __iomem *base;
int irq;
@@ -246,12 +245,12 @@ static void exynos4_tmu_work(struct work_struct *work)
writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
- kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
-
- enable_irq(data->irq);
-
clk_disable(data->clk);
mutex_unlock(&data->lock);
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+ exynos4_report_trigger();
+#endif
+ enable_irq(data->irq);
}
static irqreturn_t exynos4_tmu_irq(int irq, void *id)
@@ -264,92 +263,19 @@ static irqreturn_t exynos4_tmu_irq(int irq, void *id)
return IRQ_HANDLED;
}
-static ssize_t exynos4_tmu_show_name(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "exynos4-tmu\n");
-}
-
-static ssize_t exynos4_tmu_show_temp(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct exynos4_tmu_data *data = dev_get_drvdata(dev);
- int ret;
-
- ret = exynos4_tmu_read(data);
- if (ret < 0)
- return ret;
-
- /* convert from degree Celsius to millidegree Celsius */
- return sprintf(buf, "%d\n", ret * 1000);
-}
-
-static ssize_t exynos4_tmu_show_alarm(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct exynos4_tmu_data *data = dev_get_drvdata(dev);
- struct exynos4_tmu_platform_data *pdata = data->pdata;
- int temp;
- unsigned int trigger_level;
-
- temp = exynos4_tmu_read(data);
- if (temp < 0)
- return temp;
-
- trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
-
- return sprintf(buf, "%d\n", !!(temp > trigger_level));
-}
-
-static ssize_t exynos4_tmu_show_level(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct exynos4_tmu_data *data = dev_get_drvdata(dev);
- struct exynos4_tmu_platform_data *pdata = data->pdata;
- unsigned int temp = pdata->threshold +
- pdata->trigger_levels[attr->index];
-
- return sprintf(buf, "%u\n", temp * 1000);
-}
-
-static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
-
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
- exynos4_tmu_show_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
- exynos4_tmu_show_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
- exynos4_tmu_show_alarm, NULL, 3);
-
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
- exynos4_tmu_show_level, NULL, 3);
-
-static struct attribute *exynos4_tmu_attributes[] = {
- &dev_attr_name.attr,
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- &sensor_dev_attr_temp1_emergency.dev_attr.attr,
- NULL,
-};
-
-static const struct attribute_group exynos4_tmu_attr_group = {
- .attrs = exynos4_tmu_attributes,
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+static struct thermal_sensor_conf exynos4_sensor_conf = {
+ .name = "exynos4-therm",
+ .read_temperature = (int (*)(void *))exynos4_tmu_read,
};
+#endif
+/*CONFIG_SAMSUNG_THERMAL_INTERFACE*/
static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
{
struct exynos4_tmu_data *data;
struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
- int ret;
+ int ret, i;
if (!pdata) {
dev_err(&pdev->dev, "No platform init data supplied.\n");
@@ -418,25 +344,27 @@ static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
goto err_clk;
}
- ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+ exynos4_tmu_control(pdev, true);
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+ (&exynos4_sensor_conf)->private_data = data;
+ exynos4_sensor_conf.trip_data.trip_count = 3;
+ for (i = 0; i < exynos4_sensor_conf.trip_data.trip_count; i++)
+ exynos4_sensor_conf.trip_data.trip_val[i] =
+ pdata->threshold + pdata->trigger_levels[i + 1];
+
+ exynos4_sensor_conf.cooling_data.freq_pctg_count =
+ pdata->freq_tab_count;
+ for (i = 0; i < pdata->freq_tab_count; i++)
+ exynos4_sensor_conf.cooling_data.freq_data[i].freq_clip_pctg =
+ pdata->freq_tab[i].freq_clip_pctg;
+
+ ret = exynos4_register_thermal(&exynos4_sensor_conf);
if (ret) {
- dev_err(&pdev->dev, "Failed to create sysfs group\n");
+ dev_err(&pdev->dev, "Failed to register thermal interface\n");
goto err_clk;
}
-
- data->hwmon_dev = hwmon_device_register(&pdev->dev);
- if (IS_ERR(data->hwmon_dev)) {
- ret = PTR_ERR(data->hwmon_dev);
- dev_err(&pdev->dev, "Failed to register hwmon device\n");
- goto err_create_group;
- }
-
- exynos4_tmu_control(pdev, true);
-
+#endif
return 0;
-
-err_create_group:
- sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
err_clk:
platform_set_drvdata(pdev, NULL);
clk_put(data->clk);
@@ -458,8 +386,9 @@ static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
exynos4_tmu_control(pdev, false);
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+ exynos4_unregister_thermal();
+#endif
clk_put(data->clk);
diff --git a/include/linux/platform_data/exynos4_tmu.h b/include/linux/platform_data/exynos4_tmu.h
index 39e038c..642c508 100644
--- a/include/linux/platform_data/exynos4_tmu.h
+++ b/include/linux/platform_data/exynos4_tmu.h
@@ -21,6 +21,7 @@
#ifndef _LINUX_EXYNOS4_TMU_H
#define _LINUX_EXYNOS4_TMU_H
+#include <linux/cpu_cooling.h>
enum calibration_type {
TYPE_ONE_POINT_TRIMMING,
@@ -64,6 +65,9 @@ enum calibration_type {
* in the positive-TC generator block
* 0 <= reference_voltage <= 31
* @cal_type: calibration type for temperature
+ * @freq_pctg_table: Table representing frequency reduction percentage.
+ * @freq_tab_count: Count of the above table as frequency reduction may
+ * applicable to only some of the trigger levels.
*
* This structure is required for configuration of exynos4_tmu driver.
*/
@@ -79,5 +83,8 @@ struct exynos4_tmu_platform_data {
u8 reference_voltage;
enum calibration_type cal_type;
+
+ struct freq_pctg_table freq_tab[4];
+ unsigned int freq_tab_count;
};
#endif /* _LINUX_EXYNOS4_TMU_H */
--
1.7.1
^ permalink raw reply related
* [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
To: linux-pm, linux-samsung-soc
Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi
In-Reply-To: <1330772767-16120-1-git-send-email-amit.kachhap@linaro.org>
This movement is needed because the hwmon entries and corresponding
sysfs interface is a duplicate of utilities already provided by
driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
and add necessary calls to get the temperature information.
Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
---
Documentation/hwmon/exynos4_tmu | 81 ------
Documentation/mfd/exynos4_tmu | 81 ++++++
drivers/hwmon/Kconfig | 10 -
drivers/hwmon/Makefile | 1 -
drivers/hwmon/exynos4_tmu.c | 514 ---------------------------------------
drivers/mfd/Kconfig | 10 +
drivers/mfd/Makefile | 1 +
drivers/mfd/exynos4_tmu.c | 514 +++++++++++++++++++++++++++++++++++++++
8 files changed, 606 insertions(+), 606 deletions(-)
delete mode 100644 Documentation/hwmon/exynos4_tmu
create mode 100644 Documentation/mfd/exynos4_tmu
delete mode 100644 drivers/hwmon/exynos4_tmu.c
create mode 100644 drivers/mfd/exynos4_tmu.c
diff --git a/Documentation/hwmon/exynos4_tmu b/Documentation/hwmon/exynos4_tmu
deleted file mode 100644
index c3c6b41..0000000
--- a/Documentation/hwmon/exynos4_tmu
+++ /dev/null
@@ -1,81 +0,0 @@
-Kernel driver exynos4_tmu
-=================
-
-Supported chips:
-* ARM SAMSUNG EXYNOS4 series of SoC
- Prefix: 'exynos4-tmu'
- Datasheet: Not publicly available
-
-Authors: Donggeun Kim <dg77.kim@samsung.com>
-
-Description
------------
-
-This driver allows to read temperature inside SAMSUNG EXYNOS4 series of SoC.
-
-The chip only exposes the measured 8-bit temperature code value
-through a register.
-Temperature can be taken from the temperature code.
-There are three equations converting from temperature to temperature code.
-
-The three equations are:
- 1. Two point trimming
- Tc = (T - 25) * (TI2 - TI1) / (85 - 25) + TI1
-
- 2. One point trimming
- Tc = T + TI1 - 25
-
- 3. No trimming
- Tc = T + 50
-
- Tc: Temperature code, T: Temperature,
- TI1: Trimming info for 25 degree Celsius (stored at TRIMINFO register)
- Temperature code measured at 25 degree Celsius which is unchanged
- TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register)
- Temperature code measured at 85 degree Celsius which is unchanged
-
-TMU(Thermal Management Unit) in EXYNOS4 generates interrupt
-when temperature exceeds pre-defined levels.
-The maximum number of configurable threshold is four.
-The threshold levels are defined as follows:
- Level_0: current temperature > trigger_level_0 + threshold
- Level_1: current temperature > trigger_level_1 + threshold
- Level_2: current temperature > trigger_level_2 + threshold
- Level_3: current temperature > trigger_level_3 + threshold
-
- The threshold and each trigger_level are set
- through the corresponding registers.
-
-When an interrupt occurs, this driver notify user space of
-one of four threshold levels for the interrupt
-through kobject_uevent_env and sysfs_notify functions.
-Although an interrupt condition for level_0 can be set,
-it is not notified to user space through sysfs_notify function.
-
-Sysfs Interface
----------------
-name name of the temperature sensor
- RO
-
-temp1_input temperature
- RO
-
-temp1_max temperature for level_1 interrupt
- RO
-
-temp1_crit temperature for level_2 interrupt
- RO
-
-temp1_emergency temperature for level_3 interrupt
- RO
-
-temp1_max_alarm alarm for level_1 interrupt
- RO
-
-temp1_crit_alarm
- alarm for level_2 interrupt
- RO
-
-temp1_emergency_alarm
- alarm for level_3 interrupt
- RO
diff --git a/Documentation/mfd/exynos4_tmu b/Documentation/mfd/exynos4_tmu
new file mode 100644
index 0000000..c3c6b41
--- /dev/null
+++ b/Documentation/mfd/exynos4_tmu
@@ -0,0 +1,81 @@
+Kernel driver exynos4_tmu
+=================
+
+Supported chips:
+* ARM SAMSUNG EXYNOS4 series of SoC
+ Prefix: 'exynos4-tmu'
+ Datasheet: Not publicly available
+
+Authors: Donggeun Kim <dg77.kim@samsung.com>
+
+Description
+-----------
+
+This driver allows to read temperature inside SAMSUNG EXYNOS4 series of SoC.
+
+The chip only exposes the measured 8-bit temperature code value
+through a register.
+Temperature can be taken from the temperature code.
+There are three equations converting from temperature to temperature code.
+
+The three equations are:
+ 1. Two point trimming
+ Tc = (T - 25) * (TI2 - TI1) / (85 - 25) + TI1
+
+ 2. One point trimming
+ Tc = T + TI1 - 25
+
+ 3. No trimming
+ Tc = T + 50
+
+ Tc: Temperature code, T: Temperature,
+ TI1: Trimming info for 25 degree Celsius (stored at TRIMINFO register)
+ Temperature code measured at 25 degree Celsius which is unchanged
+ TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register)
+ Temperature code measured at 85 degree Celsius which is unchanged
+
+TMU(Thermal Management Unit) in EXYNOS4 generates interrupt
+when temperature exceeds pre-defined levels.
+The maximum number of configurable threshold is four.
+The threshold levels are defined as follows:
+ Level_0: current temperature > trigger_level_0 + threshold
+ Level_1: current temperature > trigger_level_1 + threshold
+ Level_2: current temperature > trigger_level_2 + threshold
+ Level_3: current temperature > trigger_level_3 + threshold
+
+ The threshold and each trigger_level are set
+ through the corresponding registers.
+
+When an interrupt occurs, this driver notify user space of
+one of four threshold levels for the interrupt
+through kobject_uevent_env and sysfs_notify functions.
+Although an interrupt condition for level_0 can be set,
+it is not notified to user space through sysfs_notify function.
+
+Sysfs Interface
+---------------
+name name of the temperature sensor
+ RO
+
+temp1_input temperature
+ RO
+
+temp1_max temperature for level_1 interrupt
+ RO
+
+temp1_crit temperature for level_2 interrupt
+ RO
+
+temp1_emergency temperature for level_3 interrupt
+ RO
+
+temp1_max_alarm alarm for level_1 interrupt
+ RO
+
+temp1_crit_alarm
+ alarm for level_2 interrupt
+ RO
+
+temp1_emergency_alarm
+ alarm for level_3 interrupt
+ RO
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 0226040..5c4a0fb 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -313,16 +313,6 @@ config SENSORS_DS1621
This driver can also be built as a module. If so, the module
will be called ds1621.
-config SENSORS_EXYNOS4_TMU
- tristate "Temperature sensor on Samsung EXYNOS4"
- depends on ARCH_EXYNOS4
- help
- If you say yes here you get support for TMU (Thermal Managment
- Unit) on SAMSUNG EXYNOS4 series of SoC.
-
- This driver can also be built as a module. If so, the module
- will be called exynos4-tmu.
-
config SENSORS_I5K_AMB
tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets"
depends on PCI && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 8251ce8..228a4b7 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -48,7 +48,6 @@ obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o
obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o
obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o
-obj-$(CONFIG_SENSORS_EXYNOS4_TMU) += exynos4_tmu.o
obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
obj-$(CONFIG_SENSORS_F75375S) += f75375s.o
diff --git a/drivers/hwmon/exynos4_tmu.c b/drivers/hwmon/exynos4_tmu.c
deleted file mode 100644
index f2359a0..0000000
--- a/drivers/hwmon/exynos4_tmu.c
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit)
- *
- * Copyright (C) 2011 Samsung Electronics
- * Donggeun Kim <dg77.kim@samsung.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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/clk.h>
-#include <linux/workqueue.h>
-#include <linux/sysfs.h>
-#include <linux/kobject.h>
-#include <linux/io.h>
-#include <linux/mutex.h>
-
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-
-#include <linux/platform_data/exynos4_tmu.h>
-
-#define EXYNOS4_TMU_REG_TRIMINFO 0x0
-#define EXYNOS4_TMU_REG_CONTROL 0x20
-#define EXYNOS4_TMU_REG_STATUS 0x28
-#define EXYNOS4_TMU_REG_CURRENT_TEMP 0x40
-#define EXYNOS4_TMU_REG_THRESHOLD_TEMP 0x44
-#define EXYNOS4_TMU_REG_TRIG_LEVEL0 0x50
-#define EXYNOS4_TMU_REG_TRIG_LEVEL1 0x54
-#define EXYNOS4_TMU_REG_TRIG_LEVEL2 0x58
-#define EXYNOS4_TMU_REG_TRIG_LEVEL3 0x5C
-#define EXYNOS4_TMU_REG_PAST_TEMP0 0x60
-#define EXYNOS4_TMU_REG_PAST_TEMP1 0x64
-#define EXYNOS4_TMU_REG_PAST_TEMP2 0x68
-#define EXYNOS4_TMU_REG_PAST_TEMP3 0x6C
-#define EXYNOS4_TMU_REG_INTEN 0x70
-#define EXYNOS4_TMU_REG_INTSTAT 0x74
-#define EXYNOS4_TMU_REG_INTCLEAR 0x78
-
-#define EXYNOS4_TMU_GAIN_SHIFT 8
-#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT 24
-
-#define EXYNOS4_TMU_TRIM_TEMP_MASK 0xff
-#define EXYNOS4_TMU_CORE_ON 3
-#define EXYNOS4_TMU_CORE_OFF 2
-#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET 50
-#define EXYNOS4_TMU_TRIG_LEVEL0_MASK 0x1
-#define EXYNOS4_TMU_TRIG_LEVEL1_MASK 0x10
-#define EXYNOS4_TMU_TRIG_LEVEL2_MASK 0x100
-#define EXYNOS4_TMU_TRIG_LEVEL3_MASK 0x1000
-#define EXYNOS4_TMU_INTCLEAR_VAL 0x1111
-
-struct exynos4_tmu_data {
- struct exynos4_tmu_platform_data *pdata;
- struct device *hwmon_dev;
- struct resource *mem;
- void __iomem *base;
- int irq;
- struct work_struct irq_work;
- struct mutex lock;
- struct clk *clk;
- u8 temp_error1, temp_error2;
-};
-
-/*
- * TMU treats temperature as a mapped temperature code.
- * The temperature is converted differently depending on the calibration type.
- */
-static int temp_to_code(struct exynos4_tmu_data *data, u8 temp)
-{
- struct exynos4_tmu_platform_data *pdata = data->pdata;
- int temp_code;
-
- /* temp should range between 25 and 125 */
- if (temp < 25 || temp > 125) {
- temp_code = -EINVAL;
- goto out;
- }
-
- switch (pdata->cal_type) {
- case TYPE_TWO_POINT_TRIMMING:
- temp_code = (temp - 25) *
- (data->temp_error2 - data->temp_error1) /
- (85 - 25) + data->temp_error1;
- break;
- case TYPE_ONE_POINT_TRIMMING:
- temp_code = temp + data->temp_error1 - 25;
- break;
- default:
- temp_code = temp + EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
- break;
- }
-out:
- return temp_code;
-}
-
-/*
- * Calculate a temperature value from a temperature code.
- * The unit of the temperature is degree Celsius.
- */
-static int code_to_temp(struct exynos4_tmu_data *data, u8 temp_code)
-{
- struct exynos4_tmu_platform_data *pdata = data->pdata;
- int temp;
-
- /* temp_code should range between 75 and 175 */
- if (temp_code < 75 || temp_code > 175) {
- temp = -ENODATA;
- goto out;
- }
-
- switch (pdata->cal_type) {
- case TYPE_TWO_POINT_TRIMMING:
- temp = (temp_code - data->temp_error1) * (85 - 25) /
- (data->temp_error2 - data->temp_error1) + 25;
- break;
- case TYPE_ONE_POINT_TRIMMING:
- temp = temp_code - data->temp_error1 + 25;
- break;
- default:
- temp = temp_code - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
- break;
- }
-out:
- return temp;
-}
-
-static int exynos4_tmu_initialize(struct platform_device *pdev)
-{
- struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
- struct exynos4_tmu_platform_data *pdata = data->pdata;
- unsigned int status, trim_info;
- int ret = 0, threshold_code;
-
- mutex_lock(&data->lock);
- clk_enable(data->clk);
-
- status = readb(data->base + EXYNOS4_TMU_REG_STATUS);
- if (!status) {
- ret = -EBUSY;
- goto out;
- }
-
- /* Save trimming info in order to perform calibration */
- trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO);
- data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK;
- data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK);
-
- /* Write temperature code for threshold */
- threshold_code = temp_to_code(data, pdata->threshold);
- if (threshold_code < 0) {
- ret = threshold_code;
- goto out;
- }
- writeb(threshold_code,
- data->base + EXYNOS4_TMU_REG_THRESHOLD_TEMP);
-
- writeb(pdata->trigger_levels[0],
- data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0);
- writeb(pdata->trigger_levels[1],
- data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1);
- writeb(pdata->trigger_levels[2],
- data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2);
- writeb(pdata->trigger_levels[3],
- data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3);
-
- writel(EXYNOS4_TMU_INTCLEAR_VAL,
- data->base + EXYNOS4_TMU_REG_INTCLEAR);
-out:
- clk_disable(data->clk);
- mutex_unlock(&data->lock);
-
- return ret;
-}
-
-static void exynos4_tmu_control(struct platform_device *pdev, bool on)
-{
- struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
- struct exynos4_tmu_platform_data *pdata = data->pdata;
- unsigned int con, interrupt_en;
-
- mutex_lock(&data->lock);
- clk_enable(data->clk);
-
- con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT |
- pdata->gain << EXYNOS4_TMU_GAIN_SHIFT;
- if (on) {
- con |= EXYNOS4_TMU_CORE_ON;
- interrupt_en = pdata->trigger_level3_en << 12 |
- pdata->trigger_level2_en << 8 |
- pdata->trigger_level1_en << 4 |
- pdata->trigger_level0_en;
- } else {
- con |= EXYNOS4_TMU_CORE_OFF;
- interrupt_en = 0; /* Disable all interrupts */
- }
- writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN);
- writel(con, data->base + EXYNOS4_TMU_REG_CONTROL);
-
- clk_disable(data->clk);
- mutex_unlock(&data->lock);
-}
-
-static int exynos4_tmu_read(struct exynos4_tmu_data *data)
-{
- u8 temp_code;
- int temp;
-
- mutex_lock(&data->lock);
- clk_enable(data->clk);
-
- temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP);
- temp = code_to_temp(data, temp_code);
-
- clk_disable(data->clk);
- mutex_unlock(&data->lock);
-
- return temp;
-}
-
-static void exynos4_tmu_work(struct work_struct *work)
-{
- struct exynos4_tmu_data *data = container_of(work,
- struct exynos4_tmu_data, irq_work);
-
- mutex_lock(&data->lock);
- clk_enable(data->clk);
-
- writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
-
- kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
-
- enable_irq(data->irq);
-
- clk_disable(data->clk);
- mutex_unlock(&data->lock);
-}
-
-static irqreturn_t exynos4_tmu_irq(int irq, void *id)
-{
- struct exynos4_tmu_data *data = id;
-
- disable_irq_nosync(irq);
- schedule_work(&data->irq_work);
-
- return IRQ_HANDLED;
-}
-
-static ssize_t exynos4_tmu_show_name(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "exynos4-tmu\n");
-}
-
-static ssize_t exynos4_tmu_show_temp(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct exynos4_tmu_data *data = dev_get_drvdata(dev);
- int ret;
-
- ret = exynos4_tmu_read(data);
- if (ret < 0)
- return ret;
-
- /* convert from degree Celsius to millidegree Celsius */
- return sprintf(buf, "%d\n", ret * 1000);
-}
-
-static ssize_t exynos4_tmu_show_alarm(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct exynos4_tmu_data *data = dev_get_drvdata(dev);
- struct exynos4_tmu_platform_data *pdata = data->pdata;
- int temp;
- unsigned int trigger_level;
-
- temp = exynos4_tmu_read(data);
- if (temp < 0)
- return temp;
-
- trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
-
- return sprintf(buf, "%d\n", !!(temp > trigger_level));
-}
-
-static ssize_t exynos4_tmu_show_level(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct exynos4_tmu_data *data = dev_get_drvdata(dev);
- struct exynos4_tmu_platform_data *pdata = data->pdata;
- unsigned int temp = pdata->threshold +
- pdata->trigger_levels[attr->index];
-
- return sprintf(buf, "%u\n", temp * 1000);
-}
-
-static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
-
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
- exynos4_tmu_show_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
- exynos4_tmu_show_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
- exynos4_tmu_show_alarm, NULL, 3);
-
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
- exynos4_tmu_show_level, NULL, 3);
-
-static struct attribute *exynos4_tmu_attributes[] = {
- &dev_attr_name.attr,
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- &sensor_dev_attr_temp1_emergency.dev_attr.attr,
- NULL,
-};
-
-static const struct attribute_group exynos4_tmu_attr_group = {
- .attrs = exynos4_tmu_attributes,
-};
-
-static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
-{
- struct exynos4_tmu_data *data;
- struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
- int ret;
-
- if (!pdata) {
- dev_err(&pdev->dev, "No platform init data supplied.\n");
- return -ENODEV;
- }
-
- data = kzalloc(sizeof(struct exynos4_tmu_data), GFP_KERNEL);
- if (!data) {
- dev_err(&pdev->dev, "Failed to allocate driver structure\n");
- return -ENOMEM;
- }
-
- data->irq = platform_get_irq(pdev, 0);
- if (data->irq < 0) {
- ret = data->irq;
- dev_err(&pdev->dev, "Failed to get platform irq\n");
- goto err_free;
- }
-
- INIT_WORK(&data->irq_work, exynos4_tmu_work);
-
- data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!data->mem) {
- ret = -ENOENT;
- dev_err(&pdev->dev, "Failed to get platform resource\n");
- goto err_free;
- }
-
- data->mem = request_mem_region(data->mem->start,
- resource_size(data->mem), pdev->name);
- if (!data->mem) {
- ret = -ENODEV;
- dev_err(&pdev->dev, "Failed to request memory region\n");
- goto err_free;
- }
-
- data->base = ioremap(data->mem->start, resource_size(data->mem));
- if (!data->base) {
- ret = -ENODEV;
- dev_err(&pdev->dev, "Failed to ioremap memory\n");
- goto err_mem_region;
- }
-
- ret = request_irq(data->irq, exynos4_tmu_irq,
- IRQF_TRIGGER_RISING,
- "exynos4-tmu", data);
- if (ret) {
- dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
- goto err_io_remap;
- }
-
- data->clk = clk_get(NULL, "tmu_apbif");
- if (IS_ERR(data->clk)) {
- ret = PTR_ERR(data->clk);
- dev_err(&pdev->dev, "Failed to get clock\n");
- goto err_irq;
- }
-
- data->pdata = pdata;
- platform_set_drvdata(pdev, data);
- mutex_init(&data->lock);
-
- ret = exynos4_tmu_initialize(pdev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to initialize TMU\n");
- goto err_clk;
- }
-
- ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
- if (ret) {
- dev_err(&pdev->dev, "Failed to create sysfs group\n");
- goto err_clk;
- }
-
- data->hwmon_dev = hwmon_device_register(&pdev->dev);
- if (IS_ERR(data->hwmon_dev)) {
- ret = PTR_ERR(data->hwmon_dev);
- dev_err(&pdev->dev, "Failed to register hwmon device\n");
- goto err_create_group;
- }
-
- exynos4_tmu_control(pdev, true);
-
- return 0;
-
-err_create_group:
- sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-err_clk:
- platform_set_drvdata(pdev, NULL);
- clk_put(data->clk);
-err_irq:
- free_irq(data->irq, data);
-err_io_remap:
- iounmap(data->base);
-err_mem_region:
- release_mem_region(data->mem->start, resource_size(data->mem));
-err_free:
- kfree(data);
-
- return ret;
-}
-
-static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
-{
- struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
-
- exynos4_tmu_control(pdev, false);
-
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-
- clk_put(data->clk);
-
- free_irq(data->irq, data);
-
- iounmap(data->base);
- release_mem_region(data->mem->start, resource_size(data->mem));
-
- platform_set_drvdata(pdev, NULL);
-
- kfree(data);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state)
-{
- exynos4_tmu_control(pdev, false);
-
- return 0;
-}
-
-static int exynos4_tmu_resume(struct platform_device *pdev)
-{
- exynos4_tmu_initialize(pdev);
- exynos4_tmu_control(pdev, true);
-
- return 0;
-}
-#else
-#define exynos4_tmu_suspend NULL
-#define exynos4_tmu_resume NULL
-#endif
-
-static struct platform_driver exynos4_tmu_driver = {
- .driver = {
- .name = "exynos4-tmu",
- .owner = THIS_MODULE,
- },
- .probe = exynos4_tmu_probe,
- .remove = __devexit_p(exynos4_tmu_remove),
- .suspend = exynos4_tmu_suspend,
- .resume = exynos4_tmu_resume,
-};
-
-module_platform_driver(exynos4_tmu_driver);
-
-MODULE_DESCRIPTION("EXYNOS4 TMU Driver");
-MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:exynos4-tmu");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index f147395..e29113e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -830,6 +830,16 @@ config MFD_INTEL_MSIC
Passage) chip. This chip embeds audio, battery, GPIO, etc.
devices used in Intel Medfield platforms.
+config SENSORS_EXYNOS4_TMU
+ tristate "Temperature sensor on Samsung EXYNOS4"
+ depends on ARCH_EXYNOS4
+ help
+ If you say yes here you get support for TMU (Thermal Managment
+ Unit) on SAMSUNG EXYNOS4 series of SoC.
+
+ This driver can also be built as a module. If so, the module
+ will be called exynos4-tmu.
+
endmenu
endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b953bab..766bf86 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -112,3 +112,4 @@ obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o
+obj-$(CONFIG_SENSORS_EXYNOS4_TMU) += exynos4_tmu.o
diff --git a/drivers/mfd/exynos4_tmu.c b/drivers/mfd/exynos4_tmu.c
new file mode 100644
index 0000000..f2359a0
--- /dev/null
+++ b/drivers/mfd/exynos4_tmu.c
@@ -0,0 +1,514 @@
+/*
+ * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit)
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Donggeun Kim <dg77.kim@samsung.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/platform_data/exynos4_tmu.h>
+
+#define EXYNOS4_TMU_REG_TRIMINFO 0x0
+#define EXYNOS4_TMU_REG_CONTROL 0x20
+#define EXYNOS4_TMU_REG_STATUS 0x28
+#define EXYNOS4_TMU_REG_CURRENT_TEMP 0x40
+#define EXYNOS4_TMU_REG_THRESHOLD_TEMP 0x44
+#define EXYNOS4_TMU_REG_TRIG_LEVEL0 0x50
+#define EXYNOS4_TMU_REG_TRIG_LEVEL1 0x54
+#define EXYNOS4_TMU_REG_TRIG_LEVEL2 0x58
+#define EXYNOS4_TMU_REG_TRIG_LEVEL3 0x5C
+#define EXYNOS4_TMU_REG_PAST_TEMP0 0x60
+#define EXYNOS4_TMU_REG_PAST_TEMP1 0x64
+#define EXYNOS4_TMU_REG_PAST_TEMP2 0x68
+#define EXYNOS4_TMU_REG_PAST_TEMP3 0x6C
+#define EXYNOS4_TMU_REG_INTEN 0x70
+#define EXYNOS4_TMU_REG_INTSTAT 0x74
+#define EXYNOS4_TMU_REG_INTCLEAR 0x78
+
+#define EXYNOS4_TMU_GAIN_SHIFT 8
+#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT 24
+
+#define EXYNOS4_TMU_TRIM_TEMP_MASK 0xff
+#define EXYNOS4_TMU_CORE_ON 3
+#define EXYNOS4_TMU_CORE_OFF 2
+#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET 50
+#define EXYNOS4_TMU_TRIG_LEVEL0_MASK 0x1
+#define EXYNOS4_TMU_TRIG_LEVEL1_MASK 0x10
+#define EXYNOS4_TMU_TRIG_LEVEL2_MASK 0x100
+#define EXYNOS4_TMU_TRIG_LEVEL3_MASK 0x1000
+#define EXYNOS4_TMU_INTCLEAR_VAL 0x1111
+
+struct exynos4_tmu_data {
+ struct exynos4_tmu_platform_data *pdata;
+ struct device *hwmon_dev;
+ struct resource *mem;
+ void __iomem *base;
+ int irq;
+ struct work_struct irq_work;
+ struct mutex lock;
+ struct clk *clk;
+ u8 temp_error1, temp_error2;
+};
+
+/*
+ * TMU treats temperature as a mapped temperature code.
+ * The temperature is converted differently depending on the calibration type.
+ */
+static int temp_to_code(struct exynos4_tmu_data *data, u8 temp)
+{
+ struct exynos4_tmu_platform_data *pdata = data->pdata;
+ int temp_code;
+
+ /* temp should range between 25 and 125 */
+ if (temp < 25 || temp > 125) {
+ temp_code = -EINVAL;
+ goto out;
+ }
+
+ switch (pdata->cal_type) {
+ case TYPE_TWO_POINT_TRIMMING:
+ temp_code = (temp - 25) *
+ (data->temp_error2 - data->temp_error1) /
+ (85 - 25) + data->temp_error1;
+ break;
+ case TYPE_ONE_POINT_TRIMMING:
+ temp_code = temp + data->temp_error1 - 25;
+ break;
+ default:
+ temp_code = temp + EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
+ break;
+ }
+out:
+ return temp_code;
+}
+
+/*
+ * Calculate a temperature value from a temperature code.
+ * The unit of the temperature is degree Celsius.
+ */
+static int code_to_temp(struct exynos4_tmu_data *data, u8 temp_code)
+{
+ struct exynos4_tmu_platform_data *pdata = data->pdata;
+ int temp;
+
+ /* temp_code should range between 75 and 175 */
+ if (temp_code < 75 || temp_code > 175) {
+ temp = -ENODATA;
+ goto out;
+ }
+
+ switch (pdata->cal_type) {
+ case TYPE_TWO_POINT_TRIMMING:
+ temp = (temp_code - data->temp_error1) * (85 - 25) /
+ (data->temp_error2 - data->temp_error1) + 25;
+ break;
+ case TYPE_ONE_POINT_TRIMMING:
+ temp = temp_code - data->temp_error1 + 25;
+ break;
+ default:
+ temp = temp_code - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
+ break;
+ }
+out:
+ return temp;
+}
+
+static int exynos4_tmu_initialize(struct platform_device *pdev)
+{
+ struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+ struct exynos4_tmu_platform_data *pdata = data->pdata;
+ unsigned int status, trim_info;
+ int ret = 0, threshold_code;
+
+ mutex_lock(&data->lock);
+ clk_enable(data->clk);
+
+ status = readb(data->base + EXYNOS4_TMU_REG_STATUS);
+ if (!status) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* Save trimming info in order to perform calibration */
+ trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO);
+ data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK;
+ data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK);
+
+ /* Write temperature code for threshold */
+ threshold_code = temp_to_code(data, pdata->threshold);
+ if (threshold_code < 0) {
+ ret = threshold_code;
+ goto out;
+ }
+ writeb(threshold_code,
+ data->base + EXYNOS4_TMU_REG_THRESHOLD_TEMP);
+
+ writeb(pdata->trigger_levels[0],
+ data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0);
+ writeb(pdata->trigger_levels[1],
+ data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1);
+ writeb(pdata->trigger_levels[2],
+ data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2);
+ writeb(pdata->trigger_levels[3],
+ data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3);
+
+ writel(EXYNOS4_TMU_INTCLEAR_VAL,
+ data->base + EXYNOS4_TMU_REG_INTCLEAR);
+out:
+ clk_disable(data->clk);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static void exynos4_tmu_control(struct platform_device *pdev, bool on)
+{
+ struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+ struct exynos4_tmu_platform_data *pdata = data->pdata;
+ unsigned int con, interrupt_en;
+
+ mutex_lock(&data->lock);
+ clk_enable(data->clk);
+
+ con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT |
+ pdata->gain << EXYNOS4_TMU_GAIN_SHIFT;
+ if (on) {
+ con |= EXYNOS4_TMU_CORE_ON;
+ interrupt_en = pdata->trigger_level3_en << 12 |
+ pdata->trigger_level2_en << 8 |
+ pdata->trigger_level1_en << 4 |
+ pdata->trigger_level0_en;
+ } else {
+ con |= EXYNOS4_TMU_CORE_OFF;
+ interrupt_en = 0; /* Disable all interrupts */
+ }
+ writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN);
+ writel(con, data->base + EXYNOS4_TMU_REG_CONTROL);
+
+ clk_disable(data->clk);
+ mutex_unlock(&data->lock);
+}
+
+static int exynos4_tmu_read(struct exynos4_tmu_data *data)
+{
+ u8 temp_code;
+ int temp;
+
+ mutex_lock(&data->lock);
+ clk_enable(data->clk);
+
+ temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP);
+ temp = code_to_temp(data, temp_code);
+
+ clk_disable(data->clk);
+ mutex_unlock(&data->lock);
+
+ return temp;
+}
+
+static void exynos4_tmu_work(struct work_struct *work)
+{
+ struct exynos4_tmu_data *data = container_of(work,
+ struct exynos4_tmu_data, irq_work);
+
+ mutex_lock(&data->lock);
+ clk_enable(data->clk);
+
+ writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
+
+ kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
+
+ enable_irq(data->irq);
+
+ clk_disable(data->clk);
+ mutex_unlock(&data->lock);
+}
+
+static irqreturn_t exynos4_tmu_irq(int irq, void *id)
+{
+ struct exynos4_tmu_data *data = id;
+
+ disable_irq_nosync(irq);
+ schedule_work(&data->irq_work);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t exynos4_tmu_show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "exynos4-tmu\n");
+}
+
+static ssize_t exynos4_tmu_show_temp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = exynos4_tmu_read(data);
+ if (ret < 0)
+ return ret;
+
+ /* convert from degree Celsius to millidegree Celsius */
+ return sprintf(buf, "%d\n", ret * 1000);
+}
+
+static ssize_t exynos4_tmu_show_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+ struct exynos4_tmu_platform_data *pdata = data->pdata;
+ int temp;
+ unsigned int trigger_level;
+
+ temp = exynos4_tmu_read(data);
+ if (temp < 0)
+ return temp;
+
+ trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
+
+ return sprintf(buf, "%d\n", !!(temp > trigger_level));
+}
+
+static ssize_t exynos4_tmu_show_level(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+ struct exynos4_tmu_platform_data *pdata = data->pdata;
+ unsigned int temp = pdata->threshold +
+ pdata->trigger_levels[attr->index];
+
+ return sprintf(buf, "%u\n", temp * 1000);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
+
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
+ exynos4_tmu_show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
+ exynos4_tmu_show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
+ exynos4_tmu_show_alarm, NULL, 3);
+
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
+ exynos4_tmu_show_level, NULL, 3);
+
+static struct attribute *exynos4_tmu_attributes[] = {
+ &dev_attr_name.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_emergency.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group exynos4_tmu_attr_group = {
+ .attrs = exynos4_tmu_attributes,
+};
+
+static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
+{
+ struct exynos4_tmu_data *data;
+ struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform init data supplied.\n");
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(struct exynos4_tmu_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&pdev->dev, "Failed to allocate driver structure\n");
+ return -ENOMEM;
+ }
+
+ data->irq = platform_get_irq(pdev, 0);
+ if (data->irq < 0) {
+ ret = data->irq;
+ dev_err(&pdev->dev, "Failed to get platform irq\n");
+ goto err_free;
+ }
+
+ INIT_WORK(&data->irq_work, exynos4_tmu_work);
+
+ data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!data->mem) {
+ ret = -ENOENT;
+ dev_err(&pdev->dev, "Failed to get platform resource\n");
+ goto err_free;
+ }
+
+ data->mem = request_mem_region(data->mem->start,
+ resource_size(data->mem), pdev->name);
+ if (!data->mem) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "Failed to request memory region\n");
+ goto err_free;
+ }
+
+ data->base = ioremap(data->mem->start, resource_size(data->mem));
+ if (!data->base) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "Failed to ioremap memory\n");
+ goto err_mem_region;
+ }
+
+ ret = request_irq(data->irq, exynos4_tmu_irq,
+ IRQF_TRIGGER_RISING,
+ "exynos4-tmu", data);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
+ goto err_io_remap;
+ }
+
+ data->clk = clk_get(NULL, "tmu_apbif");
+ if (IS_ERR(data->clk)) {
+ ret = PTR_ERR(data->clk);
+ dev_err(&pdev->dev, "Failed to get clock\n");
+ goto err_irq;
+ }
+
+ data->pdata = pdata;
+ platform_set_drvdata(pdev, data);
+ mutex_init(&data->lock);
+
+ ret = exynos4_tmu_initialize(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to initialize TMU\n");
+ goto err_clk;
+ }
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create sysfs group\n");
+ goto err_clk;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ ret = PTR_ERR(data->hwmon_dev);
+ dev_err(&pdev->dev, "Failed to register hwmon device\n");
+ goto err_create_group;
+ }
+
+ exynos4_tmu_control(pdev, true);
+
+ return 0;
+
+err_create_group:
+ sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+err_clk:
+ platform_set_drvdata(pdev, NULL);
+ clk_put(data->clk);
+err_irq:
+ free_irq(data->irq, data);
+err_io_remap:
+ iounmap(data->base);
+err_mem_region:
+ release_mem_region(data->mem->start, resource_size(data->mem));
+err_free:
+ kfree(data);
+
+ return ret;
+}
+
+static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
+{
+ struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+
+ exynos4_tmu_control(pdev, false);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+
+ clk_put(data->clk);
+
+ free_irq(data->irq, data);
+
+ iounmap(data->base);
+ release_mem_region(data->mem->start, resource_size(data->mem));
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ exynos4_tmu_control(pdev, false);
+
+ return 0;
+}
+
+static int exynos4_tmu_resume(struct platform_device *pdev)
+{
+ exynos4_tmu_initialize(pdev);
+ exynos4_tmu_control(pdev, true);
+
+ return 0;
+}
+#else
+#define exynos4_tmu_suspend NULL
+#define exynos4_tmu_resume NULL
+#endif
+
+static struct platform_driver exynos4_tmu_driver = {
+ .driver = {
+ .name = "exynos4-tmu",
+ .owner = THIS_MODULE,
+ },
+ .probe = exynos4_tmu_probe,
+ .remove = __devexit_p(exynos4_tmu_remove),
+ .suspend = exynos4_tmu_suspend,
+ .resume = exynos4_tmu_resume,
+};
+
+module_platform_driver(exynos4_tmu_driver);
+
+MODULE_DESCRIPTION("EXYNOS4 TMU Driver");
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:exynos4-tmu");
--
1.7.1
^ permalink raw reply related
* [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
To: linux-pm, linux-samsung-soc
Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi
In-Reply-To: <1330772767-16120-1-git-send-email-amit.kachhap@linaro.org>
This codes uses the generic linux thermal layer and creates a bridge
between temperature sensors, linux thermal framework and cooling devices
for samsung exynos platform. This layer recieves or monitor the
temperature from the sensor and informs the generic thermal layer to take
the necessary cooling action.
Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
drivers/thermal/Kconfig | 8 +
drivers/thermal/Makefile | 1 +
drivers/thermal/exynos_thermal.c | 272 ++++++++++++++++++++++++++++++++++++++
include/linux/exynos_thermal.h | 72 ++++++++++
4 files changed, 353 insertions(+), 0 deletions(-)
create mode 100644 drivers/thermal/exynos_thermal.c
create mode 100644 include/linux/exynos_thermal.h
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 298c1cd..4e8df56 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -29,3 +29,11 @@ config CPU_THERMAL
This will be useful for platforms using the generic thermal interface
and not the ACPI interface.
If you want this support, you should say Y or M here.
+
+config SAMSUNG_THERMAL_INTERFACE
+ bool "Samsung Thermal interface support"
+ depends on THERMAL && CPU_THERMAL
+ help
+ This is a samsung thermal interface which will be used as
+ a link between sensors and cooling devices with linux thermal
+ framework.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 655cbc4..c67b6b2 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_THERMAL) += thermal_sys.o
obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
+obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE) += exynos_thermal.o
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
new file mode 100644
index 0000000..878d45c
--- /dev/null
+++ b/drivers/thermal/exynos_thermal.c
@@ -0,0 +1,272 @@
+/* linux/drivers/thermal/exynos_thermal.c
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/cpu_cooling.h>
+#include <linux/exynos_thermal.h>
+
+#define MAX_COOLING_DEVICE 4
+struct exynos4_thermal_zone {
+ unsigned int idle_interval;
+ unsigned int active_interval;
+ struct thermal_zone_device *therm_dev;
+ struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
+ unsigned int cool_dev_size;
+ struct platform_device *exynos4_dev;
+ struct thermal_sensor_conf *sensor_conf;
+};
+
+static struct exynos4_thermal_zone *th_zone;
+
+static int exynos4_get_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode *mode)
+{
+ if (th_zone->sensor_conf) {
+ pr_info("Temperature sensor not initialised\n");
+ *mode = THERMAL_DEVICE_DISABLED;
+ } else
+ *mode = THERMAL_DEVICE_ENABLED;
+ return 0;
+}
+
+static int exynos4_set_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode mode)
+{
+ if (!th_zone->therm_dev) {
+ pr_notice("thermal zone not registered\n");
+ return 0;
+ }
+ if (mode == THERMAL_DEVICE_ENABLED)
+ th_zone->therm_dev->polling_delay =
+ th_zone->active_interval*1000;
+ else
+ th_zone->therm_dev->polling_delay =
+ th_zone->idle_interval*1000;
+
+ thermal_zone_device_update(th_zone->therm_dev);
+ pr_info("thermal polling set for duration=%d sec\n",
+ th_zone->therm_dev->polling_delay/1000);
+ return 0;
+}
+
+/*This may be called from interrupt based temperature sensor*/
+void exynos4_report_trigger(void)
+{
+ unsigned int monitor_temp;
+
+ if (!th_zone || !th_zone->therm_dev)
+ return;
+
+ monitor_temp = th_zone->sensor_conf->trip_data.trip_val[0];
+
+ thermal_zone_device_update(th_zone->therm_dev);
+
+ mutex_lock(&th_zone->therm_dev->lock);
+ if (th_zone->therm_dev->last_temperature > monitor_temp)
+ th_zone->therm_dev->polling_delay =
+ th_zone->active_interval*1000;
+ else
+ th_zone->therm_dev->polling_delay =
+ th_zone->idle_interval*1000;
+
+ kobject_uevent(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE);
+ mutex_unlock(&th_zone->therm_dev->lock);
+}
+
+static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int trip,
+ enum thermal_trip_type *type)
+{
+ if (trip == 0 || trip == 1)
+ *type = THERMAL_TRIP_STATE_ACTIVE;
+ else if (trip == 2)
+ *type = THERMAL_TRIP_CRITICAL;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int exynos4_get_trip_temp(struct thermal_zone_device *thermal, int trip,
+ unsigned long *temp)
+{
+ /*Monitor zone*/
+ if (trip == 0)
+ *temp = th_zone->sensor_conf->trip_data.trip_val[0];
+ /*Warn zone*/
+ else if (trip == 1)
+ *temp = th_zone->sensor_conf->trip_data.trip_val[1];
+ /*Panic zone*/
+ else if (trip == 2)
+ *temp = th_zone->sensor_conf->trip_data.trip_val[2];
+ else
+ return -EINVAL;
+ /*convert the temperature into millicelsius*/
+ *temp = *temp * 1000;
+
+ return 0;
+}
+
+static int exynos4_get_crit_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp)
+{
+ /*Panic zone*/
+ *temp = th_zone->sensor_conf->trip_data.trip_val[2];
+ /*convert the temperature into millicelsius*/
+ *temp = *temp * 1000;
+ return 0;
+}
+
+static int exynos4_bind(struct thermal_zone_device *thermal,
+ struct thermal_cooling_device *cdev)
+{
+ /* if the cooling device is the one from exynos4 bind it */
+ if (cdev != th_zone->cool_dev[0])
+ return 0;
+
+ if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
+ pr_err("error binding cooling dev\n");
+ return -EINVAL;
+ }
+ if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) {
+ pr_err("error binding cooling dev\n");
+ return -EINVAL;
+ }
+
+ return 0;
+
+}
+
+static int exynos4_unbind(struct thermal_zone_device *thermal,
+ struct thermal_cooling_device *cdev)
+{
+ if (cdev != th_zone->cool_dev[0])
+ return 0;
+
+ if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
+ pr_err("error unbinding cooling dev\n");
+ return -EINVAL;
+ }
+ if (thermal_zone_unbind_cooling_device(thermal, 1, cdev)) {
+ pr_err("error unbinding cooling dev\n");
+ return -EINVAL;
+ }
+ return 0;
+
+}
+
+static int exynos4_get_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp)
+{
+ void *data;
+
+ if (!th_zone->sensor_conf) {
+ pr_info("Temperature sensor not initialised\n");
+ return -EINVAL;
+ }
+ data = th_zone->sensor_conf->private_data;
+ *temp = th_zone->sensor_conf->read_temperature(data);
+ /*convert the temperature into millicelsius*/
+ *temp = *temp * 1000;
+ return 0;
+}
+
+/* bind callback functions to thermalzone */
+static struct thermal_zone_device_ops exynos4_dev_ops = {
+ .bind = exynos4_bind,
+ .unbind = exynos4_unbind,
+ .get_temp = exynos4_get_temp,
+ .get_mode = exynos4_get_mode,
+ .set_mode = exynos4_set_mode,
+ .get_trip_type = exynos4_get_trip_type,
+ .get_trip_temp = exynos4_get_trip_temp,
+ .get_crit_temp = exynos4_get_crit_temp,
+};
+
+int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf)
+{
+ int ret, count, tab_size;
+ struct freq_pctg_table *tab_ptr;
+
+ if (!sensor_conf || !sensor_conf->read_temperature) {
+ pr_err("Temperature sensor not initialised\n");
+ return -EINVAL;
+ }
+
+ th_zone = kzalloc(sizeof(struct exynos4_thermal_zone), GFP_KERNEL);
+ if (!th_zone) {
+ ret = -ENOMEM;
+ goto err_unregister;
+ }
+
+ th_zone->sensor_conf = sensor_conf;
+
+ tab_ptr = (struct freq_pctg_table *)sensor_conf->cooling_data.freq_data;
+ tab_size = sensor_conf->cooling_data.freq_pctg_count;
+
+ /*Register the cpufreq cooling device*/
+ th_zone->cool_dev_size = 1;
+ count = 0;
+ th_zone->cool_dev[count] = cpufreq_cooling_register(
+ (struct freq_pctg_table *)&(tab_ptr[count]),
+ tab_size, cpumask_of(0));
+
+ if (IS_ERR(th_zone->cool_dev[count])) {
+ pr_err("Failed to register cpufreq cooling device\n");
+ ret = -EINVAL;
+ th_zone->cool_dev_size = 0;
+ goto err_unregister;
+ }
+
+ th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
+ 3, NULL, &exynos4_dev_ops, 0, 0, 0, 1000);
+ if (IS_ERR(th_zone->therm_dev)) {
+ pr_err("Failed to register thermal zone device\n");
+ ret = -EINVAL;
+ goto err_unregister;
+ }
+
+ th_zone->active_interval = 1;
+ th_zone->idle_interval = 10;
+
+ exynos4_set_mode(th_zone->therm_dev, THERMAL_DEVICE_DISABLED);
+
+ pr_info("Exynos: Kernel Thermal management registered\n");
+
+ return 0;
+
+err_unregister:
+ exynos4_unregister_thermal();
+ return ret;
+}
+EXPORT_SYMBOL(exynos4_register_thermal);
+
+void exynos4_unregister_thermal(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < th_zone->cool_dev_size; i++) {
+ if (th_zone && th_zone->cool_dev[i])
+ cpufreq_cooling_unregister(th_zone->cool_dev[i]);
+ }
+
+ if (th_zone && th_zone->therm_dev)
+ thermal_zone_device_unregister(th_zone->therm_dev);
+
+ kfree(th_zone);
+
+ pr_info("Exynos: Kernel Thermal management unregistered\n");
+}
+EXPORT_SYMBOL(exynos4_unregister_thermal);
diff --git a/include/linux/exynos_thermal.h b/include/linux/exynos_thermal.h
new file mode 100644
index 0000000..186e409
--- /dev/null
+++ b/include/linux/exynos_thermal.h
@@ -0,0 +1,72 @@
+/* linux/include/linux/exynos_thermal.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef THERMAL_INTERFACE_H
+#define THERMAL_INTERFACE_H
+/* CPU Zone information */
+
+#define SENSOR_NAME_LEN 16
+#define MAX_TRIP_COUNT 8
+
+#define PANIC_ZONE 4
+#define WARN_ZONE 3
+#define MONITOR_ZONE 2
+#define SAFE_ZONE 1
+#define NO_ACTION 0
+
+
+struct thermal_trip_point_conf {
+ int trip_val[MAX_TRIP_COUNT];
+ int trip_count;
+};
+
+struct thermal_cooling_conf {
+ struct freq_pctg_table freq_data[MAX_TRIP_COUNT];
+ int freq_pctg_count;
+};
+
+/**
+ * struct exynos4_tmu_platform_data
+ * @name: name of the temperature sensor
+ * @read_temperature: A function pointer to read temperature info
+ * @private_data: Temperature sensor private data
+ * @sensor_data: Sensor specific information like trigger temperature, level
+ */
+struct thermal_sensor_conf {
+ char name[SENSOR_NAME_LEN];
+ int (*read_temperature)(void *data);
+ struct thermal_trip_point_conf trip_data;
+ struct thermal_cooling_conf cooling_data;
+ void *private_data;
+};
+
+/**
+ * exynos4_register_thermal: Register to the exynos thermal interface.
+ * @sensor_conf: Structure containing temperature sensor information
+ *
+ * returns zero on success, else negative errno.
+ */
+int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf);
+
+/**
+ * exynos4_unregister_thermal: Un-register from the exynos thermal interface.
+ *
+ * return not applicable.
+ */
+void exynos4_unregister_thermal(void);
+
+/**
+ * exynos4_report_trigger: Report any trigger level crossed in the
+ * temperature sensor. This may be useful to take any cooling action.
+ *
+ * return not applicable.
+ */
+extern void exynos4_report_trigger(void);
+#endif
--
1.7.1
^ permalink raw reply related
* [PATCH 0/4] thermal: exynos: Add kernel thermal support for exynos platform
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
To: linux-pm, linux-samsung-soc
Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi
Changes since RFC:
*Moved the Temperature sensor driver from driver/hwmon/ to driver/mfd
as discussed with Guenter Roeck <guenter.roeck@ericsson.com> and
Donggeun Kim <dg77.kim@samsung.com> (https://lkml.org/lkml/2012/1/5/7)
*Some changes according to the changes in common cpu cooling APIs
All the patchset based on Kernel version 3.3-rc5 and uses the cpufreq
cooling registration APIs implemented in earlier patchset
https://lkml.org/lkml/2012/2/22/123
The code added in this patchset adds a thermal interface layer for samsung
exynos platforms. This layer is registered from the temperature sensor driver
and recieves/monitor the temperature from the sensor and informs the
generic thermal layer to take the necessary cooling action. Currently, this
layer can be used to create only one thermal zone and hence only one
temperature sensor can register. The future goal is to make this handle
multiple thermal zones.
Some modifications are done in the temperature sensor driver to export the
information needed for the thermal interface to register with the core linux
thermal framework and with the cpu frequency based cooling devices.
A simple data/control flow diagrams to illustrate this,
Core Linux thermal <-------> Exynos thermal <-------- Temperature Sensor
| |
\|/ |
Cpufreq cooling device <-----
Amit Daniel Kachhap (4):
thermal: exynos: Add thermal interface support for linux thermal
layer
hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
thermal: exynos4: Register the tmu sensor with the thermal interface
layer
ARM: exynos4: Add thermal sensor driver platform device support
Documentation/hwmon/exynos4_tmu | 81 -----
Documentation/mfd/exynos4_tmu | 52 +++
arch/arm/mach-exynos/Kconfig | 11 +
arch/arm/mach-exynos/Makefile | 1 +
arch/arm/mach-exynos/clock.c | 4 +
arch/arm/mach-exynos/dev-tmu.c | 64 ++++
arch/arm/mach-exynos/include/mach/irqs.h | 2 +
arch/arm/mach-exynos/include/mach/map.h | 1 +
arch/arm/mach-exynos/mach-origen.c | 1 +
arch/arm/plat-samsung/include/plat/devs.h | 1 +
drivers/hwmon/Kconfig | 10 -
drivers/hwmon/Makefile | 1 -
drivers/hwmon/exynos4_tmu.c | 514 -----------------------------
drivers/mfd/Kconfig | 10 +
drivers/mfd/Makefile | 1 +
drivers/mfd/exynos4_tmu.c | 443 +++++++++++++++++++++++++
drivers/thermal/Kconfig | 8 +
drivers/thermal/Makefile | 1 +
drivers/thermal/exynos_thermal.c | 272 +++++++++++++++
include/linux/exynos_thermal.h | 72 ++++
include/linux/platform_data/exynos4_tmu.h | 7 +
21 files changed, 951 insertions(+), 606 deletions(-)
delete mode 100644 Documentation/hwmon/exynos4_tmu
create mode 100644 Documentation/mfd/exynos4_tmu
create mode 100644 arch/arm/mach-exynos/dev-tmu.c
delete mode 100644 drivers/hwmon/exynos4_tmu.c
create mode 100644 drivers/mfd/exynos4_tmu.c
create mode 100644 drivers/thermal/exynos_thermal.c
create mode 100644 include/linux/exynos_thermal.h
^ permalink raw reply
* Re: [PATCH 04/50] ACPICA: Fix to allow region arguments to reference other scopes
From: Jonathan Nieder @ 2012-03-01 15:27 UTC (permalink / raw)
To: Len Brown
Cc: linux-acpi, linux-pm, linux-kernel, Jim Green, Lin Ming,
Bob Moore, Len Brown, Arne Nordmark
In-Reply-To: <8931d9ea78848b073bf299594f148b83abde4a5e.1326802229.git.len.brown@intel.com>
Hi,
Len Brown wrote[1]:
> From: Lin Ming <ming.m.lin@intel.com>
[...]
> http://www.acpica.org/bugzilla/show_bug.cgi?id=937
> http://marc.info/?l=linux-acpi&m=131636632718222&w=2
>
> ACPI Error: [RAMB] Namespace lookup failure, AE_NOT_FOUND (20110112/psargs-359)
> ACPI Exception: AE_NOT_FOUND, Could not execute arguments for [RAMW] (Region) (20110112/nsinit-349)
>
> Scope (_SB)
> {
> Name (RAMB, 0xDF5A1018)
> OperationRegion (\RAMW, SystemMemory, RAMB, 0x00010000)
> }
>
> For above ASL code, we need to save scope node(\_SB) to lookup
> the argument node(\_SB.RAMB).
>
> Reported-by: Jim Green <student.northwestern@gmail.com>
> Signed-off-by: Lin Ming <ming.m.lin@intel.com>
> Signed-off-by: Bob Moore <robert.moore@intel.com>
> Signed-off-by: Len Brown <len.brown@intel.com>
Arne Nordmark reports[2] that this patch, when applied to 3.2.5, fixes
a resume failure. Thanks!
Would it be safe and useful to backport this fix to stable/longterm
kernels? If so, which ones?
Jonathan
[1] commit 8931d9ea7884
[2] http://bugs.debian.org/661581
^ permalink raw reply
* Re: [linux-pm] [PATCH 0/2] RFC: CPU frequency max as PM QoS param
From: Antti P Miettinen @ 2012-02-28 9:37 UTC (permalink / raw)
To: markgross
Cc: Kevin Hilman, len.brown@intel.com, cpufreq@vger.kernel.org,
linux-pm@lists.linux-foundation.org, j-pihet@ti.com, davej, pavel,
rjw
In-Reply-To: <20120228005630.GA15348@envy17>
Adding people that were part of the thread in the beginning..
mark gross <markgross@thegnar.org> writes:
> On Mon, Feb 27, 2012 at 04:04:00PM +0100, Antti Miettinen wrote:
>> To the lists too..
>>
>> On 02/27/2012 04:49 PM, Kevin Hilman wrote:
>> > mark gross <markgross@thegnar.org> writes:
>> >
>> > >> Current QoS settings could be thought of as performance constraints
>> > >> too. It's just that they determine minimum performance. Adding
>> > >> constraints for maxium performance is not a big stretch in my mind.
>> > >
>> > > Its not a big stretch to me either. I just think its a bit of a hack
>> > > and there is a bigger more interesting issue getting overlooked.
>> > >
>> > > Lastly why not simply make cpufreq thermal aware and talk directly to
>> > > it if you even need too?
>> >
>> > In fact, making a thermal framework "cooling device" that talks directly
>> > to CPUfreq is already what's being done by the Linaro PMWG folks.
>> >
>> > The problem is that CPUfreq only controls the CPU frequency.
>> >
>> > There are other devices that could be scaled back to reduce heat as well
>> > (DSP, and especially GPU), so having a more generic per-device
>> > constraint interface that can cap the frequency for *any* scalable
>> > device is a better framework IMO.
>> >
>> > It just so happens that pm_qos is already a good per-device constraint
>> > framework and can easily modified to cap performance as well as request
>> > a minimum performance.
>> >
>> > Kevin
>
> ok I'll stop trying to block it.
>
> I want to re-do the whole works anyway. If this helps in the mean time
> then go for it.
Great :-)
So what do other people think? Could we merge global CPU frequency
constraints for now?
I agree that more work is needed for e.g. per CPU constraints, user
space interface and more complete thermal management. Actually for
future I think the constraints could also become more general than just
min/max "reduction operators". For e.g. core online status you might
want union/intersection of bitmaps. Also, the more complete thermal
management is related to load management in general (power budgeting for
other reasons than just thermal).
--Antti
^ permalink raw reply
* Re: [linux-pm] [PATCH 0/2] RFC: CPU frequency max as PM QoS param
From: mark gross @ 2012-02-28 0:56 UTC (permalink / raw)
To: Antti Miettinen
Cc: Kevin Hilman, len.brown@intel.com, markgross@thegnar.org,
cpufreq@vger.kernel.org, linux-pm@lists.linux-foundation.org,
j-pihet@ti.com
In-Reply-To: <4F4B9B60.6040501@nvidia.com>
On Mon, Feb 27, 2012 at 04:04:00PM +0100, Antti Miettinen wrote:
> To the lists too..
>
> On 02/27/2012 04:49 PM, Kevin Hilman wrote:
> > mark gross <markgross@thegnar.org> writes:
> >
> > >> Current QoS settings could be thought of as performance constraints
> > >> too. It's just that they determine minimum performance. Adding
> > >> constraints for maxium performance is not a big stretch in my mind.
> > >
> > > Its not a big stretch to me either. I just think its a bit of a hack
> > > and there is a bigger more interesting issue getting overlooked.
> > >
> > > Lastly why not simply make cpufreq thermal aware and talk directly to
> > > it if you even need too?
> >
> > In fact, making a thermal framework "cooling device" that talks directly
> > to CPUfreq is already what's being done by the Linaro PMWG folks.
> >
> > The problem is that CPUfreq only controls the CPU frequency.
> >
> > There are other devices that could be scaled back to reduce heat as well
> > (DSP, and especially GPU), so having a more generic per-device
> > constraint interface that can cap the frequency for *any* scalable
> > device is a better framework IMO.
> >
> > It just so happens that pm_qos is already a good per-device constraint
> > framework and can easily modified to cap performance as well as request
> > a minimum performance.
> >
> > Kevin
ok I'll stop trying to block it.
I want to re-do the whole works anyway. If this helps in the mean time
then go for it.
--mark
^ permalink raw reply
* Re: [PATCH 0/2] RFC: CPU frequency max as PM QoS param
From: Antti Miettinen @ 2012-02-27 15:04 UTC (permalink / raw)
To: Kevin Hilman
Cc: len.brown@intel.com, markgross@thegnar.org,
cpufreq@vger.kernel.org, linux-pm@lists.linux-foundation.org,
j-pihet@ti.com
In-Reply-To: <877gz8wcud.fsf@ti.com>
To the lists too..
On 02/27/2012 04:49 PM, Kevin Hilman wrote:
> mark gross <markgross@thegnar.org> writes:
>
> >> Current QoS settings could be thought of as performance constraints
> >> too. It's just that they determine minimum performance. Adding
> >> constraints for maxium performance is not a big stretch in my mind.
> >
> > Its not a big stretch to me either. I just think its a bit of a hack
> > and there is a bigger more interesting issue getting overlooked.
> >
> > Lastly why not simply make cpufreq thermal aware and talk directly to
> > it if you even need too?
>
> In fact, making a thermal framework "cooling device" that talks directly
> to CPUfreq is already what's being done by the Linaro PMWG folks.
>
> The problem is that CPUfreq only controls the CPU frequency.
>
> There are other devices that could be scaled back to reduce heat as well
> (DSP, and especially GPU), so having a more generic per-device
> constraint interface that can cap the frequency for *any* scalable
> device is a better framework IMO.
>
> It just so happens that pm_qos is already a good per-device constraint
> framework and can easily modified to cap performance as well as request
> a minimum performance.
>
> Kevin
>
^ permalink raw reply
* Re: [linux-pm] [PATCH 0/2] RFC: CPU frequency max as PM QoS param
From: Antti P Miettinen @ 2012-02-27 11:00 UTC (permalink / raw)
To: Pihet-XID, Jean
Cc: markgross, Kevin Hilman, Valentin, Eduardo, len.brown, cpufreq,
linux-pm
In-Reply-To: <CAN5iSWq8M4OnMMDYKVZYY=M25jw6ru6ODUNmi3wLmnZ5Vxpdrw@mail.gmail.com>
"Pihet-XID, Jean" <j-pihet@ti.com> writes:
> Re-adding cpufreq and linux-pm MLs in Cc.
>
> Regards,
> Jean
Thanks Jean. And sorry for causing the lists to get dropped.
Does anyone have comments about the user space interface for per device
constraints:
>>>>>> Another complication for the per device constraints is the user space
>>>>>> interface. Dynamic minors run out pretty fast if we have per CPU
>>>>>> parameters and the system has huge number of CPUs. Does anyone have any
>>>>>> opinions about the user space interface for device PM QoS?
My current view is that CPU frequency constraints could be global just
like the CPU latency constraints are.
--Antti
^ permalink raw reply
* Re: [linux-pm] [PATCH 0/2] RFC: CPU frequency max as PM QoS param
From: Pihet-XID, Jean @ 2012-02-27 10:17 UTC (permalink / raw)
To: markgross
Cc: Kevin Hilman, Valentin, Eduardo, Antti P Miettinen, len.brown,
cpufreq, linux-pm
In-Reply-To: <20120225174449.GA17141@envy17>
Re-adding cpufreq and linux-pm MLs in Cc.
Regards,
Jean
On Sat, Feb 25, 2012 at 6:44 PM, mark gross <markgross@thegnar.org> wrote:
> On Tue, Feb 21, 2012 at 10:00:18AM -0800, Kevin Hilman wrote:
>> mark gross <markgross@thegnar.org> writes:
>>
>> > On Mon, Feb 20, 2012 at 05:44:23PM +0200, Valentin, Eduardo wrote:
>> >> Hello Antti,
>> >>
>> >> On Mon, Feb 20, 2012 at 12:00 PM, Antti P Miettinen
>> >> <amiettinen@nvidia.com> wrote:
>> >> > The following message is a courtesy copy of an article
>> >> > that has been posted to gmane.linux.kernel.cpufreq,gmane.linux.power-management.general as well.
>> >> >
>> >> > Thanks for the comments. I'd like to comment on maximum CPU frequency,
>> >> > sysfs files and per device contraints..
>> >> >
>> >> > Maximum CPU frequency could be useful for thermal. However, it is not a
>> >> > complete solution for thermal. Just like minimum CPU frequency is not a
>> >> > complete solution for computing throughput (e.g. memory and accelerator
>> >> > control are not directly addressed by a CPU frequency
>> >> > constraint). Maximum CPU frequency can be also useful for energy
>> >> > efficiency even though the constraint is not a complete solution here
>> >> > either. I guess latency constraints do not completely solve end-to-end
>> >> > latency requirements but the mechanism is useful so it is good to have
>> >> > it. I'd argue minimum and maximum frequency are simular in this respect.
>> >> >
>> >>
>> >> I believe we are aligned that this is not complete solution, but the
>> >> constraints does help on both cases.
>> >>
>> >> > There are sysfs files for constraining CPU frequency. However, there is
>> >> > no arbitration for several applications trying to place constraints. PM
>> >> > QoS provides a way to consolidate requests from several applications and
>> >> > cleanup upon application crash. I think the existing sysfs files are not
>> >> > an appropriate inferface for user space applications.
>> >>
>> >> Agreed, the current CPU frequency interfaces were not design to
>> >> achieve lists of constraints at all, but they had very simplistic
>> >> design which you would consider the last constraint applied.
>> >>
>> >> But the point Mark was trying to bring was that PM QoS was not really
>> >> meant for max- like constraints. As he said, that is not for QoS but
>> >> for constraints. We may be abusing a bit the FW by addressing the
>> >> problem with it. That would look like more as a pm-constraint FW.
>> >>
>> >> It just happens to be so that the PM QoS has already the needed infrastructure.
>> >>
>> >
>> > Aside from the naming and design intents of pm_qos (qos vrs perforce
>> > constraints) which I acknowledge is arguably not a good reason on its
>> > own to block this, the other issue I have is that thermal constraints
>> > are much more dynamic than pm-qos constraints and I don't think the
>> > existing framework is up to it.
>> >
>> > almost all applications of pm-qos are static.
>>
>> Not true.
>>
>> With the new per-device PM QoS, we are adding/removing constraints
>> dynamically depending on usage. Drivers/subsystems add/remove
>> constraints depending on what is happening and their needs. This can
>> happen dynamically and very often.
>
> I have to admit I no experience and limited insight into the per-device
> pm-qos or how its getting used. However; I think there is a difference
> between dynamic use cases and ambient environmental effects on the
> system.
>
>> Also, as you know based on changes submitted to PM QoS infrastructure
>> after it's initial addition, lots of work was done so that it can be
>> used in the fast path, so many people care about the ability to change
>> constraints dynamically.
>
> true.
>
>> > Some device has a lower bound of performance needed by some other
>> > platform component and uses pm-qos to request tat the lower bound not
>> > be violated.
>>
>> And with per-device constraints (I prefer the term constraints too),
>> this can be very dynamic, and very targetted. e.g. the constraint might
>> apply to a single power domain/island and can can be added/removed
>> dynamically and often.
>>
>> > Performance constraints depend on dynamic values (temperature, peak
>> > current, perhaps someday even noise) that are effected by ambient
>> > conditions.
>>
>> And current QoS settings also depend on dynamic values: instantaneous
>> throughput, frequency dependencies between IP blocks, etc. etc.
>> Depending on which devices/subsystems are in use by a given usecase,
>> these are all dynamic conditions.
>
> They are static per use case. Just because the use case can change
> dynamically does not make it dynamic in the same sense as thermal or
> battery power envelopes.
>
> Further the effects of truly dynamic conditions is significant to the
> user.
>
>> > I think adding perf-constraints to the pm-qos without taking the time to
>> > think through these things to be a quick and dirty hack.
>>
>> I don't see it that way, and to me it's a very logical extention.
>>
>
> I see it as a convenient extension that is very close to being a quick
> hack. That only solves a small part of a more general problem.
>
>> Current QoS settings could be thought of as performance constraints
>> too. It's just that they determine minimum performance. Adding
>> constraints for maxium performance is not a big stretch in my mind.
>
> Its not a big stretch to me either. I just think its a bit of a hack
> and there is a bigger more interesting issue getting overlooked.
>
> Lastly why not simply make cpufreq thermal aware and talk directly to
> it if you even need too?
>
> --mark
>
>> Kevin
>>
>> > I don't know
>> > if we should be quick and dirty with main line changes like this. maybe
>> > pm-qos only needs some dynamic constrain aggregation (for ambiant
>> > conditions) and a few new constraint classes. Maybe there needs to be
>> > more done.
>> >
>> > maybe I'm over thinking it and we should simply add the perf-constraint
>> > class.
>>
>> >>
>> >>
>> >> >
>> >> > Currently CPU sleep states are blocked globally for latency
>> >> > contraints. Finer granularity control would be possible with per CPU
>> >> > contraints. However - are there clients that know or want to contrain a
>> >> > specific CPU? Same question is applicable also to CPU frequency. Even
>> >> > though per CPU control is more flexible, what are the clients that want
>> >> > to constrain a specific CPU?
>> >>
>> >> If I got your point right, I agree with you, we definitely need a way
>> >> to expose who is setting the constraint.
>> >>
>> >> >
>> >> > Another complication for the per device constraints is the user space
>> >> > interface. Dynamic minors run out pretty fast if we have per CPU
>> >> > parameters and the system has huge number of CPUs. Does anyone have any
>> >> > opinions about the user space interface for device PM QoS?
>> >> >
>> >> > --Antti
>> >>
>> >>
>> >> All best,
>> >>
>> >> --
>> >>
>> >> Eduardo Valentin
^ permalink raw reply
* Re: [PATCH 2/4] thermal: Add generic cpufreq cooling implementation
From: Amit Kachhap @ 2012-02-27 4:32 UTC (permalink / raw)
To: R, Durgadoss
Cc: linux-pm@lists.linux-foundation.org, linux-kernel@vger.kernel.org,
mjg59@srcf.ucam.org, linux-acpi@vger.kernel.org, lenb@kernel.org,
linaro-dev@lists.linaro.org, rob.lee@linaro.org,
patches@linaro.org
In-Reply-To: <4D68720C2E767A4AA6A8796D42C8EB590442F2@BGSMSX101.gar.corp.intel.com>
Hi Durgadoss,
Thanks for the detailed review comments.
On 24 February 2012 16:34, R, Durgadoss <durgadoss.r@intel.com> wrote:
> Hi Amit,
>
>> -----Original Message-----
>> From: amit kachhap [mailto:amitdanielk@gmail.com] On Behalf Of Amit Daniel
>> Kachhap
>> Sent: Wednesday, February 22, 2012 3:44 PM
>> To: linux-pm@lists.linux-foundation.org
>> Cc: linux-kernel@vger.kernel.org; mjg59@srcf.ucam.org; linux-
>> acpi@vger.kernel.org; lenb@kernel.org; linaro-dev@lists.linaro.org;
>> amit.kachhap@linaro.org; R, Durgadoss; rob.lee@linaro.org; patches@linaro.org
>> Subject: [PATCH 2/4] thermal: Add generic cpufreq cooling implementation
>>
>> This patch adds support for generic cpu thermal cooling low level
>> implementations using frequency scaling up/down based on the request
>> from user. Different cpu related cooling devices can be registered by the
>
> I believe what you mean by 'user' is another Driver using this code.. right ??
Yes.
>
>> user and the binding of these cooling devices to the corresponding
>> trip points can be easily done as the registration API's return the
>> cooling device pointer. The user of these api's are responsible for
>> passing clipping frequency in percentages.
>>
>> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
>> ---
>> Documentation/thermal/cpu-cooling-api.txt | 40 ++++
>> drivers/thermal/Kconfig | 11 +
>> drivers/thermal/Makefile | 1 +
>> drivers/thermal/cpu_cooling.c | 310 +++++++++++++++++++++++++++++
>> include/linux/cpu_cooling.h | 54 +++++
>> 5 files changed, 416 insertions(+), 0 deletions(-)
>> create mode 100644 Documentation/thermal/cpu-cooling-api.txt
>> create mode 100644 drivers/thermal/cpu_cooling.c
>> create mode 100644 include/linux/cpu_cooling.h
>>
>> diff --git a/Documentation/thermal/cpu-cooling-api.txt
>> b/Documentation/thermal/cpu-cooling-api.txt
>> new file mode 100644
>> index 0000000..04de67c
>> --- /dev/null
>> +++ b/Documentation/thermal/cpu-cooling-api.txt
>> @@ -0,0 +1,40 @@
>> +CPU cooling api's How To
>> +===================================
>> +
>> +Written by Amit Daniel Kachhap <amit.kachhap@linaro.org>
>> +
>> +Updated: 13 Dec 2011
>> +
>> +Copyright (c) 2011 Samsung Electronics Co., Ltd(http://www.samsung.com)
>> +
>> +0. Introduction
>> +
>> +The generic cpu cooling(freq clipping, cpuhotplug) provides
>> +registration/unregistration api's to the user. The binding of the cooling
>> +devices to the trip point is left for the user. The registration api's returns
>> +the cooling device pointer.
>> +
>> +1. cpufreq cooling api's
>> +
>> +1.1 cpufreq registration api's
>> +1.1.1 struct thermal_cooling_device *cpufreq_cooling_register(
>> + struct freq_pctg_table *tab_ptr, unsigned int tab_size,
>> + const struct cpumask *mask_val)
>> +
>> + This interface function registers the cpufreq cooling device with the name
>> + "thermal-cpufreq-%x". This api can support multiple instance of cpufreq
>> cooling
>> + devices.
>> +
>> + tab_ptr: The table containing the percentage of frequency to be clipped
>> for
>> + each cooling state.
>> + .freq_clip_pctg: Percentage of frequency to be clipped for each allowed
>> + cpus.
>> + .polling_interval: polling interval for this cooling state.
>> + tab_size: the total number of cooling state.
>
> Although I can understand that the table size is equal to
> the total number of cooling states, it is better to name it 'num_cooling_states'
> (or something) that means what it is..
Yes your idea makes more sense. Will accept it in the next version.
>
>> + mask_val: all the allowed cpu's where frequency clipping can happen.
>> +
>> +1.1.2 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
>> +
>> + This interface function unregisters the "thermal-cpufreq-%x" cooling
>> device.
>> +
>> + cdev: Cooling device pointer which has to be unregistered.
>> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
>> index f7f71b2..298c1cd 100644
>> --- a/drivers/thermal/Kconfig
>> +++ b/drivers/thermal/Kconfig
>> @@ -18,3 +18,14 @@ config THERMAL_HWMON
>> depends on THERMAL
>> depends on HWMON=y || HWMON=THERMAL
>> default y
>> +
>> +config CPU_THERMAL
>> + bool "generic cpu cooling support"
>> + depends on THERMAL
>> + help
>> + This implements the generic cpu cooling mechanism through frequency
>> + reduction, cpu hotplug and any other ways of reducing temperature. An
>> + ACPI version of this already exists(drivers/acpi/processor_thermal.c).
>> + This will be useful for platforms using the generic thermal interface
>> + and not the ACPI interface.
>> + If you want this support, you should say Y or M here.
>> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
>> index 31108a0..655cbc4 100644
>> --- a/drivers/thermal/Makefile
>> +++ b/drivers/thermal/Makefile
>> @@ -3,3 +3,4 @@
>> #
>>
>> obj-$(CONFIG_THERMAL) += thermal_sys.o
>> +obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
>> diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
>> new file mode 100644
>> index 0000000..298f550
>> --- /dev/null
>> +++ b/drivers/thermal/cpu_cooling.c
>> @@ -0,0 +1,310 @@
>> +/*
>> + * linux/drivers/thermal/cpu_cooling.c
>> + *
>> + * Copyright (C) 2011 Samsung Electronics Co., Ltd(http://www.samsung.com)
>> + * Copyright (C) 2011 Amit Daniel <amit.kachhap@linaro.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; version 2 of the License.
>> + *
>> + * 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.
>> + *
>> + * You should have received a copy of the GNU General Public License along
>> + * with this program; if not, write to the Free Software Foundation, Inc.,
>> + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
>> + *
>> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> + */
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/thermal.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/cpufreq.h>
>> +#include <linux/err.h>
>> +#include <linux/slab.h>
>> +#include <linux/cpu.h>
>> +#include <linux/cpu_cooling.h>
>> +
>> +#ifdef CONFIG_CPU_FREQ
>
> Except the _idr methods, all the code is inside this #ifdef.
> So, I think it is better to add this dependency in Kconfig,
> and leave this code clean w/o many #ifdef's.
ok
>
>> +struct cpufreq_cooling_device {
>> + int id;
>> + struct thermal_cooling_device *cool_dev;
>> + struct freq_pctg_table *tab_ptr;
>> + unsigned int tab_size;
>> + unsigned int cpufreq_state;
>> + const struct cpumask *allowed_cpus;
>> + struct list_head node;
>> +};
>> +
>> +static LIST_HEAD(cooling_cpufreq_list);
>> +static DEFINE_MUTEX(cooling_cpufreq_lock);
>> +static DEFINE_IDR(cpufreq_idr);
>> +static struct cpufreq_cooling_device *notify_cpufreq;
>
> Please move this after the DEFINE_PER_CPU.
> Hard to notice here..
ok
>
>> +static DEFINE_PER_CPU(unsigned int, max_policy_freq);
>> +#endif /*CONFIG_CPU_FREQ*/
>> +
>> +static int get_idr(struct idr *idr, struct mutex *lock, int *id)
>> +{
>> + int err;
>> +again:
>> + if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
>> + return -ENOMEM;
>> +
>> + if (lock)
>> + mutex_lock(lock);
>> + err = idr_get_new(idr, NULL, id);
>> + if (lock)
>> + mutex_unlock(lock);
>> + if (unlikely(err == -EAGAIN))
>> + goto again;
>> + else if (unlikely(err))
>> + return err;
>> +
>> + *id = *id & MAX_ID_MASK;
>> + return 0;
>> +}
>> +
>> +static void release_idr(struct idr *idr, struct mutex *lock, int id)
>> +{
>> + if (lock)
>> + mutex_lock(lock);
>> + idr_remove(idr, id);
>> + if (lock)
>> + mutex_unlock(lock);
>> +}
>> +
>> +#ifdef CONFIG_CPU_FREQ
>> +/*Below codes defines functions to be used for cpufreq as cooling device*/
>> +static bool is_cpufreq_valid(int cpu)
>> +{
>> + struct cpufreq_policy policy;
>> + if (!cpufreq_get_policy(&policy, cpu))
>> + return true;
>> + return false;
>
> Why not just do a return !cpufreq_get_policy(&policy, cpu);
ok
>
>> +}
>> +
>> +static int cpufreq_apply_cooling(struct cpufreq_cooling_device
>> *cpufreq_device,
>> + unsigned long cooling_state)
>> +{
>> + int cpuid, this_cpu = smp_processor_id();
>> +
>> + if (!is_cpufreq_valid(this_cpu))
>
> You are not using this_cpu anywhere else..so, directly use
> Smp_processor_id() here..
>
Ok agreed.
>> + return 0;
>> +
>> + if (cooling_state > cpufreq_device->tab_size)
>> + return -EINVAL;
>> +
>> + /*Check if last cooling level is same as current cooling level*/
>
> Use either 'state' or 'level' in comments as well as the variable name
> Makes it easy to read..
Ok, will use state.
>
>> + if (cpufreq_device->cpufreq_state == cooling_state)
>> + return 0;
>> +
>> + cpufreq_device->cpufreq_state = cooling_state;
>> +
>> + /*cpufreq thermal notifier uses this cpufreq device pointer*/
>> + notify_cpufreq = cpufreq_device;
>> +
>> + for_each_cpu(cpuid, cpufreq_device->allowed_cpus) {
>> + if (is_cpufreq_valid(cpuid))
>> + cpufreq_update_policy(cpuid);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int thermal_cpufreq_notifier(struct notifier_block *nb,
>> + unsigned long event, void *data)
>> +{
>> + struct cpufreq_policy *policy = data;
>> + struct freq_pctg_table *th_table;
>> + unsigned long max_freq = 0;
>> + unsigned int th_pctg = 0, level;
>> +
>> + if (event != CPUFREQ_ADJUST)
>> + return 0;
>> +
>> + if (!notify_cpufreq)
>> + return 0;
>
> Why not combine both if's with an || ?
Ok
>
>> +
>> + level = notify_cpufreq->cpufreq_state;
>
> Yes..here it is..please use level/state..
Ok, will use state
>
>> +
>> + if (level > 0) {
>> + th_table =
>> + &(notify_cpufreq->tab_ptr[level - 1]);
>> + th_pctg = th_table->freq_clip_pctg;
>> + max_freq =
>> + (policy->cpuinfo.max_freq * (100 - th_pctg)) / 100;
>> +
>> + if (per_cpu(max_policy_freq, policy->cpu) == 0)
>> + per_cpu(max_policy_freq, policy->cpu) = policy->max;
>> + } else {
>> + if (per_cpu(max_policy_freq, policy->cpu) != 0) {
>> + max_freq = per_cpu(max_policy_freq, policy->cpu);
>> + per_cpu(max_policy_freq, policy->cpu) = 0;
>> + } else {
>> + max_freq = policy->max;
>> + }
>> + }
>> +
>> + cpufreq_verify_within_limits(policy, 0, max_freq);
>> +
>> + return 0;
>> +}
>> +
>> +/*
>> + * cpufreq cooling device callback functions
>> + */
>> +static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
>> + unsigned long *state)
>> +{
>> + struct cpufreq_cooling_device *cpufreq_device = NULL;
>
> Why assigning NULL ?
yes it is not needed.
>
>> +
>> + mutex_lock(&cooling_cpufreq_lock);
>> + list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node)
>> + if (cpufreq_device && cpufreq_device->cool_dev == cdev)
>> + break;
>> +
>> + mutex_unlock(&cooling_cpufreq_lock);
>> + if (!cpufreq_device || cpufreq_device->cool_dev != cdev)
>> + return -EINVAL;
>> +
>> + *state = cpufreq_device->tab_size;
>> + return 0;
>> +}
>
> The above can be simplified this way:
> int ret = -EINVAL;
> mutex_lock(&cooling_cpufreq_lock);
> list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node)
> if (cpufreq_device->cool_dev == cdev) {
> *state = cpufreq_device->tab_size;
> ret = 0;
> break;
> }
>
> mutex_unlock(&cooling_cpufreq_lock);
> return ret;
>
> I think the same can be done for the get_ function below..and similar ones
> in the patch 3/4.
Ok accepted.
>
>> +
>> +static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
>> + unsigned long *state)
>> +{
>> + struct cpufreq_cooling_device *cpufreq_device = NULL;
>> +
>> + mutex_lock(&cooling_cpufreq_lock);
>> + list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node)
>> + if (cpufreq_device && cpufreq_device->cool_dev == cdev)
>> + break;
>> +
>> + mutex_unlock(&cooling_cpufreq_lock);
>> + if (!cpufreq_device || cpufreq_device->cool_dev != cdev)
>> + return -EINVAL;
>> + *state = cpufreq_device->cpufreq_state;
>> + return 0;
>> +}
>> +
>> +/*This cooling may be as PASSIVE/ACTIVE type*/
>> +static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
>> + unsigned long state)
>> +{
>> + struct cpufreq_cooling_device *cpufreq_device = NULL;
>> +
>> + mutex_lock(&cooling_cpufreq_lock);
>> + list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node)
>> + if (cpufreq_device && cpufreq_device->cool_dev == cdev)
>> + break;
>> +
>> + mutex_unlock(&cooling_cpufreq_lock);
>> + if (!cpufreq_device || cpufreq_device->cool_dev != cdev)
>> + return -EINVAL;
>> +
>> + cpufreq_apply_cooling(cpufreq_device, state);
>> + return 0;
>> +}
>> +
>> +/* bind cpufreq callbacks to cpufreq cooling device */
>> +static struct thermal_cooling_device_ops cpufreq_cooling_ops = {
>> + .get_max_state = cpufreq_get_max_state,
>> + .get_cur_state = cpufreq_get_cur_state,
>> + .set_cur_state = cpufreq_set_cur_state,
>> +};
>> +
>> +static struct notifier_block thermal_cpufreq_notifier_block = {
>> + .notifier_call = thermal_cpufreq_notifier,
>> +};
>> +
>> +struct thermal_cooling_device *cpufreq_cooling_register(
>> + struct freq_pctg_table *tab_ptr, unsigned int tab_size,
>> + const struct cpumask *mask_val)
>> +{
>> + struct thermal_cooling_device *cool_dev;
>> + struct cpufreq_cooling_device *cpufreq_dev = NULL;
>> + unsigned int count = 0;
>> + char dev_name[THERMAL_NAME_LENGTH];
>> + int ret = 0, id = 0;
>> +
>> + if (tab_ptr == NULL || tab_size == 0)
>> + return ERR_PTR(-EINVAL);
>> +
>> + list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node)
>> + count++;
>> +
>> + cpufreq_dev =
>> + kzalloc(sizeof(struct cpufreq_cooling_device), GFP_KERNEL);
>> +
>> + if (!cpufreq_dev)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + cpufreq_dev->tab_ptr = tab_ptr;
>> + cpufreq_dev->tab_size = tab_size;
>> + cpufreq_dev->allowed_cpus = mask_val;
>> +
>> + ret = get_idr(&cpufreq_idr, &cooling_cpufreq_lock, &cpufreq_dev->id);
>> + if (ret) {
>> + kfree(cpufreq_dev);
>> + return ERR_PTR(-EINVAL);
>> + }
>> +
>> + sprintf(dev_name, "thermal-cpufreq-%d", cpufreq_dev->id);
>> +
>> + cool_dev = thermal_cooling_device_register(dev_name, cpufreq_dev,
>> + &cpufreq_cooling_ops);
>> + if (!cool_dev) {
>> + release_idr(&cpufreq_idr, &cooling_cpufreq_lock,
>> + cpufreq_dev->id);
>> + kfree(cpufreq_dev);
>> + return ERR_PTR(-EINVAL);
>> + }
>> + cpufreq_dev->id = id;
>> + cpufreq_dev->cool_dev = cool_dev;
>> + mutex_lock(&cooling_cpufreq_lock);
>> + list_add_tail(&cpufreq_dev->node, &cooling_cpufreq_list);
>> + mutex_unlock(&cooling_cpufreq_lock);
>> +
>> + if (count == 0)
>> + cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
>> + CPUFREQ_POLICY_NOTIFIER);
>
> Why do we register only when count is 0 ?
> Or should this be 'if (count > 0)' ?
Count represents the number of cpufreq type cooling devices. So for
the first call of this API , cpufreq_register_notifier is called.
Other call to cpufreq_cooling_register will use the same notifier. May
be using bool flag instead of count variable is better.
>
>> + return cool_dev;
>> +}
>> +EXPORT_SYMBOL(cpufreq_cooling_register);
>> +
>> +void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
>> +{
>> + struct cpufreq_cooling_device *cpufreq_dev = NULL;
>> + unsigned int count = 0;
>> +
>> + mutex_lock(&cooling_cpufreq_lock);
>> + list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node) {
>> + if (cpufreq_dev && cpufreq_dev->cool_dev == cdev)
>> + break;
>> + count++;
>> + }
>> +
>> + if (!cpufreq_dev || cpufreq_dev->cool_dev != cdev) {
>> + mutex_unlock(&cooling_cpufreq_lock);
>> + return;
>> + }
>> +
>> + list_del(&cpufreq_dev->node);
>> + mutex_unlock(&cooling_cpufreq_lock);
>> +
>> + if (count == 1)
>
> Same here..I do not get the idea behind this..
> Shouldn't this be 'if (count > 0)' ?
Same as above.
>
> In general,
> I would like to see a real driver using these API's. This will help
> everybody to understand the working of these API's much better.
Actually RFC version of the driver is already posted which uses these
api's. (https://lkml.org/lkml/2011/12/21/169). I forgot to add this
link in this patchset.
I will post the new version of the driver shortly.
>
> Thanks,
> Durga
>
>> + cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
>> + CPUFREQ_POLICY_NOTIFIER);
>> +
>> + thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
>> + release_idr(&cpufreq_idr, &cooling_cpufreq_lock, cpufreq_dev->id);
>> + kfree(cpufreq_dev);
>> +}
>> +EXPORT_SYMBOL(cpufreq_cooling_unregister);
>> +#endif /*CONFIG_CPU_FREQ*/
>> diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h
>> new file mode 100644
>> index 0000000..5dc5632
>> --- /dev/null
>> +++ b/include/linux/cpu_cooling.h
>> @@ -0,0 +1,54 @@
>> +/*
>> + * linux/include/linux/cpu_cooling.h
>> + *
>> + * Copyright (C) 2011 Samsung Electronics Co., Ltd(http://www.samsung.com)
>> + * Copyright (C) 2011 Amit Daniel <amit.kachhap@linaro.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; version 2 of the License.
>> + *
>> + * 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.
>> + *
>> + * You should have received a copy of the GNU General Public License along
>> + * with this program; if not, write to the Free Software Foundation, Inc.,
>> + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
>> + *
>> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> + */
>> +
>> +#ifndef __CPU_COOLING_H__
>> +#define __CPU_COOLING_H__
>> +
>> +#include <linux/thermal.h>
>> +
>> +struct freq_pctg_table {
>> + unsigned int freq_clip_pctg;
>> + unsigned int polling_interval;
>> +};
>> +
>> +#ifdef CONFIG_CPU_FREQ
>> +struct thermal_cooling_device *cpufreq_cooling_register(
>> + struct freq_pctg_table *tab_ptr, unsigned int tab_size,
>> + const struct cpumask *mask_val);
>> +
>> +void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev);
>> +#else /*!CONFIG_CPU_FREQ*/
>> +static inline struct thermal_cooling_device *cpufreq_cooling_register(
>> + struct freq_pctg_table *tab_ptr, unsigned int tab_size,
>> + const struct cpumask *mask_val)
>> +{
>> + return NULL;
>> +}
>> +static inline void cpufreq_cooling_unregister(
>> + struct thermal_cooling_device *cdev)
>> +{
>> + return;
>> +}
>> +#endif /*CONFIG_CPU_FREQ*/
>> +
>> +#endif /* __CPU_COOLING_H__ */
>> --
>> 1.7.1
>
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 2/4] thermal: Add generic cpufreq cooling implementation
From: R, Durgadoss @ 2012-02-24 11:04 UTC (permalink / raw)
To: Amit Daniel Kachhap, linux-pm@lists.linux-foundation.org
Cc: linaro-dev@lists.linaro.org, patches@linaro.org,
linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org,
rob.lee@linaro.org
In-Reply-To: <1329905650-30161-3-git-send-email-amit.kachhap@linaro.org>
Hi Amit,
> -----Original Message-----
> From: amit kachhap [mailto:amitdanielk@gmail.com] On Behalf Of Amit Daniel
> Kachhap
> Sent: Wednesday, February 22, 2012 3:44 PM
> To: linux-pm@lists.linux-foundation.org
> Cc: linux-kernel@vger.kernel.org; mjg59@srcf.ucam.org; linux-
> acpi@vger.kernel.org; lenb@kernel.org; linaro-dev@lists.linaro.org;
> amit.kachhap@linaro.org; R, Durgadoss; rob.lee@linaro.org; patches@linaro.org
> Subject: [PATCH 2/4] thermal: Add generic cpufreq cooling implementation
>
> This patch adds support for generic cpu thermal cooling low level
> implementations using frequency scaling up/down based on the request
> from user. Different cpu related cooling devices can be registered by the
I believe what you mean by 'user' is another Driver using this code.. right ??
> user and the binding of these cooling devices to the corresponding
> trip points can be easily done as the registration API's return the
> cooling device pointer. The user of these api's are responsible for
> passing clipping frequency in percentages.
>
> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
> ---
> Documentation/thermal/cpu-cooling-api.txt | 40 ++++
> drivers/thermal/Kconfig | 11 +
> drivers/thermal/Makefile | 1 +
> drivers/thermal/cpu_cooling.c | 310 +++++++++++++++++++++++++++++
> include/linux/cpu_cooling.h | 54 +++++
> 5 files changed, 416 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/thermal/cpu-cooling-api.txt
> create mode 100644 drivers/thermal/cpu_cooling.c
> create mode 100644 include/linux/cpu_cooling.h
>
> diff --git a/Documentation/thermal/cpu-cooling-api.txt
> b/Documentation/thermal/cpu-cooling-api.txt
> new file mode 100644
> index 0000000..04de67c
> --- /dev/null
> +++ b/Documentation/thermal/cpu-cooling-api.txt
> @@ -0,0 +1,40 @@
> +CPU cooling api's How To
> +===================================
> +
> +Written by Amit Daniel Kachhap <amit.kachhap@linaro.org>
> +
> +Updated: 13 Dec 2011
> +
> +Copyright (c) 2011 Samsung Electronics Co., Ltd(http://www.samsung.com)
> +
> +0. Introduction
> +
> +The generic cpu cooling(freq clipping, cpuhotplug) provides
> +registration/unregistration api's to the user. The binding of the cooling
> +devices to the trip point is left for the user. The registration api's returns
> +the cooling device pointer.
> +
> +1. cpufreq cooling api's
> +
> +1.1 cpufreq registration api's
> +1.1.1 struct thermal_cooling_device *cpufreq_cooling_register(
> + struct freq_pctg_table *tab_ptr, unsigned int tab_size,
> + const struct cpumask *mask_val)
> +
> + This interface function registers the cpufreq cooling device with the name
> + "thermal-cpufreq-%x". This api can support multiple instance of cpufreq
> cooling
> + devices.
> +
> + tab_ptr: The table containing the percentage of frequency to be clipped
> for
> + each cooling state.
> + .freq_clip_pctg: Percentage of frequency to be clipped for each allowed
> + cpus.
> + .polling_interval: polling interval for this cooling state.
> + tab_size: the total number of cooling state.
Although I can understand that the table size is equal to
the total number of cooling states, it is better to name it 'num_cooling_states'
(or something) that means what it is..
> + mask_val: all the allowed cpu's where frequency clipping can happen.
> +
> +1.1.2 void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
> +
> + This interface function unregisters the "thermal-cpufreq-%x" cooling
> device.
> +
> + cdev: Cooling device pointer which has to be unregistered.
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index f7f71b2..298c1cd 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -18,3 +18,14 @@ config THERMAL_HWMON
> depends on THERMAL
> depends on HWMON=y || HWMON=THERMAL
> default y
> +
> +config CPU_THERMAL
> + bool "generic cpu cooling support"
> + depends on THERMAL
> + help
> + This implements the generic cpu cooling mechanism through frequency
> + reduction, cpu hotplug and any other ways of reducing temperature. An
> + ACPI version of this already exists(drivers/acpi/processor_thermal.c).
> + This will be useful for platforms using the generic thermal interface
> + and not the ACPI interface.
> + If you want this support, you should say Y or M here.
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 31108a0..655cbc4 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -3,3 +3,4 @@
> #
>
> obj-$(CONFIG_THERMAL) += thermal_sys.o
> +obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
> diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
> new file mode 100644
> index 0000000..298f550
> --- /dev/null
> +++ b/drivers/thermal/cpu_cooling.c
> @@ -0,0 +1,310 @@
> +/*
> + * linux/drivers/thermal/cpu_cooling.c
> + *
> + * Copyright (C) 2011 Samsung Electronics Co., Ltd(http://www.samsung.com)
> + * Copyright (C) 2011 Amit Daniel <amit.kachhap@linaro.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; version 2 of the License.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
> + *
> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/thermal.h>
> +#include <linux/platform_device.h>
> +#include <linux/cpufreq.h>
> +#include <linux/err.h>
> +#include <linux/slab.h>
> +#include <linux/cpu.h>
> +#include <linux/cpu_cooling.h>
> +
> +#ifdef CONFIG_CPU_FREQ
Except the _idr methods, all the code is inside this #ifdef.
So, I think it is better to add this dependency in Kconfig,
and leave this code clean w/o many #ifdef's.
> +struct cpufreq_cooling_device {
> + int id;
> + struct thermal_cooling_device *cool_dev;
> + struct freq_pctg_table *tab_ptr;
> + unsigned int tab_size;
> + unsigned int cpufreq_state;
> + const struct cpumask *allowed_cpus;
> + struct list_head node;
> +};
> +
> +static LIST_HEAD(cooling_cpufreq_list);
> +static DEFINE_MUTEX(cooling_cpufreq_lock);
> +static DEFINE_IDR(cpufreq_idr);
> +static struct cpufreq_cooling_device *notify_cpufreq;
Please move this after the DEFINE_PER_CPU.
Hard to notice here..
> +static DEFINE_PER_CPU(unsigned int, max_policy_freq);
> +#endif /*CONFIG_CPU_FREQ*/
> +
> +static int get_idr(struct idr *idr, struct mutex *lock, int *id)
> +{
> + int err;
> +again:
> + if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
> + return -ENOMEM;
> +
> + if (lock)
> + mutex_lock(lock);
> + err = idr_get_new(idr, NULL, id);
> + if (lock)
> + mutex_unlock(lock);
> + if (unlikely(err == -EAGAIN))
> + goto again;
> + else if (unlikely(err))
> + return err;
> +
> + *id = *id & MAX_ID_MASK;
> + return 0;
> +}
> +
> +static void release_idr(struct idr *idr, struct mutex *lock, int id)
> +{
> + if (lock)
> + mutex_lock(lock);
> + idr_remove(idr, id);
> + if (lock)
> + mutex_unlock(lock);
> +}
> +
> +#ifdef CONFIG_CPU_FREQ
> +/*Below codes defines functions to be used for cpufreq as cooling device*/
> +static bool is_cpufreq_valid(int cpu)
> +{
> + struct cpufreq_policy policy;
> + if (!cpufreq_get_policy(&policy, cpu))
> + return true;
> + return false;
Why not just do a return !cpufreq_get_policy(&policy, cpu);
> +}
> +
> +static int cpufreq_apply_cooling(struct cpufreq_cooling_device
> *cpufreq_device,
> + unsigned long cooling_state)
> +{
> + int cpuid, this_cpu = smp_processor_id();
> +
> + if (!is_cpufreq_valid(this_cpu))
You are not using this_cpu anywhere else..so, directly use
Smp_processor_id() here..
> + return 0;
> +
> + if (cooling_state > cpufreq_device->tab_size)
> + return -EINVAL;
> +
> + /*Check if last cooling level is same as current cooling level*/
Use either 'state' or 'level' in comments as well as the variable name
Makes it easy to read..
> + if (cpufreq_device->cpufreq_state == cooling_state)
> + return 0;
> +
> + cpufreq_device->cpufreq_state = cooling_state;
> +
> + /*cpufreq thermal notifier uses this cpufreq device pointer*/
> + notify_cpufreq = cpufreq_device;
> +
> + for_each_cpu(cpuid, cpufreq_device->allowed_cpus) {
> + if (is_cpufreq_valid(cpuid))
> + cpufreq_update_policy(cpuid);
> + }
> +
> + return 0;
> +}
> +
> +static int thermal_cpufreq_notifier(struct notifier_block *nb,
> + unsigned long event, void *data)
> +{
> + struct cpufreq_policy *policy = data;
> + struct freq_pctg_table *th_table;
> + unsigned long max_freq = 0;
> + unsigned int th_pctg = 0, level;
> +
> + if (event != CPUFREQ_ADJUST)
> + return 0;
> +
> + if (!notify_cpufreq)
> + return 0;
Why not combine both if's with an || ?
> +
> + level = notify_cpufreq->cpufreq_state;
Yes..here it is..please use level/state..
> +
> + if (level > 0) {
> + th_table =
> + &(notify_cpufreq->tab_ptr[level - 1]);
> + th_pctg = th_table->freq_clip_pctg;
> + max_freq =
> + (policy->cpuinfo.max_freq * (100 - th_pctg)) / 100;
> +
> + if (per_cpu(max_policy_freq, policy->cpu) == 0)
> + per_cpu(max_policy_freq, policy->cpu) = policy->max;
> + } else {
> + if (per_cpu(max_policy_freq, policy->cpu) != 0) {
> + max_freq = per_cpu(max_policy_freq, policy->cpu);
> + per_cpu(max_policy_freq, policy->cpu) = 0;
> + } else {
> + max_freq = policy->max;
> + }
> + }
> +
> + cpufreq_verify_within_limits(policy, 0, max_freq);
> +
> + return 0;
> +}
> +
> +/*
> + * cpufreq cooling device callback functions
> + */
> +static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
> + unsigned long *state)
> +{
> + struct cpufreq_cooling_device *cpufreq_device = NULL;
Why assigning NULL ?
> +
> + mutex_lock(&cooling_cpufreq_lock);
> + list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node)
> + if (cpufreq_device && cpufreq_device->cool_dev == cdev)
> + break;
> +
> + mutex_unlock(&cooling_cpufreq_lock);
> + if (!cpufreq_device || cpufreq_device->cool_dev != cdev)
> + return -EINVAL;
> +
> + *state = cpufreq_device->tab_size;
> + return 0;
> +}
The above can be simplified this way:
int ret = -EINVAL;
mutex_lock(&cooling_cpufreq_lock);
list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node)
if (cpufreq_device->cool_dev == cdev) {
*state = cpufreq_device->tab_size;
ret = 0;
break;
}
mutex_unlock(&cooling_cpufreq_lock);
return ret;
I think the same can be done for the get_ function below..and similar ones
in the patch 3/4.
> +
> +static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
> + unsigned long *state)
> +{
> + struct cpufreq_cooling_device *cpufreq_device = NULL;
> +
> + mutex_lock(&cooling_cpufreq_lock);
> + list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node)
> + if (cpufreq_device && cpufreq_device->cool_dev == cdev)
> + break;
> +
> + mutex_unlock(&cooling_cpufreq_lock);
> + if (!cpufreq_device || cpufreq_device->cool_dev != cdev)
> + return -EINVAL;
> + *state = cpufreq_device->cpufreq_state;
> + return 0;
> +}
> +
> +/*This cooling may be as PASSIVE/ACTIVE type*/
> +static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
> + unsigned long state)
> +{
> + struct cpufreq_cooling_device *cpufreq_device = NULL;
> +
> + mutex_lock(&cooling_cpufreq_lock);
> + list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node)
> + if (cpufreq_device && cpufreq_device->cool_dev == cdev)
> + break;
> +
> + mutex_unlock(&cooling_cpufreq_lock);
> + if (!cpufreq_device || cpufreq_device->cool_dev != cdev)
> + return -EINVAL;
> +
> + cpufreq_apply_cooling(cpufreq_device, state);
> + return 0;
> +}
> +
> +/* bind cpufreq callbacks to cpufreq cooling device */
> +static struct thermal_cooling_device_ops cpufreq_cooling_ops = {
> + .get_max_state = cpufreq_get_max_state,
> + .get_cur_state = cpufreq_get_cur_state,
> + .set_cur_state = cpufreq_set_cur_state,
> +};
> +
> +static struct notifier_block thermal_cpufreq_notifier_block = {
> + .notifier_call = thermal_cpufreq_notifier,
> +};
> +
> +struct thermal_cooling_device *cpufreq_cooling_register(
> + struct freq_pctg_table *tab_ptr, unsigned int tab_size,
> + const struct cpumask *mask_val)
> +{
> + struct thermal_cooling_device *cool_dev;
> + struct cpufreq_cooling_device *cpufreq_dev = NULL;
> + unsigned int count = 0;
> + char dev_name[THERMAL_NAME_LENGTH];
> + int ret = 0, id = 0;
> +
> + if (tab_ptr == NULL || tab_size == 0)
> + return ERR_PTR(-EINVAL);
> +
> + list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node)
> + count++;
> +
> + cpufreq_dev =
> + kzalloc(sizeof(struct cpufreq_cooling_device), GFP_KERNEL);
> +
> + if (!cpufreq_dev)
> + return ERR_PTR(-ENOMEM);
> +
> + cpufreq_dev->tab_ptr = tab_ptr;
> + cpufreq_dev->tab_size = tab_size;
> + cpufreq_dev->allowed_cpus = mask_val;
> +
> + ret = get_idr(&cpufreq_idr, &cooling_cpufreq_lock, &cpufreq_dev->id);
> + if (ret) {
> + kfree(cpufreq_dev);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + sprintf(dev_name, "thermal-cpufreq-%d", cpufreq_dev->id);
> +
> + cool_dev = thermal_cooling_device_register(dev_name, cpufreq_dev,
> + &cpufreq_cooling_ops);
> + if (!cool_dev) {
> + release_idr(&cpufreq_idr, &cooling_cpufreq_lock,
> + cpufreq_dev->id);
> + kfree(cpufreq_dev);
> + return ERR_PTR(-EINVAL);
> + }
> + cpufreq_dev->id = id;
> + cpufreq_dev->cool_dev = cool_dev;
> + mutex_lock(&cooling_cpufreq_lock);
> + list_add_tail(&cpufreq_dev->node, &cooling_cpufreq_list);
> + mutex_unlock(&cooling_cpufreq_lock);
> +
> + if (count == 0)
> + cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
> + CPUFREQ_POLICY_NOTIFIER);
Why do we register only when count is 0 ?
Or should this be 'if (count > 0)' ?
> + return cool_dev;
> +}
> +EXPORT_SYMBOL(cpufreq_cooling_register);
> +
> +void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
> +{
> + struct cpufreq_cooling_device *cpufreq_dev = NULL;
> + unsigned int count = 0;
> +
> + mutex_lock(&cooling_cpufreq_lock);
> + list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node) {
> + if (cpufreq_dev && cpufreq_dev->cool_dev == cdev)
> + break;
> + count++;
> + }
> +
> + if (!cpufreq_dev || cpufreq_dev->cool_dev != cdev) {
> + mutex_unlock(&cooling_cpufreq_lock);
> + return;
> + }
> +
> + list_del(&cpufreq_dev->node);
> + mutex_unlock(&cooling_cpufreq_lock);
> +
> + if (count == 1)
Same here..I do not get the idea behind this..
Shouldn't this be 'if (count > 0)' ?
In general,
I would like to see a real driver using these API's. This will help
everybody to understand the working of these API's much better.
Thanks,
Durga
> + cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
> + CPUFREQ_POLICY_NOTIFIER);
> +
> + thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
> + release_idr(&cpufreq_idr, &cooling_cpufreq_lock, cpufreq_dev->id);
> + kfree(cpufreq_dev);
> +}
> +EXPORT_SYMBOL(cpufreq_cooling_unregister);
> +#endif /*CONFIG_CPU_FREQ*/
> diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h
> new file mode 100644
> index 0000000..5dc5632
> --- /dev/null
> +++ b/include/linux/cpu_cooling.h
> @@ -0,0 +1,54 @@
> +/*
> + * linux/include/linux/cpu_cooling.h
> + *
> + * Copyright (C) 2011 Samsung Electronics Co., Ltd(http://www.samsung.com)
> + * Copyright (C) 2011 Amit Daniel <amit.kachhap@linaro.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; version 2 of the License.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
> + *
> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + */
> +
> +#ifndef __CPU_COOLING_H__
> +#define __CPU_COOLING_H__
> +
> +#include <linux/thermal.h>
> +
> +struct freq_pctg_table {
> + unsigned int freq_clip_pctg;
> + unsigned int polling_interval;
> +};
> +
> +#ifdef CONFIG_CPU_FREQ
> +struct thermal_cooling_device *cpufreq_cooling_register(
> + struct freq_pctg_table *tab_ptr, unsigned int tab_size,
> + const struct cpumask *mask_val);
> +
> +void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev);
> +#else /*!CONFIG_CPU_FREQ*/
> +static inline struct thermal_cooling_device *cpufreq_cooling_register(
> + struct freq_pctg_table *tab_ptr, unsigned int tab_size,
> + const struct cpumask *mask_val)
> +{
> + return NULL;
> +}
> +static inline void cpufreq_cooling_unregister(
> + struct thermal_cooling_device *cdev)
> +{
> + return;
> +}
> +#endif /*CONFIG_CPU_FREQ*/
> +
> +#endif /* __CPU_COOLING_H__ */
> --
> 1.7.1
^ permalink raw reply
* Re: 3.3-rc4+: Reported regressions from 3.2
From: Rafael J. Wysocki @ 2012-02-24 0:13 UTC (permalink / raw)
To: David Miller
Cc: linux-kernel, maciej.rutecki, florian, akpm, torvalds,
kernel-testers, netdev, linux-acpi, linux-pm, linux-scsi,
linux-wireless, dri-devel
In-Reply-To: <20120223.182825.1031311733171854243.davem@davemloft.net>
On Friday, February 24, 2012, David Miller wrote:
> From: "Rafael J. Wysocki" <rjw@sisk.pl>
> Date: Thu, 23 Feb 2012 23:51:20 +0100 (CET)
>
> > Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=42776
> > Subject : OF-related boot crash in 3.3.0-rc3-00188-g3ec1e88
> > Submitter : Meelis Roos <mroos@linux.ee>
> > Date : 2012-02-13 7:45 (11 days old)
> > Message-ID : <alpine.SOC.1.00.1202130942030.1488@math.ut.ee>
> > References : http://marc.info/?l=linux-kernel&m=132911916331615&w=2
>
> Potential workaround/fix at:
>
> http://marc.info/?l=linux-kernel&m=132993294018762&w=2
Thanks, updated.
Rafael
^ permalink raw reply
* Re: 3.3-rc4+: Reported regressions from 3.2
From: David Miller @ 2012-02-23 23:28 UTC (permalink / raw)
To: rjw
Cc: linux-scsi, linux-acpi, netdev, linux-wireless, linux-kernel,
dri-devel, florian, akpm, kernel-testers, torvalds, linux-pm,
maciej.rutecki
In-Reply-To: <y44pDJ8xoDC.A.08E.h8rRPB@chimera>
From: "Rafael J. Wysocki" <rjw@sisk.pl>
Date: Thu, 23 Feb 2012 23:51:20 +0100 (CET)
> Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=42776
> Subject : OF-related boot crash in 3.3.0-rc3-00188-g3ec1e88
> Submitter : Meelis Roos <mroos@linux.ee>
> Date : 2012-02-13 7:45 (11 days old)
> Message-ID : <alpine.SOC.1.00.1202130942030.1488@math.ut.ee>
> References : http://marc.info/?l=linux-kernel&m=132911916331615&w=2
Potential workaround/fix at:
http://marc.info/?l=linux-kernel&m=132993294018762&w=2
^ permalink raw reply
* 3.3-rc4+: Reported regressions 3.1 -> 3.2
From: Rafael J. Wysocki @ 2012-02-23 23:05 UTC (permalink / raw)
To: Linux Kernel Mailing List
Cc: Maciej Rutecki, Florian Mickler, Andrew Morton, Linus Torvalds,
Kernel Testers List, Network Development, Linux ACPI,
Linux PM List, Linux SCSI List, Linux Wireless List, DRI
This message contains a list of some post-3.1 regressions introduced before
3.2, for which there are no fixes in the mainline known to the tracking team.
If any of them have been fixed already, please let us know.
If you know of any other unresolved post-3.1 regressions, please let us know
either and we'll add them to the list. Also, please let us know if any
of the entries below are invalid.
Each entry from the list will be sent additionally in an automatic reply to
this message with CCs to the people involved in reporting and handling the
issue.
Listed regressions statistics:
Date Total Pending Unresolved
----------------------------------------
2012-02-24 9 8 7
Unresolved regressions
----------------------
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=42714
Subject : 3.2.x Regression for SAS Controller on Dell C6100
Submitter : Herbert Poetzl <herbert-dBHVzrDq9nF4Lj/PQRBjDg@public.gmane.org>
Date : 2012-01-31 18:09 (24 days old)
Message-ID : <20120131180949.GT29272-ZD0Mn47LIGX0Pe/G4T7+5F6hYfS7NtTn@public.gmane.org>
References : http://marc.info/?l=linux-kernel&m=132803360019481&w=2
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=42685
Subject : multi-media key "mute" no longer recognized
Submitter : Toralf Förster <toralf.foerster-Mmb7MZpHnFY@public.gmane.org>
Date : 2012-01-23 10:52 (32 days old)
Message-ID : <201201231152.25344.toralf.foerster-Mmb7MZpHnFY@public.gmane.org>
References : http://marc.info/?l=linux-kernel&m=132731613606121&w=2
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=42684
Subject : FYIO: usb/video 3.2 breaks logitech 9000pro webcam focus control
Submitter : M G Berberich <berberic-vmG2aq7wDA3mFSIgyKwhw7NAH6kLmebB@public.gmane.org>
Date : 2012-01-23 21:49 (32 days old)
Message-ID : <20120123214934.GA4240@invalid>
References : http://marc.info/?l=linux-kernel&m=132735609521443&w=2
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=42631
Subject : Sound broken in 3.2.0 and 3.2.1
Submitter : Joseph Parmelee <jparmele-0t/HR9YC43JWk0Htik3J/w@public.gmane.org>
Date : 2012-01-13 19:44 (42 days old)
Message-ID : <alpine.LNX.2.00.1201131241090.1239@bruno>
References : http://marc.info/?l=linux-kernel&m=132648462806235&w=2
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=42629
Subject : recvmsg sleeping from invalid context
Submitter : Dave Jones <davej-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Date : 2012-01-13 18:24 (42 days old)
Message-ID : <20120113182401.GA25885-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
References : http://marc.info/?l=linux-kernel&m=132647921304184&w=2
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=42611
Subject : kernel 3.2 crashes too early on HP dc7700 and HP t5000 series
Submitter : Dmitry D. Khlebnikov <galaxy-cxoSlKxDwOJWk0Htik3J/w@public.gmane.org>
Date : 2012-01-10 11:49 (45 days old)
Message-ID : <20120110114905.GC13535-cxoSlKxDwOJWk0Htik3J/w@public.gmane.org>
References : http://marc.info/?l=linux-kernel&m=132619793012702&w=2
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=42610
Subject : 3.2.0 reboot powers off machine
Submitter : Tom Weber <l_linux-kernel-w8lRr17kQ6puIcIf6OMhTAC/G2K4zDHf@public.gmane.org>
Date : 2012-01-11 11:03 (44 days old)
Message-ID : <1326279818.24835.12.camel@utumno>
References : http://marc.info/?l=linux-kernel&m=132628059308023&w=2
Regressions with patches
------------------------
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=42571
Subject : BUG in ofcs2_change_file_space
Submitter : Bret Towe <magnade-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Date : 2012-01-05 22:23 (50 days old)
Message-ID : <CALjC5haTNDMOTDPL1m1uBTc2a-Qb8cvR44SCaVDFMzGc+Gw3+w@mail.gmail.com>
References : http://marc.info/?l=linux-kernel&m=132580236211209&w=2
Patch : http://oss.oracle.com/pipermail/ocfs2-devel/2012-January/008464.html
For details, please visit the bug entries and follow the links given in
references.
As you can see, there is a Bugzilla entry for each of the listed regressions.
There also is a Bugzilla entry used for tracking the regressions introduced
between 3.1 and 3.2, unresolved as well as resolved, at:
http://bugzilla.kernel.org/show_bug.cgi?id=42566
Please let the tracking team know if there are any Bugzilla entries that
should be added to the list in there.
Thanks!
^ 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