Commit 0a735aa0 authored by Matt Ranostay's avatar Matt Ranostay Committed by Jonathan Cameron
Browse files

iio: chemical: vz89x: add support for VZ89TE part



Add support the VZ89TE variant which removes the voc_short channel,
and has CRC check for data transactions.

Signed-off-by: default avatarMatt Ranostay <mranostay@gmail.com>
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
parent 8376882f
Loading
Loading
Loading
Loading
+98 −15
Original line number Original line Diff line number Diff line
@@ -34,8 +34,17 @@
#define VZ89X_VOC_TVOC_IDX		2
#define VZ89X_VOC_TVOC_IDX		2
#define VZ89X_VOC_RESISTANCE_IDX	3
#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 {
enum {
	VZ89X,
	VZ89X,
	VZ89TE,
};
};


struct vz89x_chip_data;
struct vz89x_chip_data;
@@ -47,7 +56,7 @@ struct vz89x_data {
	int (*xfer)(struct vz89x_data *data, u8 cmd);
	int (*xfer)(struct vz89x_data *data, u8 cmd);


	unsigned long last_update;
	unsigned long last_update;
	u8 buffer[VZ89X_REG_MEASUREMENT_RD_SIZE];
	u8 buffer[VZ89TE_REG_MEASUREMENT_RD_SIZE];
};
};


struct vz89x_chip_data {
struct vz89x_chip_data {
@@ -90,6 +99,40 @@ static const struct iio_chan_spec vz89x_channels[] = {
		.info_mask_separate =
		.info_mask_separate =
			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
		.address = VZ89X_VOC_RESISTANCE_IDX,
		.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,
		},
	},
	},
};
};


@@ -121,13 +164,28 @@ static bool vz89x_measurement_is_valid(struct vz89x_data *data)
	return !!(data->buffer[data->chip->read_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)
static int vz89x_i2c_xfer(struct vz89x_data *data, u8 cmd)
{
{
	const struct vz89x_chip_data *chip = data->chip;
	const struct vz89x_chip_data *chip = data->chip;
	struct i2c_client *client = data->client;
	struct i2c_client *client = data->client;
	struct i2c_msg msg[2];
	struct i2c_msg msg[2];
	int ret;
	int ret;
	u8 buf[3] = { cmd, 0, 0};
	u8 buf[6] = { cmd, 0, 0, 0, 0, 0xf3 };


	msg[0].addr = client->addr;
	msg[0].addr = client->addr;
	msg[0].flags = client->flags;
	msg[0].flags = client->flags;
@@ -186,11 +244,24 @@ static int vz89x_get_measurement(struct vz89x_data *data)
	return 0;
	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,
static int vz89x_read_raw(struct iio_dev *indio_dev,
@@ -209,15 +280,15 @@ static int vz89x_read_raw(struct iio_dev *indio_dev,
		if (ret)
		if (ret)
			return ret;
			return ret;


		switch (chan->address) {
		switch (chan->type) {
		case VZ89X_VOC_CO2_IDX:
		case IIO_CONCENTRATION:
		case VZ89X_VOC_SHORT_IDX:
		case VZ89X_VOC_TVOC_IDX:
			*val = data->buffer[chan->address];
			*val = data->buffer[chan->address];
			return IIO_VAL_INT;
			return IIO_VAL_INT;
		case VZ89X_VOC_RESISTANCE_IDX:
		case IIO_RESISTANCE:
			*val = vz89x_get_resistance_reading(data);
			ret = vz89x_get_resistance_reading(data, chan, val);
			if (!ret)
				return IIO_VAL_INT;
				return IIO_VAL_INT;
			break;
		default:
		default:
			return -EINVAL;
			return -EINVAL;
		}
		}
@@ -232,12 +303,12 @@ static int vz89x_read_raw(struct iio_dev *indio_dev,
		}
		}
		break;
		break;
	case IIO_CHAN_INFO_OFFSET:
	case IIO_CHAN_INFO_OFFSET:
		switch (chan->address) {
		switch (chan->channel2) {
		case VZ89X_VOC_CO2_IDX:
		case IIO_MOD_CO2:
			*val = 44;
			*val = 44;
			*val2 = 250000;
			*val2 = 250000;
			return IIO_VAL_INT_PLUS_MICRO;
			return IIO_VAL_INT_PLUS_MICRO;
		case VZ89X_VOC_TVOC_IDX:
		case IIO_MOD_VOC:
			*val = -13;
			*val = -13;
			return IIO_VAL_INT;
			return IIO_VAL_INT;
		default:
		default:
@@ -265,10 +336,21 @@ static const struct vz89x_chip_data vz89x_chips[] = {
		.channels = vz89x_channels,
		.channels = vz89x_channels,
		.num_channels = ARRAY_SIZE(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[] = {
static const struct of_device_id vz89x_dt_ids[] = {
	{ .compatible = "sgx,vz89x", .data = (void *) VZ89X },
	{ .compatible = "sgx,vz89x", .data = (void *) VZ89X },
	{ .compatible = "sgx,vz89te", .data = (void *) VZ89TE },
	{ }
	{ }
};
};
MODULE_DEVICE_TABLE(of, vz89x_dt_ids);
MODULE_DEVICE_TABLE(of, vz89x_dt_ids);
@@ -319,6 +401,7 @@ static int vz89x_probe(struct i2c_client *client,


static const struct i2c_device_id vz89x_id[] = {
static const struct i2c_device_id vz89x_id[] = {
	{ "vz89x", VZ89X },
	{ "vz89x", VZ89X },
	{ "vz89te", VZ89TE },
	{ }
	{ }
};
};
MODULE_DEVICE_TABLE(i2c, vz89x_id);
MODULE_DEVICE_TABLE(i2c, vz89x_id);