mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 12:42:02 +00:00
ARM: 6758/1: amba: support pm ops
Support pm_ops in the AMBA bus, required to allow drivers to use runtime pm. The implementation of AMBA bus pm ops is based on the platform bus implementation. Acked-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
2d00880fa8
commit
ba74ec7f6b
@ -13,12 +13,13 @@
|
|||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/amba/bus.h>
|
#include <linux/amba/bus.h>
|
||||||
|
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
#include <asm/sizes.h>
|
#include <asm/sizes.h>
|
||||||
|
|
||||||
#define to_amba_device(d) container_of(d, struct amba_device, dev)
|
|
||||||
#define to_amba_driver(d) container_of(d, struct amba_driver, drv)
|
#define to_amba_driver(d) container_of(d, struct amba_driver, drv)
|
||||||
|
|
||||||
static const struct amba_id *
|
static const struct amba_id *
|
||||||
@ -57,26 +58,6 @@ static int amba_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||||||
#define amba_uevent NULL
|
#define amba_uevent NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int amba_suspend(struct device *dev, pm_message_t state)
|
|
||||||
{
|
|
||||||
struct amba_driver *drv = to_amba_driver(dev->driver);
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (dev->driver && drv->suspend)
|
|
||||||
ret = drv->suspend(to_amba_device(dev), state);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int amba_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
struct amba_driver *drv = to_amba_driver(dev->driver);
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (dev->driver && drv->resume)
|
|
||||||
ret = drv->resume(to_amba_device(dev));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define amba_attr_func(name,fmt,arg...) \
|
#define amba_attr_func(name,fmt,arg...) \
|
||||||
static ssize_t name##_show(struct device *_dev, \
|
static ssize_t name##_show(struct device *_dev, \
|
||||||
struct device_attribute *attr, char *buf) \
|
struct device_attribute *attr, char *buf) \
|
||||||
@ -102,6 +83,320 @@ static struct device_attribute amba_dev_attrs[] = {
|
|||||||
__ATTR_NULL,
|
__ATTR_NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
|
||||||
|
static int amba_legacy_suspend(struct device *dev, pm_message_t mesg)
|
||||||
|
{
|
||||||
|
struct amba_driver *adrv = to_amba_driver(dev->driver);
|
||||||
|
struct amba_device *adev = to_amba_device(dev);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (dev->driver && adrv->suspend)
|
||||||
|
ret = adrv->suspend(adev, mesg);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amba_legacy_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct amba_driver *adrv = to_amba_driver(dev->driver);
|
||||||
|
struct amba_device *adev = to_amba_device(dev);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (dev->driver && adrv->resume)
|
||||||
|
ret = adrv->resume(adev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amba_pm_prepare(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (drv && drv->pm && drv->pm->prepare)
|
||||||
|
ret = drv->pm->prepare(dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amba_pm_complete(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
|
||||||
|
if (drv && drv->pm && drv->pm->complete)
|
||||||
|
drv->pm->complete(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
#define amba_pm_prepare NULL
|
||||||
|
#define amba_pm_complete NULL
|
||||||
|
|
||||||
|
#endif /* !CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
#ifdef CONFIG_SUSPEND
|
||||||
|
|
||||||
|
static int amba_pm_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!drv)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (drv->pm) {
|
||||||
|
if (drv->pm->suspend)
|
||||||
|
ret = drv->pm->suspend(dev);
|
||||||
|
} else {
|
||||||
|
ret = amba_legacy_suspend(dev, PMSG_SUSPEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amba_pm_suspend_noirq(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!drv)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (drv->pm) {
|
||||||
|
if (drv->pm->suspend_noirq)
|
||||||
|
ret = drv->pm->suspend_noirq(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amba_pm_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!drv)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (drv->pm) {
|
||||||
|
if (drv->pm->resume)
|
||||||
|
ret = drv->pm->resume(dev);
|
||||||
|
} else {
|
||||||
|
ret = amba_legacy_resume(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amba_pm_resume_noirq(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!drv)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (drv->pm) {
|
||||||
|
if (drv->pm->resume_noirq)
|
||||||
|
ret = drv->pm->resume_noirq(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !CONFIG_SUSPEND */
|
||||||
|
|
||||||
|
#define amba_pm_suspend NULL
|
||||||
|
#define amba_pm_resume NULL
|
||||||
|
#define amba_pm_suspend_noirq NULL
|
||||||
|
#define amba_pm_resume_noirq NULL
|
||||||
|
|
||||||
|
#endif /* !CONFIG_SUSPEND */
|
||||||
|
|
||||||
|
#ifdef CONFIG_HIBERNATION
|
||||||
|
|
||||||
|
static int amba_pm_freeze(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!drv)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (drv->pm) {
|
||||||
|
if (drv->pm->freeze)
|
||||||
|
ret = drv->pm->freeze(dev);
|
||||||
|
} else {
|
||||||
|
ret = amba_legacy_suspend(dev, PMSG_FREEZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amba_pm_freeze_noirq(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!drv)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (drv->pm) {
|
||||||
|
if (drv->pm->freeze_noirq)
|
||||||
|
ret = drv->pm->freeze_noirq(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amba_pm_thaw(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!drv)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (drv->pm) {
|
||||||
|
if (drv->pm->thaw)
|
||||||
|
ret = drv->pm->thaw(dev);
|
||||||
|
} else {
|
||||||
|
ret = amba_legacy_resume(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amba_pm_thaw_noirq(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!drv)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (drv->pm) {
|
||||||
|
if (drv->pm->thaw_noirq)
|
||||||
|
ret = drv->pm->thaw_noirq(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amba_pm_poweroff(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!drv)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (drv->pm) {
|
||||||
|
if (drv->pm->poweroff)
|
||||||
|
ret = drv->pm->poweroff(dev);
|
||||||
|
} else {
|
||||||
|
ret = amba_legacy_suspend(dev, PMSG_HIBERNATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amba_pm_poweroff_noirq(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!drv)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (drv->pm) {
|
||||||
|
if (drv->pm->poweroff_noirq)
|
||||||
|
ret = drv->pm->poweroff_noirq(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amba_pm_restore(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!drv)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (drv->pm) {
|
||||||
|
if (drv->pm->restore)
|
||||||
|
ret = drv->pm->restore(dev);
|
||||||
|
} else {
|
||||||
|
ret = amba_legacy_resume(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amba_pm_restore_noirq(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!drv)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (drv->pm) {
|
||||||
|
if (drv->pm->restore_noirq)
|
||||||
|
ret = drv->pm->restore_noirq(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !CONFIG_HIBERNATION */
|
||||||
|
|
||||||
|
#define amba_pm_freeze NULL
|
||||||
|
#define amba_pm_thaw NULL
|
||||||
|
#define amba_pm_poweroff NULL
|
||||||
|
#define amba_pm_restore NULL
|
||||||
|
#define amba_pm_freeze_noirq NULL
|
||||||
|
#define amba_pm_thaw_noirq NULL
|
||||||
|
#define amba_pm_poweroff_noirq NULL
|
||||||
|
#define amba_pm_restore_noirq NULL
|
||||||
|
|
||||||
|
#endif /* !CONFIG_HIBERNATION */
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
|
||||||
|
static const struct dev_pm_ops amba_pm = {
|
||||||
|
.prepare = amba_pm_prepare,
|
||||||
|
.complete = amba_pm_complete,
|
||||||
|
.suspend = amba_pm_suspend,
|
||||||
|
.resume = amba_pm_resume,
|
||||||
|
.freeze = amba_pm_freeze,
|
||||||
|
.thaw = amba_pm_thaw,
|
||||||
|
.poweroff = amba_pm_poweroff,
|
||||||
|
.restore = amba_pm_restore,
|
||||||
|
.suspend_noirq = amba_pm_suspend_noirq,
|
||||||
|
.resume_noirq = amba_pm_resume_noirq,
|
||||||
|
.freeze_noirq = amba_pm_freeze_noirq,
|
||||||
|
.thaw_noirq = amba_pm_thaw_noirq,
|
||||||
|
.poweroff_noirq = amba_pm_poweroff_noirq,
|
||||||
|
.restore_noirq = amba_pm_restore_noirq,
|
||||||
|
SET_RUNTIME_PM_OPS(
|
||||||
|
pm_generic_runtime_suspend,
|
||||||
|
pm_generic_runtime_resume,
|
||||||
|
pm_generic_runtime_idle
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
#define AMBA_PM (&amba_pm)
|
||||||
|
|
||||||
|
#else /* !CONFIG_PM */
|
||||||
|
|
||||||
|
#define AMBA_PM NULL
|
||||||
|
|
||||||
|
#endif /* !CONFIG_PM */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Primecells are part of the Advanced Microcontroller Bus Architecture,
|
* Primecells are part of the Advanced Microcontroller Bus Architecture,
|
||||||
* so we call the bus "amba".
|
* so we call the bus "amba".
|
||||||
@ -111,8 +406,7 @@ struct bus_type amba_bustype = {
|
|||||||
.dev_attrs = amba_dev_attrs,
|
.dev_attrs = amba_dev_attrs,
|
||||||
.match = amba_match,
|
.match = amba_match,
|
||||||
.uevent = amba_uevent,
|
.uevent = amba_uevent,
|
||||||
.suspend = amba_suspend,
|
.pm = AMBA_PM,
|
||||||
.resume = amba_resume,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init amba_init(void)
|
static int __init amba_init(void)
|
||||||
|
@ -58,6 +58,8 @@ enum amba_vendor {
|
|||||||
|
|
||||||
extern struct bus_type amba_bustype;
|
extern struct bus_type amba_bustype;
|
||||||
|
|
||||||
|
#define to_amba_device(d) container_of(d, struct amba_device, dev)
|
||||||
|
|
||||||
#define amba_get_drvdata(d) dev_get_drvdata(&d->dev)
|
#define amba_get_drvdata(d) dev_get_drvdata(&d->dev)
|
||||||
#define amba_set_drvdata(d,p) dev_set_drvdata(&d->dev, p)
|
#define amba_set_drvdata(d,p) dev_set_drvdata(&d->dev, p)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user