1 /*
2  * Copyright (c) 2023 Balthazar Deliers
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT aosong_ags10
8 
9 #include <zephyr/drivers/sensor.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/logging/log.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/sys/crc.h>
14 
15 #include "ags10.h"
16 
17 LOG_MODULE_REGISTER(AGS10, CONFIG_SENSOR_LOG_LEVEL);
18 
19 #define AGS10_MAX_PAYLOAD_SIZE 5U	/* Payload will be max 4 bytes + CRC (datasheet 3.1) */
20 
ags10_read(const struct device * dev,uint8_t cmd,uint8_t * data,uint8_t rx_bytes)21 static int ags10_read(const struct device *dev, uint8_t cmd, uint8_t *data, uint8_t rx_bytes)
22 {
23 	if (rx_bytes > AGS10_MAX_PAYLOAD_SIZE) {
24 		return -EINVAL;
25 	}
26 
27 	const struct ags10_config *conf = dev->config;
28 
29 	uint8_t recv_buf[AGS10_MAX_PAYLOAD_SIZE] = {0};
30 	int ret = i2c_write_read_dt(&conf->bus, &cmd, sizeof(cmd), &recv_buf, rx_bytes);
31 
32 	if (ret < 0) {
33 		return ret;
34 	}
35 
36 	memcpy(data, recv_buf, rx_bytes);
37 
38 	return 0;
39 }
40 
ags10_sample_fetch(const struct device * dev,enum sensor_channel chan)41 static int ags10_sample_fetch(const struct device *dev, enum sensor_channel chan)
42 {
43 	if (chan != SENSOR_CHAN_VOC && chan != SENSOR_CHAN_ALL) {
44 		return -ENOTSUP;
45 	}
46 
47 	struct ags10_data *data = dev->data;
48 	int ret = -ENOTSUP;
49 	uint8_t recv_buf[5] = {0};
50 
51 	ret = ags10_read(dev, AGS10_CMD_DATA_ACQUISITION, recv_buf, 5);
52 
53 	if (ret == 0) {
54 		/* If CRC is valid and data is valid too */
55 		if (crc8(&recv_buf[0], 4, 0x31, 0xFF, false) == recv_buf[4] &&
56 		    ((recv_buf[0] & AGS10_MSK_STATUS) == AGS10_REG_STATUS_NRDY_READY)) {
57 			data->status = recv_buf[0] & AGS10_MSK_STATUS;
58 			data->tvoc_ppb = sys_get_be24(&recv_buf[1]);
59 			return 0;
60 		}
61 
62 		LOG_WRN("Bad CRC or data not ready");
63 		ret = -EIO;
64 	}
65 
66 	return ret;
67 }
68 
ags10_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)69 static int ags10_channel_get(const struct device *dev, enum sensor_channel chan,
70 			     struct sensor_value *val)
71 {
72 	struct ags10_data *data = dev->data;
73 
74 	if (chan == SENSOR_CHAN_VOC) {
75 		val->val1 = data->tvoc_ppb;
76 	} else {
77 		return -ENOTSUP;
78 	}
79 
80 	val->val2 = 0;
81 
82 	return 0;
83 }
84 
ags10_init(const struct device * dev)85 static int ags10_init(const struct device *dev)
86 {
87 	const struct ags10_config *conf = dev->config;
88 	struct ags10_data *data = dev->data;
89 	int ret;
90 
91 	if (!i2c_is_ready_dt(&conf->bus)) {
92 		LOG_ERR("Device not ready");
93 		return -ENODEV;
94 	}
95 
96 	/* Set initial data values */
97 	data->tvoc_ppb = 0;
98 	data->status = 0xFF;
99 	data->version = 0;
100 
101 	/* Read firmware version and check CRC */
102 	uint8_t recv_buf[5] = {0};
103 
104 	ret = ags10_read(dev, AGS10_CMD_READ_VERSION, recv_buf, 5);
105 
106 	/* Bytes 0 to 2 are reserved, byte 3 is version, byte 4 is CRC */
107 	if (ret == 0 && crc8(&recv_buf[0], 4, 0x31, 0xFF, false) == recv_buf[4]) {
108 		data->version = recv_buf[3];
109 		LOG_DBG("Sensor detected");
110 	} else if (ret != 0) {
111 		LOG_ERR("No reply from sensor");
112 		ret = -ENODEV;
113 	} else {
114 		LOG_WRN("Bad CRC");
115 		ret = -EIO;
116 	}
117 
118 	return ret;
119 }
120 
121 static DEVICE_API(sensor, ags10_api) = {
122 	.sample_fetch = ags10_sample_fetch,
123 	.channel_get = ags10_channel_get,
124 };
125 
126 #define AGS10_INIT(n)                                                                              \
127 	static struct ags10_data ags10_data_##n;                                                   \
128                                                                                                    \
129 	static const struct ags10_config ags10_config_##n = {                                      \
130 		.bus = I2C_DT_SPEC_INST_GET(n),                                                    \
131 	};                                                                                         \
132                                                                                                    \
133 	SENSOR_DEVICE_DT_INST_DEFINE(n, ags10_init, NULL, &ags10_data_##n, &ags10_config_##n,      \
134 				     POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &ags10_api);
135 
136 DT_INST_FOREACH_STATUS_OKAY(AGS10_INIT)
137