Skip to content

Commit 719b509

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 060fefb commit 719b509

File tree

12 files changed

+327
-1
lines changed

12 files changed

+327
-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

+4
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
}

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

+230
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
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_config {
25+
/* Pointer to the clock device structure */
26+
const struct device *clock_dev;
27+
/* Clock control subsystem */
28+
clock_control_subsys_t clock_subsys;
29+
/* Pointer to the pin control device configuration */
30+
const struct pinctrl_dev_config *pcfg;
31+
/* Prescaler information of the channels */
32+
uint8_t ch_prescaler[PWM_CHANNELS];
33+
};
34+
35+
struct pwm_siwx91x_data {
36+
sl_pwm_config_t pwm_channel_config[PWM_CHANNELS];
37+
uint8_t pwm_polarity;
38+
};
39+
40+
/* Function to convert prescaler value to a programmable reg value */
41+
static int siwx91x_prescale_convert(uint8_t prescale)
42+
{
43+
44+
switch (prescale) {
45+
case 1:
46+
return SL_TIME_PERIOD_PRESCALE_1;
47+
case 2:
48+
return SL_TIME_PERIOD_PRESCALE_2;
49+
case 4:
50+
return SL_TIME_PERIOD_PRESCALE_4;
51+
case 8:
52+
return SL_TIME_PERIOD_PRESCALE_8;
53+
case 16:
54+
return SL_TIME_PERIOD_PRESCALE_16;
55+
case 32:
56+
return SL_TIME_PERIOD_PRESCALE_32;
57+
case 64:
58+
return SL_TIME_PERIOD_PRESCALE_64;
59+
default:
60+
return -EINVAL;
61+
}
62+
}
63+
64+
/* Program PWM channel with the default configurations */
65+
static int siwx91x_default_channel_config(const struct device *dev, uint32_t channel)
66+
{
67+
const struct pwm_siwx91x_config *config = dev->config;
68+
struct pwm_siwx91x_data *data = dev->data;
69+
int prescale_reg_value = siwx91x_prescale_convert(config->ch_prescaler[channel]);
70+
71+
if (prescale_reg_value < 0) {
72+
return -EINVAL;
73+
}
74+
75+
if (sl_si91x_pwm_set_output_mode(data->pwm_channel_config[channel].is_mode, channel)) {
76+
return -EINVAL;
77+
}
78+
79+
if (sl_si91x_pwm_set_base_timer_mode(data->pwm_channel_config[channel].base_timer_mode,
80+
channel)) {
81+
return -EINVAL;
82+
}
83+
84+
if (sl_si91x_pwm_control_base_timer(
85+
data->pwm_channel_config[channel].channel_timer_selection)) {
86+
return -EINVAL;
87+
}
88+
89+
if (sl_si91x_pwm_control_period(SL_TIME_PERIOD_POSTSCALE_1_1, prescale_reg_value,
90+
channel)) {
91+
return -EINVAL;
92+
}
93+
94+
return 0;
95+
}
96+
97+
static int pwm_siwx91x_set_cycles(const struct device *dev, uint32_t channel,
98+
uint32_t period_cycles, uint32_t pulse_cycles, pwm_flags_t flags)
99+
{
100+
struct pwm_siwx91x_data *data = dev->data;
101+
uint32_t prev_period;
102+
uint32_t duty_cycle;
103+
104+
if (channel >= PWM_CHANNELS) {
105+
return -EINVAL;
106+
}
107+
108+
if (flags != data->pwm_polarity) {
109+
if (flags == PWM_POLARITY_NORMAL) {
110+
/* Active high pulse */
111+
data->pwm_channel_config[channel].is_polarity_high = true;
112+
data->pwm_channel_config[channel].is_polarity_low = false;
113+
} else {
114+
/* Active low pulse */
115+
data->pwm_channel_config[channel].is_polarity_high = false;
116+
data->pwm_channel_config[channel].is_polarity_low = true;
117+
}
118+
if (sl_si91x_pwm_set_output_polarity(
119+
data->pwm_channel_config[channel].is_polarity_low,
120+
data->pwm_channel_config[channel].is_polarity_high)) {
121+
return -EINVAL;
122+
}
123+
data->pwm_polarity = flags;
124+
}
125+
126+
if (data->pwm_channel_config[channel].channel == DEFAULT_VALUE) {
127+
/* Configure the channel with default parameters */
128+
if (siwx91x_default_channel_config(dev, channel)) {
129+
return -EINVAL;
130+
}
131+
}
132+
133+
if (sl_si91x_pwm_get_time_period(channel, (uint16_t *)&prev_period)) {
134+
return -EINVAL;
135+
}
136+
137+
if (period_cycles != prev_period) {
138+
if (sl_si91x_pwm_set_time_period(channel, period_cycles, 0)) {
139+
/* Programmed value must be out of range (>65535) */
140+
return -ENOTSUP;
141+
}
142+
}
143+
144+
/* Calculate the duty cycle */
145+
duty_cycle = pulse_cycles * 100;
146+
duty_cycle /= period_cycles;
147+
148+
if (duty_cycle != data->pwm_channel_config[channel].duty_cycle) {
149+
if (sl_si91x_pwm_set_duty_cycle(pulse_cycles, channel)) {
150+
/* Programmed value must be out of range (>65535) */
151+
return -ENOTSUP;
152+
}
153+
data->pwm_channel_config[channel].duty_cycle = duty_cycle;
154+
}
155+
156+
if (data->pwm_channel_config[channel].channel == DEFAULT_VALUE) {
157+
/* Start PWM after configuring the channel for first time */
158+
if (sl_si91x_pwm_start(channel)) {
159+
return -EINVAL;
160+
}
161+
data->pwm_channel_config[channel].channel = channel;
162+
}
163+
164+
return 0;
165+
}
166+
167+
static int pwm_siwx91x_get_cycles_per_sec(const struct device *dev, uint32_t channel,
168+
uint64_t *cycles)
169+
{
170+
struct pwm_siwx91x_data *data = dev->data;
171+
172+
if (channel >= PWM_CHANNELS) {
173+
return -EINVAL;
174+
}
175+
176+
*cycles = (uint64_t)data->pwm_channel_config[channel].frequency;
177+
178+
return 0;
179+
}
180+
181+
static int pwm_siwx91x_init(const struct device *dev)
182+
{
183+
const struct pwm_siwx91x_config *config = dev->config;
184+
struct pwm_siwx91x_data *data = dev->data;
185+
int ret;
186+
187+
ret = clock_control_on(config->clock_dev, config->clock_subsys);
188+
if (ret) {
189+
return ret;
190+
}
191+
192+
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
193+
if (ret) {
194+
return ret;
195+
}
196+
197+
/* Initialize the channel config structures with default values */
198+
for (int i = 0; i < PWM_CHANNELS; i++) {
199+
data->pwm_channel_config[i].channel = DEFAULT_VALUE;
200+
data->pwm_channel_config[i].is_mode = SL_MODE_INDEPENDENT;
201+
data->pwm_channel_config[i].base_timer_mode = SL_FREE_RUN_MODE;
202+
data->pwm_channel_config[i].channel_timer_selection = SL_BASE_TIMER_EACH_CHANNEL;
203+
data->pwm_channel_config[i].frequency =
204+
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / config->ch_prescaler[i];
205+
}
206+
207+
data->pwm_polarity = DEFAULT_VALUE;
208+
209+
return 0;
210+
}
211+
212+
static const struct pwm_driver_api pwm_siwx91x_driver_api = {
213+
.set_cycles = pwm_siwx91x_set_cycles,
214+
.get_cycles_per_sec = pwm_siwx91x_get_cycles_per_sec,
215+
};
216+
217+
#define SIWX91X_PWM_INIT(inst) \
218+
PINCTRL_DT_INST_DEFINE(inst); \
219+
static struct pwm_siwx91x_data pwm_siwx91x_data_##inst; \
220+
static const struct pwm_siwx91x_config pwm_config_##inst = { \
221+
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(inst)), \
222+
.clock_subsys = (clock_control_subsys_t)DT_INST_PHA(inst, clocks, clkid), \
223+
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
224+
.ch_prescaler = DT_INST_PROP(inst, silabs_ch_prescaler), \
225+
}; \
226+
DEVICE_DT_INST_DEFINE(inst, &pwm_siwx91x_init, NULL, &pwm_siwx91x_data_##inst, \
227+
&pwm_config_##inst, PRE_KERNEL_1, CONFIG_PWM_INIT_PRIORITY, \
228+
&pwm_siwx91x_driver_api);
229+
230+
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 = <3>;
274+
silabs,ch_prescaler = <64 64 64 64>;
275+
status = "disabled";
276+
};
264277
};
265278
};
266279

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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: 3
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+
pwm-cells:
30+
- channel
31+
- period
32+
- flags

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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (c) 2025 Silicon Laboratories Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/dt-bindings/pwm/pwm.h>
8+
9+
/ {
10+
aliases {
11+
pwm-0 = &pwm;
12+
};
13+
};
14+
15+
&pinctrl0 {
16+
pwm_ch: pwm_ch {
17+
group1 {
18+
pinmux = <PWM_0H_PA7>; /* GPIO_7 P20 */
19+
};
20+
};
21+
};
22+
23+
&pwm {
24+
pinctrl-0 = <&pwm_ch>;
25+
pinctrl-names = "default";
26+
pwm_channel1: pwm_channel1 {
27+
pwms = <&pwm 0 1000000 PWM_POLARITY_NORMAL>;
28+
};
29+
status = "okay";
30+
};

west.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ manifest:
228228
groups:
229229
- hal
230230
- name: hal_silabs
231-
revision: 8a173e9e566a396a19d18da4661cb54ce098f268
231+
revision: pull/90/head
232232
path: modules/hal/silabs
233233
groups:
234234
- hal

0 commit comments

Comments
 (0)