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>hifive-unleashed-5.1
parent
12d7494913
commit
484c314b90
|
@ -9,7 +9,7 @@
|
||||||
*
|
*
|
||||||
* IIO driver for RPR-0521RS (7-bit I2C slave address 0x38).
|
* 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>
|
#include <linux/module.h>
|
||||||
|
@ -142,9 +142,11 @@ struct rpr0521_data {
|
||||||
bool als_dev_en;
|
bool als_dev_en;
|
||||||
bool pxs_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 als_ps_need_en;
|
||||||
bool pxs_ps_need_en;
|
bool pxs_ps_need_en;
|
||||||
|
bool als_need_dis;
|
||||||
|
bool pxs_need_dis;
|
||||||
|
|
||||||
struct regmap *regmap;
|
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
|
* @on: state to be set for devices in @device_mask
|
||||||
* @device_mask: bitmask specifying for which device we need to update @on state
|
* @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
|
* Calls for this function must be balanced so that each ON should have matching
|
||||||
* if (for example) PXS was enabled (pxs_dev_en = true) by a previous call to
|
* OFF. Otherwise pm usage_count gets out of sync.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
|
static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
|
||||||
u8 device_mask)
|
u8 device_mask)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
int ret;
|
int ret;
|
||||||
u8 update_mask = 0;
|
|
||||||
|
|
||||||
if (device_mask & RPR0521_MODE_ALS_MASK) {
|
if (device_mask & RPR0521_MODE_ALS_MASK) {
|
||||||
if (on && !data->als_ps_need_en && data->pxs_dev_en)
|
data->als_ps_need_en = on;
|
||||||
update_mask |= RPR0521_MODE_ALS_MASK;
|
data->als_need_dis = !on;
|
||||||
else
|
|
||||||
data->als_ps_need_en = on;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device_mask & RPR0521_MODE_PXS_MASK) {
|
if (device_mask & RPR0521_MODE_PXS_MASK) {
|
||||||
if (on && !data->pxs_ps_need_en && data->als_dev_en)
|
data->pxs_ps_need_en = on;
|
||||||
update_mask |= RPR0521_MODE_PXS_MASK;
|
data->pxs_need_dis = !on;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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) {
|
if (on) {
|
||||||
ret = pm_runtime_get_sync(&data->client->dev);
|
ret = pm_runtime_get_sync(&data->client->dev);
|
||||||
} else {
|
} else {
|
||||||
|
@ -279,6 +273,23 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on,
|
||||||
|
|
||||||
return ret;
|
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
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -425,12 +436,14 @@ static int rpr0521_init(struct rpr0521_data *data)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_PM
|
||||||
ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
|
ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
ret = rpr0521_pxs_enable(data, RPR0521_MODE_PXS_ENABLE);
|
ret = rpr0521_pxs_enable(data, RPR0521_MODE_PXS_ENABLE);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -560,9 +573,16 @@ static int rpr0521_runtime_suspend(struct device *dev)
|
||||||
struct rpr0521_data *data = iio_priv(indio_dev);
|
struct rpr0521_data *data = iio_priv(indio_dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* disable channels and sets {als,pxs}_dev_en to false */
|
|
||||||
mutex_lock(&data->lock);
|
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);
|
ret = rpr0521_poweroff(data);
|
||||||
|
regcache_mark_dirty(data->regmap);
|
||||||
mutex_unlock(&data->lock);
|
mutex_unlock(&data->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -574,6 +594,7 @@ static int rpr0521_runtime_resume(struct device *dev)
|
||||||
struct rpr0521_data *data = iio_priv(indio_dev);
|
struct rpr0521_data *data = iio_priv(indio_dev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
regcache_sync(data->regmap);
|
||||||
if (data->als_ps_need_en) {
|
if (data->als_ps_need_en) {
|
||||||
ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
|
ret = rpr0521_als_enable(data, RPR0521_MODE_ALS_ENABLE);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -587,6 +608,7 @@ static int rpr0521_runtime_resume(struct device *dev)
|
||||||
return ret;
|
return ret;
|
||||||
data->pxs_ps_need_en = false;
|
data->pxs_ps_need_en = false;
|
||||||
}
|
}
|
||||||
|
msleep(100); //wait for first measurement result
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue