* 3.1-rc1-git9: Reported regressions 2.6.39 -> 3.0
From: Rafael J. Wysocki @ 2011-08-14 19:02 UTC (permalink / raw)
To: Linux Kernel Mailing List
Cc: Linux SCSI List, Linux ACPI, Network Development,
Linux Wireless List, DRI, Florian Mickler, Andrew Morton,
Kernel Testers List, Linus Torvalds, Linux PM List,
Maciej Rutecki
[NOTE:
We already have a bug entry for tracking regressions from 3.0:
http://bugzilla.kernel.org/show_bug.cgi?id=40982
but there are no reports linked to it, mostly because Maciej is on vacation,
but also because the frequency of reporting regressions has been low
recently. So, if you're seeing a regression from 3.0, please let us know.]
This message contains a list of some post-2.6.39 regressions introduced before
3.0, 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-2.6.39 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
----------------------------------------
2011-08-14 63 22 19
Unresolved regressions
----------------------
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=40282
Subject : IP forwarding regression since 3.0-rc6
Submitter : Stephan Seitz <stse+lkml@fsing.rootsland.net>
Date : 2011-07-25 12:51 (21 days old)
Message-ID : <20110725T141145.GA.2ae38.stse@fsing.rootsland.net>
References : http://marc.info/?l=linux-kernel&m=131159880004908&w=2
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=40172
Subject : lockdep trace from post 3.0.
Submitter : Dave Jones <davej@redhat.com>
Date : 2011-07-24 1:32 (22 days old)
Message-ID : <20110724013257.GA6777@redhat.com>
References : http://marc.info/?l=linux-kernel&m=131147120206819&w=2
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=40092
Subject : RCU stall in linux-3.0.0
Submitter : Philip Armstrong <phil@kantaka.co.uk>
Date : 2011-07-25 21:44 (21 days old)
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=40072
Subject : suspend/resume problems w/ kernel 3.0 and a *docked* ThinkPad T400, unless iwlagn unloaded
Submitter : Toralf Förster <toralf.foerster@gmx.de>
Date : 2011-07-23 19:27 (23 days old)
Message-ID : <201107232127.34255.toralf.foerster@gmx.de>
References : http://marc.info/?l=linux-kernel&m=131144928023465&w=2
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=39972
Subject : [regression] Radeon multi-screen
Submitter : Robert Piasek <dagger@gentoo.org>
Date : 2011-07-25 14:04 (21 days old)
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=39922
Subject : WIFI becomes EXTREMELY slow ( ath9k ) making the connection unusable
Submitter : Lukasz Olszewski <lcpak@epoczta.pl>
Date : 2011-07-24 09:20 (22 days old)
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=39912
Subject : WARNING: at drivers/tty/tty_ldisc.c:766 tty_ldisc_reinit+0x7d/0x90()
Submitter : Vegard Nossum <vegard.nossum@gmail.com>
Date : 2011-07-19 11:33 (27 days old)
Message-ID : <CAOMGZ=HS2EPkYD=5HfkSPpwPqHB5V3q0vaFmoZ8Hh+pMM9phrQ@mail.gmail.com>
References : http://marc.info/?l=linux-kernel&m=131107522914558&w=2
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=39902
Subject : ath: Unable to reset channel (2412 MHz), reset status -5
Submitter : Justin P. Mattock <justinmattock@gmail.com>
Date : 2011-07-19 6:13 (27 days old)
Message-ID : <4E252076.1050309@gmail.com>
References : http://marc.info/?l=linux-kernel&m=131105603428323&w=2
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=39832
Subject : HDA ATI HDMI: no sound with 3.0 - 2.6.39.3 works
Submitter : Andreas Steinmetz <ast@domdv.de>
Date : 2011-07-23 01:36 (23 days old)
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=39732
Subject : JBD: Spotted dirty metadata buffer (dev = dm-2, blocknr = 2512725). There's a risk of filesystem corruption in case of system crash.
Submitter : Witold Baryluk <baryluk@smp.if.uj.edu.pl>
Date : 2011-07-22 04:22 (24 days old)
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=39412
Subject : Win Vista and Win2K8 guests' network breaks down
Submitter : Jay Ren <yongjie.ren@intel.com>
Date : 2011-07-15 15:31 (31 days old)
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=39342
Subject : [3.0.0-rc6-git7] possible recursive locking at cache_alloc_refill
Submitter : Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Date : 2011-07-11 12:37 (35 days old)
Message-ID : <201107112137.FAD00545.SHtLOFOJOMFQFV@I-love.SAKURA.ne.jp>
References : http://marc.info/?l=linux-kernel&m=131038788902151&w=2
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=39192
Subject : firmware_install fails with parallel make
Submitter : Ambroz Bizjak <ambrop7@gmail.com>
Date : 2011-07-12 09:35 (34 days old)
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=39132
Subject : Starting with 3.0.0-rc6, masquerading seems to be broken.
Submitter : David Hill <hilld@binarystorm.net>
Date : 2011-07-10 19:45 (36 days old)
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=38922
Subject : Lenovo T420 (Sandy Bridge) Crashes on SD Card gpt partition writing and io errors on insert
Submitter : Peter Vollmer <vollmerpeter@googlemail.com>
Date : 2011-07-07 17:33 (39 days old)
First-Bad-Commit: http://git.kernel.org/linus/a3c7778f8153b9e4eceea6738973280b9e63c618
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=38622
Subject : [radeon cayman regresion] artefacts on screen
Submitter : Marek Hobler, 'neutrinus' <kernellist@neutrinus.com>
Date : 2011-07-01 09:35 (45 days old)
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=38612
Subject : Boot time warnings in APIC
Submitter : Bill Huey <bill.huey@gmail.com>
Date : 2011-07-01 06:44 (45 days old)
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=38582
Subject : T510 43495KG won't resume with 32bit installation
Submitter : Marc Koschewski <marc@osknowledge.org>
Date : 2011-06-30 22:39 (46 days old)
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=37432
Subject : sdhci-pci fails on 3.0.0-rc1 on Dell E6510
Submitter : Oliver Hartkopp <socketcan@hartkopp.net>
Date : 2011-06-07 20:06 (69 days old)
First-Bad-Commit: http://git.kernel.org/linus/da7822e5ad71ec9b745b412639f1e5e0ba795a20
References : http://article.gmane.org/gmane.linux.kernel.mmc/8442
https://lkml.org/lkml/2011/6/23/660
Regressions with patches
------------------------
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=40062
Subject : USB related "unable to handle kernel paging request" in 3.0.0-rc7
Submitter : Tino Keitel <tino.keitel@tikei.de>
Date : 2011-07-22 19:27 (24 days old)
Message-ID : <20110722192722.GA9369@x61.home>
References : http://marc.info/?l=linux-kernel&m=131136334621623&w=2
Patch : https://lkml.org/lkml/2011/8/2/174
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=39882
Subject : Kernel oops when turning bluetooth mouse on
Submitter : Lamarque V. Souza <lamarque@gmail.com>
Date : 2011-07-24 02:09 (22 days old)
Patch : https://bugzilla.kernel.org/attachment.cgi?id=66632
Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=37872
Subject : CPU lockup during boot
Submitter : Bruno Wolff III <bruno@wolff.to>
Date : 2011-06-19 15:00 (57 days old)
Patch : https://bugzilla.kernel.org/show_bug.cgi?id=37872#c16
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 2.6.39 and 3.0, unresolved as well as resolved, at:
http://bugzilla.kernel.org/show_bug.cgi?id=36912
Please let the tracking team know if there are any Bugzilla entries that
should be added to the list in there.
Thanks!
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm
^ permalink raw reply
* [PATCH] thermal: Prevent polling from happening during system suspend
From: Rafael J. Wysocki @ 2011-08-14 21:30 UTC (permalink / raw)
To: Len Brown; +Cc: ACPI Devel Mailing List, Linux PM mailing list, LKML
From: Rafael J. Wysocki <rjw@sisk.pl>
The thermal driver should use a freezable workqueue to schedule
polling to prevent thermal_zone_device_update() from being run
during system suspend, when the devices it relies on may be inactive.
Make it use the system freezable workqueue for this purpose.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
drivers/thermal/thermal_sys.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
Index: linux/drivers/thermal/thermal_sys.c
===================================================================
--- linux.orig/drivers/thermal/thermal_sys.c
+++ linux/drivers/thermal/thermal_sys.c
@@ -678,10 +678,10 @@ static void thermal_zone_device_set_poll
return;
if (delay > 1000)
- schedule_delayed_work(&(tz->poll_queue),
+ queue_delayed_work(system_freezable_wq, &(tz->poll_queue),
round_jiffies(msecs_to_jiffies(delay)));
else
- schedule_delayed_work(&(tz->poll_queue),
+ queue_delayed_work(system_freezable_wq, &(tz->poll_queue),
msecs_to_jiffies(delay));
}
^ permalink raw reply
* Re: [PATCH 07/15] PM QoS: add a global notification mechanism for the device constraints
From: Rafael J. Wysocki @ 2011-08-14 21:50 UTC (permalink / raw)
To: jean.pihet
Cc: markgross, Mark Brown, Linux PM mailing list, linux-omap,
Jean Pihet
In-Reply-To: <1313075212-8366-8-git-send-email-j-pihet@ti.com>
Hi,
There is some code duplication in this patch that should better be
avoided (details below).
On Thursday, August 11, 2011, jean.pihet@newoldbits.com wrote:
> From: Jean Pihet <j-pihet@ti.com>
>
> Add a global notification chain that gets called upon changes to the
> aggregated constraint value for any device.
> The notification callbacks are passing the full constraint request data
> in order for the callees to have access to it. The current use is for the
> platform low-level code to access the target device of the constraint.
>
> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> ---
> drivers/base/power/qos.c | 114 ++++++++++++++++++++++++++++++++++++++++-----
> include/linux/pm_qos.h | 11 ++++
> kernel/power/qos.c | 2 +-
> 3 files changed, 113 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
> index 465e419..4b0b316 100644
> --- a/drivers/base/power/qos.c
> +++ b/drivers/base/power/qos.c
> @@ -8,6 +8,12 @@
> *
> * This QoS design is best effort based. Dependents register their QoS needs.
> * Watchers register to keep track of the current QoS needs of the system.
> + * Watchers can register different types of notification callbacks:
> + * . a per-device notification callback using the dev_pm_qos_*_notifier API.
> + * The notification chain data is stored in the per-device constraint
> + * data struct.
> + * . a system-wide notification callback using the dev_pm_qos_*_global_notifier
> + * API. The notification chain data is stored in a static variable.
> *
> * Note about the per-device constraint data struct allocation:
> * . The per-device constraints data struct ptr is tored into the device
> @@ -41,6 +47,7 @@
> #include <linux/kernel.h>
>
>
> +static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
> static void dev_pm_qos_constraints_allocate(struct device *dev);
>
> int dev_pm_qos_request_active(struct dev_pm_qos_request *req)
> @@ -64,6 +71,8 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_request_active);
> void dev_pm_qos_add_request(struct dev_pm_qos_request *req, struct device *dev,
> s32 value)
> {
> + int ret, curr_value;
> +
> if (!req) /*guard against callers passing in null */
> return;
>
> @@ -82,8 +91,19 @@ void dev_pm_qos_add_request(struct dev_pm_qos_request *req, struct device *dev,
> if (req->dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
> return;
>
> - pm_qos_update_target(dev->power.constraints,
> - &req->node, PM_QOS_ADD_REQ, value);
The following code:
> + /*
> + * Update constraints list and call the per-device callbacks if needed
> + */
> + ret = pm_qos_update_target(dev->power.constraints,
> + &req->node, PM_QOS_ADD_REQ, value);
> +
> + if (ret) {
> + /* Call the global callbacks if needed */
> + curr_value = pm_qos_read_value(req->dev->power.constraints);
> + blocking_notifier_call_chain(&dev_pm_notifiers,
> + (unsigned long)curr_value,
> + req);
> + }
is used in dev_pm_qos_update_request() and dev_pm_qos_remove_request()
with the only difference being the command given to pm_qos_update_target().
This asks for a common function, eg. dev_pm_qos_update_target(), containing
that code that will be called by all of them (and, apparently, by
dev_pm_qos_constraints_destroy()).
...
> @@ -250,9 +329,18 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
> * Update constraints list and call the per-device
> * callbacks if needed
> */
> - pm_qos_update_target(req->dev->power.constraints,
> - &req->node, PM_QOS_REMOVE_REQ,
> - PM_QOS_DEFAULT_VALUE);
> + ret |= pm_qos_update_target(req->dev->power.constraints,
> + &req->node,
> + PM_QOS_REMOVE_REQ,
> + PM_QOS_DEFAULT_VALUE);
I'm not sure why you're using the binary or operator here. Shouldn't that be
a simple assignment?
> +
> + if (ret) {
> + /* Call the global callbacks if needed */
> + curr_value = dev->power.constraints->default_value;
> + blocking_notifier_call_chain(&dev_pm_notifiers,
> + (unsigned long)curr_value,
> + req);
> + }
>
> kfree(dev->power.constraints->notifiers);
> kfree(dev->power.constraints);
Thanks,
Rafael
^ permalink raw reply
* Re: 3.1-rc1-git9: Reported regressions 2.6.39 -> 3.0
From: Paul E. McKenney @ 2011-08-15 15:37 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Linux SCSI List, Linux ACPI, Network Development,
Linux Wireless List, Linux Kernel Mailing List, DRI,
Florian Mickler, Andrew Morton, Kernel Testers List,
Linus Torvalds, Linux PM List, Maciej Rutecki
In-Reply-To: <xuTC4jRtHbB.A.l4H.gMCSOB@chimera>
On Sun, Aug 14, 2011 at 09:02:27PM +0200, Rafael J. Wysocki wrote:
> [NOTE:
> We already have a bug entry for tracking regressions from 3.0:
>
> http://bugzilla.kernel.org/show_bug.cgi?id=40982
>
> but there are no reports linked to it, mostly because Maciej is on vacation,
> but also because the frequency of reporting regressions has been low
> recently. So, if you're seeing a regression from 3.0, please let us know.]
>
> This message contains a list of some post-2.6.39 regressions introduced before
> 3.0, 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-2.6.39 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
> ----------------------------------------
> 2011-08-14 63 22 19
>
>
> Unresolved regressions
> ----------------------
>
> Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=40282
> Subject : IP forwarding regression since 3.0-rc6
> Submitter : Stephan Seitz <stse+lkml@fsing.rootsland.net>
> Date : 2011-07-25 12:51 (21 days old)
> Message-ID : <20110725T141145.GA.2ae38.stse@fsing.rootsland.net>
> References : http://marc.info/?l=linux-kernel&m=131159880004908&w=2
>
>
> Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=40172
> Subject : lockdep trace from post 3.0.
> Submitter : Dave Jones <davej@redhat.com>
> Date : 2011-07-24 1:32 (22 days old)
> Message-ID : <20110724013257.GA6777@redhat.com>
> References : http://marc.info/?l=linux-kernel&m=131147120206819&w=2
>
>
> Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=40092
> Subject : RCU stall in linux-3.0.0
> Submitter : Philip Armstrong <phil@kantaka.co.uk>
> Date : 2011-07-25 21:44 (21 days old)
This one was due the the CPU's idea of the current time getting way
out of sync (as in more than a minute's worth of disagreement, which is
pretty impressive when you think about it). So what happened is that
one CPU decided that the current grace period was a full minute old
immediately, and thus started screaming piteously. John Stultz provided
a configuration workaround, and he and Thomas Gleixner would be working
out the long-term fix.
Thanx, Paul
^ permalink raw reply
* Re: 3.1-rc1-git9: Reported regressions 2.6.39 -> 3.0
From: Rafael J. Wysocki @ 2011-08-15 17:43 UTC (permalink / raw)
To: paulmck
Cc: Linux SCSI List, Linux ACPI, Network Development,
Linux Wireless List, Linux Kernel Mailing List, DRI,
Florian Mickler, Andrew Morton, Kernel Testers List,
Linus Torvalds, Linux PM List, Maciej Rutecki
In-Reply-To: <20110815153759.GF2389@linux.vnet.ibm.com>
On Monday, August 15, 2011, Paul E. McKenney wrote:
> On Sun, Aug 14, 2011 at 09:02:27PM +0200, Rafael J. Wysocki wrote:
> > [NOTE:
> > We already have a bug entry for tracking regressions from 3.0:
> >
> > http://bugzilla.kernel.org/show_bug.cgi?id=40982
> >
> > but there are no reports linked to it, mostly because Maciej is on vacation,
> > but also because the frequency of reporting regressions has been low
> > recently. So, if you're seeing a regression from 3.0, please let us know.]
> >
> > This message contains a list of some post-2.6.39 regressions introduced before
> > 3.0, 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-2.6.39 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
> > ----------------------------------------
> > 2011-08-14 63 22 19
> >
> >
> > Unresolved regressions
> > ----------------------
> >
> > Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=40282
> > Subject : IP forwarding regression since 3.0-rc6
> > Submitter : Stephan Seitz <stse+lkml@fsing.rootsland.net>
> > Date : 2011-07-25 12:51 (21 days old)
> > Message-ID : <20110725T141145.GA.2ae38.stse@fsing.rootsland.net>
> > References : http://marc.info/?l=linux-kernel&m=131159880004908&w=2
> >
> >
> > Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=40172
> > Subject : lockdep trace from post 3.0.
> > Submitter : Dave Jones <davej@redhat.com>
> > Date : 2011-07-24 1:32 (22 days old)
> > Message-ID : <20110724013257.GA6777@redhat.com>
> > References : http://marc.info/?l=linux-kernel&m=131147120206819&w=2
> >
> >
> > Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=40092
> > Subject : RCU stall in linux-3.0.0
> > Submitter : Philip Armstrong <phil@kantaka.co.uk>
> > Date : 2011-07-25 21:44 (21 days old)
>
> This one was due the the CPU's idea of the current time getting way
> out of sync (as in more than a minute's worth of disagreement, which is
> pretty impressive when you think about it). So what happened is that
> one CPU decided that the current grace period was a full minute old
> immediately, and thus started screaming piteously. John Stultz provided
> a configuration workaround, and he and Thomas Gleixner would be working
> out the long-term fix.
Thanks for the update!
Rafael
^ permalink raw reply
* [Regression] GPF in usb_driver_release_interface during/after resume
From: Rafael J. Wysocki @ 2011-08-15 20:52 UTC (permalink / raw)
To: linux-usb; +Cc: Marcel Holtmann, Linux PM mailing list, Greg KH, LKML
Hi,
I get the following general protection fault sometimes (but sufficiently
often for it to be annoying) during resume from system suspend on Toshiba
Portege R500:
[ 330.839651] PM: Finishing wakeup.
[ 330.839656] Restarting tasks ...
[ 330.839742] e1000e 0000:01:00.0: PME# enabled
[ 330.840121] usb 2-2: USB disconnect, device number 3
[ 330.841626] option1 ttyUSB0: GSM modem (1-port) converter now disconnected from ttyUSB0
[ 330.841835] option 2-2:1.0: device disconnected
[ 330.842626] option1 ttyUSB1: GSM modem (1-port) converter now disconnected from ttyUSB1
[ 330.842770] option 2-2:1.1: device disconnected
[ 330.843506] option1 ttyUSB2: GSM modem (1-port) converter now disconnected from ttyUSB2
[ 330.843648] option 2-2:1.2: device disconnected
[ 330.857550] done.
[ 330.857591] video LNXVIDEO:00: Restoring backlight state
[ 330.915075] option1 ttyUSB3: GSM modem (1-port) converter now disconnected from ttyUSB3
[ 330.924372] option 2-2:1.3: device disconnected
[ 330.964520] usb 5-2: USB disconnect, device number 4
[ 331.218859] general protection fault: 0000 [#1] PREEMPT SMP
[ 331.218889] CPU 0
[ 331.218894] Modules linked in: bnep cryptd aes_x86_64 aes_generic xt_tcpudp xt_pkttype af_packet snd_pcm_oss snd_mixer_oss snd_seq snd_seq_device ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_raw xt_NOTRACK ipt_REJECT iptable_raw iptable_filter ip6table_mangle nf_conntrack_netbios_ns nf_conntrack_broadcast nf_conntrack_ipv4 nf_defrag_ipv4 ip_tables xt_conntrack nf_conntrack ip6table_filter ip6_tables x_tables ipv6 cpufreq_conservative cpufreq_ondemand cpufreq_userspace cpufreq_powersave acpi_cpufreq microcode freq_table mperf fuse dm_mod sr_mod cdrom arc4 joydev btusb bluetooth option usb_wwan iwl4965 pcmcia iwl_legacy mac80211 psmouse snd_hda_codec_realtek usbhid serio_raw sdhci_pci usb_storage hid usbserial sdhci firewire_ohci snd_hda_intel yenta_socket snd_hda_codec pcmcia_rs
rc sg firewire_core mmc_core snd_hwdep pcmcia_core crc_itu_t cfg80211 snd_pcm iTCO_wdt iTCO_vendor_support snd_timer snd soundcore e1000e rfkill snd_page_alloc toshiba_bluetooth battery ac !
uhci_hcd sd_mod crc_t10dif ehci_hcd usbcore rtc_cmos ext3 jbd ahci libahci libata fan processor thermal
[ 331.219207]
[ 331.219216] Pid: 906, comm: khubd Not tainted 2.6.39+ #388 TOSHIBA PORTEGE R500/Portable PC
[ 331.219237] RIP: 0010:[<ffffffffa00c5bd1>] [<ffffffffa00c5bd1>] usb_driver_release_interface+0x32/0x9f [usbcore]
[ 331.219289] RSP: 0018:ffff880078899be0 EFLAGS: 00010282
[ 331.219301] RAX: 0000000000000000 RBX: 6b6b6b6b6b6b6b6b RCX: 0000000000000006
[ 331.219314] RDX: 0000000000000006 RSI: 00000000000001e3 RDI: ffffffffa0011020
[ 331.219327] RBP: ffff880078899c00 R08: ffff880078899be0 R09: 09f911029d74e35b
[ 331.219341] R10: ffff880078899bb0 R11: 0000000000000000 R12: ffff88007a5e6548
[ 331.219354] R13: ffff880040a8d000 R14: ffffffffa0011020 R15: ffffffffa00110b8
[ 331.219368] FS: 0000000000000000(0000) GS:ffff88007f400000(0000) knlGS:0000000000000000
[ 331.219382] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[ 331.219394] CR2: 00007f4b2fd01080 CR3: 0000000079cf9000 CR4: 00000000000006f0
[ 331.219407] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 331.219420] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[ 331.219434] Process khubd (pid: 906, threadinfo ffff880078898000, task ffff88003798ee80)
[ 331.219448] Stack:
[ 331.219455] ffff880078899c00 ffff880037070388 ffff88007a5e6548 ffff880040a8d000
[ 331.219477] ffff880078899c30 ffffffffa000fdb4 ffff88007a5e6578 ffff88007a5e6578
[ 331.219497] ffff88007a5e6548 ffff88007856c188 ffff880078899c70 ffffffffa00c5af0
[ 331.219518] Call Trace:
[ 331.219535] [<ffffffffa000fdb4>] btusb_disconnect+0x9c/0xc2 [btusb]
[ 331.219566] [<ffffffffa00c5af0>] usb_unbind_interface+0x5a/0x109 [usbcore]
[ 331.219590] [<ffffffff812a3818>] __device_release_driver+0x81/0xca
[ 331.219606] [<ffffffff812a387a>] device_release_driver+0x19/0x26
[ 331.219622] [<ffffffff812a33e1>] bus_remove_device+0xc2/0xd3
[ 331.219637] [<ffffffff812a0e96>] device_del+0x12b/0x17a
[ 331.219666] [<ffffffffa00c477f>] usb_disable_device+0x8a/0x176 [usbcore]
[ 331.219695] [<ffffffffa00bdac4>] usb_disconnect+0xcd/0x137 [usbcore]
[ 331.219724] [<ffffffffa00bf1c2>] hub_thread+0x74b/0x113e [usbcore]
[ 331.219745] [<ffffffff81033ab7>] ? get_parent_ip+0x11/0x41
[ 331.219760] [<ffffffff8102c835>] ? need_resched+0x1e/0x28
[ 331.219777] [<ffffffff81057f49>] ? wake_up_bit+0x25/0x25
[ 331.219804] [<ffffffffa00bea77>] ? usb_new_device+0x1ca/0x1ca [usbcore]
[ 331.219821] [<ffffffff81057aa9>] kthread+0x7d/0x85
[ 331.219838] [<ffffffff8136af54>] kernel_thread_helper+0x4/0x10
[ 331.219854] [<ffffffff81369a44>] ? retint_restore_args+0xe/0xe
[ 331.219870] [<ffffffff81057a2c>] ? __init_kthread_worker+0x56/0x56
[ 331.219885] [<ffffffff8136af50>] ? gs_change+0xb/0xb
[ 331.219896] Code: 54 53 48 89 f3 48 83 ec 08 be e3 01 00 00 48 85 db 74 0a 48 85 ff 75 13 be e6 01 00 00 48 c7 c7 9c 25 0d a0 e8 b6 6d f7 e0 eb 65
[ 331.219983] 8b 83 20 01 00 00 48 85 c0 74 59 48 81 c7 98 00 00 00 48 39
[ 331.220031] RIP [<ffffffffa00c5bd1>] usb_driver_release_interface+0x32/0x9f [usbcore]
[ 331.220065] RSP <ffff880078899be0>
[ 331.240771] ---[ end trace be6a0c945bcc56b9 ]---
This started to happen during the 3.0 development cycle, as far as I can say.
According to GDB, usb_driver_release_interface+0x32 is line 484 in
drivers/usb/core/driver.c:
void usb_driver_release_interface(struct usb_driver *driver,
struct usb_interface *iface)
{
struct device *dev = &iface->dev;
/* this should never happen, don't release something that's not ours */
---> if (!dev->driver || dev->driver != &driver->drvwrap.driver)
so this looks like a use-after-free to me and since it doesn't occur every
time, there seems to be a race condition in the USB stack.
I tried to figure it out myself, but apparently it's too tricky to me. :-)
Thanks,
Rafael
^ permalink raw reply
* Re: [PATCH 05/15] PM QoS: generalize and export the constraints management code
From: mark gross @ 2011-08-16 4:08 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: markgross, Mark Brown, Linux PM mailing list, linux-omap,
Jean Pihet
In-Reply-To: <201108141537.43752.rjw@sisk.pl>
On Sun, Aug 14, 2011 at 03:37:43PM +0200, Rafael J. Wysocki wrote:
> On Sunday, August 14, 2011, Jean Pihet wrote:
> > Hi Rafael, Mark,
> >
> > On Sat, Aug 13, 2011 at 10:34 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > > On Saturday, August 13, 2011, mark gross wrote:
> > >> On Thu, Aug 11, 2011 at 05:06:42PM +0200, jean.pihet@newoldbits.com wrote:
> > >> > From: Jean Pihet <j-pihet@ti.com>
> > >> >
> > >> > In preparation for the per-device constratins support:
> > >> > - rename update_target to pm_qos_update_target
> > >> > - generalize and export pm_qos_update_target for usage by the upcoming
> > >> > per-device latency constraints framework:
> > >> > . operate on struct pm_qos_constraints for constraints management,
> > >> > . introduce an 'action' parameter for constraints add/update/remove,
> > >> > . the return value indicates if the aggregated constraint value has
> > >> > changed,
> > >> > - update the internal code to operate on struct pm_qos_constraints
> > >> > - add a NULL pointer check in the API functions
> > >> >
> > >> > Signed-off-by: Jean Pihet <j-pihet@ti.com>
> > ...
> > >> > +/* Action requested to pm_qos_update_target */
> > >> > +enum pm_qos_req_action {
> > >> > + PM_QOS_ADD_REQ, /* Add a new request */
> > >> > + PM_QOS_UPDATE_REQ, /* Update an existing request */
> > >> > + PM_QOS_REMOVE_REQ /* Remove an existing request */
> > >> > +};
> > >> > +
> > >>
> > >> What do you need this enum for? The function names *_update_*, *_add_*,
> > >> and *_remove_* seem to be pretty redundant if you have to pass an enum
> > >> that could possibly conflict with the function name.
> > >>
> > >> > #ifdef CONFIG_PM
> > >> > +int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
> > >> > + enum pm_qos_req_action action, int value);
> > >> The action for update_target better damn well be "PM_QOS_UPDATE_REQ" or
> > >> there is something strange going on.... BTW what shold this function do
> > >> if the pm_qos_req_action was *not* the UPDATE one?
> >
> > The meaning of pm_qos_update_target is 'update the PM QoS target
> > constraints lists'. As described in the changelog the intention of
> > this patch is to implement the constraints lists management logic in
> > update_target and simplify the API functions (add/update/remove). It
> > is also exported for the upcoming (patch 06/15]) to use it as well.
>
> The enums are fine by me and they allow us to simplify the code
> quite a bit.
>
Ok, but they look a bit sloppy to me as we now have an API that says
"add" we can actually pass in an enum that says "remove".
--mark
^ permalink raw reply
* Re: [PATCH 05/15] PM QoS: generalize and export the constraints management code
From: Jean Pihet @ 2011-08-16 6:44 UTC (permalink / raw)
To: markgross; +Cc: Mark Brown, Linux PM mailing list, linux-omap, Jean Pihet
In-Reply-To: <20110816040809.GA11921@gvim.org>
On Tue, Aug 16, 2011 at 6:08 AM, mark gross <markgross@thegnar.org> wrote:
> On Sun, Aug 14, 2011 at 03:37:43PM +0200, Rafael J. Wysocki wrote:
>> On Sunday, August 14, 2011, Jean Pihet wrote:
>> > Hi Rafael, Mark,
>> >
>> > On Sat, Aug 13, 2011 at 10:34 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>> > > On Saturday, August 13, 2011, mark gross wrote:
>> > >> On Thu, Aug 11, 2011 at 05:06:42PM +0200, jean.pihet@newoldbits.com wrote:
>> > >> > From: Jean Pihet <j-pihet@ti.com>
>> > >> >
>> > >> > In preparation for the per-device constratins support:
>> > >> > - rename update_target to pm_qos_update_target
>> > >> > - generalize and export pm_qos_update_target for usage by the upcoming
>> > >> > per-device latency constraints framework:
>> > >> > . operate on struct pm_qos_constraints for constraints management,
>> > >> > . introduce an 'action' parameter for constraints add/update/remove,
>> > >> > . the return value indicates if the aggregated constraint value has
>> > >> > changed,
>> > >> > - update the internal code to operate on struct pm_qos_constraints
>> > >> > - add a NULL pointer check in the API functions
>> > >> >
>> > >> > Signed-off-by: Jean Pihet <j-pihet@ti.com>
>> > ...
>> > >> > +/* Action requested to pm_qos_update_target */
>> > >> > +enum pm_qos_req_action {
>> > >> > + PM_QOS_ADD_REQ, /* Add a new request */
>> > >> > + PM_QOS_UPDATE_REQ, /* Update an existing request */
>> > >> > + PM_QOS_REMOVE_REQ /* Remove an existing request */
>> > >> > +};
>> > >> > +
>> > >>
>> > >> What do you need this enum for? The function names *_update_*, *_add_*,
>> > >> and *_remove_* seem to be pretty redundant if you have to pass an enum
>> > >> that could possibly conflict with the function name.
>> > >>
>> > >> > #ifdef CONFIG_PM
>> > >> > +int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
>> > >> > + enum pm_qos_req_action action, int value);
>> > >> The action for update_target better damn well be "PM_QOS_UPDATE_REQ" or
>> > >> there is something strange going on.... BTW what shold this function do
>> > >> if the pm_qos_req_action was *not* the UPDATE one?
>> >
>> > The meaning of pm_qos_update_target is 'update the PM QoS target
>> > constraints lists'. As described in the changelog the intention of
>> > this patch is to implement the constraints lists management logic in
>> > update_target and simplify the API functions (add/update/remove). It
>> > is also exported for the upcoming (patch 06/15]) to use it as well.
>>
>> The enums are fine by me and they allow us to simplify the code
>> quite a bit.
>>
> Ok, but they look a bit sloppy to me as we now have an API that says
> "add" we can actually pass in an enum that says "remove".
We have an API that says 'update target' that we pass in a parameter
that says 'add request', 'update request' or 'remove request'.
If it is required I could just rename the internal function
update_target, in a later patch.
>
> --mark
>
Jean
^ permalink raw reply
* Re: [PATCH v5 2/3] PM / DEVFREQ: add basic governors
From: MyungJoo Ham @ 2011-08-16 8:52 UTC (permalink / raw)
To: Turquette, Mike
Cc: Len Brown, Greg Kroah-Hartman, Kyungmin Park, linux-pm,
Thomas Gleixner
In-Reply-To: <CAJOA=zMJogYGS9xtcUmU3_a3QmBTy_wz7pf1tk6KUtf4cT1J=A@mail.gmail.com>
On Thu, Aug 11, 2011 at 10:35 AM, Turquette, Mike <mturquette@ti.com> wrote:
[]
>
> Polling is the only practical use for devfreq, assuming a QoS API
> exists for DVFS. As such powersave and performance governors should
> be removed.
Although powersave/performance governors may seem useless, they are
used as basis on measuring the usefulness of DVFS mechanism of
specific devices. If a device is going to use DVFS, we can test the
device with them to find out the potential power save (compare
powersave to performance) and the performance deterioration (compared
to performance). Often, in testing phase, QA teams use performance to
find out any issues with DVFS features (in CPUFREQ). Users may simply
want to use performance governor in some cases (power is not an issue
sometimes).
Using QoS APIs simply to set "minimum" or "maximum" is possible.
However, they are not that straightforward; e.g., how should we set
"DMA latency" to be fixed at the minimum frequency regardless of
others, or how should we set "Network latency" to be fixed at the
maximum frequency? especially without knowing the specifications of
each DVFS-capable device (such as available frequencies, valid latency
values, ...).
>
>> userspace: use the user specified frequency stored at
>> devfreq.user_set_freq. With sysfs support in the following patch, a user
>> may set the value with the sysfs interface.
>>
>> simple_ondemand: simplified version of CPUFREQ's ONDEMAND governor.
>
> I won't repeate everything from patch 1 of this series, but the
> governors should implement the queue/loop logic in the same way that
> CPUfreq does, and the individual devices should have their own
> delayed_work.
First, in case where we want to let each DVFS-capable device have
exact polling frequency (up to jiffy resolution), we only need to set
polling_interval = jiffies_to_msecs(1);.
In case of CPUFREQ, there would be only one polling loop at most for
each core. However, in case of DEVFREQ, there could be multiple
polling loops at a core if CPUFREQ-like looping logic is introduced.
Why don't we reduce that overhead while their function is same, it is
easily doable, and it reduces redundancy?
>
>> When a user updates OPP entries (enable/disable/add), OPP framework
>> automatically notifies DEVFREQ to update operating frequency
>> accordingly. Thus, DEVFREQ users (device drivers) do not need to update
>
> It would be nice if OPP library "notified" devfreq but it does not
> today. OPP library needs notifiers and devfreq can provide handlers
> for them.
That's why devfreq_update() is added in the patch. While DEVFREQ is
the only one requiring notifications from OPP, do you think we may
incur the overhead of notifier at OPP by replacing devfreq_update with
notifier? If we somehow add another module that requires notifications
from OPP for frequency availability changes, we will need to implement
notifier at OPP side, but not just yet, I guess: (discussed before at
https://lists.linux-foundation.org/pipermail/linux-pm/2011-July/032053.html
)
>
[]
>> ---
>> drivers/base/power/devfreq.c | 100 ++++++++++++++++++++++++++++++++++++++++++
>> include/linux/devfreq.h | 8 +++
>> 2 files changed, 108 insertions(+), 0 deletions(-)
>
> Governors should be split out into their own file, especially since
> they need to grow to include polling/queueing logic.
We will need to decide where to settle devfreq core, drivers, and
governors first. Would /drivers/devfreq/ be appropriate?
[]
>> +
>> + /* Set MAX if it's busy enough */
>> + if (stat.busy_time * 100 >
>> + stat.total_time * DFSO_UPTHRESHOLD) {
>
> Thresholds should not be constants, but should be tuneable parameters,
> per-device. This is yet another reason for revising the existing
> relationship between devfreq core code, governors and devices.
>
I agree. I think I should add governor specific "setup" value at
devfreq_add_device(); modifying the interface from
extern int devfreq_add_device(struct device *dev, struct
devfreq_dev_profile *profile, struct devfreq_governor *governor);
==>
extern int devfreq_add_device(struct device *dev, struct
devfreq_dev_profile *profile, struct devfreq_governor *governor, void
*gov_data);
where gov_data is fed to struct devfreq's data field.
>> + *freq = UINT_MAX;
>> + return 0;
>> + }
>> +
>> + /* Set MAX if we do not know the initial frequency */
>> + if (stat.current_frequency == 0) {
>> + *freq = UINT_MAX;
>> + return 0;
>> + }
>> +
>> + /* Keep the current frequency */
>> + if (stat.busy_time * 100 >
>> + stat.total_time * (DFSO_UPTHRESHOLD - DFSO_DOWNDIFFERENCTIAL)) {
>
> Same as above.
Yes.
>
>> + *freq = stat.current_frequency;
>> + return 0;
>> + }
>> +
>> + /* Set the desired frequency based on the load */
>> + a = stat.busy_time;
>> + a *= stat.current_frequency;
>> + b = div_u64(a, stat.total_time);
>> + b *= 100;
>> + b = div_u64(b, (DFSO_UPTHRESHOLD - DFSO_DOWNDIFFERENCTIAL / 2));
>
> Same as above.
Yes.
>
> Regards,
> Mike
>
Cheers!
MyungJoo
--
MyungJoo Ham (함명주), Ph.D.
Mobile Software Platform Lab,
Digital Media and Communications (DMC) Business
Samsung Electronics
cell: 82-10-6714-2858
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm
^ permalink raw reply
* Re: [PATCH 07/15] PM QoS: add a global notification mechanism for the device constraints
From: Jean Pihet @ 2011-08-16 9:58 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: markgross, Mark Brown, Linux PM mailing list, linux-omap,
Jean Pihet
In-Reply-To: <201108142350.42260.rjw@sisk.pl>
Hi Rafael,
2011/8/14 Rafael J. Wysocki <rjw@sisk.pl>:
> Hi,
>
> There is some code duplication in this patch that should better be
> avoided (details below).
>
> On Thursday, August 11, 2011, jean.pihet@newoldbits.com wrote:
>> From: Jean Pihet <j-pihet@ti.com>
>>
>> Add a global notification chain that gets called upon changes to the
>> aggregated constraint value for any device.
>> The notification callbacks are passing the full constraint request data
>> in order for the callees to have access to it. The current use is for the
>> platform low-level code to access the target device of the constraint.
>>
...
>
> The following code:
>
>> + /*
>> + * Update constraints list and call the per-device callbacks if needed
>> + */
>> + ret = pm_qos_update_target(dev->power.constraints,
>> + &req->node, PM_QOS_ADD_REQ, value);
>> +
>> + if (ret) {
>> + /* Call the global callbacks if needed */
>> + curr_value = pm_qos_read_value(req->dev->power.constraints);
>> + blocking_notifier_call_chain(&dev_pm_notifiers,
>> + (unsigned long)curr_value,
>> + req);
>> + }
>
> is used in dev_pm_qos_update_request() and dev_pm_qos_remove_request()
> with the only difference being the command given to pm_qos_update_target().
> This asks for a common function, eg. dev_pm_qos_update_target(), containing
> that code that will be called by all of them (and, apparently, by
> dev_pm_qos_constraints_destroy()).
Ok that makes the code cleaner.
>
> ...
>> @@ -250,9 +329,18 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
>> * Update constraints list and call the per-device
>> * callbacks if needed
>> */
>> - pm_qos_update_target(req->dev->power.constraints,
>> - &req->node, PM_QOS_REMOVE_REQ,
>> - PM_QOS_DEFAULT_VALUE);
>> + ret |= pm_qos_update_target(req->dev->power.constraints,
>> + &req->node,
>> + PM_QOS_REMOVE_REQ,
>> + PM_QOS_DEFAULT_VALUE);
>
> I'm not sure why you're using the binary or operator here. Shouldn't that be
> a simple assignment?
>
>> +
>> + if (ret) {
>> + /* Call the global callbacks if needed */
>> + curr_value = dev->power.constraints->default_value;
>> + blocking_notifier_call_chain(&dev_pm_notifiers,
>> + (unsigned long)curr_value,
>> + req);
>> + }
In the sake of optimization I had a single return value that
aggregates the return values of the calls target_value and calls the
global notifier callbacks _only once_.
As you suggested it is better to use the common update code, which
makes the code cleaner but also removes this optimization.
>>
>> kfree(dev->power.constraints->notifiers);
>> kfree(dev->power.constraints);
>
> Thanks,
> Rafael
>
Regards,
Jean
^ permalink raw reply
* Re: PM / hibernate xfs lock up / xfs_reclaim_inodes_ag
From: Christoph @ 2011-08-16 12:38 UTC (permalink / raw)
To: Pavel Machek; +Cc: Linux PM mailing list, Dave Chinner, xfs
In-Reply-To: <20110810214327.GA9320@atrey.karlin.mff.cuni.cz>
On 10.08.2011 23:43, Pavel Machek wrote:
> Hi!
>
>>>> Why don't you simply submit a patch to do that?
>>>
>>> a) I don't know how to test suspend/hibernate
>>> b) I don't have any hardware I can test it on.
Hi!
I do apologize fmy silence but I've been in a serious productions the last
weeks.
If there any patches to test, let me know. I can run some decent stress tests.
chris
>
> hibernation should work on any hardware. Just add cmdline parameter
> resume=<your swap> and use echo disk > /sys/power/state.
>
> suspend is supported on most PCs, and certainly on any notebook. Just
> echo mem > /sys/power/state.
> Pavel
^ permalink raw reply
* [PATCH v5 00/15] PM QoS: add a per-device latency constraints framework
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
From: Jean Pihet <j-pihet@ti.com>
High level implementation:
1. Preparation of the PM QoS for the addition of a device PM QoS constraints
framework:
. rename and move of the PM QoS implementation files to kernel/power/qos.c
and include/linux/pm_qos.h
. rename of API parameters and internal fields names
. Move around the PM QoS misc devices management code for better readability
. re-organize the internal data structs
. generalize and export the constraints management core code
2. Implementation of the per-device PM QoS constraints:
. create drivers/base/power/qos.c for the implementation
. create a device PM QoS API, which calls the PM QoS constraints management
core code
. the per-device latency constraints data strctures are stored in the device
dev_pm_info struct
. the device PM code calls the init and destroy of the per-device constraints
data struct in order to support the dynamic insertion and removal of the
devices in the system.
. to minimize the data usage by the per-device constraints, the data struct
is only allocated at the first call to dev_pm_qos_add_request. The data
is later free'd when the device is removed from the system
. per-device notification callbacks can be registered and called upon a
change to the aggregated constraint value
. a global mutex protects the constraints users from the data being
allocated and free'd.
3. add a global notification mechanism for the device constraints
. add a global notification chain that gets called upon changes to the
aggregated constraint value for any device.
. the notification callbacks are passing the full constraint request data
in order for the callees to have access to it. The current use is for the
platform low-level code to access the target device of the constraint
4. Implement the OMAP low level constraints management code
. create a PM layer plugin for per-device constraints, compiled under
CONFIG_OMAP_PM_CONSTRAINTS=y
. implement the devices wake-up latency constraints using the global
device PM QoS notification handler which applies the constraints to the
underlying layer
. implement the low level code which controls the power domains next power
states, through the hwmod and pwrdm layers
. add (preliminary) power domains wake-up latency figures for OMAP3&4
. cpuidle is a CPU centric framework so it decides the MPU next power state
based on the MPU exit_latency and target_residency figures. The rest of
the power domains get their next power state programmed from the devices
PM QoS framework, via the devices wake-up latency constraints.
. convert the OMAP I2C driver to the PM QoS API for MPU latency constraints
Questions:
1. the user space API is still under discussions on linux-omap and linux-pm MLs,
cf. [1]. The idea is to add a user-space API for the devices constratins
PM QoS, using a sysfs entry per device
[1] http://marc.info/?l=linux-omap&m=131232344503327&w=2
ToDo:
1. write Documentation for the new PM QoS class, once the code is agreed on
2. validate the constraints framework on OMAP4 HW (done on OMAP3)
3. Need testing on platforms other than OMAP
4. refine the power domains wake-up latency and the cpuidle figures
5. re-visit the OMAP power domains states initialization procedure. Currently
the power states that have been changed from the constraints API which were
applied before the initialization of the power domains are lost
Based on the master branch of the linux-omap git tree (3.1.0-rc1). Compile
tested using OMAP and x86 generic defconfigs.
Tested on OMAP3 Beagleboard (ES2.x).
Need testing on platforms other than OMAP, because of the impact on the
device insertion/removal in device_pm_add/remove
Changelog:
v5:
. Added a global mutex to protect the per-device data allocation/free from
the users
. More robust error checking in the device PM QoS code
. Clean-up of the device PM QoS code: refactor some duplicated code, removal
of unneeded includes
v4:
. Complete devices PM QoS re-design:
. Separation from the existing PM QoS classes by adding a device PM QoS
API. The device code is re-using the PM QoS core code for constraints
lists management and notification mechanism
. Addition of a devices PM QoS global notification mechanism, for interaction
with the low level platform code
v3:
. Complete PM QoS re-design after the comments on MLs
. Patch set split up for improved readability and easier maintenance
v2:
. Rework after comments on the mailing lists
. Added a notification of device insertion/removal from the device PM framework
. Validated on OMAP3 HW
v1:
. Initial implementation
Jean Pihet (14):
PM QoS: move and rename the implementation files
PM QoS: minor clean-ups
PM QoS: code re-organization
PM QoS: re-organize data structs
PM QoS: generalize and export the constraints management code
PM QoS: implement the per-device PM QoS constraints
PM QoS: add a global notification mechanism for the device
constraints
OMAP: convert I2C driver to PM QoS for latency constraints
OMAP: PM: create a PM layer plugin for per-device constraints
OMAP2+: powerdomain: control power domains next state
OMAP3: powerdomain data: add wake-up latency figures
OMAP2+: omap_hwmod: manage the wake-up latency constraints
OMAP: PM CONSTRAINTS: implement the devices wake-up latency
constraints
OMAP2+: cpuidle only influences the MPU state
Vishwanath BS (1):
OMAP4: powerdomain data: add wake-up latency figures
arch/arm/mach-msm/clock.c | 2 +-
arch/arm/mach-omap2/cpuidle34xx.c | 42 +--
arch/arm/mach-omap2/omap_hwmod.c | 26 ++-
arch/arm/mach-omap2/pm.h | 17 +-
arch/arm/mach-omap2/powerdomain.c | 190 ++++++++++
arch/arm/mach-omap2/powerdomain.h | 33 ++-
arch/arm/mach-omap2/powerdomains3xxx_data.c | 77 ++++
arch/arm/mach-omap2/powerdomains44xx_data.c | 84 +++++
arch/arm/plat-omap/Kconfig | 7 +
arch/arm/plat-omap/Makefile | 1 +
arch/arm/plat-omap/i2c.c | 20 -
arch/arm/plat-omap/include/plat/omap-pm.h | 128 -------
arch/arm/plat-omap/include/plat/omap_hwmod.h | 2 +
arch/arm/plat-omap/omap-pm-constraints.c | 350 ++++++++++++++++++
arch/arm/plat-omap/omap-pm-noop.c | 89 -----
drivers/acpi/processor_idle.c | 2 +-
drivers/base/power/Makefile | 4 +-
drivers/base/power/main.c | 3 +
drivers/base/power/qos.c | 339 ++++++++++++++++++
drivers/cpuidle/cpuidle.c | 2 +-
drivers/cpuidle/governors/ladder.c | 2 +-
drivers/cpuidle/governors/menu.c | 2 +-
drivers/i2c/busses/i2c-omap.c | 31 +-
drivers/media/video/via-camera.c | 4 +-
drivers/net/e1000e/netdev.c | 2 +-
drivers/net/wireless/ipw2x00/ipw2100.c | 4 +-
include/linux/netdevice.h | 4 +-
include/linux/pm.h | 9 +
include/linux/pm_qos.h | 147 ++++++++
include/linux/pm_qos_params.h | 38 --
include/sound/pcm.h | 4 +-
kernel/Makefile | 2 +-
kernel/pm_qos_params.c | 481 -------------------------
kernel/power/Makefile | 2 +-
kernel/power/qos.c | 490 ++++++++++++++++++++++++++
net/mac80211/main.c | 2 +-
net/mac80211/mlme.c | 2 +-
net/mac80211/scan.c | 2 +-
sound/core/pcm_native.c | 2 +-
39 files changed, 1823 insertions(+), 825 deletions(-)
create mode 100644 arch/arm/plat-omap/omap-pm-constraints.c
create mode 100644 drivers/base/power/qos.c
create mode 100644 include/linux/pm_qos.h
delete mode 100644 include/linux/pm_qos_params.h
delete mode 100644 kernel/pm_qos_params.c
create mode 100644 kernel/power/qos.c
--
1.7.4.1
^ permalink raw reply
* [PATCH 01/15] PM QoS: move and rename the implementation files
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313502198-9298-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
The PM QoS implementation files are better named
kernel/power/qos.c and include/linux/pm_qos.h.
The PM QoS support is compiled under the CONFIG_PM option.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
arch/arm/mach-msm/clock.c | 2 +-
drivers/acpi/processor_idle.c | 2 +-
drivers/cpuidle/cpuidle.c | 2 +-
drivers/cpuidle/governors/ladder.c | 2 +-
| 2 +-
drivers/media/video/via-camera.c | 2 +-
drivers/net/e1000e/netdev.c | 2 +-
drivers/net/wireless/ipw2x00/ipw2100.c | 2 +-
include/linux/netdevice.h | 2 +-
include/linux/pm_qos.h | 61 ++++
include/linux/pm_qos_params.h | 38 ---
include/sound/pcm.h | 2 +-
kernel/Makefile | 2 +-
kernel/pm_qos_params.c | 481 --------------------------------
kernel/power/Makefile | 2 +-
kernel/power/qos.c | 481 ++++++++++++++++++++++++++++++++
net/mac80211/main.c | 2 +-
net/mac80211/mlme.c | 2 +-
net/mac80211/scan.c | 2 +-
sound/core/pcm_native.c | 2 +-
20 files changed, 558 insertions(+), 535 deletions(-)
create mode 100644 include/linux/pm_qos.h
delete mode 100644 include/linux/pm_qos_params.h
delete mode 100644 kernel/pm_qos_params.c
create mode 100644 kernel/power/qos.c
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 22a5376..d9145df 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -18,7 +18,7 @@
#include <linux/list.h>
#include <linux/err.h>
#include <linux/spinlock.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/mutex.h>
#include <linux/clk.h>
#include <linux/string.h>
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 431ab11..2e69e09 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -37,7 +37,7 @@
#include <linux/dmi.h>
#include <linux/moduleparam.h>
#include <linux/sched.h> /* need_resched() */
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/clockchips.h>
#include <linux/cpuidle.h>
#include <linux/irqflags.h>
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index d4c5423..0df0141 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -12,7 +12,7 @@
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/notifier.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/cpu.h>
#include <linux/cpuidle.h>
#include <linux/ktime.h>
diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c
index 12c9890..f62fde2 100644
--- a/drivers/cpuidle/governors/ladder.c
+++ b/drivers/cpuidle/governors/ladder.c
@@ -14,7 +14,7 @@
#include <linux/kernel.h>
#include <linux/cpuidle.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/moduleparam.h>
#include <linux/jiffies.h>
--git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index c47f3d0..3600f19 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -12,7 +12,7 @@
#include <linux/kernel.h>
#include <linux/cpuidle.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/time.h>
#include <linux/ktime.h>
#include <linux/hrtimer.h>
diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c
index 85d3048..b3ca389 100644
--- a/drivers/media/video/via-camera.c
+++ b/drivers/media/video/via-camera.c
@@ -21,7 +21,7 @@
#include <media/videobuf-dma-sg.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/via-core.h>
#include <linux/via-gpio.h>
#include <linux/via_i2c.h>
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c
index ab4be80..40deb44 100644
--- a/drivers/net/e1000e/netdev.c
+++ b/drivers/net/e1000e/netdev.c
@@ -47,7 +47,7 @@
#include <linux/if_vlan.h>
#include <linux/cpu.h>
#include <linux/smp.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
#include <linux/aer.h>
#include <linux/prefetch.h>
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index 3774dd0..aaab76c 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -161,7 +161,7 @@ that only one external action is invoked at a time.
#include <linux/firmware.h>
#include <linux/acpi.h>
#include <linux/ctype.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <net/lib80211.h>
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index ddee79b..f72ac6b 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -31,7 +31,7 @@
#include <linux/if_link.h>
#ifdef __KERNEL__
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/atomic.h>
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
new file mode 100644
index 0000000..7ba67541
--- /dev/null
+++ b/include/linux/pm_qos.h
@@ -0,0 +1,61 @@
+#ifndef _LINUX_PM_QOS_H
+#define _LINUX_PM_QOS_H
+/* interface for the pm_qos_power infrastructure of the linux kernel.
+ *
+ * Mark Gross <mgross@linux.intel.com>
+ */
+#include <linux/plist.h>
+#include <linux/notifier.h>
+#include <linux/miscdevice.h>
+
+#define PM_QOS_RESERVED 0
+#define PM_QOS_CPU_DMA_LATENCY 1
+#define PM_QOS_NETWORK_LATENCY 2
+#define PM_QOS_NETWORK_THROUGHPUT 3
+
+#define PM_QOS_NUM_CLASSES 4
+#define PM_QOS_DEFAULT_VALUE -1
+
+#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
+#define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
+#define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0
+
+struct pm_qos_request_list {
+ struct plist_node list;
+ int pm_qos_class;
+};
+
+#ifdef CONFIG_PM
+void pm_qos_add_request(struct pm_qos_request_list *l,
+ int pm_qos_class, s32 value);
+void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
+ s32 new_value);
+void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req);
+
+int pm_qos_request(int pm_qos_class);
+int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
+int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
+int pm_qos_request_active(struct pm_qos_request_list *req);
+#else
+static inline void pm_qos_add_request(struct pm_qos_request_list *l,
+ int pm_qos_class, s32 value)
+ { return; }
+static inline void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
+ s32 new_value)
+ { return; }
+static inline void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req)
+ { return; }
+
+static inline int pm_qos_request(int pm_qos_class)
+ { return 0; }
+static inline int pm_qos_add_notifier(int pm_qos_class,
+ struct notifier_block *notifier)
+ { return 0; }
+static inline int pm_qos_remove_notifier(int pm_qos_class,
+ struct notifier_block *notifier)
+ { return 0; }
+static inline int pm_qos_request_active(struct pm_qos_request_list *req)
+ { return 0; }
+#endif
+
+#endif
diff --git a/include/linux/pm_qos_params.h b/include/linux/pm_qos_params.h
deleted file mode 100644
index a7d87f9..0000000
--- a/include/linux/pm_qos_params.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef _LINUX_PM_QOS_PARAMS_H
-#define _LINUX_PM_QOS_PARAMS_H
-/* interface for the pm_qos_power infrastructure of the linux kernel.
- *
- * Mark Gross <mgross@linux.intel.com>
- */
-#include <linux/plist.h>
-#include <linux/notifier.h>
-#include <linux/miscdevice.h>
-
-#define PM_QOS_RESERVED 0
-#define PM_QOS_CPU_DMA_LATENCY 1
-#define PM_QOS_NETWORK_LATENCY 2
-#define PM_QOS_NETWORK_THROUGHPUT 3
-
-#define PM_QOS_NUM_CLASSES 4
-#define PM_QOS_DEFAULT_VALUE -1
-
-#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
-#define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
-#define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0
-
-struct pm_qos_request_list {
- struct plist_node list;
- int pm_qos_class;
-};
-
-void pm_qos_add_request(struct pm_qos_request_list *l, int pm_qos_class, s32 value);
-void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
- s32 new_value);
-void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req);
-
-int pm_qos_request(int pm_qos_class);
-int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
-int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
-int pm_qos_request_active(struct pm_qos_request_list *req);
-
-#endif
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 57e71fa..666ee91 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -29,7 +29,7 @@
#include <linux/poll.h>
#include <linux/mm.h>
#include <linux/bitops.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#define snd_pcm_substream_chip(substream) ((substream)->private_data)
#define snd_pcm_chip(pcm) ((pcm)->private_data)
diff --git a/kernel/Makefile b/kernel/Makefile
index d06467f..e2f8f00 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -9,7 +9,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o \
rcupdate.o extable.o params.o posix-timers.o \
kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
- notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o \
+ notifier.o ksysfs.o sched_clock.o cred.o \
async.o range.o jump_label.o
obj-y += groups.o
diff --git a/kernel/pm_qos_params.c b/kernel/pm_qos_params.c
deleted file mode 100644
index 37f05d0..0000000
--- a/kernel/pm_qos_params.c
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * This module exposes the interface to kernel space for specifying
- * QoS dependencies. It provides infrastructure for registration of:
- *
- * Dependents on a QoS value : register requests
- * Watchers of QoS value : get notified when target QoS value changes
- *
- * This QoS design is best effort based. Dependents register their QoS needs.
- * Watchers register to keep track of the current QoS needs of the system.
- *
- * There are 3 basic classes of QoS parameter: latency, timeout, throughput
- * each have defined units:
- * latency: usec
- * timeout: usec <-- currently not used.
- * throughput: kbs (kilo byte / sec)
- *
- * There are lists of pm_qos_objects each one wrapping requests, notifiers
- *
- * User mode requests on a QOS parameter register themselves to the
- * subsystem by opening the device node /dev/... and writing there request to
- * the node. As long as the process holds a file handle open to the node the
- * client continues to be accounted for. Upon file release the usermode
- * request is removed and a new qos target is computed. This way when the
- * request that the application has is cleaned up when closes the file
- * pointer or exits the pm_qos_object will get an opportunity to clean up.
- *
- * Mark Gross <mgross@linux.intel.com>
- */
-
-/*#define DEBUG*/
-
-#include <linux/pm_qos_params.h>
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/time.h>
-#include <linux/fs.h>
-#include <linux/device.h>
-#include <linux/miscdevice.h>
-#include <linux/string.h>
-#include <linux/platform_device.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-
-#include <linux/uaccess.h>
-
-/*
- * locking rule: all changes to requests or notifiers lists
- * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
- * held, taken with _irqsave. One lock to rule them all
- */
-enum pm_qos_type {
- PM_QOS_MAX, /* return the largest value */
- PM_QOS_MIN /* return the smallest value */
-};
-
-/*
- * Note: The lockless read path depends on the CPU accessing
- * target_value atomically. Atomic access is only guaranteed on all CPU
- * types linux supports for 32 bit quantites
- */
-struct pm_qos_object {
- struct plist_head requests;
- struct blocking_notifier_head *notifiers;
- struct miscdevice pm_qos_power_miscdev;
- char *name;
- s32 target_value; /* Do not change to 64 bit */
- s32 default_value;
- enum pm_qos_type type;
-};
-
-static DEFINE_SPINLOCK(pm_qos_lock);
-
-static struct pm_qos_object null_pm_qos;
-static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
-static struct pm_qos_object cpu_dma_pm_qos = {
- .requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests),
- .notifiers = &cpu_dma_lat_notifier,
- .name = "cpu_dma_latency",
- .target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
- .default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
- .type = PM_QOS_MIN,
-};
-
-static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
-static struct pm_qos_object network_lat_pm_qos = {
- .requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests),
- .notifiers = &network_lat_notifier,
- .name = "network_latency",
- .target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
- .default_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
- .type = PM_QOS_MIN
-};
-
-
-static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
-static struct pm_qos_object network_throughput_pm_qos = {
- .requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests),
- .notifiers = &network_throughput_notifier,
- .name = "network_throughput",
- .target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
- .default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
- .type = PM_QOS_MAX,
-};
-
-
-static struct pm_qos_object *pm_qos_array[] = {
- &null_pm_qos,
- &cpu_dma_pm_qos,
- &network_lat_pm_qos,
- &network_throughput_pm_qos
-};
-
-static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *f_pos);
-static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
- size_t count, loff_t *f_pos);
-static int pm_qos_power_open(struct inode *inode, struct file *filp);
-static int pm_qos_power_release(struct inode *inode, struct file *filp);
-
-static const struct file_operations pm_qos_power_fops = {
- .write = pm_qos_power_write,
- .read = pm_qos_power_read,
- .open = pm_qos_power_open,
- .release = pm_qos_power_release,
- .llseek = noop_llseek,
-};
-
-/* unlocked internal variant */
-static inline int pm_qos_get_value(struct pm_qos_object *o)
-{
- if (plist_head_empty(&o->requests))
- return o->default_value;
-
- switch (o->type) {
- case PM_QOS_MIN:
- return plist_first(&o->requests)->prio;
-
- case PM_QOS_MAX:
- return plist_last(&o->requests)->prio;
-
- default:
- /* runtime check for not using enum */
- BUG();
- }
-}
-
-static inline s32 pm_qos_read_value(struct pm_qos_object *o)
-{
- return o->target_value;
-}
-
-static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value)
-{
- o->target_value = value;
-}
-
-static void update_target(struct pm_qos_object *o, struct plist_node *node,
- int del, int value)
-{
- unsigned long flags;
- int prev_value, curr_value;
-
- spin_lock_irqsave(&pm_qos_lock, flags);
- prev_value = pm_qos_get_value(o);
- /* PM_QOS_DEFAULT_VALUE is a signal that the value is unchanged */
- if (value != PM_QOS_DEFAULT_VALUE) {
- /*
- * to change the list, we atomically remove, reinit
- * with new value and add, then see if the extremal
- * changed
- */
- plist_del(node, &o->requests);
- plist_node_init(node, value);
- plist_add(node, &o->requests);
- } else if (del) {
- plist_del(node, &o->requests);
- } else {
- plist_add(node, &o->requests);
- }
- curr_value = pm_qos_get_value(o);
- pm_qos_set_value(o, curr_value);
- spin_unlock_irqrestore(&pm_qos_lock, flags);
-
- if (prev_value != curr_value)
- blocking_notifier_call_chain(o->notifiers,
- (unsigned long)curr_value,
- NULL);
-}
-
-static int register_pm_qos_misc(struct pm_qos_object *qos)
-{
- qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
- qos->pm_qos_power_miscdev.name = qos->name;
- qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
-
- return misc_register(&qos->pm_qos_power_miscdev);
-}
-
-static int find_pm_qos_object_by_minor(int minor)
-{
- int pm_qos_class;
-
- for (pm_qos_class = 0;
- pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
- if (minor ==
- pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
- return pm_qos_class;
- }
- return -1;
-}
-
-/**
- * pm_qos_request - returns current system wide qos expectation
- * @pm_qos_class: identification of which qos value is requested
- *
- * This function returns the current target value.
- */
-int pm_qos_request(int pm_qos_class)
-{
- return pm_qos_read_value(pm_qos_array[pm_qos_class]);
-}
-EXPORT_SYMBOL_GPL(pm_qos_request);
-
-int pm_qos_request_active(struct pm_qos_request_list *req)
-{
- return req->pm_qos_class != 0;
-}
-EXPORT_SYMBOL_GPL(pm_qos_request_active);
-
-/**
- * pm_qos_add_request - inserts new qos request into the list
- * @dep: pointer to a preallocated handle
- * @pm_qos_class: identifies which list of qos request to use
- * @value: defines the qos request
- *
- * This function inserts a new entry in the pm_qos_class list of requested qos
- * performance characteristics. It recomputes the aggregate QoS expectations
- * for the pm_qos_class of parameters and initializes the pm_qos_request_list
- * handle. Caller needs to save this handle for later use in updates and
- * removal.
- */
-
-void pm_qos_add_request(struct pm_qos_request_list *dep,
- int pm_qos_class, s32 value)
-{
- struct pm_qos_object *o = pm_qos_array[pm_qos_class];
- int new_value;
-
- if (pm_qos_request_active(dep)) {
- WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n");
- return;
- }
- if (value == PM_QOS_DEFAULT_VALUE)
- new_value = o->default_value;
- else
- new_value = value;
- plist_node_init(&dep->list, new_value);
- dep->pm_qos_class = pm_qos_class;
- update_target(o, &dep->list, 0, PM_QOS_DEFAULT_VALUE);
-}
-EXPORT_SYMBOL_GPL(pm_qos_add_request);
-
-/**
- * pm_qos_update_request - modifies an existing qos request
- * @pm_qos_req : handle to list element holding a pm_qos request to use
- * @value: defines the qos request
- *
- * Updates an existing qos request for the pm_qos_class of parameters along
- * with updating the target pm_qos_class value.
- *
- * Attempts are made to make this code callable on hot code paths.
- */
-void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
- s32 new_value)
-{
- s32 temp;
- struct pm_qos_object *o;
-
- if (!pm_qos_req) /*guard against callers passing in null */
- return;
-
- if (!pm_qos_request_active(pm_qos_req)) {
- WARN(1, KERN_ERR "pm_qos_update_request() called for unknown object\n");
- return;
- }
-
- o = pm_qos_array[pm_qos_req->pm_qos_class];
-
- if (new_value == PM_QOS_DEFAULT_VALUE)
- temp = o->default_value;
- else
- temp = new_value;
-
- if (temp != pm_qos_req->list.prio)
- update_target(o, &pm_qos_req->list, 0, temp);
-}
-EXPORT_SYMBOL_GPL(pm_qos_update_request);
-
-/**
- * pm_qos_remove_request - modifies an existing qos request
- * @pm_qos_req: handle to request list element
- *
- * Will remove pm qos request from the list of requests and
- * recompute the current target value for the pm_qos_class. Call this
- * on slow code paths.
- */
-void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req)
-{
- struct pm_qos_object *o;
-
- if (pm_qos_req == NULL)
- return;
- /* silent return to keep pcm code cleaner */
-
- if (!pm_qos_request_active(pm_qos_req)) {
- WARN(1, KERN_ERR "pm_qos_remove_request() called for unknown object\n");
- return;
- }
-
- o = pm_qos_array[pm_qos_req->pm_qos_class];
- update_target(o, &pm_qos_req->list, 1, PM_QOS_DEFAULT_VALUE);
- memset(pm_qos_req, 0, sizeof(*pm_qos_req));
-}
-EXPORT_SYMBOL_GPL(pm_qos_remove_request);
-
-/**
- * pm_qos_add_notifier - sets notification entry for changes to target value
- * @pm_qos_class: identifies which qos target changes should be notified.
- * @notifier: notifier block managed by caller.
- *
- * will register the notifier into a notification chain that gets called
- * upon changes to the pm_qos_class target value.
- */
-int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
-{
- int retval;
-
- retval = blocking_notifier_chain_register(
- pm_qos_array[pm_qos_class]->notifiers, notifier);
-
- return retval;
-}
-EXPORT_SYMBOL_GPL(pm_qos_add_notifier);
-
-/**
- * pm_qos_remove_notifier - deletes notification entry from chain.
- * @pm_qos_class: identifies which qos target changes are notified.
- * @notifier: notifier block to be removed.
- *
- * will remove the notifier from the notification chain that gets called
- * upon changes to the pm_qos_class target value.
- */
-int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
-{
- int retval;
-
- retval = blocking_notifier_chain_unregister(
- pm_qos_array[pm_qos_class]->notifiers, notifier);
-
- return retval;
-}
-EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
-
-static int pm_qos_power_open(struct inode *inode, struct file *filp)
-{
- long pm_qos_class;
-
- pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
- if (pm_qos_class >= 0) {
- struct pm_qos_request_list *req = kzalloc(sizeof(*req), GFP_KERNEL);
- if (!req)
- return -ENOMEM;
-
- pm_qos_add_request(req, pm_qos_class, PM_QOS_DEFAULT_VALUE);
- filp->private_data = req;
-
- if (filp->private_data)
- return 0;
- }
- return -EPERM;
-}
-
-static int pm_qos_power_release(struct inode *inode, struct file *filp)
-{
- struct pm_qos_request_list *req;
-
- req = filp->private_data;
- pm_qos_remove_request(req);
- kfree(req);
-
- return 0;
-}
-
-
-static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
- size_t count, loff_t *f_pos)
-{
- s32 value;
- unsigned long flags;
- struct pm_qos_object *o;
- struct pm_qos_request_list *pm_qos_req = filp->private_data;
-
- if (!pm_qos_req)
- return -EINVAL;
- if (!pm_qos_request_active(pm_qos_req))
- return -EINVAL;
-
- o = pm_qos_array[pm_qos_req->pm_qos_class];
- spin_lock_irqsave(&pm_qos_lock, flags);
- value = pm_qos_get_value(o);
- spin_unlock_irqrestore(&pm_qos_lock, flags);
-
- return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32));
-}
-
-static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *f_pos)
-{
- s32 value;
- struct pm_qos_request_list *pm_qos_req;
-
- if (count == sizeof(s32)) {
- if (copy_from_user(&value, buf, sizeof(s32)))
- return -EFAULT;
- } else if (count <= 11) { /* ASCII perhaps? */
- char ascii_value[11];
- unsigned long int ulval;
- int ret;
-
- if (copy_from_user(ascii_value, buf, count))
- return -EFAULT;
-
- if (count > 10) {
- if (ascii_value[10] == '\n')
- ascii_value[10] = '\0';
- else
- return -EINVAL;
- } else {
- ascii_value[count] = '\0';
- }
- ret = strict_strtoul(ascii_value, 16, &ulval);
- if (ret) {
- pr_debug("%s, 0x%lx, 0x%x\n", ascii_value, ulval, ret);
- return -EINVAL;
- }
- value = (s32)lower_32_bits(ulval);
- } else {
- return -EINVAL;
- }
-
- pm_qos_req = filp->private_data;
- pm_qos_update_request(pm_qos_req, value);
-
- return count;
-}
-
-
-static int __init pm_qos_power_init(void)
-{
- int ret = 0;
-
- ret = register_pm_qos_misc(&cpu_dma_pm_qos);
- if (ret < 0) {
- printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n");
- return ret;
- }
- ret = register_pm_qos_misc(&network_lat_pm_qos);
- if (ret < 0) {
- printk(KERN_ERR "pm_qos_param: network_latency setup failed\n");
- return ret;
- }
- ret = register_pm_qos_misc(&network_throughput_pm_qos);
- if (ret < 0)
- printk(KERN_ERR
- "pm_qos_param: network_throughput setup failed\n");
-
- return ret;
-}
-
-late_initcall(pm_qos_power_init);
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index c5ebc6a..ad6bdd8 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -1,7 +1,7 @@
ccflags-$(CONFIG_PM_DEBUG) := -DDEBUG
-obj-$(CONFIG_PM) += main.o
+obj-$(CONFIG_PM) += main.o qos.o
obj-$(CONFIG_PM_SLEEP) += console.o
obj-$(CONFIG_FREEZER) += process.o
obj-$(CONFIG_SUSPEND) += suspend.o
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
new file mode 100644
index 0000000..61b4738
--- /dev/null
+++ b/kernel/power/qos.c
@@ -0,0 +1,481 @@
+/*
+ * This module exposes the interface to kernel space for specifying
+ * QoS dependencies. It provides infrastructure for registration of:
+ *
+ * Dependents on a QoS value : register requests
+ * Watchers of QoS value : get notified when target QoS value changes
+ *
+ * This QoS design is best effort based. Dependents register their QoS needs.
+ * Watchers register to keep track of the current QoS needs of the system.
+ *
+ * There are 3 basic classes of QoS parameter: latency, timeout, throughput
+ * each have defined units:
+ * latency: usec
+ * timeout: usec <-- currently not used.
+ * throughput: kbs (kilo byte / sec)
+ *
+ * There are lists of pm_qos_objects each one wrapping requests, notifiers
+ *
+ * User mode requests on a QOS parameter register themselves to the
+ * subsystem by opening the device node /dev/... and writing there request to
+ * the node. As long as the process holds a file handle open to the node the
+ * client continues to be accounted for. Upon file release the usermode
+ * request is removed and a new qos target is computed. This way when the
+ * request that the application has is cleaned up when closes the file
+ * pointer or exits the pm_qos_object will get an opportunity to clean up.
+ *
+ * Mark Gross <mgross@linux.intel.com>
+ */
+
+/*#define DEBUG*/
+
+#include <linux/pm_qos.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include <linux/uaccess.h>
+
+/*
+ * locking rule: all changes to requests or notifiers lists
+ * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
+ * held, taken with _irqsave. One lock to rule them all
+ */
+enum pm_qos_type {
+ PM_QOS_MAX, /* return the largest value */
+ PM_QOS_MIN /* return the smallest value */
+};
+
+/*
+ * Note: The lockless read path depends on the CPU accessing
+ * target_value atomically. Atomic access is only guaranteed on all CPU
+ * types linux supports for 32 bit quantites
+ */
+struct pm_qos_object {
+ struct plist_head requests;
+ struct blocking_notifier_head *notifiers;
+ struct miscdevice pm_qos_power_miscdev;
+ char *name;
+ s32 target_value; /* Do not change to 64 bit */
+ s32 default_value;
+ enum pm_qos_type type;
+};
+
+static DEFINE_SPINLOCK(pm_qos_lock);
+
+static struct pm_qos_object null_pm_qos;
+static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
+static struct pm_qos_object cpu_dma_pm_qos = {
+ .requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests),
+ .notifiers = &cpu_dma_lat_notifier,
+ .name = "cpu_dma_latency",
+ .target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
+ .default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
+ .type = PM_QOS_MIN,
+};
+
+static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
+static struct pm_qos_object network_lat_pm_qos = {
+ .requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests),
+ .notifiers = &network_lat_notifier,
+ .name = "network_latency",
+ .target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
+ .default_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
+ .type = PM_QOS_MIN
+};
+
+
+static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
+static struct pm_qos_object network_throughput_pm_qos = {
+ .requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests),
+ .notifiers = &network_throughput_notifier,
+ .name = "network_throughput",
+ .target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
+ .default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
+ .type = PM_QOS_MAX,
+};
+
+
+static struct pm_qos_object *pm_qos_array[] = {
+ &null_pm_qos,
+ &cpu_dma_pm_qos,
+ &network_lat_pm_qos,
+ &network_throughput_pm_qos
+};
+
+static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos);
+static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos);
+static int pm_qos_power_open(struct inode *inode, struct file *filp);
+static int pm_qos_power_release(struct inode *inode, struct file *filp);
+
+static const struct file_operations pm_qos_power_fops = {
+ .write = pm_qos_power_write,
+ .read = pm_qos_power_read,
+ .open = pm_qos_power_open,
+ .release = pm_qos_power_release,
+ .llseek = noop_llseek,
+};
+
+/* unlocked internal variant */
+static inline int pm_qos_get_value(struct pm_qos_object *o)
+{
+ if (plist_head_empty(&o->requests))
+ return o->default_value;
+
+ switch (o->type) {
+ case PM_QOS_MIN:
+ return plist_first(&o->requests)->prio;
+
+ case PM_QOS_MAX:
+ return plist_last(&o->requests)->prio;
+
+ default:
+ /* runtime check for not using enum */
+ BUG();
+ }
+}
+
+static inline s32 pm_qos_read_value(struct pm_qos_object *o)
+{
+ return o->target_value;
+}
+
+static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value)
+{
+ o->target_value = value;
+}
+
+static void update_target(struct pm_qos_object *o, struct plist_node *node,
+ int del, int value)
+{
+ unsigned long flags;
+ int prev_value, curr_value;
+
+ spin_lock_irqsave(&pm_qos_lock, flags);
+ prev_value = pm_qos_get_value(o);
+ /* PM_QOS_DEFAULT_VALUE is a signal that the value is unchanged */
+ if (value != PM_QOS_DEFAULT_VALUE) {
+ /*
+ * to change the list, we atomically remove, reinit
+ * with new value and add, then see if the extremal
+ * changed
+ */
+ plist_del(node, &o->requests);
+ plist_node_init(node, value);
+ plist_add(node, &o->requests);
+ } else if (del) {
+ plist_del(node, &o->requests);
+ } else {
+ plist_add(node, &o->requests);
+ }
+ curr_value = pm_qos_get_value(o);
+ pm_qos_set_value(o, curr_value);
+ spin_unlock_irqrestore(&pm_qos_lock, flags);
+
+ if (prev_value != curr_value)
+ blocking_notifier_call_chain(o->notifiers,
+ (unsigned long)curr_value,
+ NULL);
+}
+
+static int register_pm_qos_misc(struct pm_qos_object *qos)
+{
+ qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
+ qos->pm_qos_power_miscdev.name = qos->name;
+ qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
+
+ return misc_register(&qos->pm_qos_power_miscdev);
+}
+
+static int find_pm_qos_object_by_minor(int minor)
+{
+ int pm_qos_class;
+
+ for (pm_qos_class = 0;
+ pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
+ if (minor ==
+ pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
+ return pm_qos_class;
+ }
+ return -1;
+}
+
+/**
+ * pm_qos_request - returns current system wide qos expectation
+ * @pm_qos_class: identification of which qos value is requested
+ *
+ * This function returns the current target value.
+ */
+int pm_qos_request(int pm_qos_class)
+{
+ return pm_qos_read_value(pm_qos_array[pm_qos_class]);
+}
+EXPORT_SYMBOL_GPL(pm_qos_request);
+
+int pm_qos_request_active(struct pm_qos_request_list *req)
+{
+ return req->pm_qos_class != 0;
+}
+EXPORT_SYMBOL_GPL(pm_qos_request_active);
+
+/**
+ * pm_qos_add_request - inserts new qos request into the list
+ * @dep: pointer to a preallocated handle
+ * @pm_qos_class: identifies which list of qos request to use
+ * @value: defines the qos request
+ *
+ * This function inserts a new entry in the pm_qos_class list of requested qos
+ * performance characteristics. It recomputes the aggregate QoS expectations
+ * for the pm_qos_class of parameters and initializes the pm_qos_request_list
+ * handle. Caller needs to save this handle for later use in updates and
+ * removal.
+ */
+
+void pm_qos_add_request(struct pm_qos_request_list *dep,
+ int pm_qos_class, s32 value)
+{
+ struct pm_qos_object *o = pm_qos_array[pm_qos_class];
+ int new_value;
+
+ if (pm_qos_request_active(dep)) {
+ WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n");
+ return;
+ }
+ if (value == PM_QOS_DEFAULT_VALUE)
+ new_value = o->default_value;
+ else
+ new_value = value;
+ plist_node_init(&dep->list, new_value);
+ dep->pm_qos_class = pm_qos_class;
+ update_target(o, &dep->list, 0, PM_QOS_DEFAULT_VALUE);
+}
+EXPORT_SYMBOL_GPL(pm_qos_add_request);
+
+/**
+ * pm_qos_update_request - modifies an existing qos request
+ * @pm_qos_req : handle to list element holding a pm_qos request to use
+ * @value: defines the qos request
+ *
+ * Updates an existing qos request for the pm_qos_class of parameters along
+ * with updating the target pm_qos_class value.
+ *
+ * Attempts are made to make this code callable on hot code paths.
+ */
+void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
+ s32 new_value)
+{
+ s32 temp;
+ struct pm_qos_object *o;
+
+ if (!pm_qos_req) /*guard against callers passing in null */
+ return;
+
+ if (!pm_qos_request_active(pm_qos_req)) {
+ WARN(1, KERN_ERR "pm_qos_update_request() called for unknown object\n");
+ return;
+ }
+
+ o = pm_qos_array[pm_qos_req->pm_qos_class];
+
+ if (new_value == PM_QOS_DEFAULT_VALUE)
+ temp = o->default_value;
+ else
+ temp = new_value;
+
+ if (temp != pm_qos_req->list.prio)
+ update_target(o, &pm_qos_req->list, 0, temp);
+}
+EXPORT_SYMBOL_GPL(pm_qos_update_request);
+
+/**
+ * pm_qos_remove_request - modifies an existing qos request
+ * @pm_qos_req: handle to request list element
+ *
+ * Will remove pm qos request from the list of requests and
+ * recompute the current target value for the pm_qos_class. Call this
+ * on slow code paths.
+ */
+void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req)
+{
+ struct pm_qos_object *o;
+
+ if (pm_qos_req == NULL)
+ return;
+ /* silent return to keep pcm code cleaner */
+
+ if (!pm_qos_request_active(pm_qos_req)) {
+ WARN(1, KERN_ERR "pm_qos_remove_request() called for unknown object\n");
+ return;
+ }
+
+ o = pm_qos_array[pm_qos_req->pm_qos_class];
+ update_target(o, &pm_qos_req->list, 1, PM_QOS_DEFAULT_VALUE);
+ memset(pm_qos_req, 0, sizeof(*pm_qos_req));
+}
+EXPORT_SYMBOL_GPL(pm_qos_remove_request);
+
+/**
+ * pm_qos_add_notifier - sets notification entry for changes to target value
+ * @pm_qos_class: identifies which qos target changes should be notified.
+ * @notifier: notifier block managed by caller.
+ *
+ * will register the notifier into a notification chain that gets called
+ * upon changes to the pm_qos_class target value.
+ */
+int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
+{
+ int retval;
+
+ retval = blocking_notifier_chain_register(
+ pm_qos_array[pm_qos_class]->notifiers, notifier);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(pm_qos_add_notifier);
+
+/**
+ * pm_qos_remove_notifier - deletes notification entry from chain.
+ * @pm_qos_class: identifies which qos target changes are notified.
+ * @notifier: notifier block to be removed.
+ *
+ * will remove the notifier from the notification chain that gets called
+ * upon changes to the pm_qos_class target value.
+ */
+int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
+{
+ int retval;
+
+ retval = blocking_notifier_chain_unregister(
+ pm_qos_array[pm_qos_class]->notifiers, notifier);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
+
+static int pm_qos_power_open(struct inode *inode, struct file *filp)
+{
+ long pm_qos_class;
+
+ pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
+ if (pm_qos_class >= 0) {
+ struct pm_qos_request_list *req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ pm_qos_add_request(req, pm_qos_class, PM_QOS_DEFAULT_VALUE);
+ filp->private_data = req;
+
+ if (filp->private_data)
+ return 0;
+ }
+ return -EPERM;
+}
+
+static int pm_qos_power_release(struct inode *inode, struct file *filp)
+{
+ struct pm_qos_request_list *req;
+
+ req = filp->private_data;
+ pm_qos_remove_request(req);
+ kfree(req);
+
+ return 0;
+}
+
+
+static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ s32 value;
+ unsigned long flags;
+ struct pm_qos_object *o;
+ struct pm_qos_request_list *pm_qos_req = filp->private_data;
+
+ if (!pm_qos_req)
+ return -EINVAL;
+ if (!pm_qos_request_active(pm_qos_req))
+ return -EINVAL;
+
+ o = pm_qos_array[pm_qos_req->pm_qos_class];
+ spin_lock_irqsave(&pm_qos_lock, flags);
+ value = pm_qos_get_value(o);
+ spin_unlock_irqrestore(&pm_qos_lock, flags);
+
+ return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32));
+}
+
+static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ s32 value;
+ struct pm_qos_request_list *pm_qos_req;
+
+ if (count == sizeof(s32)) {
+ if (copy_from_user(&value, buf, sizeof(s32)))
+ return -EFAULT;
+ } else if (count <= 11) { /* ASCII perhaps? */
+ char ascii_value[11];
+ unsigned long int ulval;
+ int ret;
+
+ if (copy_from_user(ascii_value, buf, count))
+ return -EFAULT;
+
+ if (count > 10) {
+ if (ascii_value[10] == '\n')
+ ascii_value[10] = '\0';
+ else
+ return -EINVAL;
+ } else {
+ ascii_value[count] = '\0';
+ }
+ ret = strict_strtoul(ascii_value, 16, &ulval);
+ if (ret) {
+ pr_debug("%s, 0x%lx, 0x%x\n", ascii_value, ulval, ret);
+ return -EINVAL;
+ }
+ value = (s32)lower_32_bits(ulval);
+ } else {
+ return -EINVAL;
+ }
+
+ pm_qos_req = filp->private_data;
+ pm_qos_update_request(pm_qos_req, value);
+
+ return count;
+}
+
+
+static int __init pm_qos_power_init(void)
+{
+ int ret = 0;
+
+ ret = register_pm_qos_misc(&cpu_dma_pm_qos);
+ if (ret < 0) {
+ printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n");
+ return ret;
+ }
+ ret = register_pm_qos_misc(&network_lat_pm_qos);
+ if (ret < 0) {
+ printk(KERN_ERR "pm_qos_param: network_latency setup failed\n");
+ return ret;
+ }
+ ret = register_pm_qos_misc(&network_throughput_pm_qos);
+ if (ret < 0)
+ printk(KERN_ERR
+ "pm_qos_param: network_throughput setup failed\n");
+
+ return ret;
+}
+
+late_initcall(pm_qos_power_init);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 866f269..bb771e9 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -19,7 +19,7 @@
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/bitmap.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/inetdevice.h>
#include <net/net_namespace.h>
#include <net/cfg80211.h>
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index d6470c7..9604abc 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -17,7 +17,7 @@
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/crc32.h>
#include <linux/slab.h>
#include <net/mac80211.h>
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 6f09eca..beefb0a 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -14,7 +14,7 @@
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <net/sch_generic.h>
#include <linux/slab.h>
#include <net/mac80211.h>
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 1c6be91..c74e228 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -23,7 +23,7 @@
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/time.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
#include <linux/uio.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
--
1.7.4.1
^ permalink raw reply related
* [PATCH 02/15] PM QoS: minor clean-ups
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313502198-9298-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
- Misc fixes to improve code readability:
. rename struct pm_qos_request_list to struct pm_qos_request,
. rename pm_qos_req parameter to req in internal code,
consistenly use req in the API parameters,
. update the in-kernel API callers to the new parameters names,
. rename of fields names (requests, list, node, constraints)
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
drivers/media/video/via-camera.c | 2 +-
drivers/net/wireless/ipw2x00/ipw2100.c | 2 +-
include/linux/netdevice.h | 2 +-
include/linux/pm_qos.h | 22 ++++----
include/sound/pcm.h | 2 +-
kernel/power/qos.c | 88 ++++++++++++++++----------------
6 files changed, 59 insertions(+), 59 deletions(-)
diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c
index b3ca389..fba6c64 100644
--- a/drivers/media/video/via-camera.c
+++ b/drivers/media/video/via-camera.c
@@ -69,7 +69,7 @@ struct via_camera {
struct mutex lock;
enum viacam_opstate opstate;
unsigned long flags;
- struct pm_qos_request_list qos_request;
+ struct pm_qos_request qos_request;
/*
* GPIO info for power/reset management
*/
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index aaab76c..db35f99 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -174,7 +174,7 @@ that only one external action is invoked at a time.
#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver"
#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
-static struct pm_qos_request_list ipw2100_pm_qos_req;
+static struct pm_qos_request ipw2100_pm_qos_req;
/* Debugging stuff */
#ifdef CONFIG_IPW2100_DEBUG
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index f72ac6b..f38ab5b 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -964,7 +964,7 @@ struct net_device {
*/
char name[IFNAMSIZ];
- struct pm_qos_request_list pm_qos_req;
+ struct pm_qos_request pm_qos_req;
/* device name hash chain */
struct hlist_node name_hlist;
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 7ba67541..6b0968f 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -20,30 +20,30 @@
#define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
#define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0
-struct pm_qos_request_list {
- struct plist_node list;
+struct pm_qos_request {
+ struct plist_node node;
int pm_qos_class;
};
#ifdef CONFIG_PM
-void pm_qos_add_request(struct pm_qos_request_list *l,
- int pm_qos_class, s32 value);
-void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
+void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class,
+ s32 value);
+void pm_qos_update_request(struct pm_qos_request *req,
s32 new_value);
-void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req);
+void pm_qos_remove_request(struct pm_qos_request *req);
int pm_qos_request(int pm_qos_class);
int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
-int pm_qos_request_active(struct pm_qos_request_list *req);
+int pm_qos_request_active(struct pm_qos_request *req);
#else
-static inline void pm_qos_add_request(struct pm_qos_request_list *l,
+static inline void pm_qos_add_request(struct pm_qos_request *req,
int pm_qos_class, s32 value)
{ return; }
-static inline void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
+static inline void pm_qos_update_request(struct pm_qos_request *req,
s32 new_value)
{ return; }
-static inline void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req)
+static inline void pm_qos_remove_request(struct pm_qos_request *req)
{ return; }
static inline int pm_qos_request(int pm_qos_class)
@@ -54,7 +54,7 @@ static inline int pm_qos_add_notifier(int pm_qos_class,
static inline int pm_qos_remove_notifier(int pm_qos_class,
struct notifier_block *notifier)
{ return 0; }
-static inline int pm_qos_request_active(struct pm_qos_request_list *req)
+static inline int pm_qos_request_active(struct pm_qos_request *req)
{ return 0; }
#endif
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 666ee91..54cb079 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -373,7 +373,7 @@ struct snd_pcm_substream {
int number;
char name[32]; /* substream name */
int stream; /* stream (direction) */
- struct pm_qos_request_list latency_pm_qos_req; /* pm_qos request */
+ struct pm_qos_request latency_pm_qos_req; /* pm_qos request */
size_t buffer_bytes_max; /* limit ring buffer size */
struct snd_dma_buffer dma_buffer;
unsigned int dma_buf_id;
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index 61b4738..aa52c44 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -45,7 +45,7 @@
#include <linux/uaccess.h>
/*
- * locking rule: all changes to requests or notifiers lists
+ * locking rule: all changes to constraints or notifiers lists
* or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
* held, taken with _irqsave. One lock to rule them all
*/
@@ -60,7 +60,7 @@ enum pm_qos_type {
* types linux supports for 32 bit quantites
*/
struct pm_qos_object {
- struct plist_head requests;
+ struct plist_head constraints;
struct blocking_notifier_head *notifiers;
struct miscdevice pm_qos_power_miscdev;
char *name;
@@ -74,7 +74,7 @@ static DEFINE_SPINLOCK(pm_qos_lock);
static struct pm_qos_object null_pm_qos;
static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
static struct pm_qos_object cpu_dma_pm_qos = {
- .requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests),
+ .constraints = PLIST_HEAD_INIT(cpu_dma_pm_qos.constraints),
.notifiers = &cpu_dma_lat_notifier,
.name = "cpu_dma_latency",
.target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
@@ -84,7 +84,7 @@ static struct pm_qos_object cpu_dma_pm_qos = {
static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
static struct pm_qos_object network_lat_pm_qos = {
- .requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests),
+ .constraints = PLIST_HEAD_INIT(network_lat_pm_qos.constraints),
.notifiers = &network_lat_notifier,
.name = "network_latency",
.target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
@@ -95,7 +95,7 @@ static struct pm_qos_object network_lat_pm_qos = {
static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
static struct pm_qos_object network_throughput_pm_qos = {
- .requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests),
+ .constraints = PLIST_HEAD_INIT(network_throughput_pm_qos.constraints),
.notifiers = &network_throughput_notifier,
.name = "network_throughput",
.target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
@@ -129,15 +129,15 @@ static const struct file_operations pm_qos_power_fops = {
/* unlocked internal variant */
static inline int pm_qos_get_value(struct pm_qos_object *o)
{
- if (plist_head_empty(&o->requests))
+ if (plist_head_empty(&o->constraints))
return o->default_value;
switch (o->type) {
case PM_QOS_MIN:
- return plist_first(&o->requests)->prio;
+ return plist_first(&o->constraints)->prio;
case PM_QOS_MAX:
- return plist_last(&o->requests)->prio;
+ return plist_last(&o->constraints)->prio;
default:
/* runtime check for not using enum */
@@ -170,13 +170,13 @@ static void update_target(struct pm_qos_object *o, struct plist_node *node,
* with new value and add, then see if the extremal
* changed
*/
- plist_del(node, &o->requests);
+ plist_del(node, &o->constraints);
plist_node_init(node, value);
- plist_add(node, &o->requests);
+ plist_add(node, &o->constraints);
} else if (del) {
- plist_del(node, &o->requests);
+ plist_del(node, &o->constraints);
} else {
- plist_add(node, &o->requests);
+ plist_add(node, &o->constraints);
}
curr_value = pm_qos_get_value(o);
pm_qos_set_value(o, curr_value);
@@ -222,7 +222,7 @@ int pm_qos_request(int pm_qos_class)
}
EXPORT_SYMBOL_GPL(pm_qos_request);
-int pm_qos_request_active(struct pm_qos_request_list *req)
+int pm_qos_request_active(struct pm_qos_request *req)
{
return req->pm_qos_class != 0;
}
@@ -230,24 +230,24 @@ EXPORT_SYMBOL_GPL(pm_qos_request_active);
/**
* pm_qos_add_request - inserts new qos request into the list
- * @dep: pointer to a preallocated handle
+ * @req: pointer to a preallocated handle
* @pm_qos_class: identifies which list of qos request to use
* @value: defines the qos request
*
* This function inserts a new entry in the pm_qos_class list of requested qos
* performance characteristics. It recomputes the aggregate QoS expectations
- * for the pm_qos_class of parameters and initializes the pm_qos_request_list
+ * for the pm_qos_class of parameters and initializes the pm_qos_request
* handle. Caller needs to save this handle for later use in updates and
* removal.
*/
-void pm_qos_add_request(struct pm_qos_request_list *dep,
+void pm_qos_add_request(struct pm_qos_request *req,
int pm_qos_class, s32 value)
{
struct pm_qos_object *o = pm_qos_array[pm_qos_class];
int new_value;
- if (pm_qos_request_active(dep)) {
+ if (pm_qos_request_active(req)) {
WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n");
return;
}
@@ -255,15 +255,15 @@ void pm_qos_add_request(struct pm_qos_request_list *dep,
new_value = o->default_value;
else
new_value = value;
- plist_node_init(&dep->list, new_value);
- dep->pm_qos_class = pm_qos_class;
- update_target(o, &dep->list, 0, PM_QOS_DEFAULT_VALUE);
+ plist_node_init(&req->node, new_value);
+ req->pm_qos_class = pm_qos_class;
+ update_target(o, &req->node, 0, PM_QOS_DEFAULT_VALUE);
}
EXPORT_SYMBOL_GPL(pm_qos_add_request);
/**
* pm_qos_update_request - modifies an existing qos request
- * @pm_qos_req : handle to list element holding a pm_qos request to use
+ * @req : handle to list element holding a pm_qos request to use
* @value: defines the qos request
*
* Updates an existing qos request for the pm_qos_class of parameters along
@@ -271,56 +271,56 @@ EXPORT_SYMBOL_GPL(pm_qos_add_request);
*
* Attempts are made to make this code callable on hot code paths.
*/
-void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
+void pm_qos_update_request(struct pm_qos_request *req,
s32 new_value)
{
s32 temp;
struct pm_qos_object *o;
- if (!pm_qos_req) /*guard against callers passing in null */
+ if (!req) /*guard against callers passing in null */
return;
- if (!pm_qos_request_active(pm_qos_req)) {
+ if (!pm_qos_request_active(req)) {
WARN(1, KERN_ERR "pm_qos_update_request() called for unknown object\n");
return;
}
- o = pm_qos_array[pm_qos_req->pm_qos_class];
+ o = pm_qos_array[req->pm_qos_class];
if (new_value == PM_QOS_DEFAULT_VALUE)
temp = o->default_value;
else
temp = new_value;
- if (temp != pm_qos_req->list.prio)
- update_target(o, &pm_qos_req->list, 0, temp);
+ if (temp != req->node.prio)
+ update_target(o, &req->node, 0, temp);
}
EXPORT_SYMBOL_GPL(pm_qos_update_request);
/**
* pm_qos_remove_request - modifies an existing qos request
- * @pm_qos_req: handle to request list element
+ * @req: handle to request list element
*
- * Will remove pm qos request from the list of requests and
+ * Will remove pm qos request from the list of constraints and
* recompute the current target value for the pm_qos_class. Call this
* on slow code paths.
*/
-void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req)
+void pm_qos_remove_request(struct pm_qos_request *req)
{
struct pm_qos_object *o;
- if (pm_qos_req == NULL)
+ if (req == NULL)
return;
/* silent return to keep pcm code cleaner */
- if (!pm_qos_request_active(pm_qos_req)) {
+ if (!pm_qos_request_active(req)) {
WARN(1, KERN_ERR "pm_qos_remove_request() called for unknown object\n");
return;
}
- o = pm_qos_array[pm_qos_req->pm_qos_class];
- update_target(o, &pm_qos_req->list, 1, PM_QOS_DEFAULT_VALUE);
- memset(pm_qos_req, 0, sizeof(*pm_qos_req));
+ o = pm_qos_array[req->pm_qos_class];
+ update_target(o, &req->node, 1, PM_QOS_DEFAULT_VALUE);
+ memset(req, 0, sizeof(*req));
}
EXPORT_SYMBOL_GPL(pm_qos_remove_request);
@@ -368,7 +368,7 @@ static int pm_qos_power_open(struct inode *inode, struct file *filp)
pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
if (pm_qos_class >= 0) {
- struct pm_qos_request_list *req = kzalloc(sizeof(*req), GFP_KERNEL);
+ struct pm_qos_request *req = kzalloc(sizeof(*req), GFP_KERNEL);
if (!req)
return -ENOMEM;
@@ -383,7 +383,7 @@ static int pm_qos_power_open(struct inode *inode, struct file *filp)
static int pm_qos_power_release(struct inode *inode, struct file *filp)
{
- struct pm_qos_request_list *req;
+ struct pm_qos_request *req;
req = filp->private_data;
pm_qos_remove_request(req);
@@ -399,14 +399,14 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
s32 value;
unsigned long flags;
struct pm_qos_object *o;
- struct pm_qos_request_list *pm_qos_req = filp->private_data;
+ struct pm_qos_request *req = filp->private_data;
- if (!pm_qos_req)
+ if (!req)
return -EINVAL;
- if (!pm_qos_request_active(pm_qos_req))
+ if (!pm_qos_request_active(req))
return -EINVAL;
- o = pm_qos_array[pm_qos_req->pm_qos_class];
+ o = pm_qos_array[req->pm_qos_class];
spin_lock_irqsave(&pm_qos_lock, flags);
value = pm_qos_get_value(o);
spin_unlock_irqrestore(&pm_qos_lock, flags);
@@ -418,7 +418,7 @@ static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
s32 value;
- struct pm_qos_request_list *pm_qos_req;
+ struct pm_qos_request *req;
if (count == sizeof(s32)) {
if (copy_from_user(&value, buf, sizeof(s32)))
@@ -449,8 +449,8 @@ static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
return -EINVAL;
}
- pm_qos_req = filp->private_data;
- pm_qos_update_request(pm_qos_req, value);
+ req = filp->private_data;
+ pm_qos_update_request(req, value);
return count;
}
--
1.7.4.1
^ permalink raw reply related
* [PATCH 03/15] PM QoS: code re-organization
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313502198-9298-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
Move around the PM QoS misc devices management code
for better readability.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
kernel/power/qos.c | 45 +++++++++++++++++++++++----------------------
1 files changed, 23 insertions(+), 22 deletions(-)
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index aa52c44..788c4cf 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -188,28 +188,6 @@ static void update_target(struct pm_qos_object *o, struct plist_node *node,
NULL);
}
-static int register_pm_qos_misc(struct pm_qos_object *qos)
-{
- qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
- qos->pm_qos_power_miscdev.name = qos->name;
- qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
-
- return misc_register(&qos->pm_qos_power_miscdev);
-}
-
-static int find_pm_qos_object_by_minor(int minor)
-{
- int pm_qos_class;
-
- for (pm_qos_class = 0;
- pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
- if (minor ==
- pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
- return pm_qos_class;
- }
- return -1;
-}
-
/**
* pm_qos_request - returns current system wide qos expectation
* @pm_qos_class: identification of which qos value is requested
@@ -362,6 +340,29 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
}
EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
+/* User space interface to PM QoS classes via misc devices */
+static int register_pm_qos_misc(struct pm_qos_object *qos)
+{
+ qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
+ qos->pm_qos_power_miscdev.name = qos->name;
+ qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
+
+ return misc_register(&qos->pm_qos_power_miscdev);
+}
+
+static int find_pm_qos_object_by_minor(int minor)
+{
+ int pm_qos_class;
+
+ for (pm_qos_class = 0;
+ pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
+ if (minor ==
+ pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
+ return pm_qos_class;
+ }
+ return -1;
+}
+
static int pm_qos_power_open(struct inode *inode, struct file *filp)
{
long pm_qos_class;
--
1.7.4.1
^ permalink raw reply related
* [PATCH 04/15] PM QoS: re-organize data structs
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313502198-9298-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
In preparation for the per-device constratins support, re-organize
the data strctures:
- add a struct pm_qos_constraints which contains the constraints
related data
- update struct pm_qos_object contents to the PM QoS internal object
data. Add a pointer to struct pm_qos_constraints
- update the internal code to use the new data structs.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
include/linux/pm_qos.h | 19 +++++++++++
kernel/power/qos.c | 85 +++++++++++++++++++++++-------------------------
2 files changed, 60 insertions(+), 44 deletions(-)
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 6b0968f..9772311 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -25,6 +25,25 @@ struct pm_qos_request {
int pm_qos_class;
};
+enum pm_qos_type {
+ PM_QOS_UNITIALIZED,
+ PM_QOS_MAX, /* return the largest value */
+ PM_QOS_MIN /* return the smallest value */
+};
+
+/*
+ * Note: The lockless read path depends on the CPU accessing
+ * target_value atomically. Atomic access is only guaranteed on all CPU
+ * types linux supports for 32 bit quantites
+ */
+struct pm_qos_constraints {
+ struct plist_head list;
+ s32 target_value; /* Do not change to 64 bit */
+ s32 default_value;
+ enum pm_qos_type type;
+ struct blocking_notifier_head *notifiers;
+};
+
#ifdef CONFIG_PM
void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class,
s32 value);
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index 788c4cf..4a35fe5 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -49,58 +49,53 @@
* or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
* held, taken with _irqsave. One lock to rule them all
*/
-enum pm_qos_type {
- PM_QOS_MAX, /* return the largest value */
- PM_QOS_MIN /* return the smallest value */
-};
-
-/*
- * Note: The lockless read path depends on the CPU accessing
- * target_value atomically. Atomic access is only guaranteed on all CPU
- * types linux supports for 32 bit quantites
- */
struct pm_qos_object {
- struct plist_head constraints;
- struct blocking_notifier_head *notifiers;
+ struct pm_qos_constraints *constraints;
struct miscdevice pm_qos_power_miscdev;
char *name;
- s32 target_value; /* Do not change to 64 bit */
- s32 default_value;
- enum pm_qos_type type;
};
static DEFINE_SPINLOCK(pm_qos_lock);
static struct pm_qos_object null_pm_qos;
+
static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
-static struct pm_qos_object cpu_dma_pm_qos = {
- .constraints = PLIST_HEAD_INIT(cpu_dma_pm_qos.constraints),
- .notifiers = &cpu_dma_lat_notifier,
- .name = "cpu_dma_latency",
+static struct pm_qos_constraints cpu_dma_constraints = {
+ .list = PLIST_HEAD_INIT(cpu_dma_constraints.list),
.target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
.default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
.type = PM_QOS_MIN,
+ .notifiers = &cpu_dma_lat_notifier,
+};
+static struct pm_qos_object cpu_dma_pm_qos = {
+ .constraints = &cpu_dma_constraints,
};
static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
-static struct pm_qos_object network_lat_pm_qos = {
- .constraints = PLIST_HEAD_INIT(network_lat_pm_qos.constraints),
- .notifiers = &network_lat_notifier,
- .name = "network_latency",
+static struct pm_qos_constraints network_lat_constraints = {
+ .list = PLIST_HEAD_INIT(network_lat_constraints.list),
.target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
.default_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
- .type = PM_QOS_MIN
+ .type = PM_QOS_MIN,
+ .notifiers = &network_lat_notifier,
+};
+static struct pm_qos_object network_lat_pm_qos = {
+ .constraints = &network_lat_constraints,
+ .name = "network_latency",
};
static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
-static struct pm_qos_object network_throughput_pm_qos = {
- .constraints = PLIST_HEAD_INIT(network_throughput_pm_qos.constraints),
- .notifiers = &network_throughput_notifier,
- .name = "network_throughput",
+static struct pm_qos_constraints network_tput_constraints = {
+ .list = PLIST_HEAD_INIT(network_tput_constraints.list),
.target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
.default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
.type = PM_QOS_MAX,
+ .notifiers = &network_throughput_notifier,
+};
+static struct pm_qos_object network_throughput_pm_qos = {
+ .constraints = &network_tput_constraints,
+ .name = "network_throughput",
};
@@ -129,15 +124,15 @@ static const struct file_operations pm_qos_power_fops = {
/* unlocked internal variant */
static inline int pm_qos_get_value(struct pm_qos_object *o)
{
- if (plist_head_empty(&o->constraints))
- return o->default_value;
+ if (plist_head_empty(&o->constraints->list))
+ return o->constraints->default_value;
- switch (o->type) {
+ switch (o->constraints->type) {
case PM_QOS_MIN:
- return plist_first(&o->constraints)->prio;
+ return plist_first(&o->constraints->list)->prio;
case PM_QOS_MAX:
- return plist_last(&o->constraints)->prio;
+ return plist_last(&o->constraints->list)->prio;
default:
/* runtime check for not using enum */
@@ -147,12 +142,12 @@ static inline int pm_qos_get_value(struct pm_qos_object *o)
static inline s32 pm_qos_read_value(struct pm_qos_object *o)
{
- return o->target_value;
+ return o->constraints->target_value;
}
static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value)
{
- o->target_value = value;
+ o->constraints->target_value = value;
}
static void update_target(struct pm_qos_object *o, struct plist_node *node,
@@ -170,20 +165,20 @@ static void update_target(struct pm_qos_object *o, struct plist_node *node,
* with new value and add, then see if the extremal
* changed
*/
- plist_del(node, &o->constraints);
+ plist_del(node, &o->constraints->list);
plist_node_init(node, value);
- plist_add(node, &o->constraints);
+ plist_add(node, &o->constraints->list);
} else if (del) {
- plist_del(node, &o->constraints);
+ plist_del(node, &o->constraints->list);
} else {
- plist_add(node, &o->constraints);
+ plist_add(node, &o->constraints->list);
}
curr_value = pm_qos_get_value(o);
pm_qos_set_value(o, curr_value);
spin_unlock_irqrestore(&pm_qos_lock, flags);
if (prev_value != curr_value)
- blocking_notifier_call_chain(o->notifiers,
+ blocking_notifier_call_chain(o->constraints->notifiers,
(unsigned long)curr_value,
NULL);
}
@@ -230,7 +225,7 @@ void pm_qos_add_request(struct pm_qos_request *req,
return;
}
if (value == PM_QOS_DEFAULT_VALUE)
- new_value = o->default_value;
+ new_value = o->constraints->default_value;
else
new_value = value;
plist_node_init(&req->node, new_value);
@@ -266,7 +261,7 @@ void pm_qos_update_request(struct pm_qos_request *req,
o = pm_qos_array[req->pm_qos_class];
if (new_value == PM_QOS_DEFAULT_VALUE)
- temp = o->default_value;
+ temp = o->constraints->default_value;
else
temp = new_value;
@@ -315,7 +310,8 @@ int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
int retval;
retval = blocking_notifier_chain_register(
- pm_qos_array[pm_qos_class]->notifiers, notifier);
+ pm_qos_array[pm_qos_class]->constraints->notifiers,
+ notifier);
return retval;
}
@@ -334,7 +330,8 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
int retval;
retval = blocking_notifier_chain_unregister(
- pm_qos_array[pm_qos_class]->notifiers, notifier);
+ pm_qos_array[pm_qos_class]->constraints->notifiers,
+ notifier);
return retval;
}
--
1.7.4.1
^ permalink raw reply related
* [PATCH 05/15] PM QoS: generalize and export the constraints management code
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313502198-9298-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
In preparation for the per-device constratins support:
- rename update_target to pm_qos_update_target
- generalize and export pm_qos_update_target for usage by the upcoming
per-device latency constraints framework:
. operate on struct pm_qos_constraints for constraints management,
. introduce an 'action' parameter for constraints add/update/remove,
. the return value indicates if the aggregated constraint value has
changed,
- update the internal code to operate on struct pm_qos_constraints
- add a NULL pointer check in the API functions
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
include/linux/pm_qos.h | 14 ++++++
kernel/power/qos.c | 123 ++++++++++++++++++++++++++----------------------
2 files changed, 81 insertions(+), 56 deletions(-)
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 9772311..84aa150 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -44,7 +44,16 @@ struct pm_qos_constraints {
struct blocking_notifier_head *notifiers;
};
+/* Action requested to pm_qos_update_target */
+enum pm_qos_req_action {
+ PM_QOS_ADD_REQ, /* Add a new request */
+ PM_QOS_UPDATE_REQ, /* Update an existing request */
+ PM_QOS_REMOVE_REQ /* Remove an existing request */
+};
+
#ifdef CONFIG_PM
+int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
+ enum pm_qos_req_action action, int value);
void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class,
s32 value);
void pm_qos_update_request(struct pm_qos_request *req,
@@ -56,6 +65,11 @@ int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
int pm_qos_request_active(struct pm_qos_request *req);
#else
+static inline int pm_qos_update_target(struct pm_qos_constraints *c,
+ struct plist_node *node,
+ enum pm_qos_req_action action,
+ int value)
+ { return 0; }
static inline void pm_qos_add_request(struct pm_qos_request *req,
int pm_qos_class, s32 value)
{ return; }
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index 4a35fe5..7c7cd18 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -122,17 +122,17 @@ static const struct file_operations pm_qos_power_fops = {
};
/* unlocked internal variant */
-static inline int pm_qos_get_value(struct pm_qos_object *o)
+static inline int pm_qos_get_value(struct pm_qos_constraints *c)
{
- if (plist_head_empty(&o->constraints->list))
- return o->constraints->default_value;
+ if (plist_head_empty(&c->list))
+ return c->default_value;
- switch (o->constraints->type) {
+ switch (c->type) {
case PM_QOS_MIN:
- return plist_first(&o->constraints->list)->prio;
+ return plist_first(&c->list)->prio;
case PM_QOS_MAX:
- return plist_last(&o->constraints->list)->prio;
+ return plist_last(&c->list)->prio;
default:
/* runtime check for not using enum */
@@ -140,47 +140,73 @@ static inline int pm_qos_get_value(struct pm_qos_object *o)
}
}
-static inline s32 pm_qos_read_value(struct pm_qos_object *o)
+static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
{
- return o->constraints->target_value;
+ return c->target_value;
}
-static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value)
+static inline void pm_qos_set_value(struct pm_qos_constraints *c, s32 value)
{
- o->constraints->target_value = value;
+ c->target_value = value;
}
-static void update_target(struct pm_qos_object *o, struct plist_node *node,
- int del, int value)
+/**
+ * pm_qos_update_target - manages the constraints list and calls the notifiers
+ * if needed
+ * @c: constraints data struct
+ * @node: request to add to the list, to update or to remove
+ * @action: action to take on the constraints list
+ * @value: value of the request to add or update
+ *
+ * This function returns 1 if the aggregated constraint value has changed, 0
+ * otherwise.
+ */
+int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
+ enum pm_qos_req_action action, int value)
{
unsigned long flags;
- int prev_value, curr_value;
+ int prev_value, curr_value, new_value;
spin_lock_irqsave(&pm_qos_lock, flags);
- prev_value = pm_qos_get_value(o);
- /* PM_QOS_DEFAULT_VALUE is a signal that the value is unchanged */
- if (value != PM_QOS_DEFAULT_VALUE) {
+ prev_value = pm_qos_get_value(c);
+ if (value == PM_QOS_DEFAULT_VALUE)
+ new_value = c->default_value;
+ else
+ new_value = value;
+
+ switch (action) {
+ case PM_QOS_REMOVE_REQ:
+ plist_del(node, &c->list);
+ break;
+ case PM_QOS_UPDATE_REQ:
/*
* to change the list, we atomically remove, reinit
* with new value and add, then see if the extremal
* changed
*/
- plist_del(node, &o->constraints->list);
- plist_node_init(node, value);
- plist_add(node, &o->constraints->list);
- } else if (del) {
- plist_del(node, &o->constraints->list);
- } else {
- plist_add(node, &o->constraints->list);
+ plist_del(node, &c->list);
+ case PM_QOS_ADD_REQ:
+ plist_node_init(node, new_value);
+ plist_add(node, &c->list);
+ break;
+ default:
+ /* no action */
+ ;
}
- curr_value = pm_qos_get_value(o);
- pm_qos_set_value(o, curr_value);
+
+ curr_value = pm_qos_get_value(c);
+ pm_qos_set_value(c, curr_value);
+
spin_unlock_irqrestore(&pm_qos_lock, flags);
- if (prev_value != curr_value)
- blocking_notifier_call_chain(o->constraints->notifiers,
+ if (prev_value != curr_value) {
+ blocking_notifier_call_chain(c->notifiers,
(unsigned long)curr_value,
NULL);
+ return 1;
+ } else {
+ return 0;
+ }
}
/**
@@ -191,7 +217,7 @@ static void update_target(struct pm_qos_object *o, struct plist_node *node,
*/
int pm_qos_request(int pm_qos_class)
{
- return pm_qos_read_value(pm_qos_array[pm_qos_class]);
+ return pm_qos_read_value(pm_qos_array[pm_qos_class]->constraints);
}
EXPORT_SYMBOL_GPL(pm_qos_request);
@@ -217,20 +243,16 @@ EXPORT_SYMBOL_GPL(pm_qos_request_active);
void pm_qos_add_request(struct pm_qos_request *req,
int pm_qos_class, s32 value)
{
- struct pm_qos_object *o = pm_qos_array[pm_qos_class];
- int new_value;
+ if (!req) /*guard against callers passing in null */
+ return;
if (pm_qos_request_active(req)) {
WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n");
return;
}
- if (value == PM_QOS_DEFAULT_VALUE)
- new_value = o->constraints->default_value;
- else
- new_value = value;
- plist_node_init(&req->node, new_value);
req->pm_qos_class = pm_qos_class;
- update_target(o, &req->node, 0, PM_QOS_DEFAULT_VALUE);
+ pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints,
+ &req->node, PM_QOS_ADD_REQ, value);
}
EXPORT_SYMBOL_GPL(pm_qos_add_request);
@@ -247,9 +269,6 @@ EXPORT_SYMBOL_GPL(pm_qos_add_request);
void pm_qos_update_request(struct pm_qos_request *req,
s32 new_value)
{
- s32 temp;
- struct pm_qos_object *o;
-
if (!req) /*guard against callers passing in null */
return;
@@ -258,15 +277,10 @@ void pm_qos_update_request(struct pm_qos_request *req,
return;
}
- o = pm_qos_array[req->pm_qos_class];
-
- if (new_value == PM_QOS_DEFAULT_VALUE)
- temp = o->constraints->default_value;
- else
- temp = new_value;
-
- if (temp != req->node.prio)
- update_target(o, &req->node, 0, temp);
+ if (new_value != req->node.prio)
+ pm_qos_update_target(
+ pm_qos_array[req->pm_qos_class]->constraints,
+ &req->node, PM_QOS_UPDATE_REQ, new_value);
}
EXPORT_SYMBOL_GPL(pm_qos_update_request);
@@ -280,9 +294,7 @@ EXPORT_SYMBOL_GPL(pm_qos_update_request);
*/
void pm_qos_remove_request(struct pm_qos_request *req)
{
- struct pm_qos_object *o;
-
- if (req == NULL)
+ if (!req) /*guard against callers passing in null */
return;
/* silent return to keep pcm code cleaner */
@@ -291,8 +303,9 @@ void pm_qos_remove_request(struct pm_qos_request *req)
return;
}
- o = pm_qos_array[req->pm_qos_class];
- update_target(o, &req->node, 1, PM_QOS_DEFAULT_VALUE);
+ pm_qos_update_target(pm_qos_array[req->pm_qos_class]->constraints,
+ &req->node, PM_QOS_REMOVE_REQ,
+ PM_QOS_DEFAULT_VALUE);
memset(req, 0, sizeof(*req));
}
EXPORT_SYMBOL_GPL(pm_qos_remove_request);
@@ -396,7 +409,6 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
{
s32 value;
unsigned long flags;
- struct pm_qos_object *o;
struct pm_qos_request *req = filp->private_data;
if (!req)
@@ -404,9 +416,8 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
if (!pm_qos_request_active(req))
return -EINVAL;
- o = pm_qos_array[req->pm_qos_class];
spin_lock_irqsave(&pm_qos_lock, flags);
- value = pm_qos_get_value(o);
+ value = pm_qos_get_value(pm_qos_array[req->pm_qos_class]->constraints);
spin_unlock_irqrestore(&pm_qos_lock, flags);
return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32));
--
1.7.4.1
^ permalink raw reply related
* [PATCH 06/15] PM QoS: implement the per-device PM QoS constraints
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313502198-9298-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
Implement the per-device PM QoS constraints by creating a device
PM QoS API, which calls the PM QoS constraints management core code.
The per-device latency constraints data strctures are stored
in the device dev_pm_info struct.
The device PM code calls the init and destroy of the per-device constraints
data struct in order to support the dynamic insertion and removal of the
devices in the system.
To minimize the data usage by the per-device constraints, the data struct
is only allocated at the first call to dev_pm_qos_add_request.
The data is later free'd when the device is removed from the system.
A global mutex protects the constraints users from the data being
allocated and free'd.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
drivers/base/power/Makefile | 4 +-
drivers/base/power/main.c | 3 +
drivers/base/power/qos.c | 291 +++++++++++++++++++++++++++++++++++++++++++
include/linux/pm.h | 9 ++
include/linux/pm_qos.h | 42 ++++++
5 files changed, 347 insertions(+), 2 deletions(-)
create mode 100644 drivers/base/power/qos.c
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 2639ae7..b707447 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_PM) += sysfs.o generic_ops.o
+obj-$(CONFIG_PM) += sysfs.o generic_ops.o qos.o
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
obj-$(CONFIG_PM_RUNTIME) += runtime.o
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
@@ -6,4 +6,4 @@ obj-$(CONFIG_PM_OPP) += opp.o
obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o
obj-$(CONFIG_HAVE_CLK) += clock_ops.o
-ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
+ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index a854591..956443f 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -22,6 +22,7 @@
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
#include <linux/resume-trace.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
@@ -97,6 +98,7 @@ void device_pm_add(struct device *dev)
dev_name(dev->parent));
list_add_tail(&dev->power.entry, &dpm_list);
mutex_unlock(&dpm_list_mtx);
+ dev_pm_qos_constraints_init(dev);
}
/**
@@ -107,6 +109,7 @@ void device_pm_remove(struct device *dev)
{
pr_debug("PM: Removing info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
+ dev_pm_qos_constraints_destroy(dev);
complete_all(&dev->power.completion);
mutex_lock(&dpm_list_mtx);
list_del_init(&dev->power.entry);
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
new file mode 100644
index 0000000..304d68d
--- /dev/null
+++ b/drivers/base/power/qos.c
@@ -0,0 +1,291 @@
+/*
+ * This module exposes the interface to kernel space for specifying
+ * per-device PM QoS dependencies. It provides infrastructure for registration
+ * of:
+ *
+ * Dependents on a QoS value : register requests
+ * Watchers of QoS value : get notified when target QoS value changes
+ *
+ * This QoS design is best effort based. Dependents register their QoS needs.
+ * Watchers register to keep track of the current QoS needs of the system.
+ *
+ * Note about the per-device constraint data struct allocation:
+ * . The per-device constraints data struct ptr is tored into the device
+ * dev_pm_info.
+ * . To minimize the data usage by the per-device constraints, the data struct
+ * is only allocated at the first call to dev_pm_qos_add_request.
+ * . The data is later free'd when the device is removed from the system.
+ * . The constraints_state variable from dev_pm_info tracks the data struct
+ * allocation state:
+ * DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
+ * allocated,
+ * DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
+ * allocated at the first call to dev_pm_qos_add_request,
+ * DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
+ * PM QoS constraints framework is operational and constraints can be
+ * added, updated or removed using the dev_pm_qos_* API.
+ * . A global mutex protects the constraints users from the data being
+ * allocated and free'd.
+ */
+
+#include <linux/pm_qos.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+
+
+static DEFINE_MUTEX(dev_pm_qos_mtx);
+static void dev_pm_qos_constraints_allocate(struct device *dev);
+
+/**
+ * dev_pm_qos_add_request - inserts new qos request into the list
+ * @dev: target device for the constraint
+ * @req: pointer to a preallocated handle
+ * @value: defines the qos request
+ *
+ * This function inserts a new entry in the device constraints list of
+ * requested qos performance characteristics. It recomputes the aggregate
+ * QoS expectations of parameters and initializes the dev_pm_qos_request
+ * handle. Caller needs to save this handle for later use in updates and
+ * removal.
+ */
+void dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
+ s32 value)
+{
+ if (!dev || !req) /*guard against callers passing in null */
+ return;
+
+ if (dev_pm_qos_request_active(req)) {
+ WARN(1, KERN_ERR "dev_pm_qos_add_request() called for already "
+ "added request\n");
+ return;
+ }
+
+ /* Allocate the constraints struct on the first call to add_request */
+ dev_pm_qos_constraints_allocate(dev);
+
+ mutex_lock(&dev_pm_qos_mtx);
+
+ req->dev = dev;
+
+ /* Silently return if the device has been removed */
+ if (req->dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
+ goto out;
+
+ pm_qos_update_target(dev->power.constraints,
+ &req->node, PM_QOS_ADD_REQ, value);
+
+out:
+ mutex_unlock(&dev_pm_qos_mtx);
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
+
+/**
+ * dev_pm_qos_update_request - modifies an existing qos request
+ * @req : handle to list element holding a dev_pm_qos request to use
+ * @value: defines the qos request
+ *
+ * Updates an existing dev PM qos request along with updating the
+ * target value.
+ *
+ * Attempts are made to make this code callable on hot code paths.
+ */
+void dev_pm_qos_update_request(struct dev_pm_qos_request *req,
+ s32 new_value)
+{
+ if (!req) /*guard against callers passing in null */
+ return;
+
+ if (!dev_pm_qos_request_active(req)) {
+ WARN(1, KERN_ERR "dev_pm_qos_update_request() called for "
+ "unknown object\n");
+ return;
+ }
+
+ mutex_lock(&dev_pm_qos_mtx);
+
+ /* Silently return if the device has been removed */
+ if (req->dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
+ goto out;
+
+ if (new_value != req->node.prio)
+ pm_qos_update_target(
+ req->dev->power.constraints,
+ &req->node, PM_QOS_UPDATE_REQ, new_value);
+
+out:
+ mutex_unlock(&dev_pm_qos_mtx);
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
+
+/**
+ * dev_pm_qos_remove_request - modifies an existing qos request
+ * @req: handle to request list element
+ *
+ * Will remove pm qos request from the list of constraints and
+ * recompute the current target value. Call this on slow code paths.
+ */
+void dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
+{
+ if (!req) /*guard against callers passing in null */
+ return;
+
+ if (!dev_pm_qos_request_active(req)) {
+ WARN(1, KERN_ERR "dev_pm_qos_remove_request() called for "
+ "unknown object\n");
+ return;
+ }
+
+ mutex_lock(&dev_pm_qos_mtx);
+
+ /* Silently return if the device has been removed */
+ if (req->dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
+ goto out;
+
+ pm_qos_update_target(req->dev->power.constraints,
+ &req->node, PM_QOS_REMOVE_REQ,
+ PM_QOS_DEFAULT_VALUE);
+ memset(req, 0, sizeof(*req));
+
+out:
+ mutex_unlock(&dev_pm_qos_mtx);
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
+
+/**
+ * dev_pm_qos_add_notifier - sets notification entry for changes to target value
+ * of per-device PM QoS constraints
+ *
+ * @dev: target device for the constraint
+ * @notifier: notifier block managed by caller.
+ *
+ * Will register the notifier into a notification chain that gets called
+ * upon changes to the target value for the device.
+ */
+int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
+{
+ int retval = 0;
+
+ mutex_lock(&dev_pm_qos_mtx);
+
+ /* Silently return if the device has been removed */
+ if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
+ goto out;
+
+ retval = blocking_notifier_chain_register(
+ dev->power.constraints->notifiers,
+ notifier);
+
+out:
+ mutex_unlock(&dev_pm_qos_mtx);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
+
+/**
+ * dev_pm_qos_remove_notifier - deletes notification for changes to target value
+ * of per-device PM QoS constraints
+ *
+ * @dev: target device for the constraint
+ * @notifier: notifier block to be removed.
+ *
+ * Will remove the notifier from the notification chain that gets called
+ * upon changes to the target value.
+ */
+int dev_pm_qos_remove_notifier(struct device *dev,
+ struct notifier_block *notifier)
+{
+ int retval = 0;
+
+ mutex_lock(&dev_pm_qos_mtx);
+
+ /* Silently return if the device has been removed */
+ if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
+ goto out;
+
+ retval = blocking_notifier_chain_unregister(
+ dev->power.constraints->notifiers,
+ notifier);
+
+out:
+ mutex_unlock(&dev_pm_qos_mtx);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
+
+/* Called at the first call to add_request, for constraint data allocation */
+static void dev_pm_qos_constraints_allocate(struct device *dev)
+{
+ struct pm_qos_constraints *c;
+ struct blocking_notifier_head *n;
+
+ mutex_lock(&dev_pm_qos_mtx);
+
+ /*
+ * Return if the data is already allocated or
+ * if the device has been removed
+ */
+ if (dev->power.constraints_state != DEV_PM_QOS_DEVICE_PRESENT)
+ goto out;
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c)
+ goto out;
+
+ n = kzalloc(sizeof(*n), GFP_KERNEL);
+ if (!n) {
+ kfree(c);
+ goto out;
+ }
+ BLOCKING_INIT_NOTIFIER_HEAD(n);
+
+ dev->power.constraints = c;
+ plist_head_init(&dev->power.constraints->list);
+ dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+ dev->power.constraints->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+ dev->power.constraints->type = PM_QOS_MIN;
+ dev->power.constraints->notifiers = n;
+ dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+
+out:
+ mutex_unlock(&dev_pm_qos_mtx);
+}
+
+/* Called from the device PM subsystem at device insertion */
+void dev_pm_qos_constraints_init(struct device *dev)
+{
+ mutex_lock(&dev_pm_qos_mtx);
+ dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+ mutex_unlock(&dev_pm_qos_mtx);
+}
+
+/* Called from the device PM subsystem at device removal */
+void dev_pm_qos_constraints_destroy(struct device *dev)
+{
+ struct dev_pm_qos_request *req, *tmp;
+
+ mutex_lock(&dev_pm_qos_mtx);
+
+ if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+ /* Flush the constraints list for the device */
+ plist_for_each_entry_safe(req, tmp,
+ &dev->power.constraints->list,
+ node)
+ /*
+ * Update constraints list and call the per-device
+ * callbacks if needed
+ */
+ pm_qos_update_target(req->dev->power.constraints,
+ &req->node, PM_QOS_REMOVE_REQ,
+ PM_QOS_DEFAULT_VALUE);
+
+ kfree(dev->power.constraints->notifiers);
+ kfree(dev->power.constraints);
+ dev->power.constraints = NULL;
+ }
+ dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
+
+ mutex_unlock(&dev_pm_qos_mtx);
+}
+
diff --git a/include/linux/pm.h b/include/linux/pm.h
index f7c84c9..7a48951 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -421,6 +421,13 @@ enum rpm_request {
struct wakeup_source;
+/* Per-device PM QoS constraints data struct state */
+enum dev_pm_qos_state {
+ DEV_PM_QOS_NO_DEVICE, /* No device present */
+ DEV_PM_QOS_DEVICE_PRESENT, /* Device present, data not allocated */
+ DEV_PM_QOS_ALLOCATED, /* Device present, data allocated */
+};
+
struct dev_pm_info {
pm_message_t power_state;
unsigned int can_wakeup:1;
@@ -463,6 +470,8 @@ struct dev_pm_info {
unsigned long accounting_timestamp;
#endif
void *subsys_data; /* Owned by the subsystem. */
+ struct pm_qos_constraints *constraints;
+ enum dev_pm_qos_state constraints_state;
};
extern void update_pm_runtime_accounting(struct device *dev);
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 84aa150..d3e2d80 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -19,12 +19,18 @@
#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
#define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
#define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0
+#define PM_QOS_DEV_LAT_DEFAULT_VALUE 0
struct pm_qos_request {
struct plist_node node;
int pm_qos_class;
};
+struct dev_pm_qos_request {
+ struct plist_node node;
+ struct device *dev;
+};
+
enum pm_qos_type {
PM_QOS_UNITIALIZED,
PM_QOS_MAX, /* return the largest value */
@@ -51,6 +57,11 @@ enum pm_qos_req_action {
PM_QOS_REMOVE_REQ /* Remove an existing request */
};
+static inline int dev_pm_qos_request_active(struct dev_pm_qos_request *req)
+{
+ return req->dev != 0;
+}
+
#ifdef CONFIG_PM
int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
enum pm_qos_req_action action, int value);
@@ -64,6 +75,17 @@ int pm_qos_request(int pm_qos_class);
int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
int pm_qos_request_active(struct pm_qos_request *req);
+
+void dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
+ s32 value);
+void dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
+void dev_pm_qos_remove_request(struct dev_pm_qos_request *req);
+int dev_pm_qos_add_notifier(struct device *dev,
+ struct notifier_block *notifier);
+int dev_pm_qos_remove_notifier(struct device *dev,
+ struct notifier_block *notifier);
+void dev_pm_qos_constraints_init(struct device *dev);
+void dev_pm_qos_constraints_destroy(struct device *dev);
#else
static inline int pm_qos_update_target(struct pm_qos_constraints *c,
struct plist_node *node,
@@ -89,6 +111,26 @@ static inline int pm_qos_remove_notifier(int pm_qos_class,
{ return 0; }
static inline int pm_qos_request_active(struct pm_qos_request *req)
{ return 0; }
+
+static inline void dev_pm_qos_add_request(struct device *dev,
+ struct dev_pm_qos_request *req,
+ s32 value)
+ { return; }
+static inline void dev_pm_qos_update_request(struct dev_pm_qos_request *req,
+ s32 new_value)
+ { return; }
+static inline void dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
+ { return; }
+static inline int dev_pm_qos_add_notifier(struct device *dev,
+ struct notifier_block *notifier)
+ { return 0; }
+static inline int dev_pm_qos_remove_notifier(struct device *dev,
+ struct notifier_block *notifier)
+ { return 0; }
+static inline void dev_pm_qos_constraints_init(struct device *dev)
+ { return; }
+static inline void dev_pm_qos_constraints_destroy(struct device *dev)
+ { return; }
#endif
#endif
--
1.7.4.1
^ permalink raw reply related
* [PATCH 07/15] PM QoS: add a global notification mechanism for the device constraints
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313502198-9298-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
Add a global notification chain that gets called upon changes to the
aggregated constraint value for any device.
The notification callbacks are passing the full constraint request data
in order for the callees to have access to it. The current use is for the
platform low-level code to access the target device of the constraint.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
drivers/base/power/qos.c | 84 ++++++++++++++++++++++++++++++++++++----------
include/linux/pm_qos.h | 11 ++++++
kernel/power/qos.c | 2 +-
3 files changed, 78 insertions(+), 19 deletions(-)
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 304d68d..b52b3e8 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -8,6 +8,12 @@
*
* This QoS design is best effort based. Dependents register their QoS needs.
* Watchers register to keep track of the current QoS needs of the system.
+ * Watchers can register different types of notification callbacks:
+ * . a per-device notification callback using the dev_pm_qos_*_notifier API.
+ * The notification chain data is stored in the per-device constraint
+ * data struct.
+ * . a system-wide notification callback using the dev_pm_qos_*_global_notifier
+ * API. The notification chain data is stored in a static variable.
*
* Note about the per-device constraint data struct allocation:
* . The per-device constraints data struct ptr is tored into the device
@@ -36,8 +42,32 @@
static DEFINE_MUTEX(dev_pm_qos_mtx);
+static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
static void dev_pm_qos_constraints_allocate(struct device *dev);
+/*
+ * Update the constraints list using the PM QoS core code and
+ * if needed call the per-device and the global notification callbacks
+ */
+static int _apply_constraint(struct dev_pm_qos_request *req,
+ enum pm_qos_req_action action, int value)
+{
+ int ret, curr_value;
+
+ ret = pm_qos_update_target(req->dev->power.constraints,
+ &req->node, action, value);
+
+ if (ret) {
+ /* Call the global callbacks if needed */
+ curr_value = pm_qos_read_value(req->dev->power.constraints);
+ blocking_notifier_call_chain(&dev_pm_notifiers,
+ (unsigned long)curr_value,
+ req);
+ }
+
+ return ret;
+}
+
/**
* dev_pm_qos_add_request - inserts new qos request into the list
* @dev: target device for the constraint
@@ -66,16 +96,13 @@ void dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
dev_pm_qos_constraints_allocate(dev);
mutex_lock(&dev_pm_qos_mtx);
-
req->dev = dev;
/* Silently return if the device has been removed */
if (req->dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
goto out;
- pm_qos_update_target(dev->power.constraints,
- &req->node, PM_QOS_ADD_REQ, value);
-
+ _apply_constraint(req, PM_QOS_ADD_REQ, value);
out:
mutex_unlock(&dev_pm_qos_mtx);
}
@@ -92,7 +119,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
* Attempts are made to make this code callable on hot code paths.
*/
void dev_pm_qos_update_request(struct dev_pm_qos_request *req,
- s32 new_value)
+ s32 new_value)
{
if (!req) /*guard against callers passing in null */
return;
@@ -110,9 +137,7 @@ void dev_pm_qos_update_request(struct dev_pm_qos_request *req,
goto out;
if (new_value != req->node.prio)
- pm_qos_update_target(
- req->dev->power.constraints,
- &req->node, PM_QOS_UPDATE_REQ, new_value);
+ _apply_constraint(req, PM_QOS_UPDATE_REQ, new_value);
out:
mutex_unlock(&dev_pm_qos_mtx);
@@ -143,9 +168,7 @@ void dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
if (req->dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
goto out;
- pm_qos_update_target(req->dev->power.constraints,
- &req->node, PM_QOS_REMOVE_REQ,
- PM_QOS_DEFAULT_VALUE);
+ _apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
memset(req, 0, sizeof(*req));
out:
@@ -214,6 +237,36 @@ out:
}
EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
+/**
+ * dev_pm_qos_add_global_notifier - sets notification entry for changes to
+ * target value of the PM QoS constraints for any device
+ *
+ * @notifier: notifier block managed by caller.
+ *
+ * Will register the notifier into a notification chain that gets called
+ * upon changes to the target value for any device.
+ */
+int dev_pm_qos_add_global_notifier(struct notifier_block *notifier)
+{
+ return blocking_notifier_chain_register(&dev_pm_notifiers, notifier);
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_add_global_notifier);
+
+/**
+ * dev_pm_qos_remove_global_notifier - deletes notification for changes to
+ * target value of PM QoS constraints for any device
+ *
+ * @notifier: notifier block to be removed.
+ *
+ * Will remove the notifier from the notification chain that gets called
+ * upon changes to the target value for any device.
+ */
+int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier)
+{
+ return blocking_notifier_chain_unregister(&dev_pm_notifiers, notifier);
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier);
+
/* Called at the first call to add_request, for constraint data allocation */
static void dev_pm_qos_constraints_allocate(struct device *dev)
{
@@ -272,13 +325,8 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
plist_for_each_entry_safe(req, tmp,
&dev->power.constraints->list,
node)
- /*
- * Update constraints list and call the per-device
- * callbacks if needed
- */
- pm_qos_update_target(req->dev->power.constraints,
- &req->node, PM_QOS_REMOVE_REQ,
- PM_QOS_DEFAULT_VALUE);
+ _apply_constraint(req, PM_QOS_REMOVE_REQ,
+ PM_QOS_DEFAULT_VALUE);
kfree(dev->power.constraints->notifiers);
kfree(dev->power.constraints);
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index d3e2d80..950c1b2 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -75,6 +75,7 @@ int pm_qos_request(int pm_qos_class);
int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
int pm_qos_request_active(struct pm_qos_request *req);
+s32 pm_qos_read_value(struct pm_qos_constraints *c);
void dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
s32 value);
@@ -84,6 +85,8 @@ int dev_pm_qos_add_notifier(struct device *dev,
struct notifier_block *notifier);
int dev_pm_qos_remove_notifier(struct device *dev,
struct notifier_block *notifier);
+int dev_pm_qos_add_global_notifier(struct notifier_block *notifier);
+int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier);
void dev_pm_qos_constraints_init(struct device *dev);
void dev_pm_qos_constraints_destroy(struct device *dev);
#else
@@ -111,6 +114,8 @@ static inline int pm_qos_remove_notifier(int pm_qos_class,
{ return 0; }
static inline int pm_qos_request_active(struct pm_qos_request *req)
{ return 0; }
+static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
+ { return 0; }
static inline void dev_pm_qos_add_request(struct device *dev,
struct dev_pm_qos_request *req,
@@ -127,6 +132,12 @@ static inline int dev_pm_qos_add_notifier(struct device *dev,
static inline int dev_pm_qos_remove_notifier(struct device *dev,
struct notifier_block *notifier)
{ return 0; }
+static inline int dev_pm_qos_add_global_notifier(
+ struct notifier_block *notifier)
+ { return 0; }
+static inline int dev_pm_qos_remove_global_notifier(
+ struct notifier_block *notifier)
+ { return 0; }
static inline void dev_pm_qos_constraints_init(struct device *dev)
{ return; }
static inline void dev_pm_qos_constraints_destroy(struct device *dev)
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index 7c7cd18..1c1797d 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -140,7 +140,7 @@ static inline int pm_qos_get_value(struct pm_qos_constraints *c)
}
}
-static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
+s32 pm_qos_read_value(struct pm_qos_constraints *c)
{
return c->target_value;
}
--
1.7.4.1
^ permalink raw reply related
* [PATCH 08/15] OMAP: convert I2C driver to PM QoS for latency constraints
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313502198-9298-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
Convert the driver from the outdated omap_pm_set_max_mpu_wakeup_lat
API to the new PM QoS API.
Since the constraint is on the MPU subsystem, use the PM_QOS_CPU_DMA_LATENCY
class of PM QoS. The resulting MPU constraints are used by cpuidle to
decide the next power state of the MPU subsystem.
Currently only OMAP3 is placing constraints on the MPU.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
arch/arm/plat-omap/i2c.c | 20 --------------------
drivers/i2c/busses/i2c-omap.c | 31 ++++++++++++++++++-------------
2 files changed, 18 insertions(+), 33 deletions(-)
diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c
index 3341ca4..e1e2502 100644
--- a/arch/arm/plat-omap/i2c.c
+++ b/arch/arm/plat-omap/i2c.c
@@ -34,7 +34,6 @@
#include <mach/irqs.h>
#include <plat/mux.h>
#include <plat/i2c.h>
-#include <plat/omap-pm.h>
#include <plat/omap_device.h>
#define OMAP_I2C_SIZE 0x3f
@@ -113,16 +112,6 @@ static inline int omap1_i2c_add_bus(int bus_id)
#ifdef CONFIG_ARCH_OMAP2PLUS
-/*
- * XXX This function is a temporary compatibility wrapper - only
- * needed until the I2C driver can be converted to call
- * omap_pm_set_max_dev_wakeup_lat() and handle a return code.
- */
-static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t)
-{
- omap_pm_set_max_mpu_wakeup_lat(dev, t);
-}
-
static struct omap_device_pm_latency omap_i2c_latency[] = {
[0] = {
.deactivate_func = omap_device_idle_hwmods,
@@ -151,15 +140,6 @@ static inline int omap2_i2c_add_bus(int bus_id)
}
pdata = &i2c_pdata[bus_id - 1];
- /*
- * When waiting for completion of a i2c transfer, we need to
- * set a wake up latency constraint for the MPU. This is to
- * ensure quick enough wakeup from idle, when transfer
- * completes.
- * Only omap3 has support for constraints
- */
- if (cpu_is_omap34xx())
- pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;
od = omap_device_build(name, bus_id, oh, pdata,
sizeof(struct omap_i2c_bus_platform_data),
omap_i2c_latency, ARRAY_SIZE(omap_i2c_latency), 0);
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 8ae8081..d80ae91 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -40,6 +40,7 @@
#include <linux/slab.h>
#include <linux/i2c-omap.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
/* I2C controller revisions */
#define OMAP_I2C_REV_2 0x20
@@ -179,8 +180,7 @@ struct omap_i2c_dev {
struct completion cmd_complete;
struct resource *ioarea;
u32 latency; /* maximum mpu wkup latency */
- void (*set_mpu_wkup_lat)(struct device *dev,
- long latency);
+ struct pm_qos_request pm_qos_request;
u32 speed; /* Speed of bus in Khz */
u16 cmd_err;
u8 *buf;
@@ -648,8 +648,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
if (r < 0)
goto out;
- if (dev->set_mpu_wkup_lat != NULL)
- dev->set_mpu_wkup_lat(dev->dev, dev->latency);
+ /*
+ * When waiting for completion of a i2c transfer, we need to
+ * set a wake up latency constraint for the MPU. This is to
+ * ensure quick enough wakeup from idle, when transfer
+ * completes.
+ * Used on OMAP3 Only
+ */
+ if (cpu_is_omap34xx())
+ pm_qos_add_request(&dev->pm_qos_request, PM_QOS_CPU_DMA_LATENCY,
+ dev->latency);
for (i = 0; i < num; i++) {
r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
@@ -657,8 +665,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
break;
}
- if (dev->set_mpu_wkup_lat != NULL)
- dev->set_mpu_wkup_lat(dev->dev, -1);
+ if (cpu_is_omap34xx())
+ pm_qos_remove_request(&dev->pm_qos_request);
if (r == 0)
r = num;
@@ -1005,13 +1013,10 @@ omap_i2c_probe(struct platform_device *pdev)
goto err_release_region;
}
- if (pdata != NULL) {
+ if (pdata != NULL)
speed = pdata->clkrate;
- dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
- } else {
+ else
speed = 100; /* Default speed */
- dev->set_mpu_wkup_lat = NULL;
- }
dev->speed = speed;
dev->idle = 1;
@@ -1064,8 +1069,8 @@ omap_i2c_probe(struct platform_device *pdev)
dev->fifo_size = (dev->fifo_size / 2);
dev->b_hw = 1; /* Enable hardware fixes */
}
- /* calculate wakeup latency constraint for MPU */
- if (dev->set_mpu_wkup_lat != NULL)
+ /* calculate wakeup latency constraint */
+ if (cpu_is_omap34xx())
dev->latency = (1000000 * dev->fifo_size) /
(1000 * speed / 8);
}
--
1.7.4.1
^ permalink raw reply related
* [PATCH 09/15] OMAP: PM: create a PM layer plugin for per-device constraints
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313502198-9298-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
Created arch/arm/plat-omap/omap-pm-constraints.c file from
arch/arm/plat-omap/omap-pm-noop.c and the associated Kconfig option
OMAP_PM_CONSTRAINTS.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
arch/arm/plat-omap/Kconfig | 7 +
arch/arm/plat-omap/Makefile | 1 +
arch/arm/plat-omap/omap-pm-constraints.c | 363 ++++++++++++++++++++++++++++++
3 files changed, 371 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/plat-omap/omap-pm-constraints.c
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index bb8f4a6..3ee47a1 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -215,6 +215,13 @@ choice
config OMAP_PM_NOOP
bool "No-op/debug PM layer"
+config OMAP_PM_CONSTRAINTS
+ depends on PM
+ bool "Per device constraints"
+ help
+ Select this option to enable the PM layer plugin for
+ the per-device constraints support
+
endchoice
endmenu
diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile
index f0233e6..f2e09f1 100644
--- a/arch/arm/plat-omap/Makefile
+++ b/arch/arm/plat-omap/Makefile
@@ -32,3 +32,4 @@ obj-y += $(i2c-omap-m) $(i2c-omap-y)
obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox.o
obj-$(CONFIG_OMAP_PM_NOOP) += omap-pm-noop.o
+obj-$(CONFIG_OMAP_PM_CONSTRAINTS) += omap-pm-constraints.o
diff --git a/arch/arm/plat-omap/omap-pm-constraints.c b/arch/arm/plat-omap/omap-pm-constraints.c
new file mode 100644
index 0000000..c8b4e4c
--- /dev/null
+++ b/arch/arm/plat-omap/omap-pm-constraints.c
@@ -0,0 +1,363 @@
+/*
+ * omap-pm.c - OMAP power management interface
+ *
+ * This code implements the OMAP power management interface to
+ * drivers, CPUIdle, CPUFreq, and DSP Bridge.
+ *
+ * Copyright (C) 2008-2009 Texas Instruments, Inc.
+ * Copyright (C) 2008-2009 Nokia Corporation
+ * Paul Walmsley
+ *
+ * Interface developed by (in alphabetical order):
+ * Karthik Dasu, Tony Lindgren, Jean Pihet, Rajendra Nayak, Sakari Poussa,
+ * Veeramanikandan Raju, Anand Sawant, Igor Stoppa, Paul Walmsley,
+ * Richard Woodruff
+ */
+
+#undef DEBUG
+
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+/* Interface documentation is in mach/omap-pm.h */
+#include <plat/omap-pm.h>
+#include <plat/omap_device.h>
+
+static bool off_mode_enabled;
+static u32 dummy_context_loss_counter;
+
+/*
+ * Device-driver-originated constraints (via board-*.c files)
+ */
+
+int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
+{
+ if (!dev || t < -1) {
+ WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
+ return -EINVAL;
+ };
+
+ if (t == -1)
+ pr_debug("OMAP PM: remove max MPU wakeup latency constraint: "
+ "dev %s\n", dev_name(dev));
+ else
+ pr_debug("OMAP PM: add max MPU wakeup latency constraint: "
+ "dev %s, t = %ld usec\n", dev_name(dev), t);
+
+ /*
+ * For current Linux, this needs to map the MPU to a
+ * powerdomain, then go through the list of current max lat
+ * constraints on the MPU and find the smallest. If
+ * the latency constraint has changed, the code should
+ * recompute the state to enter for the next powerdomain
+ * state.
+ *
+ * TI CDP code can call constraint_set here.
+ */
+
+ return 0;
+}
+
+int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
+{
+ if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
+ agent_id != OCP_TARGET_AGENT)) {
+ WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
+ return -EINVAL;
+ };
+
+ if (r == 0)
+ pr_debug("OMAP PM: remove min bus tput constraint: "
+ "dev %s for agent_id %d\n", dev_name(dev), agent_id);
+ else
+ pr_debug("OMAP PM: add min bus tput constraint: "
+ "dev %s for agent_id %d: rate %ld KiB\n",
+ dev_name(dev), agent_id, r);
+
+ /*
+ * This code should model the interconnect and compute the
+ * required clock frequency, convert that to a VDD2 OPP ID, then
+ * set the VDD2 OPP appropriately.
+ *
+ * TI CDP code can call constraint_set here on the VDD2 OPP.
+ */
+
+ return 0;
+}
+
+int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
+ long t)
+{
+ if (!req_dev || !dev || t < -1) {
+ WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
+ return -EINVAL;
+ };
+
+ if (t == -1)
+ pr_debug("OMAP PM: remove max device latency constraint: "
+ "dev %s\n", dev_name(dev));
+ else
+ pr_debug("OMAP PM: add max device latency constraint: "
+ "dev %s, t = %ld usec\n", dev_name(dev), t);
+
+ /*
+ * For current Linux, this needs to map the device to a
+ * powerdomain, then go through the list of current max lat
+ * constraints on that powerdomain and find the smallest. If
+ * the latency constraint has changed, the code should
+ * recompute the state to enter for the next powerdomain
+ * state. Conceivably, this code should also determine
+ * whether to actually disable the device clocks or not,
+ * depending on how long it takes to re-enable the clocks.
+ *
+ * TI CDP code can call constraint_set here.
+ */
+
+ return 0;
+}
+
+int omap_pm_set_max_sdma_lat(struct device *dev, long t)
+{
+ if (!dev || t < -1) {
+ WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
+ return -EINVAL;
+ };
+
+ if (t == -1)
+ pr_debug("OMAP PM: remove max DMA latency constraint: "
+ "dev %s\n", dev_name(dev));
+ else
+ pr_debug("OMAP PM: add max DMA latency constraint: "
+ "dev %s, t = %ld usec\n", dev_name(dev), t);
+
+ /*
+ * For current Linux PM QOS params, this code should scan the
+ * list of maximum CPU and DMA latencies and select the
+ * smallest, then set cpu_dma_latency pm_qos_param
+ * accordingly.
+ *
+ * For future Linux PM QOS params, with separate CPU and DMA
+ * latency params, this code should just set the dma_latency param.
+ *
+ * TI CDP code can call constraint_set here.
+ */
+
+ return 0;
+}
+
+int omap_pm_set_min_clk_rate(struct device *dev, struct clk *c, long r)
+{
+ if (!dev || !c || r < 0) {
+ WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
+ return -EINVAL;
+ }
+
+ if (r == 0)
+ pr_debug("OMAP PM: remove min clk rate constraint: "
+ "dev %s\n", dev_name(dev));
+ else
+ pr_debug("OMAP PM: add min clk rate constraint: "
+ "dev %s, rate = %ld Hz\n", dev_name(dev), r);
+
+ /*
+ * Code in a real implementation should keep track of these
+ * constraints on the clock, and determine the highest minimum
+ * clock rate. It should iterate over each OPP and determine
+ * whether the OPP will result in a clock rate that would
+ * satisfy this constraint (and any other PM constraint in effect
+ * at that time). Once it finds the lowest-voltage OPP that
+ * meets those conditions, it should switch to it, or return
+ * an error if the code is not capable of doing so.
+ */
+
+ return 0;
+}
+
+/*
+ * DSP Bridge-specific constraints
+ */
+
+const struct omap_opp *omap_pm_dsp_get_opp_table(void)
+{
+ pr_debug("OMAP PM: DSP request for OPP table\n");
+
+ /*
+ * Return DSP frequency table here: The final item in the
+ * array should have .rate = .opp_id = 0.
+ */
+
+ return NULL;
+}
+
+void omap_pm_dsp_set_min_opp(u8 opp_id)
+{
+ if (opp_id == 0) {
+ WARN_ON(1);
+ return;
+ }
+
+ pr_debug("OMAP PM: DSP requests minimum VDD1 OPP to be %d\n", opp_id);
+
+ /*
+ *
+ * For l-o dev tree, our VDD1 clk is keyed on OPP ID, so we
+ * can just test to see which is higher, the CPU's desired OPP
+ * ID or the DSP's desired OPP ID, and use whichever is
+ * highest.
+ *
+ * In CDP12.14+, the VDD1 OPP custom clock that controls the DSP
+ * rate is keyed on MPU speed, not the OPP ID. So we need to
+ * map the OPP ID to the MPU speed for use with clk_set_rate()
+ * if it is higher than the current OPP clock rate.
+ *
+ */
+}
+
+
+u8 omap_pm_dsp_get_opp(void)
+{
+ pr_debug("OMAP PM: DSP requests current DSP OPP ID\n");
+
+ /*
+ * For l-o dev tree, call clk_get_rate() on VDD1 OPP clock
+ *
+ * CDP12.14+:
+ * Call clk_get_rate() on the OPP custom clock, map that to an
+ * OPP ID using the tables defined in board-*.c/chip-*.c files.
+ */
+
+ return 0;
+}
+
+/*
+ * CPUFreq-originated constraint
+ *
+ * In the future, this should be handled by custom OPP clocktype
+ * functions.
+ */
+
+struct cpufreq_frequency_table **omap_pm_cpu_get_freq_table(void)
+{
+ pr_debug("OMAP PM: CPUFreq request for frequency table\n");
+
+ /*
+ * Return CPUFreq frequency table here: loop over
+ * all VDD1 clkrates, pull out the mpu_ck frequencies, build
+ * table
+ */
+
+ return NULL;
+}
+
+void omap_pm_cpu_set_freq(unsigned long f)
+{
+ if (f == 0) {
+ WARN_ON(1);
+ return;
+ }
+
+ pr_debug("OMAP PM: CPUFreq requests CPU frequency to be set to %lu\n",
+ f);
+
+ /*
+ * For l-o dev tree, determine whether MPU freq or DSP OPP id
+ * freq is higher. Find the OPP ID corresponding to the
+ * higher frequency. Call clk_round_rate() and clk_set_rate()
+ * on the OPP custom clock.
+ *
+ * CDP should just be able to set the VDD1 OPP clock rate here.
+ */
+}
+
+unsigned long omap_pm_cpu_get_freq(void)
+{
+ pr_debug("OMAP PM: CPUFreq requests current CPU frequency\n");
+
+ /*
+ * Call clk_get_rate() on the mpu_ck.
+ */
+
+ return 0;
+}
+
+/**
+ * omap_pm_enable_off_mode - notify OMAP PM that off-mode is enabled
+ *
+ * Intended for use only by OMAP PM core code to notify this layer
+ * that off mode has been enabled.
+ */
+void omap_pm_enable_off_mode(void)
+{
+ off_mode_enabled = true;
+}
+
+/**
+ * omap_pm_disable_off_mode - notify OMAP PM that off-mode is disabled
+ *
+ * Intended for use only by OMAP PM core code to notify this layer
+ * that off mode has been disabled.
+ */
+void omap_pm_disable_off_mode(void)
+{
+ off_mode_enabled = false;
+}
+
+/*
+ * Device context loss tracking
+ */
+
+#ifdef CONFIG_ARCH_OMAP2PLUS
+
+u32 omap_pm_get_dev_context_loss_count(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ u32 count;
+
+ if (WARN_ON(!dev))
+ return 0;
+
+ if (dev->parent == &omap_device_parent) {
+ count = omap_device_get_context_loss_count(pdev);
+ } else {
+ WARN_ONCE(off_mode_enabled, "omap_pm: using dummy context "
+ "loss counter; device %s should be converted to "
+ "omap_device", dev_name(dev));
+ if (off_mode_enabled)
+ dummy_context_loss_counter++;
+ count = dummy_context_loss_counter;
+ }
+
+ pr_debug("OMAP PM: context loss count for dev %s = %d\n",
+ dev_name(dev), count);
+
+ return count;
+}
+
+#else
+
+u32 omap_pm_get_dev_context_loss_count(struct device *dev)
+{
+ return dummy_context_loss_counter;
+}
+
+#endif
+
+/* Should be called before clk framework init */
+int __init omap_pm_if_early_init(void)
+{
+ return 0;
+}
+
+/* Must be called after clock framework is initialized */
+int __init omap_pm_if_init(void)
+{
+ return 0;
+}
+
+void omap_pm_if_exit(void)
+{
+ /* Deallocate CPUFreq frequency table here */
+}
+
--
1.7.4.1
^ permalink raw reply related
* [PATCH 10/15] OMAP2+: powerdomain: control power domains next state
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313502198-9298-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
When a PM QoS device latency constraint is requested or removed the
PM QoS layer notifies the underlying layer with the updated aggregated
constraint value. The constraint is stored in the powerdomain constraints
list and then applied to the corresponding power domain.
The power domains get the next power state programmed directly in the
registers via pwrdm_wakeuplat_update_pwrst.
Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using
wake-up latency constraints on MPU, CORE and PER.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
arch/arm/mach-omap2/powerdomain.c | 190 +++++++++++++++++++++++++++++++++++++
arch/arm/mach-omap2/powerdomain.h | 33 ++++++-
2 files changed, 221 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index 9af0847..afa8153 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -17,8 +17,10 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/list.h>
+#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/string.h>
+#include <linux/pm_qos.h>
#include <trace/events/power.h>
#include "cm2xxx_3xxx.h"
@@ -104,6 +106,11 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
for (i = 0; i < pwrdm->banks; i++)
pwrdm->ret_mem_off_counter[i] = 0;
+ /* Initialize the per-od wake-up constraints list and spinlock */
+ spin_lock_init(&pwrdm->wkup_lat_plist_lock);
+ plist_head_init(&pwrdm->wkup_lat_plist_head);
+
+ /* Initialize the pwrdm state */
pwrdm_wait_transition(pwrdm);
pwrdm->state = pwrdm_read_pwrst(pwrdm);
pwrdm->state_counter[pwrdm->state] = 1;
@@ -191,6 +198,77 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
return 0;
}
+/**
+ * pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
+ * @pwrdm: struct powerdomain * to which requesting device belongs to.
+ * @min_latency: the allowed wake-up latency for the given power domain. A
+ * value of PM_QOS_DEV_LAT_DEFAULT_VALUE means 'no constraint' on the pwrdm.
+ *
+ * Finds the power domain next power state that fulfills the constraint.
+ * Programs a new target state if it is different from current power state.
+ * The power domains get the next power state programmed directly in the
+ * registers.
+ *
+ * Returns 0 upon success.
+ */
+static int pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm,
+ long min_latency)
+{
+ int ret = 0, new_state = 0;
+
+ if (!pwrdm) {
+ WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Apply constraints to power domains by programming
+ * the pwrdm next power state.
+ */
+
+ /* Find power state with wakeup latency < minimum constraint */
+ for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
+ if (min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE ||
+ pwrdm->wakeup_lat[new_state] <= min_latency)
+ break;
+ }
+
+ switch (new_state) {
+ case PWRDM_FUNC_PWRST_OFF:
+ new_state = PWRDM_POWER_OFF;
+ break;
+ case PWRDM_FUNC_PWRST_OSWR:
+ pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
+ new_state = PWRDM_POWER_RET;
+ break;
+ case PWRDM_FUNC_PWRST_CSWR:
+ pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET);
+ new_state = PWRDM_POWER_RET;
+ break;
+ case PWRDM_FUNC_PWRST_INACTIVE:
+ new_state = PWRDM_POWER_INACTIVE;
+ break;
+ case PWRDM_FUNC_PWRST_ON:
+ new_state = PWRDM_POWER_ON;
+ break;
+ default:
+ pr_warn("powerdomain: requested latency constraint not "
+ "supported %s set to ON state\n", pwrdm->name);
+ new_state = PWRDM_POWER_ON;
+ break;
+ }
+
+ if (pwrdm_read_next_pwrst(pwrdm) != new_state)
+ ret = omap_set_pwrdm_state(pwrdm, new_state);
+
+ pr_debug("powerdomain: %s pwrst: curr=%d, prev=%d next=%d "
+ "min_latency=%ld, set_state=%d\n", pwrdm->name,
+ pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
+ pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
+
+ return ret;
+}
+
/* Public functions */
/**
@@ -930,6 +1008,118 @@ int pwrdm_post_transition(void)
return 0;
}
+/*
+ * pwrdm_set_wkup_lat_constraint - Set/update/remove a powerdomain wakeup
+ * latency constraint and apply it
+ * @pwrdm: struct powerdomain * which the constraint applies to
+ * @cookie: constraint identifier, used for tracking.
+ * @min_latency: minimum wakeup latency constraint (in microseconds) for
+ * the given pwrdm. The value of PM_QOS_DEV_LAT_DEFAULT_VALUE removes
+ * the constraint.
+ *
+ * Tracks the constraints by @cookie.
+ * Constraint set/update: Adds a new entry to powerdomain's wake-up latency
+ * constraint list.
+ * If the constraint identifier already exists in the list, the old value is
+ * overwritten.
+ * Constraint removal: Removes the identifier's entry from powerdomain's
+ * wakeup latency constraint list.
+ *
+ * Applies the strongest constraint value for the given pwrdm by calling
+ * pwrdm_wakeuplat_update_pwrst.
+ *
+ * Returns 0 upon success or a negative value in case of error.
+ *
+ * The caller must check the validity of the parameters.
+ */
+int pwrdm_set_wkup_lat_constraint(struct powerdomain *pwrdm, void *cookie,
+ long min_latency)
+{
+ struct pwrdm_wkup_constraints_entry *user = NULL;
+ struct pwrdm_wkup_constraints_entry *tmp_user, *new_user = NULL;
+ int ret = 0, free_new_user = 0, free_node = 0;
+ long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+ unsigned long flags;
+
+ pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p, min_latency=%ld\n",
+ __func__, pwrdm->name, cookie, min_latency);
+
+ if (min_latency != PM_QOS_DEV_LAT_DEFAULT_VALUE) {
+ new_user = kzalloc(sizeof(struct pwrdm_wkup_constraints_entry),
+ GFP_KERNEL);
+ if (!new_user) {
+ pr_err("%s: FATAL ERROR: kzalloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ free_new_user = 1;
+ }
+
+ spin_lock_irqsave(&pwrdm->wkup_lat_plist_lock, flags);
+
+ /* Check if there already is a constraint for cookie */
+ plist_for_each_entry(tmp_user, &pwrdm->wkup_lat_plist_head, node) {
+ if (tmp_user->cookie == cookie) {
+ user = tmp_user;
+ break;
+ }
+ }
+
+ if (min_latency != PM_QOS_DEV_LAT_DEFAULT_VALUE) {
+ /* If nothing to update, job done */
+ if (user && (user->node.prio == min_latency))
+ goto exit_ok;
+
+ if (!user) {
+ /* Add new entry to the list */
+ user = new_user;
+ user->cookie = cookie;
+ free_new_user = 0;
+ } else {
+ /* Update existing entry */
+ plist_del(&user->node, &pwrdm->wkup_lat_plist_head);
+ }
+
+ plist_node_init(&user->node, min_latency);
+ plist_add(&user->node, &pwrdm->wkup_lat_plist_head);
+ } else {
+ if (user) {
+ /* Remove the constraint from the list */
+ plist_del(&user->node, &pwrdm->wkup_lat_plist_head);
+ free_node = 1;
+ } else {
+ /* Constraint not existing or list empty, do nothing */
+ ret = -EINVAL;
+ goto exit_error;
+ }
+
+ }
+
+exit_ok:
+ /* Find the strongest constraint from the list */
+ if (!plist_head_empty(&pwrdm->wkup_lat_plist_head))
+ value = plist_first(&pwrdm->wkup_lat_plist_head)->prio;
+
+ spin_unlock_irqrestore(&pwrdm->wkup_lat_plist_lock, flags);
+
+ if (free_node)
+ kfree(user);
+
+ if (free_new_user)
+ kfree(new_user);
+
+ /* Apply the constraint to the pwrdm */
+ pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
+ __func__, pwrdm->name, value);
+ pwrdm_wakeuplat_update_pwrst(pwrdm, value);
+
+ return 0;
+
+exit_error:
+ spin_unlock_irqrestore(&pwrdm->wkup_lat_plist_lock, flags);
+
+ return ret;
+}
+
/**
* pwrdm_get_context_loss_count - get powerdomain's context loss count
* @pwrdm: struct powerdomain * to wait for
diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h
index d23d979..f2b0ed7 100644
--- a/arch/arm/mach-omap2/powerdomain.h
+++ b/arch/arm/mach-omap2/powerdomain.h
@@ -19,7 +19,9 @@
#include <linux/types.h>
#include <linux/list.h>
-
+#include <linux/plist.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
#include <linux/atomic.h>
#include <plat/cpu.h>
@@ -43,6 +45,16 @@
#define PWRSTS_RET_ON (PWRSTS_RET | PWRSTS_ON)
#define PWRSTS_OFF_RET_ON (PWRSTS_OFF_RET | PWRSTS_ON)
+/* Powerdomain functional power states */
+#define PWRDM_FUNC_PWRST_OFF 0x0
+#define PWRDM_FUNC_PWRST_OSWR 0x1
+#define PWRDM_FUNC_PWRST_CSWR 0x2
+#define PWRDM_FUNC_PWRST_INACTIVE 0x3
+#define PWRDM_FUNC_PWRST_ON 0x4
+
+#define PWRDM_MAX_FUNC_PWRSTS 5
+
+#define UNSUP_STATE -1
/* Powerdomain flags */
#define PWRDM_HAS_HDWR_SAR (1 << 0) /* hardware save-and-restore support */
@@ -93,7 +105,12 @@ struct powerdomain;
* @state_counter:
* @timer:
* @state_timer:
- *
+ * @wakeup_lat: wakeup latencies (in us) for possible powerdomain power states
+ * Note about the wakeup latencies ordering: the values must be sorted
+ * in decremental order
+ * @wkup_lat_plist_head: pwrdm wake-up latency constraints list
+ * @wkup_lat_plist_lock: spinlock that protects the constraints lists
+ * domains states
* @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
*/
struct powerdomain {
@@ -118,6 +135,15 @@ struct powerdomain {
s64 timer;
s64 state_timer[PWRDM_MAX_PWRSTS];
#endif
+ const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
+ struct plist_head wkup_lat_plist_head;
+ spinlock_t wkup_lat_plist_lock;
+};
+
+/* Linked list for the wake-up latency constraints */
+struct pwrdm_wkup_constraints_entry {
+ void *cookie;
+ struct plist_node node;
};
/**
@@ -207,6 +233,9 @@ int pwrdm_clkdm_state_switch(struct clockdomain *clkdm);
int pwrdm_pre_transition(void);
int pwrdm_post_transition(void);
int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
+
+int pwrdm_set_wkup_lat_constraint(struct powerdomain *pwrdm, void *cookie,
+ long min_latency);
u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm);
--
1.7.4.1
^ permalink raw reply related
* [PATCH 11/15] OMAP3: powerdomain data: add wake-up latency figures
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313502198-9298-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
Figures are added to the power domains structs.
Note: the figures are preliminary figures. More accurate measurements
are needed. Also the conditions of measurements shall be investigated
and described.
Tested on OMAP3 Beagleboard in RET/OFF using wake-up latency constraints
on MPU, CORE and PER.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
arch/arm/mach-omap2/powerdomains3xxx_data.c | 77 +++++++++++++++++++++++++++
1 files changed, 77 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
index 469a920..64446e7 100644
--- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
+++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
@@ -31,6 +31,13 @@
/*
* Powerdomains
+ *
+ * The wakeup_lat values are derived from measurements on
+ * the actual target.
+ *
+ * Note: the latency figures are preliminary and only used
+ * for the constraints framework validation.
+ * Actual figures and measurements conditions shall be added.
*/
static struct powerdomain iva2_pwrdm = {
@@ -52,6 +59,13 @@ static struct powerdomain iva2_pwrdm = {
[2] = PWRSTS_OFF_ON,
[3] = PWRSTS_ON,
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1100,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 350,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
static struct powerdomain mpu_3xxx_pwrdm = {
@@ -68,6 +82,13 @@ static struct powerdomain mpu_3xxx_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_OFF_ON,
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 95,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 45,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/*
@@ -98,6 +119,13 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 100,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 60,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
static struct powerdomain core_3xxx_es3_1_pwrdm = {
@@ -121,6 +149,13 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 100,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 60,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
static struct powerdomain dss_pwrdm = {
@@ -136,6 +171,13 @@ static struct powerdomain dss_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 70,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 20,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/*
@@ -157,6 +199,13 @@ static struct powerdomain sgx_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1000,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
static struct powerdomain cam_pwrdm = {
@@ -172,6 +221,13 @@ static struct powerdomain cam_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 850,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 35,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
static struct powerdomain per_pwrdm = {
@@ -187,6 +243,13 @@ static struct powerdomain per_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 200,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 110,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
static struct powerdomain emu_pwrdm = {
@@ -201,6 +264,13 @@ static struct powerdomain neon_pwrdm = {
.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
.pwrsts = PWRSTS_OFF_RET_ON,
.pwrsts_logic_ret = PWRSTS_RET,
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 200,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 35,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
static struct powerdomain usbhost_pwrdm = {
@@ -223,6 +293,13 @@ static struct powerdomain usbhost_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 800,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 150,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
static struct powerdomain dpll1_pwrdm = {
--
1.7.4.1
^ permalink raw reply related
* [PATCH 12/15] OMAP4: powerdomain data: add wake-up latency figures
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313502198-9298-1-git-send-email-j-pihet@ti.com>
From: Vishwanath BS <vishwanath.bs@ti.com>
This patch adds wake up latency numbers for OMAP4. Note that these are
preliminary numbers and need to be relooked.
Signed-off-by: Vishwanath BS <vishwanath.bs@ti.com>
The INACTIVE state is added as unsupported.
Tested on OMAP4 Pandaboard in RET/OFF using wake-up latency constraints on
MPU, CORE and PER.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
arch/arm/mach-omap2/powerdomains44xx_data.c | 84 +++++++++++++++++++++++++++
1 files changed, 84 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/powerdomains44xx_data.c b/arch/arm/mach-omap2/powerdomains44xx_data.c
index 247e794..c3f8dd4 100644
--- a/arch/arm/mach-omap2/powerdomains44xx_data.c
+++ b/arch/arm/mach-omap2/powerdomains44xx_data.c
@@ -54,6 +54,13 @@ static struct powerdomain core_44xx_pwrdm = {
[4] = PWRSTS_ON, /* ducati_unicache */
},
.flags = PWRDM_HAS_LOWPOWERSTATECHANGE,
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_OSWR] = 600,
+ [PWRDM_FUNC_PWRST_CSWR] = 300,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/* gfx_44xx_pwrdm: 3D accelerator power domain */
@@ -71,6 +78,13 @@ static struct powerdomain gfx_44xx_pwrdm = {
[0] = PWRSTS_ON, /* gfx_mem */
},
.flags = PWRDM_HAS_LOWPOWERSTATECHANGE,
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1000,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/* abe_44xx_pwrdm: Audio back end power domain */
@@ -91,6 +105,13 @@ static struct powerdomain abe_44xx_pwrdm = {
[1] = PWRSTS_ON, /* periphmem */
},
.flags = PWRDM_HAS_LOWPOWERSTATECHANGE,
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1000,
+ [PWRDM_FUNC_PWRST_OSWR] = 600,
+ [PWRDM_FUNC_PWRST_CSWR] = 300,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/* dss_44xx_pwrdm: Display subsystem power domain */
@@ -109,6 +130,13 @@ static struct powerdomain dss_44xx_pwrdm = {
[0] = PWRSTS_ON, /* dss_mem */
},
.flags = PWRDM_HAS_LOWPOWERSTATECHANGE,
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1000,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 300,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/* tesla_44xx_pwrdm: Tesla processor power domain */
@@ -131,6 +159,13 @@ static struct powerdomain tesla_44xx_pwrdm = {
[2] = PWRSTS_ON, /* tesla_l2 */
},
.flags = PWRDM_HAS_LOWPOWERSTATECHANGE,
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1000,
+ [PWRDM_FUNC_PWRST_OSWR] = 600,
+ [PWRDM_FUNC_PWRST_CSWR] = 300,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/* wkup_44xx_pwrdm: Wake-up power domain */
@@ -164,6 +199,13 @@ static struct powerdomain cpu0_44xx_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* cpu0_l1 */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1000,
+ [PWRDM_FUNC_PWRST_OSWR] = 600,
+ [PWRDM_FUNC_PWRST_CSWR] = 300,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/* cpu1_44xx_pwrdm: MPU1 processor and Neon coprocessor power domain */
@@ -181,6 +223,13 @@ static struct powerdomain cpu1_44xx_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* cpu1_l1 */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1000,
+ [PWRDM_FUNC_PWRST_OSWR] = 600,
+ [PWRDM_FUNC_PWRST_CSWR] = 300,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/* emu_44xx_pwrdm: Emulation power domain */
@@ -218,6 +267,13 @@ static struct powerdomain mpu_44xx_pwrdm = {
[1] = PWRSTS_ON, /* mpu_l2 */
[2] = PWRSTS_ON, /* mpu_ram */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1000,
+ [PWRDM_FUNC_PWRST_OSWR] = 600,
+ [PWRDM_FUNC_PWRST_CSWR] = 300,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/* ivahd_44xx_pwrdm: IVA-HD power domain */
@@ -242,6 +298,13 @@ static struct powerdomain ivahd_44xx_pwrdm = {
[3] = PWRSTS_ON, /* tcm2_mem */
},
.flags = PWRDM_HAS_LOWPOWERSTATECHANGE,
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1000,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 300,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/* cam_44xx_pwrdm: Camera subsystem power domain */
@@ -259,6 +322,13 @@ static struct powerdomain cam_44xx_pwrdm = {
[0] = PWRSTS_ON, /* cam_mem */
},
.flags = PWRDM_HAS_LOWPOWERSTATECHANGE,
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1000,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/* l3init_44xx_pwrdm: L3 initators pheripherals power domain */
@@ -277,6 +347,13 @@ static struct powerdomain l3init_44xx_pwrdm = {
[0] = PWRSTS_ON, /* l3init_bank1 */
},
.flags = PWRDM_HAS_LOWPOWERSTATECHANGE,
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1000,
+ [PWRDM_FUNC_PWRST_OSWR] = 600,
+ [PWRDM_FUNC_PWRST_CSWR] = 300,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/* l4per_44xx_pwrdm: Target peripherals power domain */
@@ -297,6 +374,13 @@ static struct powerdomain l4per_44xx_pwrdm = {
[1] = PWRSTS_ON, /* retained_bank */
},
.flags = PWRDM_HAS_LOWPOWERSTATECHANGE,
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_OSWR] = 600,
+ [PWRDM_FUNC_PWRST_CSWR] = 300,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
};
/*
--
1.7.4.1
^ permalink raw reply related
* [PATCH 13/15] OMAP2+: omap_hwmod: manage the wake-up latency constraints
From: jean.pihet @ 2011-08-16 13:43 UTC (permalink / raw)
To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313502198-9298-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
Hwmod is queried from the OMAP_PM layer to manage the power domains
wake-up latency constraints. Hwmod retrieves the correct power domain
and if it exists it calls the corresponding power domain function.
Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using wake-up
latency constraints on MPU, CORE and PER.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
arch/arm/mach-omap2/omap_hwmod.c | 26 +++++++++++++++++++++++++-
arch/arm/plat-omap/include/plat/omap_hwmod.h | 2 ++
2 files changed, 27 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 84cc0bd..c6b1cc9 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -143,6 +143,7 @@
#include "powerdomain.h"
#include <plat/clock.h>
#include <plat/omap_hwmod.h>
+#include <plat/omap_device.h>
#include <plat/prcm.h>
#include "cm2xxx_3xxx.h"
@@ -2618,11 +2619,34 @@ ohsps_unlock:
return ret;
}
+/*
+ * omap_hwmod_set_wkup_constraint- set/release a wake-up latency constraint
+ *
+ * @oh: struct omap_hwmod* to which the target device belongs to.
+ * @cookie: identifier of the constraints list for @oh.
+ * @min_latency: the minimum allowed wake-up latency for @oh.
+ *
+ * Returns 0 upon success.
+ */
+int omap_hwmod_set_wkup_lat_constraint(struct omap_hwmod *oh,
+ void *cookie, long min_latency)
+{
+ struct powerdomain *pwrdm = omap_hwmod_get_pwrdm(oh);
+
+ if (!pwrdm) {
+ pr_err("%s: Error: could not find powerdomain "
+ "for %s\n", __func__, oh->name);
+ return -EINVAL;
+ }
+
+ return pwrdm_set_wkup_lat_constraint(pwrdm, cookie, min_latency);
+}
+
/**
* omap_hwmod_get_context_loss_count - get lost context count
* @oh: struct omap_hwmod *
*
- * Query the powerdomain of of @oh to get the context loss
+ * Query the powerdomain of @oh to get the context loss
* count for this device.
*
* Returns the context loss count of the powerdomain assocated with @oh
diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
index 0e329ca..75e0e7a 100644
--- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
+++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
@@ -603,6 +603,8 @@ int omap_hwmod_for_each_by_class(const char *classname,
void *user);
int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state);
+int omap_hwmod_set_wkup_lat_constraint(struct omap_hwmod *oh, void *cookie,
+ long min_latency);
u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
int omap_hwmod_no_setup_reset(struct omap_hwmod *oh);
--
1.7.4.1
^ permalink raw reply related
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