From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= <megi@xff.cz>
Date: Thu, 10 Feb 2022 02:09:24 +0100
Subject: iio: af8133j: Add runtime power management

Power down the sensor when not in use and during system sleep.

Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
 drivers/iio/magnetometer/af8133j.c | 100 ++++++++--
 1 file changed, 81 insertions(+), 19 deletions(-)

diff --git a/drivers/iio/magnetometer/af8133j.c b/drivers/iio/magnetometer/af8133j.c
index 5fc684802663..5b84859a85ea 100644
--- a/drivers/iio/magnetometer/af8133j.c
+++ b/drivers/iio/magnetometer/af8133j.c
@@ -11,6 +11,7 @@
 #include <linux/delay.h>
 #include <linux/regmap.h>
 #include <linux/gpio/consumer.h>
+#include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 
 #include <linux/iio/iio.h>
@@ -54,6 +55,7 @@ struct af8133j_data {
 	struct gpio_desc *reset_gpiod;
 	struct iio_mount_matrix orientation;
         struct regulator_bulk_data supplies[AF8133J_NUM_SUPPLIES];
+	bool powered;
 };
 
 enum af8133j_axis {
@@ -97,6 +99,9 @@ static int af8133j_power_up(struct af8133j_data *data)
 	unsigned int val;
 	int ret;
 
+	if (data->powered)
+		return 0;
+
         ret = regulator_bulk_enable(AF8133J_NUM_SUPPLIES, data->supplies);
         if (ret) {
                 dev_err(dev, "Could not enable regulators\n");
@@ -144,6 +149,7 @@ static int af8133j_power_up(struct af8133j_data *data)
 		goto out_assert_reset;
 	}
 
+	data->powered = true;
 	return 0;
 
 out_assert_reset:
@@ -154,8 +160,14 @@ static int af8133j_power_up(struct af8133j_data *data)
 
 static void af8133j_power_down(struct af8133j_data *data)
 {
+	struct device *dev = &data->client->dev;
+
+	if (!data->powered)
+		return;
+
 	gpiod_set_value_cansleep(data->reset_gpiod, 1);
         regulator_bulk_disable(AF8133J_NUM_SUPPLIES, data->supplies);
+	data->powered = false;
 }
 
 static int af8133j_take_measurement(struct af8133j_data *data)
@@ -185,14 +197,29 @@ static int af8133j_take_measurement(struct af8133j_data *data)
 
 static int af8133j_read_measurement(struct af8133j_data *data, __le16 buf[3])
 {
+	struct device *dev = &data->client->dev;
 	int ret;
 
-	ret = af8133j_take_measurement(data);
-	if (ret < 0)
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret) {
+		dev_err(dev, "failed to power on\n");
 		return ret;
+	}
 
-	return regmap_bulk_read(data->regmap, AF8133J_REG_OUT, buf,
-				3 * sizeof(__le16));
+	mutex_lock(&data->mutex);
+
+	ret = af8133j_take_measurement(data);
+	if (ret == 0)
+		ret = regmap_bulk_read(data->regmap, AF8133J_REG_OUT, buf,
+				       3 * sizeof(__le16));
+
+	mutex_unlock(&data->mutex);
+
+	pm_runtime_mark_last_busy(dev);
+	if (pm_runtime_put_autosuspend(dev))
+		dev_err(dev, "failed to power off\n");
+
+	return ret;
 }
 
 static int af8133j_read_raw(struct iio_dev *indio_dev,
@@ -200,16 +227,15 @@ static int af8133j_read_raw(struct iio_dev *indio_dev,
 			     int *val2, long mask)
 {
 	struct af8133j_data *data = iio_priv(indio_dev);
-	int ret;
 	__le16 buf[3];
+	int ret;
 
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
-		mutex_lock(&data->mutex);
 		ret = af8133j_read_measurement(data, buf);
-		mutex_unlock(&data->mutex);
 		if (ret < 0)
 			return ret;
+
 		*val = sign_extend32(le16_to_cpu(buf[chan->address]), 15);
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SCALE:
@@ -238,18 +264,19 @@ static const struct regmap_config af8133j_regmap_config = {
 
 static int af8133j_probe(struct i2c_client *client)
 {
+	struct device *dev = &client->dev;
 	struct af8133j_data *data;
 	struct iio_dev *indio_dev;
 	struct regmap *regmap;
 	int ret, i;
 
-	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
 	if (!indio_dev)
 		return -ENOMEM;
 
 	regmap = devm_regmap_init_i2c(client, &af8133j_regmap_config);
 	if (IS_ERR(regmap))
-		return dev_err_probe(&client->dev, PTR_ERR(regmap),
+		return dev_err_probe(dev, PTR_ERR(regmap),
 				     "regmap initialization failed\n");
 
 	data = iio_priv(indio_dev);
@@ -258,20 +285,20 @@ static int af8133j_probe(struct i2c_client *client)
 	data->regmap = regmap;
 	mutex_init(&data->mutex);
 
-	data->reset_gpiod = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH);
+	data->reset_gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
 	if (IS_ERR(data->reset_gpiod))
-		return dev_err_probe(&client->dev, PTR_ERR(data->reset_gpiod),
+		return dev_err_probe(dev, PTR_ERR(data->reset_gpiod),
 				     "Failed to get reset gpio\n");
 
         for (i = 0; i < AF8133J_NUM_SUPPLIES; i++)
                 data->supplies[i].supply = af8133j_supply_names[i];
-        ret = devm_regulator_bulk_get(&client->dev, AF8133J_NUM_SUPPLIES, data->supplies);
+        ret = devm_regulator_bulk_get(dev, AF8133J_NUM_SUPPLIES, data->supplies);
         if (ret)
 		return ret;
 
-	ret = iio_read_mount_matrix(&client->dev, &data->orientation);
+	ret = iio_read_mount_matrix(dev, &data->orientation);
 	if (ret)
-		return dev_err_probe(&client->dev, ret,
+		return dev_err_probe(dev, ret,
 				     "Failed to read mount matrix\n");
 
 	/*
@@ -281,17 +308,23 @@ static int af8133j_probe(struct i2c_client *client)
 	if (ret)
 		return ret;
 
+	af8133j_power_down(data);
+
 	indio_dev->info = &af8133j_info;
 	indio_dev->name = AF8133J_DRV_NAME;
 	indio_dev->channels = af8133j_channels;
 	indio_dev->num_channels = ARRAY_SIZE(af8133j_channels);
 	indio_dev->modes = INDIO_DIRECT_MODE;
 
-	ret = devm_iio_device_register(&client->dev, indio_dev);
+	ret = devm_iio_device_register(dev, indio_dev);
 	if (ret)
-		return dev_err_probe(&client->dev, ret,
+		return dev_err_probe(dev, ret,
 				     "Failed to register iio device");
 
+	pm_runtime_enable(dev);
+	pm_runtime_set_autosuspend_delay(dev, 500);
+	pm_runtime_use_autosuspend(dev);
+
 	return 0;
 }
 
@@ -299,10 +332,38 @@ static void af8133j_remove(struct i2c_client *client)
 {
 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
 	struct af8133j_data *data = iio_priv(indio_dev);
+	struct device *dev = &data->client->dev;
+
+	pm_runtime_disable(dev);
+	pm_runtime_set_suspended(dev);
+	pm_runtime_put_noidle(dev);
+
+	af8133j_power_down(data);
+}
+
+static int __maybe_unused af8133j_runtime_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct af8133j_data *data = iio_priv(indio_dev);
 
 	af8133j_power_down(data);
+
+	return 0;
 }
 
+static int __maybe_unused af8133j_runtime_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct af8133j_data *data = iio_priv(indio_dev);
+
+	return af8133j_power_up(data);
+}
+
+const struct dev_pm_ops af8133j_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+	SET_RUNTIME_PM_OPS(af8133j_runtime_suspend, af8133j_runtime_resume, NULL)
+};
+
 static const struct of_device_id af8133j_of_match[] = {
 	{ .compatible = "voltafield,af8133j", },
 	{ }
@@ -319,10 +380,11 @@ static struct i2c_driver af8133j_driver = {
 	.driver = {
 		.name = AF8133J_DRV_NAME,
 		.of_match_table = af8133j_of_match,
+		.pm = &af8133j_pm_ops,
 	},
-	.probe		= af8133j_probe,
-	.remove		= af8133j_remove,
-	.id_table	= af8133j_id,
+	.probe = af8133j_probe,
+	.remove = af8133j_remove,
+	.id_table = af8133j_id,
 };
 
 module_i2c_driver(af8133j_driver);
-- 
Armbian

