1 /*
2 * Copyright (c) 2024, Vitrolife A/S
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ist_tsic_xx6
8
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/pwm.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/logging/log.h>
14 #include <zephyr/sys/atomic.h>
15 #include <zephyr/sys/util.h>
16 #include <zephyr/sys/util_macro.h>
17
18 LOG_MODULE_REGISTER(TSIC_XX6, CONFIG_SENSOR_LOG_LEVEL);
19
20 #define FRAME_BIT_PERIOD_US 125
21
22 enum {
23 FRAME_PARITIY_BIT_LSB,
24 FRAME_DATA_BIT_0,
25 FRAME_DATA_BIT_1,
26 FRAME_DATA_BIT_2,
27 FRAME_DATA_BIT_3,
28 FRAME_DATA_BIT_4,
29 FRAME_DATA_BIT_5,
30 FRAME_DATA_BIT_6,
31 FRAME_DATA_BIT_7,
32 FRAME_START_BIT_LSB,
33 /* Theres a single bit period between the two packets that is constant high. This bit will
34 * be part of the 2nd packet's start bit thus frame length is not affected.
35 */
36 FRAME_PARITIY_BIT_MSB,
37 FRAME_DATA_BIT_8,
38 FRAME_DATA_BIT_9,
39 FRAME_DATA_BIT_10,
40 FRAME_DATA_BIT_11,
41 FRAME_DATA_BIT_12,
42 FRAME_DATA_BIT_13,
43 FRAME_ZERO_BIT_0,
44 FRAME_ZERO_BIT_1,
45 FRAME_START_BIT_MSB,
46 FRAME_READY_BIT,
47 FRAME_FLAGS,
48 };
49
50 struct tsic_xx6_config {
51 const struct pwm_dt_spec pwm;
52 const int8_t lower_temperature_limit;
53 const uint8_t higher_temperature_limit;
54 const uint8_t data_bits;
55 };
56
57 struct tsic_xx6_data {
58 uint64_t frame_cycles;
59 struct sensor_value val;
60
61 ATOMIC_DEFINE(frame, FRAME_FLAGS);
62 uint32_t buf;
63 uint8_t buf_index;
64 };
65
tsic_xx6_buf_reset(struct tsic_xx6_data * data)66 static inline void tsic_xx6_buf_reset(struct tsic_xx6_data *data)
67 {
68 data->buf_index = FRAME_START_BIT_MSB;
69 }
70
tsic_xx6_is_buf_reset(struct tsic_xx6_data * data)71 static inline bool tsic_xx6_is_buf_reset(struct tsic_xx6_data *data)
72 {
73 return data->buf_index == FRAME_START_BIT_MSB;
74 }
75
tsic_xx6_is_data_line_idle(struct tsic_xx6_data * data,uint64_t period_cycles)76 static inline bool tsic_xx6_is_data_line_idle(struct tsic_xx6_data *data, uint64_t period_cycles)
77 {
78 /* If the period is larger than two frames assume the data line has been idle */
79 return period_cycles > data->frame_cycles * 2;
80 }
81
tsic_xx6_pwm_callback(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,int status,void * user_data)82 static void tsic_xx6_pwm_callback(const struct device *dev, uint32_t channel,
83 uint32_t period_cycles, uint32_t pulse_cycles, int status,
84 void *user_data)
85 {
86 const struct device *tsic_xx6_dev = user_data;
87 const struct tsic_xx6_config *config = tsic_xx6_dev->config;
88 struct tsic_xx6_data *data = tsic_xx6_dev->data;
89 uint32_t low_cycles;
90 bool val;
91
92 if (dev != config->pwm.dev || channel != config->pwm.channel) {
93 return;
94 }
95
96 if (status != 0) {
97 LOG_ERR("callback failed: %d", status);
98 return;
99 }
100
101 if (!tsic_xx6_is_buf_reset(data) && tsic_xx6_is_data_line_idle(data, period_cycles)) {
102 LOG_ERR("unexpected data idle");
103 tsic_xx6_buf_reset(data);
104 }
105
106 /*
107 * Calculate low cycles: The sensor sends the pulse in the last part of the period. The PWM
108 * capture driver triggers on rising edge with normal polarity. Therefore only the low part
109 * of the frame bit is present.
110 */
111 low_cycles = period_cycles - pulse_cycles;
112
113 /* 25 % duty cycle is 0, 75 % duty cycle is 1 */
114 val = low_cycles * 2 < data->frame_cycles;
115 WRITE_BIT(data->buf, data->buf_index, val);
116
117 if (data->buf_index > 0) {
118 --data->buf_index;
119 } else {
120 WRITE_BIT(data->buf, FRAME_READY_BIT, 1);
121 (void)atomic_set(data->frame, data->buf);
122 tsic_xx6_buf_reset(data);
123 }
124 }
125
tsic_xx6_parity_check(uint8_t data,bool parity)126 static inline bool tsic_xx6_parity_check(uint8_t data, bool parity)
127 {
128 bool data_parity = false;
129 size_t i;
130
131 for (i = 0; i < 8; ++i) {
132 data_parity ^= FIELD_GET(BIT(i), data);
133 }
134
135 return (parity ^ data_parity) == 0;
136 }
137
tsic_xx6_get_data_bits(const struct tsic_xx6_config * config,uint16_t * data_bits,uint32_t frame)138 static int tsic_xx6_get_data_bits(const struct tsic_xx6_config *config, uint16_t *data_bits,
139 uint32_t frame)
140 {
141 uint8_t frame_data_bit_high =
142 config->data_bits == 14 ? FRAME_DATA_BIT_13 : FRAME_DATA_BIT_10;
143 uint8_t data_msb = FIELD_GET(GENMASK(frame_data_bit_high, FRAME_DATA_BIT_8), frame);
144 uint8_t data_lsb = FIELD_GET(GENMASK(FRAME_DATA_BIT_7, FRAME_DATA_BIT_0), frame);
145 bool parity_msb = FIELD_GET(BIT(FRAME_PARITIY_BIT_MSB), frame);
146 bool parity_lsb = BIT(FRAME_PARITIY_BIT_LSB) & frame;
147
148 if (!tsic_xx6_parity_check(data_msb, parity_msb) ||
149 !tsic_xx6_parity_check(data_lsb, parity_lsb)) {
150 return -EIO;
151 }
152
153 *data_bits = data_msb << 8 | data_lsb;
154
155 return 0;
156 }
157
tsic_xx6_get_value(const struct tsic_xx6_config * config,struct tsic_xx6_data * data,uint16_t data_bits)158 static void tsic_xx6_get_value(const struct tsic_xx6_config *config, struct tsic_xx6_data *data,
159 uint16_t data_bits)
160 {
161 int64_t tmp;
162
163 /* Apply the datasheet formula scaled to micro celcius */
164 tmp = (int64_t)data_bits *
165 (config->higher_temperature_limit - config->lower_temperature_limit);
166 tmp = tmp * 1000000 / (BIT(config->data_bits) - 1);
167 tmp += (int64_t)config->lower_temperature_limit * 1000000;
168
169 data->val.val1 = tmp / 1000000;
170 data->val.val2 = tmp % 1000000;
171 }
172
tsic_xx6_sample_fetch(const struct device * dev,enum sensor_channel chan)173 static int tsic_xx6_sample_fetch(const struct device *dev, enum sensor_channel chan)
174 {
175 const struct tsic_xx6_config *config = dev->config;
176 struct tsic_xx6_data *data = dev->data;
177 uint32_t frame;
178 uint16_t data_bits;
179 int rc;
180
181 if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
182 return -ENOTSUP;
183 }
184
185 frame = atomic_and(data->frame, ~BIT(FRAME_READY_BIT));
186
187 if (FIELD_GET(BIT(FRAME_READY_BIT), frame) == 0) {
188 return -EBUSY;
189 }
190
191 rc = tsic_xx6_get_data_bits(config, &data_bits, frame);
192 if (rc != 0) {
193 return rc;
194 }
195
196 tsic_xx6_get_value(config, data, data_bits);
197
198 return 0;
199 }
200
tsic_xx6_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)201 static int tsic_xx6_channel_get(const struct device *dev, enum sensor_channel chan,
202 struct sensor_value *val)
203 {
204 struct tsic_xx6_data *data = dev->data;
205
206 if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
207 return -ENOTSUP;
208 }
209
210 *val = data->val;
211
212 return 0;
213 }
214
215 static DEVICE_API(sensor, tsic_xx6_driver_api) = {
216 .sample_fetch = tsic_xx6_sample_fetch,
217 .channel_get = tsic_xx6_channel_get,
218 };
219
tsic_xx6_get_frame_cycles(const struct tsic_xx6_config * config,uint64_t * frame_cycles)220 static int tsic_xx6_get_frame_cycles(const struct tsic_xx6_config *config, uint64_t *frame_cycles)
221 {
222 uint64_t tmp;
223 int rc;
224
225 rc = pwm_get_cycles_per_sec(config->pwm.dev, config->pwm.channel, &tmp);
226 if (rc != 0) {
227 return rc;
228 }
229
230 if (u64_mul_overflow(tmp, FRAME_BIT_PERIOD_US, &tmp)) {
231 return -ERANGE;
232 }
233
234 *frame_cycles = tmp / USEC_PER_SEC;
235
236 return 0;
237 }
238
tsic_xx6_init(const struct device * dev)239 static int tsic_xx6_init(const struct device *dev)
240 {
241 const struct tsic_xx6_config *config = dev->config;
242 struct tsic_xx6_data *data = dev->data;
243 int rc;
244
245 if (!pwm_is_ready_dt(&config->pwm)) {
246 return -ENODEV;
247 }
248
249 rc = tsic_xx6_get_frame_cycles(config, &data->frame_cycles);
250 if (rc != 0) {
251 return rc;
252 }
253
254 rc = pwm_configure_capture(config->pwm.dev, config->pwm.channel,
255 config->pwm.flags | PWM_CAPTURE_TYPE_BOTH |
256 PWM_CAPTURE_MODE_CONTINUOUS,
257 tsic_xx6_pwm_callback, (void *)dev);
258 if (rc != 0) {
259 return rc;
260 }
261
262 tsic_xx6_buf_reset(data);
263
264 rc = pwm_enable_capture(config->pwm.dev, config->pwm.channel);
265 if (rc != 0) {
266 return rc;
267 }
268
269 return 0;
270 }
271
272 #define TSIC_XX6_DEVICE(n) \
273 \
274 static struct tsic_xx6_data tsic_xx6_data_##n; \
275 \
276 static const struct tsic_xx6_config tsic_xx6_config_##n = { \
277 .pwm = PWM_DT_SPEC_INST_GET(n), \
278 .lower_temperature_limit = (int8_t)DT_INST_PROP(n, lower_temperature_limit), \
279 .higher_temperature_limit = DT_INST_PROP(n, higher_temperature_limit), \
280 .data_bits = DT_INST_PROP(n, data_bits), \
281 }; \
282 \
283 SENSOR_DEVICE_DT_INST_DEFINE(n, &tsic_xx6_init, NULL, &tsic_xx6_data_##n, \
284 &tsic_xx6_config_##n, POST_KERNEL, \
285 CONFIG_SENSOR_INIT_PRIORITY, &tsic_xx6_driver_api);
286
287 DT_INST_FOREACH_STATUS_OKAY(TSIC_XX6_DEVICE)
288