brintos

brintos / linux-shallow public Read only

0
0
Text · 16.9 KiB · 52cad54 Raw
767 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Sensirion SCD4X carbon dioxide sensor i2c driver4 *5 * Copyright (C) 2021 Protonic Holland6 * Author: Roan van Dijk <roan@protonic.nl>7 *8 * I2C slave address: 0x629 *10 * Datasheets:11 * https://www.sensirion.com/file/datasheet_scd4x12 */13 14#include <linux/unaligned.h>15#include <linux/crc8.h>16#include <linux/delay.h>17#include <linux/device.h>18#include <linux/i2c.h>19#include <linux/iio/buffer.h>20#include <linux/iio/iio.h>21#include <linux/iio/sysfs.h>22#include <linux/iio/trigger.h>23#include <linux/iio/trigger_consumer.h>24#include <linux/iio/triggered_buffer.h>25#include <linux/iio/types.h>26#include <linux/kernel.h>27#include <linux/mutex.h>28#include <linux/string.h>29#include <linux/sysfs.h>30#include <linux/types.h>31 32#define SCD4X_CRC8_POLYNOMIAL 0x3133#define SCD4X_TIMEOUT_ERR 100034#define SCD4X_READ_BUF_SIZE 935#define SCD4X_COMMAND_BUF_SIZE 236#define SCD4X_WRITE_BUF_SIZE 537#define SCD4X_FRC_MIN_PPM 038#define SCD4X_FRC_MAX_PPM 200039#define SCD4X_PRESSURE_COMP_MIN_MBAR 70040#define SCD4X_PRESSURE_COMP_MAX_MBAR 120041#define SCD4X_READY_MASK 0x0142 43/*Commands SCD4X*/44enum scd4x_cmd {45	CMD_START_MEAS          = 0x21b1,46	CMD_READ_MEAS           = 0xec05,47	CMD_STOP_MEAS           = 0x3f86,48	CMD_SET_TEMP_OFFSET     = 0x241d,49	CMD_GET_TEMP_OFFSET     = 0x2318,50	CMD_SET_AMB_PRESSURE	= 0xe000,51	CMD_GET_AMB_PRESSURE	= 0xe000,52	CMD_FRC                 = 0x362f,53	CMD_SET_ASC             = 0x2416,54	CMD_GET_ASC             = 0x2313,55	CMD_GET_DATA_READY      = 0xe4b8,56};57 58enum scd4x_channel_idx {59	SCD4X_CO2,60	SCD4X_TEMP,61	SCD4X_HR,62};63 64struct scd4x_state {65	struct i2c_client *client;66	/* maintain access to device, to prevent concurrent reads/writes */67	struct mutex lock;68	struct regulator *vdd;69};70 71DECLARE_CRC8_TABLE(scd4x_crc8_table);72 73static int scd4x_i2c_xfer(struct scd4x_state *state, char *txbuf, int txsize,74				char *rxbuf, int rxsize)75{76	struct i2c_client *client = state->client;77	int ret;78 79	ret = i2c_master_send(client, txbuf, txsize);80 81	if (ret < 0)82		return ret;83	if (ret != txsize)84		return -EIO;85 86	if (rxsize == 0)87		return 0;88 89	ret = i2c_master_recv(client, rxbuf, rxsize);90	if (ret < 0)91		return ret;92	if (ret != rxsize)93		return -EIO;94 95	return 0;96}97 98static int scd4x_send_command(struct scd4x_state *state, enum scd4x_cmd cmd)99{100	char buf[SCD4X_COMMAND_BUF_SIZE];101	int ret;102 103	/*104	 * Measurement needs to be stopped before sending commands.105	 * Except stop and start command.106	 */107	if ((cmd != CMD_STOP_MEAS) && (cmd != CMD_START_MEAS)) {108 109		ret = scd4x_send_command(state, CMD_STOP_MEAS);110		if (ret)111			return ret;112 113		/* execution time for stopping measurement */114		msleep_interruptible(500);115	}116 117	put_unaligned_be16(cmd, buf);118	ret = scd4x_i2c_xfer(state, buf, 2, buf, 0);119	if (ret)120		return ret;121 122	if ((cmd != CMD_STOP_MEAS) && (cmd != CMD_START_MEAS)) {123		ret = scd4x_send_command(state, CMD_START_MEAS);124		if (ret)125			return ret;126	}127 128	return 0;129}130 131static int scd4x_read(struct scd4x_state *state, enum scd4x_cmd cmd,132			void *response, int response_sz)133{134	struct i2c_client *client = state->client;135	char buf[SCD4X_READ_BUF_SIZE];136	char *rsp = response;137	int i, ret;138	char crc;139 140	/*141	 * Measurement needs to be stopped before sending commands.142	 * Except for reading measurement and data ready command.143	 */144	if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS) &&145	    (cmd != CMD_GET_AMB_PRESSURE)) {146		ret = scd4x_send_command(state, CMD_STOP_MEAS);147		if (ret)148			return ret;149 150		/* execution time for stopping measurement */151		msleep_interruptible(500);152	}153 154	/* CRC byte for every 2 bytes of data */155	response_sz += response_sz / 2;156 157	put_unaligned_be16(cmd, buf);158	ret = scd4x_i2c_xfer(state, buf, 2, buf, response_sz);159	if (ret)160		return ret;161 162	for (i = 0; i < response_sz; i += 3) {163		crc = crc8(scd4x_crc8_table, buf + i, 2, CRC8_INIT_VALUE);164		if (crc != buf[i + 2]) {165			dev_err(&client->dev, "CRC error\n");166			return -EIO;167		}168 169		*rsp++ = buf[i];170		*rsp++ = buf[i + 1];171	}172 173	/* start measurement */174	if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS) &&175	    (cmd != CMD_GET_AMB_PRESSURE)) {176		ret = scd4x_send_command(state, CMD_START_MEAS);177		if (ret)178			return ret;179	}180 181	return 0;182}183 184static int scd4x_write(struct scd4x_state *state, enum scd4x_cmd cmd, uint16_t arg)185{186	char buf[SCD4X_WRITE_BUF_SIZE];187	int ret;188	char crc;189 190	put_unaligned_be16(cmd, buf);191	put_unaligned_be16(arg, buf + 2);192 193	crc = crc8(scd4x_crc8_table, buf + 2, 2, CRC8_INIT_VALUE);194	buf[4] = crc;195 196	/* measurement needs to be stopped before sending commands */197	if (cmd != CMD_SET_AMB_PRESSURE) {198		ret = scd4x_send_command(state, CMD_STOP_MEAS);199		if (ret)200			return ret;201	}202 203	/* execution time */204	msleep_interruptible(500);205 206	ret = scd4x_i2c_xfer(state, buf, SCD4X_WRITE_BUF_SIZE, buf, 0);207	if (ret)208		return ret;209 210	/* start measurement, except for forced calibration command */211	if ((cmd != CMD_FRC) && (cmd != CMD_SET_AMB_PRESSURE)) {212		ret = scd4x_send_command(state, CMD_START_MEAS);213		if (ret)214			return ret;215	}216 217	return 0;218}219 220static int scd4x_write_and_fetch(struct scd4x_state *state, enum scd4x_cmd cmd,221				uint16_t arg, void *response, int response_sz)222{223	struct i2c_client *client = state->client;224	char buf[SCD4X_READ_BUF_SIZE];225	char *rsp = response;226	int i, ret;227	char crc;228 229	ret = scd4x_write(state, CMD_FRC, arg);230	if (ret)231		goto err;232 233	/* execution time */234	msleep_interruptible(400);235 236	/* CRC byte for every 2 bytes of data */237	response_sz += response_sz / 2;238 239	ret = i2c_master_recv(client, buf, response_sz);240	if (ret < 0)241		goto err;242	if (ret != response_sz) {243		ret = -EIO;244		goto err;245	}246 247	for (i = 0; i < response_sz; i += 3) {248		crc = crc8(scd4x_crc8_table, buf + i, 2, CRC8_INIT_VALUE);249		if (crc != buf[i + 2]) {250			dev_err(&client->dev, "CRC error\n");251			ret = -EIO;252			goto err;253		}254 255		*rsp++ = buf[i];256		*rsp++ = buf[i + 1];257	}258 259	return scd4x_send_command(state, CMD_START_MEAS);260 261err:262	/*263	 * on error try to start the measurement,264	 * puts sensor back into continuous measurement265	 */266	scd4x_send_command(state, CMD_START_MEAS);267 268	return ret;269}270 271static int scd4x_read_meas(struct scd4x_state *state, uint16_t *meas)272{273	int i, ret;274	__be16 buf[3];275 276	ret = scd4x_read(state, CMD_READ_MEAS, buf, sizeof(buf));277	if (ret)278		return ret;279 280	for (i = 0; i < ARRAY_SIZE(buf); i++)281		meas[i] = be16_to_cpu(buf[i]);282 283	return 0;284}285 286static int scd4x_wait_meas_poll(struct scd4x_state *state)287{288	struct i2c_client *client = state->client;289	int tries = 6;290	int ret;291 292	do {293		__be16 bval;294		uint16_t val;295 296		ret = scd4x_read(state, CMD_GET_DATA_READY, &bval, sizeof(bval));297		if (ret)298			return -EIO;299		val = be16_to_cpu(bval);300 301		/* new measurement available */302		if (val & 0x7FF)303			return 0;304 305		msleep_interruptible(1000);306	} while (--tries);307 308	/* try to start sensor on timeout */309	ret = scd4x_send_command(state, CMD_START_MEAS);310	if (ret)311		dev_err(&client->dev, "failed to start measurement: %d\n", ret);312 313	return -ETIMEDOUT;314}315 316static int scd4x_read_poll(struct scd4x_state *state, uint16_t *buf)317{318	int ret;319 320	ret = scd4x_wait_meas_poll(state);321	if (ret)322		return ret;323 324	return scd4x_read_meas(state, buf);325}326 327static int scd4x_read_channel(struct scd4x_state *state, int chan)328{329	int ret;330	uint16_t buf[3];331 332	ret = scd4x_read_poll(state, buf);333	if (ret)334		return ret;335 336	return buf[chan];337}338 339static int scd4x_read_raw(struct iio_dev *indio_dev,340			struct iio_chan_spec const *chan, int *val,341			int *val2, long mask)342{343	struct scd4x_state *state = iio_priv(indio_dev);344	int ret;345	__be16 tmp;346 347	switch (mask) {348	case IIO_CHAN_INFO_RAW:349		if (chan->output) {350			mutex_lock(&state->lock);351			ret = scd4x_read(state, CMD_GET_AMB_PRESSURE, &tmp, sizeof(tmp));352			mutex_unlock(&state->lock);353 354			if (ret)355				return ret;356 357			*val = be16_to_cpu(tmp);358			return IIO_VAL_INT;359		}360 361		ret = iio_device_claim_direct_mode(indio_dev);362		if (ret)363			return ret;364 365		mutex_lock(&state->lock);366		ret = scd4x_read_channel(state, chan->address);367		mutex_unlock(&state->lock);368 369		iio_device_release_direct_mode(indio_dev);370		if (ret < 0)371			return ret;372 373		*val = ret;374		return IIO_VAL_INT;375	case IIO_CHAN_INFO_SCALE:376		if (chan->type == IIO_CONCENTRATION) {377			*val = 0;378			*val2 = 100;379			return IIO_VAL_INT_PLUS_MICRO;380		} else if (chan->type == IIO_TEMP) {381			*val = 175000;382			*val2 = 65536;383			return IIO_VAL_FRACTIONAL;384		} else if (chan->type == IIO_HUMIDITYRELATIVE) {385			*val = 100000;386			*val2 = 65536;387			return IIO_VAL_FRACTIONAL;388		}389		return -EINVAL;390	case IIO_CHAN_INFO_OFFSET:391		*val = -16852;392		*val2 = 114286;393		return IIO_VAL_INT_PLUS_MICRO;394	case IIO_CHAN_INFO_CALIBBIAS:395		mutex_lock(&state->lock);396		ret = scd4x_read(state, CMD_GET_TEMP_OFFSET, &tmp, sizeof(tmp));397		mutex_unlock(&state->lock);398		if (ret)399			return ret;400 401		*val = be16_to_cpu(tmp);402 403		return IIO_VAL_INT;404	default:405		return -EINVAL;406	}407}408 409static const int scd4x_pressure_calibbias_available[] = {410	SCD4X_PRESSURE_COMP_MIN_MBAR, 1, SCD4X_PRESSURE_COMP_MAX_MBAR,411};412 413static int scd4x_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,414			    const int **vals, int *type, int *length, long mask)415{416	switch (mask) {417	case IIO_CHAN_INFO_RAW:418		*vals = scd4x_pressure_calibbias_available;419		*type = IIO_VAL_INT;420 421		return IIO_AVAIL_RANGE;422	}423 424	return -EINVAL;425}426 427 428static int scd4x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,429				int val, int val2, long mask)430{431	struct scd4x_state *state = iio_priv(indio_dev);432	int ret = 0;433 434	switch (mask) {435	case IIO_CHAN_INFO_CALIBBIAS:436		mutex_lock(&state->lock);437		ret = scd4x_write(state, CMD_SET_TEMP_OFFSET, val);438		mutex_unlock(&state->lock);439 440		return ret;441	case IIO_CHAN_INFO_RAW:442		switch (chan->type) {443		case IIO_PRESSURE:444			if (val < SCD4X_PRESSURE_COMP_MIN_MBAR ||445			    val > SCD4X_PRESSURE_COMP_MAX_MBAR)446				return -EINVAL;447 448			mutex_lock(&state->lock);449			ret = scd4x_write(state, CMD_SET_AMB_PRESSURE, val);450			mutex_unlock(&state->lock);451 452			return ret;453		default:454			return -EINVAL;455		}456	default:457		return -EINVAL;458	}459}460 461static ssize_t calibration_auto_enable_show(struct device *dev,462			struct device_attribute *attr, char *buf)463{464	struct iio_dev *indio_dev = dev_to_iio_dev(dev);465	struct scd4x_state *state = iio_priv(indio_dev);466	int ret;467	__be16 bval;468	u16 val;469 470	mutex_lock(&state->lock);471	ret = scd4x_read(state, CMD_GET_ASC, &bval, sizeof(bval));472	mutex_unlock(&state->lock);473	if (ret) {474		dev_err(dev, "failed to read automatic calibration");475		return ret;476	}477 478	val = (be16_to_cpu(bval) & SCD4X_READY_MASK) ? 1 : 0;479 480	return sysfs_emit(buf, "%d\n", val);481}482 483static ssize_t calibration_auto_enable_store(struct device *dev,484					struct device_attribute *attr,485					const char *buf, size_t len)486{487	struct iio_dev *indio_dev = dev_to_iio_dev(dev);488	struct scd4x_state *state = iio_priv(indio_dev);489	bool val;490	int ret;491	uint16_t value;492 493	ret = kstrtobool(buf, &val);494	if (ret)495		return ret;496 497	value = val;498 499	mutex_lock(&state->lock);500	ret = scd4x_write(state, CMD_SET_ASC, value);501	mutex_unlock(&state->lock);502	if (ret)503		dev_err(dev, "failed to set automatic calibration");504 505	return ret ?: len;506}507 508static ssize_t calibration_forced_value_store(struct device *dev,509					struct device_attribute *attr,510					const char *buf, size_t len)511{512	struct iio_dev *indio_dev = dev_to_iio_dev(dev);513	struct scd4x_state *state = iio_priv(indio_dev);514	uint16_t val, arg;515	int ret;516 517	ret = kstrtou16(buf, 0, &arg);518	if (ret)519		return ret;520 521	if (arg < SCD4X_FRC_MIN_PPM || arg > SCD4X_FRC_MAX_PPM)522		return -EINVAL;523 524	mutex_lock(&state->lock);525	ret = scd4x_write_and_fetch(state, CMD_FRC, arg, &val, sizeof(val));526	mutex_unlock(&state->lock);527 528	if (ret)529		return ret;530 531	if (val == 0xff) {532		dev_err(dev, "forced calibration has failed");533		return -EINVAL;534	}535 536	return len;537}538 539static IIO_DEVICE_ATTR_RW(calibration_auto_enable, 0);540static IIO_DEVICE_ATTR_WO(calibration_forced_value, 0);541 542static IIO_CONST_ATTR(calibration_forced_value_available,543	       __stringify([SCD4X_FRC_MIN_PPM 1 SCD4X_FRC_MAX_PPM]));544 545static struct attribute *scd4x_attrs[] = {546	&iio_dev_attr_calibration_auto_enable.dev_attr.attr,547	&iio_dev_attr_calibration_forced_value.dev_attr.attr,548	&iio_const_attr_calibration_forced_value_available.dev_attr.attr,549	NULL550};551 552static const struct attribute_group scd4x_attr_group = {553	.attrs = scd4x_attrs,554};555 556static const struct iio_info scd4x_info = {557	.attrs = &scd4x_attr_group,558	.read_raw = scd4x_read_raw,559	.write_raw = scd4x_write_raw,560	.read_avail = scd4x_read_avail,561};562 563static const struct iio_chan_spec scd4x_channels[] = {564	{565		/*566		 * this channel is special in a sense we are pretending that567		 * sensor is able to change measurement chamber pressure but in568		 * fact we're just setting pressure compensation value569		 */570		.type = IIO_PRESSURE,571		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),572		.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),573		.output = 1,574		.scan_index = -1,575	},576	{577		.type = IIO_CONCENTRATION,578		.channel2 = IIO_MOD_CO2,579		.modified = 1,580		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |581					BIT(IIO_CHAN_INFO_SCALE),582		.address = SCD4X_CO2,583		.scan_index = SCD4X_CO2,584		.scan_type = {585			.sign = 'u',586			.realbits = 16,587			.storagebits = 16,588			.endianness = IIO_BE,589		},590	},591	{592		.type = IIO_TEMP,593		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |594					BIT(IIO_CHAN_INFO_SCALE) |595					BIT(IIO_CHAN_INFO_OFFSET) |596					BIT(IIO_CHAN_INFO_CALIBBIAS),597		.address = SCD4X_TEMP,598		.scan_index = SCD4X_TEMP,599		.scan_type = {600			.sign = 'u',601			.realbits = 16,602			.storagebits = 16,603			.endianness = IIO_BE,604		},605	},606	{607		.type = IIO_HUMIDITYRELATIVE,608		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |609					BIT(IIO_CHAN_INFO_SCALE),610		.address = SCD4X_HR,611		.scan_index = SCD4X_HR,612		.scan_type = {613			.sign = 'u',614			.realbits = 16,615			.storagebits = 16,616			.endianness = IIO_BE,617		},618	},619};620 621static int scd4x_suspend(struct device *dev)622{623	struct iio_dev *indio_dev = dev_get_drvdata(dev);624	struct scd4x_state *state  = iio_priv(indio_dev);625	int ret;626 627	ret = scd4x_send_command(state, CMD_STOP_MEAS);628	if (ret)629		return ret;630 631	return regulator_disable(state->vdd);632}633 634static int scd4x_resume(struct device *dev)635{636	struct iio_dev *indio_dev = dev_get_drvdata(dev);637	struct scd4x_state *state = iio_priv(indio_dev);638	int ret;639 640	ret = regulator_enable(state->vdd);641	if (ret)642		return ret;643 644	return scd4x_send_command(state, CMD_START_MEAS);645}646 647static DEFINE_SIMPLE_DEV_PM_OPS(scd4x_pm_ops, scd4x_suspend, scd4x_resume);648 649static void scd4x_stop_meas(void *state)650{651	scd4x_send_command(state, CMD_STOP_MEAS);652}653 654static void scd4x_disable_regulator(void *data)655{656	struct scd4x_state *state = data;657 658	regulator_disable(state->vdd);659}660 661static irqreturn_t scd4x_trigger_handler(int irq, void *p)662{663	struct iio_poll_func *pf = p;664	struct iio_dev *indio_dev = pf->indio_dev;665	struct scd4x_state *state = iio_priv(indio_dev);666	struct {667		uint16_t data[3];668		int64_t ts __aligned(8);669	} scan;670	int ret;671 672	memset(&scan, 0, sizeof(scan));673	mutex_lock(&state->lock);674	ret = scd4x_read_poll(state, scan.data);675	mutex_unlock(&state->lock);676	if (ret)677		goto out;678 679	iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev));680out:681	iio_trigger_notify_done(indio_dev->trig);682	return IRQ_HANDLED;683}684 685static int scd4x_probe(struct i2c_client *client)686{687	static const unsigned long scd4x_scan_masks[] = { 0x07, 0x00 };688	struct device *dev = &client->dev;689	struct iio_dev *indio_dev;690	struct scd4x_state *state;691	int ret;692 693	indio_dev = devm_iio_device_alloc(dev, sizeof(*state));694	if (!indio_dev)695		return -ENOMEM;696 697	state = iio_priv(indio_dev);698	mutex_init(&state->lock);699	state->client = client;700	crc8_populate_msb(scd4x_crc8_table, SCD4X_CRC8_POLYNOMIAL);701 702	indio_dev->info = &scd4x_info;703	indio_dev->name = client->name;704	indio_dev->channels = scd4x_channels;705	indio_dev->num_channels = ARRAY_SIZE(scd4x_channels);706	indio_dev->modes = INDIO_DIRECT_MODE;707	indio_dev->available_scan_masks = scd4x_scan_masks;708 709	state->vdd = devm_regulator_get(dev, "vdd");710	if (IS_ERR(state->vdd))711		return dev_err_probe(dev, PTR_ERR(state->vdd), "failed to get regulator\n");712 713	ret = regulator_enable(state->vdd);714	if (ret)715		return ret;716 717	ret = devm_add_action_or_reset(dev, scd4x_disable_regulator, state);718	if (ret)719		return ret;720 721	ret = scd4x_send_command(state, CMD_STOP_MEAS);722	if (ret) {723		dev_err(dev, "failed to stop measurement: %d\n", ret);724		return ret;725	}726 727	/* execution time */728	msleep_interruptible(500);729 730	ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, scd4x_trigger_handler, NULL);731	if (ret)732		return ret;733 734	ret = scd4x_send_command(state, CMD_START_MEAS);735	if (ret) {736		dev_err(dev, "failed to start measurement: %d\n", ret);737		return ret;738	}739 740	ret = devm_add_action_or_reset(dev, scd4x_stop_meas, state);741	if (ret)742		return ret;743 744	return devm_iio_device_register(dev, indio_dev);745}746 747static const struct of_device_id scd4x_dt_ids[] = {748	{ .compatible = "sensirion,scd40" },749	{ .compatible = "sensirion,scd41" },750	{ }751};752MODULE_DEVICE_TABLE(of, scd4x_dt_ids);753 754static struct i2c_driver scd4x_i2c_driver = {755	.driver = {756		.name = KBUILD_MODNAME,757		.of_match_table = scd4x_dt_ids,758		.pm = pm_sleep_ptr(&scd4x_pm_ops),759	},760	.probe = scd4x_probe,761};762module_i2c_driver(scd4x_i2c_driver);763 764MODULE_AUTHOR("Roan van Dijk <roan@protonic.nl>");765MODULE_DESCRIPTION("Sensirion SCD4X carbon dioxide sensor core driver");766MODULE_LICENSE("GPL v2");767