Commit 4599cac8 authored by Jean-Baptiste Maneyrol's avatar Jean-Baptiste Maneyrol Committed by Jonathan Cameron
Browse files

iio: imu: inv_mpu6050: use runtime pm with autosuspend



Use runtime power management for handling chip power and
sensor engines on/off. Simplifies things a lot since pm
runtime already has reference counter.
Usage of autosuspend reduces the number of power on/off. This
makes polling interface now usable to get data at low
frequency.

Signed-off-by: default avatarJean-Baptiste Maneyrol <jmaneyrol@invensense.com>
Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 5e95ca36
Loading
Loading
Loading
Loading
+175 −95
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include "inv_mpu_iio.h"
#include "inv_mpu_magn.h"

@@ -396,30 +398,18 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
	return 0;
}

int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on)
static int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st,
				     bool power_on)
{
	int result;

	if (power_on) {
		if (!st->powerup_count) {
			result = inv_mpu6050_pwr_mgmt_1_write(st, false, -1, -1);
	result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, -1, -1);
	if (result)
		return result;

	if (power_on)
		usleep_range(INV_MPU6050_REG_UP_TIME_MIN,
			     INV_MPU6050_REG_UP_TIME_MAX);
		}
		st->powerup_count++;
	} else {
		if (st->powerup_count == 1) {
			result = inv_mpu6050_pwr_mgmt_1_write(st, true, -1, -1);
			if (result)
				return result;
		}
		st->powerup_count--;
	}

	dev_dbg(regmap_get_device(st->map), "set power %d, count=%u\n",
		power_on, st->powerup_count);

	return 0;
}
@@ -563,6 +553,7 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev,
					 int *val)
{
	struct inv_mpu6050_state *st = iio_priv(indio_dev);
	struct device *pdev = regmap_get_device(st->map);
	unsigned int freq_hz, period_us, min_sleep_us, max_sleep_us;
	int result;
	int ret;
@@ -571,12 +562,15 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev,
	freq_hz = INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
	period_us = 1000000 / freq_hz;

	result = inv_mpu6050_set_power_itg(st, true);
	if (result)
	result = pm_runtime_get_sync(pdev);
	if (result < 0) {
		pm_runtime_put_noidle(pdev);
		return result;
	}

	switch (chan->type) {
	case IIO_ANGL_VEL:
		if (!st->chip_config.gyro_en) {
			result = inv_mpu6050_switch_engine(st, true,
					INV_MPU6050_SENSOR_GYRO);
			if (result)
@@ -585,14 +579,12 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev,
			min_sleep_us = 2 * period_us;
			max_sleep_us = 2 * (period_us + period_us / 2);
			usleep_range(min_sleep_us, max_sleep_us);
		}
		ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro,
					      chan->channel2, val);
		result = inv_mpu6050_switch_engine(st, false,
				INV_MPU6050_SENSOR_GYRO);
		if (result)
			goto error_power_off;
		break;
	case IIO_ACCEL:
		if (!st->chip_config.accl_en) {
			result = inv_mpu6050_switch_engine(st, true,
					INV_MPU6050_SENSOR_ACCL);
			if (result)
@@ -601,14 +593,12 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev,
			min_sleep_us = period_us;
			max_sleep_us = period_us + period_us / 2;
			usleep_range(min_sleep_us, max_sleep_us);
		}
		ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl,
					      chan->channel2, val);
		result = inv_mpu6050_switch_engine(st, false,
				INV_MPU6050_SENSOR_ACCL);
		if (result)
			goto error_power_off;
		break;
	case IIO_TEMP:
		if (!st->chip_config.temp_en) {
			result = inv_mpu6050_switch_engine(st, true,
					INV_MPU6050_SENSOR_TEMP);
			if (result)
@@ -617,14 +607,12 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev,
			min_sleep_us = period_us;
			max_sleep_us = period_us + period_us / 2;
			usleep_range(min_sleep_us, max_sleep_us);
		}
		ret = inv_mpu6050_sensor_show(st, st->reg->temperature,
					      IIO_MOD_X, val);
		result = inv_mpu6050_switch_engine(st, false,
				INV_MPU6050_SENSOR_TEMP);
		if (result)
			goto error_power_off;
		break;
	case IIO_MAGN:
		if (!st->chip_config.magn_en) {
			result = inv_mpu6050_switch_engine(st, true,
					INV_MPU6050_SENSOR_MAGN);
			if (result)
@@ -638,25 +626,21 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev,
			min_sleep_us = 2 * period_us;
			max_sleep_us = 2 * (period_us + period_us / 2);
			usleep_range(min_sleep_us, max_sleep_us);
		}
		ret = inv_mpu_magn_read(st, chan->channel2, val);
		result = inv_mpu6050_switch_engine(st, false,
				INV_MPU6050_SENSOR_MAGN);
		if (result)
			goto error_power_off;
		break;
	default:
		ret = -EINVAL;
		break;
	}

	result = inv_mpu6050_set_power_itg(st, false);
	if (result)
		goto error_power_off;
	pm_runtime_mark_last_busy(pdev);
	pm_runtime_put_autosuspend(pdev);

	return ret;

