#include "greybus.h"
#include "gbphy.h"
+#define GB_GBPHY_AUTOSUSPEND_MS 3000
+
struct gbphy_host {
struct gb_bundle *bundle;
struct list_head devices;
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 = {
.name = "gbphy_device",
.release = gbphy_dev_release,
+ .pm = &gb_gbphy_pm_ops,
};
static int gbphy_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver);
struct gbphy_device *gbphy_dev = to_gbphy_dev(dev);
const struct gbphy_device_id *id;
+ int ret;
id = gbphy_dev_match_id(gbphy_dev, gbphy_drv);
if (!id)
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)
struct gbphy_device *gbphy_dev = to_gbphy_dev(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;
}
{
struct gbphy_host *gbphy_host = greybus_get_drvdata(bundle);
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_del(&gbphy_dev->list);
list_add(&gbphy_dev->list, &gbphy_host->devices);
}
+ gb_pm_runtime_put_autosuspend(bundle);
+
return 0;
}
#define module_gbphy_driver(__gbphy_driver) \
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 */