brintos

brintos / linux-shallow public Read only

0
0
Text · 13.3 KiB · cdb8696 Raw
538 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Senseair Sunrise 006-0-0007 CO2 sensor driver.4 *5 * Copyright (C) 2021 Jacopo Mondi6 *7 * List of features not yet supported by the driver:8 * - controllable EN pin9 * - single-shot operations using the nDRY pin.10 * - ABC/target calibration11 */12 13#include <linux/bitops.h>14#include <linux/i2c.h>15#include <linux/kernel.h>16#include <linux/mod_devicetable.h>17#include <linux/module.h>18#include <linux/mutex.h>19#include <linux/regmap.h>20#include <linux/time64.h>21 22#include <linux/iio/iio.h>23 24#define DRIVER_NAME "sunrise_co2"25 26#define SUNRISE_ERROR_STATUS_REG		0x0027#define SUNRISE_CO2_FILTERED_COMP_REG		0x0628#define SUNRISE_CHIP_TEMPERATURE_REG		0x0829#define SUNRISE_CALIBRATION_STATUS_REG		0x8130#define SUNRISE_CALIBRATION_COMMAND_REG		0x8231#define SUNRISE_CALIBRATION_FACTORY_CMD		0x7c0232#define SUNRISE_CALIBRATION_BACKGROUND_CMD	0x7c0633/*34 * The calibration timeout is not characterized in the datasheet.35 * Use 30 seconds as a reasonable upper limit.36 */37#define SUNRISE_CALIBRATION_TIMEOUT_US		(30 * USEC_PER_SEC)38 39struct sunrise_dev {40	struct i2c_client *client;41	struct regmap *regmap;42	/* Protects access to IIO attributes. */43	struct mutex lock;44	bool ignore_nak;45};46 47/* Custom regmap read/write operations: perform unlocked access to the i2c bus. */48 49static int sunrise_regmap_read(void *context, const void *reg_buf,50			       size_t reg_size, void *val_buf, size_t val_size)51{52	struct i2c_client *client = context;53	struct sunrise_dev *sunrise = i2c_get_clientdata(client);54	union i2c_smbus_data data;55	int ret;56 57	if (reg_size != 1 || !val_size)58		return -EINVAL;59 60	memset(&data, 0, sizeof(data));61	data.block[0] = val_size;62 63	/*64	 * Wake up sensor by sending sensor address: START, sensor address,65	 * STOP. Sensor will not ACK this byte.66	 *67	 * The chip enters a low power state after 15ms without68	 * communications or after a complete read/write sequence.69	 */70	__i2c_smbus_xfer(client->adapter, client->addr,71			 sunrise->ignore_nak ? I2C_M_IGNORE_NAK : 0,72			 I2C_SMBUS_WRITE, 0, I2C_SMBUS_BYTE_DATA, &data);73 74	usleep_range(500, 1500);75 76	ret = __i2c_smbus_xfer(client->adapter, client->addr, client->flags,77			       I2C_SMBUS_READ, ((u8 *)reg_buf)[0],78			       I2C_SMBUS_I2C_BLOCK_DATA, &data);79	if (ret < 0)80		return ret;81 82	memcpy(val_buf, &data.block[1], data.block[0]);83 84	return 0;85}86 87static int sunrise_regmap_write(void *context, const void *val_buf, size_t count)88{89	struct i2c_client *client = context;90	struct sunrise_dev *sunrise = i2c_get_clientdata(client);91	union i2c_smbus_data data;92 93	/* Discard reg address from values count. */94	if (!count)95		return -EINVAL;96	count--;97 98	memset(&data, 0, sizeof(data));99	data.block[0] = count;100	memcpy(&data.block[1], (u8 *)val_buf + 1, count);101 102	__i2c_smbus_xfer(client->adapter, client->addr,103			 sunrise->ignore_nak ? I2C_M_IGNORE_NAK : 0,104			 I2C_SMBUS_WRITE, 0, I2C_SMBUS_BYTE_DATA, &data);105 106	usleep_range(500, 1500);107 108	return __i2c_smbus_xfer(client->adapter, client->addr, client->flags,109				I2C_SMBUS_WRITE, ((u8 *)val_buf)[0],110				I2C_SMBUS_I2C_BLOCK_DATA, &data);111}112 113/*114 * Sunrise i2c read/write operations: lock the i2c segment to avoid losing the115 * wake up session. Use custom regmap operations that perform unlocked access to116 * the i2c bus.117 */118static int sunrise_read_byte(struct sunrise_dev *sunrise, u8 reg)119{120	const struct i2c_client *client = sunrise->client;121	const struct device *dev = &client->dev;122	unsigned int val;123	int ret;124 125	i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);126	ret = regmap_read(sunrise->regmap, reg, &val);127	i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);128	if (ret) {129		dev_err(dev, "Read byte failed: reg 0x%02x (%d)\n", reg, ret);130		return ret;131	}132 133	return val;134}135 136static int sunrise_read_word(struct sunrise_dev *sunrise, u8 reg, u16 *val)137{138	const struct i2c_client *client = sunrise->client;139	const struct device *dev = &client->dev;140	__be16 be_val;141	int ret;142 143	i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);144	ret = regmap_bulk_read(sunrise->regmap, reg, &be_val, sizeof(be_val));145	i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);146	if (ret) {147		dev_err(dev, "Read word failed: reg 0x%02x (%d)\n", reg, ret);148		return ret;149	}150 151	*val = be16_to_cpu(be_val);152 153	return 0;154}155 156static int sunrise_write_byte(struct sunrise_dev *sunrise, u8 reg, u8 val)157{158	const struct i2c_client *client = sunrise->client;159	const struct device *dev = &client->dev;160	int ret;161 162	i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);163	ret = regmap_write(sunrise->regmap, reg, val);164	i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);165	if (ret)166		dev_err(dev, "Write byte failed: reg 0x%02x (%d)\n", reg, ret);167 168	return ret;169}170 171static int sunrise_write_word(struct sunrise_dev *sunrise, u8 reg, u16 data)172{173	const struct i2c_client *client = sunrise->client;174	const struct device *dev = &client->dev;175	__be16 be_data = cpu_to_be16(data);176	int ret;177 178	i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);179	ret = regmap_bulk_write(sunrise->regmap, reg, &be_data, sizeof(be_data));180	i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);181	if (ret)182		dev_err(dev, "Write word failed: reg 0x%02x (%d)\n", reg, ret);183 184	return ret;185}186 187/* Trigger a calibration cycle. */188 189enum {190	SUNRISE_CALIBRATION_FACTORY,191	SUNRISE_CALIBRATION_BACKGROUND,192};193 194static const struct sunrise_calib_data {195	u16 cmd;196	u8 bit;197	const char * const name;198} calib_data[] = {199	[SUNRISE_CALIBRATION_FACTORY] = {200		SUNRISE_CALIBRATION_FACTORY_CMD,201		BIT(2),202		"factory_calibration",203	},204	[SUNRISE_CALIBRATION_BACKGROUND] = {205		SUNRISE_CALIBRATION_BACKGROUND_CMD,206		BIT(5),207		"background_calibration",208	},209};210 211static int sunrise_calibrate(struct sunrise_dev *sunrise,212			     const struct sunrise_calib_data *data)213{214	unsigned int status;215	int ret;216 217	/* Reset the calibration status reg. */218	ret = sunrise_write_byte(sunrise, SUNRISE_CALIBRATION_STATUS_REG, 0x00);219	if (ret)220		return ret;221 222	/* Write a calibration command and poll the calibration status bit. */223	ret = sunrise_write_word(sunrise, SUNRISE_CALIBRATION_COMMAND_REG, data->cmd);224	if (ret)225		return ret;226 227	dev_dbg(&sunrise->client->dev, "%s in progress\n", data->name);228 229	/*230	 * Calibration takes several seconds, so the sleep time between reads231	 * can be pretty relaxed.232	 */233	return read_poll_timeout(sunrise_read_byte, status, status & data->bit,234				 200000, SUNRISE_CALIBRATION_TIMEOUT_US, false,235				 sunrise, SUNRISE_CALIBRATION_STATUS_REG);236}237 238static ssize_t sunrise_cal_factory_write(struct iio_dev *iiodev,239					 uintptr_t private,240					 const struct iio_chan_spec *chan,241					 const char *buf, size_t len)242{243	struct sunrise_dev *sunrise = iio_priv(iiodev);244	bool enable;245	int ret;246 247	ret = kstrtobool(buf, &enable);248	if (ret)249		return ret;250 251	if (!enable)252		return len;253 254	mutex_lock(&sunrise->lock);255	ret = sunrise_calibrate(sunrise, &calib_data[SUNRISE_CALIBRATION_FACTORY]);256	mutex_unlock(&sunrise->lock);257	if (ret)258		return ret;259 260	return len;261}262 263static ssize_t sunrise_cal_background_write(struct iio_dev *iiodev,264					    uintptr_t private,265					    const struct iio_chan_spec *chan,266					    const char *buf, size_t len)267{268	struct sunrise_dev *sunrise = iio_priv(iiodev);269	bool enable;270	int ret;271 272	ret = kstrtobool(buf, &enable);273	if (ret)274		return ret;275 276	if (!enable)277		return len;278 279	mutex_lock(&sunrise->lock);280	ret = sunrise_calibrate(sunrise, &calib_data[SUNRISE_CALIBRATION_BACKGROUND]);281	mutex_unlock(&sunrise->lock);282	if (ret)283		return ret;284 285	return len;286}287 288 /* Enumerate and retrieve the chip error status. */289enum {290	SUNRISE_ERROR_FATAL,291	SUNRISE_ERROR_I2C,292	SUNRISE_ERROR_ALGORITHM,293	SUNRISE_ERROR_CALIBRATION,294	SUNRISE_ERROR_SELF_DIAGNOSTIC,295	SUNRISE_ERROR_OUT_OF_RANGE,296	SUNRISE_ERROR_MEMORY,297	SUNRISE_ERROR_NO_MEASUREMENT,298	SUNRISE_ERROR_LOW_VOLTAGE,299	SUNRISE_ERROR_MEASUREMENT_TIMEOUT,300};301 302static const char * const sunrise_error_statuses[] = {303	[SUNRISE_ERROR_FATAL] = "error_fatal",304	[SUNRISE_ERROR_I2C] = "error_i2c",305	[SUNRISE_ERROR_ALGORITHM] = "error_algorithm",306	[SUNRISE_ERROR_CALIBRATION] = "error_calibration",307	[SUNRISE_ERROR_SELF_DIAGNOSTIC] = "error_self_diagnostic",308	[SUNRISE_ERROR_OUT_OF_RANGE] = "error_out_of_range",309	[SUNRISE_ERROR_MEMORY] = "error_memory",310	[SUNRISE_ERROR_NO_MEASUREMENT] = "error_no_measurement",311	[SUNRISE_ERROR_LOW_VOLTAGE] = "error_low_voltage",312	[SUNRISE_ERROR_MEASUREMENT_TIMEOUT] = "error_measurement_timeout",313};314 315static const struct iio_enum sunrise_error_statuses_enum = {316	.items = sunrise_error_statuses,317	.num_items = ARRAY_SIZE(sunrise_error_statuses),318};319 320static ssize_t sunrise_error_status_read(struct iio_dev *iiodev,321					 uintptr_t private,322					 const struct iio_chan_spec *chan,323					 char *buf)324{325	struct sunrise_dev *sunrise = iio_priv(iiodev);326	unsigned long errors;327	ssize_t len = 0;328	u16 value;329	int ret;330	u8 i;331 332	mutex_lock(&sunrise->lock);333	ret = sunrise_read_word(sunrise, SUNRISE_ERROR_STATUS_REG, &value);334	if (ret) {335		mutex_unlock(&sunrise->lock);336		return ret;337	}338 339	errors = value;340	for_each_set_bit(i, &errors, ARRAY_SIZE(sunrise_error_statuses))341		len += sysfs_emit_at(buf, len, "%s ", sunrise_error_statuses[i]);342 343	if (len)344		buf[len - 1] = '\n';345 346	mutex_unlock(&sunrise->lock);347 348	return len;349}350 351static const struct iio_chan_spec_ext_info sunrise_concentration_ext_info[] = {352	/* Calibration triggers. */353	{354		.name = "calibration_factory",355		.write = sunrise_cal_factory_write,356		.shared = IIO_SEPARATE,357	},358	{359		.name = "calibration_background",360		.write = sunrise_cal_background_write,361		.shared = IIO_SEPARATE,362	},363 364	/* Error statuses. */365	{366		.name = "error_status",367		.read = sunrise_error_status_read,368		.shared = IIO_SHARED_BY_ALL,369	},370	{371		.name = "error_status_available",372		.shared = IIO_SHARED_BY_ALL,373		.read = iio_enum_available_read,374		.private = (uintptr_t)&sunrise_error_statuses_enum,375	},376	{}377};378 379static const struct iio_chan_spec sunrise_channels[] = {380	{381		.type = IIO_CONCENTRATION,382		.modified = 1,383		.channel2 = IIO_MOD_CO2,384		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |385				      BIT(IIO_CHAN_INFO_SCALE),386		.ext_info = sunrise_concentration_ext_info,387	},388	{389		.type = IIO_TEMP,390		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |391				      BIT(IIO_CHAN_INFO_SCALE),392	},393};394 395static int sunrise_read_raw(struct iio_dev *iio_dev,396			    const struct iio_chan_spec *chan,397			    int *val, int *val2, long mask)398{399	struct sunrise_dev *sunrise = iio_priv(iio_dev);400	u16 value;401	int ret;402 403	switch (mask) {404	case IIO_CHAN_INFO_RAW:405		switch (chan->type) {406		case IIO_CONCENTRATION:407			mutex_lock(&sunrise->lock);408			ret = sunrise_read_word(sunrise, SUNRISE_CO2_FILTERED_COMP_REG,409						&value);410			mutex_unlock(&sunrise->lock);411 412			if (ret)413				return ret;414 415			*val = value;416			return IIO_VAL_INT;417 418		case IIO_TEMP:419			mutex_lock(&sunrise->lock);420			ret = sunrise_read_word(sunrise, SUNRISE_CHIP_TEMPERATURE_REG,421						&value);422			mutex_unlock(&sunrise->lock);423 424			if (ret)425				return ret;426 427			*val = value;428			return IIO_VAL_INT;429 430		default:431			return -EINVAL;432		}433 434	case IIO_CHAN_INFO_SCALE:435		switch (chan->type) {436		case IIO_CONCENTRATION:437			/*438			 * 1 / 10^4 to comply with IIO scale for CO2439			 * (percentage). The chip CO2 reading range is [400 -440			 * 5000] ppm which corresponds to [0,004 - 0,5] %.441			 */442			*val = 1;443			*val2 = 10000;444			return IIO_VAL_FRACTIONAL;445 446		case IIO_TEMP:447			/* x10 to comply with IIO scale (millidegrees celsius). */448			*val = 10;449			return IIO_VAL_INT;450 451		default:452			return -EINVAL;453		}454 455	default:456		return -EINVAL;457	}458}459 460static const struct iio_info sunrise_info = {461	.read_raw = sunrise_read_raw,462};463 464static const struct regmap_bus sunrise_regmap_bus = {465	.read = sunrise_regmap_read,466	.write = sunrise_regmap_write,467};468 469static const struct regmap_config sunrise_regmap_config = {470	.reg_bits = 8,471	.val_bits = 8,472};473 474static int sunrise_probe(struct i2c_client *client)475{476	struct sunrise_dev *sunrise;477	struct iio_dev *iio_dev;478 479	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |480						      I2C_FUNC_SMBUS_BLOCK_DATA)) {481		dev_err(&client->dev,482			"Adapter does not support required functionalities\n");483		return -EOPNOTSUPP;484	}485 486	iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*sunrise));487	if (!iio_dev)488		return -ENOMEM;489 490	sunrise = iio_priv(iio_dev);491	sunrise->client = client;492	mutex_init(&sunrise->lock);493 494	i2c_set_clientdata(client, sunrise);495 496	sunrise->regmap = devm_regmap_init(&client->dev, &sunrise_regmap_bus,497					   client, &sunrise_regmap_config);498	if (IS_ERR(sunrise->regmap)) {499		dev_err(&client->dev, "Failed to initialize regmap\n");500		return PTR_ERR(sunrise->regmap);501	}502 503	/*504	 * The chip nacks the wake up message. If the adapter does not support505	 * protocol mangling do not set the I2C_M_IGNORE_NAK flag at the expense506	 * of possible cruft in the logs.507	 */508	if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING))509		sunrise->ignore_nak = true;510 511	iio_dev->info = &sunrise_info;512	iio_dev->name = DRIVER_NAME;513	iio_dev->channels = sunrise_channels;514	iio_dev->num_channels = ARRAY_SIZE(sunrise_channels);515	iio_dev->modes = INDIO_DIRECT_MODE;516 517	return devm_iio_device_register(&client->dev, iio_dev);518}519 520static const struct of_device_id sunrise_of_match[] = {521	{ .compatible = "senseair,sunrise-006-0-0007" },522	{}523};524MODULE_DEVICE_TABLE(of, sunrise_of_match);525 526static struct i2c_driver sunrise_driver = {527	.driver = {528		.name = DRIVER_NAME,529		.of_match_table = sunrise_of_match,530	},531	.probe = sunrise_probe,532};533module_i2c_driver(sunrise_driver);534 535MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>");536MODULE_DESCRIPTION("Senseair Sunrise 006-0-0007 CO2 sensor IIO driver");537MODULE_LICENSE("GPL v2");538