spi / PM: Support dev_pm_ops

Allow SPI drivers to use runtime PM and other dev_pm_ops features by
implementing dev_pm_ops for the bus. The existing bus specific suspend
and resume functions will be called if a driver does not provide dev_pm_ops
allowing for transition to the new model.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index b02d0cb..34bb17f 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -28,6 +28,7 @@
 #include <linux/mod_devicetable.h>
 #include <linux/spi/spi.h>
 #include <linux/of_spi.h>
+#include <linux/pm_runtime.h>
 
 static void spidev_release(struct device *dev)
 {
@@ -100,9 +101,8 @@
 	return 0;
 }
 
-#ifdef	CONFIG_PM
-
-static int spi_suspend(struct device *dev, pm_message_t message)
+#ifdef CONFIG_PM_SLEEP
+static int spi_legacy_suspend(struct device *dev, pm_message_t message)
 {
 	int			value = 0;
 	struct spi_driver	*drv = to_spi_driver(dev->driver);
@@ -117,7 +117,7 @@
 	return value;
 }
 
-static int spi_resume(struct device *dev)
+static int spi_legacy_resume(struct device *dev)
 {
 	int			value = 0;
 	struct spi_driver	*drv = to_spi_driver(dev->driver);
@@ -132,18 +132,94 @@
 	return value;
 }
 
+static int spi_pm_suspend(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_suspend(dev);
+	else
+		return spi_legacy_suspend(dev, PMSG_SUSPEND);
+}
+
+static int spi_pm_resume(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_resume(dev);
+	else
+		return spi_legacy_resume(dev);
+}
+
+static int spi_pm_freeze(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_freeze(dev);
+	else
+		return spi_legacy_suspend(dev, PMSG_FREEZE);
+}
+
+static int spi_pm_thaw(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_thaw(dev);
+	else
+		return spi_legacy_resume(dev);
+}
+
+static int spi_pm_poweroff(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_poweroff(dev);
+	else
+		return spi_legacy_suspend(dev, PMSG_HIBERNATE);
+}
+
+static int spi_pm_restore(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_restore(dev);
+	else
+		return spi_legacy_resume(dev);
+}
 #else
-#define spi_suspend	NULL
-#define spi_resume	NULL
+#define spi_pm_suspend	NULL
+#define spi_pm_resume	NULL
+#define spi_pm_freeze	NULL
+#define spi_pm_thaw	NULL
+#define spi_pm_poweroff	NULL
+#define spi_pm_restore	NULL
 #endif
 
+static const struct dev_pm_ops spi_pm = {
+	.suspend = spi_pm_suspend,
+	.resume = spi_pm_resume,
+	.freeze = spi_pm_freeze,
+	.thaw = spi_pm_thaw,
+	.poweroff = spi_pm_poweroff,
+	.restore = spi_pm_restore,
+	SET_RUNTIME_PM_OPS(
+		pm_generic_runtime_suspend,
+		pm_generic_runtime_resume,
+		pm_generic_runtime_idle
+	)
+};
+
 struct bus_type spi_bus_type = {
 	.name		= "spi",
 	.dev_attrs	= spi_dev_attrs,
 	.match		= spi_match_device,
 	.uevent		= spi_uevent,
-	.suspend	= spi_suspend,
-	.resume		= spi_resume,
+	.pm		= &spi_pm,
 };
 EXPORT_SYMBOL_GPL(spi_bus_type);