summaryrefslogtreecommitdiff
path: root/drivers/hwmon/aht10.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon/aht10.c')
-rw-r--r--drivers/hwmon/aht10.c192
1 files changed, 116 insertions, 76 deletions
diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c
index 2d9770cb4401..007befdba977 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)
*/
@@ -33,6 +37,8 @@
#define AHT10_CMD_MEAS 0b10101100
#define AHT10_CMD_RST 0b10111010
+#define DHT20_CMD_INIT 0x71
+
/*
* Flags in the answer byte/command
*/
@@ -44,11 +50,19 @@
#define AHT10_MAX_POLL_INTERVAL_LEN 30
+enum aht10_variant { aht10, aht20, dht20};
+
+static const struct i2c_device_id aht10_id[] = {
+ { "aht10", aht10 },
+ { "aht20", aht20 },
+ { "dht20", dht20 },
+ { },
+};
+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
- * @lock: a mutex that is used to prevent parallel access to the
- * i2c client
+ * struct aht10_data - All the data required to operate an AHT10/AHT20 chip
+ * @client: the i2c client associated with the AHT10/AHT20
* @min_poll_interval: the minimum poll interval
* While the poll rate limit is not 100% necessary,
* the datasheet recommends that a measurement
@@ -56,42 +70,42 @@
* 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
+ * @init_cmd: Initialization command
*/
struct aht10_data {
struct i2c_client *client;
- /*
- * Prevent simultaneous access to the i2c
- * client and previous_poll_time
- */
- struct mutex lock;
ktime_t min_poll_interval;
ktime_t previous_poll_time;
int temperature;
int humidity;
+ bool crc8;
+ unsigned int meas_size;
+ u8 init_cmd;
};
-/**
- * aht10_init() - Initialize an AHT10 chip
- * @client: the i2c client associated with the AHT10
- * @data: the data associated with this AHT10 chip
- * Return: 0 if succesfull, 1 if not
+/*
+ * 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)
{
- const u8 cmd_init[] = {AHT10_CMD_INIT, AHT10_CAL_ENABLED | AHT10_MODE_CYC,
+ const u8 cmd_init[] = {data->init_cmd, AHT10_CAL_ENABLED | AHT10_MODE_CYC,
0x00};
int res;
u8 status;
struct i2c_client *client = data->client;
- res = i2c_master_send(client, cmd_init, 3);
+ res = i2c_master_send(client, cmd_init, sizeof(cmd_init));
if (res < 0)
return res;
@@ -108,7 +122,7 @@ static int aht10_init(struct aht10_data *data)
return 0;
}
-/**
+/*
* aht10_polltime_expired() - check if the minimum poll interval has
* expired
* @data: the data containing the time to compare
@@ -122,59 +136,74 @@ static int aht10_polltime_expired(struct aht10_data *data)
return ktime_after(difference, data->min_poll_interval);
}
-/**
- * aht10_read_values() - read and parse the raw data from the AHT10
- * @aht10_data: the struct aht10_data to use for the lock
- * Return: 0 if succesfull, 1 if not
+DECLARE_CRC8_TABLE(crc8_table);
+
+/*
+ * 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
*/
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);
- if (aht10_polltime_expired(data)) {
- res = i2c_master_send(client, cmd_meas, sizeof(cmd_meas));
- if (res < 0) {
- mutex_unlock(&data->lock);
- return res;
- }
-
- 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) {
- mutex_unlock(&data->lock);
- if (res >= 0)
- return -ENODATA;
- else
- return res;
- }
-
- hum = ((u32)raw_data[1] << 12u) |
- ((u32)raw_data[2] << 4u) |
- ((raw_data[3] & 0xF0u) >> 4u);
-
- temp = ((u32)(raw_data[3] & 0x0Fu) << 16u) |
- ((u32)raw_data[4] << 8u) |
- raw_data[5];
-
- temp = ((temp * 625) >> 15u) * 10;
- hum = ((hum * 625) >> 16u) * 10;
-
- data->temperature = (int)temp - 50000;
- data->humidity = hum;
- data->previous_poll_time = ktime_get_boottime();
+ if (!aht10_polltime_expired(data))
+ return 0;
+
+ res = i2c_master_send(client, cmd_meas, sizeof(cmd_meas));
+ if (res < 0)
+ return res;
+
+ usleep_range(AHT10_MEAS_DELAY, AHT10_MEAS_DELAY + AHT10_DELAY_EXTRA);
+
+ res = i2c_master_recv(client, raw_data, data->meas_size);
+ if (res != data->meas_size) {
+ if (res >= 0)
+ return -ENODATA;
+ return res;
}
- mutex_unlock(&data->lock);
+
+ if (data->crc8 && crc8_check(raw_data, data->meas_size))
+ return -EIO;
+
+ hum = ((u32)raw_data[1] << 12u) |
+ ((u32)raw_data[2] << 4u) |
+ ((raw_data[3] & 0xF0u) >> 4u);
+
+ temp = ((u32)(raw_data[3] & 0x0Fu) << 16u) |
+ ((u32)raw_data[4] << 8u) |
+ raw_data[5];
+
+ temp = ((temp * 625) >> 15u) * 10;
+ hum = ((hum * 625) >> 16u) * 10;
+
+ data->temperature = (int)temp - 50000;
+ data->humidity = hum;
+ data->previous_poll_time = ktime_get_boottime();
+
return 0;
}
-/**
+/*
* aht10_interval_write() - store the given minimum poll interval.
* Return: 0 on success, -EINVAL if a value lower than the
* AHT10_MIN_POLL_INTERVAL is given
@@ -186,7 +215,7 @@ static ssize_t aht10_interval_write(struct aht10_data *data,
return 0;
}
-/**
+/*
* aht10_interval_read() - read the minimum poll interval
* in milliseconds
*/
@@ -197,7 +226,7 @@ static ssize_t aht10_interval_read(struct aht10_data *data,
return 0;
}
-/**
+/*
* aht10_temperature1_read() - read the temperature in millidegrees
*/
static int aht10_temperature1_read(struct aht10_data *data, long *val)
@@ -212,7 +241,7 @@ static int aht10_temperature1_read(struct aht10_data *data, long *val)
return 0;
}
-/**
+/*
* aht10_humidity1_read() - read the relative humidity in millipercent
*/
static int aht10_humidity1_read(struct aht10_data *data, long *val)
@@ -271,7 +300,7 @@ static int aht10_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
}
}
-static const struct hwmon_channel_info *aht10_info[] = {
+static const struct hwmon_channel_info * const aht10_info[] = {
HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL),
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT),
@@ -289,9 +318,9 @@ static const struct hwmon_chip_info aht10_chip_info = {
.info = aht10_info,
};
-static int aht10_probe(struct i2c_client *client,
- const struct i2c_device_id *aht10_id)
+static int aht10_probe(struct i2c_client *client)
{
+ enum aht10_variant variant = (uintptr_t)i2c_get_match_data(client);
struct device *device = &client->dev;
struct device *hwmon_dev;
struct aht10_data *data;
@@ -307,7 +336,24 @@ static int aht10_probe(struct i2c_client *client,
data->min_poll_interval = ms_to_ktime(AHT10_DEFAULT_MIN_POLL_INTERVAL);
data->client = client;
- mutex_init(&data->lock);
+ switch (variant) {
+ case aht20:
+ data->meas_size = AHT20_MEAS_SIZE;
+ data->crc8 = true;
+ crc8_populate_msb(crc8_table, AHT20_CRC8_POLY);
+ data->init_cmd = AHT10_CMD_INIT;
+ break;
+ case dht20:
+ data->meas_size = AHT20_MEAS_SIZE;
+ data->crc8 = true;
+ crc8_populate_msb(crc8_table, AHT20_CRC8_POLY);
+ data->init_cmd = DHT20_CMD_INIT;
+ break;
+ default:
+ data->meas_size = AHT10_MEAS_SIZE;
+ data->init_cmd = AHT10_CMD_INIT;
+ break;
+ }
res = aht10_init(data);
if (res < 0)
@@ -326,12 +372,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 +383,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");