1 /*
2 * Copyright (c) 2022 Mizuki Agawa <agawa.mizuki@fujitsu.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT invensense_icp10125
8
9 #include <zephyr/drivers/i2c.h>
10 #include <zephyr/drivers/sensor.h>
11 #include <zephyr/logging/log.h>
12 #include <zephyr/sys/byteorder.h>
13
14 #ifdef CONFIG_ICP10125_CHECK_CRC
15 #include <zephyr/sys/crc.h>
16 #endif /* CONFIG_ICP10125_CHECK_CRC */
17
18 LOG_MODULE_REGISTER(ICP10125, CONFIG_SENSOR_LOG_LEVEL);
19
20 #define CRC_POLY 0x31
21 #define SENSOR_DATA_SIZE 2
22
23 #define AMBIENT_TEMP_DATA_NUM 1
24 #define PRESS_DATA_NUM 2
25 #define PRESS_AND_AMBIENT_TEMP_DATA_NUM (AMBIENT_TEMP_DATA_NUM + PRESS_DATA_NUM)
26
27 enum {
28 LOW_POWER,
29 NORMAL,
30 LOW_NOISE,
31 ULTRA_LOW_NOISE,
32 NUM_MEASURE_MODE
33 };
34
35 struct icp10125_data {
36 uint16_t raw_ambient_temp;
37 uint32_t raw_press;
38 float sensor_constants[4];
39 };
40
41 struct icp10125_dev_config {
42 struct i2c_dt_spec i2c;
43 uint8_t ambient_temp_mode;
44 uint8_t press_mode;
45 };
46
47 struct icp10125_cmd {
48 uint8_t data[2];
49 };
50
51 struct icp10125_sensor_data {
52 uint8_t data[2];
53 uint8_t crc;
54 };
55
56 struct icp10125_otp_read_setup {
57 struct icp10125_cmd cmd;
58 uint8_t data[3];
59 } __packed __aligned(1);
60
61 /* ambient temperature measurement command for each mode.
62 * (Section 5.2 MEASUREMENT COMMANDS in the Datasheet)
63 */
64 static const struct icp10125_cmd ambient_temp_measurement_cmds[] = {
65 {{0x60, 0x9C}}, {{0x68, 0x25}}, {{0x70, 0xDF}}, {{0x78, 0x66}}
66 };
67
68 /* pressure measurement command for each mode.
69 * (Section 5.2 MEASUREMENT COMMANDS in the Datasheet)
70 */
71 static const struct icp10125_cmd press_measurement_cmds[] = {
72 {{0x40, 0x1A}}, {{0x48, 0xA3}}, {{0x50, 0x59}}, {{0x59, 0xE0}}
73 };
74
75 /* Request preparation for OTP data read. It should issue before data read request.
76 * (Section 5.2 MEASUREMENT COMMANDS in the Datasheet)
77 */
78 static const struct icp10125_otp_read_setup otp_read_setup = {
79 .cmd = {{0xC5, 0x95}},
80 .data = {0x00, 0x66, 0x9C}
81 };
82
83 /* OTP data read request.
84 * After issue this command 2byte x 4 sensor constant value can readable.
85 */
86 static const struct icp10125_cmd otp_read_request_cmd = {{0xC7, 0xF7}};
87
88 /* The max conversion time for each modes.
89 * (Section 2.2 OPERATION MODES in the Datasheet)
90 */
91 static const uint32_t conv_time_max[] = {1800, 6300, 23800, 94500};
92
93 /* The typical conversion time for each modes.
94 * (Section 2.2 OPERATION MODES in the Datasheet)
95 */
96 static const uint32_t conv_time_typ[] = {1600, 5600, 20800, 83200};
97
98 /* The Datasheet has no mention of the constants and formulas.
99 * Instead, it shows only how to use it in the sample code.
100 * Since there is no detailed description in the ICP10125 product manual,
101 * the calculation of the pressure implements is the same as shown in
102 * the 5.11 SAMPLE CODE: EXAMPLE C SYNTAX
103 */
104
icp10125_calculate_conversion_constants(const float * p_LUT,float * A,float * B,float * C)105 static void icp10125_calculate_conversion_constants(const float *p_LUT, float *A, float *B,
106 float *C)
107 {
108 const float p_Pa[] = {45000.0, 80000.0, 105000.0};
109
110 *C = (p_LUT[0] * p_LUT[1] * (p_Pa[0] - p_Pa[1]) +
111 p_LUT[1] * p_LUT[2] * (p_Pa[1] - p_Pa[2]) +
112 p_LUT[2] * p_LUT[0] * (p_Pa[2] - p_Pa[0])) /
113 (p_LUT[2] * (p_Pa[0] - p_Pa[1]) + p_LUT[0] * (p_Pa[1] - p_Pa[2]) +
114 p_LUT[1] * (p_Pa[2] - p_Pa[0]));
115 *A = (p_Pa[0] * p_LUT[0] - p_Pa[1] * p_LUT[1] - (p_Pa[1] - p_Pa[0]) * (*C)) /
116 (p_LUT[0] - p_LUT[1]);
117 *B = (p_Pa[0] - (*A)) * (p_LUT[0] + (*C));
118 }
119
icp10125_calc_calibrated_ambient_temp(const struct icp10125_data * data)120 static float icp10125_calc_calibrated_ambient_temp(const struct icp10125_data *data)
121 {
122 return -45.f + 175.f / 65536.f * data->raw_ambient_temp;
123 }
124
icp10125_calc_calibrated_press(const struct icp10125_data * data)125 static float icp10125_calc_calibrated_press(const struct icp10125_data *data)
126 {
127 const float quadr_factor = 1 / 16777216.0;
128 const float offst_factor = 2048.0;
129 const float LUT_lower = 3.5 * (1 << 20);
130 const float LUT_upper = 11.5 * (1 << 20);
131 float t;
132 float in[3];
133 float A, B, C;
134
135 t = data->raw_ambient_temp - 32768.f;
136 in[0] = LUT_lower + (data->sensor_constants[0] * t * t) * quadr_factor;
137 in[1] = offst_factor * data->sensor_constants[3] +
138 (data->sensor_constants[1] * t * t) * quadr_factor;
139 in[2] = LUT_upper + (data->sensor_constants[2] * t * t) * quadr_factor;
140 icp10125_calculate_conversion_constants(in, &A, &B, &C);
141
142 return A + B / (C + data->raw_press);
143 }
144
145 /* End of porting the 5.11 SAMPLE CODE: EXAMPLE C SYNTAX */
146
icp10125_read_otp(const struct device * dev)147 static int icp10125_read_otp(const struct device *dev)
148 {
149 struct icp10125_data *data = dev->data;
150 struct icp10125_sensor_data sensor_data;
151
152 const struct icp10125_dev_config *cfg = dev->config;
153 int rc = 0;
154
155 rc = i2c_write_dt(&cfg->i2c, (uint8_t *)&otp_read_setup, sizeof(otp_read_setup));
156 if (rc < 0) {
157 LOG_ERR("Failed to write otp_read_setup.\n");
158 return rc;
159 }
160
161 for (size_t i = 0; i < ARRAY_SIZE(data->sensor_constants); i++) {
162 rc = i2c_write_dt(&cfg->i2c, (uint8_t *)&otp_read_request_cmd,
163 sizeof(otp_read_request_cmd));
164 if (rc < 0) {
165 LOG_ERR("Failed to write otp_read_request.\n");
166 return rc;
167 }
168
169 rc = i2c_read_dt(&cfg->i2c, (uint8_t *)&sensor_data, sizeof(sensor_data));
170 if (rc < 0) {
171 LOG_ERR("Failed to read otp_read_request.\n");
172 return rc;
173 }
174
175 data->sensor_constants[i] = sys_get_be16(sensor_data.data);
176 }
177
178 return 0;
179 }
180
181 #ifdef CONFIG_ICP10125_CHECK_CRC
icp10125_check_crc(const uint8_t * data,const size_t len)182 static int icp10125_check_crc(const uint8_t *data, const size_t len)
183 {
184 /* Details of CRC are described in Chapter 5 Section 8 of the product
185 * specifications.
186 */
187 return crc8(data, len, CRC_POLY, 0xFF, false);
188 }
189 #endif
190
icp10125_measure(const struct i2c_dt_spec * i2c,const struct icp10125_cmd * cmds,const uint8_t mode,struct icp10125_sensor_data * sensor_data,const size_t data_num)191 static int icp10125_measure(const struct i2c_dt_spec *i2c, const struct icp10125_cmd *cmds,
192 const uint8_t mode, struct icp10125_sensor_data *sensor_data,
193 const size_t data_num)
194 {
195 int rc = 0;
196
197 rc = i2c_write_dt(i2c, (uint8_t *)&cmds[mode], sizeof(cmds[mode]));
198 if (rc < 0) {
199 LOG_ERR("Failed to start measurement.\n");
200 return rc;
201 }
202
203 /* Wait for the sensor to become readable.
204 * First wait for the typical time and then read.
205 * If that fails, wait until the time to surely became readable.
206 */
207 k_sleep(K_USEC(conv_time_typ[mode]));
208 if (i2c_read_dt(i2c, (uint8_t *)sensor_data, sizeof(sensor_data[0]) * data_num) < 0) {
209 k_sleep(K_USEC(conv_time_max[mode] - conv_time_typ[mode]));
210 rc = i2c_read_dt(i2c, (uint8_t *)sensor_data, sizeof(sensor_data[0]) * data_num);
211 if (rc < 0) {
212 LOG_ERR("Failed to read measurement.\n");
213 return rc;
214 }
215 }
216
217 #ifdef CONFIG_ICP10125_CHECK_CRC
218 /* Calculate CRC from Chapter 5 Section 8 of ICP10125 Product manuals. */
219 for (size_t i = 0; i < data_num; i++) {
220 if (!icp10125_check_crc(sensor_data[i].data, SENSOR_DATA_SIZE)) {
221 LOG_ERR("Sensor data has invalid CRC.\n");
222 return -EIO;
223 }
224 }
225 #endif /* CONFIG_ICP10125_CHECK_CRC */
226
227 return 0;
228 }
229
icp10125_sample_fetch(const struct device * dev,const enum sensor_channel chan)230 static int icp10125_sample_fetch(const struct device *dev, const enum sensor_channel chan)
231 {
232 struct icp10125_data *data = dev->data;
233 const struct icp10125_dev_config *cfg = dev->config;
234 uint8_t endian_conversion[3];
235 struct icp10125_sensor_data sensor_data[PRESS_AND_AMBIENT_TEMP_DATA_NUM] = {0};
236 int rc = 0;
237
238 if (!(chan == SENSOR_CHAN_AMBIENT_TEMP || chan == SENSOR_CHAN_PRESS ||
239 chan == SENSOR_CHAN_ALL)) {
240 return -ENOTSUP;
241 }
242
243 if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
244 rc = icp10125_measure(&cfg->i2c, ambient_temp_measurement_cmds,
245 cfg->ambient_temp_mode, sensor_data, AMBIENT_TEMP_DATA_NUM);
246 if (rc < 0) {
247 return rc;
248 }
249
250 data->raw_ambient_temp = sys_get_be16(sensor_data[0].data);
251 } else {
252 rc = icp10125_measure(&cfg->i2c, press_measurement_cmds, cfg->press_mode,
253 sensor_data, PRESS_AND_AMBIENT_TEMP_DATA_NUM);
254 if (rc < 0) {
255 return rc;
256 }
257
258 endian_conversion[0] = sensor_data[0].data[0];
259 endian_conversion[1] = sensor_data[0].data[1];
260 endian_conversion[2] = sensor_data[1].data[0];
261 data->raw_press = sys_get_be24(endian_conversion);
262 data->raw_ambient_temp = sys_get_be16(sensor_data[2].data);
263 }
264
265 return 0;
266 }
267
icp10125_convert_press_value(struct icp10125_data * data,struct sensor_value * val)268 static void icp10125_convert_press_value(struct icp10125_data *data, struct sensor_value *val)
269 {
270 sensor_value_from_float(val, icp10125_calc_calibrated_press(data) / 1000.f);
271 }
272
icp10125_convert_ambient_temp_value(struct icp10125_data * data,struct sensor_value * val)273 static void icp10125_convert_ambient_temp_value(struct icp10125_data *data,
274 struct sensor_value *val)
275 {
276 sensor_value_from_float(val, icp10125_calc_calibrated_ambient_temp(data));
277 }
278
icp10125_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)279 static int icp10125_channel_get(const struct device *dev, enum sensor_channel chan,
280 struct sensor_value *val)
281 {
282 struct icp10125_data *data = dev->data;
283
284 if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
285 icp10125_convert_ambient_temp_value(data, val);
286 } else if (chan == SENSOR_CHAN_PRESS) {
287 icp10125_convert_press_value(data, val);
288 } else {
289 return -ENOTSUP;
290 }
291
292 return 0;
293 }
294
icp10125_init(const struct device * dev)295 static int icp10125_init(const struct device *dev)
296 {
297 int rc = icp10125_read_otp(dev);
298
299 if (rc < 0) {
300 return rc;
301 }
302
303 return 0;
304 }
305
306 static const struct sensor_driver_api icp10125_api_funcs = {
307 .sample_fetch = icp10125_sample_fetch,
308 .channel_get = icp10125_channel_get,
309 };
310
311 #define ICP10125_DEFINE(inst) \
312 static struct icp10125_data icp10125_drv_##inst; \
313 static const struct icp10125_dev_config icp10125_config_##inst = { \
314 .i2c = I2C_DT_SPEC_INST_GET(inst), \
315 .ambient_temp_mode = DT_INST_ENUM_IDX(inst, temperature_measurement_mode), \
316 .press_mode = DT_INST_ENUM_IDX(inst, pressure_measurement_mode)}; \
317 DEVICE_DT_INST_DEFINE(inst, icp10125_init, NULL, &icp10125_drv_##inst, \
318 &icp10125_config_##inst, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
319 &icp10125_api_funcs);
320
321 DT_INST_FOREACH_STATUS_OKAY(ICP10125_DEFINE)
322