iio: pressure: bmp280: add power management
authorLinus Walleij <linus.walleij@linaro.org>
Thu, 30 Jun 2016 01:48:53 +0000 (03:48 +0200)
committerJonathan Cameron <jic23@kernel.org>
Sun, 3 Jul 2016 10:32:14 +0000 (11:32 +0100)
The PM280 has an internal standby-mode, but to really save power
we should shut the sensor down and disconnect the power. With
the proper .pm hooks we can enable both runtime and system power
management of the sensor. We use the *force callbacks from the
system PM hooks. When the sensor comes back we always reconfigure
it to make sure it is ready to roll as expected.

Cc: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
drivers/iio/pressure/bmp280-core.c
drivers/iio/pressure/bmp280-i2c.c
drivers/iio/pressure/bmp280-spi.c
drivers/iio/pressure/bmp280.h

index 3ebd84f06e36609f85292e3a13721ad9505d5c5f..f25df7f32ac8e43eadc61eaf395fb908ca13d24e 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/interrupt.h>
 #include <linux/irq.h> /* For irq_get_irq_data() */
 #include <linux/completion.h>
+#include <linux/pm_runtime.h>
 
 #include "bmp280.h"
 
@@ -336,6 +337,7 @@ static int bmp280_read_raw(struct iio_dev *indio_dev,
        int ret;
        struct bmp280_data *data = iio_priv(indio_dev);
 
+       pm_runtime_get_sync(data->dev);
        mutex_lock(&data->lock);
 
        switch (mask) {
@@ -380,6 +382,8 @@ static int bmp280_read_raw(struct iio_dev *indio_dev,
        }
 
        mutex_unlock(&data->lock);
+       pm_runtime_mark_last_busy(data->dev);
+       pm_runtime_put_autosuspend(data->dev);
 
        return ret;
 }
@@ -444,6 +448,7 @@ static int bmp280_write_raw(struct iio_dev *indio_dev,
 
        switch (mask) {
        case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+               pm_runtime_get_sync(data->dev);
                mutex_lock(&data->lock);
                switch (chan->type) {
                case IIO_HUMIDITYRELATIVE:
@@ -460,6 +465,8 @@ static int bmp280_write_raw(struct iio_dev *indio_dev,
                        break;
                }
                mutex_unlock(&data->lock);
+               pm_runtime_mark_last_busy(data->dev);
+               pm_runtime_put_autosuspend(data->dev);
                break;
        default:
                return -EINVAL;
@@ -1021,12 +1028,29 @@ int bmp280_common_probe(struct device *dev,
                        goto out_disable_vdda;
        }
 
+       /* Enable runtime PM */
+       pm_runtime_get_noresume(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+       /*
+        * Set autosuspend to two orders of magnitude larger than the
+        * start-up time.
+        */
+       pm_runtime_set_autosuspend_delay(dev, data->start_up_time *100);
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_put(dev);
+
        ret = iio_device_register(indio_dev);
        if (ret)
-               goto out_disable_vdda;
+               goto out_runtime_pm_disable;
+
 
        return 0;
 
+out_runtime_pm_disable:
+       pm_runtime_get_sync(data->dev);
+       pm_runtime_put_noidle(data->dev);
+       pm_runtime_disable(data->dev);
 out_disable_vdda:
        regulator_disable(data->vdda);
 out_disable_vddd:
@@ -1041,12 +1065,51 @@ int bmp280_common_remove(struct device *dev)
        struct bmp280_data *data = iio_priv(indio_dev);
 
        iio_device_unregister(indio_dev);
+       pm_runtime_get_sync(data->dev);
+       pm_runtime_put_noidle(data->dev);
+       pm_runtime_disable(data->dev);
        regulator_disable(data->vdda);
        regulator_disable(data->vddd);
        return 0;
 }
 EXPORT_SYMBOL(bmp280_common_remove);
 
+#ifdef CONFIG_PM
+static int bmp280_runtime_suspend(struct device *dev)
+{
+       struct bmp280_data *data = dev_get_drvdata(dev);
+       int ret;
+
+       ret = regulator_disable(data->vdda);
+       if (ret)
+               return ret;
+       return regulator_disable(data->vddd);
+}
+
+static int bmp280_runtime_resume(struct device *dev)
+{
+       struct bmp280_data *data = dev_get_drvdata(dev);
+       int ret;
+
+       ret = regulator_enable(data->vddd);
+       if (ret)
+               return ret;
+       ret = regulator_enable(data->vdda);
+       if (ret)
+               return ret;
+       msleep(data->start_up_time);
+       return data->chip_info->chip_config(data);
+}
+#endif /* CONFIG_PM */
+
+const struct dev_pm_ops bmp280_dev_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                               pm_runtime_force_resume)
+       SET_RUNTIME_PM_OPS(bmp280_runtime_suspend,
+                          bmp280_runtime_resume, NULL)
+};
+EXPORT_SYMBOL(bmp280_dev_pm_ops);
+
 MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
 MODULE_DESCRIPTION("Driver for Bosch Sensortec BMP180/BMP280 pressure and temperature sensor");
 MODULE_LICENSE("GPL v2");
index 8cf8a900bdaab533fab0c841b14a0a5ca1de874e..03742b15b72a4c1578a05f2df4d5126e1e8a7c7e 100644 (file)
@@ -78,6 +78,7 @@ static struct i2c_driver bmp280_i2c_driver = {
                .name   = "bmp280",
                .acpi_match_table = ACPI_PTR(bmp280_acpi_i2c_match),
                .of_match_table = of_match_ptr(bmp280_of_i2c_match),
+               .pm = &bmp280_dev_pm_ops,
        },
        .probe          = bmp280_i2c_probe,
        .remove         = bmp280_i2c_remove,
index cd365ba74b68f0a56a05cd3ab603440e1c6ac00d..17bc95586f9e23ab7a3afb9f912553625435380a 100644 (file)
@@ -113,6 +113,7 @@ static struct spi_driver bmp280_spi_driver = {
        .driver = {
                .name = "bmp280",
                .of_match_table = bmp280_of_spi_match,
+               .pm = &bmp280_dev_pm_ops,
        },
        .id_table = bmp280_spi_id,
        .probe = bmp280_spi_probe,
index 573334b8e93b6161a9c407192b5af5c6f889da82..2c770e13be0e4ad0f4e884f5778d8b1e318b4a86 100644 (file)
@@ -107,3 +107,6 @@ int bmp280_common_probe(struct device *dev,
                        const char *name,
                        int irq);
 int bmp280_common_remove(struct device *dev);
+
+/* PM ops */
+extern const struct dev_pm_ops bmp280_dev_pm_ops;