@@ -24,15 +24,25 @@ LOG_MODULE_REGISTER(pwm_mspm0, CONFIG_PWM_LOG_LEVEL);
24
24
/* capture and compare block count per timer */
25
25
#define MSPM0_TIMER_CC_COUNT 2
26
26
#define MSPM0_TIMER_CC_MAX 4
27
+ #define MSPM0_CC_INTR_BIT_OFFSET 4
28
+
29
+ enum mspm0_capture_mode {
30
+ CMODE_EDGE_TIME ,
31
+ CMODE_PULSE_WIDTH
32
+ };
27
33
28
34
struct pwm_mspm0_config {
29
35
const struct mspm0_sys_clock clock_subsys ;
30
36
const struct pinctrl_dev_config * pincfg ;
31
37
const struct device * clock_dev ;
32
38
GPTIMER_Regs * base ;
33
39
DL_Timer_ClockConfig clk_config ;
40
+ #ifdef CONFIG_PWM_CAPTURE
41
+ void (* irq_config_func )(const struct device * dev );
42
+ #endif
34
43
uint8_t cc_idx [MSPM0_TIMER_CC_MAX ];
35
44
uint8_t cc_idx_cnt ;
45
+ bool is_capture ;
36
46
};
37
47
38
48
struct pwm_mspm0_data {
@@ -41,6 +51,14 @@ struct pwm_mspm0_data {
41
51
struct k_mutex lock ;
42
52
43
53
DL_TIMER_PWM_MODE out_mode ;
54
+ #ifdef CONFIG_PWM_CAPTURE
55
+ uint32_t last_sample ;
56
+ enum mspm0_capture_mode cmode ;
57
+ pwm_capture_callback_handler_t callback ;
58
+ pwm_flags_t flags ;
59
+ void * user_data ;
60
+ bool is_synced ;
61
+ #endif
44
62
};
45
63
46
64
static void mspm0_setup_pwm_out (const struct pwm_mspm0_config * config ,
@@ -137,6 +155,197 @@ static int mspm0_pwm_get_cycles_per_sec(const struct device *dev,
137
155
return 0 ;
138
156
}
139
157
158
+ #ifdef CONFIG_PWM_CAPTURE
159
+ #define MSPM0_CTRCTL_CAC_CCCTL_ACOND (x ) (x << 10)
160
+
161
+ static void mspm0_set_combined_mode (const struct pwm_mspm0_config * config ,
162
+ struct pwm_mspm0_data * data )
163
+ {
164
+ DL_Timer_setLoadValue (config -> base , data -> period );
165
+ DL_Timer_setCaptureCompareInput (config -> base , 0 ,
166
+ ((config -> cc_idx [0 ] & 0x1 ) ?
167
+ DL_TIMER_CC_IN_SEL_CCPX : DL_TIMER_CC_IN_SEL_CCP0 ),
168
+ config -> cc_idx [0 ]);
169
+
170
+ DL_Timer_setCaptureCompareInput (config -> base , 0 ,
171
+ GPTIMER_IFCTL_01_ISEL_CCPX_INPUT_PAIR ,
172
+ (config -> cc_idx [0 ] ^ 1 ));
173
+
174
+ DL_Timer_setCaptureCompareCtl (config -> base ,
175
+ DL_TIMER_CC_MODE_CAPTURE ,
176
+ DL_TIMER_CC_CCOND_TRIG_FALL ,
177
+ config -> cc_idx [0 ]);
178
+
179
+ DL_Timer_setCaptureCompareCtl (config -> base ,
180
+ DL_TIMER_CC_MODE_CAPTURE ,
181
+ DL_TIMER_CC_CCOND_TRIG_RISE ,
182
+ (config -> cc_idx [0 ] ^ 1 ));
183
+
184
+ DL_Timer_setCCPDirection (config -> base , DL_TIMER_CC0_INPUT );
185
+
186
+ DL_Timer_setCounterControl (config -> base ,
187
+ DL_TIMER_CZC_CCCTL0_ZCOND ,
188
+ MSPM0_CTRCTL_CAC_CCCTL_ACOND (config -> cc_idx [0 ]),
189
+ DL_TIMER_CLC_CCCTL0_LCOND );
190
+
191
+ DL_Timer_setCounterRepeatMode (config -> base , DL_TIMER_REPEAT_MODE_ENABLED );
192
+ DL_Timer_setCounterMode (config -> base , DL_TIMER_COUNT_MODE_DOWN );
193
+ }
194
+
195
+ static void mspm0_setup_capture (const struct device * dev ,
196
+ const struct pwm_mspm0_config * config ,
197
+ struct pwm_mspm0_data * data )
198
+ {
199
+ if (data -> cmode == CMODE_EDGE_TIME ) {
200
+ DL_Timer_CaptureConfig cc_cfg = { 0 };
201
+
202
+ cc_cfg .inputChan = config -> cc_idx [0 ];
203
+ cc_cfg .period = data -> period ;
204
+ cc_cfg .edgeCaptMode = DL_TIMER_CAPTURE_EDGE_DETECTION_MODE_RISING ;
205
+
206
+ DL_Timer_initCaptureMode (config -> base , & cc_cfg );
207
+ } else {
208
+ mspm0_set_combined_mode (config , data );
209
+ }
210
+
211
+ DL_Timer_enableClock (config -> base );
212
+ config -> irq_config_func (dev );
213
+ }
214
+
215
+ static int mspm0_capture_configure (const struct device * dev ,
216
+ uint32_t channel ,
217
+ pwm_flags_t flags ,
218
+ pwm_capture_callback_handler_t cb ,
219
+ void * user_data )
220
+ {
221
+ const struct pwm_mspm0_config * config = dev -> config ;
222
+ struct pwm_mspm0_data * data = dev -> data ;
223
+ uint32_t intr_mask ;
224
+
225
+ if (config -> is_capture != true ||
226
+ channel != 0 ) {
227
+ LOG_ERR ("Invalid channel %d" , channel );
228
+ return - EINVAL ;
229
+ }
230
+
231
+ switch (flags & PWM_CAPTURE_TYPE_MASK ) {
232
+ case PWM_CAPTURE_TYPE_PULSE :
233
+ case PWM_CAPTURE_TYPE_BOTH :
234
+ case PWM_CAPTURE_TYPE_PERIOD :
235
+ /* CCD1/CCD0 event for capture index 0/1 respectively */
236
+ intr_mask = BIT (!(config -> cc_idx [0 ]) + MSPM0_CC_INTR_BIT_OFFSET ) |
237
+ DL_TIMER_INTERRUPT_ZERO_EVENT ;
238
+ break ;
239
+
240
+ default :
241
+ /* edge time event */
242
+ intr_mask = BIT (config -> cc_idx [0 ] + MSPM0_CC_INTR_BIT_OFFSET );
243
+ }
244
+
245
+ k_mutex_lock (& data -> lock , K_FOREVER );
246
+
247
+ /* If interrupt is enabled --> channel is on-going */
248
+ if (DL_Timer_getEnabledInterrupts (config -> base , intr_mask )) {
249
+ LOG_ERR ("Channel %d is busy" , channel );
250
+ k_mutex_unlock (& data -> lock );
251
+ return - EBUSY ;
252
+ }
253
+
254
+ data -> flags = flags ;
255
+ data -> callback = cb ;
256
+ data -> user_data = user_data ;
257
+
258
+ k_mutex_unlock (& data -> lock );
259
+
260
+ return 0 ;
261
+ }
262
+
263
+ static int mspm0_capture_enable (const struct device * dev , uint32_t channel )
264
+ {
265
+ const struct pwm_mspm0_config * config = dev -> config ;
266
+ struct pwm_mspm0_data * data = dev -> data ;
267
+ uint32_t intr_mask ;
268
+
269
+ if (config -> is_capture != true ||
270
+ channel != 0 ) {
271
+ LOG_ERR ("Invalid capture mode or channel" );
272
+ return - EINVAL ;
273
+ }
274
+
275
+ if (!data -> callback ) {
276
+ LOG_ERR ("Callback is not configured" );
277
+ return - EINVAL ;
278
+ }
279
+
280
+ switch (data -> flags & PWM_CAPTURE_TYPE_MASK ) {
281
+ case PWM_CAPTURE_TYPE_PULSE :
282
+ case PWM_CAPTURE_TYPE_BOTH :
283
+ case PWM_CAPTURE_TYPE_PERIOD :
284
+ /* CCD1/CCD0 event for capture index 0/1 respectively */
285
+ intr_mask = BIT (!(config -> cc_idx [0 ]) + MSPM0_CC_INTR_BIT_OFFSET ) |
286
+ DL_TIMER_INTERRUPT_ZERO_EVENT ;
287
+ break ;
288
+
289
+ default :
290
+ /* edge time event */
291
+ intr_mask = BIT (config -> cc_idx [0 ] + MSPM0_CC_INTR_BIT_OFFSET );
292
+ }
293
+
294
+ k_mutex_lock (& data -> lock , K_FOREVER );
295
+
296
+ /* If interrupt is enabled --> channel is on-going */
297
+ if (DL_Timer_getEnabledInterrupts (config -> base , intr_mask )) {
298
+ LOG_ERR ("Channel %d is busy" , channel );
299
+ k_mutex_unlock (& data -> lock );
300
+ return - EBUSY ;
301
+ }
302
+
303
+ DL_Timer_setTimerCount (config -> base , data -> period );
304
+ DL_Timer_startCounter (config -> base );
305
+ DL_Timer_clearInterruptStatus (config -> base , intr_mask );
306
+ DL_Timer_enableInterrupt (config -> base , intr_mask );
307
+
308
+ k_mutex_unlock (& data -> lock );
309
+ return 0 ;
310
+ }
311
+
312
+ static int mspm0_capture_disable (const struct device * dev , uint32_t channel )
313
+ {
314
+ const struct pwm_mspm0_config * config = dev -> config ;
315
+ struct pwm_mspm0_data * data = dev -> data ;
316
+ uint32_t intr_mask ;
317
+
318
+ if (config -> is_capture != true ||
319
+ channel != 0 ) {
320
+ LOG_ERR ("Invalid channel" );
321
+ return - EINVAL ;
322
+ }
323
+
324
+ switch (data -> flags & PWM_CAPTURE_TYPE_MASK ) {
325
+ case PWM_CAPTURE_TYPE_PULSE :
326
+ case PWM_CAPTURE_TYPE_BOTH :
327
+ case PWM_CAPTURE_TYPE_PERIOD :
328
+ /* CCD1/CCD0 event for capture index 0/1 respectively */
329
+ intr_mask = BIT (!(config -> cc_idx [0 ]) + MSPM0_CC_INTR_BIT_OFFSET ) |
330
+ DL_TIMER_INTERRUPT_ZERO_EVENT ;
331
+ break ;
332
+
333
+ default :
334
+ /* edge time event */
335
+ intr_mask = BIT (config -> cc_idx [0 ] + MSPM0_CC_INTR_BIT_OFFSET );
336
+ }
337
+
338
+ k_mutex_lock (& data -> lock , K_FOREVER );
339
+
340
+ DL_Timer_disableInterrupt (config -> base , intr_mask );
341
+ DL_Timer_stopCounter (config -> base );
342
+ data -> is_synced = false;
343
+ k_mutex_unlock (& data -> lock );
344
+
345
+ return 0 ;
346
+ }
347
+ #endif
348
+
140
349
static int pwm_mspm0_init (const struct device * dev )
141
350
{
142
351
const struct pwm_mspm0_config * config = dev -> config ;
@@ -163,18 +372,116 @@ static int pwm_mspm0_init(const struct device *dev)
163
372
delay_cycles (CONFIG_MSPM0_PERIPH_STARTUP_DELAY );
164
373
DL_Timer_setClockConfig (config -> base ,
165
374
(DL_Timer_ClockConfig * )& config -> clk_config );
166
-
167
- mspm0_setup_pwm_out (config , data );
375
+ if (config -> is_capture ) {
376
+ #ifdef CONFIG_PWM_CAPTURE
377
+ mspm0_setup_capture (dev , config , data );
378
+ #endif
379
+ } else {
380
+ mspm0_setup_pwm_out (config , data );
381
+ }
168
382
169
383
return 0 ;
170
384
}
171
385
172
386
static const struct pwm_driver_api pwm_mspm0_driver_api = {
173
387
.set_cycles = mspm0_pwm_set_cycles ,
174
388
.get_cycles_per_sec = mspm0_pwm_get_cycles_per_sec ,
389
+ #ifdef CONFIG_PWM_CAPTURE
390
+ .configure_capture = mspm0_capture_configure ,
391
+ .enable_capture = mspm0_capture_enable ,
392
+ .disable_capture = mspm0_capture_disable ,
393
+ #endif
175
394
};
176
395
396
+ #ifdef CONFIG_PWM_CAPTURE
397
+ static void mspm0_cc_isr (const struct device * dev )
398
+ {
399
+ const struct pwm_mspm0_config * config = dev -> config ;
400
+ struct pwm_mspm0_data * data = dev -> data ;
401
+ uint32_t status ;
402
+ uint32_t cc1 = 0 ;
403
+ uint32_t cc0 = 0 ;
404
+ uint32_t period = 0 ;
405
+ uint32_t pulse = 0 ;
406
+
407
+ status = DL_Timer_getPendingInterrupt (config -> base );
408
+
409
+ switch (status ) {
410
+ case DL_TIMER_IIDX_CC0_DN :
411
+ case DL_TIMER_IIDX_CC1_DN :
412
+ break ;
413
+
414
+ /* Timer reached zero no pwm signal is detected */
415
+ case DL_TIMERG_IIDX_ZERO :
416
+ if (data -> callback &&
417
+ !(data -> flags & PWM_CAPTURE_MODE_CONTINUOUS )) {
418
+ data -> callback (dev , 0 , 0 , 0 , - ERANGE , data -> user_data );
419
+ DL_Timer_stopCounter (config -> base );
420
+ }
421
+ __fallthrough ;
422
+
423
+ default :
424
+ return ;
425
+ }
426
+
427
+ if (data -> flags & PWM_CAPTURE_TYPE_PERIOD ) {
428
+ cc1 = DL_Timer_getCaptureCompareValue (config -> base ,
429
+ config -> cc_idx [0 ] ^ 0x1 );
430
+ }
431
+
432
+ /* ignore the unsynced counter value for pwm mode */
433
+ if (data -> is_synced == false &&
434
+ data -> cmode != CMODE_EDGE_TIME ) {
435
+ data -> last_sample = cc1 ;
436
+ data -> is_synced = true;
437
+ return ;
438
+ }
439
+
440
+ if (data -> flags & PWM_CAPTURE_TYPE_PULSE ||
441
+ data -> cmode == CMODE_EDGE_TIME ) {
442
+ cc0 = DL_Timer_getCaptureCompareValue (config -> base ,
443
+ config -> cc_idx [0 ]);
444
+ }
445
+
446
+ if (!(data -> flags & PWM_CAPTURE_MODE_CONTINUOUS )) {
447
+ DL_Timer_stopCounter (config -> base );
448
+ data -> is_synced = false;
449
+ }
450
+
451
+
452
+ period = ((data -> last_sample - cc1 + UINT16_MAX ) % UINT16_MAX );
453
+ pulse = ((data -> last_sample - cc0 + UINT16_MAX ) % UINT16_MAX );
454
+
455
+ /* fixme: random intermittent cc0 is greater than cc1 due to capture block error */
456
+ if (pulse > period ) {
457
+ pulse -= period ;
458
+ }
459
+
460
+ if (data -> callback && period ) {
461
+ data -> callback (dev , 0 , period , pulse , 0 , data -> user_data );
462
+ }
463
+
464
+ data -> last_sample = cc1 ;
465
+ }
466
+
467
+ #define MSP_CC_IRQ_REGISTER (n ) \
468
+ static void mspm0_cc_## n ##_irq_register(const struct device *dev) \
469
+ { \
470
+ const struct pwm_mspm0_config *config = dev->config; \
471
+ if (!config->is_capture) { \
472
+ return; \
473
+ } \
474
+ IRQ_CONNECT(DT_IRQN(DT_INST_PARENT(n)), \
475
+ DT_IRQ(DT_INST_PARENT(n), priority), mspm0_cc_isr, \
476
+ DEVICE_DT_INST_GET(n), 0); \
477
+ irq_enable(DT_IRQN(DT_INST_PARENT(n))); \
478
+ }
479
+ #else
480
+ #define MSP_CC_IRQ_REGISTER (n )
481
+ #endif
482
+
177
483
#define MSPM0_PWM_MODE (mode ) DT_CAT(DL_TIMER_PWM_MODE_, mode)
484
+ #define MSPM0_CAPTURE_MODE (mode ) DT_CAT(CMODE_, mode)
178
485
#define MSPM0_CLK_DIV (div ) DT_CAT(DL_TIMER_CLOCK_DIVIDE_, div)
179
486
180
487
#define MSPM0_CC_IDX_ARRAY (node_id , prop , idx ) \
@@ -184,13 +491,20 @@ static const struct pwm_driver_api pwm_mspm0_driver_api = {
184
491
.out_mode = MSPM0_PWM_MODE(DT_STRING_TOKEN(DT_DRV_INST(n), \
185
492
ti_pwm_mode)),
186
493
494
+ #define MSPM0_CAPTURE_DATA (n ) \
495
+ IF_ENABLED(CONFIG_PWM_CAPTURE, \
496
+ (.cmode = MSPM0_CAPTURE_MODE(DT_STRING_TOKEN(DT_DRV_INST(n), ti_cc_mode)),))
497
+
187
498
#define PWM_DEVICE_INIT_MSPM0 (n ) \
188
499
static struct pwm_mspm0_data pwm_mspm0_data_ ## n = { \
189
500
.period = DT_PROP(DT_DRV_INST(n), ti_period), \
190
501
COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), ti_pwm_mode), \
191
502
(MSPM0_PWM_DATA(n)), ()) \
503
+ COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), ti_cc_mode), \
504
+ (MSPM0_CAPTURE_DATA(n)), ()) \
192
505
}; \
193
506
PINCTRL_DT_INST_DEFINE(n); \
507
+ COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), ti_cc_mode), (MSP_CC_IRQ_REGISTER(n)), ()) \
194
508
\
195
509
static const struct pwm_mspm0_config pwm_mspm0_config_ ## n = { \
196
510
.base = (GPTIMER_Regs *)DT_REG_ADDR(DT_INST_PARENT(n)), \
@@ -213,6 +527,10 @@ static const struct pwm_driver_api pwm_mspm0_driver_api = {
213
527
ti_clk_div)), \
214
528
.prescale = DT_PROP(DT_INST_PARENT(n), ti_clk_prescaler),\
215
529
}, \
530
+ .is_capture = DT_NODE_HAS_PROP(DT_DRV_INST(n), ti_cc_mode), \
531
+ IF_ENABLED(CONFIG_PWM_CAPTURE, \
532
+ (.irq_config_func = COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), ti_cc_mode), \
533
+ (mspm0_cc_## n ##_irq_register), (NULL)))) \
216
534
}; \
217
535
\
218
536
DEVICE_DT_INST_DEFINE(n, \
0 commit comments