Skip to content

Commit aa08572

Browse files
committed
drivers: pwm: sam4s: Add period and fault interrupts
The SAM4S pwm supports several interrupts. This commit implements the interrupts when a channel period has ended or a fault event has occured. Signed-off-by: Jaro Van Landschoot <[email protected]>
1 parent e18a84f commit aa08572

File tree

1 file changed

+123
-8
lines changed

1 file changed

+123
-8
lines changed

drivers/pwm/pwm_sam.c

Lines changed: 123 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
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/irq.h>
1516

1617
#include <zephyr/logging/log.h>
1718

@@ -35,10 +36,19 @@ struct sam_pwm_config {
3536
const struct pinctrl_dev_config *pcfg;
3637
uint8_t prescaler;
3738
uint8_t divider;
39+
#ifdef CONFIG_PWM_INTERRUPT
40+
void (*irq_config)(void);
41+
#endif /* CONFIG_PWM_INTERRUPT */
3842
};
3943

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)
4252
{
4353
const struct sam_pwm_config *config = dev->config;
4454
uint8_t prescaler = config->prescaler;
@@ -99,6 +109,88 @@ static int sam_pwm_set_cycles(const struct device *dev, uint32_t channel,
99109
return 0;
100110
}
101111

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+
102194
static int sam_pwm_init(const struct device *dev)
103195
{
104196
const struct sam_pwm_config *config = dev->config;
@@ -110,6 +202,10 @@ static int sam_pwm_init(const struct device *dev)
110202

111203
/* FIXME: way to validate prescaler & divider */
112204

205+
#ifdef CONFIG_PWM_INTERRUPT
206+
config->irq_config();
207+
#endif
208+
113209
/* Enable PWM clock in PMC */
114210
(void)clock_control_on(SAM_DT_PMC_CONTROLLER,
115211
(clock_control_subsys_t)&config->clock_cfg);
@@ -128,23 +224,42 @@ static int sam_pwm_init(const struct device *dev)
128224
static DEVICE_API(pwm, sam_pwm_driver_api) = {
129225
.set_cycles = sam_pwm_set_cycles,
130226
.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 */
131232
};
132233

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+
133242
#define SAM_INST_INIT(inst) \
134243
PINCTRL_DT_INST_DEFINE(inst); \
244+
\
245+
IF_ENABLED(CONFIG_PWM_INTERRUPT, (SAM_PWM_INTERRUPT_INIT(inst)))\
246+
\
135247
static const struct sam_pwm_config sam_pwm_config_##inst = { \
136248
.regs = (Pwm *)DT_INST_REG_ADDR(inst), \
137249
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
138250
.clock_cfg = SAM_DT_INST_CLOCK_PMC_CFG(inst), \
139251
.prescaler = DT_INST_PROP(inst, prescaler), \
140252
.divider = DT_INST_PROP(inst, divider), \
253+
IF_ENABLED(CONFIG_PWM_INTERRUPT, \
254+
(.irq_config = sam_pwm_irq_config_##inst,)) \
141255
}; \
142256
\
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);
149264

150265
DT_INST_FOREACH_STATUS_OKAY(SAM_INST_INIT)

0 commit comments

Comments
 (0)