Skip to content

Commit e051ec2

Browse files
ssekar15nashif
authored andcommitted
drivers: pwm: Add a support for TI MSPM0 PWM capture
TI MSPM0 timer module has capture block used to capture timings of input signal. Add a support for TI MSPM0 PWM capture. Signed-off-by: Saravanan Sekar <[email protected]>
1 parent 9709452 commit e051ec2

File tree

2 files changed

+332
-2
lines changed

2 files changed

+332
-2
lines changed

drivers/pwm/pwm_mspm0.c

Lines changed: 320 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,25 @@ LOG_MODULE_REGISTER(pwm_mspm0, CONFIG_PWM_LOG_LEVEL);
2424
/* capture and compare block count per timer */
2525
#define MSPM0_TIMER_CC_COUNT 2
2626
#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+
};
2733

2834
struct pwm_mspm0_config {
2935
const struct mspm0_sys_clock clock_subsys;
3036
const struct pinctrl_dev_config *pincfg;
3137
const struct device *clock_dev;
3238
GPTIMER_Regs *base;
3339
DL_Timer_ClockConfig clk_config;
40+
#ifdef CONFIG_PWM_CAPTURE
41+
void (*irq_config_func)(const struct device *dev);
42+
#endif
3443
uint8_t cc_idx[MSPM0_TIMER_CC_MAX];
3544
uint8_t cc_idx_cnt;
45+
bool is_capture;
3646
};
3747

3848
struct pwm_mspm0_data {
@@ -41,6 +51,14 @@ struct pwm_mspm0_data {
4151
struct k_mutex lock;
4252

4353
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
4462
};
4563

4664
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,
137155
return 0;
138156
}
139157

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+
140349
static int pwm_mspm0_init(const struct device *dev)
141350
{
142351
const struct pwm_mspm0_config *config = dev->config;
@@ -163,18 +372,116 @@ static int pwm_mspm0_init(const struct device *dev)
163372
delay_cycles(CONFIG_MSPM0_PERIPH_STARTUP_DELAY);
164373
DL_Timer_setClockConfig(config->base,
165374
(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+
}
168382

169383
return 0;
170384
}
171385

172386
static const struct pwm_driver_api pwm_mspm0_driver_api = {
173387
.set_cycles = mspm0_pwm_set_cycles,
174388
.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
175394
};
176395

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+
177483
#define MSPM0_PWM_MODE(mode) DT_CAT(DL_TIMER_PWM_MODE_, mode)
484+
#define MSPM0_CAPTURE_MODE(mode) DT_CAT(CMODE_, mode)
178485
#define MSPM0_CLK_DIV(div) DT_CAT(DL_TIMER_CLOCK_DIVIDE_, div)
179486

180487
#define MSPM0_CC_IDX_ARRAY(node_id, prop, idx) \
@@ -184,13 +491,20 @@ static const struct pwm_driver_api pwm_mspm0_driver_api = {
184491
.out_mode = MSPM0_PWM_MODE(DT_STRING_TOKEN(DT_DRV_INST(n), \
185492
ti_pwm_mode)),
186493

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+
187498
#define PWM_DEVICE_INIT_MSPM0(n) \
188499
static struct pwm_mspm0_data pwm_mspm0_data_ ## n = { \
189500
.period = DT_PROP(DT_DRV_INST(n), ti_period), \
190501
COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), ti_pwm_mode), \
191502
(MSPM0_PWM_DATA(n)), ()) \
503+
COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), ti_cc_mode), \
504+
(MSPM0_CAPTURE_DATA(n)), ()) \
192505
}; \
193506
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)), ()) \
194508
\
195509
static const struct pwm_mspm0_config pwm_mspm0_config_ ## n = { \
196510
.base = (GPTIMER_Regs *)DT_REG_ADDR(DT_INST_PARENT(n)), \
@@ -213,6 +527,10 @@ static const struct pwm_driver_api pwm_mspm0_driver_api = {
213527
ti_clk_div)), \
214528
.prescale = DT_PROP(DT_INST_PARENT(n), ti_clk_prescaler),\
215529
}, \
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)))) \
216534
}; \
217535
\
218536
DEVICE_DT_INST_DEFINE(n, \

0 commit comments

Comments
 (0)