error_power_off:
	inv_mpu6050_set_power_itg(st, false);
	pm_runtime_put_autosuspend(pdev);
	return result;
}

@@ -795,6 +779,7 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
				 int val, int val2, long mask)
{
	struct inv_mpu6050_state  *st = iio_priv(indio_dev);
	struct device *pdev = regmap_get_device(st->map);
	int result;

	/*
@@ -806,9 +791,11 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
		return result;

	mutex_lock(&st->lock);
	result = inv_mpu6050_set_power_itg(st, true);
	if (result)
	result = pm_runtime_get_sync(pdev);
	if (result < 0) {
		pm_runtime_put_noidle(pdev);
		goto error_write_raw_unlock;
	}

	switch (mask) {
	case IIO_CHAN_INFO_SCALE:
@@ -846,7 +833,8 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
		break;
	}

	result |= inv_mpu6050_set_power_itg(st, false);
	pm_runtime_mark_last_busy(pdev);
	pm_runtime_put_autosuspend(pdev);
error_write_raw_unlock:
	mutex_unlock(&st->lock);
	iio_device_release_direct_mode(indio_dev);
@@ -903,6 +891,7 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
	int result;
	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
	struct inv_mpu6050_state *st = iio_priv(indio_dev);
	struct device *pdev = regmap_get_device(st->map);

	if (kstrtoint(buf, 10, &fifo_rate))
		return -EINVAL;
@@ -920,9 +909,11 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
		result = 0;
		goto fifo_rate_fail_unlock;
	}
	result = inv_mpu6050_set_power_itg(st, true);
	if (result)
	result = pm_runtime_get_sync(pdev);
	if (result < 0) {
		pm_runtime_put_noidle(pdev);
		goto fifo_rate_fail_unlock;
	}

	result = regmap_write(st->map, st->reg->sample_rate_div, d);
	if (result)
@@ -938,8 +929,9 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
	if (result)
		goto fifo_rate_fail_power_off;

	pm_runtime_mark_last_busy(pdev);
fifo_rate_fail_power_off:
	result |= inv_mpu6050_set_power_itg(st, false);
	pm_runtime_put_autosuspend(pdev);
fifo_rate_fail_unlock:
	mutex_unlock(&st->lock);
	if (result)
@@ -1385,6 +1377,14 @@ static void inv_mpu_core_disable_regulator_action(void *_data)
	inv_mpu_core_disable_regulator_vddio(st);
}

static void inv_mpu_pm_disable(void *data)
{
	struct device *dev = data;

	pm_runtime_put_sync_suspend(dev);
	pm_runtime_disable(dev);
}

int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
		int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type)
{
@@ -1409,7 +1409,6 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
	st = iio_priv(indio_dev);
	mutex_init(&st->lock);
	st->chip_type = chip_type;
	st->powerup_count = 0;
	st->irq = irq;
	st->map = regmap;

@@ -1521,8 +1520,16 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
			goto error_power_off;
	}

	/* chip init is done, turning off */
	result = inv_mpu6050_set_power_itg(st, false);
	/* chip init is done, turning on runtime power management */
	result = pm_runtime_set_active(dev);
	if (result)
		goto error_power_off;
	pm_runtime_get_noresume(dev);
	pm_runtime_enable(dev);
	pm_runtime_set_autosuspend_delay(dev, INV_MPU6050_SUSPEND_DELAY_MS);
	pm_runtime_use_autosuspend(dev);
	pm_runtime_put(dev);
	result = devm_add_action_or_reset(dev, inv_mpu_pm_disable, dev);
	if (result)
		return result;

@@ -1590,11 +1597,10 @@ error_power_off:
}
EXPORT_SYMBOL_GPL(inv_mpu_core_probe);

