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