Skip to content

Commit abaa219

Browse files
committed
drivers: pwm: sf32lb: add pwm driver support
Add pwm driver for sf32lb platform Signed-off-by: Qingsong Gou <[email protected]>
1 parent 0735cf9 commit abaa219

File tree

4 files changed

+182
-0
lines changed

4 files changed

+182
-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 pwm_sf32lb.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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright (c) 2025, Qingsong Gou <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config PWM_SF32LB
5+
bool "PWM driver for SF32LB family of MCUs"
6+
default y
7+
depends on DT_HAS_SIFLI_SF32LB_PWM_ENABLED
8+
select USE_SIFLI_HAL
9+
select USE_SIFLI_HAL_GPT
10+
help
11+
Enable PWM driver for SF32LB series of MCUs. This driver uses
12+
the timer peripheral to implement PWM functionality.

drivers/pwm/pwm_sf32lb.c

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
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_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+
#include <bf0_hal.h>
18+
#include <bf0_hal_tim.h>
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_CCMRX(ch) (GPT_CCMR1 + (ch >> 1U))
29+
#define CCRX(ch) (GPT_CCR1 + ((ch) << 2U))
30+
31+
LOG_MODULE_REGISTER(pwm_sf32lb, CONFIG_PWM_LOG_LEVEL);
32+
33+
struct pwm_sf32lb_config {
34+
const struct pinctrl_dev_config *pcfg;
35+
const struct device *clock_dev;
36+
uint16_t clock_id;
37+
uintptr_t base;
38+
uint32_t clock_freq;
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 == 0) {
53+
LOG_ERR("Invalid period cycles");
54+
return -EINVAL;
55+
}
56+
57+
if (pulse_cycles > period_cycles) {
58+
LOG_ERR("Pulse cycles %d > period cycles %d", pulse_cycles, period_cycles);
59+
return -EINVAL;
60+
}
61+
62+
if ((period_cycles > UINT16_MAX + 1U) || (pulse_cycles > UINT16_MAX + 1U)) {
63+
return -EINVAL;
64+
}
65+
66+
sys_clear_bit(config->base + GPT_CCER, pos);
67+
68+
if (flags & PWM_POLARITY_INVERTED) {
69+
sys_set_bits(config->base + GPT_CCER, GPT_CCER_CC1P << pos);
70+
}
71+
72+
sys_write32(period_cycles - 1, config->base + GPT_ARR);
73+
sys_write32(pulse_cycles - 1, config->base + CCRX(channel));
74+
75+
switch (channel) {
76+
case 0:
77+
sys_clear_bits(config->base + GPT_CCMRX(channel), GPT_CCMR1_OC1M);
78+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_CCMR1_OC1PE);
79+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_OCMODE_PWM1);
80+
break;
81+
case 1:
82+
sys_clear_bits(config->base + GPT_CCMRX(channel), GPT_CCMR1_OC2M);
83+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_CCMR1_OC2PE);
84+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_OCMODE_PWM1);
85+
break;
86+
case 2:
87+
sys_clear_bits(config->base + GPT_CCMRX(channel), GPT_CCMR2_OC3M);
88+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_CCMR2_OC3PE);
89+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_OCMODE_PWM1);
90+
break;
91+
case 3:
92+
sys_clear_bits(config->base + GPT_CCMRX(channel), GPT_CCMR2_OC4M);
93+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_CCMR2_OC4PE);
94+
sys_set_bits(config->base + GPT_CCMRX(channel), GPT_OCMODE_PWM1);
95+
break;
96+
default:
97+
break;
98+
}
99+
100+
sys_set_bit(config->base + GPT_CCER, pos);
101+
102+
return 0;
103+
}
104+
105+
static int pwm_sf32lb_get_cycles_per_sec(const struct device *dev, uint32_t channel,
106+
uint64_t *cycles)
107+
{
108+
const struct pwm_sf32lb_config *config = dev->config;
109+
110+
if (config->clock_freq > 0) {
111+
*cycles = (uint64_t)(config->clock_freq / (config->prescaler + 1U));
112+
return 0;
113+
}
114+
115+
*cycles = (uint64_t)48000000UL;
116+
117+
return 0;
118+
}
119+
120+
static int pwm_sf32lb_init(const struct device *dev)
121+
{
122+
const struct pwm_sf32lb_config *config = dev->config;
123+
int ret;
124+
125+
if (!device_is_ready(config->clock_dev)) {
126+
return -ENODEV;
127+
}
128+
129+
ret = clock_control_on(config->clock_dev, (clock_control_subsys_t)&config->clock_id);
130+
if (ret < 0) {
131+
return ret;
132+
}
133+
134+
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
135+
if (ret < 0) {
136+
LOG_ERR("Failed to configure pins");
137+
return ret;
138+
}
139+
140+
sys_write32(config->prescaler, config->base + GPT_PSC);
141+
sys_set_bit(config->base + GPT_EGR, GPT_EGR_UG_Pos);
142+
143+
sys_set_bit(config->base + GPT_CR1, GPT_CR1_CEN_Pos);
144+
145+
return ret;
146+
}
147+
148+
static DEVICE_API(pwm, pwm_sf32lb_driver_api) = {
149+
.set_cycles = pwm_sf32lb_set_cycles,
150+
.get_cycles_per_sec = pwm_sf32lb_get_cycles_per_sec,
151+
};
152+
153+
#define PWM_SF32LB_DEFINE(n) \
154+
PINCTRL_DT_INST_DEFINE(n); \
155+
static const struct pwm_sf32lb_config pwm_sf32lb_config_##n = { \
156+
.base = DT_REG_ADDR(DT_INST_PARENT(n)), \
157+
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
158+
.clock_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_INST_PARENT(n))), \
159+
.clock_id = DT_CLOCKS_CELL(DT_INST_PARENT(n), id), \
160+
.prescaler = DT_PROP(DT_INST_PARENT(n), prescaler), \
161+
.clock_freq = DT_PROP_OR(DT_INST_PARENT(n), clock_frequency, {}), \
162+
}; \
163+
DEVICE_DT_INST_DEFINE(n, pwm_sf32lb_init, NULL, NULL, \
164+
&pwm_sf32lb_config_##n, POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
165+
&pwm_sf32lb_driver_api);
166+
167+
DT_INST_FOREACH_STATUS_OKAY(PWM_SF32LB_DEFINE)

0 commit comments

Comments
 (0)