Skip to content

Commit da3de36

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 adf9300 commit da3de36

File tree

4 files changed

+186
-0
lines changed

4 files changed

+186
-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_GPT pwm_sf32lb_gpt.c)
6162
zephyr_library_sources_ifdef(CONFIG_PWM_SIFIVE pwm_sifive.c)
6263
zephyr_library_sources_ifdef(CONFIG_PWM_SILABS_LETIMER pwm_silabs_letimer.c)
6364
zephyr_library_sources_ifdef(CONFIG_PWM_SILABS_SIWX91X pwm_silabs_siwx91x.c)

drivers/pwm/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ source "drivers/pwm/Kconfig.rv32m1_tpm"
7878
source "drivers/pwm/Kconfig.sam"
7979
source "drivers/pwm/Kconfig.sam0"
8080
source "drivers/pwm/Kconfig.sam0_tc"
81+
source "drivers/pwm/Kconfig.sf32lb"
8182
source "drivers/pwm/Kconfig.sifive"
8283
source "drivers/pwm/Kconfig.silabs"
8384
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: 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_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+
if (channel > 3) {
51+
LOG_ERR("Invalid PWM channel: %u. Must be 0-3.", channel);
52+
return -EINVAL;
53+
}
54+
55+
LOG_DBG("Setting PWM period_cycles: %d, pulse_cycles: %d", period_cycles, pulse_cycles);
56+
57+
if ((period_cycles > UINT16_MAX) || (pulse_cycles > UINT16_MAX)) {
58+
LOG_ERR("Cannot set PWM output, value exceeds 16-bit timer limit.");
59+
return -ENOTSUP;
60+
}
61+
62+
if (period_cycles == 0U) {
63+
sys_clear_bit(config->base + GPT_CCER, pos);
64+
return 0;
65+
}
66+
67+
sys_clear_bit(config->base + GPT_CCER, pos);
68+
sys_clear_bits(config->base + GPT_CCER, GPT_CCER_CC1P << pos);
69+
70+
if (flags & PWM_POLARITY_INVERTED) {
71+
sys_set_bits(config->base + GPT_CCER, GPT_CCER_CC1P << pos);
72+
}
73+
74+
sys_write32(period_cycles - 1, config->base + GPT_ARR);
75+
sys_write32(pulse_cycles, config->base + CCRX(channel));
76+
77+
switch (channel) {
78+
case 0:
79+
sys_clear_bits(config->base + GPT_CCMRX(channel), GPT_CCMR1_OC1M);
80+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_CCMR1_OC1PE);
81+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_OCMODE_PWM1);
82+
break;
83+
case 1:
84+
sys_clear_bits(config->base + GPT_CCMRX(channel), GPT_CCMR1_OC2M);
85+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_CCMR1_OC2PE);
86+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_OCMODE_PWM1);
87+
break;
88+
case 2:
89+
sys_clear_bits(config->base + GPT_CCMRX(channel), GPT_CCMR2_OC3M);
90+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_CCMR2_OC3PE);
91+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_OCMODE_PWM1);
92+
break;
93+
case 3:
94+
sys_clear_bits(config->base + GPT_CCMRX(channel), GPT_CCMR2_OC4M);
95+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_CCMR2_OC4PE);
96+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_OCMODE_PWM1);
97+
break;
98+
default:
99+
break;
100+
}
101+
102+
sys_set_bit(config->base + GPT_CCER, pos);
103+
104+
return 0;
105+
}
106+
107+
static int pwm_sf32lb_get_cycles_per_sec(const struct device *dev, uint32_t channel,
108+
uint64_t *cycles)
109+
{
110+
const struct pwm_sf32lb_config *config = dev->config;
111+
uint32_t clock_freq;
112+
int ret;
113+
114+
if (channel > 3) {
115+
LOG_ERR("Invalid PWM channel: %u. Must be 0-3.", channel);
116+
return -EINVAL;
117+
}
118+
119+
ret = sf32lb_clock_control_get_rate_dt(&config->clock, &clock_freq);
120+
if (ret < 0) {
121+
return ret;
122+
}
123+
124+
*cycles = (uint64_t)(clock_freq / (config->prescaler + 1U));
125+
126+
return ret;
127+
}
128+
129+
static int pwm_sf32lb_init(const struct device *dev)
130+
{
131+
const struct pwm_sf32lb_config *config = dev->config;
132+
int ret;
133+
134+
if (!sf32lb_clock_is_ready_dt(&config->clock)) {
135+
return -ENODEV;
136+
}
137+
138+
ret = sf32lb_clock_control_on_dt(&config->clock);
139+
if (ret < 0) {
140+
return ret;
141+
}
142+
143+
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
144+
if (ret < 0) {
145+
LOG_ERR("Failed to configure pins");
146+
return ret;
147+
}
148+
149+
sys_write32(config->prescaler, config->base + GPT_PSC);
150+
sys_set_bit(config->base + GPT_EGR, GPT_EGR_UG_Pos);
151+
152+
sys_set_bit(config->base + GPT_CR1, GPT_CR1_CEN_Pos);
153+
154+
return ret;
155+
}
156+
157+
static DEVICE_API(pwm, pwm_sf32lb_driver_api) = {
158+
.set_cycles = pwm_sf32lb_set_cycles,
159+
.get_cycles_per_sec = pwm_sf32lb_get_cycles_per_sec,
160+
};
161+
162+
#define PWM_SF32LB_DEFINE(n) \
163+
PINCTRL_DT_INST_DEFINE(n); \
164+
static const struct pwm_sf32lb_config pwm_sf32lb_config_##n = { \
165+
.base = DT_REG_ADDR(DT_INST_PARENT(n)), \
166+
.pcfg = 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), prescaler), \
169+
}; \
170+
DEVICE_DT_INST_DEFINE(n, pwm_sf32lb_init, NULL, NULL, \
171+
&pwm_sf32lb_config_##n, POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
172+
&pwm_sf32lb_driver_api);
173+
174+
DT_INST_FOREACH_STATUS_OKAY(PWM_SF32LB_DEFINE)

0 commit comments

Comments
 (0)