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