Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96c0cb2b authored by Martin Kepplinger's avatar Martin Kepplinger Committed by Jonathan Cameron
Browse files

iio: mma8452: add support for runtime power management



This adds support for runtime power management and, if configured, activates
automatic standby after 2 seconds of inactivity.

Inactivity means no read of acceleration values and no events triggered or
activated.

If CONFIG_PM is not set, this doesn't change anything for existing users.

Signed-off-by: default avatarMartin Kepplinger <martink@posteo.de>
Signed-off-by: default avatarChristoph Muellner <christoph.muellner@theobroma-systems.com>
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
parent e866853d
Loading
Loading
Loading
Loading
+108 −10
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/pm_runtime.h>

#define MMA8452_STATUS				0x00
#define  MMA8452_STATUS_DRDY			(BIT(2) | BIT(1) | BIT(0))
@@ -92,6 +93,8 @@
#define MMA8652_DEVICE_ID			0x4a
#define MMA8653_DEVICE_ID			0x5a

#define MMA8452_AUTO_SUSPEND_DELAY_MS		2000

struct mma8452_data {
	struct i2c_client *client;
	struct mutex lock;
@@ -172,6 +175,31 @@ static int mma8452_drdy(struct mma8452_data *data)
	return -EIO;
}

static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on)
{
#ifdef CONFIG_PM
	int ret;

	if (on) {
		ret = pm_runtime_get_sync(&client->dev);
	} else {
		pm_runtime_mark_last_busy(&client->dev);
		ret = pm_runtime_put_autosuspend(&client->dev);
	}

	if (ret < 0) {
		dev_err(&client->dev,
			"failed to change power state to %d\n", on);
		if (on)
			pm_runtime_put_noidle(&client->dev);

		return ret;
	}
#endif

	return 0;
}

static int mma8452_read(struct mma8452_data *data, __be16 buf[3])
{
	int ret = mma8452_drdy(data);
@@ -179,8 +207,16 @@ static int mma8452_read(struct mma8452_data *data, __be16 buf[3])
	if (ret < 0)
		return ret;

	return i2c_smbus_read_i2c_block_data(data->client, MMA8452_OUT_X,
	ret = mma8452_set_runtime_pm_state(data->client, true);
	if (ret)
		return ret;

	ret = i2c_smbus_read_i2c_block_data(data->client, MMA8452_OUT_X,
					    3 * sizeof(__be16), (u8 *)buf);

	ret = mma8452_set_runtime_pm_state(data->client, false);

	return ret;
}

static ssize_t mma8452_show_int_plus_micros(char *buf, const int (*vals)[2],
@@ -707,7 +743,11 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev,
{
	struct mma8452_data *data = iio_priv(indio_dev);
	const struct mma_chip_info *chip = data->chip_info;
	int val;
	int val, ret;

	ret = mma8452_set_runtime_pm_state(data->client, state);
	if (ret)
		return ret;

	switch (dir) {
	case IIO_EV_DIR_FALLING:
@@ -1139,7 +1179,11 @@ static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig,
{
	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
	struct mma8452_data *data = iio_priv(indio_dev);
	int reg;
	int reg, ret;

	ret = mma8452_set_runtime_pm_state(data->client, state);
	if (ret)
		return ret;

	reg = i2c_smbus_read_byte_data(data->client, MMA8452_CTRL_REG4);
	if (reg < 0)
@@ -1365,6 +1409,15 @@ static int mma8452_probe(struct i2c_client *client,
			goto buffer_cleanup;
	}

	ret = pm_runtime_set_active(&client->dev);
	if (ret < 0)
		goto buffer_cleanup;

	pm_runtime_enable(&client->dev);
	pm_runtime_set_autosuspend_delay(&client->dev,
					 MMA8452_AUTO_SUSPEND_DELAY_MS);
	pm_runtime_use_autosuspend(&client->dev);

	ret = iio_device_register(indio_dev);
	if (ret < 0)
		goto buffer_cleanup;
@@ -1389,6 +1442,11 @@ static int mma8452_remove(struct i2c_client *client)
	struct iio_dev *indio_dev = i2c_get_clientdata(client);

	iio_device_unregister(indio_dev);

	pm_runtime_disable(&client->dev);
	pm_runtime_set_suspended(&client->dev);
	pm_runtime_put_noidle(&client->dev);

	iio_triggered_buffer_cleanup(indio_dev);
	mma8452_trigger_cleanup(indio_dev);
	mma8452_standby(iio_priv(indio_dev));
@@ -1396,6 +1454,45 @@ static int mma8452_remove(struct i2c_client *client)
	return 0;
}

#ifdef CONFIG_PM
static int mma8452_runtime_suspend(struct device *dev)
{
	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
	struct mma8452_data *data = iio_priv(indio_dev);
	int ret;

	mutex_lock(&data->lock);
	ret = mma8452_standby(data);
	mutex_unlock(&data->lock);
	if (ret < 0) {
		dev_err(&data->client->dev, "powering off device failed\n");
		return -EAGAIN;
	}

	return 0;
}

static int mma8452_runtime_resume(struct device *dev)
{
	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
	struct mma8452_data *data = iio_priv(indio_dev);
	int ret, sleep_val;

	ret = mma8452_active(data);
	if (ret < 0)
		return ret;

	ret = mma8452_get_odr_index(data);
	sleep_val = 1000 / mma8452_samp_freq[ret][0];
	if (sleep_val < 20)
		usleep_range(sleep_val * 1000, 20000);
	else
		msleep_interruptible(sleep_val);

	return 0;
}
#endif

#ifdef CONFIG_PM_SLEEP
static int mma8452_suspend(struct device *dev)
{
@@ -1408,13 +1505,14 @@ static int mma8452_resume(struct device *dev)
	return mma8452_active(iio_priv(i2c_get_clientdata(
		to_i2c_client(dev))));
}

static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume);
#define MMA8452_PM_OPS (&mma8452_pm_ops)
#else
#define MMA8452_PM_OPS NULL
#endif

static const struct dev_pm_ops mma8452_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(mma8452_suspend, mma8452_resume)
	SET_RUNTIME_PM_OPS(mma8452_runtime_suspend,
			   mma8452_runtime_resume, NULL)
};

static const struct i2c_device_id mma8452_id[] = {
	{ "mma8452", mma8452 },
	{ "mma8453", mma8453 },
@@ -1428,7 +1526,7 @@ static struct i2c_driver mma8452_driver = {
	.driver = {
		.name	= "mma8452",
		.of_match_table = of_match_ptr(mma8452_dt_ids),
		.pm	= MMA8452_PM_OPS,
		.pm	= &mma8452_pm_ops,
	},
	.probe = mma8452_probe,
	.remove = mma8452_remove,