162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * mlx90614.c - Support for Melexis MLX90614/MLX90615 contactless IR temperature sensor
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
662306a36Sopenharmony_ci * Copyright (c) 2015 Essensium NV
762306a36Sopenharmony_ci * Copyright (c) 2015 Melexis
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Driver for the Melexis MLX90614/MLX90615 I2C 16-bit IR thermopile sensor
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * MLX90614 - 17-bit ADC + MLX90302 DSP
1262306a36Sopenharmony_ci * MLX90615 - 16-bit ADC + MLX90325 DSP
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * (7-bit I2C slave address 0x5a, 100KHz bus speed only!)
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * To wake up from sleep mode, the SDA line must be held low while SCL is high
1762306a36Sopenharmony_ci * for at least 33ms.  This is achieved with an extra GPIO that can be connected
1862306a36Sopenharmony_ci * directly to the SDA line.  In normal operation, the GPIO is set as input and
1962306a36Sopenharmony_ci * will not interfere in I2C communication.  While the GPIO is driven low, the
2062306a36Sopenharmony_ci * i2c adapter is locked since it cannot be used by other clients.  The SCL line
2162306a36Sopenharmony_ci * always has a pull-up so we do not need an extra GPIO to drive it high.  If
2262306a36Sopenharmony_ci * the "wakeup" GPIO is not given, power management will be disabled.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/delay.h>
2662306a36Sopenharmony_ci#include <linux/err.h>
2762306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2862306a36Sopenharmony_ci#include <linux/i2c.h>
2962306a36Sopenharmony_ci#include <linux/jiffies.h>
3062306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
3162306a36Sopenharmony_ci#include <linux/module.h>
3262306a36Sopenharmony_ci#include <linux/pm_runtime.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <linux/iio/iio.h>
3562306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define MLX90614_OP_RAM		0x00
3862306a36Sopenharmony_ci#define MLX90614_OP_EEPROM	0x20
3962306a36Sopenharmony_ci#define MLX90614_OP_SLEEP	0xff
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define MLX90615_OP_EEPROM	0x10
4262306a36Sopenharmony_ci#define MLX90615_OP_RAM		0x20
4362306a36Sopenharmony_ci#define MLX90615_OP_SLEEP	0xc6
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Control bits in configuration register */
4662306a36Sopenharmony_ci#define MLX90614_CONFIG_IIR_SHIFT 0 /* IIR coefficient */
4762306a36Sopenharmony_ci#define MLX90614_CONFIG_IIR_MASK (0x7 << MLX90614_CONFIG_IIR_SHIFT)
4862306a36Sopenharmony_ci#define MLX90614_CONFIG_DUAL_SHIFT 6 /* single (0) or dual (1) IR sensor */
4962306a36Sopenharmony_ci#define MLX90614_CONFIG_DUAL_MASK (1 << MLX90614_CONFIG_DUAL_SHIFT)
5062306a36Sopenharmony_ci#define MLX90614_CONFIG_FIR_SHIFT 8 /* FIR coefficient */
5162306a36Sopenharmony_ci#define MLX90614_CONFIG_FIR_MASK (0x7 << MLX90614_CONFIG_FIR_SHIFT)
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define MLX90615_CONFIG_IIR_SHIFT 12 /* IIR coefficient */
5462306a36Sopenharmony_ci#define MLX90615_CONFIG_IIR_MASK (0x7 << MLX90615_CONFIG_IIR_SHIFT)
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* Timings (in ms) */
5762306a36Sopenharmony_ci#define MLX90614_TIMING_EEPROM 20 /* time for EEPROM write/erase to complete */
5862306a36Sopenharmony_ci#define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */
5962306a36Sopenharmony_ci#define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define MLX90615_TIMING_WAKEUP 22 /* time to hold SCL low for wake-up */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define MLX90614_AUTOSLEEP_DELAY 5000 /* default autosleep delay */
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/* Magic constants */
6662306a36Sopenharmony_ci#define MLX90614_CONST_OFFSET_DEC -13657 /* decimal part of the Kelvin offset */
6762306a36Sopenharmony_ci#define MLX90614_CONST_OFFSET_REM 500000 /* remainder of offset (273.15*50) */
6862306a36Sopenharmony_ci#define MLX90614_CONST_SCALE 20 /* Scale in milliKelvin (0.02 * 1000) */
6962306a36Sopenharmony_ci#define MLX90614_CONST_FIR 0x7 /* Fixed value for FIR part of low pass filter */
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */
7262306a36Sopenharmony_ci#define field_get(_mask, _reg)	(((_reg) & (_mask)) >> (ffs(_mask) - 1))
7362306a36Sopenharmony_ci#define field_prep(_mask, _val)	(((_val) << (ffs(_mask) - 1)) & (_mask))
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistruct mlx_chip_info {
7662306a36Sopenharmony_ci	/* EEPROM offsets with 16-bit data, MSB first */
7762306a36Sopenharmony_ci	/* emissivity correction coefficient */
7862306a36Sopenharmony_ci	u8			op_eeprom_emissivity;
7962306a36Sopenharmony_ci	u8			op_eeprom_config1;
8062306a36Sopenharmony_ci	/* RAM offsets with 16-bit data, MSB first */
8162306a36Sopenharmony_ci	/* ambient temperature */
8262306a36Sopenharmony_ci	u8			op_ram_ta;
8362306a36Sopenharmony_ci	/* object 1 temperature */
8462306a36Sopenharmony_ci	u8			op_ram_tobj1;
8562306a36Sopenharmony_ci	/* object 2 temperature */
8662306a36Sopenharmony_ci	u8			op_ram_tobj2;
8762306a36Sopenharmony_ci	u8			op_sleep;
8862306a36Sopenharmony_ci	/* support for two input channels (MLX90614 only) */
8962306a36Sopenharmony_ci	u8			dual_channel;
9062306a36Sopenharmony_ci	u8			wakeup_delay_ms;
9162306a36Sopenharmony_ci	u16			emissivity_max;
9262306a36Sopenharmony_ci	u16			fir_config_mask;
9362306a36Sopenharmony_ci	u16			iir_config_mask;
9462306a36Sopenharmony_ci	int			iir_valid_offset;
9562306a36Sopenharmony_ci	u16			iir_values[8];
9662306a36Sopenharmony_ci	int			iir_freqs[8][2];
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistruct mlx90614_data {
10062306a36Sopenharmony_ci	struct i2c_client *client;
10162306a36Sopenharmony_ci	struct mutex lock; /* for EEPROM access only */
10262306a36Sopenharmony_ci	struct gpio_desc *wakeup_gpio; /* NULL to disable sleep/wake-up */
10362306a36Sopenharmony_ci	const struct mlx_chip_info *chip_info; /* Chip hardware details */
10462306a36Sopenharmony_ci	unsigned long ready_timestamp; /* in jiffies */
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * Erase an address and write word.
10962306a36Sopenharmony_ci * The mutex must be locked before calling.
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_cistatic s32 mlx90614_write_word(const struct i2c_client *client, u8 command,
11262306a36Sopenharmony_ci			       u16 value)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	/*
11562306a36Sopenharmony_ci	 * Note: The mlx90614 requires a PEC on writing but does not send us a
11662306a36Sopenharmony_ci	 * valid PEC on reading.  Hence, we cannot set I2C_CLIENT_PEC in
11762306a36Sopenharmony_ci	 * i2c_client.flags.  As a workaround, we use i2c_smbus_xfer here.
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ci	union i2c_smbus_data data;
12062306a36Sopenharmony_ci	s32 ret;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	dev_dbg(&client->dev, "Writing 0x%x to address 0x%x", value, command);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	data.word = 0x0000; /* erase command */
12562306a36Sopenharmony_ci	ret = i2c_smbus_xfer(client->adapter, client->addr,
12662306a36Sopenharmony_ci			     client->flags | I2C_CLIENT_PEC,
12762306a36Sopenharmony_ci			     I2C_SMBUS_WRITE, command,
12862306a36Sopenharmony_ci			     I2C_SMBUS_WORD_DATA, &data);
12962306a36Sopenharmony_ci	if (ret < 0)
13062306a36Sopenharmony_ci		return ret;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	msleep(MLX90614_TIMING_EEPROM);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	data.word = value; /* actual write */
13562306a36Sopenharmony_ci	ret = i2c_smbus_xfer(client->adapter, client->addr,
13662306a36Sopenharmony_ci			     client->flags | I2C_CLIENT_PEC,
13762306a36Sopenharmony_ci			     I2C_SMBUS_WRITE, command,
13862306a36Sopenharmony_ci			     I2C_SMBUS_WORD_DATA, &data);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	msleep(MLX90614_TIMING_EEPROM);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return ret;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/*
14662306a36Sopenharmony_ci * Find the IIR value inside iir_values array and return its position
14762306a36Sopenharmony_ci * which is equivalent to the bit value in sensor register
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_cistatic inline s32 mlx90614_iir_search(const struct i2c_client *client,
15062306a36Sopenharmony_ci				      int value)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
15362306a36Sopenharmony_ci	struct mlx90614_data *data = iio_priv(indio_dev);
15462306a36Sopenharmony_ci	const struct mlx_chip_info *chip_info = data->chip_info;
15562306a36Sopenharmony_ci	int i;
15662306a36Sopenharmony_ci	s32 ret;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	for (i = chip_info->iir_valid_offset;
15962306a36Sopenharmony_ci	     i < ARRAY_SIZE(chip_info->iir_values);
16062306a36Sopenharmony_ci	     i++) {
16162306a36Sopenharmony_ci		if (value == chip_info->iir_values[i])
16262306a36Sopenharmony_ci			break;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (i == ARRAY_SIZE(chip_info->iir_values))
16662306a36Sopenharmony_ci		return -EINVAL;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/*
16962306a36Sopenharmony_ci	 * CONFIG register values must not be changed so
17062306a36Sopenharmony_ci	 * we must read them before we actually write
17162306a36Sopenharmony_ci	 * changes
17262306a36Sopenharmony_ci	 */
17362306a36Sopenharmony_ci	ret = i2c_smbus_read_word_data(client, chip_info->op_eeprom_config1);
17462306a36Sopenharmony_ci	if (ret < 0)
17562306a36Sopenharmony_ci		return ret;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* Modify FIR on parts which have configurable FIR filter */
17862306a36Sopenharmony_ci	if (chip_info->fir_config_mask) {
17962306a36Sopenharmony_ci		ret &= ~chip_info->fir_config_mask;
18062306a36Sopenharmony_ci		ret |= field_prep(chip_info->fir_config_mask, MLX90614_CONST_FIR);
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	ret &= ~chip_info->iir_config_mask;
18462306a36Sopenharmony_ci	ret |= field_prep(chip_info->iir_config_mask, i);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* Write changed values */
18762306a36Sopenharmony_ci	ret = mlx90614_write_word(client, chip_info->op_eeprom_config1, ret);
18862306a36Sopenharmony_ci	return ret;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci#ifdef CONFIG_PM
19262306a36Sopenharmony_ci/*
19362306a36Sopenharmony_ci * If @startup is true, make sure MLX90614_TIMING_STARTUP ms have elapsed since
19462306a36Sopenharmony_ci * the last wake-up.  This is normally only needed to get a valid temperature
19562306a36Sopenharmony_ci * reading.  EEPROM access does not need such delay.
19662306a36Sopenharmony_ci * Return 0 on success, <0 on error.
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_cistatic int mlx90614_power_get(struct mlx90614_data *data, bool startup)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	unsigned long now;
20162306a36Sopenharmony_ci	int ret;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (!data->wakeup_gpio)
20462306a36Sopenharmony_ci		return 0;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(&data->client->dev);
20762306a36Sopenharmony_ci	if (ret < 0)
20862306a36Sopenharmony_ci		return ret;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (startup) {
21162306a36Sopenharmony_ci		now = jiffies;
21262306a36Sopenharmony_ci		if (time_before(now, data->ready_timestamp) &&
21362306a36Sopenharmony_ci		    msleep_interruptible(jiffies_to_msecs(
21462306a36Sopenharmony_ci				data->ready_timestamp - now)) != 0) {
21562306a36Sopenharmony_ci			pm_runtime_put_autosuspend(&data->client->dev);
21662306a36Sopenharmony_ci			return -EINTR;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return 0;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void mlx90614_power_put(struct mlx90614_data *data)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	if (!data->wakeup_gpio)
22662306a36Sopenharmony_ci		return;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	pm_runtime_mark_last_busy(&data->client->dev);
22962306a36Sopenharmony_ci	pm_runtime_put_autosuspend(&data->client->dev);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci#else
23262306a36Sopenharmony_cistatic inline int mlx90614_power_get(struct mlx90614_data *data, bool startup)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	return 0;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic inline void mlx90614_power_put(struct mlx90614_data *data)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci#endif
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic int mlx90614_read_raw(struct iio_dev *indio_dev,
24362306a36Sopenharmony_ci			    struct iio_chan_spec const *channel, int *val,
24462306a36Sopenharmony_ci			    int *val2, long mask)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct mlx90614_data *data = iio_priv(indio_dev);
24762306a36Sopenharmony_ci	const struct mlx_chip_info *chip_info = data->chip_info;
24862306a36Sopenharmony_ci	u8 cmd, idx;
24962306a36Sopenharmony_ci	s32 ret;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	switch (mask) {
25262306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW: /* 0.02K / LSB */
25362306a36Sopenharmony_ci		switch (channel->channel2) {
25462306a36Sopenharmony_ci		case IIO_MOD_TEMP_AMBIENT:
25562306a36Sopenharmony_ci			cmd = chip_info->op_ram_ta;
25662306a36Sopenharmony_ci			break;
25762306a36Sopenharmony_ci		case IIO_MOD_TEMP_OBJECT:
25862306a36Sopenharmony_ci			if (chip_info->dual_channel && channel->channel)
25962306a36Sopenharmony_ci				return -EINVAL;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci			switch (channel->channel) {
26262306a36Sopenharmony_ci			case 0:
26362306a36Sopenharmony_ci				cmd = chip_info->op_ram_tobj1;
26462306a36Sopenharmony_ci				break;
26562306a36Sopenharmony_ci			case 1:
26662306a36Sopenharmony_ci				cmd = chip_info->op_ram_tobj2;
26762306a36Sopenharmony_ci				break;
26862306a36Sopenharmony_ci			default:
26962306a36Sopenharmony_ci				return -EINVAL;
27062306a36Sopenharmony_ci			}
27162306a36Sopenharmony_ci			break;
27262306a36Sopenharmony_ci		default:
27362306a36Sopenharmony_ci			return -EINVAL;
27462306a36Sopenharmony_ci		}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		ret = mlx90614_power_get(data, true);
27762306a36Sopenharmony_ci		if (ret < 0)
27862306a36Sopenharmony_ci			return ret;
27962306a36Sopenharmony_ci		ret = i2c_smbus_read_word_data(data->client, cmd);
28062306a36Sopenharmony_ci		mlx90614_power_put(data);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		if (ret < 0)
28362306a36Sopenharmony_ci			return ret;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		/* MSB is an error flag */
28662306a36Sopenharmony_ci		if (ret & 0x8000)
28762306a36Sopenharmony_ci			return -EIO;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		*val = ret;
29062306a36Sopenharmony_ci		return IIO_VAL_INT;
29162306a36Sopenharmony_ci	case IIO_CHAN_INFO_OFFSET:
29262306a36Sopenharmony_ci		*val = MLX90614_CONST_OFFSET_DEC;
29362306a36Sopenharmony_ci		*val2 = MLX90614_CONST_OFFSET_REM;
29462306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_MICRO;
29562306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
29662306a36Sopenharmony_ci		*val = MLX90614_CONST_SCALE;
29762306a36Sopenharmony_ci		return IIO_VAL_INT;
29862306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/emissivity_max / LSB */
29962306a36Sopenharmony_ci		ret = mlx90614_power_get(data, false);
30062306a36Sopenharmony_ci		if (ret < 0)
30162306a36Sopenharmony_ci			return ret;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		mutex_lock(&data->lock);
30462306a36Sopenharmony_ci		ret = i2c_smbus_read_word_data(data->client,
30562306a36Sopenharmony_ci					       chip_info->op_eeprom_emissivity);
30662306a36Sopenharmony_ci		mutex_unlock(&data->lock);
30762306a36Sopenharmony_ci		mlx90614_power_put(data);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		if (ret < 0)
31062306a36Sopenharmony_ci			return ret;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		if (ret == chip_info->emissivity_max) {
31362306a36Sopenharmony_ci			*val = 1;
31462306a36Sopenharmony_ci			*val2 = 0;
31562306a36Sopenharmony_ci		} else {
31662306a36Sopenharmony_ci			*val = 0;
31762306a36Sopenharmony_ci			*val2 = ret * NSEC_PER_SEC / chip_info->emissivity_max;
31862306a36Sopenharmony_ci		}
31962306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_NANO;
32062306a36Sopenharmony_ci	/* IIR setting with FIR=1024 (MLX90614) or FIR=65536 (MLX90615) */
32162306a36Sopenharmony_ci	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
32262306a36Sopenharmony_ci		ret = mlx90614_power_get(data, false);
32362306a36Sopenharmony_ci		if (ret < 0)
32462306a36Sopenharmony_ci			return ret;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		mutex_lock(&data->lock);
32762306a36Sopenharmony_ci		ret = i2c_smbus_read_word_data(data->client,
32862306a36Sopenharmony_ci					       chip_info->op_eeprom_config1);
32962306a36Sopenharmony_ci		mutex_unlock(&data->lock);
33062306a36Sopenharmony_ci		mlx90614_power_put(data);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		if (ret < 0)
33362306a36Sopenharmony_ci			return ret;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		idx = field_get(chip_info->iir_config_mask, ret) -
33662306a36Sopenharmony_ci		      chip_info->iir_valid_offset;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci		*val = chip_info->iir_values[idx] / 100;
33962306a36Sopenharmony_ci		*val2 = (chip_info->iir_values[idx] % 100) * 10000;
34062306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_MICRO;
34162306a36Sopenharmony_ci	default:
34262306a36Sopenharmony_ci		return -EINVAL;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic int mlx90614_write_raw(struct iio_dev *indio_dev,
34762306a36Sopenharmony_ci			     struct iio_chan_spec const *channel, int val,
34862306a36Sopenharmony_ci			     int val2, long mask)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct mlx90614_data *data = iio_priv(indio_dev);
35162306a36Sopenharmony_ci	const struct mlx_chip_info *chip_info = data->chip_info;
35262306a36Sopenharmony_ci	s32 ret;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	switch (mask) {
35562306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/emissivity_max / LSB */
35662306a36Sopenharmony_ci		if (val < 0 || val2 < 0 || val > 1 || (val == 1 && val2 != 0))
35762306a36Sopenharmony_ci			return -EINVAL;
35862306a36Sopenharmony_ci		val = val * chip_info->emissivity_max +
35962306a36Sopenharmony_ci		      val2 * chip_info->emissivity_max / NSEC_PER_SEC;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		ret = mlx90614_power_get(data, false);
36262306a36Sopenharmony_ci		if (ret < 0)
36362306a36Sopenharmony_ci			return ret;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		mutex_lock(&data->lock);
36662306a36Sopenharmony_ci		ret = mlx90614_write_word(data->client,
36762306a36Sopenharmony_ci					  chip_info->op_eeprom_emissivity, val);
36862306a36Sopenharmony_ci		mutex_unlock(&data->lock);
36962306a36Sopenharmony_ci		mlx90614_power_put(data);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		return ret;
37262306a36Sopenharmony_ci	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: /* IIR Filter setting */
37362306a36Sopenharmony_ci		if (val < 0 || val2 < 0)
37462306a36Sopenharmony_ci			return -EINVAL;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		ret = mlx90614_power_get(data, false);
37762306a36Sopenharmony_ci		if (ret < 0)
37862306a36Sopenharmony_ci			return ret;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		mutex_lock(&data->lock);
38162306a36Sopenharmony_ci		ret = mlx90614_iir_search(data->client,
38262306a36Sopenharmony_ci					  val * 100 + val2 / 10000);
38362306a36Sopenharmony_ci		mutex_unlock(&data->lock);
38462306a36Sopenharmony_ci		mlx90614_power_put(data);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		return ret;
38762306a36Sopenharmony_ci	default:
38862306a36Sopenharmony_ci		return -EINVAL;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic int mlx90614_write_raw_get_fmt(struct iio_dev *indio_dev,
39362306a36Sopenharmony_ci				     struct iio_chan_spec const *channel,
39462306a36Sopenharmony_ci				     long mask)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	switch (mask) {
39762306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBEMISSIVITY:
39862306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_NANO;
39962306a36Sopenharmony_ci	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
40062306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_MICRO;
40162306a36Sopenharmony_ci	default:
40262306a36Sopenharmony_ci		return -EINVAL;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic int mlx90614_read_avail(struct iio_dev *indio_dev,
40762306a36Sopenharmony_ci			       struct iio_chan_spec const *chan,
40862306a36Sopenharmony_ci			       const int **vals, int *type, int *length,
40962306a36Sopenharmony_ci			       long mask)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct mlx90614_data *data = iio_priv(indio_dev);
41262306a36Sopenharmony_ci	const struct mlx_chip_info *chip_info = data->chip_info;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	switch (mask) {
41562306a36Sopenharmony_ci	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
41662306a36Sopenharmony_ci		*vals = (int *)chip_info->iir_freqs;
41762306a36Sopenharmony_ci		*type = IIO_VAL_INT_PLUS_MICRO;
41862306a36Sopenharmony_ci		*length = 2 * (ARRAY_SIZE(chip_info->iir_freqs) -
41962306a36Sopenharmony_ci			       chip_info->iir_valid_offset);
42062306a36Sopenharmony_ci		return IIO_AVAIL_LIST;
42162306a36Sopenharmony_ci	default:
42262306a36Sopenharmony_ci		return -EINVAL;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic const struct iio_chan_spec mlx90614_channels[] = {
42762306a36Sopenharmony_ci	{
42862306a36Sopenharmony_ci		.type = IIO_TEMP,
42962306a36Sopenharmony_ci		.modified = 1,
43062306a36Sopenharmony_ci		.channel2 = IIO_MOD_TEMP_AMBIENT,
43162306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
43262306a36Sopenharmony_ci		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
43362306a36Sopenharmony_ci		    BIT(IIO_CHAN_INFO_SCALE),
43462306a36Sopenharmony_ci	},
43562306a36Sopenharmony_ci	{
43662306a36Sopenharmony_ci		.type = IIO_TEMP,
43762306a36Sopenharmony_ci		.modified = 1,
43862306a36Sopenharmony_ci		.channel2 = IIO_MOD_TEMP_OBJECT,
43962306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
44062306a36Sopenharmony_ci		    BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) |
44162306a36Sopenharmony_ci			BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
44262306a36Sopenharmony_ci		.info_mask_separate_available =
44362306a36Sopenharmony_ci			BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
44462306a36Sopenharmony_ci		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
44562306a36Sopenharmony_ci		    BIT(IIO_CHAN_INFO_SCALE),
44662306a36Sopenharmony_ci	},
44762306a36Sopenharmony_ci	{
44862306a36Sopenharmony_ci		.type = IIO_TEMP,
44962306a36Sopenharmony_ci		.indexed = 1,
45062306a36Sopenharmony_ci		.modified = 1,
45162306a36Sopenharmony_ci		.channel = 1,
45262306a36Sopenharmony_ci		.channel2 = IIO_MOD_TEMP_OBJECT,
45362306a36Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
45462306a36Sopenharmony_ci		    BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) |
45562306a36Sopenharmony_ci			BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
45662306a36Sopenharmony_ci		.info_mask_separate_available =
45762306a36Sopenharmony_ci			BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
45862306a36Sopenharmony_ci		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
45962306a36Sopenharmony_ci		    BIT(IIO_CHAN_INFO_SCALE),
46062306a36Sopenharmony_ci	},
46162306a36Sopenharmony_ci};
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic const struct iio_info mlx90614_info = {
46462306a36Sopenharmony_ci	.read_raw = mlx90614_read_raw,
46562306a36Sopenharmony_ci	.write_raw = mlx90614_write_raw,
46662306a36Sopenharmony_ci	.write_raw_get_fmt = mlx90614_write_raw_get_fmt,
46762306a36Sopenharmony_ci	.read_avail = mlx90614_read_avail,
46862306a36Sopenharmony_ci};
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci#ifdef CONFIG_PM
47162306a36Sopenharmony_cistatic int mlx90614_sleep(struct mlx90614_data *data)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	const struct mlx_chip_info *chip_info = data->chip_info;
47462306a36Sopenharmony_ci	s32 ret;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	if (!data->wakeup_gpio) {
47762306a36Sopenharmony_ci		dev_dbg(&data->client->dev, "Sleep disabled");
47862306a36Sopenharmony_ci		return -ENOSYS;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	dev_dbg(&data->client->dev, "Requesting sleep");
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	mutex_lock(&data->lock);
48462306a36Sopenharmony_ci	ret = i2c_smbus_xfer(data->client->adapter, data->client->addr,
48562306a36Sopenharmony_ci			     data->client->flags | I2C_CLIENT_PEC,
48662306a36Sopenharmony_ci			     I2C_SMBUS_WRITE, chip_info->op_sleep,
48762306a36Sopenharmony_ci			     I2C_SMBUS_BYTE, NULL);
48862306a36Sopenharmony_ci	mutex_unlock(&data->lock);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	return ret;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic int mlx90614_wakeup(struct mlx90614_data *data)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	const struct mlx_chip_info *chip_info = data->chip_info;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (!data->wakeup_gpio) {
49862306a36Sopenharmony_ci		dev_dbg(&data->client->dev, "Wake-up disabled");
49962306a36Sopenharmony_ci		return -ENOSYS;
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	dev_dbg(&data->client->dev, "Requesting wake-up");
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	i2c_lock_bus(data->client->adapter, I2C_LOCK_ROOT_ADAPTER);
50562306a36Sopenharmony_ci	gpiod_direction_output(data->wakeup_gpio, 0);
50662306a36Sopenharmony_ci	msleep(chip_info->wakeup_delay_ms);
50762306a36Sopenharmony_ci	gpiod_direction_input(data->wakeup_gpio);
50862306a36Sopenharmony_ci	i2c_unlock_bus(data->client->adapter, I2C_LOCK_ROOT_ADAPTER);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	data->ready_timestamp = jiffies +
51162306a36Sopenharmony_ci			msecs_to_jiffies(MLX90614_TIMING_STARTUP);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	/*
51462306a36Sopenharmony_ci	 * Quirk: the i2c controller may get confused right after the
51562306a36Sopenharmony_ci	 * wake-up signal has been sent.  As a workaround, do a dummy read.
51662306a36Sopenharmony_ci	 * If the read fails, the controller will probably be reset so that
51762306a36Sopenharmony_ci	 * further reads will work.
51862306a36Sopenharmony_ci	 */
51962306a36Sopenharmony_ci	i2c_smbus_read_word_data(data->client, chip_info->op_eeprom_config1);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return 0;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci/* Return wake-up GPIO or NULL if sleep functionality should be disabled. */
52562306a36Sopenharmony_cistatic struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct gpio_desc *gpio;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (!i2c_check_functionality(client->adapter,
53062306a36Sopenharmony_ci						I2C_FUNC_SMBUS_WRITE_BYTE)) {
53162306a36Sopenharmony_ci		dev_info(&client->dev,
53262306a36Sopenharmony_ci			 "i2c adapter does not support SMBUS_WRITE_BYTE, sleep disabled");
53362306a36Sopenharmony_ci		return NULL;
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	gpio = devm_gpiod_get_optional(&client->dev, "wakeup", GPIOD_IN);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	if (IS_ERR(gpio)) {
53962306a36Sopenharmony_ci		dev_warn(&client->dev,
54062306a36Sopenharmony_ci			 "gpio acquisition failed with error %ld, sleep disabled",
54162306a36Sopenharmony_ci			 PTR_ERR(gpio));
54262306a36Sopenharmony_ci		return NULL;
54362306a36Sopenharmony_ci	} else if (!gpio) {
54462306a36Sopenharmony_ci		dev_info(&client->dev,
54562306a36Sopenharmony_ci			 "wakeup-gpio not found, sleep disabled");
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	return gpio;
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci#else
55162306a36Sopenharmony_cistatic inline int mlx90614_sleep(struct mlx90614_data *data)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	return -ENOSYS;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_cistatic inline int mlx90614_wakeup(struct mlx90614_data *data)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	return -ENOSYS;
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_cistatic inline struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	return NULL;
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci#endif
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci/* Return 0 for single sensor, 1 for dual sensor, <0 on error. */
56662306a36Sopenharmony_cistatic int mlx90614_probe_num_ir_sensors(struct i2c_client *client)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
56962306a36Sopenharmony_ci	struct mlx90614_data *data = iio_priv(indio_dev);
57062306a36Sopenharmony_ci	const struct mlx_chip_info *chip_info = data->chip_info;
57162306a36Sopenharmony_ci	s32 ret;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	if (chip_info->dual_channel)
57462306a36Sopenharmony_ci		return 0;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	ret = i2c_smbus_read_word_data(client, chip_info->op_eeprom_config1);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	if (ret < 0)
57962306a36Sopenharmony_ci		return ret;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	return (ret & MLX90614_CONFIG_DUAL_MASK) ? 1 : 0;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic int mlx90614_probe(struct i2c_client *client)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	const struct i2c_device_id *id = i2c_client_get_device_id(client);
58762306a36Sopenharmony_ci	struct iio_dev *indio_dev;
58862306a36Sopenharmony_ci	struct mlx90614_data *data;
58962306a36Sopenharmony_ci	int ret;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
59262306a36Sopenharmony_ci		return -EOPNOTSUPP;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
59562306a36Sopenharmony_ci	if (!indio_dev)
59662306a36Sopenharmony_ci		return -ENOMEM;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	data = iio_priv(indio_dev);
59962306a36Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
60062306a36Sopenharmony_ci	data->client = client;
60162306a36Sopenharmony_ci	mutex_init(&data->lock);
60262306a36Sopenharmony_ci	data->wakeup_gpio = mlx90614_probe_wakeup(client);
60362306a36Sopenharmony_ci	data->chip_info = device_get_match_data(&client->dev);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	mlx90614_wakeup(data);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	indio_dev->name = id->name;
60862306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
60962306a36Sopenharmony_ci	indio_dev->info = &mlx90614_info;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	ret = mlx90614_probe_num_ir_sensors(client);
61262306a36Sopenharmony_ci	switch (ret) {
61362306a36Sopenharmony_ci	case 0:
61462306a36Sopenharmony_ci		dev_dbg(&client->dev, "Found single sensor");
61562306a36Sopenharmony_ci		indio_dev->channels = mlx90614_channels;
61662306a36Sopenharmony_ci		indio_dev->num_channels = 2;
61762306a36Sopenharmony_ci		break;
61862306a36Sopenharmony_ci	case 1:
61962306a36Sopenharmony_ci		dev_dbg(&client->dev, "Found dual sensor");
62062306a36Sopenharmony_ci		indio_dev->channels = mlx90614_channels;
62162306a36Sopenharmony_ci		indio_dev->num_channels = 3;
62262306a36Sopenharmony_ci		break;
62362306a36Sopenharmony_ci	default:
62462306a36Sopenharmony_ci		return ret;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	if (data->wakeup_gpio) {
62862306a36Sopenharmony_ci		pm_runtime_set_autosuspend_delay(&client->dev,
62962306a36Sopenharmony_ci						 MLX90614_AUTOSLEEP_DELAY);
63062306a36Sopenharmony_ci		pm_runtime_use_autosuspend(&client->dev);
63162306a36Sopenharmony_ci		pm_runtime_set_active(&client->dev);
63262306a36Sopenharmony_ci		pm_runtime_enable(&client->dev);
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return iio_device_register(indio_dev);
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic void mlx90614_remove(struct i2c_client *client)
63962306a36Sopenharmony_ci{
64062306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
64162306a36Sopenharmony_ci	struct mlx90614_data *data = iio_priv(indio_dev);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	iio_device_unregister(indio_dev);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	if (data->wakeup_gpio) {
64662306a36Sopenharmony_ci		pm_runtime_disable(&client->dev);
64762306a36Sopenharmony_ci		if (!pm_runtime_status_suspended(&client->dev))
64862306a36Sopenharmony_ci			mlx90614_sleep(data);
64962306a36Sopenharmony_ci		pm_runtime_set_suspended(&client->dev);
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic const struct mlx_chip_info mlx90614_chip_info = {
65462306a36Sopenharmony_ci	.op_eeprom_emissivity		= MLX90614_OP_EEPROM | 0x04,
65562306a36Sopenharmony_ci	.op_eeprom_config1		= MLX90614_OP_EEPROM | 0x05,
65662306a36Sopenharmony_ci	.op_ram_ta			= MLX90614_OP_RAM | 0x06,
65762306a36Sopenharmony_ci	.op_ram_tobj1			= MLX90614_OP_RAM | 0x07,
65862306a36Sopenharmony_ci	.op_ram_tobj2			= MLX90614_OP_RAM | 0x08,
65962306a36Sopenharmony_ci	.op_sleep			= MLX90614_OP_SLEEP,
66062306a36Sopenharmony_ci	.dual_channel			= true,
66162306a36Sopenharmony_ci	.wakeup_delay_ms		= MLX90614_TIMING_WAKEUP,
66262306a36Sopenharmony_ci	.emissivity_max			= 65535,
66362306a36Sopenharmony_ci	.fir_config_mask		= MLX90614_CONFIG_FIR_MASK,
66462306a36Sopenharmony_ci	.iir_config_mask		= MLX90614_CONFIG_IIR_MASK,
66562306a36Sopenharmony_ci	.iir_valid_offset		= 0,
66662306a36Sopenharmony_ci	.iir_values			= { 77, 31, 20, 15, 723, 153, 110, 86 },
66762306a36Sopenharmony_ci	.iir_freqs			= {
66862306a36Sopenharmony_ci		{ 0, 150000 },	/* 13% ~= 0.15 Hz */
66962306a36Sopenharmony_ci		{ 0, 200000 },	/* 17% ~= 0.20 Hz */
67062306a36Sopenharmony_ci		{ 0, 310000 },	/* 25% ~= 0.31 Hz */
67162306a36Sopenharmony_ci		{ 0, 770000 },	/* 50% ~= 0.77 Hz */
67262306a36Sopenharmony_ci		{ 0, 860000 },	/* 57% ~= 0.86 Hz */
67362306a36Sopenharmony_ci		{ 1, 100000 },	/* 67% ~= 1.10 Hz */
67462306a36Sopenharmony_ci		{ 1, 530000 },	/* 80% ~= 1.53 Hz */
67562306a36Sopenharmony_ci		{ 7, 230000 }	/* 100% ~= 7.23 Hz */
67662306a36Sopenharmony_ci	},
67762306a36Sopenharmony_ci};
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_cistatic const struct mlx_chip_info mlx90615_chip_info = {
68062306a36Sopenharmony_ci	.op_eeprom_emissivity		= MLX90615_OP_EEPROM | 0x03,
68162306a36Sopenharmony_ci	.op_eeprom_config1		= MLX90615_OP_EEPROM | 0x02,
68262306a36Sopenharmony_ci	.op_ram_ta			= MLX90615_OP_RAM | 0x06,
68362306a36Sopenharmony_ci	.op_ram_tobj1			= MLX90615_OP_RAM | 0x07,
68462306a36Sopenharmony_ci	.op_ram_tobj2			= MLX90615_OP_RAM | 0x08,
68562306a36Sopenharmony_ci	.op_sleep			= MLX90615_OP_SLEEP,
68662306a36Sopenharmony_ci	.dual_channel			= false,
68762306a36Sopenharmony_ci	.wakeup_delay_ms		= MLX90615_TIMING_WAKEUP,
68862306a36Sopenharmony_ci	.emissivity_max			= 16383,
68962306a36Sopenharmony_ci	.fir_config_mask		= 0,	/* MLX90615 FIR is fixed */
69062306a36Sopenharmony_ci	.iir_config_mask		= MLX90615_CONFIG_IIR_MASK,
69162306a36Sopenharmony_ci	/* IIR value 0 is FORBIDDEN COMBINATION on MLX90615 */
69262306a36Sopenharmony_ci	.iir_valid_offset		= 1,
69362306a36Sopenharmony_ci	.iir_values			= { 500, 50, 30, 20, 15, 13, 10 },
69462306a36Sopenharmony_ci	.iir_freqs			= {
69562306a36Sopenharmony_ci		{ 0, 100000 },	/* 14% ~= 0.10 Hz */
69662306a36Sopenharmony_ci		{ 0, 130000 },	/* 17% ~= 0.13 Hz */
69762306a36Sopenharmony_ci		{ 0, 150000 },	/* 20% ~= 0.15 Hz */
69862306a36Sopenharmony_ci		{ 0, 200000 },	/* 25% ~= 0.20 Hz */
69962306a36Sopenharmony_ci		{ 0, 300000 },	/* 33% ~= 0.30 Hz */
70062306a36Sopenharmony_ci		{ 0, 500000 },	/* 50% ~= 0.50 Hz */
70162306a36Sopenharmony_ci		{ 5, 000000 },	/* 100% ~= 5.00 Hz */
70262306a36Sopenharmony_ci	},
70362306a36Sopenharmony_ci};
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic const struct i2c_device_id mlx90614_id[] = {
70662306a36Sopenharmony_ci	{ "mlx90614", .driver_data = (kernel_ulong_t)&mlx90614_chip_info },
70762306a36Sopenharmony_ci	{ "mlx90615", .driver_data = (kernel_ulong_t)&mlx90615_chip_info },
70862306a36Sopenharmony_ci	{ }
70962306a36Sopenharmony_ci};
71062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mlx90614_id);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic const struct of_device_id mlx90614_of_match[] = {
71362306a36Sopenharmony_ci	{ .compatible = "melexis,mlx90614", .data = &mlx90614_chip_info },
71462306a36Sopenharmony_ci	{ .compatible = "melexis,mlx90615", .data = &mlx90615_chip_info },
71562306a36Sopenharmony_ci	{ }
71662306a36Sopenharmony_ci};
71762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mlx90614_of_match);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic int mlx90614_pm_suspend(struct device *dev)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
72262306a36Sopenharmony_ci	struct mlx90614_data *data = iio_priv(indio_dev);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (data->wakeup_gpio && pm_runtime_active(dev))
72562306a36Sopenharmony_ci		return mlx90614_sleep(data);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	return 0;
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cistatic int mlx90614_pm_resume(struct device *dev)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
73362306a36Sopenharmony_ci	struct mlx90614_data *data = iio_priv(indio_dev);
73462306a36Sopenharmony_ci	int err;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (data->wakeup_gpio) {
73762306a36Sopenharmony_ci		err = mlx90614_wakeup(data);
73862306a36Sopenharmony_ci		if (err < 0)
73962306a36Sopenharmony_ci			return err;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci		pm_runtime_disable(dev);
74262306a36Sopenharmony_ci		pm_runtime_set_active(dev);
74362306a36Sopenharmony_ci		pm_runtime_enable(dev);
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	return 0;
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic int mlx90614_pm_runtime_suspend(struct device *dev)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
75262306a36Sopenharmony_ci	struct mlx90614_data *data = iio_priv(indio_dev);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	return mlx90614_sleep(data);
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic int mlx90614_pm_runtime_resume(struct device *dev)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
76062306a36Sopenharmony_ci	struct mlx90614_data *data = iio_priv(indio_dev);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	return mlx90614_wakeup(data);
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic const struct dev_pm_ops mlx90614_pm_ops = {
76662306a36Sopenharmony_ci	SYSTEM_SLEEP_PM_OPS(mlx90614_pm_suspend, mlx90614_pm_resume)
76762306a36Sopenharmony_ci	RUNTIME_PM_OPS(mlx90614_pm_runtime_suspend,
76862306a36Sopenharmony_ci		       mlx90614_pm_runtime_resume, NULL)
76962306a36Sopenharmony_ci};
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cistatic struct i2c_driver mlx90614_driver = {
77262306a36Sopenharmony_ci	.driver = {
77362306a36Sopenharmony_ci		.name	= "mlx90614",
77462306a36Sopenharmony_ci		.of_match_table = mlx90614_of_match,
77562306a36Sopenharmony_ci		.pm	= pm_ptr(&mlx90614_pm_ops),
77662306a36Sopenharmony_ci	},
77762306a36Sopenharmony_ci	.probe = mlx90614_probe,
77862306a36Sopenharmony_ci	.remove = mlx90614_remove,
77962306a36Sopenharmony_ci	.id_table = mlx90614_id,
78062306a36Sopenharmony_ci};
78162306a36Sopenharmony_cimodule_i2c_driver(mlx90614_driver);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ciMODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
78462306a36Sopenharmony_ciMODULE_AUTHOR("Vianney le Clément de Saint-Marcq <vianney.leclement@essensium.com>");
78562306a36Sopenharmony_ciMODULE_AUTHOR("Crt Mori <cmo@melexis.com>");
78662306a36Sopenharmony_ciMODULE_DESCRIPTION("Melexis MLX90614 contactless IR temperature sensor driver");
78762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
788