1 /*
2  * Copyright (c) 2020-2021 Vestas Wind Systems A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/drivers/pwm.h>
9 #include <zephyr/ztest.h>
10 
11 #include "test_pwm_loopback.h"
12 
13 #define TEST_PWM_PERIOD_NSEC 100000000
14 #define TEST_PWM_PULSE_NSEC   15000000
15 #define TEST_PWM_PERIOD_USEC    100000
16 #define TEST_PWM_PULSE_USEC      75000
17 
18 enum test_pwm_unit {
19 	TEST_PWM_UNIT_NSEC,
20 	TEST_PWM_UNIT_USEC,
21 };
22 
get_test_pwms(struct test_pwm * out,struct test_pwm * in)23 void get_test_pwms(struct test_pwm *out, struct test_pwm *in)
24 {
25 	/* PWM generator device */
26 	out->dev = DEVICE_DT_GET(PWM_LOOPBACK_OUT_CTLR);
27 	out->pwm = PWM_LOOPBACK_OUT_CHANNEL;
28 	out->flags = PWM_LOOPBACK_OUT_FLAGS;
29 	zassert_true(device_is_ready(out->dev), "pwm loopback output device is not ready");
30 
31 	/* PWM capture device */
32 	in->dev = DEVICE_DT_GET(PWM_LOOPBACK_IN_CTLR);
33 	in->pwm = PWM_LOOPBACK_IN_CHANNEL;
34 	in->flags = PWM_LOOPBACK_IN_FLAGS;
35 	zassert_true(device_is_ready(in->dev), "pwm loopback input device is not ready");
36 }
37 
test_capture(uint32_t period,uint32_t pulse,enum test_pwm_unit unit,pwm_flags_t flags)38 static void test_capture(uint32_t period, uint32_t pulse, enum test_pwm_unit unit,
39 		  pwm_flags_t flags)
40 {
41 	struct test_pwm in;
42 	struct test_pwm out;
43 	uint64_t period_capture = 0;
44 	uint64_t pulse_capture = 0;
45 	int err = 0;
46 
47 	get_test_pwms(&out, &in);
48 
49 	switch (unit) {
50 	case TEST_PWM_UNIT_NSEC:
51 		TC_PRINT("Testing PWM capture @ %u/%u nsec\n",
52 			 pulse, period);
53 		err = pwm_set(out.dev, out.pwm, period, pulse, out.flags ^=
54 			      (flags & PWM_POLARITY_MASK));
55 		break;
56 
57 	case TEST_PWM_UNIT_USEC:
58 		TC_PRINT("Testing PWM capture @ %u/%u usec\n",
59 			 pulse, period);
60 		err = pwm_set(out.dev, out.pwm, PWM_USEC(period),
61 			      PWM_USEC(pulse), out.flags ^=
62 			      (flags & PWM_POLARITY_MASK));
63 		break;
64 
65 	default:
66 		TC_PRINT("Unsupported test unit");
67 		ztest_test_fail();
68 	}
69 
70 	zassert_equal(err, 0, "failed to set pwm output (err %d)", err);
71 
72 	switch (unit) {
73 	case TEST_PWM_UNIT_NSEC:
74 		err = pwm_capture_nsec(in.dev, in.pwm, flags, &period_capture,
75 				       &pulse_capture, K_NSEC(period * 10));
76 		break;
77 
78 	case TEST_PWM_UNIT_USEC:
79 		err = pwm_capture_usec(in.dev, in.pwm, flags, &period_capture,
80 				       &pulse_capture, K_USEC(period * 10));
81 		break;
82 
83 	default:
84 		TC_PRINT("Unsupported test unit");
85 		ztest_test_fail();
86 	}
87 
88 	pwm_disable_capture(in.dev, in.pwm);
89 
90 	if (err == -ENOTSUP) {
91 		TC_PRINT("capture type not supported\n");
92 		ztest_test_skip();
93 	}
94 
95 	zassert_equal(err, 0, "failed to capture pwm (err %d)", err);
96 
97 	if (flags & PWM_CAPTURE_TYPE_PERIOD) {
98 		zassert_within(period_capture, period, period / 100,
99 			       "period capture off by more than 1%");
100 	}
101 
102 	if (flags & PWM_CAPTURE_TYPE_PULSE) {
103 		zassert_within(pulse_capture, pulse, pulse / 100,
104 			       "pulse capture off by more than 1%");
105 	}
106 }
107 
ZTEST_USER(pwm_loopback,test_pulse_capture)108 ZTEST_USER(pwm_loopback, test_pulse_capture)
109 {
110 	test_capture(TEST_PWM_PERIOD_NSEC, TEST_PWM_PULSE_NSEC,
111 		     TEST_PWM_UNIT_NSEC,
112 		     PWM_CAPTURE_TYPE_PULSE | PWM_POLARITY_NORMAL);
113 	test_capture(TEST_PWM_PERIOD_USEC, TEST_PWM_PULSE_USEC,
114 		     TEST_PWM_UNIT_USEC,
115 		     PWM_CAPTURE_TYPE_PULSE | PWM_POLARITY_NORMAL);
116 }
117 
ZTEST_USER(pwm_loopback,test_pulse_capture_inverted)118 ZTEST_USER(pwm_loopback, test_pulse_capture_inverted)
119 {
120 	test_capture(TEST_PWM_PERIOD_NSEC, TEST_PWM_PULSE_NSEC,
121 		     TEST_PWM_UNIT_NSEC,
122 		     PWM_CAPTURE_TYPE_PULSE | PWM_POLARITY_INVERTED);
123 	test_capture(TEST_PWM_PERIOD_USEC, TEST_PWM_PULSE_USEC,
124 		     TEST_PWM_UNIT_USEC,
125 		     PWM_CAPTURE_TYPE_PULSE | PWM_POLARITY_INVERTED);
126 }
127 
ZTEST_USER(pwm_loopback,test_period_capture)128 ZTEST_USER(pwm_loopback, test_period_capture)
129 {
130 	test_capture(TEST_PWM_PERIOD_NSEC, TEST_PWM_PULSE_NSEC,
131 		     TEST_PWM_UNIT_NSEC,
132 		     PWM_CAPTURE_TYPE_PERIOD | PWM_POLARITY_NORMAL);
133 	test_capture(TEST_PWM_PERIOD_USEC, TEST_PWM_PULSE_USEC,
134 		     TEST_PWM_UNIT_USEC,
135 		     PWM_CAPTURE_TYPE_PERIOD | PWM_POLARITY_NORMAL);
136 }
137 
ZTEST_USER(pwm_loopback,test_period_capture_inverted)138 ZTEST_USER(pwm_loopback, test_period_capture_inverted)
139 {
140 	test_capture(TEST_PWM_PERIOD_NSEC, TEST_PWM_PULSE_NSEC,
141 		     TEST_PWM_UNIT_NSEC,
142 		     PWM_CAPTURE_TYPE_PERIOD | PWM_POLARITY_INVERTED);
143 	test_capture(TEST_PWM_PERIOD_USEC, TEST_PWM_PULSE_USEC,
144 		     TEST_PWM_UNIT_USEC,
145 		     PWM_CAPTURE_TYPE_PERIOD | PWM_POLARITY_INVERTED);
146 }
147 
ZTEST_USER(pwm_loopback,test_pulse_and_period_capture)148 ZTEST_USER(pwm_loopback, test_pulse_and_period_capture)
149 {
150 	test_capture(TEST_PWM_PERIOD_NSEC, TEST_PWM_PULSE_NSEC,
151 		     TEST_PWM_UNIT_NSEC,
152 		     PWM_CAPTURE_TYPE_BOTH | PWM_POLARITY_NORMAL);
153 	test_capture(TEST_PWM_PERIOD_USEC, TEST_PWM_PULSE_USEC,
154 		     TEST_PWM_UNIT_USEC,
155 		     PWM_CAPTURE_TYPE_BOTH | PWM_POLARITY_NORMAL);
156 }
157 
ZTEST_USER(pwm_loopback,test_capture_timeout)158 ZTEST_USER(pwm_loopback, test_capture_timeout)
159 {
160 	struct test_pwm in;
161 	struct test_pwm out;
162 	uint32_t period;
163 	uint32_t pulse;
164 	int err;
165 
166 	get_test_pwms(&out, &in);
167 
168 	err = pwm_set_cycles(out.dev, out.pwm, 100, 0, out.flags);
169 	zassert_equal(err, 0, "failed to set pwm output (err %d)", err);
170 
171 	err = pwm_capture_cycles(in.dev, in.pwm, PWM_CAPTURE_TYPE_PULSE,
172 				 &period, &pulse, K_MSEC(1000));
173 	if (err == -ENOTSUP) {
174 		TC_PRINT("Pulse capture not supported, "
175 			 "trying period capture\n");
176 		err = pwm_capture_cycles(in.dev, in.pwm,
177 					 PWM_CAPTURE_TYPE_PERIOD, &period,
178 					 &pulse, K_MSEC(1000));
179 	}
180 
181 	zassert_equal(err, -EAGAIN, "pwm capture did not timeout (err %d)",
182 		      err);
183 }
184 
continuous_capture_callback(const struct device * dev,uint32_t pwm,uint32_t period_cycles,uint32_t pulse_cycles,int status,void * user_data)185 static void continuous_capture_callback(const struct device *dev,
186 					uint32_t pwm,
187 					uint32_t period_cycles,
188 					uint32_t pulse_cycles,
189 					int status,
190 					void *user_data)
191 {
192 	struct test_pwm_callback_data *data = user_data;
193 
194 	if (data->count > data->buffer_len) {
195 		/* Safe guard in case capture is not disabled */
196 		return;
197 	}
198 
199 	if (status != 0) {
200 		/* Error occurred */
201 		data->status = status;
202 		k_sem_give(&data->sem);
203 	}
204 
205 	if (data->pulse_capture) {
206 		data->buffer[data->count++] = pulse_cycles;
207 	} else {
208 		data->buffer[data->count++] = period_cycles;
209 	}
210 
211 	if (data->count > data->buffer_len) {
212 		data->status = 0;
213 		k_sem_give(&data->sem);
214 	}
215 }
216 
ZTEST(pwm_loopback,test_continuous_capture)217 ZTEST(pwm_loopback, test_continuous_capture)
218 {
219 	struct test_pwm in;
220 	struct test_pwm out;
221 	uint32_t buffer[10];
222 	struct test_pwm_callback_data data = {
223 		.buffer = buffer,
224 		.buffer_len = ARRAY_SIZE(buffer),
225 		.count = 0,
226 		.pulse_capture = true,
227 	};
228 	uint64_t usec = 0;
229 	int err;
230 	int i;
231 
232 	get_test_pwms(&out, &in);
233 
234 	memset(buffer, 0, sizeof(buffer));
235 	k_sem_init(&data.sem, 0, 1);
236 
237 	err = pwm_set(out.dev, out.pwm, PWM_USEC(TEST_PWM_PERIOD_USEC),
238 		      PWM_USEC(TEST_PWM_PULSE_USEC), out.flags);
239 	zassert_equal(err, 0, "failed to set pwm output (err %d)", err);
240 
241 	err = pwm_configure_capture(in.dev, in.pwm,
242 				    in.flags |
243 				    PWM_CAPTURE_MODE_CONTINUOUS |
244 				    PWM_CAPTURE_TYPE_PULSE,
245 				    continuous_capture_callback, &data);
246 	if (err == -ENOTSUP) {
247 		TC_PRINT("Pulse capture not supported, "
248 			 "trying period capture\n");
249 		err = pwm_configure_capture(in.dev, in.pwm,
250 					    in.flags |
251 					    PWM_CAPTURE_MODE_CONTINUOUS |
252 					    PWM_CAPTURE_TYPE_PERIOD,
253 					    continuous_capture_callback, &data);
254 		zassert_equal(err, 0, "failed to configure pwm input (err %d)",
255 			      err);
256 		data.pulse_capture = false;
257 	}
258 
259 	err = pwm_enable_capture(in.dev, in.pwm);
260 	zassert_equal(err, 0, "failed to enable pwm capture (err %d)", err);
261 
262 	err = k_sem_take(&data.sem, K_USEC(TEST_PWM_PERIOD_USEC * data.buffer_len * 10));
263 	zassert_equal(err, 0, "pwm capture timed out (err %d)", err);
264 	zassert_equal(data.status, 0, "pwm capture failed (err %d)", err);
265 
266 	err = pwm_disable_capture(in.dev, in.pwm);
267 	zassert_equal(err, 0, "failed to disable pwm capture (err %d)", err);
268 
269 	for (i = 0; i < data.buffer_len; i++) {
270 		err = pwm_cycles_to_usec(in.dev, in.pwm, buffer[i], &usec);
271 		zassert_equal(err, 0, "failed to calculate usec (err %d)", err);
272 
273 		if (data.pulse_capture) {
274 			zassert_within(usec, TEST_PWM_PULSE_USEC, TEST_PWM_PULSE_USEC / 100,
275 				       "pulse capture off by more than 1%");
276 		} else {
277 			zassert_within(usec, TEST_PWM_PERIOD_USEC, TEST_PWM_PERIOD_USEC / 100,
278 				       "period capture off by more than 1%");
279 		}
280 	}
281 }
282 
ZTEST(pwm_loopback,test_capture_busy)283 ZTEST(pwm_loopback, test_capture_busy)
284 {
285 	struct test_pwm in;
286 	struct test_pwm out;
287 	uint32_t buffer[10];
288 	struct test_pwm_callback_data data = {
289 		.buffer = buffer,
290 		.buffer_len = ARRAY_SIZE(buffer),
291 		.count = 0,
292 		.pulse_capture = true,
293 	};
294 	pwm_flags_t flags = PWM_CAPTURE_MODE_SINGLE |
295 		PWM_CAPTURE_TYPE_PULSE;
296 	int err;
297 
298 	get_test_pwms(&out, &in);
299 
300 	memset(buffer, 0, sizeof(buffer));
301 	k_sem_init(&data.sem, 0, 1);
302 
303 	err = pwm_set_cycles(out.dev, out.pwm, 100, 0, out.flags);
304 	zassert_equal(err, 0, "failed to set pwm output (err %d)", err);
305 
306 	err = pwm_configure_capture(in.dev, in.pwm, in.flags | flags,
307 				    continuous_capture_callback, &data);
308 	if (err == -ENOTSUP) {
309 		TC_PRINT("Pulse capture not supported, "
310 			 "trying period capture\n");
311 		flags = PWM_CAPTURE_MODE_SINGLE | PWM_CAPTURE_TYPE_PERIOD;
312 		err = pwm_configure_capture(in.dev, in.pwm, in.flags | flags,
313 					    continuous_capture_callback, &data);
314 		zassert_equal(err, 0, "failed to configure pwm input (err %d)",
315 			      err);
316 		data.pulse_capture = false;
317 	}
318 
319 	err = pwm_enable_capture(in.dev, in.pwm);
320 	zassert_equal(err, 0, "failed to enable pwm capture (err %d)", err);
321 
322 	err = pwm_configure_capture(in.dev, in.pwm, in.flags | flags,
323 				    continuous_capture_callback, &data);
324 	zassert_equal(err, -EBUSY, "pwm capture not busy (err %d)", err);
325 
326 	err = pwm_enable_capture(in.dev, in.pwm);
327 	zassert_equal(err, -EBUSY, "pwm capture not busy (err %d)", err);
328 
329 	err = pwm_disable_capture(in.dev, in.pwm);
330 	zassert_equal(err, 0, "failed to disable pwm capture (err %d)", err);
331 }
332