]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
greybus: bundle: add 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)
This patch adds runtime pm support for the bundle core. Unbound bundle
devices are always deactivated. During probe, Runtime PM status is set
to enabled and active and the usage count is incremented. If the driver
supports runtime PM, it should call pm_runtime_put() in its probe
routine and pm_runtime_get_sync() in remove routine as bundle needs to
be resume before it can be deactivated.

Testing Done:
 - Check runtime_status of the bundle driver when bundle goes to suspend

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/bundle.c
drivers/staging/greybus/bundle.h
drivers/staging/greybus/core.c

index 80f54c9775093e63ec3376eeff994addc424a0ad..5bd7731237f536c89d296e90376a907ca7998a18 100644 (file)
@@ -89,9 +89,89 @@ static void gb_bundle_release(struct device *dev)
        kfree(bundle);
 }
 
+#ifdef CONFIG_PM_RUNTIME
+
+static void gb_bundle_disable_all_connections(struct gb_bundle *bundle)
+{
+       struct gb_connection *connection;
+
+       list_for_each_entry(connection, &bundle->connections, bundle_links)
+               gb_connection_disable(connection);
+}
+
+static void gb_bundle_enable_all_connections(struct gb_bundle *bundle)
+{
+       struct gb_connection *connection;
+
+       list_for_each_entry(connection, &bundle->connections, bundle_links)
+               gb_connection_enable(connection);
+}
+
+static int gb_bundle_suspend(struct device *dev)
+{
+       struct gb_bundle *bundle = to_gb_bundle(dev);
+       const struct dev_pm_ops *pm = dev->driver->pm;
+       int ret;
+
+       if (pm && pm->runtime_suspend) {
+               ret = pm->runtime_suspend(&bundle->dev);
+               if (ret)
+                       return ret;
+       } else {
+               gb_bundle_disable_all_connections(bundle);
+       }
+
+       ret = gb_control_bundle_suspend(bundle->intf->control, bundle->id);
+       if (ret) {
+               if (pm && pm->runtime_resume)
+                       ret = pm->runtime_resume(dev);
+               else
+                       gb_bundle_enable_all_connections(bundle);
+
+               return ret;
+       }
+
+       return 0;
+}
+
+static int gb_bundle_resume(struct device *dev)
+{
+       struct gb_bundle *bundle = to_gb_bundle(dev);
+       const struct dev_pm_ops *pm = dev->driver->pm;
+       int ret;
+
+       ret = gb_control_bundle_resume(bundle->intf->control, bundle->id);
+       if (ret)
+               return ret;
+
+       if (pm && pm->runtime_resume) {
+               ret = pm->runtime_resume(dev);
+               if (ret)
+                       return ret;
+       } else {
+               gb_bundle_enable_all_connections(bundle);
+       }
+
+       return 0;
+}
+
+static int gb_bundle_idle(struct device *dev)
+{
+       pm_runtime_mark_last_busy(dev);
+       pm_request_autosuspend(dev);
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops gb_bundle_pm_ops = {
+       SET_RUNTIME_PM_OPS(gb_bundle_suspend, gb_bundle_resume, gb_bundle_idle)
+};
+
 struct device_type greybus_bundle_type = {
        .name =         "greybus_bundle",
        .release =      gb_bundle_release,
+       .pm =           &gb_bundle_pm_ops,
 };
 
 /*
index 3895f94f43c42111242e252fbc85df43cb788844..349845ee893c4c60d0720282c31e1ac7ba8e00c4 100644 (file)
@@ -40,4 +40,51 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id,
 int gb_bundle_add(struct gb_bundle *bundle);
 void gb_bundle_destroy(struct gb_bundle *bundle);
 
+/* Bundle Runtime PM wrappers */
+#ifdef CONFIG_PM_RUNTIME
+static inline int gb_pm_runtime_get_sync(struct gb_bundle *bundle)
+{
+       int retval;
+
+       retval = pm_runtime_get_sync(&bundle->dev);
+       if (retval < 0) {
+               dev_err(&bundle->dev,
+                       "pm_runtime_get_sync failed: %d\n", retval);
+               pm_runtime_put_noidle(&bundle->dev);
+               return retval;
+       }
+
+       return 0;
+}
+
+static inline int gb_pm_runtime_put_autosuspend(struct gb_bundle *bundle)
+{
+       int retval;
+
+       pm_runtime_mark_last_busy(&bundle->dev);
+       retval = pm_runtime_put_autosuspend(&bundle->dev);
+
+       return retval;
+}
+
+static inline void gb_pm_runtime_get_noresume(struct gb_bundle *bundle)
+{
+       pm_runtime_get_noresume(&bundle->dev);
+}
+
+static inline void gb_pm_runtime_put_noidle(struct gb_bundle *bundle)
+{
+       pm_runtime_put_noidle(&bundle->dev);
+}
+
+#else
+static inline int gb_pm_runtime_get_sync(struct gb_bundle *bundle)
+{ return 0; }
+static inline int gb_pm_runtime_put_autosuspend(struct gb_bundle *bundle)
+{ return 0; }
+
+static inline void gb_pm_runtime_get_noresume(struct gb_bundle *bundle) {}
+static inline void gb_pm_runtime_put_noidle(struct gb_bundle *bundle) {}
+#endif
+
 #endif /* __BUNDLE_H */
index ca3bad910aaeb45d3934ad4fe4e73a023e9d4848..53d9ba151aeb1326bf442a5084f001c9e6e935d9 100644 (file)
@@ -13,6 +13,8 @@
 #include "greybus.h"
 #include "greybus_trace.h"
 
+#define GB_BUNDLE_AUTOSUSPEND_MS       3000
+
 /* Allow greybus to be disabled at boot if needed */
 static bool nogreybus;
 #ifdef MODULE
@@ -162,6 +164,12 @@ static int greybus_probe(struct device *dev)
        if (!id)
                return -ENODEV;
 
+       retval = pm_runtime_get_sync(&bundle->intf->dev);
+       if (retval < 0) {
+               pm_runtime_put_noidle(&bundle->intf->dev);
+               return retval;
+       }
+
        /*
         * FIXME: We need to perform error handling on bundle activate call
         * below when firmware is ready. We just allow the activate operation to
@@ -169,6 +177,19 @@ static int greybus_probe(struct device *dev)
         */
        gb_control_bundle_activate(bundle->intf->control, bundle->id);
 
+       /*
+        * Unbound bundle devices are always deactivated. During probe, the
+        * Runtime PM is set to enabled and active and the usage count is
+        * incremented. If the driver supports runtime PM, it should call
+        * pm_runtime_put() in its probe routine and pm_runtime_get_sync()
+        * in remove routine.
+        */
+       pm_runtime_set_autosuspend_delay(dev, GB_BUNDLE_AUTOSUSPEND_MS);
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_get_noresume(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
        retval = driver->probe(bundle, id);
        if (retval) {
                /*
@@ -178,11 +199,19 @@ static int greybus_probe(struct device *dev)
 
                gb_control_bundle_deactivate(bundle->intf->control, bundle->id);
 
+               pm_runtime_disable(dev);
+               pm_runtime_set_suspended(dev);
+               pm_runtime_put_noidle(dev);
+               pm_runtime_dont_use_autosuspend(dev);
+               pm_runtime_put(&bundle->intf->dev);
+
                return retval;
        }
 
        gb_timesync_schedule_asynchronous(bundle->intf);
 
+       pm_runtime_put(&bundle->intf->dev);
+
        return 0;
 }
 
@@ -191,6 +220,11 @@ static int greybus_remove(struct device *dev)
        struct greybus_driver *driver = to_greybus_driver(dev->driver);
        struct gb_bundle *bundle = to_gb_bundle(dev);
        struct gb_connection *connection;
+       int retval;
+
+       retval = pm_runtime_get_sync(dev);
+       if (retval < 0)
+               dev_err(dev, "failed to resume bundle: %d\n", retval);
 
        /*
         * Disable (non-offloaded) connections early in case the interface is
@@ -215,6 +249,12 @@ static int greybus_remove(struct device *dev)
        if (!bundle->intf->disconnected)
                gb_control_bundle_deactivate(bundle->intf->control, bundle->id);
 
+       pm_runtime_put_noidle(dev);
+       pm_runtime_disable(dev);
+       pm_runtime_set_suspended(dev);
+       pm_runtime_dont_use_autosuspend(dev);
+       pm_runtime_put_noidle(dev);
+
        return 0;
 }