Skip to content

Commit 644c646

Browse files
committed
drivers: pwm: sf32lb: add atim based pwm driver
add atim based pwm driver for sf32lb platform Signed-off-by: Qingsong Gou <[email protected]>
1 parent 8cd341d commit 644c646

File tree

3 files changed

+183
-0
lines changed

3 files changed

+183
-0
lines changed

drivers/pwm/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ zephyr_library_sources_ifdef(CONFIG_PWM_RV32M1_TPM pwm_rv32m1_tpm.c)
5858
zephyr_library_sources_ifdef(CONFIG_PWM_SAM pwm_sam.c)
5959
zephyr_library_sources_ifdef(CONFIG_PWM_SAM0_TC pwm_sam0_tc.c)
6060
zephyr_library_sources_ifdef(CONFIG_PWM_SAM0_TCC pwm_sam0_tcc.c)
61+
zephyr_library_sources_ifdef(CONFIG_PWM_SF32LB_ATIM pwm_sf32lb_atim.c)
6162
zephyr_library_sources_ifdef(CONFIG_PWM_SF32LB_GPT pwm_sf32lb_gpt.c)
6263
zephyr_library_sources_ifdef(CONFIG_PWM_SIFIVE pwm_sifive.c)
6364
zephyr_library_sources_ifdef(CONFIG_PWM_SILABS_LETIMER pwm_silabs_letimer.c)

drivers/pwm/Kconfig.sf32lb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,11 @@ config PWM_SF32LB_GPT
88
help
99
Enable PWM driver for SF32LB series of MCUs. This driver uses
1010
the timer peripheral to implement PWM functionality.
11+
12+
config PWM_SF32LB_ATIM
13+
bool "ATIM based PWM driver for SF32LB family of MCUs"
14+
default y
15+
depends on DT_HAS_SIFLI_SF32LB_ATIM_PWM_ENABLED
16+
help
17+
Enable PWM driver for SF32LB series of MCUs. This driver uses
18+
the advanced timer peripheral to implement PWM functionality.

