Skip to content

Commit c991b6b

Browse files
committed
drivers: pwm: siwx91x: Add siwx91x PWM driver
Implement PWM driver for siwx91x device Signed-off-by: Sai Santhosh Malae <[email protected]>
1 parent 1ea6c8f commit c991b6b

File tree

12 files changed

+346
-1
lines changed

12 files changed

+346
-1
lines changed

boards/silabs/radio_boards/siwx917_rb4338a/siwx917_rb4338a.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ supported:
1414
- flash
1515
- gpio
1616
- i2c
17+
- pwm
1718
- wifi
1819
vendor: silabs

drivers/clock_control/clock_control_silabs_siwx91x.c

+8
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ static int siwx91x_clock_on(const struct device *dev, clock_control_subsys_t sys
6464
RSI_PS_M4ssPeriPowerUp(M4SS_PWRGATE_ULP_EFUSE_PERI);
6565
RSI_CLK_PeripheralClkEnable(M4CLK, UDMA_CLK, ENABLE_STATIC_CLK);
6666
break;
67+
case SIWX91X_CLK_PWM:
68+
RSI_PS_M4ssPeriPowerUp(M4SS_PWRGATE_ULP_EFUSE_PERI);
69+
RSI_CLK_PeripheralClkEnable(M4CLK, PWM_CLK, ENABLE_STATIC_CLK);
70+
break;
6771
default:
6872
return -EINVAL;
6973
}
@@ -121,6 +125,10 @@ static int siwx91x_clock_get_rate(const struct device *dev, clock_control_subsys
121125
case SIWX91X_CLK_UART2:
122126
*rate = RSI_CLK_GetBaseClock(M4_UART1);
123127
return 0;
128+
case SIWX91X_CLK_PWM:
129+
/* PWM peripheral operates at the system clock frequency */
130+
*rate = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC;
131+
return 0;
124132
default:
125133
/* For now, no other driver need clock rate */
126134
return -EINVAL;

drivers/pwm/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ zephyr_library_sources_ifdef(CONFIG_PWM_NPCX pwm_npcx.c)
3131
zephyr_library_sources_ifdef(CONFIG_PWM_XLNX_AXI_TIMER pwm_xlnx_axi_timer.c)
3232
zephyr_library_sources_ifdef(CONFIG_PWM_MCUX_PWT pwm_mcux_pwt.c)
3333
zephyr_library_sources_ifdef(CONFIG_PWM_GECKO pwm_gecko.c)
34+
zephyr_library_sources_ifdef(CONFIG_PWM_SILABS_SIWX91X pwm_silabs_siwx91x.c)
3435
zephyr_library_sources_ifdef(CONFIG_PWM_GD32 pwm_gd32.c)
3536
zephyr_library_sources_ifdef(CONFIG_PWM_RCAR pwm_rcar.c)
3637
zephyr_library_sources_ifdef(CONFIG_PWM_PCA9685 pwm_pca9685.c)

drivers/pwm/Kconfig

+2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ source "drivers/pwm/Kconfig.mcux_pwt"
8484

8585
source "drivers/pwm/Kconfig.gecko"
8686

87+
source "drivers/pwm/Kconfig.siwx91x"
88+
8789
source "drivers/pwm/Kconfig.gd32"
8890

8991
source "drivers/pwm/Kconfig.rcar"

drivers/pwm/Kconfig.siwx91x

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2025 Silicon Laboratories Inc.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config PWM_SILABS_SIWX91X
5+
bool "Silabs SiWx91x PWM driver"
6+
default y
7+
depends on DT_HAS_SILABS_SIWX91X_PWM_ENABLED
8+
help
9+
Enable the PWM driver for the Silabs SiWx91x SoC series.

drivers/pwm/pwm_silabs_siwx91x.c

+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
/*
2+
* Copyright (c) 2025 Silicon Laboratories Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <errno.h>
8+
#include <zephyr/irq.h>
9+
#include <zephyr/sys/util.h>
10+
#include <zephyr/sys/util_macro.h>
11+
#include <zephyr/device.h>
12+
#include <zephyr/devicetree.h>
13+
#include <zephyr/drivers/pwm.h>
14+
#include <zephyr/drivers/pinctrl.h>
15+
#include <zephyr/drivers/clock_control.h>
16+
#include <zephyr/types.h>
17+
#include "sl_si91x_pwm.h"
18+
19+
#define DT_DRV_COMPAT silabs_siwx91x_pwm
20+
21+
#define PWM_CHANNELS 4
22+
#define DEFAULT_VALUE 0xFF
23+
24+
struct pwm_siwx91x_channel_config {
25+
uint8_t duty_cycle;
26+
uint32_t frequency;
27+
bool is_chan_active;
28+
};
29+
30+
struct pwm_siwx91x_config {
31+
/* Pointer to the clock device structure */
32+
const struct device *clock_dev;
33+
/* Clock control subsystem */
34+
clock_control_subsys_t clock_subsys;
35+
/* Pointer to the pin control device configuration */
36+
const struct pinctrl_dev_config *pcfg;
37+
/* Prescaler information of the channels */
38+
uint8_t ch_prescaler[PWM_CHANNELS];
39+
/* Common PWM polarity for all the channels */
40+
uint8_t pwm_polarity;
41+
};
42+
43+
struct pwm_siwx91x_data {
44+
struct pwm_siwx91x_channel_config pwm_channel_cfg[PWM_CHANNELS];
45+
};
46+
47+
/* Function to convert prescaler value to a programmable reg value */
48+
static int siwx91x_prescale_convert(uint8_t prescale)
49+
{
50+
51+
switch (prescale) {
52+
case 1:
53+
return SL_TIME_PERIOD_PRESCALE_1;
54+
case 2:
55+
return SL_TIME_PERIOD_PRESCALE_2;
56+
case 4:
57+
return SL_TIME_PERIOD_PRESCALE_4;
58+
case 8:
59+
return SL_TIME_PERIOD_PRESCALE_8;
60+
case 16:
61+
return SL_TIME_PERIOD_PRESCALE_16;
62+
case 32:
63+
return SL_TIME_PERIOD_PRESCALE_32;
64+
case 64:
65+
return SL_TIME_PERIOD_PRESCALE_64;
66+
default:
67+
return -EINVAL;
68+
}
69+
}
70+
71+
/* Program PWM channel with the default configurations */
72+
static int siwx91x_default_channel_config(const struct device *dev, uint32_t channel)
73+
{
74+
const struct pwm_siwx91x_config *config = dev->config;
75+
int prescale_reg_value = siwx91x_prescale_convert(config->ch_prescaler[channel]);
76+
int ret;
77+
78+
if (prescale_reg_value < 0) {
79+
return -EINVAL;
80+
}
81+
82+
ret = sl_si91x_pwm_set_output_mode(SL_MODE_INDEPENDENT, channel);
83+
if (ret) {
84+
return -EINVAL;
85+
}
86+
87+
ret = sl_si91x_pwm_set_base_timer_mode(SL_FREE_RUN_MODE, channel);
88+
if (ret) {
89+
return -EINVAL;
90+
}
91+
92+
ret = sl_si91x_pwm_control_base_timer(SL_BASE_TIMER_EACH_CHANNEL);
93+
if (ret) {
94+
return -EINVAL;
95+
}
96+
97+
ret = sl_si91x_pwm_control_period(SL_TIME_PERIOD_POSTSCALE_1_1, prescale_reg_value,
98+
channel);
99+
if (ret) {
100+
return -EINVAL;
101+
}
102+
103+
return 0;
104+
}
105+
106+
static int pwm_siwx91x_set_cycles(const struct device *dev, uint32_t channel,
107+
uint32_t period_cycles, uint32_t pulse_cycles, pwm_flags_t flags)
108+
{
109+
const struct pwm_siwx91x_config *config = dev->config;
110+
struct pwm_siwx91x_data *data = dev->data;
111+
uint32_t prev_period;
112+
uint32_t duty_cycle;
113+
int ret;
114+
115+
if (channel >= ARRAY_SIZE(data->pwm_channel_cfg)) {
116+
return -EINVAL;
117+
}
118+
119+
if (config->pwm_polarity != flags) {
120+
/* Polarity mismatch */
121+
return -ENOTSUP;
122+
}
123+
124+
if (data->pwm_channel_cfg[channel].is_chan_active == false) {
125+
/* Configure the channel with default parameters */
126+
ret = siwx91x_default_channel_config(dev, channel);
127+
if (ret) {
128+
return -EINVAL;
129+
}
130+
}
131+
132+
ret = sl_si91x_pwm_get_time_period(channel, (uint16_t *)&prev_period);
133+
if (ret) {
134+
return -EINVAL;
135+
}
136+
137+
if (period_cycles != prev_period) {
138+
ret = sl_si91x_pwm_set_time_period(channel, period_cycles, 0);
139+
if (ret) {
140+
/* Programmed value must be out of range (>65535) */
141+
return -EINVAL;
142+
}
143+
}
144+
145+
/* Calculate the duty cycle */
146+
duty_cycle = pulse_cycles * 100;
147+
duty_cycle /= period_cycles;
148+
149+
if (duty_cycle != data->pwm_channel_cfg[channel].duty_cycle) {
150+
ret = sl_si91x_pwm_set_duty_cycle(pulse_cycles, channel);
151+
if (ret) {
152+
return -EINVAL;
153+
}
154+
data->pwm_channel_cfg[channel].duty_cycle = duty_cycle;
155+
}
156+
157+
if (data->pwm_channel_cfg[channel].is_chan_active == false) {
158+
/* Start PWM after configuring the channel for first time */
159+
ret = sl_si91x_pwm_start(channel);
160+
if (ret) {
161+
return -EINVAL;
162+
}
163+
data->pwm_channel_cfg[channel].is_chan_active = true;
164+
}
165+
166+
return 0;
167+
}
168+
169+
static int pwm_siwx91x_get_cycles_per_sec(const struct device *dev, uint32_t channel,
170+
uint64_t *cycles)
171+
{
172+
struct pwm_siwx91x_data *data = dev->data;
173+
174+
if (channel >= ARRAY_SIZE(data->pwm_channel_cfg)) {
175+
return -EINVAL;
176+
}
177+
178+
*cycles = (uint64_t)data->pwm_channel_cfg[channel].frequency;
179+
180+
return 0;
181+
}
182+
183+
static int pwm_siwx91x_init(const struct device *dev)
184+
{
185+
const struct pwm_siwx91x_config *config = dev->config;
186+
struct pwm_siwx91x_data *data = dev->data;
187+
bool polarity_inverted = (config->pwm_polarity == PWM_POLARITY_INVERTED);
188+
uint32_t pwm_frequency;
189+
int ret;
190+
191+
ret = clock_control_on(config->clock_dev, config->clock_subsys);
192+
if (ret) {
193+
return ret;
194+
}
195+
196+
ret = clock_control_get_rate(config->clock_dev, config->clock_subsys, &pwm_frequency);
197+
if (ret) {
198+
return ret;
199+
}
200+
201+
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
202+
if (ret) {
203+
return ret;
204+
}
205+
206+
ARRAY_FOR_EACH(data->pwm_channel_cfg, i) {
207+
data->pwm_channel_cfg[i].frequency = pwm_frequency / config->ch_prescaler[i];
208+
}
209+
210+
ret = sl_si91x_pwm_set_output_polarity(polarity_inverted, !polarity_inverted);
211+
if (ret) {
212+
return -EINVAL;
213+
}
214+
215+
return 0;
216+
}
217+
218+
static DEVICE_API(pwm, pwm_siwx91x_driver_api) = {
219+
.set_cycles = pwm_siwx91x_set_cycles,
220+
.get_cycles_per_sec = pwm_siwx91x_get_cycles_per_sec,
221+
};
222+
223+
#define SIWX91X_PWM_INIT(inst) \
224+
PINCTRL_DT_INST_DEFINE(inst); \
225+
static struct pwm_siwx91x_data pwm_siwx91x_data_##inst; \
226+
static const struct pwm_siwx91x_config pwm_config_##inst = { \
227+
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(inst)), \
228+
.clock_subsys = (clock_control_subsys_t)DT_INST_PHA(inst, clocks, clkid), \
229+
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
230+
.ch_prescaler = DT_INST_PROP(inst, silabs_ch_prescaler), \
231+
.pwm_polarity = DT_INST_PROP(inst, silabs_pwm_polarity), \
232+
}; \
233+
DEVICE_DT_INST_DEFINE(inst, &pwm_siwx91x_init, NULL, &pwm_siwx91x_data_##inst, \
234+
&pwm_config_##inst, PRE_KERNEL_1, CONFIG_PWM_INIT_PRIORITY, \
235+
&pwm_siwx91x_driver_api);
236+
237+
DT_INST_FOREACH_STATUS_OKAY(SIWX91X_PWM_INIT)

dts/arm/silabs/siwg917.dtsi

+13
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,19 @@
261261
dma-channels = <12>;
262262
status = "disabled";
263263
};
264+
265+
pwm: pwm@47070000 {
266+
compatible = "silabs,siwx91x-pwm";
267+
#address-cells = <1>;
268+
#size-cells = <0>;
269+
reg = <0x47070000 0x14C>;
270+
interrupts = <48 0>;
271+
interrupt-names = "pwm";
272+
clocks = <&clock0 SIWX91X_CLK_PWM>;
273+
#pwm-cells = <2>;
274+
silabs,ch_prescaler = <64 64 64 64>;
275+
status = "disabled";
276+
};
264277
};
265278
};
266279

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
description: |
2+
Silabs siwx91x PWM Controller
3+
4+
The siwx91x PWM controller is designed to generate PWM signals. The mapping
5+
between PWM channels and GPIO pins is configured through pinctrl settings.
6+
7+
compatible: "silabs,siwx91x-pwm"
8+
9+
include: [base.yaml, pinctrl-device.yaml, pwm-controller.yaml]
10+
11+
properties:
12+
"#pwm-cells":
13+
const: 2
14+
15+
silabs,ch_prescaler:
16+
type: array
17+
required: true
18+
description: |
19+
Contains the prescaler values for all the 4 channels
20+
enum:
21+
- 1
22+
- 2
23+
- 4
24+
- 8
25+
- 16
26+
- 32
27+
- 64
28+
29+
silabs,pwm_polarity:
30+
type: int
31+
required: true
32+
description: |
33+
Common PWM polarity for all the channels
34+
0 - Normal polarity
35+
1 - Inverted polarity
36+
37+
pwm-cells:
38+
- channel
39+
- period

