|
@@ -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;
|
|
|
}
|