From: Jonathan Cameron <jic23@kernel.org>
To: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Cc: linux-iio@vger.kernel.org
Subject: Re: [PATCH v2 1/2] iio: accel: kxcjk-1013: support runtime pm
Date: Thu, 07 Aug 2014 11:25:24 +0100 [thread overview]
Message-ID: <53E35414.8050403@kernel.org> (raw)
In-Reply-To: <1407275881-29139-2-git-send-email-srinivas.pandruvada@linux.intel.com>
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@linux.intel.com>
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,
>
next prev parent reply other threads:[~2014-08-07 10:25 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-08-05 21:57 [PATCH v2 0/2] kxcjk-1013 updates after last review Srinivas Pandruvada
2014-08-05 21:58 ` [PATCH v2 1/2] iio: accel: kxcjk-1013: support runtime pm Srinivas Pandruvada
2014-08-07 10:25 ` Jonathan Cameron [this message]
2014-08-05 21:58 ` [PATCH v2 2/2] iio: accel: kxcjk-1013: Set adjustable range Srinivas Pandruvada
2014-08-07 10:27 ` Jonathan Cameron
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=53E35414.8050403@kernel.org \
--to=jic23@kernel.org \
--cc=linux-iio@vger.kernel.org \
--cc=srinivas.pandruvada@linux.intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).