greybus: gbphy: add gbphy runtime pm support
Since GBphy is a child of the Bundle device driver, for those runtime pm settings that are common to all the protocol drivers need to go in to the GBphy bus driver. Testing Done: - Check gbphy driver can be autosuspended Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org>
This commit is contained in:
parent
211634f2ca
commit
af5dc7f8c0
2 changed files with 98 additions and 1 deletions
|
@ -19,6 +19,8 @@
|
||||||
#include "greybus.h"
|
#include "greybus.h"
|
||||||
#include "gbphy.h"
|
#include "gbphy.h"
|
||||||
|
|
||||||
|
#define GB_GBPHY_AUTOSUSPEND_MS 3000
|
||||||
|
|
||||||
struct gbphy_host {
|
struct gbphy_host {
|
||||||
struct gb_bundle *bundle;
|
struct gb_bundle *bundle;
|
||||||
struct list_head devices;
|
struct list_head devices;
|
||||||
|
@ -50,9 +52,25 @@ static void gbphy_dev_release(struct device *dev)
|
||||||
kfree(gbphy_dev);
|
kfree(gbphy_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
static int gb_gbphy_idle(struct device *dev)
|
||||||
|
{
|
||||||
|
pm_runtime_mark_last_busy(dev);
|
||||||
|
pm_request_autosuspend(dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops gb_gbphy_pm_ops = {
|
||||||
|
SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend,
|
||||||
|
pm_generic_runtime_resume,
|
||||||
|
gb_gbphy_idle)
|
||||||
|
};
|
||||||
|
|
||||||
static struct device_type greybus_gbphy_dev_type = {
|
static struct device_type greybus_gbphy_dev_type = {
|
||||||
.name = "gbphy_device",
|
.name = "gbphy_device",
|
||||||
.release = gbphy_dev_release,
|
.release = gbphy_dev_release,
|
||||||
|
.pm = &gb_gbphy_pm_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int gbphy_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
|
static int gbphy_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||||
|
@ -118,12 +136,38 @@ static int gbphy_dev_probe(struct device *dev)
|
||||||
struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver);
|
struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver);
|
||||||
struct gbphy_device *gbphy_dev = to_gbphy_dev(dev);
|
struct gbphy_device *gbphy_dev = to_gbphy_dev(dev);
|
||||||
const struct gbphy_device_id *id;
|
const struct gbphy_device_id *id;
|
||||||
|
int ret;
|
||||||
|
|
||||||
id = gbphy_dev_match_id(gbphy_dev, gbphy_drv);
|
id = gbphy_dev_match_id(gbphy_dev, gbphy_drv);
|
||||||
if (!id)
|
if (!id)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
return gbphy_drv->probe(gbphy_dev, id);
|
/* for old kernels we need get_sync to resume parent devices */
|
||||||
|
ret = gb_pm_runtime_get_sync(gbphy_dev->bundle);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
pm_runtime_set_autosuspend_delay(dev, GB_GBPHY_AUTOSUSPEND_MS);
|
||||||
|
pm_runtime_use_autosuspend(dev);
|
||||||
|
pm_runtime_get_noresume(dev);
|
||||||
|
pm_runtime_set_active(dev);
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Drivers should call put on the gbphy dev before returning
|
||||||
|
* from probe if they support runtime pm.
|
||||||
|
*/
|
||||||
|
ret = gbphy_drv->probe(gbphy_dev, id);
|
||||||
|
if (ret) {
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
pm_runtime_set_suspended(dev);
|
||||||
|
pm_runtime_put_noidle(dev);
|
||||||
|
pm_runtime_dont_use_autosuspend(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_pm_runtime_put_autosuspend(gbphy_dev->bundle);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gbphy_dev_remove(struct device *dev)
|
static int gbphy_dev_remove(struct device *dev)
|
||||||
|
@ -132,6 +176,12 @@ static int gbphy_dev_remove(struct device *dev)
|
||||||
struct gbphy_device *gbphy_dev = to_gbphy_dev(dev);
|
struct gbphy_device *gbphy_dev = to_gbphy_dev(dev);
|
||||||
|
|
||||||
gbphy_drv->remove(gbphy_dev);
|
gbphy_drv->remove(gbphy_dev);
|
||||||
|
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
pm_runtime_set_suspended(dev);
|
||||||
|
pm_runtime_put_noidle(dev);
|
||||||
|
pm_runtime_dont_use_autosuspend(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,6 +261,11 @@ static void gb_gbphy_disconnect(struct gb_bundle *bundle)
|
||||||
{
|
{
|
||||||
struct gbphy_host *gbphy_host = greybus_get_drvdata(bundle);
|
struct gbphy_host *gbphy_host = greybus_get_drvdata(bundle);
|
||||||
struct gbphy_device *gbphy_dev, *temp;
|
struct gbphy_device *gbphy_dev, *temp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = gb_pm_runtime_get_sync(bundle);
|
||||||
|
if (ret < 0)
|
||||||
|
gb_pm_runtime_get_noresume(bundle);
|
||||||
|
|
||||||
list_for_each_entry_safe(gbphy_dev, temp, &gbphy_host->devices, list) {
|
list_for_each_entry_safe(gbphy_dev, temp, &gbphy_host->devices, list) {
|
||||||
list_del(&gbphy_dev->list);
|
list_del(&gbphy_dev->list);
|
||||||
|
@ -251,6 +306,8 @@ static int gb_gbphy_probe(struct gb_bundle *bundle,
|
||||||
list_add(&gbphy_dev->list, &gbphy_host->devices);
|
list_add(&gbphy_dev->list, &gbphy_host->devices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gb_pm_runtime_put_autosuspend(bundle);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,5 +66,45 @@ void gb_gbphy_deregister_driver(struct gbphy_driver *driver);
|
||||||
#define module_gbphy_driver(__gbphy_driver) \
|
#define module_gbphy_driver(__gbphy_driver) \
|
||||||
module_driver(__gbphy_driver, gb_gbphy_register, gb_gbphy_deregister)
|
module_driver(__gbphy_driver, gb_gbphy_register, gb_gbphy_deregister)
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
static inline int gbphy_runtime_get_sync(struct gbphy_device *gbphy_dev)
|
||||||
|
{
|
||||||
|
struct device *dev = &gbphy_dev->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pm_runtime_get_sync(dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "pm_runtime_get_sync failed: %d\n", ret);
|
||||||
|
pm_runtime_put_noidle(dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gbphy_runtime_put_autosuspend(struct gbphy_device *gbphy_dev)
|
||||||
|
{
|
||||||
|
struct device *dev = &gbphy_dev->dev;
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(dev);
|
||||||
|
pm_runtime_put_autosuspend(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gbphy_runtime_get_noresume(struct gbphy_device *gbphy_dev)
|
||||||
|
{
|
||||||
|
pm_runtime_get_noresume(&gbphy_dev->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gbphy_runtime_put_noidle(struct gbphy_device *gbphy_dev)
|
||||||
|
{
|
||||||
|
pm_runtime_put_noidle(&gbphy_dev->dev);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline int gbphy_runtime_get_sync(struct device *dev) { return 0; }
|
||||||
|
static inline void gbphy_runtime_put_autosuspend(struct device *dev) {}
|
||||||
|
static inline void gbphy_runtime_get_noresume(struct gbphy_device *gbphy_dev) {}
|
||||||
|
static inline void gbphy_runtime_put_noidle(struct gbphy_device *gbphy_dev) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* __GBPHY_H */
|
#endif /* __GBPHY_H */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue