Skip to content

Commit 935c8e4

Browse files
smalaekartben
authored andcommitted
drivers: pwm: siwx91x: Add siwx91x PWM driver
Implement PWM driver for siwx91x device Signed-off-by: Sai Santhosh Malae <Santhosh.Malae@silabs.com>
1 parent 128ba44 commit 935c8e4

File tree

12 files changed

+345
-1
lines changed

12 files changed

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

+236
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
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+
switch (prescale) {
51+
case 1:
52+
return SL_TIME_PERIOD_PRESCALE_1;
53+
case 2:
54+
return SL_TIME_PERIOD_PRESCALE_2;
55+
case 4:
56+
return SL_TIME_PERIOD_PRESCALE_4;
57+
case 8:
58+
return SL_TIME_PERIOD_PRESCALE_8;
59+
case 16:
60+
return SL_TIME_PERIOD_PRESCALE_16;
61+
case 32:
62+
return SL_TIME_PERIOD_PRESCALE_32;
63+
case 64:
64+
return SL_TIME_PERIOD_PRESCALE_64;
65+
default:
66+
return -EINVAL;
67+
}
68+
}
69+
70+
/* Program PWM channel with the default configurations */
71+
static int siwx91x_default_channel_config(const struct device *dev, uint32_t channel)
72+
{
73+
const struct pwm_siwx91x_config *config = dev->config;
74+
int prescale_reg_value = siwx91x_prescale_convert(config->ch_prescaler[channel]);
75+
int ret;
76+
77+
if (prescale_reg_value < 0) {
78+
return -EINVAL;
79+
}
80+
81+
ret = sl_si91x_pwm_set_output_mode(SL_MODE_INDEPENDENT, channel);
82+
if (ret) {
83+
return -EINVAL;
84+
}
85+
86+
ret = sl_si91x_pwm_set_base_timer_mode(SL_FREE_RUN_MODE, channel);
87+
if (ret) {
88+
return -EINVAL;
89+
}
90+
91+
ret = sl_si91x_pwm_control_base_timer(SL_BASE_TIMER_EACH_CHANNEL);
92+
if (ret) {
93+
return -EINVAL;
94+
}
95+
96+
ret = sl_si91x_pwm_control_period(SL_TIME_PERIOD_POSTSCALE_1_1, prescale_reg_value,
97+
channel);
98+
if (ret) {
99+
return -EINVAL;
100+
}
101+
102+
return 0;
103+
}
104+
105+
static int pwm_siwx91x_set_cycles(const struct device *dev, uint32_t channel,
106+
uint32_t period_cycles, uint32_t pulse_cycles, pwm_flags_t flags)
107+
{
108+
const struct pwm_siwx91x_config *config = dev->config;
109+
struct pwm_siwx91x_data *data = dev->data;
110+
uint32_t prev_period;
111+
uint32_t duty_cycle;
112+
int ret;
113+
114+
if (channel >= ARRAY_SIZE(data->pwm_channel_cfg)) {
115+
return -EINVAL;
116+
}
117+
118+
if (config->pwm_polarity != flags) {
119+
/* Polarity mismatch */
120+
return -ENOTSUP;
121+
}
122+
123+
if (data->pwm_channel_cfg[channel].is_chan_active == false) {
124+
/* Configure the channel with default parameters */
125+
ret = siwx91x_default_channel_config(dev, channel);
126+
if (ret) {
127+
return -EINVAL;
128+
}
129+
}
130+
131+
ret = sl_si91x_pwm_get_time_period(channel, (uint16_t *)&prev_period);
132+
if (ret) {
133+
return -EINVAL;
134+
}
135+
136+
if (period_cycles != prev_period) {
137+
ret = sl_si91x_pwm_set_time_period(channel, period_cycles, 0);
138+
if (ret) {
139+
/* Programmed value must be out of range (>65535) */
140+
return -EINVAL;
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_cfg[channel].duty_cycle) {
149+
ret = sl_si91x_pwm_set_duty_cycle(pulse_cycles, channel);
150+
if (ret) {
151+
return -EINVAL;
152+
}
153+
data->pwm_channel_cfg[channel].duty_cycle = duty_cycle;
154+
}
155+
156+
if (data->pwm_channel_cfg[channel].is_chan_active == false) {
157+
/* Start PWM after configuring the channel for first time */
158+
ret = sl_si91x_pwm_start(channel);
159+
if (ret) {
160+
return -EINVAL;
161+
}
162+
data->pwm_channel_cfg[channel].is_chan_active = true;
163+
}
164+
165+
return 0;
166+
}
167+
168+
static int pwm_siwx91x_get_cycles_per_sec(const struct device *dev, uint32_t channel,
169+
uint64_t *cycles)
170+
{
171+
struct pwm_siwx91x_data *data = dev->data;
172+
173+
if (channel >= ARRAY_SIZE(data->pwm_channel_cfg)) {
174+
return -EINVAL;
175+
}
176+
177+
*cycles = (uint64_t)data->pwm_channel_cfg[channel].frequency;
178+
179+
return 0;
180+
}
181+
182+
static int pwm_siwx91x_init(const struct device *dev)
183+
{
184+
const struct pwm_siwx91x_config *config = dev->config;
185+
struct pwm_siwx91x_data *data = dev->data;
186+
bool polarity_inverted = (config->pwm_polarity == PWM_POLARITY_INVERTED);
187+
uint32_t pwm_frequency;
188+
int ret;
189+
190+
ret = clock_control_on(config->clock_dev, config->clock_subsys);
191+
if (ret) {
192+
return ret;
193+
}
194+
195+
ret = clock_control_get_rate(config->clock_dev, config->clock_subsys, &pwm_frequency);
196+
if (ret) {
197+
return ret;
198+
}
199+
200+
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
201+
if (ret) {
202+
return ret;
203+
}
204+
205+
ARRAY_FOR_EACH(data->pwm_channel_cfg, i) {
206+
data->pwm_channel_cfg[i].frequency = pwm_frequency / config->ch_prescaler[i];
207+
}
208+
209+
ret = sl_si91x_pwm_set_output_polarity(polarity_inverted, !polarity_inverted);
210+
if (ret) {
211+
return -EINVAL;
212+
}
213+
214+
return 0;
215+
}
216+
217+
static DEVICE_API(pwm, pwm_siwx91x_driver_api) = {
218+
.set_cycles = pwm_siwx91x_set_cycles,
219+
.get_cycles_per_sec = pwm_siwx91x_get_cycles_per_sec,
220+
};
221+
222+
#define SIWX91X_PWM_INIT(inst) \
223+
PINCTRL_DT_INST_DEFINE(inst); \
224+
static struct pwm_siwx91x_data pwm_siwx91x_data_##inst; \
225+
static const struct pwm_siwx91x_config pwm_config_##inst = { \
226+
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(inst)), \
227+
.clock_subsys = (clock_control_subsys_t)DT_INST_PHA(inst, clocks, clkid), \
228+
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
229+
.ch_prescaler = DT_INST_PROP(inst, silabs_ch_prescaler), \
230+
.pwm_polarity = DT_INST_PROP(inst, silabs_pwm_polarity), \
231+
}; \
232+
DEVICE_DT_INST_DEFINE(inst, &pwm_siwx91x_init, NULL, &pwm_siwx91x_data_##inst, \
233+
&pwm_config_##inst, PRE_KERNEL_1, CONFIG_PWM_INIT_PRIORITY, \
234+
&pwm_siwx91x_driver_api);
235+
236+
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)