Skip to content

Commit 8407f17

Browse files
javlandsnashif
authored andcommitted
drivers: pwm: sam: Add period and fault events
The SAM4S pwm supports several events. This commit implements the events when a channel period has ended or a fault event has occured. Signed-off-by: Jaro Van Landschoot <jaro.vanlandschoot@basalte.be>
1 parent c6e64a1 commit 8407f17

File tree

1 file changed

+117
-5
lines changed

1 file changed

+117
-5
lines changed

drivers/pwm/pwm_sam.c

Lines changed: 117 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
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

4864
static 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+
123210
static 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)
149243
static 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

171283
DT_INST_FOREACH_STATUS_OKAY(SAM_INST_INIT)

0 commit comments

Comments
 (0)