1 /*
2 * Copyright (c) 2021, NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7
8 #define DT_DRV_COMPAT nxp_sctimer_pwm
9
10 #include <errno.h>
11 #include <drivers/pwm.h>
12 #include <fsl_sctimer.h>
13 #include <fsl_clock.h>
14
15 #define LOG_LEVEL CONFIG_PWM_LOG_LEVEL
16 #include <logging/log.h>
17 LOG_MODULE_REGISTER(pwm_mcux_sctimer);
18
19 #define CHANNEL_COUNT FSL_FEATURE_SCT_NUMBER_OF_OUTPUTS
20
21 struct pwm_mcux_sctimer_config {
22 SCT_Type *base;
23 uint32_t prescale;
24 };
25
26 struct pwm_mcux_sctimer_data {
27 uint32_t period_cycles[CHANNEL_COUNT];
28 uint32_t event_number[CHANNEL_COUNT];
29 sctimer_pwm_signal_param_t channel[CHANNEL_COUNT];
30 };
31
mcux_sctimer_pwm_pin_set(const struct device * dev,uint32_t pwm,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)32 static int mcux_sctimer_pwm_pin_set(const struct device *dev, uint32_t pwm,
33 uint32_t period_cycles, uint32_t pulse_cycles,
34 pwm_flags_t flags)
35 {
36 const struct pwm_mcux_sctimer_config *config = dev->config;
37 struct pwm_mcux_sctimer_data *data = dev->data;
38 uint8_t duty_cycle;
39
40 if (pwm >= CHANNEL_COUNT) {
41 LOG_ERR("Invalid channel");
42 return -EINVAL;
43 }
44
45 if ((period_cycles == 0) || (pulse_cycles > period_cycles)) {
46 LOG_ERR("Invalid combination: period_cycles=%u, "
47 "pulse_cycles=%u", period_cycles, pulse_cycles);
48 return -EINVAL;
49 }
50
51 if ((flags & PWM_POLARITY_INVERTED) == 0) {
52 data->channel[pwm].level = kSCTIMER_HighTrue;
53 } else {
54 data->channel[pwm].level = kSCTIMER_LowTrue;
55 }
56
57 duty_cycle = 100 * pulse_cycles / period_cycles;
58
59 if (duty_cycle == 0) {
60 SCT_Type *base = config->base;
61
62 SCTIMER_StopTimer(config->base, kSCTIMER_Counter_U);
63
64 /* Set the output to inactive State */
65 if (data->channel[pwm].level == kSCTIMER_HighTrue) {
66 base->OUTPUT &= ~(1UL << pwm);
67 } else {
68 base->OUTPUT |= (1UL << pwm);
69 }
70
71 /* Make sure the PWM is setup */
72 if (data->period_cycles[pwm] != 0) {
73 SCTIMER_StartTimer(config->base, kSCTIMER_Counter_U);
74 }
75
76 return 0;
77 }
78
79 if (period_cycles != data->period_cycles[pwm]) {
80 uint32_t clock_freq;
81 uint32_t pwm_freq;
82
83 data->period_cycles[pwm] = period_cycles;
84
85 /*
86 * Do not divide by the prescale factor as this is accounted for in
87 * the SDK function
88 */
89 clock_freq = CLOCK_GetFreq(kCLOCK_BusClk);
90 pwm_freq = (clock_freq / config->prescale) / period_cycles;
91
92 if (pwm_freq == 0) {
93 LOG_ERR("Could not set up pwm_freq=%d", pwm_freq);
94 return -EINVAL;
95 }
96
97 SCTIMER_StopTimer(config->base, kSCTIMER_Counter_U);
98
99 LOG_DBG("SETUP dutycycle to %u\n", duty_cycle);
100 data->channel[pwm].dutyCyclePercent = duty_cycle;
101 if (SCTIMER_SetupPwm(config->base, &data->channel[pwm], kSCTIMER_EdgeAlignedPwm,
102 pwm_freq, clock_freq, &data->event_number[pwm]) == kStatus_Fail) {
103 LOG_ERR("Could not set up pwm");
104 return -ENOTSUP;
105 }
106
107 SCTIMER_StartTimer(config->base, kSCTIMER_Counter_U);
108 } else {
109 SCTIMER_UpdatePwmDutycycle(config->base, pwm, duty_cycle, data->event_number[pwm]);
110 }
111
112 return 0;
113 }
114
mcux_sctimer_pwm_get_cycles_per_sec(const struct device * dev,uint32_t pwm,uint64_t * cycles)115 static int mcux_sctimer_pwm_get_cycles_per_sec(const struct device *dev, uint32_t pwm,
116 uint64_t *cycles)
117 {
118 const struct pwm_mcux_sctimer_config *config = dev->config;
119
120 *cycles = CLOCK_GetFreq(kCLOCK_BusClk) / config->prescale;
121
122 return 0;
123 }
124
mcux_sctimer_pwm_init(const struct device * dev)125 static int mcux_sctimer_pwm_init(const struct device *dev)
126 {
127 const struct pwm_mcux_sctimer_config *config = dev->config;
128 struct pwm_mcux_sctimer_data *data = dev->data;
129 sctimer_config_t pwm_config;
130 status_t status;
131 int i;
132
133 SCTIMER_GetDefaultConfig(&pwm_config);
134 /* Divide the SCT clock by 8 */
135 pwm_config.prescale_l = config->prescale - 1;
136
137 status = SCTIMER_Init(config->base, &pwm_config);
138 if (status != kStatus_Success) {
139 LOG_ERR("Unable to init PWM");
140 return -EIO;
141 }
142
143 for (i = 0; i < CHANNEL_COUNT; i++) {
144 data->channel[i].output = i;
145 data->channel[i].level = kSCTIMER_HighTrue;
146 data->channel[i].dutyCyclePercent = 0;
147 data->period_cycles[i] = 0;
148 }
149
150 return 0;
151 }
152
153 static const struct pwm_driver_api pwm_mcux_sctimer_driver_api = {
154 .pin_set = mcux_sctimer_pwm_pin_set,
155 .get_cycles_per_sec = mcux_sctimer_pwm_get_cycles_per_sec,
156 };
157
158 #define PWM_MCUX_SCTIMER_DEVICE_INIT_MCUX(n) \
159 static struct pwm_mcux_sctimer_data pwm_mcux_sctimer_data_##n; \
160 \
161 static const struct pwm_mcux_sctimer_config pwm_mcux_sctimer_config_##n = { \
162 .base = (SCT_Type *)DT_INST_REG_ADDR(n), \
163 .prescale = DT_INST_PROP(n, prescaler), \
164 }; \
165 \
166 DEVICE_DT_INST_DEFINE(n, \
167 mcux_sctimer_pwm_init, \
168 NULL, \
169 &pwm_mcux_sctimer_data_##n, \
170 &pwm_mcux_sctimer_config_##n, \
171 POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
172 &pwm_mcux_sctimer_driver_api);
173
174 DT_INST_FOREACH_STATUS_OKAY(PWM_MCUX_SCTIMER_DEVICE_INIT_MCUX)
175