Skip to content

Commit 0d2a95f

Browse files
committed
drivers: pwm: sf32lb: add gpt based pwm driver support
Add gpt based pwm driver for sf32lb platform Signed-off-by: Qingsong Gou <[email protected]>
1 parent cae1ea2 commit 0d2a95f

File tree

4 files changed

+171
-0
lines changed

4 files changed

+171
-0
lines changed

drivers/pwm/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ zephyr_library_sources_ifdef(CONFIG_PWM_MAX32 pwm_max32.c)
2929
zephyr_library_sources_ifdef(CONFIG_PWM_MCUX_TPM pwm_mcux_tpm.c)
3030
zephyr_library_sources_ifdef(CONFIG_PWM_SAM0_TCC pwm_sam0_tcc.c)
3131
zephyr_library_sources_ifdef(CONFIG_PWM_SAM0_TC pwm_sam0_tc.c)
32+
zephyr_library_sources_ifdef(CONFIG_PWM_SF32LB_GPT pwm_sf32lb_gpt.c)
3233
zephyr_library_sources_ifdef(CONFIG_PWM_NPCX pwm_npcx.c)
3334
zephyr_library_sources_ifdef(CONFIG_PWM_XLNX_AXI_TIMER pwm_xlnx_axi_timer.c)
3435
zephyr_library_sources_ifdef(CONFIG_PWM_MCUX_PWT pwm_mcux_pwt.c)

drivers/pwm/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ source "drivers/pwm/Kconfig.mcux_pwt"
8888

8989
source "drivers/pwm/Kconfig.gecko"
9090

91+
source "drivers/pwm/Kconfig.sf32lb"
92+
9193
source "drivers/pwm/Kconfig.silabs"
9294

9395
source "drivers/pwm/Kconfig.siwx91x"

drivers/pwm/Kconfig.sf32lb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2025, Qingsong Gou <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config PWM_SF32LB_GPT
5+
bool "GPT based PWM driver for SF32LB family of MCUs"
6+
default y
7+
depends on DT_HAS_SIFLI_SF32LB_GPT_PWM_ENABLED
8+
help
9+
Enable PWM driver for SF32LB series of MCUs. This driver uses
10+
the timer peripheral to implement PWM functionality.

