Re: [PATCH v2 1/2] iio: accel: kxcjk-1013: support runtime pm

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 05/08/14 22:58, Srinivas Pandruvada wrote:
> In an effort to improve raw read performance and at the same time enter
> low power state at every possible chance.
> For raw reads, it will keep the system powered on for a default or user
> specified time, via autosuspend_delay attribute of device power.
> This will help read multiple samples without power on/off sequence.
> For triggers it will keep the system on till, requested to be turned
> off by trigger state by utilizing run time PM usage counters.
> 
> When runtime pm is not enabled, then it keeps the chip in operation
> mode always.
> 
> Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@xxxxxxxxxxxxxxx>
Looks good.

Applied to the togreg branch of iio.git - initiallly pushed out as
testing.  Thanks.

J
> ---
>  drivers/iio/accel/kxcjk-1013.c | 206 +++++++++++++++++++++++++++++++----------
>  1 file changed, 156 insertions(+), 50 deletions(-)
> 
> diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c
> index 7941cf2..b32bdd1 100644
> --- a/drivers/iio/accel/kxcjk-1013.c
> +++ b/drivers/iio/accel/kxcjk-1013.c
> @@ -21,6 +21,8 @@
>  #include <linux/string.h>
>  #include <linux/acpi.h>
>  #include <linux/gpio/consumer.h>
> +#include <linux/pm.h>
> +#include <linux/pm_runtime.h>
>  #include <linux/iio/iio.h>
>  #include <linux/iio/sysfs.h>
>  #include <linux/iio/buffer.h>
> @@ -71,15 +73,17 @@
>  #define KXCJK1013_DATA_MASK_12_BIT	0x0FFF
>  #define KXCJK1013_MAX_STARTUP_TIME_US	100000
>  
> +#define KXCJK1013_SLEEP_DELAY_MS	2000
> +
>  struct kxcjk1013_data {
>  	struct i2c_client *client;
>  	struct iio_trigger *trig;
>  	bool trig_mode;
>  	struct mutex mutex;
>  	s16 buffer[8];
> -	int power_state;
>  	u8 odr_bits;
>  	bool active_high_intr;
> +	bool trigger_on;
>  };
>  
>  enum kxcjk1013_axis {
> @@ -138,6 +142,25 @@ static int kxcjk1013_set_mode(struct kxcjk1013_data *data,
>  	return 0;
>  }
>  
> +static int kxcjk1013_get_mode(struct kxcjk1013_data *data,
> +			      enum kxcjk1013_mode *mode)
> +{
> +	int ret;
> +
> +	ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1);
> +	if (ret < 0) {
> +		dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
> +		return ret;
> +	}
> +
> +	if (ret & KXCJK1013_REG_CTRL1_BIT_PC1)
> +		*mode = OPERATION;
> +	else
> +		*mode = STANDBY;
> +
> +	return 0;
> +}
> +
>  static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
>  {
>  	int ret;
> @@ -201,6 +224,41 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
>  		return ret;
>  	}
>  
> +	ret = kxcjk1013_set_mode(data, OPERATION);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int kxcjk1013_get_startup_times(struct kxcjk1013_data *data)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(odr_start_up_times); ++i) {
> +		if (odr_start_up_times[i].odr_bits == data->odr_bits)
> +			return odr_start_up_times[i].usec;
> +	}
> +
> +	return KXCJK1013_MAX_STARTUP_TIME_US;
> +}
> +
> +static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
> +{
> +	int ret;
> +
> +	if (on)
> +		ret = pm_runtime_get_sync(&data->client->dev);
> +	else {
> +		pm_runtime_mark_last_busy(&data->client->dev);
> +		ret = pm_runtime_put_autosuspend(&data->client->dev);
> +	}
> +	if (ret < 0) {
> +		dev_err(&data->client->dev,
> +			"Failed: kxcjk1013_set_power_state for %d\n", on);
> +		return ret;
> +	}
> +
>  	return 0;
>  }
>  
> @@ -208,6 +266,11 @@ static int kxcjk1013_chip_setup_interrupt(struct kxcjk1013_data *data,
>  					  bool status)
>  {
>  	int ret;
> +	enum kxcjk1013_mode store_mode;
> +
> +	ret = kxcjk1013_get_mode(data, &store_mode);
> +	if (ret < 0)
> +		return ret;
>  
>  	/* This is requirement by spec to change state to STANDBY */
>  	ret = kxcjk1013_set_mode(data, STANDBY);
> @@ -250,7 +313,13 @@ static int kxcjk1013_chip_setup_interrupt(struct kxcjk1013_data *data,
>  		return ret;
>  	}
>  
> -	return ret;
> +	if (store_mode == OPERATION) {
> +		ret = kxcjk1013_set_mode(data, OPERATION);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
>  }
>  
>  static int kxcjk1013_convert_freq_to_bit(int val, int val2)
> @@ -271,6 +340,11 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2)
>  {
>  	int ret;
>  	int odr_bits;
> +	enum kxcjk1013_mode store_mode;
> +
> +	ret = kxcjk1013_get_mode(data, &store_mode);
> +	if (ret < 0)
> +		return ret;
>  
>  	odr_bits = kxcjk1013_convert_freq_to_bit(val, val2);
>  	if (odr_bits < 0)
> @@ -290,9 +364,7 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2)
>  
>  	data->odr_bits = odr_bits;
>  
> -	/* Check, if the ODR is changed after data enable */
> -	if (data->power_state) {
> -		/* Set the state back to operation */
> +	if (store_mode == OPERATION) {
>  		ret = kxcjk1013_set_mode(data, OPERATION);
>  		if (ret < 0)
>  			return ret;
> @@ -331,18 +403,6 @@ static int kxcjk1013_get_acc_reg(struct kxcjk1013_data *data, int axis)
>  	return ret;
>  }
>  
> -static int kxcjk1013_get_startup_times(struct kxcjk1013_data *data)
> -{
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(odr_start_up_times); ++i) {
> -		if (odr_start_up_times[i].odr_bits == data->odr_bits)
> -			return odr_start_up_times[i].usec;
> -	}
> -
> -	return KXCJK1013_MAX_STARTUP_TIME_US;
> -}
> -
>  static int kxcjk1013_read_raw(struct iio_dev *indio_dev,
>  			      struct iio_chan_spec const *chan, int *val,
>  			      int *val2, long mask)
> @@ -356,29 +416,25 @@ static int kxcjk1013_read_raw(struct iio_dev *indio_dev,
>  		if (iio_buffer_enabled(indio_dev))
>  			ret = -EBUSY;
>  		else {
> -			int sleep_val;
> -
> -			ret = kxcjk1013_set_mode(data, OPERATION);
> +			ret = kxcjk1013_set_power_state(data, true);
>  			if (ret < 0) {
>  				mutex_unlock(&data->mutex);
>  				return ret;
>  			}
> -			++data->power_state;
> -			sleep_val = kxcjk1013_get_startup_times(data);
> -			if (sleep_val < 20000)
> -				usleep_range(sleep_val, 20000);
> -			else
> -				msleep_interruptible(sleep_val/1000);
>  			ret = kxcjk1013_get_acc_reg(data, chan->scan_index);
> -			if (--data->power_state == 0)
> -				kxcjk1013_set_mode(data, STANDBY);
> +			if (ret < 0) {
> +				kxcjk1013_set_power_state(data, false);
> +				mutex_unlock(&data->mutex);
> +				return ret;
> +			}
> +			*val = sign_extend32(ret >> 4, 11);
> +			ret = kxcjk1013_set_power_state(data, false);
>  		}
>  		mutex_unlock(&data->mutex);
>  
>  		if (ret < 0)
>  			return ret;
>  
> -		*val = sign_extend32(ret >> 4, 11);
>  		return IIO_VAL_INT;
>  
>  	case IIO_CHAN_INFO_SCALE:
> @@ -520,20 +576,21 @@ static int kxcjk1013_data_rdy_trigger_set_state(struct iio_trigger *trig,
>  {
>  	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
>  	struct kxcjk1013_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (state && data->trigger_on)
> +		return 0;
>  
>  	mutex_lock(&data->mutex);
> -	if (state) {
> -		kxcjk1013_chip_setup_interrupt(data, true);
> -		kxcjk1013_set_mode(data, OPERATION);
> -		++data->power_state;
> -	} else {
> -		if (--data->power_state) {
> +	ret = kxcjk1013_chip_setup_interrupt(data, state);
> +	if (!ret) {
> +		ret = kxcjk1013_set_power_state(data, state);
> +		if (ret < 0) {
>  			mutex_unlock(&data->mutex);
> -			return 0;
> +			return ret;
>  		}
> -		kxcjk1013_chip_setup_interrupt(data, false);
> -		kxcjk1013_set_mode(data, STANDBY);
>  	}
> +	data->trigger_on = state;
>  	mutex_unlock(&data->mutex);
>  
>  	return 0;
> @@ -661,14 +718,25 @@ static int kxcjk1013_probe(struct i2c_client *client,
>  		}
>  	}
>  
> -	ret = devm_iio_device_register(&client->dev, indio_dev);
> +	ret = iio_device_register(indio_dev);
>  	if (ret < 0) {
>  		dev_err(&client->dev, "unable to register iio device\n");
>  		goto err_buffer_cleanup;
>  	}
>  
> +	ret = pm_runtime_set_active(&client->dev);
> +	if (ret)
> +		goto err_iio_unregister;
> +
> +	pm_runtime_enable(&client->dev);
> +	pm_runtime_set_autosuspend_delay(&client->dev,
> +					 KXCJK1013_SLEEP_DELAY_MS);
> +	pm_runtime_use_autosuspend(&client->dev);
> +
>  	return 0;
>  
> +err_iio_unregister:
> +	iio_device_unregister(indio_dev);
>  err_buffer_cleanup:
>  	if (data->trig_mode)
>  		iio_triggered_buffer_cleanup(indio_dev);
> @@ -687,6 +755,12 @@ static int kxcjk1013_remove(struct i2c_client *client)
>  	struct iio_dev *indio_dev = i2c_get_clientdata(client);
>  	struct kxcjk1013_data *data = iio_priv(indio_dev);
>  
> +	pm_runtime_disable(&client->dev);
> +	pm_runtime_set_suspended(&client->dev);
> +	pm_runtime_put_noidle(&client->dev);
> +
> +	iio_device_unregister(indio_dev);
> +
>  	if (data->trig_mode) {
>  		iio_triggered_buffer_cleanup(indio_dev);
>  		iio_trigger_unregister(data->trig);
> @@ -705,35 +779,67 @@ static int kxcjk1013_suspend(struct device *dev)
>  {
>  	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
>  	struct kxcjk1013_data *data = iio_priv(indio_dev);
> +	int ret;
>  
>  	mutex_lock(&data->mutex);
> -	kxcjk1013_set_mode(data, STANDBY);
> +	ret = kxcjk1013_set_mode(data, STANDBY);
>  	mutex_unlock(&data->mutex);
>  
> -	return 0;
> +	return ret;
>  }
>  
>  static int kxcjk1013_resume(struct device *dev)
>  {
>  	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
>  	struct kxcjk1013_data *data = iio_priv(indio_dev);
> +	int ret = 0;
>  
>  	mutex_lock(&data->mutex);
> +	/* Check, if the suspend occured while active */
> +	if (data->trigger_on)
> +		ret = kxcjk1013_set_mode(data, OPERATION);
> +	mutex_unlock(&data->mutex);
>  
> -	if (data->power_state)
> -		kxcjk1013_set_mode(data, OPERATION);
> +	return ret;
> +}
> +#endif
>  
> -	mutex_unlock(&data->mutex);
> +#ifdef CONFIG_PM_RUNTIME
> +static int kxcjk1013_runtime_suspend(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> +	struct kxcjk1013_data *data = iio_priv(indio_dev);
>  
> -	return 0;
> +	return kxcjk1013_set_mode(data, STANDBY);
>  }
>  
> -static SIMPLE_DEV_PM_OPS(kxcjk1013_pm_ops, kxcjk1013_suspend, kxcjk1013_resume);
> -#define KXCJK1013_PM_OPS (&kxcjk1013_pm_ops)
> -#else
> -#define KXCJK1013_PM_OPS NULL
> +static int kxcjk1013_runtime_resume(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> +	struct kxcjk1013_data *data = iio_priv(indio_dev);
> +	int ret;
> +	int sleep_val;
> +
> +	ret = kxcjk1013_set_mode(data, OPERATION);
> +	if (ret < 0)
> +		return ret;
> +
> +	sleep_val = kxcjk1013_get_startup_times(data);
> +	if (sleep_val < 20000)
> +		usleep_range(sleep_val, 20000);
> +	else
> +		msleep_interruptible(sleep_val/1000);
> +
> +	return 0;
> +}
>  #endif
>  
> +static const struct dev_pm_ops kxcjk1013_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(kxcjk1013_suspend, kxcjk1013_resume)
> +	SET_RUNTIME_PM_OPS(kxcjk1013_runtime_suspend,
> +			   kxcjk1013_runtime_resume, NULL)
> +};
> +
>  static const struct acpi_device_id kx_acpi_match[] = {
>  	{"KXCJ1013", 0},
>  	{ },
> @@ -751,7 +857,7 @@ static struct i2c_driver kxcjk1013_driver = {
>  	.driver = {
>  		.name	= KXCJK1013_DRV_NAME,
>  		.acpi_match_table = ACPI_PTR(kx_acpi_match),
> -		.pm	= KXCJK1013_PM_OPS,
> +		.pm	= &kxcjk1013_pm_ops,
>  	},
>  	.probe		= kxcjk1013_probe,
>  	.remove		= kxcjk1013_remove,
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux