brintos

brintos / linux-shallow public Read only

0
0
Text · 6.4 KiB · 6b0aa3a Raw
273 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Aosong AM2315 relative humidity and temperature4 *5 * Copyright (c) 2016, Intel Corporation.6 *7 * 7-bit I2C address: 0x5C.8 */9 10#include <linux/delay.h>11#include <linux/i2c.h>12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/iio/buffer.h>15#include <linux/iio/iio.h>16#include <linux/iio/sysfs.h>17#include <linux/iio/trigger_consumer.h>18#include <linux/iio/triggered_buffer.h>19 20#define AM2315_REG_HUM_MSB			0x0021#define AM2315_REG_HUM_LSB			0x0122#define AM2315_REG_TEMP_MSB			0x0223#define AM2315_REG_TEMP_LSB			0x0324 25#define AM2315_FUNCTION_READ			0x0326#define AM2315_HUM_OFFSET			227#define AM2315_TEMP_OFFSET			428#define AM2315_ALL_CHANNEL_MASK			GENMASK(1, 0)29 30#define AM2315_DRIVER_NAME			"am2315"31 32struct am2315_data {33	struct i2c_client *client;34	struct mutex lock;35	/* Ensure timestamp is naturally aligned */36	struct {37		s16 chans[2];38		s64 timestamp __aligned(8);39	} scan;40};41 42struct am2315_sensor_data {43	s16 hum_data;44	s16 temp_data;45};46 47static const struct iio_chan_spec am2315_channels[] = {48	{49		.type = IIO_HUMIDITYRELATIVE,50		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |51				      BIT(IIO_CHAN_INFO_SCALE),52		.scan_index = 0,53		.scan_type = {54			.sign = 's',55			.realbits = 16,56			.storagebits = 16,57			.endianness = IIO_CPU,58		},59	},60	{61		.type = IIO_TEMP,62		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |63				      BIT(IIO_CHAN_INFO_SCALE),64		.scan_index = 1,65		.scan_type = {66			.sign = 's',67			.realbits = 16,68			.storagebits = 16,69			.endianness = IIO_CPU,70		},71	},72	IIO_CHAN_SOFT_TIMESTAMP(2),73};74 75/* CRC calculation algorithm, as specified in the datasheet (page 13). */76static u16 am2315_crc(u8 *data, u8 nr_bytes)77{78	int i;79	u16 crc = 0xffff;80 81	while (nr_bytes--) {82		crc ^= *data++;83		for (i = 0; i < 8; i++) {84			if (crc & 0x01) {85				crc >>= 1;86				crc ^= 0xA001;87			} else {88				crc >>= 1;89			}90		}91	}92 93	return crc;94}95 96/* Simple function that sends a few bytes to the device to wake it up. */97static void am2315_ping(struct i2c_client *client)98{99	i2c_smbus_read_byte_data(client, AM2315_REG_HUM_MSB);100}101 102static int am2315_read_data(struct am2315_data *data,103			    struct am2315_sensor_data *sensor_data)104{105	int ret;106	/* tx_buf format: <function code> <start addr> <nr of regs to read> */107	u8 tx_buf[3] = { AM2315_FUNCTION_READ, AM2315_REG_HUM_MSB, 4 };108	/*109	 * rx_buf format:110	 * <function code> <number of registers read>111	 * <humidity MSB> <humidity LSB> <temp MSB> <temp LSB>112	 * <CRC LSB> <CRC MSB>113	 */114	u8 rx_buf[8];115	u16 crc;116 117	/* First wake up the device. */118	am2315_ping(data->client);119 120	mutex_lock(&data->lock);121	ret = i2c_master_send(data->client, tx_buf, sizeof(tx_buf));122	if (ret < 0) {123		dev_err(&data->client->dev, "failed to send read request\n");124		goto exit_unlock;125	}126	/* Wait 2-3 ms, then read back the data sent by the device. */127	usleep_range(2000, 3000);128	/* Do a bulk data read, then pick out what we need. */129	ret = i2c_master_recv(data->client, rx_buf, sizeof(rx_buf));130	if (ret < 0) {131		dev_err(&data->client->dev, "failed to read sensor data\n");132		goto exit_unlock;133	}134	mutex_unlock(&data->lock);135	/*136	 * Do a CRC check on the data and compare it to the value137	 * calculated by the device.138	 */139	crc = am2315_crc(rx_buf, sizeof(rx_buf) - 2);140	if ((crc & 0xff) != rx_buf[6] || (crc >> 8) != rx_buf[7]) {141		dev_err(&data->client->dev, "failed to verify sensor data\n");142		return -EIO;143	}144 145	sensor_data->hum_data = (rx_buf[AM2315_HUM_OFFSET] << 8) |146				 rx_buf[AM2315_HUM_OFFSET + 1];147	sensor_data->temp_data = (rx_buf[AM2315_TEMP_OFFSET] << 8) |148				  rx_buf[AM2315_TEMP_OFFSET + 1];149 150	return ret;151 152exit_unlock:153	mutex_unlock(&data->lock);154	return ret;155}156 157static irqreturn_t am2315_trigger_handler(int irq, void *p)158{159	int i;160	int ret;161	int bit;162	struct iio_poll_func *pf = p;163	struct iio_dev *indio_dev = pf->indio_dev;164	struct am2315_data *data = iio_priv(indio_dev);165	struct am2315_sensor_data sensor_data;166 167	ret = am2315_read_data(data, &sensor_data);168	if (ret < 0)169		goto err;170 171	mutex_lock(&data->lock);172	if (*(indio_dev->active_scan_mask) == AM2315_ALL_CHANNEL_MASK) {173		data->scan.chans[0] = sensor_data.hum_data;174		data->scan.chans[1] = sensor_data.temp_data;175	} else {176		i = 0;177		iio_for_each_active_channel(indio_dev, bit) {178			data->scan.chans[i] = (bit ? sensor_data.temp_data :179					       sensor_data.hum_data);180			i++;181		}182	}183	mutex_unlock(&data->lock);184 185	iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,186					   pf->timestamp);187err:188	iio_trigger_notify_done(indio_dev->trig);189	return IRQ_HANDLED;190}191 192static int am2315_read_raw(struct iio_dev *indio_dev,193			   struct iio_chan_spec const *chan,194			   int *val, int *val2, long mask)195{196	int ret;197	struct am2315_sensor_data sensor_data;198	struct am2315_data *data = iio_priv(indio_dev);199 200	switch (mask) {201	case IIO_CHAN_INFO_RAW:202		ret = am2315_read_data(data, &sensor_data);203		if (ret < 0)204			return ret;205		*val = (chan->type == IIO_HUMIDITYRELATIVE) ?206				sensor_data.hum_data : sensor_data.temp_data;207		return IIO_VAL_INT;208	case IIO_CHAN_INFO_SCALE:209		*val = 100;210		return IIO_VAL_INT;211	}212 213	return -EINVAL;214}215 216static const struct iio_info am2315_info = {217	.read_raw		= am2315_read_raw,218};219 220static int am2315_probe(struct i2c_client *client)221{222	int ret;223	struct iio_dev *indio_dev;224	struct am2315_data *data;225 226	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));227	if (!indio_dev) {228		dev_err(&client->dev, "iio allocation failed!\n");229		return -ENOMEM;230	}231 232	data = iio_priv(indio_dev);233	data->client = client;234	i2c_set_clientdata(client, indio_dev);235	mutex_init(&data->lock);236 237	indio_dev->info = &am2315_info;238	indio_dev->name = AM2315_DRIVER_NAME;239	indio_dev->modes = INDIO_DIRECT_MODE;240	indio_dev->channels = am2315_channels;241	indio_dev->num_channels = ARRAY_SIZE(am2315_channels);242 243	ret = devm_iio_triggered_buffer_setup(&client->dev,244					indio_dev, iio_pollfunc_store_time,245					 am2315_trigger_handler, NULL);246	if (ret < 0) {247		dev_err(&client->dev, "iio triggered buffer setup failed\n");248		return ret;249	}250 251	return devm_iio_device_register(&client->dev, indio_dev);252}253 254static const struct i2c_device_id am2315_i2c_id[] = {255	{ "am2315" },256	{}257};258MODULE_DEVICE_TABLE(i2c, am2315_i2c_id);259 260static struct i2c_driver am2315_driver = {261	.driver = {262		.name = "am2315",263	},264	.probe =        am2315_probe,265	.id_table =         am2315_i2c_id,266};267 268module_i2c_driver(am2315_driver);269 270MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");271MODULE_DESCRIPTION("Aosong AM2315 relative humidity and temperature");272MODULE_LICENSE("GPL v2");273