diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2022-11-25 18:35:16 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2022-11-25 18:35:16 +0100 |
commit | 56d784d1778c1d33e0803fdd00a59c2bb13989fb (patch) | |
tree | b1fd1a7a858214f321eeae59a25f52ac09fa7258 /drivers/iio/temperature/mlx90632.c | |
parent | 02cd3032b154fa02fdf90e7467abaeed889330b2 (diff) | |
parent | 12491d35551df69709777bd7769e1e33641943cc (diff) |
Merge tag 'iio-for-6.2a-take2' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-next
Jonathan writes:
1st set of IIO new device support, feature and cleanup for 6.2 (take2)
We have finally managed to take the mlock mutex entirely private so as
to avoid it being used for multiple purposes. Now it is just used to
protect device mode transitions (typically to and from buffered capture).
Includes merge of an immutable i2c branch to get the new
i2c_client_get_device_id() (thanks to Wolfram for providing the branch).
Based on rc3 to pick up some precursor fixes from early in the cycle and
avoid an unnecessarily messy history.
New device support
* adi,ad4310
- New driver to support this very flexible measurement device including
a 24 bit ADC. Later fix for documentation build issue.
* adi,adxl355
- Add support of the ADXL359 accelerometer.
* adi,ltc2983
- Support additional variants of the temperatures sensor:
LTC2984 with an EEPROM
LTC2985, LTC2986 with only 10 channels.
* invensense,icm42600
- Add support for icm42631 (needed only ID and WHOAMI)
* kionix,kx022a
- New driver for this 3 axis accelerometer.
* maxim,max11401
- New driver to support this 24-bit 10 channel ADC.
Includes some new ABI to support configuration of notch filters.
* mediatek,mt6370
- Add new driver to support the ADC part of the mt6370.
* st,lsm6dsx
- Add support for LSM6DSV accelerometer and gyroscope. Simple additional
of chip specific data and IDs.
- Add support for LSM6DSV16X accelerometer and gyroscope. Compatible with
features currently implemented for the LSM6DSV.
* st,stm32-adc
- Add support for stm32pm13x SoCs.
core / subsystem wide:
- Add new IIO_STATIC_CONST_DEVICE_ATTR() which is a dance necessary to
allow for the wrapping of attributes in the code that duplicates them
for multiple buffers.
- Harden against future issues with expectation that all buffer attributes
are iio_dev_attrs by changing the code to take an array of pointers
of the correct type.
- Last transitions of drivers to local locks rather than missuses of mlock.
- Add an iio_device_claim_buffer_mode() callback to avoid a race in the
max30100 driver without directly using mlock.
- Move mlock to the opaque IIO device structure to prevent misuse.
- Add missing spi_device_id tables to support auto loading of modules.
- Update some ADI maintainers in DT bindings.
- A few more moves of bus drivers and core module sets to export
name spaces.
- Extensive use of new devm_regulator_get_enable() and friends.
- Switch a bunch of i2c drivers to probe_new() including the bmp280
which makes use of the new i2c_client_get_device_id() helper to
simplify this change.
dt-bindings:
- More use of spi-peripheral-props.yaml.
Features
* freescale,mpl115
- Use runtime PM to implement shutdown GPIO support.
* melexis,mlx90632
- More sophisticated runtime power management
- Provide access to sampling frequency.
- Trivial follow up fixes.
* microchip,mcp3911
- Support control of PGA.
* st,lsm6dsx
- Add support for software triggers for cases where the IRQ lines
are not wired up.
* vishay,vcnl4000
- Add control of integration time.
Minor cleanups and fixes
* adi,ad4130
- Improve ABI documentation formatting.
- Kconfig dependency fixup.
* adi,ad5758
- Minor dt binding fix.
* adi,ad9834
- Tidy up line breaks.
* adi,ade7854
- Minor improvement in code clarity by replacing a ternary.
* adi,admv8818
- Harden code against hardware returning wrong values.
* adi,adxl355
- Warn only if unknown device ID detected to allow for fall back
device tree compatibles on future devices.
* adi,ltc2983
- dt-bindings clarifications and general improvements.
- Ensure DMA safe buffer for bulk writes without relying on current
regmap implementation choices.
* avago,adps9960
- Fix up a disconnect between event enable attributes and what was
enabled.
* bosch,bma400
- Switch to dev_err_probe() from open coded EPROBE_DEFER handling.
* cosmic,cc10001
- Fully devm managed probe() and related tidying up.
* meas,ms5611
- Add an example of spi-max-frequency.
* meleixs,mlx90632
- Tidy up confusing error return value.
- Style improvements.
* multiplexer
- Switch to dev_err_probe() from open coded EPROBE_DEFER handling.
* qcom,spmi-vadc
- Minor dt binding improvements.
* rockchip,saradc
- Add ID for rv1126.
* semtech,sx9360
- Add SAMM0208 ACPI ID. Doesn't appear to be a valid vendor prefix
but is in the wild.
* st,lsm6dsx
- Factor out common code as _device_set_enable().
- Fix up wrong docs after LSM6DSV addition.
* st,stm32-adc
- Manage the min sampling time on all internal channels.
* trig,sysfs
- Improve error labels.
* tag 'iio-for-6.2a-take2' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (146 commits)
iio: pressure: bmp280: convert to i2c's .probe_new()
iio: imu: st_lsm6dsx: fix LSM6DSV sensor description
iio: adc: ad4130: depend on GPIOLIB
staging: iio: meter: replace ternary operator by if condition
iio: light: apds9960: Fix iio_event_spec structures
dt-bindings: iio: imu: Add inv_icm42600 documentation
iio: imu: inv_icm42600: Add support for icm42631
dt-bindings: iio: adc: rockchip-saradc: Add saradc for rv1126
dt-bindings: iio: dac: adi,ad5758: Drop 'contains' from 'adi,dc-dc-mode'
dt-bindings: iio: imu: st_lsm6dsx: add lsm6dsv16x
iio: imu: st_lsm6dsx: add support to LSM6DSV16X
iio: proximity: sx9360: Add a new ACPI hardware ID
iio: temperature: mlx90632: Add missing static marking on devm_pm_ops
iio: temperature: mlx90632: Add error handling for devm_pm_runtime_enable()
iio: temperature: ltc2983: support more parts
dt-bindings: iio: temperature: ltc2983: support more parts
dt-bindings: iio: temperature: ltc2983: use generic node name in example
dt-bindings: iio: temperature: ltc2983: describe broken mux delay property
dt-bindings: iio: temperature: ltc2983: refine descriptions
dt-bindings: iio: temperature: ltc2983: change default excitation for custom thermistors
...
Diffstat (limited to 'drivers/iio/temperature/mlx90632.c')
-rw-r--r-- | drivers/iio/temperature/mlx90632.c | 474 |
1 files changed, 392 insertions, 82 deletions
diff --git a/drivers/iio/temperature/mlx90632.c b/drivers/iio/temperature/mlx90632.c index f6dec0e5f097..f1f5ebc145b1 100644 --- a/drivers/iio/temperature/mlx90632.c +++ b/drivers/iio/temperature/mlx90632.c @@ -6,11 +6,14 @@ * * Driver for the Melexis MLX90632 I2C 16-bit IR thermopile sensor */ +#include <linux/bitfield.h> #include <linux/delay.h> +#include <linux/device.h> #include <linux/err.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/iopoll.h> +#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/limits.h> #include <linux/mod_devicetable.h> @@ -55,6 +58,12 @@ #define MLX90632_EE_Ha 0x2481 /* Ha customer calib value reg 16bit */ #define MLX90632_EE_Hb 0x2482 /* Hb customer calib value reg 16bit */ +#define MLX90632_EE_MEDICAL_MEAS1 0x24E1 /* Medical measurement 1 16bit */ +#define MLX90632_EE_MEDICAL_MEAS2 0x24E2 /* Medical measurement 2 16bit */ +#define MLX90632_EE_EXTENDED_MEAS1 0x24F1 /* Extended measurement 1 16bit */ +#define MLX90632_EE_EXTENDED_MEAS2 0x24F2 /* Extended measurement 2 16bit */ +#define MLX90632_EE_EXTENDED_MEAS3 0x24F3 /* Extended measurement 3 16bit */ + /* Register addresses - volatile */ #define MLX90632_REG_I2C_ADDR 0x3000 /* Chip I2C address register */ @@ -62,13 +71,19 @@ #define MLX90632_REG_CONTROL 0x3001 /* Control Register address */ #define MLX90632_CFG_PWR_MASK GENMASK(2, 1) /* PowerMode Mask */ #define MLX90632_CFG_MTYP_MASK GENMASK(8, 4) /* Meas select Mask */ +#define MLX90632_CFG_SOB_MASK BIT(11) /* PowerModes statuses */ #define MLX90632_PWR_STATUS(ctrl_val) (ctrl_val << 1) #define MLX90632_PWR_STATUS_HALT MLX90632_PWR_STATUS(0) /* hold */ -#define MLX90632_PWR_STATUS_SLEEP_STEP MLX90632_PWR_STATUS(1) /* sleep step*/ +#define MLX90632_PWR_STATUS_SLEEP_STEP MLX90632_PWR_STATUS(1) /* sleep step */ #define MLX90632_PWR_STATUS_STEP MLX90632_PWR_STATUS(2) /* step */ -#define MLX90632_PWR_STATUS_CONTINUOUS MLX90632_PWR_STATUS(3) /* continuous*/ +#define MLX90632_PWR_STATUS_CONTINUOUS MLX90632_PWR_STATUS(3) /* continuous */ + +#define MLX90632_EE_RR GENMASK(10, 8) /* Only Refresh Rate bits */ +#define MLX90632_REFRESH_RATE(ee_val) FIELD_GET(MLX90632_EE_RR, ee_val) + /* Extract Refresh Rate from ee register */ +#define MLX90632_REFRESH_RATE_STATUS(refresh_rate) (refresh_rate << 8) /* Measurement types */ #define MLX90632_MTYP_MEDICAL 0 @@ -116,8 +131,9 @@ #define MLX90632_REF_12 12LL /* ResCtrlRef value of Ch 1 or Ch 2 */ #define MLX90632_REF_3 12LL /* ResCtrlRef value of Channel 3 */ #define MLX90632_MAX_MEAS_NUM 31 /* Maximum measurements in list */ -#define MLX90632_SLEEP_DELAY_MS 3000 /* Autosleep delay */ +#define MLX90632_SLEEP_DELAY_MS 6000 /* Autosleep delay */ #define MLX90632_EXTENDED_LIMIT 27000 /* Extended mode raw value limit */ +#define MLX90632_MEAS_MAX_TIME 2000 /* Max measurement time in ms for the lowest refresh rate */ /** * struct mlx90632_data - private data for the MLX90632 device @@ -130,6 +146,9 @@ * @object_ambient_temperature: Ambient temperature at object (might differ of * the ambient temperature of sensor. * @regulator: Regulator of the device + * @powerstatus: Current POWER status of the device + * @interaction_ts: Timestamp of the last temperature read that is used + * for power management in jiffies */ struct mlx90632_data { struct i2c_client *client; @@ -139,6 +158,8 @@ struct mlx90632_data { u8 mtyp; u32 object_ambient_temperature; struct regulator *regulator; + int powerstatus; + unsigned long interaction_ts; }; static const struct regmap_range mlx90632_volatile_reg_range[] = { @@ -158,6 +179,8 @@ static const struct regmap_range mlx90632_read_reg_range[] = { regmap_reg_range(MLX90632_EE_VERSION, MLX90632_EE_Ka), regmap_reg_range(MLX90632_EE_CTRL, MLX90632_EE_I2C_ADDR), regmap_reg_range(MLX90632_EE_Ha, MLX90632_EE_Hb), + regmap_reg_range(MLX90632_EE_MEDICAL_MEAS1, MLX90632_EE_MEDICAL_MEAS2), + regmap_reg_range(MLX90632_EE_EXTENDED_MEAS1, MLX90632_EE_EXTENDED_MEAS3), regmap_reg_range(MLX90632_REG_I2C_ADDR, MLX90632_REG_CONTROL), regmap_reg_range(MLX90632_REG_I2C_CMD, MLX90632_REG_I2C_CMD), regmap_reg_range(MLX90632_REG_STATUS, MLX90632_REG_STATUS), @@ -196,18 +219,40 @@ static const struct regmap_config mlx90632_regmap = { .cache_type = REGCACHE_RBTREE, }; -static s32 mlx90632_pwr_set_sleep_step(struct regmap *regmap) +static int mlx90632_pwr_set_sleep_step(struct regmap *regmap) { - return regmap_update_bits(regmap, MLX90632_REG_CONTROL, - MLX90632_CFG_PWR_MASK, - MLX90632_PWR_STATUS_SLEEP_STEP); + struct mlx90632_data *data = + iio_priv(dev_get_drvdata(regmap_get_device(regmap))); + int ret; + + if (data->powerstatus == MLX90632_PWR_STATUS_SLEEP_STEP) + return 0; + + ret = regmap_write_bits(regmap, MLX90632_REG_CONTROL, MLX90632_CFG_PWR_MASK, + MLX90632_PWR_STATUS_SLEEP_STEP); + if (ret < 0) + return ret; + + data->powerstatus = MLX90632_PWR_STATUS_SLEEP_STEP; + return 0; } -static s32 mlx90632_pwr_continuous(struct regmap *regmap) +static int mlx90632_pwr_continuous(struct regmap *regmap) { - return regmap_update_bits(regmap, MLX90632_REG_CONTROL, - MLX90632_CFG_PWR_MASK, - MLX90632_PWR_STATUS_CONTINUOUS); + struct mlx90632_data *data = + iio_priv(dev_get_drvdata(regmap_get_device(regmap))); + int ret; + + if (data->powerstatus == MLX90632_PWR_STATUS_CONTINUOUS) + return 0; + + ret = regmap_write_bits(regmap, MLX90632_REG_CONTROL, MLX90632_CFG_PWR_MASK, + MLX90632_PWR_STATUS_CONTINUOUS); + if (ret < 0) + return ret; + + data->powerstatus = MLX90632_PWR_STATUS_CONTINUOUS; + return 0; } /** @@ -219,6 +264,63 @@ static void mlx90632_reset_delay(void) usleep_range(150, 200); } +static int mlx90632_get_measurement_time(struct regmap *regmap, u16 meas) +{ + unsigned int reg; + int ret; + + ret = regmap_read(regmap, meas, ®); + if (ret < 0) + return ret; + + return MLX90632_MEAS_MAX_TIME >> FIELD_GET(MLX90632_EE_RR, reg); +} + +static int mlx90632_calculate_dataset_ready_time(struct mlx90632_data *data) +{ + unsigned int refresh_time; + int ret; + + if (data->mtyp == MLX90632_MTYP_MEDICAL) { + ret = mlx90632_get_measurement_time(data->regmap, + MLX90632_EE_MEDICAL_MEAS1); + if (ret < 0) + return ret; + + refresh_time = ret; + + ret = mlx90632_get_measurement_time(data->regmap, + MLX90632_EE_MEDICAL_MEAS2); + if (ret < 0) + return ret; + + refresh_time += ret; + } else { + ret = mlx90632_get_measurement_time(data->regmap, + MLX90632_EE_EXTENDED_MEAS1); + if (ret < 0) + return ret; + + refresh_time = ret; + + ret = mlx90632_get_measurement_time(data->regmap, + MLX90632_EE_EXTENDED_MEAS2); + if (ret < 0) + return ret; + + refresh_time += ret; + + ret = mlx90632_get_measurement_time(data->regmap, + MLX90632_EE_EXTENDED_MEAS3); + if (ret < 0) + return ret; + + refresh_time += ret; + } + + return refresh_time; +} + /** * mlx90632_perform_measurement() - Trigger and retrieve current measurement cycle * @data: pointer to mlx90632_data object containing regmap information @@ -249,26 +351,75 @@ static int mlx90632_perform_measurement(struct mlx90632_data *data) return (reg_status & MLX90632_STAT_CYCLE_POS) >> 2; } -static int mlx90632_set_meas_type(struct regmap *regmap, u8 type) +/** + * mlx90632_perform_measurement_burst() - Trigger and retrieve current measurement + * cycle in step sleep mode + * @data: pointer to mlx90632_data object containing regmap information + * + * Perform a measurement and return 2 as measurement cycle position reported + * by sensor. This is a blocking function for amount dependent on the sensor + * refresh rate. + */ +static int mlx90632_perform_measurement_burst(struct mlx90632_data *data) +{ + unsigned int reg_status; + int ret; + + ret = regmap_write_bits(data->regmap, MLX90632_REG_CONTROL, + MLX90632_CFG_SOB_MASK, MLX90632_CFG_SOB_MASK); + if (ret < 0) + return ret; + + ret = mlx90632_calculate_dataset_ready_time(data); + if (ret < 0) + return ret; + + msleep(ret); /* Wait minimum time for dataset to be ready */ + + ret = regmap_read_poll_timeout(data->regmap, MLX90632_REG_STATUS, + reg_status, + (reg_status & MLX90632_STAT_BUSY) == 0, + 10000, 100 * 10000); + if (ret < 0) { + dev_err(&data->client->dev, "data not ready"); + return -ETIMEDOUT; + } + + return 2; +} + +static int mlx90632_set_meas_type(struct mlx90632_data *data, u8 type) { + int current_powerstatus; int ret; - if ((type != MLX90632_MTYP_MEDICAL) && (type != MLX90632_MTYP_EXTENDED)) - return -EINVAL; + if (data->mtyp == type) + return 0; + + current_powerstatus = data->powerstatus; + ret = mlx90632_pwr_continuous(data->regmap); + if (ret < 0) + return ret; - ret = regmap_write(regmap, MLX90632_REG_I2C_CMD, MLX90632_RESET_CMD); + ret = regmap_write(data->regmap, MLX90632_REG_I2C_CMD, MLX90632_RESET_CMD); if (ret < 0) return ret; mlx90632_reset_delay(); - ret = regmap_write_bits(regmap, MLX90632_REG_CONTROL, + ret = regmap_update_bits(data->regmap, MLX90632_REG_CONTROL, (MLX90632_CFG_MTYP_MASK | MLX90632_CFG_PWR_MASK), (MLX90632_MTYP_STATUS(type) | MLX90632_PWR_STATUS_HALT)); if (ret < 0) return ret; - return mlx90632_pwr_continuous(regmap); + data->mtyp = type; + data->powerstatus = MLX90632_PWR_STATUS_HALT; + + if (current_powerstatus == MLX90632_PWR_STATUS_SLEEP_STEP) + return mlx90632_pwr_set_sleep_step(data->regmap); + + return mlx90632_pwr_continuous(data->regmap); } static int mlx90632_channel_new_select(int perform_ret, uint8_t *channel_new, @@ -284,7 +435,7 @@ static int mlx90632_channel_new_select(int perform_ret, uint8_t *channel_new, *channel_old = 1; break; default: - return -EINVAL; + return -ECHRNG; } return 0; @@ -293,8 +444,8 @@ static int mlx90632_channel_new_select(int perform_ret, uint8_t *channel_new, static int mlx90632_read_ambient_raw(struct regmap *regmap, s16 *ambient_new_raw, s16 *ambient_old_raw) { - int ret; unsigned int read_tmp; + int ret; ret = regmap_read(regmap, MLX90632_RAM_3(1), &read_tmp); if (ret < 0) @@ -313,11 +464,11 @@ static int mlx90632_read_object_raw(struct regmap *regmap, int perform_measurement_ret, s16 *object_new_raw, s16 *object_old_raw) { - int ret; unsigned int read_tmp; - s16 read; - u8 channel = 0; u8 channel_old = 0; + u8 channel = 0; + s16 read; + int ret; ret = mlx90632_channel_new_select(perform_measurement_ret, &channel, &channel_old); @@ -352,14 +503,34 @@ static int mlx90632_read_all_channel(struct mlx90632_data *data, s16 *ambient_new_raw, s16 *ambient_old_raw, s16 *object_new_raw, s16 *object_old_raw) { - s32 ret, measurement; + s32 measurement; + int ret; mutex_lock(&data->lock); - measurement = mlx90632_perform_measurement(data); - if (measurement < 0) { - ret = measurement; + ret = mlx90632_set_meas_type(data, MLX90632_MTYP_MEDICAL); + if (ret < 0) + goto read_unlock; + + switch (data->powerstatus) { + case MLX90632_PWR_STATUS_CONTINUOUS: + ret = mlx90632_perform_measurement(data); + if (ret < 0) + goto read_unlock; + + break; + case MLX90632_PWR_STATUS_SLEEP_STEP: + ret = mlx90632_perform_measurement_burst(data); + if (ret < 0) + goto read_unlock; + + break; + default: + ret = -EOPNOTSUPP; goto read_unlock; } + + measurement = ret; /* If we came here ret holds the measurement position */ + ret = mlx90632_read_ambient_raw(data->regmap, ambient_new_raw, ambient_old_raw); if (ret < 0) @@ -441,14 +612,26 @@ static int mlx90632_read_all_channel_extended(struct mlx90632_data *data, s16 *o s32 ret, meas; mutex_lock(&data->lock); - ret = mlx90632_set_meas_type(data->regmap, MLX90632_MTYP_EXTENDED); + ret = mlx90632_set_meas_type(data, MLX90632_MTYP_EXTENDED); if (ret < 0) goto read_unlock; - ret = read_poll_timeout(mlx90632_perform_measurement, meas, meas == 19, - 50000, 800000, false, data); - if (ret != 0) + switch (data->powerstatus) { + case MLX90632_PWR_STATUS_CONTINUOUS: + ret = read_poll_timeout(mlx90632_perform_measurement, meas, meas == 19, + 50000, 800000, false, data); + if (ret) + goto read_unlock; + break; + case MLX90632_PWR_STATUS_SLEEP_STEP: + ret = mlx90632_perform_measurement_burst(data); + if (ret < 0) + goto read_unlock; + break; + default: + ret = -EOPNOTSUPP; goto read_unlock; + } ret = mlx90632_read_object_raw_extended(data->regmap, object_new_raw); if (ret < 0) @@ -457,8 +640,6 @@ static int mlx90632_read_all_channel_extended(struct mlx90632_data *data, s16 *o ret = mlx90632_read_ambient_raw_extended(data->regmap, ambient_new_raw, ambient_old_raw); read_unlock: - (void) mlx90632_set_meas_type(data->regmap, MLX90632_MTYP_MEDICAL); - mutex_unlock(&data->lock); return ret; } @@ -466,9 +647,9 @@ read_unlock: static int mlx90632_read_ee_register(struct regmap *regmap, u16 reg_lsb, s32 *reg_value) { - s32 ret; unsigned int read; u32 value; + int ret; ret = regmap_read(regmap, reg_lsb, &read); if (ret < 0) @@ -632,12 +813,12 @@ static s32 mlx90632_calc_temp_object_extended(s64 object, s64 ambient, s64 refle static int mlx90632_calc_object_dsp105(struct mlx90632_data *data, int *val) { - s32 ret; + s16 ambient_new_raw, ambient_old_raw, object_new_raw, object_old_raw; s32 Ea, Eb, Fa, Fb, Ga; unsigned int read_tmp; - s16 Ha, Hb, Gb, Ka; - s16 ambient_new_raw, ambient_old_raw, object_new_raw, object_old_raw; s64 object, ambient; + s16 Ha, Hb, Gb, Ka; + int ret; ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Ea, &Ea); if (ret < 0) @@ -711,11 +892,11 @@ static int mlx90632_calc_object_dsp105(struct mlx90632_data *data, int *val) static int mlx90632_calc_ambient_dsp105(struct mlx90632_data *data, int *val) { - s32 ret; + s16 ambient_new_raw, ambient_old_raw; unsigned int read_tmp; s32 PT, PR, PG, PO; + int ret; s16 Gb; - s16 ambient_new_raw, ambient_old_raw; ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_P_R, &PR); if (ret < 0) @@ -743,12 +924,73 @@ static int mlx90632_calc_ambient_dsp105(struct mlx90632_data *data, int *val) return ret; } +static int mlx90632_get_refresh_rate(struct mlx90632_data *data, + int *refresh_rate) +{ + unsigned int meas1; + int ret; + + ret = regmap_read(data->regmap, MLX90632_EE_MEDICAL_MEAS1, &meas1); + if (ret < 0) + return ret; + + *refresh_rate = MLX90632_REFRESH_RATE(meas1); + + return ret; +} + +static const int mlx90632_freqs[][2] = { + {0, 500000}, + {1, 0}, + {2, 0}, + {4, 0}, + {8, 0}, + {16, 0}, + {32, 0}, + {64, 0} +}; + +/** + * mlx90632_pm_interraction_wakeup() - Measure time between user interactions to change powermode + * @data: pointer to mlx90632_data object containing interaction_ts information + * + * Switch to continuous mode when interaction is faster than MLX90632_MEAS_MAX_TIME. Update the + * interaction_ts for each function call with the jiffies to enable measurement between function + * calls. Initial value of the interaction_ts needs to be set before this function call. + */ +static int mlx90632_pm_interraction_wakeup(struct mlx90632_data *data) +{ + unsigned long now; + int ret; + + now = jiffies; + if (time_in_range(now, data->interaction_ts, + data->interaction_ts + + msecs_to_jiffies(MLX90632_MEAS_MAX_TIME + 100))) { + if (data->powerstatus == MLX90632_PWR_STATUS_SLEEP_STEP) { + ret = mlx90632_pwr_continuous(data->regmap); + if (ret < 0) + return ret; + } + } + + data->interaction_ts = now; + + return 0; +} + static int mlx90632_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *channel, int *val, int *val2, long mask) { struct mlx90632_data *data = iio_priv(indio_dev); int ret; + int cr; + + pm_runtime_get_sync(&data->client->dev); + ret = mlx90632_pm_interraction_wakeup(data); + if (ret < 0) + goto mlx90632_read_raw_pm; switch (mask) { case IIO_CHAN_INFO_PROCESSED: @@ -756,16 +998,22 @@ static int mlx90632_read_raw(struct iio_dev *indio_dev, case IIO_MOD_TEMP_AMBIENT: ret = mlx90632_calc_ambient_dsp105(data, val); if (ret < 0) - return ret; - return IIO_VAL_INT; + goto mlx90632_read_raw_pm; + + ret = IIO_VAL_INT; + break; case IIO_MOD_TEMP_OBJECT: ret = mlx90632_calc_object_dsp105(data, val); if (ret < 0) - return ret; - return IIO_VAL_INT; + goto mlx90632_read_raw_pm; + + ret = IIO_VAL_INT; + break; default: - return -EINVAL; + ret = -EINVAL; + break; } + break; case IIO_CHAN_INFO_CALIBEMISSIVITY: if (data->emissivity == 1000) { *val = 1; @@ -774,13 +1022,30 @@ static int mlx90632_read_raw(struct iio_dev *indio_dev, *val = 0; *val2 = data->emissivity * 1000; } - return IIO_VAL_INT_PLUS_MICRO; + ret = IIO_VAL_INT_PLUS_MICRO; + break; case IIO_CHAN_INFO_CALIBAMBIENT: *val = data->object_ambient_temperature; - return IIO_VAL_INT; + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = mlx90632_get_refresh_rate(data, &cr); + if (ret < 0) + goto mlx90632_read_raw_pm; + + *val = mlx90632_freqs[cr][0]; + *val2 = mlx90632_freqs[cr][1]; + ret = IIO_VAL_INT_PLUS_MICRO; + break; default: - return -EINVAL; + ret = -EINVAL; + break; } + +mlx90632_read_raw_pm: + pm_runtime_mark_last_busy(&data->client->dev); + pm_runtime_put_autosuspend(&data->client->dev); + return ret; } static int mlx90632_write_raw(struct iio_dev *indio_dev, @@ -805,12 +1070,30 @@ static int mlx90632_write_raw(struct iio_dev *indio_dev, } } +static int mlx90632_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (int *)mlx90632_freqs; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = 2 * ARRAY_SIZE(mlx90632_freqs); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + static const struct iio_chan_spec mlx90632_channels[] = { { .type = IIO_TEMP, .modified = 1, .channel2 = IIO_MOD_TEMP_AMBIENT, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), }, { .type = IIO_TEMP, @@ -818,19 +1101,29 @@ static const struct iio_chan_spec mlx90632_channels[] = { .channel2 = IIO_MOD_TEMP_OBJECT, .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) | BIT(IIO_CHAN_INFO_CALIBAMBIENT), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), }, }; static const struct iio_info mlx90632_info = { .read_raw = mlx90632_read_raw, .write_raw = mlx90632_write_raw, + .read_avail = mlx90632_read_avail, }; -static int mlx90632_sleep(struct mlx90632_data *data) +static void mlx90632_sleep(void *_data) +{ + struct mlx90632_data *data = _data; + + mlx90632_pwr_set_sleep_step(data->regmap); +} + +static int mlx90632_suspend(struct mlx90632_data *data) { regcache_mark_dirty(data->regmap); - dev_dbg(&data->client->dev, "Requesting sleep"); + dev_dbg(&data->client->dev, "Requesting suspend"); return mlx90632_pwr_set_sleep_step(data->regmap); } @@ -878,11 +1171,11 @@ static int mlx90632_enable_regulator(struct mlx90632_data *data) static int mlx90632_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct iio_dev *indio_dev; struct mlx90632_data *mlx90632; + struct iio_dev *indio_dev; struct regmap *regmap; - int ret; unsigned int read; + int ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*mlx90632)); if (!indio_dev) { @@ -902,6 +1195,7 @@ static int mlx90632_probe(struct i2c_client *client, mlx90632->client = client; mlx90632->regmap = regmap; mlx90632->mtyp = MLX90632_MTYP_MEDICAL; + mlx90632->powerstatus = MLX90632_PWR_STATUS_HALT; mutex_init(&mlx90632->lock); indio_dev->name = id->name; @@ -933,6 +1227,13 @@ static int mlx90632_probe(struct i2c_client *client, return ret; } + ret = devm_add_action_or_reset(&client->dev, mlx90632_sleep, mlx90632); + if (ret < 0) { + dev_err(&client->dev, "Failed to setup low power cleanup action %d\n", + ret); + return ret; + } + ret = regmap_read(mlx90632->regmap, MLX90632_EE_VERSION, &read); if (ret < 0) { dev_err(&client->dev, "read of version failed: %d\n", ret); @@ -961,32 +1262,20 @@ static int mlx90632_probe(struct i2c_client *client, mlx90632->emissivity = 1000; mlx90632->object_ambient_temperature = 25000; /* 25 degrees milliCelsius */ + mlx90632->interaction_ts = jiffies; /* Set initial value */ - pm_runtime_disable(&client->dev); - ret = pm_runtime_set_active(&client->dev); - if (ret < 0) { - mlx90632_sleep(mlx90632); + pm_runtime_get_noresume(&client->dev); + pm_runtime_set_active(&client->dev); + + ret = devm_pm_runtime_enable(&client->dev); + if (ret) return ret; - } - pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, MLX90632_SLEEP_DELAY_MS); pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); - return iio_device_register(indio_dev); -} - -static void mlx90632_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct mlx90632_data *data = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); - - mlx90632_sleep(data); + return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id mlx90632_id[] = { @@ -1001,33 +1290,54 @@ static const struct of_device_id mlx90632_of_match[] = { }; MODULE_DEVICE_TABLE(of, mlx90632_of_match); -static int __maybe_unused mlx90632_pm_suspend(struct device *dev) +static int mlx90632_pm_suspend(struct device *dev) { - struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); - struct mlx90632_data *data = iio_priv(indio_dev); + struct mlx90632_data *data = iio_priv(dev_get_drvdata(dev)); + int ret; - return mlx90632_sleep(data); + ret = mlx90632_suspend(data); + if (ret < 0) + return ret; + + ret = regulator_disable(data->regulator); + if (ret < 0) + dev_err(regmap_get_device(data->regmap), + "Failed to disable power regulator: %d\n", ret); + + return ret; } -static int __maybe_unused mlx90632_pm_resume(struct device *dev) +static int mlx90632_pm_resume(struct device *dev) { - struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); - struct mlx90632_data *data = iio_priv(indio_dev); + struct mlx90632_data *data = iio_priv(dev_get_drvdata(dev)); + int ret; + + ret = mlx90632_enable_regulator(data); + if (ret < 0) + return ret; return mlx90632_wakeup(data); } -static UNIVERSAL_DEV_PM_OPS(mlx90632_pm_ops, mlx90632_pm_suspend, - mlx90632_pm_resume, NULL); +static int mlx90632_pm_runtime_suspend(struct device *dev) +{ + struct mlx90632_data *data = iio_priv(dev_get_drvdata(dev)); + + return mlx90632_pwr_set_sleep_step(data->regmap); +} + +static const struct dev_pm_ops mlx90632_pm_ops = { + SYSTEM_SLEEP_PM_OPS(mlx90632_pm_suspend, mlx90632_pm_resume) + RUNTIME_PM_OPS(mlx90632_pm_runtime_suspend, NULL, NULL) +}; static struct i2c_driver mlx90632_driver = { .driver = { .name = "mlx90632", .of_match_table = mlx90632_of_match, - .pm = &mlx90632_pm_ops, + .pm = pm_ptr(&mlx90632_pm_ops), }, .probe = mlx90632_probe, - .remove = mlx90632_remove, .id_table = mlx90632_id, }; module_i2c_driver(mlx90632_driver); |