cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

am2315.c (6624B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Aosong AM2315 relative humidity and temperature
      4 *
      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			0x00
     21#define AM2315_REG_HUM_LSB			0x01
     22#define AM2315_REG_TEMP_MSB			0x02
     23#define AM2315_REG_TEMP_LSB			0x03
     24
     25#define AM2315_FUNCTION_READ			0x03
     26#define AM2315_HUM_OFFSET			2
     27#define AM2315_TEMP_OFFSET			4
     28#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 value
    137	 * 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		for_each_set_bit(bit, indio_dev->active_scan_mask,
    178				 indio_dev->masklength) {
    179			data->scan.chans[i] = (bit ? sensor_data.temp_data :
    180					       sensor_data.hum_data);
    181			i++;
    182		}
    183	}
    184	mutex_unlock(&data->lock);
    185
    186	iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
    187					   pf->timestamp);
    188err:
    189	iio_trigger_notify_done(indio_dev->trig);
    190	return IRQ_HANDLED;
    191}
    192
    193static int am2315_read_raw(struct iio_dev *indio_dev,
    194			   struct iio_chan_spec const *chan,
    195			   int *val, int *val2, long mask)
    196{
    197	int ret;
    198	struct am2315_sensor_data sensor_data;
    199	struct am2315_data *data = iio_priv(indio_dev);
    200
    201	switch (mask) {
    202	case IIO_CHAN_INFO_RAW:
    203		ret = am2315_read_data(data, &sensor_data);
    204		if (ret < 0)
    205			return ret;
    206		*val = (chan->type == IIO_HUMIDITYRELATIVE) ?
    207				sensor_data.hum_data : sensor_data.temp_data;
    208		return IIO_VAL_INT;
    209	case IIO_CHAN_INFO_SCALE:
    210		*val = 100;
    211		return IIO_VAL_INT;
    212	}
    213
    214	return -EINVAL;
    215}
    216
    217static const struct iio_info am2315_info = {
    218	.read_raw		= am2315_read_raw,
    219};
    220
    221static int am2315_probe(struct i2c_client *client,
    222			const struct i2c_device_id *id)
    223{
    224	int ret;
    225	struct iio_dev *indio_dev;
    226	struct am2315_data *data;
    227
    228	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
    229	if (!indio_dev) {
    230		dev_err(&client->dev, "iio allocation failed!\n");
    231		return -ENOMEM;
    232	}
    233
    234	data = iio_priv(indio_dev);
    235	data->client = client;
    236	i2c_set_clientdata(client, indio_dev);
    237	mutex_init(&data->lock);
    238
    239	indio_dev->info = &am2315_info;
    240	indio_dev->name = AM2315_DRIVER_NAME;
    241	indio_dev->modes = INDIO_DIRECT_MODE;
    242	indio_dev->channels = am2315_channels;
    243	indio_dev->num_channels = ARRAY_SIZE(am2315_channels);
    244
    245	ret = devm_iio_triggered_buffer_setup(&client->dev,
    246					indio_dev, iio_pollfunc_store_time,
    247					 am2315_trigger_handler, NULL);
    248	if (ret < 0) {
    249		dev_err(&client->dev, "iio triggered buffer setup failed\n");
    250		return ret;
    251	}
    252
    253	return devm_iio_device_register(&client->dev, indio_dev);
    254}
    255
    256static const struct i2c_device_id am2315_i2c_id[] = {
    257	{"am2315", 0},
    258	{}
    259};
    260MODULE_DEVICE_TABLE(i2c, am2315_i2c_id);
    261
    262static struct i2c_driver am2315_driver = {
    263	.driver = {
    264		.name = "am2315",
    265	},
    266	.probe =            am2315_probe,
    267	.id_table =         am2315_i2c_id,
    268};
    269
    270module_i2c_driver(am2315_driver);
    271
    272MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
    273MODULE_DESCRIPTION("Aosong AM2315 relative humidity and temperature");
    274MODULE_LICENSE("GPL v2");