forked from Minki/linux
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6: (23 commits) at_hdmac: Rework suspend_late()/resume_early() PM: Reset transition_started at dpm_resume_noirq PM: Update kerneldoc comments in drivers/base/power/main.c PM: Add convenience macro to make switching to dev_pm_ops less error-prone hp-wmi: Switch driver to dev_pm_ops floppy: Switch driver to dev_pm_ops PM: Trivial fixes PM / Hibernate / Memory hotplug: Always use for_each_populated_zone() PM/Hibernate: Do not try to allocate too much memory too hard (rev. 2) PM/Hibernate: Do not release preallocated memory unnecessarily (rev. 2) PM/Hibernate: Rework shrinking of memory PM: Fix typo in label name s/Platofrm_finish/Platform_finish/ PM: Run-time PM platform device bus support PM: Introduce core framework for run-time PM of I/O devices (rev. 17) Driver Core: Make PM operations a const pointer PM: Remove platform device suspend_late()/resume_early() V2 USB: Rework musb suspend()/resume_early() I2C: Rework i2c-s3c2410 suspend_late()/resume() V2 I2C: Rework i2c-pxa suspend_late()/resume_early() DMA: Rework txx9dmac suspend_late()/resume_early() ... Fix trivial conflict in drivers/base/platform.c (due to same constification patch being merged in both sides, along with some other PM work in the PM branch)
This commit is contained in:
commit
f86054c245
378
Documentation/power/runtime_pm.txt
Normal file
378
Documentation/power/runtime_pm.txt
Normal file
@ -0,0 +1,378 @@
|
||||
Run-time Power Management Framework for I/O Devices
|
||||
|
||||
(C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
|
||||
|
||||
1. Introduction
|
||||
|
||||
Support for run-time power management (run-time PM) of I/O devices is provided
|
||||
at the power management core (PM core) level by means of:
|
||||
|
||||
* The power management workqueue pm_wq in which bus types and device drivers can
|
||||
put their PM-related work items. It is strongly recommended that pm_wq be
|
||||
used for queuing all work items related to run-time PM, because this allows
|
||||
them to be synchronized with system-wide power transitions (suspend to RAM,
|
||||
hibernation and resume from system sleep states). pm_wq is declared in
|
||||
include/linux/pm_runtime.h and defined in kernel/power/main.c.
|
||||
|
||||
* A number of run-time PM fields in the 'power' member of 'struct device' (which
|
||||
is of the type 'struct dev_pm_info', defined in include/linux/pm.h) that can
|
||||
be used for synchronizing run-time PM operations with one another.
|
||||
|
||||
* Three device run-time PM callbacks in 'struct dev_pm_ops' (defined in
|
||||
include/linux/pm.h).
|
||||
|
||||
* A set of helper functions defined in drivers/base/power/runtime.c that can be
|
||||
used for carrying out run-time PM operations in such a way that the
|
||||
synchronization between them is taken care of by the PM core. Bus types and
|
||||
device drivers are encouraged to use these functions.
|
||||
|
||||
The run-time PM callbacks present in 'struct dev_pm_ops', the device run-time PM
|
||||
fields of 'struct dev_pm_info' and the core helper functions provided for
|
||||
run-time PM are described below.
|
||||
|
||||
2. Device Run-time PM Callbacks
|
||||
|
||||
There are three device run-time PM callbacks defined in 'struct dev_pm_ops':
|
||||
|
||||
struct dev_pm_ops {
|
||||
...
|
||||
int (*runtime_suspend)(struct device *dev);
|
||||
int (*runtime_resume)(struct device *dev);
|
||||
void (*runtime_idle)(struct device *dev);
|
||||
...
|
||||
};
|
||||
|
||||
The ->runtime_suspend() callback is executed by the PM core for the bus type of
|
||||
the device being suspended. The bus type's callback is then _entirely_
|
||||
_responsible_ for handling the device as appropriate, which may, but need not
|
||||
include executing the device driver's own ->runtime_suspend() callback (from the
|
||||
PM core's point of view it is not necessary to implement a ->runtime_suspend()
|
||||
callback in a device driver as long as the bus type's ->runtime_suspend() knows
|
||||
what to do to handle the device).
|
||||
|
||||
* Once the bus type's ->runtime_suspend() callback has completed successfully
|
||||
for given device, the PM core regards the device as suspended, which need
|
||||
not mean that the device has been put into a low power state. It is
|
||||
supposed to mean, however, that the device will not process data and will
|
||||
not communicate with the CPU(s) and RAM until its bus type's
|
||||
->runtime_resume() callback is executed for it. The run-time PM status of
|
||||
a device after successful execution of its bus type's ->runtime_suspend()
|
||||
callback is 'suspended'.
|
||||
|
||||
* If the bus type's ->runtime_suspend() callback returns -EBUSY or -EAGAIN,
|
||||
the device's run-time PM status is supposed to be 'active', which means that
|
||||
the device _must_ be fully operational afterwards.
|
||||
|
||||
* If the bus type's ->runtime_suspend() callback returns an error code
|
||||
different from -EBUSY or -EAGAIN, the PM core regards this as a fatal
|
||||
error and will refuse to run the helper functions described in Section 4
|
||||
for the device, until the status of it is directly set either to 'active'
|
||||
or to 'suspended' (the PM core provides special helper functions for this
|
||||
purpose).
|
||||
|
||||
In particular, if the driver requires remote wakeup capability for proper
|
||||
functioning and device_may_wakeup() returns 'false' for the device, then
|
||||
->runtime_suspend() should return -EBUSY. On the other hand, if
|
||||
device_may_wakeup() returns 'true' for the device and the device is put
|
||||
into a low power state during the execution of its bus type's
|
||||
->runtime_suspend(), it is expected that remote wake-up (i.e. hardware mechanism
|
||||
allowing the device to request a change of its power state, such as PCI PME)
|
||||
will be enabled for the device. Generally, remote wake-up should be enabled
|
||||
for all input devices put into a low power state at run time.
|
||||
|
||||
The ->runtime_resume() callback is executed by the PM core for the bus type of
|
||||
the device being woken up. The bus type's callback is then _entirely_
|
||||
_responsible_ for handling the device as appropriate, which may, but need not
|
||||
include executing the device driver's own ->runtime_resume() callback (from the
|
||||
PM core's point of view it is not necessary to implement a ->runtime_resume()
|
||||
callback in a device driver as long as the bus type's ->runtime_resume() knows
|
||||
what to do to handle the device).
|
||||
|
||||
* Once the bus type's ->runtime_resume() callback has completed successfully,
|
||||
the PM core regards the device as fully operational, which means that the
|
||||
device _must_ be able to complete I/O operations as needed. The run-time
|
||||
PM status of the device is then 'active'.
|
||||
|
||||
* If the bus type's ->runtime_resume() callback returns an error code, the PM
|
||||
core regards this as a fatal error and will refuse to run the helper
|
||||
functions described in Section 4 for the device, until its status is
|
||||
directly set either to 'active' or to 'suspended' (the PM core provides
|
||||
special helper functions for this purpose).
|
||||
|
||||
The ->runtime_idle() callback is executed by the PM core for the bus type of
|
||||
given device whenever the device appears to be idle, which is indicated to the
|
||||
PM core by two counters, the device's usage counter and the counter of 'active'
|
||||
children of the device.
|
||||
|
||||
* If any of these counters is decreased using a helper function provided by
|
||||
the PM core and it turns out to be equal to zero, the other counter is
|
||||
checked. If that counter also is equal to zero, the PM core executes the
|
||||
device bus type's ->runtime_idle() callback (with the device as an
|
||||
argument).
|
||||
|
||||
The action performed by a bus type's ->runtime_idle() callback is totally
|
||||
dependent on the bus type in question, but the expected and recommended action
|
||||
is to check if the device can be suspended (i.e. if all of the conditions
|
||||
necessary for suspending the device are satisfied) and to queue up a suspend
|
||||
request for the device in that case.
|
||||
|
||||
The helper functions provided by the PM core, described in Section 4, guarantee
|
||||
that the following constraints are met with respect to the bus type's run-time
|
||||
PM callbacks:
|
||||
|
||||
(1) The callbacks are mutually exclusive (e.g. it is forbidden to execute
|
||||
->runtime_suspend() in parallel with ->runtime_resume() or with another
|
||||
instance of ->runtime_suspend() for the same device) with the exception that
|
||||
->runtime_suspend() or ->runtime_resume() can be executed in parallel with
|
||||
->runtime_idle() (although ->runtime_idle() will not be started while any
|
||||
of the other callbacks is being executed for the same device).
|
||||
|
||||
(2) ->runtime_idle() and ->runtime_suspend() can only be executed for 'active'
|
||||
devices (i.e. the PM core will only execute ->runtime_idle() or
|
||||
->runtime_suspend() for the devices the run-time PM status of which is
|
||||
'active').
|
||||
|
||||
(3) ->runtime_idle() and ->runtime_suspend() can only be executed for a device
|
||||
the usage counter of which is equal to zero _and_ either the counter of
|
||||
'active' children of which is equal to zero, or the 'power.ignore_children'
|
||||
flag of which is set.
|
||||
|
||||
(4) ->runtime_resume() can only be executed for 'suspended' devices (i.e. the
|
||||
PM core will only execute ->runtime_resume() for the devices the run-time
|
||||
PM status of which is 'suspended').
|
||||
|
||||
Additionally, the helper functions provided by the PM core obey the following
|
||||
rules:
|
||||
|
||||
* If ->runtime_suspend() is about to be executed or there's a pending request
|
||||
to execute it, ->runtime_idle() will not be executed for the same device.
|
||||
|
||||
* A request to execute or to schedule the execution of ->runtime_suspend()
|
||||
will cancel any pending requests to execute ->runtime_idle() for the same
|
||||
device.
|
||||
|
||||
* If ->runtime_resume() is about to be executed or there's a pending request
|
||||
to execute it, the other callbacks will not be executed for the same device.
|
||||
|
||||
* A request to execute ->runtime_resume() will cancel any pending or
|
||||
scheduled requests to execute the other callbacks for the same device.
|
||||
|
||||
3. Run-time PM Device Fields
|
||||
|
||||
The following device run-time PM fields are present in 'struct dev_pm_info', as
|
||||
defined in include/linux/pm.h:
|
||||
|
||||
struct timer_list suspend_timer;
|
||||
- timer used for scheduling (delayed) suspend request
|
||||
|
||||
unsigned long timer_expires;
|
||||
- timer expiration time, in jiffies (if this is different from zero, the
|
||||
timer is running and will expire at that time, otherwise the timer is not
|
||||
running)
|
||||
|
||||
struct work_struct work;
|
||||
- work structure used for queuing up requests (i.e. work items in pm_wq)
|
||||
|
||||
wait_queue_head_t wait_queue;
|
||||
- wait queue used if any of the helper functions needs to wait for another
|
||||
one to complete
|
||||
|
||||
spinlock_t lock;
|
||||
- lock used for synchronisation
|
||||
|
||||
atomic_t usage_count;
|
||||
- the usage counter of the device
|
||||
|
||||
atomic_t child_count;
|
||||
- the count of 'active' children of the device
|
||||
|
||||
unsigned int ignore_children;
|
||||
- if set, the value of child_count is ignored (but still updated)
|
||||
|
||||
unsigned int disable_depth;
|
||||
- used for disabling the helper funcions (they work normally if this is
|
||||
equal to zero); the initial value of it is 1 (i.e. run-time PM is
|
||||
initially disabled for all devices)
|
||||
|
||||
unsigned int runtime_error;
|
||||
- if set, there was a fatal error (one of the callbacks returned error code
|
||||
as described in Section 2), so the helper funtions will not work until
|
||||
this flag is cleared; this is the error code returned by the failing
|
||||
callback
|
||||
|
||||
unsigned int idle_notification;
|
||||
- if set, ->runtime_idle() is being executed
|
||||
|
||||
unsigned int request_pending;
|
||||
- if set, there's a pending request (i.e. a work item queued up into pm_wq)
|
||||
|
||||
enum rpm_request request;
|
||||
- type of request that's pending (valid if request_pending is set)
|
||||
|
||||
unsigned int deferred_resume;
|
||||
- set if ->runtime_resume() is about to be run while ->runtime_suspend() is
|
||||
being executed for that device and it is not practical to wait for the
|
||||
suspend to complete; means "start a resume as soon as you've suspended"
|
||||
|
||||
enum rpm_status runtime_status;
|
||||
- the run-time PM status of the device; this field's initial value is
|
||||
RPM_SUSPENDED, which means that each device is initially regarded by the
|
||||
PM core as 'suspended', regardless of its real hardware status
|
||||
|
||||
All of the above fields are members of the 'power' member of 'struct device'.
|
||||
|
||||
4. Run-time PM Device Helper Functions
|
||||
|
||||
The following run-time PM helper functions are defined in
|
||||
drivers/base/power/runtime.c and include/linux/pm_runtime.h:
|
||||
|
||||
void pm_runtime_init(struct device *dev);
|
||||
- initialize the device run-time PM fields in 'struct dev_pm_info'
|
||||
|
||||
void pm_runtime_remove(struct device *dev);
|
||||
- make sure that the run-time PM of the device will be disabled after
|
||||
removing the device from device hierarchy
|
||||
|
||||
int pm_runtime_idle(struct device *dev);
|
||||
- execute ->runtime_idle() for the device's bus type; returns 0 on success
|
||||
or error code on failure, where -EINPROGRESS means that ->runtime_idle()
|
||||
is already being executed
|
||||
|
||||
int pm_runtime_suspend(struct device *dev);
|
||||
- execute ->runtime_suspend() for the device's bus type; returns 0 on
|
||||
success, 1 if the device's run-time PM status was already 'suspended', or
|
||||
error code on failure, where -EAGAIN or -EBUSY means it is safe to attempt
|
||||
to suspend the device again in future
|
||||
|
||||
int pm_runtime_resume(struct device *dev);
|
||||
- execute ->runtime_resume() for the device's bus type; returns 0 on
|
||||
success, 1 if the device's run-time PM status was already 'active' or
|
||||
error code on failure, where -EAGAIN means it may be safe to attempt to
|
||||
resume the device again in future, but 'power.runtime_error' should be
|
||||
checked additionally
|
||||
|
||||
int pm_request_idle(struct device *dev);
|
||||
- submit a request to execute ->runtime_idle() for the device's bus type
|
||||
(the request is represented by a work item in pm_wq); returns 0 on success
|
||||
or error code if the request has not been queued up
|
||||
|
||||
int pm_schedule_suspend(struct device *dev, unsigned int delay);
|
||||
- schedule the execution of ->runtime_suspend() for the device's bus type
|
||||
in future, where 'delay' is the time to wait before queuing up a suspend
|
||||
work item in pm_wq, in milliseconds (if 'delay' is zero, the work item is
|
||||
queued up immediately); returns 0 on success, 1 if the device's PM
|
||||
run-time status was already 'suspended', or error code if the request
|
||||
hasn't been scheduled (or queued up if 'delay' is 0); if the execution of
|
||||
->runtime_suspend() is already scheduled and not yet expired, the new
|
||||
value of 'delay' will be used as the time to wait
|
||||
|
||||
int pm_request_resume(struct device *dev);
|
||||
- submit a request to execute ->runtime_resume() for the device's bus type
|
||||
(the request is represented by a work item in pm_wq); returns 0 on
|
||||
success, 1 if the device's run-time PM status was already 'active', or
|
||||
error code if the request hasn't been queued up
|
||||
|
||||
void pm_runtime_get_noresume(struct device *dev);
|
||||
- increment the device's usage counter
|
||||
|
||||
int pm_runtime_get(struct device *dev);
|
||||
- increment the device's usage counter, run pm_request_resume(dev) and
|
||||
return its result
|
||||
|
||||
int pm_runtime_get_sync(struct device *dev);
|
||||
- increment the device's usage counter, run pm_runtime_resume(dev) and
|
||||
return its result
|
||||
|
||||
void pm_runtime_put_noidle(struct device *dev);
|
||||
- decrement the device's usage counter
|
||||
|
||||
int pm_runtime_put(struct device *dev);
|
||||
- decrement the device's usage counter, run pm_request_idle(dev) and return
|
||||
its result
|
||||
|
||||
int pm_runtime_put_sync(struct device *dev);
|
||||
- decrement the device's usage counter, run pm_runtime_idle(dev) and return
|
||||
its result
|
||||
|
||||
void pm_runtime_enable(struct device *dev);
|
||||
- enable the run-time PM helper functions to run the device bus type's
|
||||
run-time PM callbacks described in Section 2
|
||||
|
||||
int pm_runtime_disable(struct device *dev);
|
||||
- prevent the run-time PM helper functions from running the device bus
|
||||
type's run-time PM callbacks, make sure that all of the pending run-time
|
||||
PM operations on the device are either completed or canceled; returns
|
||||
1 if there was a resume request pending and it was necessary to execute
|
||||
->runtime_resume() for the device's bus type to satisfy that request,
|
||||
otherwise 0 is returned
|
||||
|
||||
void pm_suspend_ignore_children(struct device *dev, bool enable);
|
||||
- set/unset the power.ignore_children flag of the device
|
||||
|
||||
int pm_runtime_set_active(struct device *dev);
|
||||
- clear the device's 'power.runtime_error' flag, set the device's run-time
|
||||
PM status to 'active' and update its parent's counter of 'active'
|
||||
children as appropriate (it is only valid to use this function if
|
||||
'power.runtime_error' is set or 'power.disable_depth' is greater than
|
||||
zero); it will fail and return error code if the device has a parent
|
||||
which is not active and the 'power.ignore_children' flag of which is unset
|
||||
|
||||
void pm_runtime_set_suspended(struct device *dev);
|
||||
- clear the device's 'power.runtime_error' flag, set the device's run-time
|
||||
PM status to 'suspended' and update its parent's counter of 'active'
|
||||
children as appropriate (it is only valid to use this function if
|
||||
'power.runtime_error' is set or 'power.disable_depth' is greater than
|
||||
zero)
|
||||
|
||||
It is safe to execute the following helper functions from interrupt context:
|
||||
|
||||
pm_request_idle()
|
||||
pm_schedule_suspend()
|
||||
pm_request_resume()
|
||||
pm_runtime_get_noresume()
|
||||
pm_runtime_get()
|
||||
pm_runtime_put_noidle()
|
||||
pm_runtime_put()
|
||||
pm_suspend_ignore_children()
|
||||
pm_runtime_set_active()
|
||||
pm_runtime_set_suspended()
|
||||
pm_runtime_enable()
|
||||
|
||||
5. Run-time PM Initialization, Device Probing and Removal
|
||||
|
||||
Initially, the run-time PM is disabled for all devices, which means that the
|
||||
majority of the run-time PM helper funtions described in Section 4 will return
|
||||
-EAGAIN until pm_runtime_enable() is called for the device.
|
||||
|
||||
In addition to that, the initial run-time PM status of all devices is
|
||||
'suspended', but it need not reflect the actual physical state of the device.
|
||||
Thus, if the device is initially active (i.e. it is able to process I/O), its
|
||||
run-time PM status must be changed to 'active', with the help of
|
||||
pm_runtime_set_active(), before pm_runtime_enable() is called for the device.
|
||||
|
||||
However, if the device has a parent and the parent's run-time PM is enabled,
|
||||
calling pm_runtime_set_active() for the device will affect the parent, unless
|
||||
the parent's 'power.ignore_children' flag is set. Namely, in that case the
|
||||
parent won't be able to suspend at run time, using the PM core's helper
|
||||
functions, as long as the child's status is 'active', even if the child's
|
||||
run-time PM is still disabled (i.e. pm_runtime_enable() hasn't been called for
|
||||
the child yet or pm_runtime_disable() has been called for it). For this reason,
|
||||
once pm_runtime_set_active() has been called for the device, pm_runtime_enable()
|
||||
should be called for it too as soon as reasonably possible or its run-time PM
|
||||
status should be changed back to 'suspended' with the help of
|
||||
pm_runtime_set_suspended().
|
||||
|
||||
If the default initial run-time PM status of the device (i.e. 'suspended')
|
||||
reflects the actual state of the device, its bus type's or its driver's
|
||||
->probe() callback will likely need to wake it up using one of the PM core's
|
||||
helper functions described in Section 4. In that case, pm_runtime_resume()
|
||||
should be used. Of course, for this purpose the device's run-time PM has to be
|
||||
enabled earlier by calling pm_runtime_enable().
|
||||
|
||||
If the device bus type's or driver's ->probe() or ->remove() callback runs
|
||||
pm_runtime_suspend() or pm_runtime_idle() or their asynchronous counterparts,
|
||||
they will fail returning -EAGAIN, because the device's usage counter is
|
||||
incremented by the core before executing ->probe() and ->remove(). Still, it
|
||||
may be desirable to suspend the device as soon as ->probe() or ->remove() has
|
||||
finished, so the PM core uses pm_runtime_idle_sync() to invoke the device bus
|
||||
type's ->runtime_idle() callback at that time.
|
@ -12,4 +12,7 @@ struct dev_archdata {
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pdev_archdata {
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -281,24 +281,27 @@ static int /* __init */ fpga_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fpga_suspend_late(struct platform_device *pdev, pm_message_t mesg)
|
||||
static int fpga_suspend_noirq(struct device *dev)
|
||||
{
|
||||
__raw_writew(~0, &fpga->leds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fpga_resume_early(struct platform_device *pdev)
|
||||
static int fpga_resume_noirq(struct device *dev)
|
||||
{
|
||||
__raw_writew(~hw_led_state, &fpga->leds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_pm_ops fpga_dev_pm_ops = {
|
||||
.suspend_noirq = fpga_suspend_noirq,
|
||||
.resume_noirq = fpga_resume_noirq,
|
||||
};
|
||||
|
||||
static struct platform_driver led_driver = {
|
||||
.driver.name = "omap_dbg_led",
|
||||
.driver.pm = &fpga_dev_pm_ops,
|
||||
.probe = fpga_probe,
|
||||
.suspend_late = fpga_suspend_late,
|
||||
.resume_early = fpga_resume_early,
|
||||
};
|
||||
|
||||
static int __init fpga_init(void)
|
||||
|
@ -1418,8 +1418,9 @@ static struct irq_chip mpuio_irq_chip = {
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
static int omap_mpuio_suspend_late(struct platform_device *pdev, pm_message_t mesg)
|
||||
static int omap_mpuio_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct gpio_bank *bank = platform_get_drvdata(pdev);
|
||||
void __iomem *mask_reg = bank->base + OMAP_MPUIO_GPIO_MASKIT;
|
||||
unsigned long flags;
|
||||
@ -1432,8 +1433,9 @@ static int omap_mpuio_suspend_late(struct platform_device *pdev, pm_message_t me
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_mpuio_resume_early(struct platform_device *pdev)
|
||||
static int omap_mpuio_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct gpio_bank *bank = platform_get_drvdata(pdev);
|
||||
void __iomem *mask_reg = bank->base + OMAP_MPUIO_GPIO_MASKIT;
|
||||
unsigned long flags;
|
||||
@ -1445,14 +1447,18 @@ static int omap_mpuio_resume_early(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_pm_ops omap_mpuio_dev_pm_ops = {
|
||||
.suspend_noirq = omap_mpuio_suspend_noirq,
|
||||
.resume_noirq = omap_mpuio_resume_noirq,
|
||||
};
|
||||
|
||||
/* use platform_driver for this, now that there's no longer any
|
||||
* point to sys_device (other than not disturbing old code).
|
||||
*/
|
||||
static struct platform_driver omap_mpuio_driver = {
|
||||
.suspend_late = omap_mpuio_suspend_late,
|
||||
.resume_early = omap_mpuio_resume_early,
|
||||
.driver = {
|
||||
.name = "mpuio",
|
||||
.pm = &omap_mpuio_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -15,4 +15,7 @@ struct dev_archdata {
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pdev_archdata {
|
||||
};
|
||||
|
||||
#endif /* _ASM_IA64_DEVICE_H */
|
||||
|
@ -16,6 +16,9 @@ struct dev_archdata {
|
||||
struct device_node *of_node;
|
||||
};
|
||||
|
||||
struct pdev_archdata {
|
||||
};
|
||||
|
||||
#endif /* _ASM_MICROBLAZE_DEVICE_H */
|
||||
|
||||
|
||||
|
@ -30,4 +30,7 @@ dev_archdata_get_node(const struct dev_archdata *ad)
|
||||
return ad->of_node;
|
||||
}
|
||||
|
||||
struct pdev_archdata {
|
||||
};
|
||||
|
||||
#endif /* _ASM_POWERPC_DEVICE_H */
|
||||
|
@ -32,4 +32,7 @@ dev_archdata_get_node(const struct dev_archdata *ad)
|
||||
return ad->prom_node;
|
||||
}
|
||||
|
||||
struct pdev_archdata {
|
||||
};
|
||||
|
||||
#endif /* _ASM_SPARC_DEVICE_H */
|
||||
|
@ -13,4 +13,7 @@ struct dma_map_ops *dma_ops;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pdev_archdata {
|
||||
};
|
||||
|
||||
#endif /* _ASM_X86_DEVICE_H */
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/async.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "base.h"
|
||||
#include "power/power.h"
|
||||
@ -202,7 +203,10 @@ int driver_probe_device(struct device_driver *drv, struct device *dev)
|
||||
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
|
||||
drv->bus->name, __func__, dev_name(dev), drv->name);
|
||||
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_barrier(dev);
|
||||
ret = really_probe(dev, drv);
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -245,7 +249,9 @@ int device_attach(struct device *dev)
|
||||
ret = 0;
|
||||
}
|
||||
} else {
|
||||
pm_runtime_get_noresume(dev);
|
||||
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
|
||||
pm_runtime_put_sync(dev);
|
||||
}
|
||||
up(&dev->sem);
|
||||
return ret;
|
||||
@ -306,6 +312,9 @@ static void __device_release_driver(struct device *dev)
|
||||
|
||||
drv = dev->driver;
|
||||
if (drv) {
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_barrier(dev);
|
||||
|
||||
driver_sysfs_remove(dev);
|
||||
|
||||
if (dev->bus)
|
||||
@ -324,6 +333,8 @@ static void __device_release_driver(struct device *dev)
|
||||
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
|
||||
BUS_NOTIFY_UNBOUND_DRIVER,
|
||||
dev);
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "base.h"
|
||||
|
||||
@ -625,30 +626,6 @@ static int platform_legacy_suspend(struct device *dev, pm_message_t mesg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int platform_legacy_suspend_late(struct device *dev, pm_message_t mesg)
|
||||
{
|
||||
struct platform_driver *pdrv = to_platform_driver(dev->driver);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (dev->driver && pdrv->suspend_late)
|
||||
ret = pdrv->suspend_late(pdev, mesg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int platform_legacy_resume_early(struct device *dev)
|
||||
{
|
||||
struct platform_driver *pdrv = to_platform_driver(dev->driver);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (dev->driver && pdrv->resume_early)
|
||||
ret = pdrv->resume_early(pdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int platform_legacy_resume(struct device *dev)
|
||||
{
|
||||
struct platform_driver *pdrv = to_platform_driver(dev->driver);
|
||||
@ -680,6 +657,13 @@ static void platform_pm_complete(struct device *dev)
|
||||
drv->pm->complete(dev);
|
||||
}
|
||||
|
||||
#else /* !CONFIG_PM_SLEEP */
|
||||
|
||||
#define platform_pm_prepare NULL
|
||||
#define platform_pm_complete NULL
|
||||
|
||||
#endif /* !CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
|
||||
static int platform_pm_suspend(struct device *dev)
|
||||
@ -711,8 +695,6 @@ static int platform_pm_suspend_noirq(struct device *dev)
|
||||
if (drv->pm) {
|
||||
if (drv->pm->suspend_noirq)
|
||||
ret = drv->pm->suspend_noirq(dev);
|
||||
} else {
|
||||
ret = platform_legacy_suspend_late(dev, PMSG_SUSPEND);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -747,8 +729,6 @@ static int platform_pm_resume_noirq(struct device *dev)
|
||||
if (drv->pm) {
|
||||
if (drv->pm->resume_noirq)
|
||||
ret = drv->pm->resume_noirq(dev);
|
||||
} else {
|
||||
ret = platform_legacy_resume_early(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -794,8 +774,6 @@ static int platform_pm_freeze_noirq(struct device *dev)
|
||||
if (drv->pm) {
|
||||
if (drv->pm->freeze_noirq)
|
||||
ret = drv->pm->freeze_noirq(dev);
|
||||
} else {
|
||||
ret = platform_legacy_suspend_late(dev, PMSG_FREEZE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -830,8 +808,6 @@ static int platform_pm_thaw_noirq(struct device *dev)
|
||||
if (drv->pm) {
|
||||
if (drv->pm->thaw_noirq)
|
||||
ret = drv->pm->thaw_noirq(dev);
|
||||
} else {
|
||||
ret = platform_legacy_resume_early(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -866,8 +842,6 @@ static int platform_pm_poweroff_noirq(struct device *dev)
|
||||
if (drv->pm) {
|
||||
if (drv->pm->poweroff_noirq)
|
||||
ret = drv->pm->poweroff_noirq(dev);
|
||||
} else {
|
||||
ret = platform_legacy_suspend_late(dev, PMSG_HIBERNATE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -902,8 +876,6 @@ static int platform_pm_restore_noirq(struct device *dev)
|
||||
if (drv->pm) {
|
||||
if (drv->pm->restore_noirq)
|
||||
ret = drv->pm->restore_noirq(dev);
|
||||
} else {
|
||||
ret = platform_legacy_resume_early(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -922,6 +894,31 @@ static int platform_pm_restore_noirq(struct device *dev)
|
||||
|
||||
#endif /* !CONFIG_HIBERNATION */
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
int __weak platform_pm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
return -ENOSYS;
|
||||
};
|
||||
|
||||
int __weak platform_pm_runtime_resume(struct device *dev)
|
||||
{
|
||||
return -ENOSYS;
|
||||
};
|
||||
|
||||
int __weak platform_pm_runtime_idle(struct device *dev)
|
||||
{
|
||||
return -ENOSYS;
|
||||
};
|
||||
|
||||
#else /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
#define platform_pm_runtime_suspend NULL
|
||||
#define platform_pm_runtime_resume NULL
|
||||
#define platform_pm_runtime_idle NULL
|
||||
|
||||
#endif /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
static const struct dev_pm_ops platform_dev_pm_ops = {
|
||||
.prepare = platform_pm_prepare,
|
||||
.complete = platform_pm_complete,
|
||||
@ -937,22 +934,17 @@ static const struct dev_pm_ops platform_dev_pm_ops = {
|
||||
.thaw_noirq = platform_pm_thaw_noirq,
|
||||
.poweroff_noirq = platform_pm_poweroff_noirq,
|
||||
.restore_noirq = platform_pm_restore_noirq,
|
||||
.runtime_suspend = platform_pm_runtime_suspend,
|
||||
.runtime_resume = platform_pm_runtime_resume,
|
||||
.runtime_idle = platform_pm_runtime_idle,
|
||||
};
|
||||
|
||||
#define PLATFORM_PM_OPS_PTR (&platform_dev_pm_ops)
|
||||
|
||||
#else /* !CONFIG_PM_SLEEP */
|
||||
|
||||
#define PLATFORM_PM_OPS_PTR NULL
|
||||
|
||||
#endif /* !CONFIG_PM_SLEEP */
|
||||
|
||||
struct bus_type platform_bus_type = {
|
||||
.name = "platform",
|
||||
.dev_attrs = platform_dev_attrs,
|
||||
.match = platform_match,
|
||||
.uevent = platform_uevent,
|
||||
.pm = PLATFORM_PM_OPS_PTR,
|
||||
.pm = &platform_dev_pm_ops,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(platform_bus_type);
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
obj-$(CONFIG_PM) += sysfs.o
|
||||
obj-$(CONFIG_PM_SLEEP) += main.o
|
||||
obj-$(CONFIG_PM_RUNTIME) += runtime.o
|
||||
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
|
||||
|
||||
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/resume-trace.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -49,7 +50,17 @@ static DEFINE_MUTEX(dpm_list_mtx);
|
||||
static bool transition_started;
|
||||
|
||||
/**
|
||||
* device_pm_lock - lock the list of active devices used by the PM core
|
||||
* device_pm_init - Initialize the PM-related part of a device object.
|
||||
* @dev: Device object being initialized.
|
||||
*/
|
||||
void device_pm_init(struct device *dev)
|
||||
{
|
||||
dev->power.status = DPM_ON;
|
||||
pm_runtime_init(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* device_pm_lock - Lock the list of active devices used by the PM core.
|
||||
*/
|
||||
void device_pm_lock(void)
|
||||
{
|
||||
@ -57,7 +68,7 @@ void device_pm_lock(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* device_pm_unlock - unlock the list of active devices used by the PM core
|
||||
* device_pm_unlock - Unlock the list of active devices used by the PM core.
|
||||
*/
|
||||
void device_pm_unlock(void)
|
||||
{
|
||||
@ -65,8 +76,8 @@ void device_pm_unlock(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* device_pm_add - add a device to the list of active devices
|
||||
* @dev: Device to be added to the list
|
||||
* device_pm_add - Add a device to the PM core's list of active devices.
|
||||
* @dev: Device to add to the list.
|
||||
*/
|
||||
void device_pm_add(struct device *dev)
|
||||
{
|
||||
@ -92,10 +103,8 @@ void device_pm_add(struct device *dev)
|
||||
}
|
||||
|
||||
/**
|
||||
* device_pm_remove - remove a device from the list of active devices
|
||||
* @dev: Device to be removed from the list
|
||||
*
|
||||
* This function also removes the device's PM-related sysfs attributes.
|
||||
* device_pm_remove - Remove a device from the PM core's list of active devices.
|
||||
* @dev: Device to be removed from the list.
|
||||
*/
|
||||
void device_pm_remove(struct device *dev)
|
||||
{
|
||||
@ -105,12 +114,13 @@ void device_pm_remove(struct device *dev)
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
list_del_init(&dev->power.entry);
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
pm_runtime_remove(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* device_pm_move_before - move device in dpm_list
|
||||
* @deva: Device to move in dpm_list
|
||||
* @devb: Device @deva should come before
|
||||
* device_pm_move_before - Move device in the PM core's list of active devices.
|
||||
* @deva: Device to move in dpm_list.
|
||||
* @devb: Device @deva should come before.
|
||||
*/
|
||||
void device_pm_move_before(struct device *deva, struct device *devb)
|
||||
{
|
||||
@ -124,9 +134,9 @@ void device_pm_move_before(struct device *deva, struct device *devb)
|
||||
}
|
||||
|
||||
/**
|
||||
* device_pm_move_after - move device in dpm_list
|
||||
* @deva: Device to move in dpm_list
|
||||
* @devb: Device @deva should come after
|
||||
* device_pm_move_after - Move device in the PM core's list of active devices.
|
||||
* @deva: Device to move in dpm_list.
|
||||
* @devb: Device @deva should come after.
|
||||
*/
|
||||
void device_pm_move_after(struct device *deva, struct device *devb)
|
||||
{
|
||||
@ -140,8 +150,8 @@ void device_pm_move_after(struct device *deva, struct device *devb)
|
||||
}
|
||||
|
||||
/**
|
||||
* device_pm_move_last - move device to end of dpm_list
|
||||
* @dev: Device to move in dpm_list
|
||||
* device_pm_move_last - Move device to end of the PM core's list of devices.
|
||||
* @dev: Device to move in dpm_list.
|
||||
*/
|
||||
void device_pm_move_last(struct device *dev)
|
||||
{
|
||||
@ -152,10 +162,10 @@ void device_pm_move_last(struct device *dev)
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_op - execute the PM operation appropiate for given PM event
|
||||
* @dev: Device.
|
||||
* @ops: PM operations to choose from.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* pm_op - Execute the PM operation appropriate for given PM event.
|
||||
* @dev: Device to handle.
|
||||
* @ops: PM operations to choose from.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*/
|
||||
static int pm_op(struct device *dev,
|
||||
const struct dev_pm_ops *ops,
|
||||
@ -213,13 +223,13 @@ static int pm_op(struct device *dev,
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_noirq_op - execute the PM operation appropiate for given PM event
|
||||
* @dev: Device.
|
||||
* @ops: PM operations to choose from.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* pm_noirq_op - Execute the PM operation appropriate for given PM event.
|
||||
* @dev: Device to handle.
|
||||
* @ops: PM operations to choose from.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* The operation is executed with interrupts disabled by the only remaining
|
||||
* functional CPU in the system.
|
||||
* The driver of @dev will not receive interrupts while this function is being
|
||||
* executed.
|
||||
*/
|
||||
static int pm_noirq_op(struct device *dev,
|
||||
const struct dev_pm_ops *ops,
|
||||
@ -317,11 +327,12 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info,
|
||||
/*------------------------- Resume routines -------------------------*/
|
||||
|
||||
/**
|
||||
* device_resume_noirq - Power on one device (early resume).
|
||||
* @dev: Device.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* device_resume_noirq - Execute an "early resume" callback for given device.
|
||||
* @dev: Device to handle.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Must be called with interrupts disabled.
|
||||
* The driver of @dev will not receive interrupts while this function is being
|
||||
* executed.
|
||||
*/
|
||||
static int device_resume_noirq(struct device *dev, pm_message_t state)
|
||||
{
|
||||
@ -343,20 +354,18 @@ static int device_resume_noirq(struct device *dev, pm_message_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_resume_noirq - Power on all regular (non-sysdev) devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* dpm_resume_noirq - Execute "early resume" callbacks for non-sysdev devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Call the "noirq" resume handlers for all devices marked as
|
||||
* DPM_OFF_IRQ and enable device drivers to receive interrupts.
|
||||
*
|
||||
* Must be called under dpm_list_mtx. Device drivers should not receive
|
||||
* interrupts while it's being executed.
|
||||
* Call the "noirq" resume handlers for all devices marked as DPM_OFF_IRQ and
|
||||
* enable device drivers to receive interrupts.
|
||||
*/
|
||||
void dpm_resume_noirq(pm_message_t state)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
transition_started = false;
|
||||
list_for_each_entry(dev, &dpm_list, power.entry)
|
||||
if (dev->power.status > DPM_OFF) {
|
||||
int error;
|
||||
@ -372,9 +381,9 @@ void dpm_resume_noirq(pm_message_t state)
|
||||
EXPORT_SYMBOL_GPL(dpm_resume_noirq);
|
||||
|
||||
/**
|
||||
* device_resume - Restore state for one device.
|
||||
* @dev: Device.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* device_resume - Execute "resume" callbacks for given device.
|
||||
* @dev: Device to handle.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*/
|
||||
static int device_resume(struct device *dev, pm_message_t state)
|
||||
{
|
||||
@ -423,11 +432,11 @@ static int device_resume(struct device *dev, pm_message_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_resume - Resume every device.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* dpm_resume - Execute "resume" callbacks for non-sysdev devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Execute the appropriate "resume" callback for all devices the status of
|
||||
* which indicates that they are inactive.
|
||||
* Execute the appropriate "resume" callback for all devices whose status
|
||||
* indicates that they are suspended.
|
||||
*/
|
||||
static void dpm_resume(pm_message_t state)
|
||||
{
|
||||
@ -435,7 +444,6 @@ static void dpm_resume(pm_message_t state)
|
||||
|
||||
INIT_LIST_HEAD(&list);
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
transition_started = false;
|
||||
while (!list_empty(&dpm_list)) {
|
||||
struct device *dev = to_device(dpm_list.next);
|
||||
|
||||
@ -464,9 +472,9 @@ static void dpm_resume(pm_message_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* device_complete - Complete a PM transition for given device
|
||||
* @dev: Device.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* device_complete - Complete a PM transition for given device.
|
||||
* @dev: Device to handle.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*/
|
||||
static void device_complete(struct device *dev, pm_message_t state)
|
||||
{
|
||||
@ -491,11 +499,11 @@ static void device_complete(struct device *dev, pm_message_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_complete - Complete a PM transition for all devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* dpm_complete - Complete a PM transition for all non-sysdev devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Execute the ->complete() callbacks for all devices that are not marked
|
||||
* as DPM_ON.
|
||||
* Execute the ->complete() callbacks for all devices whose PM status is not
|
||||
* DPM_ON (this allows new devices to be registered).
|
||||
*/
|
||||
static void dpm_complete(pm_message_t state)
|
||||
{
|
||||
@ -512,6 +520,7 @@ static void dpm_complete(pm_message_t state)
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
|
||||
device_complete(dev, state);
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
}
|
||||
@ -524,11 +533,11 @@ static void dpm_complete(pm_message_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_resume_end - Restore state of each device in system.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* dpm_resume_end - Execute "resume" callbacks and complete system transition.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Resume all the devices, unlock them all, and allow new
|
||||
* devices to be registered once again.
|
||||
* Execute "resume" callbacks for all devices and complete the PM transition of
|
||||
* the system.
|
||||
*/
|
||||
void dpm_resume_end(pm_message_t state)
|
||||
{
|
||||
@ -542,9 +551,11 @@ EXPORT_SYMBOL_GPL(dpm_resume_end);
|
||||
/*------------------------- Suspend routines -------------------------*/
|
||||
|
||||
/**
|
||||
* resume_event - return a PM message representing the resume event
|
||||
* corresponding to given sleep state.
|
||||
* @sleep_state: PM message representing a sleep state.
|
||||
* resume_event - Return a "resume" message for given "suspend" sleep state.
|
||||
* @sleep_state: PM message representing a sleep state.
|
||||
*
|
||||
* Return a PM message representing the resume event corresponding to given
|
||||
* sleep state.
|
||||
*/
|
||||
static pm_message_t resume_event(pm_message_t sleep_state)
|
||||
{
|
||||
@ -561,11 +572,12 @@ static pm_message_t resume_event(pm_message_t sleep_state)
|
||||
}
|
||||
|
||||
/**
|
||||
* device_suspend_noirq - Shut down one device (late suspend).
|
||||
* @dev: Device.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* device_suspend_noirq - Execute a "late suspend" callback for given device.
|
||||
* @dev: Device to handle.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* This is called with interrupts off and only a single CPU running.
|
||||
* The driver of @dev will not receive interrupts while this function is being
|
||||
* executed.
|
||||
*/
|
||||
static int device_suspend_noirq(struct device *dev, pm_message_t state)
|
||||
{
|
||||
@ -582,13 +594,11 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_suspend_noirq - Power down all regular (non-sysdev) devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* dpm_suspend_noirq - Execute "late suspend" callbacks for non-sysdev devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Prevent device drivers from receiving interrupts and call the "noirq"
|
||||
* suspend handlers.
|
||||
*
|
||||
* Must be called under dpm_list_mtx.
|
||||
* Prevent device drivers from receiving interrupts and call the "noirq" suspend
|
||||
* handlers for all non-sysdev devices.
|
||||
*/
|
||||
int dpm_suspend_noirq(pm_message_t state)
|
||||
{
|
||||
@ -613,9 +623,9 @@ int dpm_suspend_noirq(pm_message_t state)
|
||||
EXPORT_SYMBOL_GPL(dpm_suspend_noirq);
|
||||
|
||||
/**
|
||||
* device_suspend - Save state of one device.
|
||||
* @dev: Device.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* device_suspend - Execute "suspend" callbacks for given device.
|
||||
* @dev: Device to handle.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*/
|
||||
static int device_suspend(struct device *dev, pm_message_t state)
|
||||
{
|
||||
@ -662,10 +672,8 @@ static int device_suspend(struct device *dev, pm_message_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_suspend - Suspend every device.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Execute the appropriate "suspend" callbacks for all devices.
|
||||
* dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*/
|
||||
static int dpm_suspend(pm_message_t state)
|
||||
{
|
||||
@ -699,9 +707,12 @@ static int dpm_suspend(pm_message_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* device_prepare - Execute the ->prepare() callback(s) for given device.
|
||||
* @dev: Device.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* device_prepare - Prepare a device for system power transition.
|
||||
* @dev: Device to handle.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Execute the ->prepare() callback(s) for given device. No new children of the
|
||||
* device may be registered after this function has returned.
|
||||
*/
|
||||
static int device_prepare(struct device *dev, pm_message_t state)
|
||||
{
|
||||
@ -737,10 +748,10 @@ static int device_prepare(struct device *dev, pm_message_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_prepare - Prepare all devices for a PM transition.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* dpm_prepare - Prepare all non-sysdev devices for a system PM transition.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Execute the ->prepare() callback for all devices.
|
||||
* Execute the ->prepare() callback(s) for all devices.
|
||||
*/
|
||||
static int dpm_prepare(pm_message_t state)
|
||||
{
|
||||
@ -757,7 +768,14 @@ static int dpm_prepare(pm_message_t state)
|
||||
dev->power.status = DPM_PREPARING;
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
|
||||
error = device_prepare(dev, state);
|
||||
pm_runtime_get_noresume(dev);
|
||||
if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {
|
||||
/* Wake-up requested during system sleep transition. */
|
||||
pm_runtime_put_noidle(dev);
|
||||
error = -EBUSY;
|
||||
} else {
|
||||
error = device_prepare(dev, state);
|
||||
}
|
||||
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
if (error) {
|
||||
@ -784,10 +802,11 @@ static int dpm_prepare(pm_message_t state)
|
||||
}
|
||||
|
||||
/**
|
||||
* dpm_suspend_start - Save state and stop all devices in system.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* dpm_suspend_start - Prepare devices for PM transition and suspend them.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*
|
||||
* Prepare and suspend all devices.
|
||||
* Prepare all non-sysdev devices for system PM transition and execute "suspend"
|
||||
* callbacks for them.
|
||||
*/
|
||||
int dpm_suspend_start(pm_message_t state)
|
||||
{
|
||||
|
@ -1,7 +1,14 @@
|
||||
static inline void device_pm_init(struct device *dev)
|
||||
{
|
||||
dev->power.status = DPM_ON;
|
||||
}
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
extern void pm_runtime_init(struct device *dev);
|
||||
extern void pm_runtime_remove(struct device *dev);
|
||||
|
||||
#else /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
static inline void pm_runtime_init(struct device *dev) {}
|
||||
static inline void pm_runtime_remove(struct device *dev) {}
|
||||
|
||||
#endif /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
@ -16,23 +23,33 @@ static inline struct device *to_device(struct list_head *entry)
|
||||
return container_of(entry, struct device, power.entry);
|
||||
}
|
||||
|
||||
extern void device_pm_init(struct device *dev);
|
||||
extern void device_pm_add(struct device *);
|
||||
extern void device_pm_remove(struct device *);
|
||||
extern void device_pm_move_before(struct device *, struct device *);
|
||||
extern void device_pm_move_after(struct device *, struct device *);
|
||||
extern void device_pm_move_last(struct device *);
|
||||
|
||||
#else /* CONFIG_PM_SLEEP */
|
||||
#else /* !CONFIG_PM_SLEEP */
|
||||
|
||||
static inline void device_pm_init(struct device *dev)
|
||||
{
|
||||
pm_runtime_init(dev);
|
||||
}
|
||||
|
||||
static inline void device_pm_remove(struct device *dev)
|
||||
{
|
||||
pm_runtime_remove(dev);
|
||||
}
|
||||
|
||||
static inline void device_pm_add(struct device *dev) {}
|
||||
static inline void device_pm_remove(struct device *dev) {}
|
||||
static inline void device_pm_move_before(struct device *deva,
|
||||
struct device *devb) {}
|
||||
static inline void device_pm_move_after(struct device *deva,
|
||||
struct device *devb) {}
|
||||
static inline void device_pm_move_last(struct device *dev) {}
|
||||
|
||||
#endif
|
||||
#endif /* !CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
|
1011
drivers/base/power/runtime.c
Normal file
1011
drivers/base/power/runtime.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -4151,7 +4151,7 @@ static void floppy_device_release(struct device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static int floppy_resume(struct platform_device *dev)
|
||||
static int floppy_resume(struct device *dev)
|
||||
{
|
||||
int fdc;
|
||||
|
||||
@ -4162,10 +4162,15 @@ static int floppy_resume(struct platform_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver floppy_driver = {
|
||||
static struct dev_pm_ops floppy_pm_ops = {
|
||||
.resume = floppy_resume,
|
||||
.restore = floppy_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver floppy_driver = {
|
||||
.driver = {
|
||||
.name = "floppy",
|
||||
.pm = &floppy_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1166,32 +1166,37 @@ static void at_dma_shutdown(struct platform_device *pdev)
|
||||
clk_disable(atdma->clk);
|
||||
}
|
||||
|
||||
static int at_dma_suspend_late(struct platform_device *pdev, pm_message_t mesg)
|
||||
static int at_dma_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct at_dma *atdma = platform_get_drvdata(pdev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct at_dma *atdma = platform_get_drvdata(pdev);
|
||||
|
||||
at_dma_off(platform_get_drvdata(pdev));
|
||||
clk_disable(atdma->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at_dma_resume_early(struct platform_device *pdev)
|
||||
static int at_dma_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct at_dma *atdma = platform_get_drvdata(pdev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct at_dma *atdma = platform_get_drvdata(pdev);
|
||||
|
||||
clk_enable(atdma->clk);
|
||||
dma_writel(atdma, EN, AT_DMA_ENABLE);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static struct dev_pm_ops at_dma_dev_pm_ops = {
|
||||
.suspend_noirq = at_dma_suspend_noirq,
|
||||
.resume_noirq = at_dma_resume_noirq,
|
||||
};
|
||||
|
||||
static struct platform_driver at_dma_driver = {
|
||||
.remove = __exit_p(at_dma_remove),
|
||||
.shutdown = at_dma_shutdown,
|
||||
.suspend_late = at_dma_suspend_late,
|
||||
.resume_early = at_dma_resume_early,
|
||||
.driver = {
|
||||
.name = "at_hdmac",
|
||||
.pm = &at_dma_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1399,8 +1399,9 @@ static void dw_shutdown(struct platform_device *pdev)
|
||||
clk_disable(dw->clk);
|
||||
}
|
||||
|
||||
static int dw_suspend_late(struct platform_device *pdev, pm_message_t mesg)
|
||||
static int dw_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct dw_dma *dw = platform_get_drvdata(pdev);
|
||||
|
||||
dw_dma_off(platform_get_drvdata(pdev));
|
||||
@ -1408,23 +1409,27 @@ static int dw_suspend_late(struct platform_device *pdev, pm_message_t mesg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_resume_early(struct platform_device *pdev)
|
||||
static int dw_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct dw_dma *dw = platform_get_drvdata(pdev);
|
||||
|
||||
clk_enable(dw->clk);
|
||||
dma_writel(dw, CFG, DW_CFG_DMA_EN);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static struct dev_pm_ops dw_dev_pm_ops = {
|
||||
.suspend_noirq = dw_suspend_noirq,
|
||||
.resume_noirq = dw_resume_noirq,
|
||||
};
|
||||
|
||||
static struct platform_driver dw_driver = {
|
||||
.remove = __exit_p(dw_remove),
|
||||
.shutdown = dw_shutdown,
|
||||
.suspend_late = dw_suspend_late,
|
||||
.resume_early = dw_resume_early,
|
||||
.driver = {
|
||||
.name = "dw_dmac",
|
||||
.pm = &dw_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1291,17 +1291,18 @@ static void txx9dmac_shutdown(struct platform_device *pdev)
|
||||
txx9dmac_off(ddev);
|
||||
}
|
||||
|
||||
static int txx9dmac_suspend_late(struct platform_device *pdev,
|
||||
pm_message_t mesg)
|
||||
static int txx9dmac_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct txx9dmac_dev *ddev = platform_get_drvdata(pdev);
|
||||
|
||||
txx9dmac_off(ddev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int txx9dmac_resume_early(struct platform_device *pdev)
|
||||
static int txx9dmac_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct txx9dmac_dev *ddev = platform_get_drvdata(pdev);
|
||||
struct txx9dmac_platform_data *pdata = pdev->dev.platform_data;
|
||||
u32 mcr;
|
||||
@ -1314,6 +1315,11 @@ static int txx9dmac_resume_early(struct platform_device *pdev)
|
||||
|
||||
}
|
||||
|
||||
static struct dev_pm_ops txx9dmac_dev_pm_ops = {
|
||||
.suspend_noirq = txx9dmac_suspend_noirq,
|
||||
.resume_noirq = txx9dmac_resume_noirq,
|
||||
};
|
||||
|
||||
static struct platform_driver txx9dmac_chan_driver = {
|
||||
.remove = __exit_p(txx9dmac_chan_remove),
|
||||
.driver = {
|
||||
@ -1324,10 +1330,9 @@ static struct platform_driver txx9dmac_chan_driver = {
|
||||
static struct platform_driver txx9dmac_driver = {
|
||||
.remove = __exit_p(txx9dmac_remove),
|
||||
.shutdown = txx9dmac_shutdown,
|
||||
.suspend_late = txx9dmac_suspend_late,
|
||||
.resume_early = txx9dmac_resume_early,
|
||||
.driver = {
|
||||
.name = "txx9dmac",
|
||||
.pm = &txx9dmac_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1134,35 +1134,44 @@ static int __exit i2c_pxa_remove(struct platform_device *dev)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int i2c_pxa_suspend_late(struct platform_device *dev, pm_message_t state)
|
||||
static int i2c_pxa_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct pxa_i2c *i2c = platform_get_drvdata(dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct pxa_i2c *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable(i2c->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_pxa_resume_early(struct platform_device *dev)
|
||||
static int i2c_pxa_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct pxa_i2c *i2c = platform_get_drvdata(dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct pxa_i2c *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
clk_enable(i2c->clk);
|
||||
i2c_pxa_reset(i2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_pm_ops i2c_pxa_dev_pm_ops = {
|
||||
.suspend_noirq = i2c_pxa_suspend_noirq,
|
||||
.resume_noirq = i2c_pxa_resume_noirq,
|
||||
};
|
||||
|
||||
#define I2C_PXA_DEV_PM_OPS (&i2c_pxa_dev_pm_ops)
|
||||
#else
|
||||
#define i2c_pxa_suspend_late NULL
|
||||
#define i2c_pxa_resume_early NULL
|
||||
#define I2C_PXA_DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver i2c_pxa_driver = {
|
||||
.probe = i2c_pxa_probe,
|
||||
.remove = __exit_p(i2c_pxa_remove),
|
||||
.suspend_late = i2c_pxa_suspend_late,
|
||||
.resume_early = i2c_pxa_resume_early,
|
||||
.driver = {
|
||||
.name = "pxa2xx-i2c",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = I2C_PXA_DEV_PM_OPS,
|
||||
},
|
||||
.id_table = i2c_pxa_id_table,
|
||||
};
|
||||
|
@ -946,17 +946,20 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int s3c24xx_i2c_suspend_late(struct platform_device *dev,
|
||||
pm_message_t msg)
|
||||
static int s3c24xx_i2c_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct s3c24xx_i2c *i2c = platform_get_drvdata(dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
i2c->suspended = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c24xx_i2c_resume(struct platform_device *dev)
|
||||
static int s3c24xx_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct s3c24xx_i2c *i2c = platform_get_drvdata(dev);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
i2c->suspended = 0;
|
||||
s3c24xx_i2c_init(i2c);
|
||||
@ -964,9 +967,14 @@ static int s3c24xx_i2c_resume(struct platform_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = {
|
||||
.suspend_noirq = s3c24xx_i2c_suspend_noirq,
|
||||
.resume = s3c24xx_i2c_resume,
|
||||
};
|
||||
|
||||
#define S3C24XX_DEV_PM_OPS (&s3c24xx_i2c_dev_pm_ops)
|
||||
#else
|
||||
#define s3c24xx_i2c_suspend_late NULL
|
||||
#define s3c24xx_i2c_resume NULL
|
||||
#define S3C24XX_DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
/* device driver for platform bus bits */
|
||||
@ -985,12 +993,11 @@ MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
|
||||
static struct platform_driver s3c24xx_i2c_driver = {
|
||||
.probe = s3c24xx_i2c_probe,
|
||||
.remove = s3c24xx_i2c_remove,
|
||||
.suspend_late = s3c24xx_i2c_suspend_late,
|
||||
.resume = s3c24xx_i2c_resume,
|
||||
.id_table = s3c24xx_driver_ids,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "s3c-i2c",
|
||||
.pm = S3C24XX_DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -53,7 +53,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
|
||||
|
||||
static int __init hp_wmi_bios_setup(struct platform_device *device);
|
||||
static int __exit hp_wmi_bios_remove(struct platform_device *device);
|
||||
static int hp_wmi_resume_handler(struct platform_device *device);
|
||||
static int hp_wmi_resume_handler(struct device *device);
|
||||
|
||||
struct bios_args {
|
||||
u32 signature;
|
||||
@ -94,14 +94,19 @@ static struct rfkill *wifi_rfkill;
|
||||
static struct rfkill *bluetooth_rfkill;
|
||||
static struct rfkill *wwan_rfkill;
|
||||
|
||||
static struct dev_pm_ops hp_wmi_pm_ops = {
|
||||
.resume = hp_wmi_resume_handler,
|
||||
.restore = hp_wmi_resume_handler,
|
||||
};
|
||||
|
||||
static struct platform_driver hp_wmi_driver = {
|
||||
.driver = {
|
||||
.name = "hp-wmi",
|
||||
.owner = THIS_MODULE,
|
||||
.name = "hp-wmi",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &hp_wmi_pm_ops,
|
||||
},
|
||||
.probe = hp_wmi_bios_setup,
|
||||
.remove = hp_wmi_bios_remove,
|
||||
.resume = hp_wmi_resume_handler,
|
||||
};
|
||||
|
||||
static int hp_wmi_perform_query(int query, int write, int value)
|
||||
@ -512,7 +517,7 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hp_wmi_resume_handler(struct platform_device *device)
|
||||
static int hp_wmi_resume_handler(struct device *device)
|
||||
{
|
||||
/*
|
||||
* Hardware state may have changed while suspended, so trigger
|
||||
|
@ -2167,8 +2167,9 @@ static int __devexit musb_remove(struct platform_device *pdev)
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int musb_suspend(struct platform_device *pdev, pm_message_t message)
|
||||
static int musb_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
unsigned long flags;
|
||||
struct musb *musb = dev_to_musb(&pdev->dev);
|
||||
|
||||
@ -2195,8 +2196,9 @@ static int musb_suspend(struct platform_device *pdev, pm_message_t message)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int musb_resume_early(struct platform_device *pdev)
|
||||
static int musb_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct musb *musb = dev_to_musb(&pdev->dev);
|
||||
|
||||
if (!musb->clock)
|
||||
@ -2214,9 +2216,14 @@ static int musb_resume_early(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_pm_ops musb_dev_pm_ops = {
|
||||
.suspend = musb_suspend,
|
||||
.resume_noirq = musb_resume_noirq,
|
||||
};
|
||||
|
||||
#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops)
|
||||
#else
|
||||
#define musb_suspend NULL
|
||||
#define musb_resume_early NULL
|
||||
#define MUSB_DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver musb_driver = {
|
||||
@ -2224,11 +2231,10 @@ static struct platform_driver musb_driver = {
|
||||
.name = (char *)musb_driver_name,
|
||||
.bus = &platform_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = MUSB_DEV_PM_OPS,
|
||||
},
|
||||
.remove = __devexit_p(musb_remove),
|
||||
.shutdown = musb_shutdown,
|
||||
.suspend = musb_suspend,
|
||||
.resume_early = musb_resume_early,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -9,4 +9,7 @@
|
||||
struct dev_archdata {
|
||||
};
|
||||
|
||||
struct pdev_archdata {
|
||||
};
|
||||
|
||||
#endif /* _ASM_GENERIC_DEVICE_H */
|
||||
|
@ -22,6 +22,9 @@ struct platform_device {
|
||||
struct resource * resource;
|
||||
|
||||
struct platform_device_id *id_entry;
|
||||
|
||||
/* arch specific additions */
|
||||
struct pdev_archdata archdata;
|
||||
};
|
||||
|
||||
#define platform_get_device_id(pdev) ((pdev)->id_entry)
|
||||
@ -57,8 +60,6 @@ struct platform_driver {
|
||||
int (*remove)(struct platform_device *);
|
||||
void (*shutdown)(struct platform_device *);
|
||||
int (*suspend)(struct platform_device *, pm_message_t state);
|
||||
int (*suspend_late)(struct platform_device *, pm_message_t state);
|
||||
int (*resume_early)(struct platform_device *);
|
||||
int (*resume)(struct platform_device *);
|
||||
struct device_driver driver;
|
||||
struct platform_device_id *id_table;
|
||||
|
@ -22,6 +22,10 @@
|
||||
#define _LINUX_PM_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
/*
|
||||
* Callbacks for platform drivers to implement.
|
||||
@ -165,6 +169,28 @@ typedef struct pm_message {
|
||||
* It is allowed to unregister devices while the above callbacks are being
|
||||
* executed. However, it is not allowed to unregister a device from within any
|
||||
* of its own callbacks.
|
||||
*
|
||||
* There also are the following callbacks related to run-time power management
|
||||
* of devices:
|
||||
*
|
||||
* @runtime_suspend: Prepare the device for a condition in which it won't be
|
||||
* able to communicate with the CPU(s) and RAM due to power management.
|
||||
* This need not mean that the device should be put into a low power state.
|
||||
* For example, if the device is behind a link which is about to be turned
|
||||
* off, the device may remain at full power. If the device does go to low
|
||||
* power and if device_may_wakeup(dev) is true, remote wake-up (i.e., a
|
||||
* hardware mechanism allowing the device to request a change of its power
|
||||
* state, such as PCI PME) should be enabled for it.
|
||||
*
|
||||
* @runtime_resume: Put the device into the fully active state in response to a
|
||||
* wake-up event generated by hardware or at the request of software. If
|
||||
* necessary, put the device into the full power state and restore its
|
||||
* registers, so that it is fully operational.
|
||||
*
|
||||
* @runtime_idle: Device appears to be inactive and it might be put into a low
|
||||
* power state if all of the necessary conditions are satisfied. Check
|
||||
* these conditions and handle the device as appropriate, possibly queueing
|
||||
* a suspend request for it. The return value is ignored by the PM core.
|
||||
*/
|
||||
|
||||
struct dev_pm_ops {
|
||||
@ -182,8 +208,25 @@ struct dev_pm_ops {
|
||||
int (*thaw_noirq)(struct device *dev);
|
||||
int (*poweroff_noirq)(struct device *dev);
|
||||
int (*restore_noirq)(struct device *dev);
|
||||
int (*runtime_suspend)(struct device *dev);
|
||||
int (*runtime_resume)(struct device *dev);
|
||||
int (*runtime_idle)(struct device *dev);
|
||||
};
|
||||
|
||||
/*
|
||||
* Use this if you want to use the same suspend and resume callbacks for suspend
|
||||
* to RAM and hibernation.
|
||||
*/
|
||||
#define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \
|
||||
struct dev_pm_ops name = { \
|
||||
.suspend = suspend_fn, \
|
||||
.resume = resume_fn, \
|
||||
.freeze = suspend_fn, \
|
||||
.thaw = resume_fn, \
|
||||
.poweroff = suspend_fn, \
|
||||
.restore = resume_fn, \
|
||||
}
|
||||
|
||||
/**
|
||||
* PM_EVENT_ messages
|
||||
*
|
||||
@ -315,14 +358,80 @@ enum dpm_state {
|
||||
DPM_OFF_IRQ,
|
||||
};
|
||||
|
||||
/**
|
||||
* Device run-time power management status.
|
||||
*
|
||||
* These status labels are used internally by the PM core to indicate the
|
||||
* current status of a device with respect to the PM core operations. They do
|
||||
* not reflect the actual power state of the device or its status as seen by the
|
||||
* driver.
|
||||
*
|
||||
* RPM_ACTIVE Device is fully operational. Indicates that the device
|
||||
* bus type's ->runtime_resume() callback has completed
|
||||
* successfully.
|
||||
*
|
||||
* RPM_SUSPENDED Device bus type's ->runtime_suspend() callback has
|
||||
* completed successfully. The device is regarded as
|
||||
* suspended.
|
||||
*
|
||||
* RPM_RESUMING Device bus type's ->runtime_resume() callback is being
|
||||
* executed.
|
||||
*
|
||||
* RPM_SUSPENDING Device bus type's ->runtime_suspend() callback is being
|
||||
* executed.
|
||||
*/
|
||||
|
||||
enum rpm_status {
|
||||
RPM_ACTIVE = 0,
|
||||
RPM_RESUMING,
|
||||
RPM_SUSPENDED,
|
||||
RPM_SUSPENDING,
|
||||
};
|
||||
|
||||
/**
|
||||
* Device run-time power management request types.
|
||||
*
|
||||
* RPM_REQ_NONE Do nothing.
|
||||
*
|
||||
* RPM_REQ_IDLE Run the device bus type's ->runtime_idle() callback
|
||||
*
|
||||
* RPM_REQ_SUSPEND Run the device bus type's ->runtime_suspend() callback
|
||||
*
|
||||
* RPM_REQ_RESUME Run the device bus type's ->runtime_resume() callback
|
||||
*/
|
||||
|
||||
enum rpm_request {
|
||||
RPM_REQ_NONE = 0,
|
||||
RPM_REQ_IDLE,
|
||||
RPM_REQ_SUSPEND,
|
||||
RPM_REQ_RESUME,
|
||||
};
|
||||
|
||||
struct dev_pm_info {
|
||||
pm_message_t power_state;
|
||||
unsigned can_wakeup:1;
|
||||
unsigned should_wakeup:1;
|
||||
unsigned int can_wakeup:1;
|
||||
unsigned int should_wakeup:1;
|
||||
enum dpm_state status; /* Owned by the PM core */
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
struct list_head entry;
|
||||
#endif
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
struct timer_list suspend_timer;
|
||||
unsigned long timer_expires;
|
||||
struct work_struct work;
|
||||
wait_queue_head_t wait_queue;
|
||||
spinlock_t lock;
|
||||
atomic_t usage_count;
|
||||
atomic_t child_count;
|
||||
unsigned int disable_depth:3;
|
||||
unsigned int ignore_children:1;
|
||||
unsigned int idle_notification:1;
|
||||
unsigned int request_pending:1;
|
||||
unsigned int deferred_resume:1;
|
||||
enum rpm_request request;
|
||||
enum rpm_status runtime_status;
|
||||
int runtime_error;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
114
include/linux/pm_runtime.h
Normal file
114
include/linux/pm_runtime.h
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* pm_runtime.h - Device run-time power management helper functions.
|
||||
*
|
||||
* Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>
|
||||
*
|
||||
* This file is released under the GPLv2.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_PM_RUNTIME_H
|
||||
#define _LINUX_PM_RUNTIME_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
extern struct workqueue_struct *pm_wq;
|
||||
|
||||
extern int pm_runtime_idle(struct device *dev);
|
||||
extern int pm_runtime_suspend(struct device *dev);
|
||||
extern int pm_runtime_resume(struct device *dev);
|
||||
extern int pm_request_idle(struct device *dev);
|
||||
extern int pm_schedule_suspend(struct device *dev, unsigned int delay);
|
||||
extern int pm_request_resume(struct device *dev);
|
||||
extern int __pm_runtime_get(struct device *dev, bool sync);
|
||||
extern int __pm_runtime_put(struct device *dev, bool sync);
|
||||
extern int __pm_runtime_set_status(struct device *dev, unsigned int status);
|
||||
extern int pm_runtime_barrier(struct device *dev);
|
||||
extern void pm_runtime_enable(struct device *dev);
|
||||
extern void __pm_runtime_disable(struct device *dev, bool check_resume);
|
||||
|
||||
static inline bool pm_children_suspended(struct device *dev)
|
||||
{
|
||||
return dev->power.ignore_children
|
||||
|| !atomic_read(&dev->power.child_count);
|
||||
}
|
||||
|
||||
static inline void pm_suspend_ignore_children(struct device *dev, bool enable)
|
||||
{
|
||||
dev->power.ignore_children = enable;
|
||||
}
|
||||
|
||||
static inline void pm_runtime_get_noresume(struct device *dev)
|
||||
{
|
||||
atomic_inc(&dev->power.usage_count);
|
||||
}
|
||||
|
||||
static inline void pm_runtime_put_noidle(struct device *dev)
|
||||
{
|
||||
atomic_add_unless(&dev->power.usage_count, -1, 0);
|
||||
}
|
||||
|
||||
#else /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
static inline int pm_runtime_idle(struct device *dev) { return -ENOSYS; }
|
||||
static inline int pm_runtime_suspend(struct device *dev) { return -ENOSYS; }
|
||||
static inline int pm_runtime_resume(struct device *dev) { return 0; }
|
||||
static inline int pm_request_idle(struct device *dev) { return -ENOSYS; }
|
||||
static inline int pm_schedule_suspend(struct device *dev, unsigned int delay)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
static inline int pm_request_resume(struct device *dev) { return 0; }
|
||||
static inline int __pm_runtime_get(struct device *dev, bool sync) { return 1; }
|
||||
static inline int __pm_runtime_put(struct device *dev, bool sync) { return 0; }
|
||||
static inline int __pm_runtime_set_status(struct device *dev,
|
||||
unsigned int status) { return 0; }
|
||||
static inline int pm_runtime_barrier(struct device *dev) { return 0; }
|
||||
static inline void pm_runtime_enable(struct device *dev) {}
|
||||
static inline void __pm_runtime_disable(struct device *dev, bool c) {}
|
||||
|
||||
static inline bool pm_children_suspended(struct device *dev) { return false; }
|
||||
static inline void pm_suspend_ignore_children(struct device *dev, bool en) {}
|
||||
static inline void pm_runtime_get_noresume(struct device *dev) {}
|
||||
static inline void pm_runtime_put_noidle(struct device *dev) {}
|
||||
|
||||
#endif /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
static inline int pm_runtime_get(struct device *dev)
|
||||
{
|
||||
return __pm_runtime_get(dev, false);
|
||||
}
|
||||
|
||||
static inline int pm_runtime_get_sync(struct device *dev)
|
||||
{
|
||||
return __pm_runtime_get(dev, true);
|
||||
}
|
||||
|
||||
static inline int pm_runtime_put(struct device *dev)
|
||||
{
|
||||
return __pm_runtime_put(dev, false);
|
||||
}
|
||||
|
||||
static inline int pm_runtime_put_sync(struct device *dev)
|
||||
{
|
||||
return __pm_runtime_put(dev, true);
|
||||
}
|
||||
|
||||
static inline int pm_runtime_set_active(struct device *dev)
|
||||
{
|
||||
return __pm_runtime_set_status(dev, RPM_ACTIVE);
|
||||
}
|
||||
|
||||
static inline void pm_runtime_set_suspended(struct device *dev)
|
||||
{
|
||||
__pm_runtime_set_status(dev, RPM_SUSPENDED);
|
||||
}
|
||||
|
||||
static inline void pm_runtime_disable(struct device *dev)
|
||||
{
|
||||
__pm_runtime_disable(dev, true);
|
||||
}
|
||||
|
||||
#endif
|
@ -208,3 +208,17 @@ config APM_EMULATION
|
||||
random kernel OOPSes or reboots that don't seem to be related to
|
||||
anything, try disabling/enabling this option (or disabling/enabling
|
||||
APM in your BIOS).
|
||||
|
||||
config PM_RUNTIME
|
||||
bool "Run-time PM core functionality"
|
||||
depends on PM
|
||||
---help---
|
||||
Enable functionality allowing I/O devices to be put into energy-saving
|
||||
(low power) states at run time (or autosuspended) after a specified
|
||||
period of inactivity and woken up in response to a hardware-generated
|
||||
wake-up event or a driver's request.
|
||||
|
||||
Hardware support is generally required for this functionality to work
|
||||
and the bus type drivers of the buses the devices are on are
|
||||
responsible for the actual handling of the autosuspend requests and
|
||||
wake-up events.
|
||||
|
@ -298,8 +298,8 @@ int hibernation_snapshot(int platform_mode)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Free memory before shutting down devices. */
|
||||
error = swsusp_shrink_memory();
|
||||
/* Preallocate image memory before shutting down devices. */
|
||||
error = hibernate_preallocate_memory();
|
||||
if (error)
|
||||
goto Close;
|
||||
|
||||
@ -315,6 +315,10 @@ int hibernation_snapshot(int platform_mode)
|
||||
/* Control returns here after successful restore */
|
||||
|
||||
Resume_devices:
|
||||
/* We may need to release the preallocated image pages here. */
|
||||
if (error || !in_suspend)
|
||||
swsusp_free();
|
||||
|
||||
dpm_resume_end(in_suspend ?
|
||||
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
|
||||
resume_console();
|
||||
@ -460,11 +464,11 @@ int hibernation_platform_enter(void)
|
||||
|
||||
error = hibernation_ops->prepare();
|
||||
if (error)
|
||||
goto Platofrm_finish;
|
||||
goto Platform_finish;
|
||||
|
||||
error = disable_nonboot_cpus();
|
||||
if (error)
|
||||
goto Platofrm_finish;
|
||||
goto Platform_finish;
|
||||
|
||||
local_irq_disable();
|
||||
sysdev_suspend(PMSG_HIBERNATE);
|
||||
@ -476,7 +480,7 @@ int hibernation_platform_enter(void)
|
||||
* We don't need to reenable the nonboot CPUs or resume consoles, since
|
||||
* the system is going to be halted anyway.
|
||||
*/
|
||||
Platofrm_finish:
|
||||
Platform_finish:
|
||||
hibernation_ops->finish();
|
||||
|
||||
dpm_suspend_noirq(PMSG_RESTORE);
|
||||
@ -578,7 +582,10 @@ int hibernate(void)
|
||||
goto Thaw;
|
||||
|
||||
error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
|
||||
if (in_suspend && !error) {
|
||||
if (error)
|
||||
goto Thaw;
|
||||
|
||||
if (in_suspend) {
|
||||
unsigned int flags = 0;
|
||||
|
||||
if (hibernation_mode == HIBERNATION_PLATFORM)
|
||||
@ -590,8 +597,8 @@ int hibernate(void)
|
||||
power_down();
|
||||
} else {
|
||||
pr_debug("PM: Image restored successfully.\n");
|
||||
swsusp_free();
|
||||
}
|
||||
|
||||
Thaw:
|
||||
thaw_processes();
|
||||
Finish:
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/resume-trace.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "power.h"
|
||||
|
||||
@ -217,8 +218,24 @@ static struct attribute_group attr_group = {
|
||||
.attrs = g,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
struct workqueue_struct *pm_wq;
|
||||
|
||||
static int __init pm_start_workqueue(void)
|
||||
{
|
||||
pm_wq = create_freezeable_workqueue("pm");
|
||||
|
||||
return pm_wq ? 0 : -ENOMEM;
|
||||
}
|
||||
#else
|
||||
static inline int pm_start_workqueue(void) { return 0; }
|
||||
#endif
|
||||
|
||||
static int __init pm_init(void)
|
||||
{
|
||||
int error = pm_start_workqueue();
|
||||
if (error)
|
||||
return error;
|
||||
power_kobj = kobject_create_and_add("power", NULL);
|
||||
if (!power_kobj)
|
||||
return -ENOMEM;
|
||||
|
@ -74,7 +74,7 @@ extern asmlinkage int swsusp_arch_resume(void);
|
||||
|
||||
extern int create_basic_memory_bitmaps(void);
|
||||
extern void free_basic_memory_bitmaps(void);
|
||||
extern int swsusp_shrink_memory(void);
|
||||
extern int hibernate_preallocate_memory(void);
|
||||
|
||||
/**
|
||||
* Auxiliary structure used for reading the snapshot image data and
|
||||
|
@ -233,7 +233,7 @@ static void *chain_alloc(struct chain_allocator *ca, unsigned int size)
|
||||
|
||||
#define BM_END_OF_MAP (~0UL)
|
||||
|
||||
#define BM_BITS_PER_BLOCK (PAGE_SIZE << 3)
|
||||
#define BM_BITS_PER_BLOCK (PAGE_SIZE * BITS_PER_BYTE)
|
||||
|
||||
struct bm_block {
|
||||
struct list_head hook; /* hook into a list of bitmap blocks */
|
||||
@ -275,7 +275,7 @@ static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free);
|
||||
|
||||
/**
|
||||
* create_bm_block_list - create a list of block bitmap objects
|
||||
* @nr_blocks - number of blocks to allocate
|
||||
* @pages - number of pages to track
|
||||
* @list - list to put the allocated blocks into
|
||||
* @ca - chain allocator to be used for allocating memory
|
||||
*/
|
||||
@ -853,7 +853,7 @@ static unsigned int count_highmem_pages(void)
|
||||
struct zone *zone;
|
||||
unsigned int n = 0;
|
||||
|
||||
for_each_zone(zone) {
|
||||
for_each_populated_zone(zone) {
|
||||
unsigned long pfn, max_zone_pfn;
|
||||
|
||||
if (!is_highmem(zone))
|
||||
@ -916,7 +916,7 @@ static unsigned int count_data_pages(void)
|
||||
unsigned long pfn, max_zone_pfn;
|
||||
unsigned int n = 0;
|
||||
|
||||
for_each_zone(zone) {
|
||||
for_each_populated_zone(zone) {
|
||||
if (is_highmem(zone))
|
||||
continue;
|
||||
|
||||
@ -1010,7 +1010,7 @@ copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
|
||||
struct zone *zone;
|
||||
unsigned long pfn;
|
||||
|
||||
for_each_zone(zone) {
|
||||
for_each_populated_zone(zone) {
|
||||
unsigned long max_zone_pfn;
|
||||
|
||||
mark_free_pages(zone);
|
||||
@ -1033,6 +1033,25 @@ copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
|
||||
static unsigned int nr_copy_pages;
|
||||
/* Number of pages needed for saving the original pfns of the image pages */
|
||||
static unsigned int nr_meta_pages;
|
||||
/*
|
||||
* Numbers of normal and highmem page frames allocated for hibernation image
|
||||
* before suspending devices.
|
||||
*/
|
||||
unsigned int alloc_normal, alloc_highmem;
|
||||
/*
|
||||
* Memory bitmap used for marking saveable pages (during hibernation) or
|
||||
* hibernation image pages (during restore)
|
||||
*/
|
||||
static struct memory_bitmap orig_bm;
|
||||
/*
|
||||
* Memory bitmap used during hibernation for marking allocated page frames that
|
||||
* will contain copies of saveable pages. During restore it is initially used
|
||||
* for marking hibernation image pages, but then the set bits from it are
|
||||
* duplicated in @orig_bm and it is released. On highmem systems it is next
|
||||
* used for marking "safe" highmem pages, but it has to be reinitialized for
|
||||
* this purpose.
|
||||
*/
|
||||
static struct memory_bitmap copy_bm;
|
||||
|
||||
/**
|
||||
* swsusp_free - free pages allocated for the suspend.
|
||||
@ -1046,7 +1065,7 @@ void swsusp_free(void)
|
||||
struct zone *zone;
|
||||
unsigned long pfn, max_zone_pfn;
|
||||
|
||||
for_each_zone(zone) {
|
||||
for_each_populated_zone(zone) {
|
||||
max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
|
||||
for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
|
||||
if (pfn_valid(pfn)) {
|
||||
@ -1064,74 +1083,286 @@ void swsusp_free(void)
|
||||
nr_meta_pages = 0;
|
||||
restore_pblist = NULL;
|
||||
buffer = NULL;
|
||||
alloc_normal = 0;
|
||||
alloc_highmem = 0;
|
||||
}
|
||||
|
||||
/* Helper functions used for the shrinking of memory. */
|
||||
|
||||
#define GFP_IMAGE (GFP_KERNEL | __GFP_NOWARN)
|
||||
|
||||
/**
|
||||
* preallocate_image_pages - Allocate a number of pages for hibernation image
|
||||
* @nr_pages: Number of page frames to allocate.
|
||||
* @mask: GFP flags to use for the allocation.
|
||||
*
|
||||
* Return value: Number of page frames actually allocated
|
||||
*/
|
||||
static unsigned long preallocate_image_pages(unsigned long nr_pages, gfp_t mask)
|
||||
{
|
||||
unsigned long nr_alloc = 0;
|
||||
|
||||
while (nr_pages > 0) {
|
||||
struct page *page;
|
||||
|
||||
page = alloc_image_page(mask);
|
||||
if (!page)
|
||||
break;
|
||||
memory_bm_set_bit(©_bm, page_to_pfn(page));
|
||||
if (PageHighMem(page))
|
||||
alloc_highmem++;
|
||||
else
|
||||
alloc_normal++;
|
||||
nr_pages--;
|
||||
nr_alloc++;
|
||||
}
|
||||
|
||||
return nr_alloc;
|
||||
}
|
||||
|
||||
static unsigned long preallocate_image_memory(unsigned long nr_pages)
|
||||
{
|
||||
return preallocate_image_pages(nr_pages, GFP_IMAGE);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
static unsigned long preallocate_image_highmem(unsigned long nr_pages)
|
||||
{
|
||||
return preallocate_image_pages(nr_pages, GFP_IMAGE | __GFP_HIGHMEM);
|
||||
}
|
||||
|
||||
/**
|
||||
* swsusp_shrink_memory - Try to free as much memory as needed
|
||||
*
|
||||
* ... but do not OOM-kill anyone
|
||||
*
|
||||
* Notice: all userland should be stopped before it is called, or
|
||||
* livelock is possible.
|
||||
* __fraction - Compute (an approximation of) x * (multiplier / base)
|
||||
*/
|
||||
|
||||
#define SHRINK_BITE 10000
|
||||
static inline unsigned long __shrink_memory(long tmp)
|
||||
static unsigned long __fraction(u64 x, u64 multiplier, u64 base)
|
||||
{
|
||||
if (tmp > SHRINK_BITE)
|
||||
tmp = SHRINK_BITE;
|
||||
return shrink_all_memory(tmp);
|
||||
x *= multiplier;
|
||||
do_div(x, base);
|
||||
return (unsigned long)x;
|
||||
}
|
||||
|
||||
int swsusp_shrink_memory(void)
|
||||
static unsigned long preallocate_highmem_fraction(unsigned long nr_pages,
|
||||
unsigned long highmem,
|
||||
unsigned long total)
|
||||
{
|
||||
unsigned long alloc = __fraction(nr_pages, highmem, total);
|
||||
|
||||
return preallocate_image_pages(alloc, GFP_IMAGE | __GFP_HIGHMEM);
|
||||
}
|
||||
#else /* CONFIG_HIGHMEM */
|
||||
static inline unsigned long preallocate_image_highmem(unsigned long nr_pages)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned long preallocate_highmem_fraction(unsigned long nr_pages,
|
||||
unsigned long highmem,
|
||||
unsigned long total)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_HIGHMEM */
|
||||
|
||||
/**
|
||||
* free_unnecessary_pages - Release preallocated pages not needed for the image
|
||||
*/
|
||||
static void free_unnecessary_pages(void)
|
||||
{
|
||||
unsigned long save_highmem, to_free_normal, to_free_highmem;
|
||||
|
||||
to_free_normal = alloc_normal - count_data_pages();
|
||||
save_highmem = count_highmem_pages();
|
||||
if (alloc_highmem > save_highmem) {
|
||||
to_free_highmem = alloc_highmem - save_highmem;
|
||||
} else {
|
||||
to_free_highmem = 0;
|
||||
to_free_normal -= save_highmem - alloc_highmem;
|
||||
}
|
||||
|
||||
memory_bm_position_reset(©_bm);
|
||||
|
||||
while (to_free_normal > 0 && to_free_highmem > 0) {
|
||||
unsigned long pfn = memory_bm_next_pfn(©_bm);
|
||||
struct page *page = pfn_to_page(pfn);
|
||||
|
||||
if (PageHighMem(page)) {
|
||||
if (!to_free_highmem)
|
||||
continue;
|
||||
to_free_highmem--;
|
||||
alloc_highmem--;
|
||||
} else {
|
||||
if (!to_free_normal)
|
||||
continue;
|
||||
to_free_normal--;
|
||||
alloc_normal--;
|
||||
}
|
||||
memory_bm_clear_bit(©_bm, pfn);
|
||||
swsusp_unset_page_forbidden(page);
|
||||
swsusp_unset_page_free(page);
|
||||
__free_page(page);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* minimum_image_size - Estimate the minimum acceptable size of an image
|
||||
* @saveable: Number of saveable pages in the system.
|
||||
*
|
||||
* We want to avoid attempting to free too much memory too hard, so estimate the
|
||||
* minimum acceptable size of a hibernation image to use as the lower limit for
|
||||
* preallocating memory.
|
||||
*
|
||||
* We assume that the minimum image size should be proportional to
|
||||
*
|
||||
* [number of saveable pages] - [number of pages that can be freed in theory]
|
||||
*
|
||||
* where the second term is the sum of (1) reclaimable slab pages, (2) active
|
||||
* and (3) inactive anonymouns pages, (4) active and (5) inactive file pages,
|
||||
* minus mapped file pages.
|
||||
*/
|
||||
static unsigned long minimum_image_size(unsigned long saveable)
|
||||
{
|
||||
unsigned long size;
|
||||
|
||||
size = global_page_state(NR_SLAB_RECLAIMABLE)
|
||||
+ global_page_state(NR_ACTIVE_ANON)
|
||||
+ global_page_state(NR_INACTIVE_ANON)
|
||||
+ global_page_state(NR_ACTIVE_FILE)
|
||||
+ global_page_state(NR_INACTIVE_FILE)
|
||||
- global_page_state(NR_FILE_MAPPED);
|
||||
|
||||
return saveable <= size ? 0 : saveable - size;
|
||||
}
|
||||
|
||||
/**
|
||||
* hibernate_preallocate_memory - Preallocate memory for hibernation image
|
||||
*
|
||||
* To create a hibernation image it is necessary to make a copy of every page
|
||||
* frame in use. We also need a number of page frames to be free during
|
||||
* hibernation for allocations made while saving the image and for device
|
||||
* drivers, in case they need to allocate memory from their hibernation
|
||||
* callbacks (these two numbers are given by PAGES_FOR_IO and SPARE_PAGES,
|
||||
* respectively, both of which are rough estimates). To make this happen, we
|
||||
* compute the total number of available page frames and allocate at least
|
||||
*
|
||||
* ([page frames total] + PAGES_FOR_IO + [metadata pages]) / 2 + 2 * SPARE_PAGES
|
||||
*
|
||||
* of them, which corresponds to the maximum size of a hibernation image.
|
||||
*
|
||||
* If image_size is set below the number following from the above formula,
|
||||
* the preallocation of memory is continued until the total number of saveable
|
||||
* pages in the system is below the requested image size or the minimum
|
||||
* acceptable image size returned by minimum_image_size(), whichever is greater.
|
||||
*/
|
||||
int hibernate_preallocate_memory(void)
|
||||
{
|
||||
long tmp;
|
||||
struct zone *zone;
|
||||
unsigned long pages = 0;
|
||||
unsigned int i = 0;
|
||||
char *p = "-\\|/";
|
||||
unsigned long saveable, size, max_size, count, highmem, pages = 0;
|
||||
unsigned long alloc, save_highmem, pages_highmem;
|
||||
struct timeval start, stop;
|
||||
int error;
|
||||
|
||||
printk(KERN_INFO "PM: Shrinking memory... ");
|
||||
printk(KERN_INFO "PM: Preallocating image memory... ");
|
||||
do_gettimeofday(&start);
|
||||
do {
|
||||
long size, highmem_size;
|
||||
|
||||
highmem_size = count_highmem_pages();
|
||||
size = count_data_pages() + PAGES_FOR_IO + SPARE_PAGES;
|
||||
tmp = size;
|
||||
size += highmem_size;
|
||||
for_each_populated_zone(zone) {
|
||||
tmp += snapshot_additional_pages(zone);
|
||||
if (is_highmem(zone)) {
|
||||
highmem_size -=
|
||||
zone_page_state(zone, NR_FREE_PAGES);
|
||||
} else {
|
||||
tmp -= zone_page_state(zone, NR_FREE_PAGES);
|
||||
tmp += zone->lowmem_reserve[ZONE_NORMAL];
|
||||
}
|
||||
}
|
||||
error = memory_bm_create(&orig_bm, GFP_IMAGE, PG_ANY);
|
||||
if (error)
|
||||
goto err_out;
|
||||
|
||||
if (highmem_size < 0)
|
||||
highmem_size = 0;
|
||||
error = memory_bm_create(©_bm, GFP_IMAGE, PG_ANY);
|
||||
if (error)
|
||||
goto err_out;
|
||||
|
||||
tmp += highmem_size;
|
||||
if (tmp > 0) {
|
||||
tmp = __shrink_memory(tmp);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
pages += tmp;
|
||||
} else if (size > image_size / PAGE_SIZE) {
|
||||
tmp = __shrink_memory(size - (image_size / PAGE_SIZE));
|
||||
pages += tmp;
|
||||
}
|
||||
printk("\b%c", p[i++%4]);
|
||||
} while (tmp > 0);
|
||||
alloc_normal = 0;
|
||||
alloc_highmem = 0;
|
||||
|
||||
/* Count the number of saveable data pages. */
|
||||
save_highmem = count_highmem_pages();
|
||||
saveable = count_data_pages();
|
||||
|
||||
/*
|
||||
* Compute the total number of page frames we can use (count) and the
|
||||
* number of pages needed for image metadata (size).
|
||||
*/
|
||||
count = saveable;
|
||||
saveable += save_highmem;
|
||||
highmem = save_highmem;
|
||||
size = 0;
|
||||
for_each_populated_zone(zone) {
|
||||
size += snapshot_additional_pages(zone);
|
||||
if (is_highmem(zone))
|
||||
highmem += zone_page_state(zone, NR_FREE_PAGES);
|
||||
else
|
||||
count += zone_page_state(zone, NR_FREE_PAGES);
|
||||
}
|
||||
count += highmem;
|
||||
count -= totalreserve_pages;
|
||||
|
||||
/* Compute the maximum number of saveable pages to leave in memory. */
|
||||
max_size = (count - (size + PAGES_FOR_IO)) / 2 - 2 * SPARE_PAGES;
|
||||
size = DIV_ROUND_UP(image_size, PAGE_SIZE);
|
||||
if (size > max_size)
|
||||
size = max_size;
|
||||
/*
|
||||
* If the maximum is not less than the current number of saveable pages
|
||||
* in memory, allocate page frames for the image and we're done.
|
||||
*/
|
||||
if (size >= saveable) {
|
||||
pages = preallocate_image_highmem(save_highmem);
|
||||
pages += preallocate_image_memory(saveable - pages);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Estimate the minimum size of the image. */
|
||||
pages = minimum_image_size(saveable);
|
||||
if (size < pages)
|
||||
size = min_t(unsigned long, pages, max_size);
|
||||
|
||||
/*
|
||||
* Let the memory management subsystem know that we're going to need a
|
||||
* large number of page frames to allocate and make it free some memory.
|
||||
* NOTE: If this is not done, performance will be hurt badly in some
|
||||
* test cases.
|
||||
*/
|
||||
shrink_all_memory(saveable - size);
|
||||
|
||||
/*
|
||||
* The number of saveable pages in memory was too high, so apply some
|
||||
* pressure to decrease it. First, make room for the largest possible
|
||||
* image and fail if that doesn't work. Next, try to decrease the size
|
||||
* of the image as much as indicated by 'size' using allocations from
|
||||
* highmem and non-highmem zones separately.
|
||||
*/
|
||||
pages_highmem = preallocate_image_highmem(highmem / 2);
|
||||
alloc = (count - max_size) - pages_highmem;
|
||||
pages = preallocate_image_memory(alloc);
|
||||
if (pages < alloc)
|
||||
goto err_out;
|
||||
size = max_size - size;
|
||||
alloc = size;
|
||||
size = preallocate_highmem_fraction(size, highmem, count);
|
||||
pages_highmem += size;
|
||||
alloc -= size;
|
||||
pages += preallocate_image_memory(alloc);
|
||||
pages += pages_highmem;
|
||||
|
||||
/*
|
||||
* We only need as many page frames for the image as there are saveable
|
||||
* pages in memory, but we have allocated more. Release the excessive
|
||||
* ones now.
|
||||
*/
|
||||
free_unnecessary_pages();
|
||||
|
||||
out:
|
||||
do_gettimeofday(&stop);
|
||||
printk("\bdone (%lu pages freed)\n", pages);
|
||||
swsusp_show_speed(&start, &stop, pages, "Freed");
|
||||
printk(KERN_CONT "done (allocated %lu pages)\n", pages);
|
||||
swsusp_show_speed(&start, &stop, pages, "Allocated");
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
printk(KERN_CONT "\n");
|
||||
swsusp_free();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
@ -1142,7 +1373,7 @@ int swsusp_shrink_memory(void)
|
||||
|
||||
static unsigned int count_pages_for_highmem(unsigned int nr_highmem)
|
||||
{
|
||||
unsigned int free_highmem = count_free_highmem_pages();
|
||||
unsigned int free_highmem = count_free_highmem_pages() + alloc_highmem;
|
||||
|
||||
if (free_highmem >= nr_highmem)
|
||||
nr_highmem = 0;
|
||||
@ -1164,19 +1395,17 @@ count_pages_for_highmem(unsigned int nr_highmem) { return 0; }
|
||||
static int enough_free_mem(unsigned int nr_pages, unsigned int nr_highmem)
|
||||
{
|
||||
struct zone *zone;
|
||||
unsigned int free = 0, meta = 0;
|
||||
unsigned int free = alloc_normal;
|
||||
|
||||
for_each_zone(zone) {
|
||||
meta += snapshot_additional_pages(zone);
|
||||
for_each_populated_zone(zone)
|
||||
if (!is_highmem(zone))
|
||||
free += zone_page_state(zone, NR_FREE_PAGES);
|
||||
}
|
||||
|
||||
nr_pages += count_pages_for_highmem(nr_highmem);
|
||||
pr_debug("PM: Normal pages needed: %u + %u + %u, available pages: %u\n",
|
||||
nr_pages, PAGES_FOR_IO, meta, free);
|
||||
pr_debug("PM: Normal pages needed: %u + %u, available pages: %u\n",
|
||||
nr_pages, PAGES_FOR_IO, free);
|
||||
|
||||
return free > nr_pages + PAGES_FOR_IO + meta;
|
||||
return free > nr_pages + PAGES_FOR_IO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
@ -1198,7 +1427,7 @@ static inline int get_highmem_buffer(int safe_needed)
|
||||
*/
|
||||
|
||||
static inline unsigned int
|
||||
alloc_highmem_image_pages(struct memory_bitmap *bm, unsigned int nr_highmem)
|
||||
alloc_highmem_pages(struct memory_bitmap *bm, unsigned int nr_highmem)
|
||||
{
|
||||
unsigned int to_alloc = count_free_highmem_pages();
|
||||
|
||||
@ -1218,7 +1447,7 @@ alloc_highmem_image_pages(struct memory_bitmap *bm, unsigned int nr_highmem)
|
||||
static inline int get_highmem_buffer(int safe_needed) { return 0; }
|
||||
|
||||
static inline unsigned int
|
||||
alloc_highmem_image_pages(struct memory_bitmap *bm, unsigned int n) { return 0; }
|
||||
alloc_highmem_pages(struct memory_bitmap *bm, unsigned int n) { return 0; }
|
||||
#endif /* CONFIG_HIGHMEM */
|
||||
|
||||
/**
|
||||
@ -1237,51 +1466,36 @@ static int
|
||||
swsusp_alloc(struct memory_bitmap *orig_bm, struct memory_bitmap *copy_bm,
|
||||
unsigned int nr_pages, unsigned int nr_highmem)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = memory_bm_create(orig_bm, GFP_ATOMIC | __GFP_COLD, PG_ANY);
|
||||
if (error)
|
||||
goto Free;
|
||||
|
||||
error = memory_bm_create(copy_bm, GFP_ATOMIC | __GFP_COLD, PG_ANY);
|
||||
if (error)
|
||||
goto Free;
|
||||
int error = 0;
|
||||
|
||||
if (nr_highmem > 0) {
|
||||
error = get_highmem_buffer(PG_ANY);
|
||||
if (error)
|
||||
goto Free;
|
||||
|
||||
nr_pages += alloc_highmem_image_pages(copy_bm, nr_highmem);
|
||||
goto err_out;
|
||||
if (nr_highmem > alloc_highmem) {
|
||||
nr_highmem -= alloc_highmem;
|
||||
nr_pages += alloc_highmem_pages(copy_bm, nr_highmem);
|
||||
}
|
||||
}
|
||||
while (nr_pages-- > 0) {
|
||||
struct page *page = alloc_image_page(GFP_ATOMIC | __GFP_COLD);
|
||||
if (nr_pages > alloc_normal) {
|
||||
nr_pages -= alloc_normal;
|
||||
while (nr_pages-- > 0) {
|
||||
struct page *page;
|
||||
|
||||
if (!page)
|
||||
goto Free;
|
||||
|
||||
memory_bm_set_bit(copy_bm, page_to_pfn(page));
|
||||
page = alloc_image_page(GFP_ATOMIC | __GFP_COLD);
|
||||
if (!page)
|
||||
goto err_out;
|
||||
memory_bm_set_bit(copy_bm, page_to_pfn(page));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
Free:
|
||||
err_out:
|
||||
swsusp_free();
|
||||
return -ENOMEM;
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Memory bitmap used for marking saveable pages (during suspend) or the
|
||||
* suspend image pages (during resume)
|
||||
*/
|
||||
static struct memory_bitmap orig_bm;
|
||||
/* Memory bitmap used on suspend for marking allocated pages that will contain
|
||||
* the copies of saveable pages. During resume it is initially used for
|
||||
* marking the suspend image pages, but then its set bits are duplicated in
|
||||
* @orig_bm and it is released. Next, on systems with high memory, it may be
|
||||
* used for marking "safe" highmem pages, but it has to be reinitialized for
|
||||
* this purpose.
|
||||
*/
|
||||
static struct memory_bitmap copy_bm;
|
||||
|
||||
asmlinkage int swsusp_save(void)
|
||||
{
|
||||
unsigned int nr_pages, nr_highmem;
|
||||
@ -1474,7 +1688,7 @@ static int mark_unsafe_pages(struct memory_bitmap *bm)
|
||||
unsigned long pfn, max_zone_pfn;
|
||||
|
||||
/* Clear page flags */
|
||||
for_each_zone(zone) {
|
||||
for_each_populated_zone(zone) {
|
||||
max_zone_pfn = zone->zone_start_pfn + zone->spanned_pages;
|
||||
for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
|
||||
if (pfn_valid(pfn))
|
||||
|
Loading…
Reference in New Issue
Block a user