1 /*
2 * Copyright (c) 2016 Intel Corporation.
3 * Copyright (c) 2020-2021 Vestas Wind Systems A/S
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /**
9 * @file
10 * @brief Public PWM Driver APIs
11 */
12
13 #ifndef ZEPHYR_INCLUDE_DRIVERS_PWM_H_
14 #define ZEPHYR_INCLUDE_DRIVERS_PWM_H_
15
16 /**
17 * @brief PWM Interface
18 * @defgroup pwm_interface PWM Interface
19 * @ingroup io_interfaces
20 * @{
21 */
22
23 #include <errno.h>
24 #include <zephyr/types.h>
25 #include <stddef.h>
26 #include <sys/math_extras.h>
27 #include <device.h>
28 #include <dt-bindings/pwm/pwm.h>
29
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33
34 /**
35 * @name PWM capture configuration flags
36 * @{
37 */
38
39 /** @cond INTERNAL_HIDDEN */
40 /* Bit 0 is used for PWM_POLARITY_NORMAL/PWM_POLARITY_INVERTED */
41 #define PWM_CAPTURE_TYPE_SHIFT 1U
42 #define PWM_CAPTURE_TYPE_MASK (3U << PWM_CAPTURE_TYPE_SHIFT)
43 #define PWM_CAPTURE_MODE_SHIFT 3U
44 #define PWM_CAPTURE_MODE_MASK (1U << PWM_CAPTURE_MODE_SHIFT)
45 /** @endcond */
46
47 /** PWM pin capture captures period. */
48 #define PWM_CAPTURE_TYPE_PERIOD (1U << PWM_CAPTURE_TYPE_SHIFT)
49
50 /** PWM pin capture captures pulse width. */
51 #define PWM_CAPTURE_TYPE_PULSE (2U << PWM_CAPTURE_TYPE_SHIFT)
52
53 /** PWM pin capture captures both period and pulse width. */
54 #define PWM_CAPTURE_TYPE_BOTH (PWM_CAPTURE_TYPE_PERIOD | \
55 PWM_CAPTURE_TYPE_PULSE)
56
57 /** PWM pin capture captures a single period/pulse width. */
58 #define PWM_CAPTURE_MODE_SINGLE (0U << PWM_CAPTURE_MODE_SHIFT)
59
60 /** PWM pin capture captures period/pulse width continuously. */
61 #define PWM_CAPTURE_MODE_CONTINUOUS (1U << PWM_CAPTURE_MODE_SHIFT)
62
63 /** @} */
64
65 /**
66 * @brief Provides a type to hold PWM configuration flags.
67 */
68 typedef uint8_t pwm_flags_t;
69
70 /**
71 * @typedef pwm_pin_set_t
72 * @brief Callback API upon setting the pin
73 * See @a pwm_pin_set_cycles() for argument description
74 */
75 typedef int (*pwm_pin_set_t)(const struct device *dev, uint32_t pwm,
76 uint32_t period_cycles, uint32_t pulse_cycles,
77 pwm_flags_t flags);
78
79 /**
80 * @typedef pwm_capture_callback_handler_t
81 * @brief PWM capture callback handler function signature
82 *
83 * @note The callback handler will be called in interrupt context.
84 *
85 * @note @kconfig{CONFIG_PWM_CAPTURE} must be selected to enable PWM capture
86 * support.
87 *
88 * @param dev Pointer to the device structure for the driver instance.
89 * @param pwm PWM pin.
90
91 * @param period_cycles Captured PWM period width (in clock cycles). HW
92 * specific.
93 * @param pulse_cycles Captured PWM pulse width (in clock cycles). HW specific.
94 * @param status Status for the PWM capture (0 if no error, negative errno
95 * otherwise. See @a pwm_pin_capture_cycles() return value
96 * descriptions for details).
97 * @param user_data User data passed to @a pwm_pin_configure_capture()
98 */
99 typedef void (*pwm_capture_callback_handler_t)(const struct device *dev,
100 uint32_t pwm,
101 uint32_t period_cycles,
102 uint32_t pulse_cycles,
103 int status,
104 void *user_data);
105
106 /**
107 * @typedef pwm_pin_configure_capture_t
108 * @brief Callback API upon configuring PWM pin capture
109 * See @a pwm_pin_configure_capture() for argument description
110 */
111 typedef int (*pwm_pin_configure_capture_t)(const struct device *dev,
112 uint32_t pwm,
113 pwm_flags_t flags,
114 pwm_capture_callback_handler_t cb,
115 void *user_data);
116 /**
117 * @typedef pwm_pin_enable_capture_t
118 * @brief Callback API upon enabling PWM pin capture
119 * See @a pwm_pin_enable_capture() for argument description
120 */
121 typedef int (*pwm_pin_enable_capture_t)(const struct device *dev,
122 uint32_t pwm);
123
124 /**
125 * @typedef pwm_pin_disable_capture_t
126 * @brief Callback API upon disabling PWM pin capture
127 * See @a pwm_pin_disable_capture() for argument description
128 */
129 typedef int (*pwm_pin_disable_capture_t)(const struct device *dev,
130 uint32_t pwm);
131
132 /**
133 * @typedef pwm_get_cycles_per_sec_t
134 * @brief Callback API upon getting cycles per second
135 * See @a pwm_get_cycles_per_sec() for argument description
136 */
137 typedef int (*pwm_get_cycles_per_sec_t)(const struct device *dev,
138 uint32_t pwm,
139 uint64_t *cycles);
140
141 /** @brief PWM driver API definition. */
142 __subsystem struct pwm_driver_api {
143 pwm_pin_set_t pin_set;
144 #ifdef CONFIG_PWM_CAPTURE
145 pwm_pin_configure_capture_t pin_configure_capture;
146 pwm_pin_enable_capture_t pin_enable_capture;
147 pwm_pin_disable_capture_t pin_disable_capture;
148 #endif /* CONFIG_PWM_CAPTURE */
149 pwm_get_cycles_per_sec_t get_cycles_per_sec;
150 };
151
152 /**
153 * @brief Set the period and pulse width for a single PWM output.
154 *
155 * The PWM period and pulse width will synchronously be set to the new values
156 * without glitches in the PWM signal, but the call will not block for the
157 * change to take effect.
158 *
159 * @note Not all PWM controllers support synchronous, glitch-free updates of the
160 * PWM period and pulse width. Depending on the hardware, changing the PWM
161 * period and/or pulse width may cause a glitch in the generated PWM signal.
162 *
163 * @note Some multi-channel PWM controllers share the PWM period across all
164 * channels. Depending on the hardware, changing the PWM period for one channel
165 * may affect the PWM period for the other channels of the same PWM controller.
166 *
167 * Passing 0 as @p pulse will cause the pin to be driven to a constant
168 * inactive level.
169 * Passing a non-zero @p pulse equal to @p period will cause the pin
170 * to be driven to a constant active level.
171 *
172 * @param dev Pointer to the device structure for the driver instance.
173 * @param pwm PWM pin.
174 * @param period Period (in clock cycle) set to the PWM. HW specific.
175 * @param pulse Pulse width (in clock cycle) set to the PWM. HW specific.
176 * @param flags Flags for pin configuration (polarity).
177 *
178 * @retval 0 If successful.
179 * @retval Negative errno code if failure.
180 */
181 __syscall int pwm_pin_set_cycles(const struct device *dev, uint32_t pwm,
182 uint32_t period, uint32_t pulse, pwm_flags_t flags);
183
z_impl_pwm_pin_set_cycles(const struct device * dev,uint32_t pwm,uint32_t period,uint32_t pulse,pwm_flags_t flags)184 static inline int z_impl_pwm_pin_set_cycles(const struct device *dev,
185 uint32_t pwm,
186 uint32_t period, uint32_t pulse,
187 pwm_flags_t flags)
188 {
189 struct pwm_driver_api *api;
190
191 api = (struct pwm_driver_api *)dev->api;
192 return api->pin_set(dev, pwm, period, pulse, flags);
193 }
194
195 /**
196 * @brief Configure PWM period/pulse width capture for a single PWM input.
197 *
198 * After configuring PWM capture using this function, the capture can be
199 * enabled/disabled using @a pwm_pin_enable_capture() and @a
200 * pwm_pin_disable_capture().
201 *
202 * @note This API function cannot be invoked from user space due to the use of a
203 * function callback. In user space, one of the simpler API functions (@a
204 * pwm_pin_capture_cycles(), @a pwm_pin_capture_usec(), or @a
205 * pwm_pin_capture_nsec()) can be used instead.
206 *
207 * @note @kconfig{CONFIG_PWM_CAPTURE} must be selected for this function to be
208 * available.
209 *
210 * @param dev Pointer to the device structure for the driver instance.
211 * @param pwm PWM pin.
212 * @param flags PWM capture flags
213 * @param cb Application callback handler function to be called upon capture
214 * @param user_data User data to pass to the application callback handler
215 * function
216 *
217 * @retval -EINVAL if invalid function parameters were given
218 * @retval -ENOSYS if PWM capture is not supported or the given flags are not
219 * supported
220 * @retval -EIO if IO error occurred while configuring
221 * @retval -EBUSY if PWM capture is already in progress
222 */
223 #ifdef CONFIG_PWM_CAPTURE
pwm_pin_configure_capture(const struct device * dev,uint32_t pwm,pwm_flags_t flags,pwm_capture_callback_handler_t cb,void * user_data)224 static inline int pwm_pin_configure_capture(const struct device *dev,
225 uint32_t pwm,
226 pwm_flags_t flags,
227 pwm_capture_callback_handler_t cb,
228 void *user_data)
229 {
230 const struct pwm_driver_api *api = (struct pwm_driver_api *)dev->api;
231
232 if (api->pin_configure_capture == NULL) {
233 return -ENOSYS;
234 }
235
236 return api->pin_configure_capture(dev, pwm, flags, cb, user_data);
237 }
238 #endif /* CONFIG_PWM_CAPTURE */
239
240 /**
241 * @brief Enable PWM period/pulse width capture for a single PWM input.
242 *
243 * The PWM pin must be configured using @a pwm_pin_configure_capture() prior to
244 * calling this function.
245 *
246 * @note @kconfig{CONFIG_PWM_CAPTURE} must be selected for this function to be
247 * available.
248 *
249 * @param dev Pointer to the device structure for the driver instance.
250 * @param pwm PWM pin.
251 *
252 * @retval 0 If successful.
253 * @retval -EINVAL if invalid function parameters were given
254 * @retval -ENOSYS if PWM capture is not supported
255 * @retval -EIO if IO error occurred while enabling PWM capture
256 * @retval -EBUSY if PWM capture is already in progress
257 */
258 __syscall int pwm_pin_enable_capture(const struct device *dev, uint32_t pwm);
259
260 #ifdef CONFIG_PWM_CAPTURE
z_impl_pwm_pin_enable_capture(const struct device * dev,uint32_t pwm)261 static inline int z_impl_pwm_pin_enable_capture(const struct device *dev,
262 uint32_t pwm)
263 {
264 const struct pwm_driver_api *api = (struct pwm_driver_api *)dev->api;
265
266 if (api->pin_enable_capture == NULL) {
267 return -ENOSYS;
268 }
269
270 return api->pin_enable_capture(dev, pwm);
271 }
272 #endif /* CONFIG_PWM_CAPTURE */
273
274 /**
275 * @brief Disable PWM period/pulse width capture for a single PWM input.
276 *
277 *
278 * @note @kconfig{CONFIG_PWM_CAPTURE} must be selected for this function to be
279 * available.
280 *
281 * @param dev Pointer to the device structure for the driver instance.
282 * @param pwm PWM pin.
283 *
284 * @retval 0 If successful.
285 * @retval -EINVAL if invalid function parameters were given
286 * @retval -ENOSYS if PWM capture is not supported
287 * @retval -EIO if IO error occurred while disabling PWM capture
288 */
289 __syscall int pwm_pin_disable_capture(const struct device *dev, uint32_t pwm);
290
291 #ifdef CONFIG_PWM_CAPTURE
z_impl_pwm_pin_disable_capture(const struct device * dev,uint32_t pwm)292 static inline int z_impl_pwm_pin_disable_capture(const struct device *dev,
293 uint32_t pwm)
294 {
295 const struct pwm_driver_api *api = (struct pwm_driver_api *)dev->api;
296
297 if (api->pin_disable_capture == NULL) {
298 return -ENOSYS;
299 }
300
301 return api->pin_disable_capture(dev, pwm);
302 }
303 #endif /* CONFIG_PWM_CAPTURE */
304
305 /**
306 * @brief Capture a single PWM period/pulse width in clock cycles for a single
307 * PWM input.
308 *
309 * This API function wraps calls to @a pwm_pin_configure_capture(), @a
310 * pwm_pin_enable_capture(), and @a pwm_pin_disable_capture() and passes the
311 * capture result to the caller. The function is blocking until either the PWM
312 * capture is completed or a timeout occurs.
313 *
314 * @note @kconfig{CONFIG_PWM_CAPTURE} must be selected for this function to be
315 * available.
316 *
317 * @param dev Pointer to the device structure for the driver instance.
318 * @param pwm PWM pin.
319 * @param flags PWM capture flags.
320 * @param period Pointer to the memory to store the captured PWM period width
321 * (in clock cycles). HW specific.
322 * @param pulse Pointer to the memory to store the captured PWM pulse width (in
323 * clock cycles). HW specific.
324 * @param timeout Waiting period for the capture to complete.
325 *
326 * @retval 0 If successful.
327 * @retval -EBUSY PWM capture already in progress.
328 * @retval -EAGAIN Waiting period timed out.
329 * @retval -EIO IO error while capturing.
330 * @retval -ERANGE If result is too large.
331 */
332 __syscall int pwm_pin_capture_cycles(const struct device *dev, uint32_t pwm,
333 pwm_flags_t flags,
334 uint32_t *period,
335 uint32_t *pulse,
336 k_timeout_t timeout);
337
338 /**
339 * @brief Get the clock rate (cycles per second) for a single PWM output.
340 *
341 * @param dev Pointer to the device structure for the driver instance.
342 * @param pwm PWM pin.
343 * @param cycles Pointer to the memory to store clock rate (cycles per sec).
344 * HW specific.
345 *
346 * @retval 0 If successful.
347 * @retval Negative errno code if failure.
348 */
349 __syscall int pwm_get_cycles_per_sec(const struct device *dev, uint32_t pwm,
350 uint64_t *cycles);
351
z_impl_pwm_get_cycles_per_sec(const struct device * dev,uint32_t pwm,uint64_t * cycles)352 static inline int z_impl_pwm_get_cycles_per_sec(const struct device *dev,
353 uint32_t pwm,
354 uint64_t *cycles)
355 {
356 struct pwm_driver_api *api;
357
358 api = (struct pwm_driver_api *)dev->api;
359 return api->get_cycles_per_sec(dev, pwm, cycles);
360 }
361
362 /**
363 * @brief Set the period and pulse width for a single PWM output.
364 *
365 * @param dev Pointer to the device structure for the driver instance.
366 * @param pwm PWM pin.
367 * @param period Period (in microseconds) set to the PWM.
368 * @param pulse Pulse width (in microseconds) set to the PWM.
369 * @param flags Flags for pin configuration (polarity).
370 *
371 * @retval 0 If successful.
372 * @retval Negative errno code if failure.
373 */
pwm_pin_set_usec(const struct device * dev,uint32_t pwm,uint32_t period,uint32_t pulse,pwm_flags_t flags)374 static inline int pwm_pin_set_usec(const struct device *dev, uint32_t pwm,
375 uint32_t period, uint32_t pulse,
376 pwm_flags_t flags)
377 {
378 uint64_t period_cycles, pulse_cycles, cycles_per_sec;
379
380 if (pwm_get_cycles_per_sec(dev, pwm, &cycles_per_sec) != 0) {
381 return -EIO;
382 }
383
384 period_cycles = (period * cycles_per_sec) / USEC_PER_SEC;
385 if (period_cycles >= ((uint64_t)1 << 32)) {
386 return -ENOTSUP;
387 }
388
389 pulse_cycles = (pulse * cycles_per_sec) / USEC_PER_SEC;
390 if (pulse_cycles >= ((uint64_t)1 << 32)) {
391 return -ENOTSUP;
392 }
393
394 return pwm_pin_set_cycles(dev, pwm, (uint32_t)period_cycles,
395 (uint32_t)pulse_cycles, flags);
396 }
397
398 /**
399 * @brief Set the period and pulse width for a single PWM output.
400 *
401 * @param dev Pointer to the device structure for the driver instance.
402 * @param pwm PWM pin.
403 * @param period Period (in nanoseconds) set to the PWM.
404 * @param pulse Pulse width (in nanoseconds) set to the PWM.
405 * @param flags Flags for pin configuration (polarity).
406 *
407 * @retval 0 If successful.
408 * @retval Negative errno code if failure.
409 */
pwm_pin_set_nsec(const struct device * dev,uint32_t pwm,uint32_t period,uint32_t pulse,pwm_flags_t flags)410 static inline int pwm_pin_set_nsec(const struct device *dev, uint32_t pwm,
411 uint32_t period, uint32_t pulse,
412 pwm_flags_t flags)
413 {
414 uint64_t period_cycles, pulse_cycles, cycles_per_sec;
415
416 if (pwm_get_cycles_per_sec(dev, pwm, &cycles_per_sec) != 0) {
417 return -EIO;
418 }
419
420 period_cycles = (period * cycles_per_sec) / NSEC_PER_SEC;
421 if (period_cycles >= ((uint64_t)1 << 32)) {
422 return -ENOTSUP;
423 }
424
425 pulse_cycles = (pulse * cycles_per_sec) / NSEC_PER_SEC;
426 if (pulse_cycles >= ((uint64_t)1 << 32)) {
427 return -ENOTSUP;
428 }
429
430 return pwm_pin_set_cycles(dev, pwm, (uint32_t)period_cycles,
431 (uint32_t)pulse_cycles, flags);
432 }
433
434 /**
435 * @brief Convert from PWM cycles to microseconds.
436 *
437 * @param dev Pointer to the device structure for the driver instance.
438 * @param pwm PWM pin.
439 * @param cycles Cycles to be converted.
440 * @param usec Pointer to the memory to store calculated usec.
441 *
442 * @retval 0 If successful.
443 * @retval -EIO If cycles per second cannot be determined.
444 * @retval -ERANGE If result is too large.
445 */
pwm_pin_cycles_to_usec(const struct device * dev,uint32_t pwm,uint32_t cycles,uint64_t * usec)446 static inline int pwm_pin_cycles_to_usec(const struct device *dev, uint32_t pwm,
447 uint32_t cycles, uint64_t *usec)
448 {
449 uint64_t cycles_per_sec;
450 uint64_t temp;
451
452 if (pwm_get_cycles_per_sec(dev, pwm, &cycles_per_sec) != 0) {
453 return -EIO;
454 }
455
456 if (u64_mul_overflow(cycles, (uint64_t)USEC_PER_SEC, &temp)) {
457 return -ERANGE;
458 }
459
460 *usec = temp / cycles_per_sec;
461
462 return 0;
463 }
464
465 /**
466 * @brief Convert from PWM cycles to nanoseconds.
467 *
468 * @param dev Pointer to the device structure for the driver instance.
469 * @param pwm PWM pin.
470 * @param cycles Cycles to be converted.
471 * @param nsec Pointer to the memory to store the calculated nsec.
472 *
473 * @retval 0 If successful.
474 * @retval -EIO If cycles per second cannot be determined.
475 * @retval -ERANGE If result is too large.
476 */
pwm_pin_cycles_to_nsec(const struct device * dev,uint32_t pwm,uint32_t cycles,uint64_t * nsec)477 static inline int pwm_pin_cycles_to_nsec(const struct device *dev, uint32_t pwm,
478 uint32_t cycles, uint64_t *nsec)
479 {
480 uint64_t cycles_per_sec;
481 uint64_t temp;
482
483 if (pwm_get_cycles_per_sec(dev, pwm, &cycles_per_sec) != 0) {
484 return -EIO;
485 }
486
487 if (u64_mul_overflow(cycles, (uint64_t)NSEC_PER_SEC, &temp)) {
488 return -ERANGE;
489 }
490
491 *nsec = temp / cycles_per_sec;
492
493 return 0;
494 }
495
496 /**
497 * @brief Capture a single PWM period/pulse width in microseconds for a single
498 * PWM input.
499 *
500 * This API function wraps calls to @a pwm_pin_capture_cycles() and @a
501 * pwm_pin_cycles_to_usec() and passes the capture result to the caller. The
502 * function is blocking until either the PWM capture is completed or a timeout
503 * occurs.
504 *
505 * @note @kconfig{CONFIG_PWM_CAPTURE} must be selected for this function to be
506 * available.
507 *
508 * @param dev Pointer to the device structure for the driver instance.
509 * @param pwm PWM pin.
510 * @param flags PWM capture flags.
511 * @param period Pointer to the memory to store the captured PWM period width
512 * (in usec).
513 * @param pulse Pointer to the memory to store the captured PWM pulse width (in
514 * usec).
515 * @param timeout Waiting period for the capture to complete.
516 *
517 * @retval 0 If successful.
518 * @retval -EBUSY PWM capture already in progress.
519 * @retval -EAGAIN Waiting period timed out.
520 * @retval -EIO IO error while capturing.
521 * @retval -ERANGE If result is too large.
522 */
pwm_pin_capture_usec(const struct device * dev,uint32_t pwm,pwm_flags_t flags,uint64_t * period,uint64_t * pulse,k_timeout_t timeout)523 static inline int pwm_pin_capture_usec(const struct device *dev, uint32_t pwm,
524 pwm_flags_t flags,
525 uint64_t *period,
526 uint64_t *pulse,
527 k_timeout_t timeout)
528 {
529 uint32_t period_cycles;
530 uint32_t pulse_cycles;
531 int err;
532
533 err = pwm_pin_capture_cycles(dev, pwm, flags, &period_cycles,
534 &pulse_cycles, timeout);
535 if (err) {
536 return err;
537 }
538
539 err = pwm_pin_cycles_to_usec(dev, pwm, period_cycles, period);
540 if (err) {
541 return err;
542 }
543
544 err = pwm_pin_cycles_to_usec(dev, pwm, pulse_cycles, pulse);
545 if (err) {
546 return err;
547 }
548
549 return 0;
550 }
551
552 /**
553 * @brief Capture a single PWM period/pulse width in nanoseconds for a single
554 * PWM input.
555 *
556 * This API function wraps calls to @a pwm_pin_capture_cycles() and @a
557 * pwm_pin_cycles_to_nsec() and passes the capture result to the caller. The
558 * function is blocking until either the PWM capture is completed or a timeout
559 * occurs.
560 *
561 * @note @kconfig{CONFIG_PWM_CAPTURE} must be selected for this function to be
562 * available.
563 *
564 * @param dev Pointer to the device structure for the driver instance.
565 * @param pwm PWM pin.
566 * @param flags PWM capture flags.
567 * @param period Pointer to the memory to store the captured PWM period width
568 * (in nsec).
569 * @param pulse Pointer to the memory to store the captured PWM pulse width (in
570 * nsec).
571 * @param timeout Waiting period for the capture to complete.
572 *
573 * @retval 0 If successful.
574 * @retval -EBUSY PWM capture already in progress.
575 * @retval -EAGAIN Waiting period timed out.
576 * @retval -EIO IO error while capturing.
577 * @retval -ERANGE If result is too large.
578 */
pwm_pin_capture_nsec(const struct device * dev,uint32_t pwm,pwm_flags_t flags,uint64_t * period,uint64_t * pulse,k_timeout_t timeout)579 static inline int pwm_pin_capture_nsec(const struct device *dev, uint32_t pwm,
580 pwm_flags_t flags,
581 uint64_t *period,
582 uint64_t *pulse,
583 k_timeout_t timeout)
584 {
585 uint32_t period_cycles;
586 uint32_t pulse_cycles;
587 int err;
588
589 err = pwm_pin_capture_cycles(dev, pwm, flags, &period_cycles,
590 &pulse_cycles, timeout);
591 if (err) {
592 return err;
593 }
594
595 err = pwm_pin_cycles_to_nsec(dev, pwm, period_cycles, period);
596 if (err) {
597 return err;
598 }
599
600 err = pwm_pin_cycles_to_nsec(dev, pwm, pulse_cycles, pulse);
601 if (err) {
602 return err;
603 }
604
605 return 0;
606 }
607
608 #ifdef __cplusplus
609 }
610 #endif
611
612 /**
613 * @}
614 */
615
616 #include <syscalls/pwm.h>
617
618 #endif /* ZEPHYR_INCLUDE_DRIVERS_PWM_H_ */
619