iio: light: rpr0521 on-off sequence change for CONFIG_PM

Refactor _set_power_state(), _resume() and _suspend().
Enable measurement only when needed, not in _init(). System can suspend
during measurement and measurement is continued on resume.
Pm turns off measurement when both ps and als measurements are disabled for
2 seconds. During off-time the power save is 20-500mA, typically 180mA.

Signed-off-by: Mikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
Acked-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
This commit is contained in:
Mikko Koivunen 2017-05-18 15:12:51 +03:00 committed by Jonathan Cameron
parent 12d7494913
commit 484c314b90
1 changed files with 46 additions and 24 deletions

View File

@ -9,7 +9,7 @@
*
* IIO driver for RPR-0521RS (7-bit I2C slave address 0x38).
*
* TODO: illuminance channel, PM support, buffer
* TODO: illuminance channel, buffer
*/
#include <linux/module.h>
@ -142,9 +142,11 @@ struct rpr0521_data {
bool als_dev_en;
bool pxs_dev_en;
/* optimize runtime pm ops - enable device only if needed */
/* optimize runtime pm ops - enable/disable device only if needed */
bool als_ps_need_en;
bool pxs_ps_need_en;
bool als_need_dis;
bool pxs_need_dis;
struct regmap *regmap;
};
@ -230,40 +232,32 @@ static int rpr0521_pxs_enable(struct rpr0521_data *data, u8 status)
* @on: state to be set for devices in @device_mask
* @device_mask: bitmask specifying for which device we need to update @on state
*
* We rely on rpr0521_runtime_resume to enable our @device_mask devices, but
* if (for example) PXS was enabled (pxs_dev_en = true) by a previous call to
* rpr0521_runtime_resume and we want to enable ALS we MUST set ALS enable
* bit of RPR0521_REG_MODE_CTRL here because rpr0521_runtime_resume will not
* be called twice.
* Calls for this function must be balanced so that each ON should have matching
* OFF. Otherwise pm usage_count gets out of sync.
*/
static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
u8 device_mask)
{
#ifdef CONFIG_PM
int ret;
u8 update_mask = 0;
if (device_mask & RPR0521_MODE_ALS_MASK) {
if (on && !data->als_ps_need_en && data->pxs_dev_en)
update_mask |= RPR0521_MODE_ALS_MASK;
else
data->als_ps_need_en = on;
data->als_ps_need_en = on;
data->als_need_dis = !on;
}
if (device_mask & RPR0521_MODE_PXS_MASK) {
if (on && !data->pxs_ps_need_en && data->als_dev_en)
update_mask |= RPR0521_MODE_PXS_MASK;
else
data->pxs_ps_need_en = on;
}
if (update_mask) {
ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL,
update_mask, update_mask);
if (ret < 0)
return ret;
data->pxs_ps_need_en = on;
data->pxs_need_dis = !on;
}
/*
* On: _resume() is called only when we are suspended
* Off: _suspend() is called after delay if _resume() is not
* called before that.
* Note: If either measurement is re-enabled before _suspend(),
* both stay enabled until _suspend().
*/
if (on) {
ret = pm_runtime_get_sync(&data->client->dev);
} else {
@ -279,6 +273,23 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
return ret;
}
if (on) {
/* If _resume() was not called, enable measurement now. */
if (data->als_ps_need_en) {
ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
if (ret)
return ret;
data->als_ps_need_en = false;
}
if (data->pxs_ps_need_en) {
ret = rpr0521_pxs_enable(data, RPR0521_MODE_PXS_ENABLE);
if (ret)
return ret;
data->pxs_ps_need_en = false;
}
}
#endif
return 0;
}
@ -425,12 +436,14 @@ static int rpr0521_init(struct rpr0521_data *data)
return ret;
}
#ifndef CONFIG_PM
ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
if (ret < 0)
return ret;
ret = rpr0521_pxs_enable(data, RPR0521_MODE_PXS_ENABLE);
if (ret < 0)
return ret;
#endif
return 0;
}
@ -560,9 +573,16 @@ static int rpr0521_runtime_suspend(struct device *dev)
struct rpr0521_data *data = iio_priv(indio_dev);
int ret;
/* disable channels and sets {als,pxs}_dev_en to false */
mutex_lock(&data->lock);
/* If measurements are enabled, enable them on resume */
if (!data->als_need_dis)
data->als_ps_need_en = data->als_dev_en;
if (!data->pxs_need_dis)
data->pxs_ps_need_en = data->pxs_dev_en;
/* disable channels and sets {als,pxs}_dev_en to false */
ret = rpr0521_poweroff(data);
regcache_mark_dirty(data->regmap);
mutex_unlock(&data->lock);
return ret;
@ -574,6 +594,7 @@ static int rpr0521_runtime_resume(struct device *dev)
struct rpr0521_data *data = iio_priv(indio_dev);
int ret;
regcache_sync(data->regmap);
if (data->als_ps_need_en) {
ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
if (ret < 0)
@ -587,6 +608,7 @@ static int rpr0521_runtime_resume(struct device *dev)
return ret;
data->pxs_ps_need_en = false;
}
msleep(100); //wait for first measurement result
return 0;
}