1 /*
2  * Copyright (c) 2024 Renesas Electronics Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT renesas_hs400x
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/sys/__assert.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/sys/byteorder.h>
16 #include <zephyr/sys/crc.h>
17 
18 #define CRC_POLYNOMIAL 0x1D
19 #define CRC_INITIAL 0xFF
20 
21 LOG_MODULE_REGISTER(HS400X, CONFIG_SENSOR_LOG_LEVEL);
22 
23 struct hs400x_config {
24 	struct i2c_dt_spec bus;
25 };
26 
27 struct hs400x_data {
28 	int16_t t_sample;
29 	uint16_t rh_sample;
30 };
31 
hs400x_read_sample(const struct device * dev,uint16_t * t_sample,uint16_t * rh_sample)32 static int hs400x_read_sample(const struct device *dev, uint16_t *t_sample, uint16_t *rh_sample)
33 {
34 	const struct hs400x_config *cfg = dev->config;
35 	uint8_t rx_buf[5];
36 	int rc;
37 
38 	rc = i2c_read_dt(&cfg->bus, rx_buf, sizeof(rx_buf));
39 	if (rc < 0) {
40 		LOG_ERR("Failed to read data from device.");
41 		return rc;
42 	}
43 
44 	*rh_sample = sys_get_be16(rx_buf);
45 	*t_sample = sys_get_be16(&rx_buf[2]);
46 
47 	/*
48 	 * The sensor sends a checkum after each measurement. See datasheet "CRC Checksum
49 	 * Calculation" section for more details on checking the checksum.
50 	 */
51 #if CONFIG_HS400X_CRC
52 	uint8_t crc = crc8(rx_buf, 4, CRC_POLYNOMIAL, CRC_INITIAL, 0);
53 
54 	if (crc != rx_buf[4]) {
55 		LOG_ERR("CRC check failed: computed=%u,expected=%u", crc, rx_buf[4]);
56 		return -EIO;
57 	}
58 #endif
59 
60 	return 0;
61 }
62 
hs400x_sample_fetch(const struct device * dev,enum sensor_channel chan)63 static int hs400x_sample_fetch(const struct device *dev, enum sensor_channel chan)
64 {
65 	struct hs400x_data *data = dev->data;
66 	const struct hs400x_config *cfg = dev->config;
67 	int rc;
68 	uint8_t no_hold_measurement = 0xF5;
69 
70 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP &&
71 	    chan != SENSOR_CHAN_HUMIDITY) {
72 		return -ENOTSUP;
73 	}
74 
75 	rc = i2c_write_dt(&cfg->bus, (const uint8_t *)&no_hold_measurement, 1);
76 	if (rc < 0) {
77 		LOG_ERR("Failed to send measurement.");
78 		return rc;
79 	}
80 
81 	/*
82 	 * According to datasheet maximum time to make temperature and humidity
83 	 * measurements is 33ms, add a little safety margin...
84 	 */
85 	k_msleep(50);
86 
87 	rc = hs400x_read_sample(dev, &data->t_sample, &data->rh_sample);
88 	if (rc < 0) {
89 		LOG_ERR("Failed to fetch data.");
90 		return rc;
91 	}
92 
93 	return 0;
94 }
95 
hs400x_temp_convert(struct sensor_value * val,int16_t raw)96 static void hs400x_temp_convert(struct sensor_value *val, int16_t raw)
97 {
98 	int32_t micro_c;
99 
100 	/*
101 	 * Convert to micro Celsius. See datasheet "Calculating Humidity and
102 	 * Temperature Output" section for more details on processing sample data.
103 	 */
104 	micro_c = (((int64_t)raw * 165000000) / 16383) - 40000000;
105 
106 	val->val1 = micro_c / 1000000;
107 	val->val2 = micro_c % 1000000;
108 }
109 
hs400x_rh_convert(struct sensor_value * val,uint16_t raw)110 static void hs400x_rh_convert(struct sensor_value *val, uint16_t raw)
111 {
112 	int32_t micro_rh;
113 
114 	/*
115 	 * Convert to micro %RH. See datasheet "Calculating Humidity and
116 	 * Temperature Output" section for more details on processing sample data.
117 	 */
118 	micro_rh = ((uint64_t)raw * 100000000) / 16383;
119 
120 	val->val1 = micro_rh / 1000000;
121 	val->val2 = micro_rh % 1000000;
122 }
123 
hs400x_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)124 static int hs400x_channel_get(const struct device *dev, enum sensor_channel chan,
125 			      struct sensor_value *val)
126 {
127 	const struct hs400x_data *data = dev->data;
128 
129 	if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
130 		hs400x_temp_convert(val, data->t_sample);
131 	} else if (chan == SENSOR_CHAN_HUMIDITY) {
132 		hs400x_rh_convert(val, data->rh_sample);
133 	} else {
134 		return -ENOTSUP;
135 	}
136 
137 	return 0;
138 }
139 
hs400x_init(const struct device * dev)140 static int hs400x_init(const struct device *dev)
141 {
142 	const struct hs400x_config *cfg = dev->config;
143 
144 	if (!i2c_is_ready_dt(&cfg->bus)) {
145 		LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name);
146 		return -ENODEV;
147 	}
148 
149 	return 0;
150 }
151 
152 static DEVICE_API(sensor, hs400x_driver_api) = {
153 	.sample_fetch = hs400x_sample_fetch,
154 	.channel_get = hs400x_channel_get,
155 };
156 
157 #define DEFINE_HS400X(n)                                                                           \
158 	static struct hs400x_data hs400x_data_##n;                                                 \
159                                                                                                    \
160 	static const struct hs400x_config hs400x_config_##n = {.bus = I2C_DT_SPEC_INST_GET(n)};    \
161                                                                                                    \
162 	SENSOR_DEVICE_DT_INST_DEFINE(n, hs400x_init, NULL, &hs400x_data_##n, &hs400x_config_##n,   \
163 				     POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,                     \
164 				     &hs400x_driver_api);
165 
166 DT_INST_FOREACH_STATUS_OKAY(DEFINE_HS400X)
167