18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/** 38c2ecf20Sopenharmony_ci * Aosong AM2315 relative humidity and temperature 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2016, Intel Corporation. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * 7-bit I2C address: 0x5C. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/acpi.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h> 168c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 178c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 188c2ecf20Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 198c2ecf20Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define AM2315_REG_HUM_MSB 0x00 228c2ecf20Sopenharmony_ci#define AM2315_REG_HUM_LSB 0x01 238c2ecf20Sopenharmony_ci#define AM2315_REG_TEMP_MSB 0x02 248c2ecf20Sopenharmony_ci#define AM2315_REG_TEMP_LSB 0x03 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define AM2315_FUNCTION_READ 0x03 278c2ecf20Sopenharmony_ci#define AM2315_HUM_OFFSET 2 288c2ecf20Sopenharmony_ci#define AM2315_TEMP_OFFSET 4 298c2ecf20Sopenharmony_ci#define AM2315_ALL_CHANNEL_MASK GENMASK(1, 0) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define AM2315_DRIVER_NAME "am2315" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct am2315_data { 348c2ecf20Sopenharmony_ci struct i2c_client *client; 358c2ecf20Sopenharmony_ci struct mutex lock; 368c2ecf20Sopenharmony_ci /* Ensure timestamp is naturally aligned */ 378c2ecf20Sopenharmony_ci struct { 388c2ecf20Sopenharmony_ci s16 chans[2]; 398c2ecf20Sopenharmony_ci s64 timestamp __aligned(8); 408c2ecf20Sopenharmony_ci } scan; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct am2315_sensor_data { 448c2ecf20Sopenharmony_ci s16 hum_data; 458c2ecf20Sopenharmony_ci s16 temp_data; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic const struct iio_chan_spec am2315_channels[] = { 498c2ecf20Sopenharmony_ci { 508c2ecf20Sopenharmony_ci .type = IIO_HUMIDITYRELATIVE, 518c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 528c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 538c2ecf20Sopenharmony_ci .scan_index = 0, 548c2ecf20Sopenharmony_ci .scan_type = { 558c2ecf20Sopenharmony_ci .sign = 's', 568c2ecf20Sopenharmony_ci .realbits = 16, 578c2ecf20Sopenharmony_ci .storagebits = 16, 588c2ecf20Sopenharmony_ci .endianness = IIO_CPU, 598c2ecf20Sopenharmony_ci }, 608c2ecf20Sopenharmony_ci }, 618c2ecf20Sopenharmony_ci { 628c2ecf20Sopenharmony_ci .type = IIO_TEMP, 638c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 648c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 658c2ecf20Sopenharmony_ci .scan_index = 1, 668c2ecf20Sopenharmony_ci .scan_type = { 678c2ecf20Sopenharmony_ci .sign = 's', 688c2ecf20Sopenharmony_ci .realbits = 16, 698c2ecf20Sopenharmony_ci .storagebits = 16, 708c2ecf20Sopenharmony_ci .endianness = IIO_CPU, 718c2ecf20Sopenharmony_ci }, 728c2ecf20Sopenharmony_ci }, 738c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(2), 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* CRC calculation algorithm, as specified in the datasheet (page 13). */ 778c2ecf20Sopenharmony_cistatic u16 am2315_crc(u8 *data, u8 nr_bytes) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci int i; 808c2ecf20Sopenharmony_ci u16 crc = 0xffff; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci while (nr_bytes--) { 838c2ecf20Sopenharmony_ci crc ^= *data++; 848c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 858c2ecf20Sopenharmony_ci if (crc & 0x01) { 868c2ecf20Sopenharmony_ci crc >>= 1; 878c2ecf20Sopenharmony_ci crc ^= 0xA001; 888c2ecf20Sopenharmony_ci } else { 898c2ecf20Sopenharmony_ci crc >>= 1; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return crc; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* Simple function that sends a few bytes to the device to wake it up. */ 988c2ecf20Sopenharmony_cistatic void am2315_ping(struct i2c_client *client) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(client, AM2315_REG_HUM_MSB); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int am2315_read_data(struct am2315_data *data, 1048c2ecf20Sopenharmony_ci struct am2315_sensor_data *sensor_data) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci int ret; 1078c2ecf20Sopenharmony_ci /* tx_buf format: <function code> <start addr> <nr of regs to read> */ 1088c2ecf20Sopenharmony_ci u8 tx_buf[3] = { AM2315_FUNCTION_READ, AM2315_REG_HUM_MSB, 4 }; 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * rx_buf format: 1118c2ecf20Sopenharmony_ci * <function code> <number of registers read> 1128c2ecf20Sopenharmony_ci * <humidity MSB> <humidity LSB> <temp MSB> <temp LSB> 1138c2ecf20Sopenharmony_ci * <CRC LSB> <CRC MSB> 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci u8 rx_buf[8]; 1168c2ecf20Sopenharmony_ci u16 crc; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* First wake up the device. */ 1198c2ecf20Sopenharmony_ci am2315_ping(data->client); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 1228c2ecf20Sopenharmony_ci ret = i2c_master_send(data->client, tx_buf, sizeof(tx_buf)); 1238c2ecf20Sopenharmony_ci if (ret < 0) { 1248c2ecf20Sopenharmony_ci dev_err(&data->client->dev, "failed to send read request\n"); 1258c2ecf20Sopenharmony_ci goto exit_unlock; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci /* Wait 2-3 ms, then read back the data sent by the device. */ 1288c2ecf20Sopenharmony_ci usleep_range(2000, 3000); 1298c2ecf20Sopenharmony_ci /* Do a bulk data read, then pick out what we need. */ 1308c2ecf20Sopenharmony_ci ret = i2c_master_recv(data->client, rx_buf, sizeof(rx_buf)); 1318c2ecf20Sopenharmony_ci if (ret < 0) { 1328c2ecf20Sopenharmony_ci dev_err(&data->client->dev, "failed to read sensor data\n"); 1338c2ecf20Sopenharmony_ci goto exit_unlock; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 1368c2ecf20Sopenharmony_ci /* 1378c2ecf20Sopenharmony_ci * Do a CRC check on the data and compare it to the value 1388c2ecf20Sopenharmony_ci * calculated by the device. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci crc = am2315_crc(rx_buf, sizeof(rx_buf) - 2); 1418c2ecf20Sopenharmony_ci if ((crc & 0xff) != rx_buf[6] || (crc >> 8) != rx_buf[7]) { 1428c2ecf20Sopenharmony_ci dev_err(&data->client->dev, "failed to verify sensor data\n"); 1438c2ecf20Sopenharmony_ci return -EIO; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci sensor_data->hum_data = (rx_buf[AM2315_HUM_OFFSET] << 8) | 1478c2ecf20Sopenharmony_ci rx_buf[AM2315_HUM_OFFSET + 1]; 1488c2ecf20Sopenharmony_ci sensor_data->temp_data = (rx_buf[AM2315_TEMP_OFFSET] << 8) | 1498c2ecf20Sopenharmony_ci rx_buf[AM2315_TEMP_OFFSET + 1]; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciexit_unlock: 1548c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 1558c2ecf20Sopenharmony_ci return ret; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic irqreturn_t am2315_trigger_handler(int irq, void *p) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci int i; 1618c2ecf20Sopenharmony_ci int ret; 1628c2ecf20Sopenharmony_ci int bit; 1638c2ecf20Sopenharmony_ci struct iio_poll_func *pf = p; 1648c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = pf->indio_dev; 1658c2ecf20Sopenharmony_ci struct am2315_data *data = iio_priv(indio_dev); 1668c2ecf20Sopenharmony_ci struct am2315_sensor_data sensor_data; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci ret = am2315_read_data(data, &sensor_data); 1698c2ecf20Sopenharmony_ci if (ret < 0) 1708c2ecf20Sopenharmony_ci goto err; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 1738c2ecf20Sopenharmony_ci if (*(indio_dev->active_scan_mask) == AM2315_ALL_CHANNEL_MASK) { 1748c2ecf20Sopenharmony_ci data->scan.chans[0] = sensor_data.hum_data; 1758c2ecf20Sopenharmony_ci data->scan.chans[1] = sensor_data.temp_data; 1768c2ecf20Sopenharmony_ci } else { 1778c2ecf20Sopenharmony_ci i = 0; 1788c2ecf20Sopenharmony_ci for_each_set_bit(bit, indio_dev->active_scan_mask, 1798c2ecf20Sopenharmony_ci indio_dev->masklength) { 1808c2ecf20Sopenharmony_ci data->scan.chans[i] = (bit ? sensor_data.temp_data : 1818c2ecf20Sopenharmony_ci sensor_data.hum_data); 1828c2ecf20Sopenharmony_ci i++; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, 1888c2ecf20Sopenharmony_ci pf->timestamp); 1898c2ecf20Sopenharmony_cierr: 1908c2ecf20Sopenharmony_ci iio_trigger_notify_done(indio_dev->trig); 1918c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int am2315_read_raw(struct iio_dev *indio_dev, 1958c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1968c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci int ret; 1998c2ecf20Sopenharmony_ci struct am2315_sensor_data sensor_data; 2008c2ecf20Sopenharmony_ci struct am2315_data *data = iio_priv(indio_dev); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci switch (mask) { 2038c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 2048c2ecf20Sopenharmony_ci ret = am2315_read_data(data, &sensor_data); 2058c2ecf20Sopenharmony_ci if (ret < 0) 2068c2ecf20Sopenharmony_ci return ret; 2078c2ecf20Sopenharmony_ci *val = (chan->type == IIO_HUMIDITYRELATIVE) ? 2088c2ecf20Sopenharmony_ci sensor_data.hum_data : sensor_data.temp_data; 2098c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2108c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 2118c2ecf20Sopenharmony_ci *val = 100; 2128c2ecf20Sopenharmony_ci return IIO_VAL_INT; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return -EINVAL; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic const struct iio_info am2315_info = { 2198c2ecf20Sopenharmony_ci .read_raw = am2315_read_raw, 2208c2ecf20Sopenharmony_ci}; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int am2315_probe(struct i2c_client *client, 2238c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci int ret; 2268c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 2278c2ecf20Sopenharmony_ci struct am2315_data *data; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 2308c2ecf20Sopenharmony_ci if (!indio_dev) { 2318c2ecf20Sopenharmony_ci dev_err(&client->dev, "iio allocation failed!\n"); 2328c2ecf20Sopenharmony_ci return -ENOMEM; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 2368c2ecf20Sopenharmony_ci data->client = client; 2378c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 2388c2ecf20Sopenharmony_ci mutex_init(&data->lock); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci indio_dev->info = &am2315_info; 2418c2ecf20Sopenharmony_ci indio_dev->name = AM2315_DRIVER_NAME; 2428c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 2438c2ecf20Sopenharmony_ci indio_dev->channels = am2315_channels; 2448c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(am2315_channels); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci ret = devm_iio_triggered_buffer_setup(&client->dev, 2478c2ecf20Sopenharmony_ci indio_dev, iio_pollfunc_store_time, 2488c2ecf20Sopenharmony_ci am2315_trigger_handler, NULL); 2498c2ecf20Sopenharmony_ci if (ret < 0) { 2508c2ecf20Sopenharmony_ci dev_err(&client->dev, "iio triggered buffer setup failed\n"); 2518c2ecf20Sopenharmony_ci return ret; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return devm_iio_device_register(&client->dev, indio_dev); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic const struct i2c_device_id am2315_i2c_id[] = { 2588c2ecf20Sopenharmony_ci {"am2315", 0}, 2598c2ecf20Sopenharmony_ci {} 2608c2ecf20Sopenharmony_ci}; 2618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, am2315_i2c_id); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic const struct acpi_device_id am2315_acpi_id[] = { 2648c2ecf20Sopenharmony_ci {"AOS2315", 0}, 2658c2ecf20Sopenharmony_ci {} 2668c2ecf20Sopenharmony_ci}; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, am2315_acpi_id); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic struct i2c_driver am2315_driver = { 2718c2ecf20Sopenharmony_ci .driver = { 2728c2ecf20Sopenharmony_ci .name = "am2315", 2738c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(am2315_acpi_id), 2748c2ecf20Sopenharmony_ci }, 2758c2ecf20Sopenharmony_ci .probe = am2315_probe, 2768c2ecf20Sopenharmony_ci .id_table = am2315_i2c_id, 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cimodule_i2c_driver(am2315_driver); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>"); 2828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Aosong AM2315 relative humidity and temperature"); 2838c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 284