diff options
Diffstat (limited to 'drivers/iio/chemical')
-rw-r--r-- | drivers/iio/chemical/Kconfig | 1 | ||||
-rw-r--r-- | drivers/iio/chemical/atlas-ph-sensor.c | 83 | ||||
-rw-r--r-- | drivers/iio/chemical/vz89x.c | 201 |
3 files changed, 225 insertions, 60 deletions
diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index 4bcc025e8c8a..cea7f9857a1f 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -16,6 +16,7 @@ config ATLAS_PH_SENSOR Atlas Scientific OEM SM sensors: * pH SM sensor * EC SM sensor + * ORP SM sensor To compile this driver as module, choose M here: the module will be called atlas-ph-sensor. diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c index ae038a59d256..bd321b305a0a 100644 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -66,12 +66,17 @@ #define ATLAS_REG_TDS_DATA 0x1c #define ATLAS_REG_PSS_DATA 0x20 +#define ATLAS_REG_ORP_CALIB_STATUS 0x0d +#define ATLAS_REG_ORP_DATA 0x0e + #define ATLAS_PH_INT_TIME_IN_US 450000 #define ATLAS_EC_INT_TIME_IN_US 650000 +#define ATLAS_ORP_INT_TIME_IN_US 450000 enum { ATLAS_PH_SM, ATLAS_EC_SM, + ATLAS_ORP_SM, }; struct atlas_data { @@ -84,26 +89,10 @@ struct atlas_data { __be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */ }; -static const struct regmap_range atlas_volatile_ranges[] = { - regmap_reg_range(ATLAS_REG_INT_CONTROL, ATLAS_REG_INT_CONTROL), - regmap_reg_range(ATLAS_REG_PH_DATA, ATLAS_REG_PH_DATA + 4), - regmap_reg_range(ATLAS_REG_EC_DATA, ATLAS_REG_PSS_DATA + 4), -}; - -static const struct regmap_access_table atlas_volatile_table = { - .yes_ranges = atlas_volatile_ranges, - .n_yes_ranges = ARRAY_SIZE(atlas_volatile_ranges), -}; - static const struct regmap_config atlas_regmap_config = { .name = ATLAS_REGMAP_NAME, - .reg_bits = 8, .val_bits = 8, - - .volatile_table = &atlas_volatile_table, - .max_register = ATLAS_REG_PSS_DATA + 4, - .cache_type = REGCACHE_RBTREE, }; static const struct iio_chan_spec atlas_ph_channels[] = { @@ -175,6 +164,23 @@ static const struct iio_chan_spec atlas_ec_channels[] = { }, }; +static const struct iio_chan_spec atlas_orp_channels[] = { + { + .type = IIO_VOLTAGE, + .address = ATLAS_REG_ORP_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + static int atlas_check_ph_calibration(struct atlas_data *data) { struct device *dev = &data->client->dev; @@ -240,6 +246,22 @@ static int atlas_check_ec_calibration(struct atlas_data *data) return 0; } +static int atlas_check_orp_calibration(struct atlas_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, ATLAS_REG_ORP_CALIB_STATUS, &val); + if (ret) + return ret; + + if (!val) + dev_warn(dev, "device has not been calibrated\n"); + + return 0; +}; + struct atlas_device { const struct iio_chan_spec *channels; int num_channels; @@ -264,7 +286,13 @@ static struct atlas_device atlas_devices[] = { .calibration = &atlas_check_ec_calibration, .delay = ATLAS_EC_INT_TIME_IN_US, }, - + [ATLAS_ORP_SM] = { + .channels = atlas_orp_channels, + .num_channels = 2, + .data_reg = ATLAS_REG_ORP_DATA, + .calibration = &atlas_check_orp_calibration, + .delay = ATLAS_ORP_INT_TIME_IN_US, + }, }; static int atlas_set_powermode(struct atlas_data *data, int on) @@ -402,15 +430,14 @@ static int atlas_read_raw(struct iio_dev *indio_dev, case IIO_PH: case IIO_CONCENTRATION: case IIO_ELECTRICALCONDUCTIVITY: - mutex_lock(&indio_dev->mlock); + case IIO_VOLTAGE: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; - if (iio_buffer_enabled(indio_dev)) - ret = -EBUSY; - else - ret = atlas_read_measurement(data, - chan->address, ®); + ret = atlas_read_measurement(data, chan->address, ®); - mutex_unlock(&indio_dev->mlock); + iio_device_release_direct_mode(indio_dev); break; default: ret = -EINVAL; @@ -434,12 +461,16 @@ static int atlas_read_raw(struct iio_dev *indio_dev, break; case IIO_ELECTRICALCONDUCTIVITY: *val = 1; /* 0.00001 */ - *val = 100000; + *val2 = 100000; break; case IIO_CONCENTRATION: *val = 0; /* 0.000000001 */ *val2 = 1000; return IIO_VAL_INT_PLUS_NANO; + case IIO_VOLTAGE: + *val = 1; /* 0.1 */ + *val2 = 10; + break; default: return -EINVAL; } @@ -475,6 +506,7 @@ static const struct iio_info atlas_info = { static const struct i2c_device_id atlas_id[] = { { "atlas-ph-sm", ATLAS_PH_SM}, { "atlas-ec-sm", ATLAS_EC_SM}, + { "atlas-orp-sm", ATLAS_ORP_SM}, {} }; MODULE_DEVICE_TABLE(i2c, atlas_id); @@ -482,6 +514,7 @@ MODULE_DEVICE_TABLE(i2c, atlas_id); static const struct of_device_id atlas_dt_ids[] = { { .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, }, { .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, }, + { .compatible = "atlas,orp-sm", .data = (void *)ATLAS_ORP_SM, }, { } }; MODULE_DEVICE_TABLE(of, atlas_dt_ids); diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c index 652649da500f..8e0e4415c161 100644 --- a/drivers/iio/chemical/vz89x.c +++ b/drivers/iio/chemical/vz89x.c @@ -19,25 +19,55 @@ #include <linux/mutex.h> #include <linux/init.h> #include <linux/i2c.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #define VZ89X_REG_MEASUREMENT 0x09 -#define VZ89X_REG_MEASUREMENT_SIZE 6 +#define VZ89X_REG_MEASUREMENT_RD_SIZE 6 +#define VZ89X_REG_MEASUREMENT_WR_SIZE 3 #define VZ89X_VOC_CO2_IDX 0 #define VZ89X_VOC_SHORT_IDX 1 #define VZ89X_VOC_TVOC_IDX 2 #define VZ89X_VOC_RESISTANCE_IDX 3 +#define VZ89TE_REG_MEASUREMENT 0x0c +#define VZ89TE_REG_MEASUREMENT_RD_SIZE 7 +#define VZ89TE_REG_MEASUREMENT_WR_SIZE 6 + +#define VZ89TE_VOC_TVOC_IDX 0 +#define VZ89TE_VOC_CO2_IDX 1 +#define VZ89TE_VOC_RESISTANCE_IDX 2 + +enum { + VZ89X, + VZ89TE, +}; + +struct vz89x_chip_data; + struct vz89x_data { struct i2c_client *client; + const struct vz89x_chip_data *chip; struct mutex lock; int (*xfer)(struct vz89x_data *data, u8 cmd); + bool is_valid; unsigned long last_update; - u8 buffer[VZ89X_REG_MEASUREMENT_SIZE]; + u8 buffer[VZ89TE_REG_MEASUREMENT_RD_SIZE]; +}; + +struct vz89x_chip_data { + bool (*valid)(struct vz89x_data *data); + const struct iio_chan_spec *channels; + u8 num_channels; + + u8 cmd; + u8 read_size; + u8 write_size; }; static const struct iio_chan_spec vz89x_channels[] = { @@ -70,6 +100,40 @@ static const struct iio_chan_spec vz89x_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .address = VZ89X_VOC_RESISTANCE_IDX, + .scan_index = -1, + .scan_type = { + .endianness = IIO_LE, + }, + }, +}; + +static const struct iio_chan_spec vz89te_channels[] = { + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_VOC, + .modified = 1, + .info_mask_separate = + BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW), + .address = VZ89TE_VOC_TVOC_IDX, + }, + + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_CO2, + .modified = 1, + .info_mask_separate = + BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW), + .address = VZ89TE_VOC_CO2_IDX, + }, + { + .type = IIO_RESISTANCE, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .address = VZ89TE_VOC_RESISTANCE_IDX, + .scan_index = -1, + .scan_type = { + .endianness = IIO_BE, + }, }, }; @@ -93,29 +157,45 @@ static const struct attribute_group vz89x_attrs_group = { * always zero, and by also confirming the VOC_short isn't zero. */ -static int vz89x_measurement_is_valid(struct vz89x_data *data) +static bool vz89x_measurement_is_valid(struct vz89x_data *data) { if (data->buffer[VZ89X_VOC_SHORT_IDX] == 0) - return 1; + return true; - return !!(data->buffer[VZ89X_REG_MEASUREMENT_SIZE - 1] > 0); + return !!(data->buffer[data->chip->read_size - 1] > 0); +} + +/* VZ89TE device has a modified CRC-8 two complement check */ +static bool vz89te_measurement_is_valid(struct vz89x_data *data) +{ + u8 crc = 0; + int i, sum = 0; + + for (i = 0; i < (data->chip->read_size - 1); i++) { + sum = crc + data->buffer[i]; + crc = sum; + crc += sum / 256; + } + + return !((0xff - crc) == data->buffer[data->chip->read_size - 1]); } static int vz89x_i2c_xfer(struct vz89x_data *data, u8 cmd) { + const struct vz89x_chip_data *chip = data->chip; struct i2c_client *client = data->client; struct i2c_msg msg[2]; int ret; - u8 buf[3] = { cmd, 0, 0}; + u8 buf[6] = { cmd, 0, 0, 0, 0, 0xf3 }; msg[0].addr = client->addr; msg[0].flags = client->flags; - msg[0].len = 3; + msg[0].len = chip->write_size; msg[0].buf = (char *) &buf; msg[1].addr = client->addr; msg[1].flags = client->flags | I2C_M_RD; - msg[1].len = VZ89X_REG_MEASUREMENT_SIZE; + msg[1].len = chip->read_size; msg[1].buf = (char *) &data->buffer; ret = i2c_transfer(client->adapter, msg, 2); @@ -133,7 +213,7 @@ static int vz89x_smbus_xfer(struct vz89x_data *data, u8 cmd) if (ret < 0) return ret; - for (i = 0; i < VZ89X_REG_MEASUREMENT_SIZE; i++) { + for (i = 0; i < data->chip->read_size; i++) { ret = i2c_smbus_read_byte(client); if (ret < 0) return ret; @@ -145,30 +225,47 @@ static int vz89x_smbus_xfer(struct vz89x_data *data, u8 cmd) static int vz89x_get_measurement(struct vz89x_data *data) { + const struct vz89x_chip_data *chip = data->chip; int ret; /* sensor can only be polled once a second max per datasheet */ if (!time_after(jiffies, data->last_update + HZ)) - return 0; + return data->is_valid ? 0 : -EAGAIN; + + data->is_valid = false; + data->last_update = jiffies; - ret = data->xfer(data, VZ89X_REG_MEASUREMENT); + ret = data->xfer(data, chip->cmd); if (ret < 0) return ret; - ret = vz89x_measurement_is_valid(data); + ret = chip->valid(data); if (ret) return -EAGAIN; - data->last_update = jiffies; + data->is_valid = true; return 0; } -static int vz89x_get_resistance_reading(struct vz89x_data *data) +static int vz89x_get_resistance_reading(struct vz89x_data *data, + struct iio_chan_spec const *chan, + int *val) { - u8 *buf = &data->buffer[VZ89X_VOC_RESISTANCE_IDX]; + u8 *tmp = (u8 *) &data->buffer[chan->address]; - return buf[0] | (buf[1] << 8); + switch (chan->scan_type.endianness) { + case IIO_LE: + *val = le32_to_cpup((__le32 *) tmp) & GENMASK(23, 0); + break; + case IIO_BE: + *val = be32_to_cpup((__be32 *) tmp) >> 8; + break; + default: + return -EINVAL; + } + + return 0; } static int vz89x_read_raw(struct iio_dev *indio_dev, @@ -187,15 +284,15 @@ static int vz89x_read_raw(struct iio_dev *indio_dev, if (ret) return ret; - switch (chan->address) { - case VZ89X_VOC_CO2_IDX: - case VZ89X_VOC_SHORT_IDX: - case VZ89X_VOC_TVOC_IDX: + switch (chan->type) { + case IIO_CONCENTRATION: *val = data->buffer[chan->address]; return IIO_VAL_INT; - case VZ89X_VOC_RESISTANCE_IDX: - *val = vz89x_get_resistance_reading(data); - return IIO_VAL_INT; + case IIO_RESISTANCE: + ret = vz89x_get_resistance_reading(data, chan, val); + if (!ret) + return IIO_VAL_INT; + break; default: return -EINVAL; } @@ -210,12 +307,12 @@ static int vz89x_read_raw(struct iio_dev *indio_dev, } break; case IIO_CHAN_INFO_OFFSET: - switch (chan->address) { - case VZ89X_VOC_CO2_IDX: + switch (chan->channel2) { + case IIO_MOD_CO2: *val = 44; *val2 = 250000; return IIO_VAL_INT_PLUS_MICRO; - case VZ89X_VOC_TVOC_IDX: + case IIO_MOD_VOC: *val = -13; return IIO_VAL_INT; default: @@ -232,11 +329,43 @@ static const struct iio_info vz89x_info = { .driver_module = THIS_MODULE, }; +static const struct vz89x_chip_data vz89x_chips[] = { + { + .valid = vz89x_measurement_is_valid, + + .cmd = VZ89X_REG_MEASUREMENT, + .read_size = VZ89X_REG_MEASUREMENT_RD_SIZE, + .write_size = VZ89X_REG_MEASUREMENT_WR_SIZE, + + .channels = vz89x_channels, + .num_channels = ARRAY_SIZE(vz89x_channels), + }, + { + .valid = vz89te_measurement_is_valid, + + .cmd = VZ89TE_REG_MEASUREMENT, + .read_size = VZ89TE_REG_MEASUREMENT_RD_SIZE, + .write_size = VZ89TE_REG_MEASUREMENT_WR_SIZE, + + .channels = vz89te_channels, + .num_channels = ARRAY_SIZE(vz89te_channels), + }, +}; + +static const struct of_device_id vz89x_dt_ids[] = { + { .compatible = "sgx,vz89x", .data = (void *) VZ89X }, + { .compatible = "sgx,vz89te", .data = (void *) VZ89TE }, + { } +}; +MODULE_DEVICE_TABLE(of, vz89x_dt_ids); + static int vz89x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct iio_dev *indio_dev; struct vz89x_data *data; + const struct of_device_id *of_id; + int chip_id; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -251,8 +380,15 @@ static int vz89x_probe(struct i2c_client *client, else return -EOPNOTSUPP; + of_id = of_match_device(vz89x_dt_ids, &client->dev); + if (!of_id) + chip_id = id->driver_data; + else + chip_id = (unsigned long)of_id->data; + i2c_set_clientdata(client, indio_dev); data->client = client; + data->chip = &vz89x_chips[chip_id]; data->last_update = jiffies - HZ; mutex_init(&data->lock); @@ -261,24 +397,19 @@ static int vz89x_probe(struct i2c_client *client, indio_dev->name = dev_name(&client->dev); indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = vz89x_channels; - indio_dev->num_channels = ARRAY_SIZE(vz89x_channels); + indio_dev->channels = data->chip->channels; + indio_dev->num_channels = data->chip->num_channels; return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id vz89x_id[] = { - { "vz89x", 0 }, + { "vz89x", VZ89X }, + { "vz89te", VZ89TE }, { } }; MODULE_DEVICE_TABLE(i2c, vz89x_id); -static const struct of_device_id vz89x_dt_ids[] = { - { .compatible = "sgx,vz89x" }, - { } -}; -MODULE_DEVICE_TABLE(of, vz89x_dt_ids); - static struct i2c_driver vz89x_driver = { .driver = { .name = "vz89x", |