drivers/pwm/pwm_sf32lb_gpt.c

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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_gpt_pwm
7+
8+
#include <zephyr/kernel.h>
9+
#include <zephyr/drivers/pwm.h>
10+
#include <zephyr/drivers/clock_control/sf32lb.h>
11+
#include <zephyr/drivers/pinctrl.h>
12+
#include <zephyr/logging/log.h>
13+
#include <zephyr/irq.h>
14+
15+
#include <register.h>
16+
17+
#define GPT_CCMRX(ch) (GPT_CCMR1 + (ch >> 1U))
18+
#define CCRX(ch) (GPT_CCR1 + ((ch) << 2U))
19+
20+
#define GPT_CR1 offsetof(GPT_TypeDef, CR1)
21+
#define GPT_PSC offsetof(GPT_TypeDef, PSC)
22+
#define GPT_ARR offsetof(GPT_TypeDef, ARR)
23+
#define GPT_CCR1 offsetof(GPT_TypeDef, CCR1)
24+
#define GPT_CCER offsetof(GPT_TypeDef, CCER)
25+
#define GPT_CCMR1 offsetof(GPT_TypeDef, CCMR1)
26+
#define GPT_EGR offsetof(GPT_TypeDef, EGR)
27+
28+
#define GPT_CCMR1_OC1M_1 (0x2U << GPT_CCMR1_OC1M_Pos)
29+
#define GPT_CCMR1_OC1M_2 (0x4U << GPT_CCMR1_OC1M_Pos)
30+
31+
#define GPT_OCMODE_PWM1 (GPT_CCMR1_OC1M_1 | GPT_CCMR1_OC1M_2)
32+
33+
LOG_MODULE_REGISTER(pwm_sf32lb, CONFIG_PWM_LOG_LEVEL);
34+
35+
struct pwm_sf32lb_config {
36+
uintptr_t base;
37+
const struct pinctrl_dev_config *pcfg;
38+
struct sf32lb_clock_dt_spec clock;
39+
uint16_t prescaler;
40+
};
41+
42+
static int pwm_sf32lb_set_cycles(const struct device *dev, uint32_t channel, uint32_t period_cycles,
43+
uint32_t pulse_cycles, pwm_flags_t flags)
44+
{
45+
const struct pwm_sf32lb_config *config = dev->config;
46+
uint8_t pos;
47+
48+
pos = channel * 4U;
49+
50+
LOG_DBG("Setting PWM period_cycles: %d, pulse_cycles: %d", period_cycles, pulse_cycles);
51+
52+
if ((period_cycles > UINT16_MAX + 1U) || (pulse_cycles > UINT16_MAX + 1U)) {
53+
LOG_ERR("Cannot set PWM output, value exceeds 16-bit timer limit.");
54+
return -ENOTSUP;
55+
}
56+
57+
sys_clear_bit(config->base + GPT_CCER, pos);
58+
59+
if (flags & PWM_POLARITY_INVERTED) {
60+
sys_set_bits(config->base + GPT_CCER, GPT_CCER_CC1P << pos);
61+
}
62+
63+
sys_write32(period_cycles - 1, config->base + GPT_ARR);
64+
sys_write32(pulse_cycles - 1, config->base + CCRX(channel));
65+
66+
switch (channel) {
67+
case 0:
68+
sys_clear_bits(config->base + GPT_CCMRX(channel), GPT_CCMR1_OC1M);
69+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_CCMR1_OC1PE);
70+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_OCMODE_PWM1);
71+
break;
72+
case 1:
73+
sys_clear_bits(config->base + GPT_CCMRX(channel), GPT_CCMR1_OC2M);
74+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_CCMR1_OC2PE);
75+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_OCMODE_PWM1);
76+
break;
77+
case 2:
78+
sys_clear_bits(config->base + GPT_CCMRX(channel), GPT_CCMR2_OC3M);
79+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_CCMR2_OC3PE);
80+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_OCMODE_PWM1);
81+
break;
82+
case 3:
83+
sys_clear_bits(config->base + GPT_CCMRX(channel), GPT_CCMR2_OC4M);
84+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_CCMR2_OC4PE);
85+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_OCMODE_PWM1);
86+
break;
87+
default:
88+
break;
89+
}
90+
91+
sys_set_bit(config->base + GPT_CCER, pos);
92+
93+
return 0;
94+
}
95+
96+
static int pwm_sf32lb_get_cycles_per_sec(const struct device *dev, uint32_t channel,
97+
uint64_t *cycles)
98+
{
99+
const struct pwm_sf32lb_config *config = dev->config;
100+
uint32_t clock_freq;
101+
int ret;
102+
103+
ret = sf32lb_clock_control_get_rate_dt(&config->clock, &clock_freq);
104+
if (ret < 0) {
105+
return ret;
106+
}
107+
108+
*cycles = (uint64_t)(clock_freq / (config->prescaler + 1U));
109+
110+
return ret;
111+
}
112+
113+
static int pwm_sf32lb_init(const struct device *dev)
114+
{
115+
const struct pwm_sf32lb_config *config = dev->config;
116+
int ret;
117+
118+
if (!sf32lb_clock_is_ready_dt(&config->clock)) {
119+
return -ENODEV;
120+
}
121+
122+
ret = sf32lb_clock_control_on_dt(&config->clock);
123+
if (ret < 0) {
124+
return ret;
125+
}
126+
127+
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
128+
if (ret < 0) {
129+
LOG_ERR("Failed to configure pins");
130+
return ret;
131+
}
132+
133+
sys_write32(config->prescaler, config->base + GPT_PSC);
134+
sys_set_bit(config->base + GPT_EGR, GPT_EGR_UG_Pos);
135+
136+
sys_set_bit(config->base + GPT_CR1, GPT_CR1_CEN_Pos);
137+
138+
return ret;
139+
}
140+
141+
static DEVICE_API(pwm, pwm_sf32lb_driver_api) = {
142+
.set_cycles = pwm_sf32lb_set_cycles,
143+
.get_cycles_per_sec = pwm_sf32lb_get_cycles_per_sec,
144+
};
145+
146+
#define PWM_SF32LB_DEFINE(n) \
147+
PINCTRL_DT_INST_DEFINE(n); \
148+
static const struct pwm_sf32lb_config pwm_sf32lb_config_##n = { \
149+
.base = DT_REG_ADDR(DT_INST_PARENT(n)), \
150+
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
151+
.clock = SF32LB_CLOCK_DT_INST_PARENT_SPEC_GET(n), \
152+
.prescaler = DT_PROP(DT_INST_PARENT(n), prescaler), \
153+
}; \
154+
DEVICE_DT_INST_DEFINE(n, pwm_sf32lb_init, NULL, NULL, \
155+
&pwm_sf32lb_config_##n, POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
156+
&pwm_sf32lb_driver_api);
157+
158+
DT_INST_FOREACH_STATUS_OKAY(PWM_SF32LB_DEFINE)

0 commit comments

Comments
 (0)