drivers/pwm/pwm_sf32lb_atim.c

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/*
2+
* Copyright (c) 2025, Qingsong Gou <[email protected]>
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#define DT_DRV_COMPAT sifli_sf32lb_atim_pwm
7+
8+
#include <zephyr/drivers/pwm.h>
9+
#include <zephyr/drivers/clock_control/sf32lb.h>
10+
#include <zephyr/drivers/pinctrl.h>
11+
#include <zephyr/kernel.h>
12+
#include <zephyr/logging/log.h>
13+
14+
#include <register.h>
15+
16+
LOG_MODULE_REGISTER(pwm_sf32lb_atim, CONFIG_PWM_LOG_LEVEL);
17+
18+
#define CR1 offsetof(ATIM_TypeDef, CR1)
19+
#define CR2 offsetof(ATIM_TypeDef, CR2)
20+
#define DIER offsetof(ATIM_TypeDef, DIER)
21+
#define SR offsetof(ATIM_TypeDef, SR)
22+
#define EGR offsetof(ATIM_TypeDef, EGR)
23+
#define CCMR1 offsetof(ATIM_TypeDef, CCMR1)
24+
#define CCMR2 offsetof(ATIM_TypeDef, CCMR2)
25+
#define CCER offsetof(ATIM_TypeDef, CCER)
26+
#define PSC offsetof(ATIM_TypeDef, PSC)
27+
#define ARR offsetof(ATIM_TypeDef, ARR)
28+
#define CCR1 offsetof(ATIM_TypeDef, CCR1)
29+
#define CCR2 offsetof(ATIM_TypeDef, CCR2)
30+
#define CCR3 offsetof(ATIM_TypeDef, CCR3)
31+
#define CCR4 offsetof(ATIM_TypeDef, CCR4)
32+
#define BDTR offsetof(ATIM_TypeDef, BDTR)
33+
34+
#define ATIM_PWM_MODE1 (6U)
35+
36+
#define CCMRX(ch) ((ch) <= 1 ? CCMR1 : CCMR2)
37+
38+
#define MAX_CH_NUM (4U)
39+
40+
struct pwm_sf32lb_atim_config {
41+
uintptr_t base;
42+
const struct pinctrl_dev_config *pincfg;
43+
struct sf32lb_clock_dt_spec clock;
44+
uint32_t prescaler;
45+
};
46+
47+
static int pwm_sf32lb_atim_set_cycles(const struct device *dev, uint32_t channel,
48+
uint32_t period_cycles, uint32_t pulse_cycles,
49+
pwm_flags_t flags)
50+
{
51+
const struct pwm_sf32lb_atim_config *cfg = dev->config;
52+
uint32_t ccmr;
53+
54+
if (channel >= MAX_CH_NUM) {
55+
return -EINVAL;
56+
}
57+
58+
/* disable the channel */
59+
sys_clear_bit(cfg->base + CCER, channel * 4);
60+
61+
sys_write32(period_cycles - 1, cfg->base + ARR);
62+
63+
switch (channel) {
64+
case 0:
65+
sys_write32(pulse_cycles, cfg->base + CCR1);
66+
ccmr = sys_read32(cfg->base + CCMRX(channel));
67+
ccmr &= ~ATIM_CCMR1_OC1M_Msk;
68+
ccmr |= FIELD_PREP(ATIM_CCMR1_OC1M_Msk, ATIM_PWM_MODE1);
69+
ccmr |= ATIM_CCMR1_OC1PE;
70+
sys_write32(ccmr, cfg->base + CCMRX(channel));
71+
break;
72+
case 1:
73+
sys_write32(pulse_cycles, cfg->base + CCR2);
74+
ccmr = sys_read32(cfg->base + CCMRX(channel));
75+
ccmr &= ~ATIM_CCMR1_OC2M_Msk;
76+
ccmr |= FIELD_PREP(ATIM_CCMR1_OC2M_Msk, ATIM_PWM_MODE1);
77+
ccmr |= ATIM_CCMR1_OC2PE;
78+
sys_write32(ccmr, cfg->base + CCMRX(channel));
79+
break;
80+
case 2:
81+
sys_write32(pulse_cycles, cfg->base + CCR3);
82+
ccmr = sys_read32(cfg->base + CCMRX(channel));
83+
ccmr &= ~ATIM_CCMR2_OC3M_Msk;
84+
ccmr |= FIELD_PREP(ATIM_CCMR2_OC3M_Msk, ATIM_PWM_MODE1);
85+
ccmr |= ATIM_CCMR2_OC3PE;
86+
sys_write32(ccmr, cfg->base + CCMRX(channel));
87+
break;
88+
case 3:
89+
sys_write32(pulse_cycles, cfg->base + CCR4);
90+
ccmr = sys_read32(cfg->base + CCMRX(channel));
91+
ccmr &= ~ATIM_CCMR2_OC4M_Msk;
92+
ccmr |= FIELD_PREP(ATIM_CCMR2_OC4M_Msk, ATIM_PWM_MODE1);
93+
ccmr |= ATIM_CCMR2_OC4PE;
94+
sys_write32(ccmr, cfg->base + CCMRX(channel));
95+
break;
96+
default:
97+
return -EINVAL;
98+
}
99+
100+
if (flags & PWM_POLARITY_INVERTED) {
101+
sys_set_bit(cfg->base + CCER, channel * 4 + 1);
102+
}
103+
104+
/* enable the channel */
105+
sys_set_bit(cfg->base + CCER, channel * 4);
106+
107+
return 0;
108+
}
109+
110+
static int pwm_sf32lb_atim_get_cycles_per_sec(const struct device *dev, uint32_t channel,
111+
uint64_t *cycles)
112+
{
113+
const struct pwm_sf32lb_atim_config *cfg = dev->config;
114+
uint32_t clk_rate;
115+
116+
if (sf32lb_clock_control_get_rate_dt(&cfg->clock, &clk_rate)) {
117+
return -EIO;
118+
}
119+
120+
*cycles = clk_rate / (cfg->prescaler + 1);
121+
printk("clk_rate: %d, prescaler: %d, cycles: %lld\n", clk_rate, cfg->prescaler, *cycles);
122+
return 0;
123+
}
124+
125+
static const struct pwm_driver_api pwm_sf32lb_atim_api = {
126+
.set_cycles = pwm_sf32lb_atim_set_cycles,
127+
.get_cycles_per_sec = pwm_sf32lb_atim_get_cycles_per_sec,
128+
};
129+
130+
static int pwm_sf32lb_atim_init(const struct device *dev)
131+
{
132+
const struct pwm_sf32lb_atim_config *cfg = dev->config;
133+
int err;
134+
135+
err = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT);
136+
if (err < 0) {
137+
return err;
138+
}
139+
140+
if (!sf32lb_clock_is_ready_dt(&cfg->clock)) {
141+
return -ENODEV;
142+
}
143+
144+
err = sf32lb_clock_control_on_dt(&cfg->clock);
145+
if (err < 0) {
146+
return err;
147+
}
148+
149+
sys_write32(cfg->prescaler, cfg->base + PSC);
150+
sys_set_bit(cfg->base + EGR, ATIM_EGR_UG_Pos);
151+
152+
/* enable auto-reload preload */
153+
sys_set_bit(cfg->base + CR1, ATIM_CR1_ARPE_Pos);
154+
155+
/* enable timer */
156+
sys_set_bit(cfg->base + CR1, ATIM_CR1_CEN_Pos);
157+
sys_set_bit(cfg->base + BDTR, ATIM_BDTR_MOE_Pos);
158+
159+
return err;
160+
}
161+
162+
#define PWM_SF32LB_ATIM_DEFINE(n) \
163+
PINCTRL_DT_INST_DEFINE(n); \
164+
static const struct pwm_sf32lb_atim_config pwm_sf32lb_atim_config_##n = { \
165+
.base = DT_REG_ADDR(DT_INST_PARENT(n)), \
166+
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
167+
.clock = SF32LB_CLOCK_DT_INST_PARENT_SPEC_GET(n), \
168+
.prescaler = DT_PROP(DT_INST_PARENT(n), sifli_prescaler), \
169+
}; \
170+
DEVICE_DT_INST_DEFINE(n, pwm_sf32lb_atim_init, NULL, NULL, \
171+
&pwm_sf32lb_atim_config_##n, POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
172+
&pwm_sf32lb_atim_api);
173+
174+
DT_INST_FOREACH_STATUS_OKAY(PWM_SF32LB_ATIM_DEFINE)

0 commit comments

Comments
 (0)