#ifdef CONFIG_PM_SLEEP

static int inv_mpu_resume(struct device *dev)
static int __maybe_unused inv_mpu_resume(struct device *dev)
{
	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
	struct iio_dev *indio_dev = dev_get_drvdata(dev);
	struct inv_mpu6050_state *st = iio_priv(indio_dev);
	int result;

	mutex_lock(&st->lock);
@@ -1603,27 +1609,101 @@ static int inv_mpu_resume(struct device *dev)
		goto out_unlock;

	result = inv_mpu6050_set_power_itg(st, true);
	if (result)
		goto out_unlock;

	result = inv_mpu6050_switch_engine(st, true, st->suspended_sensors);
	if (result)
		goto out_unlock;

	if (iio_buffer_enabled(indio_dev))
		result = inv_mpu6050_prepare_fifo(st, true);

out_unlock:
	mutex_unlock(&st->lock);

	return result;
}

static int inv_mpu_suspend(struct device *dev)
static int __maybe_unused inv_mpu_suspend(struct device *dev)
{
	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
	struct iio_dev *indio_dev = dev_get_drvdata(dev);
	struct inv_mpu6050_state *st = iio_priv(indio_dev);
	int result;

	mutex_lock(&st->lock);

	if (iio_buffer_enabled(indio_dev)) {
		result = inv_mpu6050_prepare_fifo(st, false);
		if (result)
			goto out_unlock;
	}

	st->suspended_sensors = 0;
	if (st->chip_config.accl_en)
		st->suspended_sensors |= INV_MPU6050_SENSOR_ACCL;
	if (st->chip_config.gyro_en)
		st->suspended_sensors |= INV_MPU6050_SENSOR_GYRO;
	if (st->chip_config.temp_en)
		st->suspended_sensors |= INV_MPU6050_SENSOR_TEMP;
	if (st->chip_config.magn_en)
		st->suspended_sensors |= INV_MPU6050_SENSOR_MAGN;
	result = inv_mpu6050_switch_engine(st, false, st->suspended_sensors);
	if (result)
		goto out_unlock;

	result = inv_mpu6050_set_power_itg(st, false);
	if (result)
		goto out_unlock;

	inv_mpu_core_disable_regulator_vddio(st);
out_unlock:
	mutex_unlock(&st->lock);

	return result;
}
#endif /* CONFIG_PM_SLEEP */

SIMPLE_DEV_PM_OPS(inv_mpu_pmops, inv_mpu_suspend, inv_mpu_resume);
static int __maybe_unused inv_mpu_runtime_suspend(struct device *dev)
{
	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
	unsigned int sensors;
	int ret;

	mutex_lock(&st->lock);

	sensors = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO |
			INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN;
	ret = inv_mpu6050_switch_engine(st, false, sensors);
	if (ret)
		goto out_unlock;

	ret = inv_mpu6050_set_power_itg(st, false);
	if (ret)
		goto out_unlock;

	inv_mpu_core_disable_regulator_vddio(st);

out_unlock:
	mutex_unlock(&st->lock);
	return ret;
}

static int __maybe_unused inv_mpu_runtime_resume(struct device *dev)
{
	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
	int ret;

	ret = inv_mpu_core_enable_regulator_vddio(st);
	if (ret)
		return ret;

	return inv_mpu6050_set_power_itg(st, true);
}

