Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 484c314b authored by Mikko Koivunen's avatar Mikko Koivunen Committed by Jonathan Cameron
Browse files

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: default avatarMikko Koivunen <mikko.koivunen@fi.rohmeurope.com>
Acked-by: default avatarDaniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
parent 12d74949
Loading
Loading
Loading
Loading
+46 −24
Original line number Diff line number Diff line
@@ -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_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;
		data->pxs_need_dis = !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) {
		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;
}