12
12
#include <zephyr/drivers/pwm.h>
13
13
#include <zephyr/drivers/pinctrl.h>
14
14
#include <zephyr/drivers/clock_control/atmel_sam_pmc.h>
15
+ #include <zephyr/irq.h>
15
16
16
17
#include <zephyr/logging/log.h>
17
18
@@ -35,10 +36,19 @@ struct sam_pwm_config {
35
36
const struct pinctrl_dev_config * pcfg ;
36
37
uint8_t prescaler ;
37
38
uint8_t divider ;
39
+ #ifdef CONFIG_PWM_INTERRUPT
40
+ void (* irq_config )(void );
41
+ #endif /* CONFIG_PWM_INTERRUPT */
38
42
};
39
43
40
- static int sam_pwm_get_cycles_per_sec (const struct device * dev ,
41
- uint32_t channel , uint64_t * cycles )
44
+ struct sam_pwm_data {
45
+ #ifdef CONFIG_PWM_INTERRUPT
46
+ pwm_interrupt_callback_handler_t callback ;
47
+ void * user_data ;
48
+ #endif /* CONFIG_PWM_INTERRUPT */
49
+ };
50
+
51
+ static int sam_pwm_get_cycles_per_sec (const struct device * dev , uint32_t channel , uint64_t * cycles )
42
52
{
43
53
const struct sam_pwm_config * config = dev -> config ;
44
54
uint8_t prescaler = config -> prescaler ;
@@ -99,6 +109,88 @@ static int sam_pwm_set_cycles(const struct device *dev, uint32_t channel,
99
109
return 0 ;
100
110
}
101
111
112
+ #ifdef CONFIG_PWM_INTERRUPT
113
+ static void sam_pwm_isr (const struct device * dev )
114
+ {
115
+ const struct sam_pwm_config * config = dev -> config ;
116
+ struct sam_pwm_data * data = dev -> data ;
117
+ Pwm * const pwm = config -> regs ;
118
+ pwm_flags_t flags ;
119
+
120
+ uint32_t status = pwm -> PWM_ISR1 ;
121
+
122
+ if (data -> callback == NULL ) {
123
+ return ;
124
+ }
125
+
126
+ for (uint32_t i = 0 ; i < PWMCH_NUM_NUMBER ; i ++ ) {
127
+ flags = 0 ;
128
+ if ((status & (PWM_ISR1_CHID0 << i )) != 0 ) {
129
+ flags |= PWM_INTERRUPT_TYPE_PERIOD ;
130
+ }
131
+ if ((status & (PWM_ISR1_FCHID0 << i )) != 0 ) {
132
+ flags |= PWM_INTERRUPT_TYPE_FAULT ;
133
+ }
134
+
135
+ if (flags > 0 ) {
136
+ data -> callback (dev , i , flags , data -> user_data );
137
+ }
138
+ }
139
+ }
140
+
141
+ static int sam_pwm_configure_interrupt (const struct device * dev ,
142
+ pwm_interrupt_callback_handler_t cb , void * user_data )
143
+ {
144
+ struct sam_pwm_data * data = dev -> data ;
145
+
146
+ data -> callback = cb ;
147
+ data -> user_data = user_data ;
148
+
149
+ return 0 ;
150
+ }
151
+
152
+ static int sam_pwm_enable_interrupt (const struct device * dev , uint32_t channel , pwm_flags_t flags )
153
+ {
154
+ const struct sam_pwm_config * config = dev -> config ;
155
+ uint32_t pwm_ier1 = 0 ;
156
+
157
+ Pwm * const pwm = config -> regs ;
158
+
159
+ /* Dummy read to clear status register*/
160
+ (void )pwm -> PWM_ISR1 ;
161
+
162
+ if ((flags & PWM_INTERRUPT_TYPE_PERIOD ) != 0 ) {
163
+ pwm_ier1 |= (PWM_ENA_CHID0 << channel );
164
+ }
165
+ if ((flags & PWM_INTERRUPT_TYPE_FAULT ) != 0 ) {
166
+ pwm_ier1 |= (PWM_IER1_FCHID0 << channel );
167
+ }
168
+
169
+ pwm -> PWM_IER1 = pwm_ier1 ;
170
+
171
+ return 0 ;
172
+ }
173
+
174
+ static int sam_pwm_disable_interrupt (const struct device * dev , uint32_t channel , pwm_flags_t flags )
175
+ {
176
+ const struct sam_pwm_config * config = dev -> config ;
177
+ uint32_t pwm_idr1 = 0 ;
178
+
179
+ Pwm * const pwm = config -> regs ;
180
+
181
+ if ((flags & PWM_INTERRUPT_TYPE_PERIOD ) != 0 ) {
182
+ pwm_idr1 |= (PWM_IDR1_CHID0 << channel );
183
+ }
184
+ if ((flags & PWM_INTERRUPT_TYPE_FAULT ) != 0 ) {
185
+ pwm_idr1 |= (PWM_IDR1_FCHID0 << channel );
186
+ }
187
+
188
+ pwm -> PWM_IDR1 = pwm_idr1 ;
189
+
190
+ return 0 ;
191
+ }
192
+ #endif /* CONFIG_PWM_INTERRUPT */
193
+
102
194
static int sam_pwm_init (const struct device * dev )
103
195
{
104
196
const struct sam_pwm_config * config = dev -> config ;
@@ -110,6 +202,10 @@ static int sam_pwm_init(const struct device *dev)
110
202
111
203
/* FIXME: way to validate prescaler & divider */
112
204
205
+ #ifdef CONFIG_PWM_INTERRUPT
206
+ config -> irq_config ();
207
+ #endif
208
+
113
209
/* Enable PWM clock in PMC */
114
210
(void )clock_control_on (SAM_DT_PMC_CONTROLLER ,
115
211
(clock_control_subsys_t )& config -> clock_cfg );
@@ -128,23 +224,42 @@ static int sam_pwm_init(const struct device *dev)
128
224
static DEVICE_API (pwm , sam_pwm_driver_api ) = {
129
225
.set_cycles = sam_pwm_set_cycles ,
130
226
.get_cycles_per_sec = sam_pwm_get_cycles_per_sec ,
227
+ #ifdef CONFIG_PWM_INTERRUPT
228
+ .configure_interrupt = sam_pwm_configure_interrupt ,
229
+ .enable_interrupt = sam_pwm_enable_interrupt ,
230
+ .disable_interrupt = sam_pwm_disable_interrupt ,
231
+ #endif /* CONFIG_PWM_INTERRUPT */
131
232
};
132
233
234
+ #define SAM_PWM_INTERRUPT_INIT (inst ) \
235
+ static void sam_pwm_irq_config_##inst(void) \
236
+ { \
237
+ IRQ_CONNECT(DT_INST_IRQN(inst), 0, sam_pwm_isr, \
238
+ DEVICE_DT_INST_GET(inst), 0); \
239
+ irq_enable(DT_INST_IRQN(inst)); \
240
+ }
241
+
133
242
#define SAM_INST_INIT (inst ) \
134
243
PINCTRL_DT_INST_DEFINE(inst); \
244
+ \
245
+ IF_ENABLED(CONFIG_PWM_INTERRUPT, (SAM_PWM_INTERRUPT_INIT(inst)))\
246
+ \
135
247
static const struct sam_pwm_config sam_pwm_config_##inst = { \
136
248
.regs = (Pwm *)DT_INST_REG_ADDR(inst), \
137
249
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
138
250
.clock_cfg = SAM_DT_INST_CLOCK_PMC_CFG(inst), \
139
251
.prescaler = DT_INST_PROP(inst, prescaler), \
140
252
.divider = DT_INST_PROP(inst, divider), \
253
+ IF_ENABLED(CONFIG_PWM_INTERRUPT, \
254
+ (.irq_config = sam_pwm_irq_config_##inst,)) \
141
255
}; \
142
256
\
143
- DEVICE_DT_INST_DEFINE(inst, \
144
- &sam_pwm_init, NULL, \
145
- NULL, &sam_pwm_config_##inst, \
146
- POST_KERNEL, \
147
- CONFIG_PWM_INIT_PRIORITY, \
148
- &sam_pwm_driver_api);
257
+ static struct sam_pwm_data sam_pwm_data_##inst; \
258
+ \
259
+ DEVICE_DT_INST_DEFINE(inst, &sam_pwm_init, NULL, \
260
+ &sam_pwm_data_##inst, \
261
+ &sam_pwm_config_##inst, POST_KERNEL, \
262
+ CONFIG_PWM_INIT_PRIORITY, \
263
+ &sam_pwm_driver_api);
149
264
150
265
DT_INST_FOREACH_STATUS_OKAY (SAM_INST_INIT )
0 commit comments