On 11/01/15 19:10, Irina Tirdea wrote: > Add support for runtime pm to reduce the power consumed by the device > when not used. > > If CONFIG_PM is not enabled, the device will be powered on at > init and only powered off on system suspend. > > If CONFIG_PM is enabled, runtime pm autosuspend is used: > - for raw reads will keep the device on for a specified time > - for events it will keep the device on as long as we have at least > one event active > > Signed-off-by: Irina Tirdea <irina.tirdea@xxxxxxxxx> > Reviewed-by: Vlad Dogaru <vlad.dogaru@xxxxxxxxx> Looks good. Applied to the togreg branch of iio.git (at least we are driving down the size of the patch set for the next revision!) Jonathan > --- > drivers/iio/accel/mma9551.c | 162 +++++++++++++++++++++++++++++++++++++------- > 1 file changed, 139 insertions(+), 23 deletions(-) > > diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c > index 6563e26..f1a5a06 100644 > --- a/drivers/iio/accel/mma9551.c > +++ b/drivers/iio/accel/mma9551.c > @@ -22,6 +22,7 @@ > #include <linux/iio/iio.h> > #include <linux/iio/sysfs.h> > #include <linux/iio/events.h> > +#include <linux/pm_runtime.h> > > #define MMA9551_DRV_NAME "mma9551" > #define MMA9551_IRQ_NAME "mma9551_event" > @@ -71,6 +72,7 @@ enum mma9551_gpio_pin { > /* Sleep/Wake application */ > #define MMA9551_SLEEP_CFG 0x06 > #define MMA9551_SLEEP_CFG_SNCEN BIT(0) > +#define MMA9551_SLEEP_CFG_FLEEN BIT(1) > #define MMA9551_SLEEP_CFG_SCHEN BIT(2) > > /* AFE application */ > @@ -114,6 +116,9 @@ enum mma9551_tilt_axis { > #define MMA9551_I2C_READ_RETRIES 5 > #define MMA9551_I2C_READ_DELAY 50 /* us */ > > +#define MMA9551_DEFAULT_SAMPLE_RATE 122 /* Hz */ > +#define MMA9551_AUTO_SUSPEND_DELAY_MS 2000 > + > struct mma9551_mbox_request { > u8 start_mbox; /* Always 0. */ > u8 app_id; > @@ -387,16 +392,55 @@ static int mma9551_read_version(struct i2c_client *client) > } > > /* > + * Power on chip and enable doze mode. > * Use 'false' as the second parameter to cause the device to enter > * sleep. > */ > -static int mma9551_set_device_state(struct i2c_client *client, > - bool enable) > +static int mma9551_set_device_state(struct i2c_client *client, bool enable) > { > return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE, > MMA9551_SLEEP_CFG, > - MMA9551_SLEEP_CFG_SNCEN, > - enable ? 0 : MMA9551_SLEEP_CFG_SNCEN); > + MMA9551_SLEEP_CFG_SNCEN | > + MMA9551_SLEEP_CFG_FLEEN | > + MMA9551_SLEEP_CFG_SCHEN, > + enable ? MMA9551_SLEEP_CFG_SCHEN | > + MMA9551_SLEEP_CFG_FLEEN : > + MMA9551_SLEEP_CFG_SNCEN); > +} > + > +static int mma9551_set_power_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 void mma9551_sleep(int freq) > +{ > + int sleep_val = 1000 / freq; > + > + if (sleep_val < 20) > + usleep_range(sleep_val * 1000, 20000); > + else > + msleep_interruptible(sleep_val); > } > > static int mma9551_read_incli_chan(struct i2c_client *client, > @@ -424,15 +468,19 @@ static int mma9551_read_incli_chan(struct i2c_client *client, > return -EINVAL; > } > > + ret = mma9551_set_power_state(client, true); > + if (ret < 0) > + return ret; > + > ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT, > reg_addr, &angle); > if (ret < 0) > - return ret; > + goto out_poweroff; > > ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT, > MMA9551_TILT_QUAD_REG, &quadrant); > if (ret < 0) > - return ret; > + goto out_poweroff; > > angle &= ~MMA9551_TILT_ANGFLG; > quadrant = (quadrant >> quad_shift) & 0x03; > @@ -442,7 +490,11 @@ static int mma9551_read_incli_chan(struct i2c_client *client, > else > *val = angle + 90 * quadrant; > > - return IIO_VAL_INT; > + ret = IIO_VAL_INT; > + > +out_poweroff: > + mma9551_set_power_state(client, false); > + return ret; > } > > static int mma9551_read_accel_chan(struct i2c_client *client, > @@ -467,14 +519,22 @@ static int mma9551_read_accel_chan(struct i2c_client *client, > return -EINVAL; > } > > + ret = mma9551_set_power_state(client, true); > + if (ret < 0) > + return ret; > + > ret = mma9551_read_status_word(client, MMA9551_APPID_AFE, > reg_addr, &raw_accel); > if (ret < 0) > - return ret; > + goto out_poweroff; > > *val = raw_accel; > > - return IIO_VAL_INT; > + ret = IIO_VAL_INT; > + > +out_poweroff: > + mma9551_set_power_state(client, false); > + return ret; > } > > static int mma9551_read_raw(struct iio_dev *indio_dev, > @@ -556,6 +616,10 @@ static int mma9551_config_incli_event(struct iio_dev *indio_dev, > MMA9551_APPID_NONE, 0, 0); > if (ret < 0) > return ret; > + > + ret = mma9551_set_power_state(data->client, false); > + if (ret < 0) > + return ret; > } else { > int bitnum; > > @@ -574,11 +638,18 @@ static int mma9551_config_incli_event(struct iio_dev *indio_dev, > return -EINVAL; > } > > + Trivial but one blank line here is enough... > + ret = mma9551_set_power_state(data->client, true); > + if (ret < 0) > + return ret; > + > ret = mma9551_gpio_config(data->client, > (enum mma9551_gpio_pin)mma_axis, > MMA9551_APPID_TILT, bitnum, 0); > - if (ret < 0) > + if (ret < 0) { > + mma9551_set_power_state(data->client, false); > return ret; > + } > } > > data->event_enabled[mma_axis] = state; > @@ -771,12 +842,7 @@ static int mma9551_init(struct mma9551_data *data) > if (ret) > return ret; > > - /* Power on chip and enable doze mode. */ > - return mma9551_update_config_bits(data->client, > - MMA9551_APPID_SLEEP_WAKE, > - MMA9551_SLEEP_CFG, > - MMA9551_SLEEP_CFG_SCHEN | MMA9551_SLEEP_CFG_SNCEN, > - MMA9551_SLEEP_CFG_SCHEN); > + return mma9551_set_device_state(data->client, true); > } > > static int mma9551_gpio_probe(struct iio_dev *indio_dev) > @@ -869,8 +935,19 @@ static int mma9551_probe(struct i2c_client *client, > goto out_poweroff; > } > > + ret = pm_runtime_set_active(&client->dev); > + if (ret < 0) > + goto out_iio_unregister; > + > + pm_runtime_enable(&client->dev); > + pm_runtime_set_autosuspend_delay(&client->dev, > + MMA9551_AUTO_SUSPEND_DELAY_MS); > + pm_runtime_use_autosuspend(&client->dev); > + > return 0; > > +out_iio_unregister: > + iio_device_unregister(indio_dev); > out_poweroff: > mma9551_set_device_state(client, false); > > @@ -882,6 +959,10 @@ static int mma9551_remove(struct i2c_client *client) > struct iio_dev *indio_dev = i2c_get_clientdata(client); > struct mma9551_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); > mutex_lock(&data->mutex); > mma9551_set_device_state(data->client, false); > @@ -890,37 +971,72 @@ static int mma9551_remove(struct i2c_client *client) > return 0; > } > > +#ifdef CONFIG_PM > +static int mma9551_runtime_suspend(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct mma9551_data *data = iio_priv(indio_dev); > + int ret; > + > + mutex_lock(&data->mutex); > + ret = mma9551_set_device_state(data->client, false); > + mutex_unlock(&data->mutex); > + if (ret < 0) { > + dev_err(&data->client->dev, "powering off device failed\n"); > + return -EAGAIN; > + } > + > + return 0; > +} > + > +static int mma9551_runtime_resume(struct device *dev) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > + struct mma9551_data *data = iio_priv(indio_dev); > + int ret; > + > + ret = mma9551_set_device_state(data->client, true); > + if (ret < 0) > + return ret; > + > + mma9551_sleep(MMA9551_DEFAULT_SAMPLE_RATE); > + > + return 0; > +} > +#endif > + > #ifdef CONFIG_PM_SLEEP > static int mma9551_suspend(struct device *dev) > { > struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > struct mma9551_data *data = iio_priv(indio_dev); > + int ret; > > mutex_lock(&data->mutex); > - mma9551_set_device_state(data->client, false); > + ret = mma9551_set_device_state(data->client, false); > mutex_unlock(&data->mutex); > > - return 0; > + return ret; > } > > static int mma9551_resume(struct device *dev) > { > struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); > struct mma9551_data *data = iio_priv(indio_dev); > + int ret; > > mutex_lock(&data->mutex); > - mma9551_set_device_state(data->client, true); > + ret = mma9551_set_device_state(data->client, true); > mutex_unlock(&data->mutex); > > - return 0; > + return ret; > } > -#else > -#define mma9551_suspend NULL > -#define mma9551_resume NULL > #endif > > static const struct dev_pm_ops mma9551_pm_ops = { > SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume) > + SET_RUNTIME_PM_OPS(mma9551_runtime_suspend, > + mma9551_runtime_resume, NULL) > }; > > static const struct acpi_device_id mma9551_acpi_match[] = { > -- 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