greybus: gbphy: add gbphy runtime pm support
authorDavid Lin <dtwlin@google.com>
Thu, 14 Jul 2016 20:13:00 +0000 (15:13 -0500)
committerAlex Elder <elder@linaro.org>
Thu, 14 Jul 2016 21:53:55 +0000 (16:53 -0500)
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>
drivers/staging/greybus/gbphy.c
drivers/staging/greybus/gbphy.h

index 2727e4a..478c162 100644 (file)
@@ -19,6 +19,8 @@
 #include "greybus.h"
 #include "gbphy.h"
 
+#define GB_GBPHY_AUTOSUSPEND_MS        3000
+
 struct gbphy_host {
        struct gb_bundle *bundle;
        struct list_head devices;
@@ -50,9 +52,25 @@ static void gbphy_dev_release(struct device *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 = {
        .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)
@@ -118,12 +136,38 @@ static int gbphy_dev_probe(struct device *dev)
        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)
@@ -132,6 +176,12 @@ 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;
 }
 
@@ -211,6 +261,11 @@ static void gb_gbphy_disconnect(struct gb_bundle *bundle)
 {
        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);
@@ -251,6 +306,8 @@ static int gb_gbphy_probe(struct gb_bundle *bundle,
                list_add(&gbphy_dev->list, &gbphy_host->devices);
        }
 
+       gb_pm_runtime_put_autosuspend(bundle);
+
        return 0;
 }
 
index 68ad518..57c6f65 100644 (file)
@@ -66,5 +66,45 @@ void gb_gbphy_deregister_driver(struct gbphy_driver *driver);
 #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 */