#include <linux/i2c.h>
 #include <linux/jiffies.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
 #include <linux/pm_runtime.h>
 
 #include <linux/iio/iio.h>
 #define MLX90614_OP_EEPROM     0x20
 #define MLX90614_OP_SLEEP      0xff
 
-/* RAM offsets with 16-bit data, MSB first */
-#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 */
-
 /* Control bits in configuration register */
 #define MLX90614_CONFIG_IIR_SHIFT 0 /* IIR coefficient */
 #define MLX90614_CONFIG_IIR_MASK (0x7 << MLX90614_CONFIG_IIR_SHIFT)
 #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_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.
 }
 
 /*
- * 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;
 
        /*
         * 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;
 }
 
                            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;
        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 * NSEC_PER_SEC /
-                               MLX90614_CONST_RAW_EMISSIVITY_MAX;
+                       *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
                        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;
                             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_RAW_EMISSIVITY_MAX / NSEC_PER_SEC;
+               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);
 
                               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;
 #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) {
        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);
 
 
 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;
 
        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);
 
         * 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;
 }
 /* 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;
        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);
 
        }
 }
 
+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 i2c_device_id mlx90614_id[] = {
-       { "mlx90614", 0 },
+       { "mlx90614", .driver_data = (kernel_ulong_t)&mlx90614_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 },
        { }
 };
 MODULE_DEVICE_TABLE(of, mlx90614_of_match);