summaryrefslogtreecommitdiff
path: root/drivers/iio/temperature/mlx90614.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/temperature/mlx90614.c')
-rw-r--r--drivers/iio/temperature/mlx90614.c239
1 files changed, 169 insertions, 70 deletions
diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c
index 909fadb62349..676dc8701924 100644
--- a/drivers/iio/temperature/mlx90614.c
+++ b/drivers/iio/temperature/mlx90614.c
@@ -1,12 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * mlx90614.c - Support for Melexis MLX90614 contactless IR temperature sensor
+ * mlx90614.c - Support for Melexis MLX90614/MLX90615 contactless IR temperature sensor
*
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
* Copyright (c) 2015 Essensium NV
* Copyright (c) 2015 Melexis
*
- * Driver for the Melexis MLX90614 I2C 16-bit IR thermopile sensor
+ * Driver for the Melexis MLX90614/MLX90615 I2C 16-bit IR thermopile sensor
+ *
+ * MLX90614 - 17-bit ADC + MLX90302 DSP
+ * MLX90615 - 16-bit ADC + MLX90325 DSP
*
* (7-bit I2C slave address 0x5a, 100KHz bus speed only!)
*
@@ -19,12 +22,13 @@
* the "wakeup" GPIO is not given, power management will be disabled.
*/
+#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
-#include <linux/module.h>
-#include <linux/delay.h>
#include <linux/jiffies.h>
-#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
@@ -34,16 +38,9 @@
#define MLX90614_OP_EEPROM 0x20
#define MLX90614_OP_SLEEP 0xff
-/* RAM offsets with 16-bit data, MSB first */
-#define MLX90614_RAW1 (MLX90614_OP_RAM | 0x04) /* raw data IR channel 1 */
-#define MLX90614_RAW2 (MLX90614_OP_RAM | 0x05) /* raw data IR channel 2 */
-#define MLX90614_TA (MLX90614_OP_RAM | 0x06) /* ambient temperature */
-#define MLX90614_TOBJ1 (MLX90614_OP_RAM | 0x07) /* object 1 temperature */
-#define MLX90614_TOBJ2 (MLX90614_OP_RAM | 0x08) /* object 2 temperature */
-
-/* EEPROM offsets with 16-bit data, MSB first */
-#define MLX90614_EMISSIVITY (MLX90614_OP_EEPROM | 0x04) /* emissivity correction coefficient */
-#define MLX90614_CONFIG (MLX90614_OP_EEPROM | 0x05) /* configuration register */
+#define MLX90615_OP_EEPROM 0x10
+#define MLX90615_OP_RAM 0x20
+#define MLX90615_OP_SLEEP 0xc6
/* Control bits in configuration register */
#define MLX90614_CONFIG_IIR_SHIFT 0 /* IIR coefficient */
@@ -52,44 +49,61 @@
#define MLX90614_CONFIG_DUAL_MASK (1 << MLX90614_CONFIG_DUAL_SHIFT)
#define MLX90614_CONFIG_FIR_SHIFT 8 /* FIR coefficient */
#define MLX90614_CONFIG_FIR_MASK (0x7 << MLX90614_CONFIG_FIR_SHIFT)
-#define MLX90614_CONFIG_GAIN_SHIFT 11 /* gain */
-#define MLX90614_CONFIG_GAIN_MASK (0x7 << MLX90614_CONFIG_GAIN_SHIFT)
+
+#define MLX90615_CONFIG_IIR_SHIFT 12 /* IIR coefficient */
+#define MLX90615_CONFIG_IIR_MASK (0x7 << MLX90615_CONFIG_IIR_SHIFT)
/* Timings (in ms) */
#define MLX90614_TIMING_EEPROM 20 /* time for EEPROM write/erase to complete */
#define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */
#define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */
+#define MLX90615_TIMING_WAKEUP 22 /* time to hold SCL low for wake-up */
+
#define MLX90614_AUTOSLEEP_DELAY 5000 /* default autosleep delay */
/* Magic constants */
#define MLX90614_CONST_OFFSET_DEC -13657 /* decimal part of the Kelvin offset */
#define MLX90614_CONST_OFFSET_REM 500000 /* remainder of offset (273.15*50) */
#define MLX90614_CONST_SCALE 20 /* Scale in milliKelvin (0.02 * 1000) */
-#define MLX90614_CONST_RAW_EMISSIVITY_MAX 65535 /* max value for emissivity */
-#define MLX90614_CONST_EMISSIVITY_RESOLUTION 15259 /* 1/65535 ~ 0.000015259 */
#define MLX90614_CONST_FIR 0x7 /* Fixed value for FIR part of low pass filter */
+/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */
+#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
+#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
+
+struct mlx_chip_info {
+ /* EEPROM offsets with 16-bit data, MSB first */
+ /* emissivity correction coefficient */
+ u8 op_eeprom_emissivity;
+ u8 op_eeprom_config1;
+ /* RAM offsets with 16-bit data, MSB first */
+ /* ambient temperature */
+ u8 op_ram_ta;
+ /* object 1 temperature */
+ u8 op_ram_tobj1;
+ /* object 2 temperature */
+ u8 op_ram_tobj2;
+ u8 op_sleep;
+ /* support for two input channels (MLX90614 only) */
+ u8 dual_channel;
+ u8 wakeup_delay_ms;
+ u16 emissivity_max;
+ u16 fir_config_mask;
+ u16 iir_config_mask;
+ int iir_valid_offset;
+ u16 iir_values[8];
+ int iir_freqs[8][2];
+};
+
struct mlx90614_data {
struct i2c_client *client;
struct mutex lock; /* for EEPROM access only */
struct gpio_desc *wakeup_gpio; /* NULL to disable sleep/wake-up */
+ const struct mlx_chip_info *chip_info; /* Chip hardware details */
unsigned long ready_timestamp; /* in jiffies */
};
-/* Bandwidth values for IIR filtering */
-static const int mlx90614_iir_values[] = {77, 31, 20, 15, 723, 153, 110, 86};
-static const int mlx90614_freqs[][2] = {
- {0, 150000},
- {0, 200000},
- {0, 310000},
- {0, 770000},
- {0, 860000},
- {1, 100000},
- {1, 530000},
- {7, 230000}
-};
-
/*
* Erase an address and write word.
* The mutex must be locked before calling.
@@ -129,21 +143,26 @@ static s32 mlx90614_write_word(const struct i2c_client *client, u8 command,
}
/*
- * Find the IIR value inside mlx90614_iir_values array and return its position
+ * Find the IIR value inside iir_values array and return its position
* which is equivalent to the bit value in sensor register
*/
static inline s32 mlx90614_iir_search(const struct i2c_client *client,
int value)
{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct mlx90614_data *data = iio_priv(indio_dev);
+ const struct mlx_chip_info *chip_info = data->chip_info;
int i;
s32 ret;
- for (i = 0; i < ARRAY_SIZE(mlx90614_iir_values); ++i) {
- if (value == mlx90614_iir_values[i])
+ for (i = chip_info->iir_valid_offset;
+ i < ARRAY_SIZE(chip_info->iir_values);
+ i++) {
+ if (value == chip_info->iir_values[i])
break;
}
- if (i == ARRAY_SIZE(mlx90614_iir_values))
+ if (i == ARRAY_SIZE(chip_info->iir_values))
return -EINVAL;
/*
@@ -151,17 +170,21 @@ static inline s32 mlx90614_iir_search(const struct i2c_client *client,
* we must read them before we actually write
* changes
*/
- ret = i2c_smbus_read_word_data(client, MLX90614_CONFIG);
+ ret = i2c_smbus_read_word_data(client, chip_info->op_eeprom_config1);
if (ret < 0)
return ret;
- ret &= ~MLX90614_CONFIG_FIR_MASK;
- ret |= MLX90614_CONST_FIR << MLX90614_CONFIG_FIR_SHIFT;
- ret &= ~MLX90614_CONFIG_IIR_MASK;
- ret |= i << MLX90614_CONFIG_IIR_SHIFT;
+ /* Modify FIR on parts which have configurable FIR filter */
+ if (chip_info->fir_config_mask) {
+ ret &= ~chip_info->fir_config_mask;
+ ret |= field_prep(chip_info->fir_config_mask, MLX90614_CONST_FIR);
+ }
+
+ ret &= ~chip_info->iir_config_mask;
+ ret |= field_prep(chip_info->iir_config_mask, i);
/* Write changed values */
- ret = mlx90614_write_word(client, MLX90614_CONFIG, ret);
+ ret = mlx90614_write_word(client, chip_info->op_eeprom_config1, ret);
return ret;
}
@@ -221,22 +244,26 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
struct mlx90614_data *data = iio_priv(indio_dev);
- u8 cmd;
+ const struct mlx_chip_info *chip_info = data->chip_info;
+ u8 cmd, idx;
s32 ret;
switch (mask) {
case IIO_CHAN_INFO_RAW: /* 0.02K / LSB */
switch (channel->channel2) {
case IIO_MOD_TEMP_AMBIENT:
- cmd = MLX90614_TA;
+ cmd = chip_info->op_ram_ta;
break;
case IIO_MOD_TEMP_OBJECT:
+ if (chip_info->dual_channel && channel->channel)
+ return -EINVAL;
+
switch (channel->channel) {
case 0:
- cmd = MLX90614_TOBJ1;
+ cmd = chip_info->op_ram_tobj1;
break;
case 1:
- cmd = MLX90614_TOBJ2;
+ cmd = chip_info->op_ram_tobj2;
break;
default:
return -EINVAL;
@@ -268,45 +295,48 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SCALE:
*val = MLX90614_CONST_SCALE;
return IIO_VAL_INT;
- case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
+ case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/emissivity_max / LSB */
ret = mlx90614_power_get(data, false);
if (ret < 0)
return ret;
mutex_lock(&data->lock);
ret = i2c_smbus_read_word_data(data->client,
- MLX90614_EMISSIVITY);
+ chip_info->op_eeprom_emissivity);
mutex_unlock(&data->lock);
mlx90614_power_put(data);
if (ret < 0)
return ret;
- if (ret == MLX90614_CONST_RAW_EMISSIVITY_MAX) {
+ if (ret == chip_info->emissivity_max) {
*val = 1;
*val2 = 0;
} else {
*val = 0;
- *val2 = ret * MLX90614_CONST_EMISSIVITY_RESOLUTION;
+ *val2 = ret * NSEC_PER_SEC / chip_info->emissivity_max;
}
return IIO_VAL_INT_PLUS_NANO;
- case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: /* IIR setting with
- FIR = 1024 */
+ /* IIR setting with FIR=1024 (MLX90614) or FIR=65536 (MLX90615) */
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
ret = mlx90614_power_get(data, false);
if (ret < 0)
return ret;
mutex_lock(&data->lock);
- ret = i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
+ ret = i2c_smbus_read_word_data(data->client,
+ chip_info->op_eeprom_config1);
mutex_unlock(&data->lock);
mlx90614_power_put(data);
if (ret < 0)
return ret;
- *val = mlx90614_iir_values[ret & MLX90614_CONFIG_IIR_MASK] / 100;
- *val2 = (mlx90614_iir_values[ret & MLX90614_CONFIG_IIR_MASK] % 100) *
- 10000;
+ idx = field_get(chip_info->iir_config_mask, ret) -
+ chip_info->iir_valid_offset;
+
+ *val = chip_info->iir_values[idx] / 100;
+ *val2 = (chip_info->iir_values[idx] % 100) * 10000;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
@@ -318,22 +348,23 @@ static int mlx90614_write_raw(struct iio_dev *indio_dev,
int val2, long mask)
{
struct mlx90614_data *data = iio_priv(indio_dev);
+ const struct mlx_chip_info *chip_info = data->chip_info;
s32 ret;
switch (mask) {
- case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
+ case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/emissivity_max / LSB */
if (val < 0 || val2 < 0 || val > 1 || (val == 1 && val2 != 0))
return -EINVAL;
- val = val * MLX90614_CONST_RAW_EMISSIVITY_MAX +
- val2 / MLX90614_CONST_EMISSIVITY_RESOLUTION;
+ val = val * chip_info->emissivity_max +
+ val2 * chip_info->emissivity_max / NSEC_PER_SEC;
ret = mlx90614_power_get(data, false);
if (ret < 0)
return ret;
mutex_lock(&data->lock);
- ret = mlx90614_write_word(data->client, MLX90614_EMISSIVITY,
- val);
+ ret = mlx90614_write_word(data->client,
+ chip_info->op_eeprom_emissivity, val);
mutex_unlock(&data->lock);
mlx90614_power_put(data);
@@ -377,11 +408,15 @@ static int mlx90614_read_avail(struct iio_dev *indio_dev,
const int **vals, int *type, int *length,
long mask)
{
+ struct mlx90614_data *data = iio_priv(indio_dev);
+ const struct mlx_chip_info *chip_info = data->chip_info;
+
switch (mask) {
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
- *vals = (int *)mlx90614_freqs;
+ *vals = (int *)chip_info->iir_freqs;
*type = IIO_VAL_INT_PLUS_MICRO;
- *length = 2 * ARRAY_SIZE(mlx90614_freqs);
+ *length = 2 * (ARRAY_SIZE(chip_info->iir_freqs) -
+ chip_info->iir_valid_offset);
return IIO_AVAIL_LIST;
default:
return -EINVAL;
@@ -435,6 +470,7 @@ static const struct iio_info mlx90614_info = {
#ifdef CONFIG_PM
static int mlx90614_sleep(struct mlx90614_data *data)
{
+ const struct mlx_chip_info *chip_info = data->chip_info;
s32 ret;
if (!data->wakeup_gpio) {
@@ -447,7 +483,7 @@ static int mlx90614_sleep(struct mlx90614_data *data)
mutex_lock(&data->lock);
ret = i2c_smbus_xfer(data->client->adapter, data->client->addr,
data->client->flags | I2C_CLIENT_PEC,
- I2C_SMBUS_WRITE, MLX90614_OP_SLEEP,
+ I2C_SMBUS_WRITE, chip_info->op_sleep,
I2C_SMBUS_BYTE, NULL);
mutex_unlock(&data->lock);
@@ -456,6 +492,8 @@ static int mlx90614_sleep(struct mlx90614_data *data)
static int mlx90614_wakeup(struct mlx90614_data *data)
{
+ const struct mlx_chip_info *chip_info = data->chip_info;
+
if (!data->wakeup_gpio) {
dev_dbg(&data->client->dev, "Wake-up disabled");
return -ENOSYS;
@@ -465,7 +503,7 @@ static int mlx90614_wakeup(struct mlx90614_data *data)
i2c_lock_bus(data->client->adapter, I2C_LOCK_ROOT_ADAPTER);
gpiod_direction_output(data->wakeup_gpio, 0);
- msleep(MLX90614_TIMING_WAKEUP);
+ msleep(chip_info->wakeup_delay_ms);
gpiod_direction_input(data->wakeup_gpio);
i2c_unlock_bus(data->client->adapter, I2C_LOCK_ROOT_ADAPTER);
@@ -478,7 +516,7 @@ static int mlx90614_wakeup(struct mlx90614_data *data)
* If the read fails, the controller will probably be reset so that
* further reads will work.
*/
- i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
+ i2c_smbus_read_word_data(data->client, chip_info->op_eeprom_config1);
return 0;
}
@@ -527,9 +565,15 @@ static inline struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
/* Return 0 for single sensor, 1 for dual sensor, <0 on error. */
static int mlx90614_probe_num_ir_sensors(struct i2c_client *client)
{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct mlx90614_data *data = iio_priv(indio_dev);
+ const struct mlx_chip_info *chip_info = data->chip_info;
s32 ret;
- ret = i2c_smbus_read_word_data(client, MLX90614_CONFIG);
+ if (chip_info->dual_channel)
+ return 0;
+
+ ret = i2c_smbus_read_word_data(client, chip_info->op_eeprom_config1);
if (ret < 0)
return ret;
@@ -556,6 +600,7 @@ static int mlx90614_probe(struct i2c_client *client)
data->client = client;
mutex_init(&data->lock);
data->wakeup_gpio = mlx90614_probe_wakeup(client);
+ data->chip_info = device_get_match_data(&client->dev);
mlx90614_wakeup(data);
@@ -605,14 +650,68 @@ static void mlx90614_remove(struct i2c_client *client)
}
}
+static const struct mlx_chip_info mlx90614_chip_info = {
+ .op_eeprom_emissivity = MLX90614_OP_EEPROM | 0x04,
+ .op_eeprom_config1 = MLX90614_OP_EEPROM | 0x05,
+ .op_ram_ta = MLX90614_OP_RAM | 0x06,
+ .op_ram_tobj1 = MLX90614_OP_RAM | 0x07,
+ .op_ram_tobj2 = MLX90614_OP_RAM | 0x08,
+ .op_sleep = MLX90614_OP_SLEEP,
+ .dual_channel = true,
+ .wakeup_delay_ms = MLX90614_TIMING_WAKEUP,
+ .emissivity_max = 65535,
+ .fir_config_mask = MLX90614_CONFIG_FIR_MASK,
+ .iir_config_mask = MLX90614_CONFIG_IIR_MASK,
+ .iir_valid_offset = 0,
+ .iir_values = { 77, 31, 20, 15, 723, 153, 110, 86 },
+ .iir_freqs = {
+ { 0, 150000 }, /* 13% ~= 0.15 Hz */
+ { 0, 200000 }, /* 17% ~= 0.20 Hz */
+ { 0, 310000 }, /* 25% ~= 0.31 Hz */
+ { 0, 770000 }, /* 50% ~= 0.77 Hz */
+ { 0, 860000 }, /* 57% ~= 0.86 Hz */
+ { 1, 100000 }, /* 67% ~= 1.10 Hz */
+ { 1, 530000 }, /* 80% ~= 1.53 Hz */
+ { 7, 230000 } /* 100% ~= 7.23 Hz */
+ },
+};
+
+static const struct mlx_chip_info mlx90615_chip_info = {
+ .op_eeprom_emissivity = MLX90615_OP_EEPROM | 0x03,
+ .op_eeprom_config1 = MLX90615_OP_EEPROM | 0x02,
+ .op_ram_ta = MLX90615_OP_RAM | 0x06,
+ .op_ram_tobj1 = MLX90615_OP_RAM | 0x07,
+ .op_ram_tobj2 = MLX90615_OP_RAM | 0x08,
+ .op_sleep = MLX90615_OP_SLEEP,
+ .dual_channel = false,
+ .wakeup_delay_ms = MLX90615_TIMING_WAKEUP,
+ .emissivity_max = 16383,
+ .fir_config_mask = 0, /* MLX90615 FIR is fixed */
+ .iir_config_mask = MLX90615_CONFIG_IIR_MASK,
+ /* IIR value 0 is FORBIDDEN COMBINATION on MLX90615 */
+ .iir_valid_offset = 1,
+ .iir_values = { 500, 50, 30, 20, 15, 13, 10 },
+ .iir_freqs = {
+ { 0, 100000 }, /* 14% ~= 0.10 Hz */
+ { 0, 130000 }, /* 17% ~= 0.13 Hz */
+ { 0, 150000 }, /* 20% ~= 0.15 Hz */
+ { 0, 200000 }, /* 25% ~= 0.20 Hz */
+ { 0, 300000 }, /* 33% ~= 0.30 Hz */
+ { 0, 500000 }, /* 50% ~= 0.50 Hz */
+ { 5, 000000 }, /* 100% ~= 5.00 Hz */
+ },
+};
+
static const struct i2c_device_id mlx90614_id[] = {
- { "mlx90614", 0 },
+ { "mlx90614", .driver_data = (kernel_ulong_t)&mlx90614_chip_info },
+ { "mlx90615", .driver_data = (kernel_ulong_t)&mlx90615_chip_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, mlx90614_id);
static const struct of_device_id mlx90614_of_match[] = {
- { .compatible = "melexis,mlx90614" },
+ { .compatible = "melexis,mlx90614", .data = &mlx90614_chip_info },
+ { .compatible = "melexis,mlx90615", .data = &mlx90615_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, mlx90614_of_match);
@@ -675,7 +774,7 @@ static struct i2c_driver mlx90614_driver = {
.of_match_table = mlx90614_of_match,
.pm = pm_ptr(&mlx90614_pm_ops),
},
- .probe_new = mlx90614_probe,
+ .probe = mlx90614_probe,
.remove = mlx90614_remove,
.id_table = mlx90614_id,
};