diff options
Diffstat (limited to 'drivers/iio/temperature/mlx90614.c')
-rw-r--r-- | drivers/iio/temperature/mlx90614.c | 239 |
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, }; |