diff options
Diffstat (limited to 'drivers/hwmon/aht10.c')
-rw-r--r-- | drivers/hwmon/aht10.c | 84 |
1 files changed, 65 insertions, 19 deletions
diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c index ec7459575c58..f136bf3ff40a 100644 --- a/drivers/hwmon/aht10.c +++ b/drivers/hwmon/aht10.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * aht10.c - Linux hwmon driver for AHT10 Temperature and Humidity sensor + * aht10.c - Linux hwmon driver for AHT10/AHT20 Temperature and Humidity sensors * Copyright (C) 2020 Johannes Cornelis Draaijer */ @@ -10,9 +10,13 @@ #include <linux/i2c.h> #include <linux/ktime.h> #include <linux/module.h> +#include <linux/crc8.h> #define AHT10_MEAS_SIZE 6 +#define AHT20_MEAS_SIZE 7 +#define AHT20_CRC8_POLY 0x31 + /* * Poll intervals (in milliseconds) */ @@ -44,9 +48,18 @@ #define AHT10_MAX_POLL_INTERVAL_LEN 30 +enum aht10_variant { aht10, aht20 }; + +static const struct i2c_device_id aht10_id[] = { + { "aht10", aht10 }, + { "aht20", aht20 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, aht10_id); + /** - * struct aht10_data - All the data required to operate an AHT10 chip - * @client: the i2c client associated with the AHT10 + * struct aht10_data - All the data required to operate an AHT10/AHT20 chip + * @client: the i2c client associated with the AHT10/AHT20 * @lock: a mutex that is used to prevent parallel access to the * i2c client * @min_poll_interval: the minimum poll interval @@ -56,12 +69,14 @@ * the chip from warming up due to the heat it generates. * If it's unwanted, it can be ignored setting it to * it to 0. Default value is 2000 ms - * @previous_poll_time: the previous time that the AHT10 + * @previous_poll_time: the previous time that the AHT10/AHT20 * was polled * @temperature: the latest temperature value received from - * the AHT10 + * the AHT10/AHT20 * @humidity: the latest humidity value received from the - * AHT10 + * AHT10/AHT20 + * @crc8: crc8 support flag + * @meas_size: measurements data size */ struct aht10_data { @@ -75,11 +90,13 @@ struct aht10_data { ktime_t previous_poll_time; int temperature; int humidity; + bool crc8; + unsigned int meas_size; }; /** - * aht10_init() - Initialize an AHT10 chip - * @data: the data associated with this AHT10 chip + * aht10_init() - Initialize an AHT10/AHT20 chip + * @data: the data associated with this AHT10/AHT20 chip * Return: 0 if successful, 1 if not */ static int aht10_init(struct aht10_data *data) @@ -121,8 +138,25 @@ static int aht10_polltime_expired(struct aht10_data *data) return ktime_after(difference, data->min_poll_interval); } +DECLARE_CRC8_TABLE(crc8_table); + /** - * aht10_read_values() - read and parse the raw data from the AHT10 + * crc8_check() - check crc of the sensor's measurements + * @raw_data: data frame received from sensor(including crc as the last byte) + * @count: size of the data frame + * Return: 0 if successful, 1 if not + */ +static int crc8_check(u8 *raw_data, int count) +{ + /* + * crc calculated on the whole frame(including crc byte) should yield + * zero in case of correctly received bytes + */ + return crc8(crc8_table, raw_data, count, CRC8_INIT_VALUE); +} + +/** + * aht10_read_values() - read and parse the raw data from the AHT10/AHT20 * @data: the struct aht10_data to use for the lock * Return: 0 if successful, 1 if not */ @@ -131,7 +165,7 @@ static int aht10_read_values(struct aht10_data *data) const u8 cmd_meas[] = {AHT10_CMD_MEAS, 0x33, 0x00}; u32 temp, hum; int res; - u8 raw_data[AHT10_MEAS_SIZE]; + u8 raw_data[AHT20_MEAS_SIZE]; struct i2c_client *client = data->client; mutex_lock(&data->lock); @@ -148,14 +182,19 @@ static int aht10_read_values(struct aht10_data *data) usleep_range(AHT10_MEAS_DELAY, AHT10_MEAS_DELAY + AHT10_DELAY_EXTRA); - res = i2c_master_recv(client, raw_data, AHT10_MEAS_SIZE); - if (res != AHT10_MEAS_SIZE) { + res = i2c_master_recv(client, raw_data, data->meas_size); + if (res != data->meas_size) { mutex_unlock(&data->lock); if (res >= 0) return -ENODATA; return res; } + if (data->crc8 && crc8_check(raw_data, data->meas_size)) { + mutex_unlock(&data->lock); + return -EIO; + } + hum = ((u32)raw_data[1] << 12u) | ((u32)raw_data[2] << 4u) | ((raw_data[3] & 0xF0u) >> 4u); @@ -292,6 +331,8 @@ static const struct hwmon_chip_info aht10_chip_info = { static int aht10_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_match_id(aht10_id, client); + enum aht10_variant variant = id->driver_data; struct device *device = &client->dev; struct device *hwmon_dev; struct aht10_data *data; @@ -307,6 +348,17 @@ static int aht10_probe(struct i2c_client *client) data->min_poll_interval = ms_to_ktime(AHT10_DEFAULT_MIN_POLL_INTERVAL); data->client = client; + switch (variant) { + case aht20: + data->meas_size = AHT20_MEAS_SIZE; + data->crc8 = true; + crc8_populate_msb(crc8_table, AHT20_CRC8_POLY); + break; + default: + data->meas_size = AHT10_MEAS_SIZE; + break; + } + mutex_init(&data->lock); res = aht10_init(data); @@ -326,12 +378,6 @@ static int aht10_probe(struct i2c_client *client) return PTR_ERR_OR_ZERO(hwmon_dev); } -static const struct i2c_device_id aht10_id[] = { - { "aht10", 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, aht10_id); - static struct i2c_driver aht10_driver = { .driver = { .name = "aht10", @@ -343,6 +389,6 @@ static struct i2c_driver aht10_driver = { module_i2c_driver(aht10_driver); MODULE_AUTHOR("Johannes Cornelis Draaijer <jcdra1@gmail.com>"); -MODULE_DESCRIPTION("AHT10 Temperature and Humidity sensor driver"); +MODULE_DESCRIPTION("AHT10/AHT20 Temperature and Humidity sensor driver"); MODULE_VERSION("1.0"); MODULE_LICENSE("GPL v2"); |