const struct dev_pm_ops inv_mpu_pmops = {
	SET_SYSTEM_SLEEP_PM_OPS(inv_mpu_suspend, inv_mpu_resume)
	SET_RUNTIME_PM_OPS(inv_mpu_runtime_suspend, inv_mpu_runtime_resume, NULL)
};
EXPORT_SYMBOL_GPL(inv_mpu_pmops);

MODULE_AUTHOR("Invensense Corporation");
+3 −2
Original line number Diff line number Diff line
@@ -164,6 +164,7 @@ struct inv_mpu6050_hw {
 *  @magn_disabled:     magnetometer disabled for backward compatibility reason.
 *  @magn_raw_to_gauss:	coefficient to convert mag raw value to Gauss.
 *  @magn_orient:       magnetometer sensor chip orientation if available.
 *  @suspended_sensors:	sensors mask of sensors turned off for suspend
 */
struct inv_mpu6050_state {
	struct mutex lock;
@@ -174,7 +175,6 @@ struct inv_mpu6050_state {
	enum   inv_devices chip_type;
	struct i2c_mux_core *muxc;
	struct i2c_client *mux_client;
	unsigned int powerup_count;
	struct inv_mpu6050_platform_data plat_data;
	struct iio_mount_matrix orientation;
	struct regmap *map;
@@ -189,6 +189,7 @@ struct inv_mpu6050_state {
	bool magn_disabled;
	s32 magn_raw_to_gauss[3];
	struct iio_mount_matrix magn_orient;
	unsigned int suspended_sensors;
};

/*register and associated bit definition*/
@@ -312,6 +313,7 @@ struct inv_mpu6050_state {
#define INV_MPU6050_ACCEL_UP_TIME            20
#define INV_MPU6050_GYRO_UP_TIME             35
#define INV_MPU6050_GYRO_DOWN_TIME           150
#define INV_MPU6050_SUSPEND_DELAY_MS         2000

/* delay time in microseconds */
#define INV_MPU6050_REG_UP_TIME_MIN          5000
@@ -439,7 +441,6 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable);
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
			      unsigned int mask);
int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val);
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on);
int inv_mpu_acpi_create_mux_client(struct i2c_client *client);
void inv_mpu_acpi_delete_mux_client(struct i2c_client *client);
int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
+18 −15
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
* Copyright (C) 2012 Invensense, Inc.
*/

#include <linux/pm_runtime.h>
#include "inv_mpu_iio.h"

static unsigned int inv_scan_query_mpu6050(struct iio_dev *indio_dev)
@@ -156,41 +157,43 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable)
static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
{
	struct inv_mpu6050_state *st = iio_priv(indio_dev);
	struct device *pdev = regmap_get_device(st->map);
	unsigned int scan;
	int result;

	scan = inv_scan_query(indio_dev);

	if (enable) {
		result = inv_mpu6050_set_power_itg(st, true);
		if (result)
		scan = inv_scan_query(indio_dev);
		result = pm_runtime_get_sync(pdev);
		if (result < 0) {
			pm_runtime_put_noidle(pdev);
			return result;
		}
		/*
		 * In case autosuspend didn't trigger, turn off first not
		 * required sensors.
		 */
		result = inv_mpu6050_switch_engine(st, false, ~scan);
		if (result)
			goto error_power_off;
		result = inv_mpu6050_switch_engine(st, true, scan);
		if (result)
			goto error_power_off;
		st->skip_samples = inv_compute_skip_samples(st);
		result = inv_mpu6050_prepare_fifo(st, true);
		if (result)
			goto error_sensors_off;
			goto error_power_off;
	} else {
		result = inv_mpu6050_prepare_fifo(st, false);
		if (result)
			goto error_sensors_off;
		result = inv_mpu6050_switch_engine(st, false, scan);
		if (result)
			goto error_power_off;

		result = inv_mpu6050_set_power_itg(st, false);
		if (result)
			goto error_power_off;
		pm_runtime_mark_last_busy(pdev);
		pm_runtime_put_autosuspend(pdev);
	}

	return 0;

error_sensors_off:
	inv_mpu6050_switch_engine(st, false, scan);
error_power_off:
	inv_mpu6050_set_power_itg(st, false);
	pm_runtime_put_autosuspend(pdev);
	return result;
}