1212#include <zephyr/drivers/pwm.h>
1313#include <zephyr/drivers/pinctrl.h>
1414#include <zephyr/drivers/clock_control/atmel_sam_pmc.h>
15+ #include <zephyr/drivers/pwm/pwm_utils.h>
16+ #include <zephyr/spinlock.h>
17+
18+ #ifdef CONFIG_PWM_EVENT
19+ #include <zephyr/irq.h>
20+ #endif
1521
1622#include <zephyr/logging/log.h>
1723
@@ -43,6 +49,16 @@ struct sam_pwm_config {
4349 const struct pinctrl_dev_config * pcfg ;
4450 uint8_t prescaler ;
4551 uint8_t divider ;
52+ #ifdef CONFIG_PWM_EVENT
53+ void (* irq_config )(void );
54+ #endif /* CONFIG_PWM_EVENT */
55+ };
56+
57+ struct sam_pwm_data {
58+ #ifdef CONFIG_PWM_EVENT
59+ sys_slist_t event_callbacks ;
60+ struct k_spinlock lock ;
61+ #endif /* CONFIG_PWM_EVENT */
4662};
4763
4864static int sam_pwm_get_cycles_per_sec (const struct device * dev ,
@@ -120,6 +136,77 @@ static int sam_pwm_set_cycles(const struct device *dev, uint32_t channel,
120136 return 0 ;
121137}
122138
139+ #ifdef CONFIG_PWM_EVENT
140+ static void update_interrupts (const struct device * dev )
141+ {
142+ const struct sam_pwm_config * config = dev -> config ;
143+ struct sam_pwm_data * data = dev -> data ;
144+ struct pwm_event_callback * cb , * tmp ;
145+ Pwm * const pwm = config -> regs ;
146+ uint32_t pwm_ier1 = 0 ;
147+
148+ SYS_SLIST_FOR_EACH_CONTAINER_SAFE (& data -> event_callbacks , cb , tmp , node ) {
149+ if ((cb -> event_mask & PWM_EVENT_TYPE_PERIOD ) != 0 ) {
150+ pwm_ier1 |= PWM_IER1_CHID0 << cb -> channel ;
151+ }
152+ if ((cb -> event_mask & PWM_EVENT_TYPE_FAULT ) != 0 ) {
153+ pwm_ier1 |= PWM_IER1_FCHID0 << cb -> channel ;
154+ }
155+ }
156+
157+ /* Disable all interrupts */
158+ pwm -> PWM_IDR1 = UINT32_MAX ;
159+
160+ /* Dummy read to clear status register */
161+ (void )pwm -> PWM_ISR1 ;
162+
163+ /* Reenable interrupts */
164+ pwm -> PWM_IER1 = pwm_ier1 ;
165+ }
166+
167+ static void sam_pwm_isr (const struct device * dev )
168+ {
169+ const struct sam_pwm_config * config = dev -> config ;
170+ struct sam_pwm_data * data = dev -> data ;
171+ Pwm * const pwm = config -> regs ;
172+ pwm_events_t events ;
173+
174+ uint32_t status = pwm -> PWM_ISR1 ;
175+
176+ for (uint32_t i = 0 ; i < PWMCHNUM_NUMBER ; i ++ ) {
177+ events = 0 ;
178+ if ((status & (PWM_ISR1_CHID0 << i )) != 0 ) {
179+ events |= PWM_EVENT_TYPE_PERIOD ;
180+ }
181+ if ((status & (PWM_ISR1_FCHID0 << i )) != 0 ) {
182+ events |= PWM_EVENT_TYPE_FAULT ;
183+ }
184+
185+ if (events > 0 ) {
186+ pwm_fire_event_callbacks (& data -> event_callbacks , dev , i , events );
187+ }
188+ }
189+ }
190+
191+ static int sam_pwm_manage_event_callback (const struct device * dev ,
192+ struct pwm_event_callback * callback , bool set )
193+ {
194+ struct sam_pwm_data * data = dev -> data ;
195+ int ret ;
196+
197+ ret = pwm_manage_event_callback (& data -> event_callbacks , callback , set );
198+ if (ret < 0 ) {
199+ return ret ;
200+ }
201+
202+ K_SPINLOCK (& data -> lock ) {
203+ update_interrupts (dev );
204+ }
205+
206+ return 0 ;
207+ }
208+ #endif /* CONFIG_PWM_EVENT */
209+
123210static int sam_pwm_init (const struct device * dev )
124211{
125212 const struct sam_pwm_config * config = dev -> config ;
@@ -131,6 +218,13 @@ static int sam_pwm_init(const struct device *dev)
131218
132219 /* FIXME: way to validate prescaler & divider */
133220
221+ #ifdef CONFIG_PWM_EVENT
222+ struct sam_pwm_data * data = dev -> data ;
223+
224+ sys_slist_init (& data -> event_callbacks );
225+ config -> irq_config ();
226+ #endif
227+
134228 /* Enable PWM clock in PMC */
135229 (void )clock_control_on (SAM_DT_PMC_CONTROLLER ,
136230 (clock_control_subsys_t )& config -> clock_cfg );
@@ -149,23 +243,41 @@ static int sam_pwm_init(const struct device *dev)
149243static DEVICE_API (pwm , sam_pwm_driver_api ) = {
150244 .set_cycles = sam_pwm_set_cycles ,
151245 .get_cycles_per_sec = sam_pwm_get_cycles_per_sec ,
246+ #ifdef CONFIG_PWM_EVENT
247+ .manage_event_callback = sam_pwm_manage_event_callback ,
248+ #endif /* CONFIG_PWM_EVENT */
152249};
153250
251+ #define SAM_PWM_INTERRUPT_INIT (inst ) \
252+ static void sam_pwm_irq_config_##inst(void) \
253+ { \
254+ IRQ_CONNECT(DT_INST_IRQN(inst), 0, sam_pwm_isr, DEVICE_DT_INST_GET(inst), 0); \
255+ irq_enable(DT_INST_IRQN(inst)); \
256+ }
257+
154258#define SAM_INST_INIT (inst ) \
155259 PINCTRL_DT_INST_DEFINE(inst); \
260+ \
261+ IF_ENABLED(CONFIG_PWM_EVENT, (SAM_PWM_INTERRUPT_INIT(inst))) \
262+ \
156263 static const struct sam_pwm_config sam_pwm_config_##inst = { \
157264 .regs = (Pwm *)DT_INST_REG_ADDR(inst), \
158265 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
159266 .clock_cfg = SAM_DT_INST_CLOCK_PMC_CFG(inst), \
160267 .prescaler = DT_INST_PROP(inst, prescaler), \
161268 .divider = DT_INST_PROP(inst, divider), \
269+ IF_ENABLED(CONFIG_PWM_EVENT, \
270+ (.irq_config = sam_pwm_irq_config_##inst,)) \
162271 }; \
163272 \
273+ static struct sam_pwm_data sam_pwm_data_##inst; \
274+ \
164275 DEVICE_DT_INST_DEFINE(inst, \
165- &sam_pwm_init, NULL, \
166- NULL, &sam_pwm_config_##inst, \
167- POST_KERNEL, \
168- CONFIG_PWM_INIT_PRIORITY, \
169- &sam_pwm_driver_api);
276+ &sam_pwm_init, NULL, \
277+ &sam_pwm_data_##inst, \
278+ &sam_pwm_config_##inst, \
279+ POST_KERNEL, \
280+ CONFIG_PWM_INIT_PRIORITY, \
281+ &sam_pwm_driver_api);
170282
171283DT_INST_FOREACH_STATUS_OKAY (SAM_INST_INIT )
0 commit comments