include/zephyr/dt-bindings/clock/silabs/siwx91x-clock.h

+1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@
1212
#define SIWX91X_CLK_I2C0 5
1313
#define SIWX91X_CLK_I2C1 6
1414
#define SIWX91X_CLK_DMA0 7
15+
#define SIWX91X_CLK_PWM 9
1516

1617
#endif

modules/hal_silabs/wiseconnect/CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ zephyr_compile_definitions(
2222
zephyr_include_directories(
2323
${SISDK_DIR}/platform/common/inc
2424
${SISDK_DIR}/platform/common/config
25+
${WISECONNECT_DIR}/components/board/silabs/inc
2526
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/core/config
2627
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/core/chip/inc
2728
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/rom_driver/inc
@@ -40,11 +41,13 @@ zephyr_library_sources(
4041
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/core/chip/src/rsi_deepsleep_soc.c
4142
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/core/chip/src/system_si91x.c
4243
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/peripheral_drivers/src/clock_update.c
44+
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/peripheral_drivers/src/rsi_pwm.c
4345
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/systemlevel/src/rsi_ipmu.c
4446
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/systemlevel/src/rsi_pll.c
4547
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/systemlevel/src/rsi_ulpss_clk.c
4648
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/service/clock_manager/src/sl_si91x_clock_manager.c
4749
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/unified_api/src/sl_si91x_driver_gpio.c
50+
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/unified_api/src/sl_si91x_pwm.c
4851
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/drivers/unified_peripheral_drivers/src/sl_si91x_peripheral_gpio.c
4952
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/core/chip/src/iPMU_prog/iPMU_dotc/ipmu_apis.c
5053
${WISECONNECT_DIR}/components/device/silabs/si91x/mcu/core/chip/src/iPMU_prog/iPMU_dotc/rsi_system_config_917.c

0 commit comments

Comments
 (0)