Skip to content

Commit d878027

Browse files
henrikbrixandersenkartben
authored andcommitted
drivers: pwm: add driver for the NEORV32 PWM controller
Add driver for the NEORV32 PWM controller. Signed-off-by: Henrik Brix Andersen <[email protected]>
1 parent 87e38af commit d878027

File tree

4 files changed

+176
-0
lines changed

4 files changed

+176
-0
lines changed

drivers/pwm/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ zephyr_library_sources_ifdef(CONFIG_PWM_RENESAS_RA pwm_renesas_ra.c)
5151
zephyr_library_sources_ifdef(CONFIG_PWM_INFINEON_CAT1 pwm_ifx_cat1.c)
5252
zephyr_library_sources_ifdef(CONFIG_PWM_FAKE pwm_fake.c)
5353
zephyr_library_sources_ifdef(CONFIG_PWM_RENESAS_RZ_GPT pwm_renesas_rz_gpt.c)
54+
zephyr_library_sources_ifdef(CONFIG_PWM_NEORV32 pwm_neorv32.c)
5455
zephyr_library_sources_ifdef(CONFIG_USERSPACE pwm_handlers.c)
5556
zephyr_library_sources_ifdef(CONFIG_PWM_CAPTURE pwm_capture.c)
5657
zephyr_library_sources_ifdef(CONFIG_PWM_SHELL pwm_shell.c)

drivers/pwm/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,6 @@ source "drivers/pwm/Kconfig.renesas_rz"
124124

125125
source "drivers/pwm/Kconfig.rts5912"
126126

127+
source "drivers/pwm/Kconfig.neorv32"
128+
127129
endif # PWM

drivers/pwm/Kconfig.neorv32

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# NEORV32 PWM configuration options
2+
3+
# Copyright (c) 2025 Henrik Brix Andersen <[email protected]>
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config PWM_NEORV32
7+
bool "NEORV32 PWM driver"
8+
default y
9+
depends on DT_HAS_NEORV32_PWM_ENABLED
10+
depends on SYSCON
11+
help
12+
Enable NEORV32 PWM driver.

drivers/pwm/pwm_neorv32.c

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Copyright (c) 2025 Henrik Brix Andersen <[email protected]>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT neorv32_pwm
8+
9+
#include <zephyr/device.h>
10+
#include <zephyr/drivers/pwm.h>
11+
#include <zephyr/drivers/syscon.h>
12+
#include <zephyr/sys/sys_io.h>
13+
#include <zephyr/sys/util.h>
14+
#include <zephyr/logging/log.h>
15+
16+
#include <soc.h>
17+
18+
LOG_MODULE_REGISTER(pwm_neorv32, CONFIG_PWM_LOG_LEVEL);
19+
20+
/* NEORV32 PWM CHANNEL_CFG[0..15] register bits */
21+
#define NEORV32_PWM_CFG_EN BIT(31)
22+
#define NEORV32_PWM_CFG_PRSC GENMASK(30, 28)
23+
#define NEORV32_PWM_CFG_POL BIT(27)
24+
#define NEORV32_PWM_CFG_CDIV GENMASK(17, 8)
25+
#define NEORV32_PWM_CFG_DUTY GENMASK(7, 0)
26+
27+
/* Maximum number of PWMs supported */
28+
#define NEORV32_PWM_CHANNELS 16
29+
30+
struct neorv32_pwm_config {
31+
const struct device *syscon;
32+
mm_reg_t base;
33+
};
34+
35+
static inline void neorv32_pwm_write_channel_cfg(const struct device *dev, uint8_t channel,
36+
uint32_t cfg)
37+
{
38+
const struct neorv32_pwm_config *config = dev->config;
39+
40+
__ASSERT_NO_MSG(channel < NEORV32_PWM_CHANNELS);
41+
42+
sys_write32(cfg, config->base + (channel * sizeof(uint32_t)));
43+
}
44+
45+
static int neorv32_pwm_set_cycles(const struct device *dev, uint32_t channel,
46+
uint32_t period_cycles, uint32_t pulse_cycles, pwm_flags_t flags)
47+
{
48+
static const uint16_t cdiv_max = FIELD_GET(NEORV32_PWM_CFG_CDIV, NEORV32_PWM_CFG_CDIV);
49+
static const uint16_t steps = FIELD_GET(NEORV32_PWM_CFG_DUTY, NEORV32_PWM_CFG_DUTY) + 1U;
50+
static const uint16_t prsc_tbl[] = {2, 4, 8, 64, 128, 1024, 2048, 4096};
51+
uint32_t cfg = 0U;
52+
uint8_t duty = 0U;
53+
uint16_t cdiv;
54+
uint8_t prsc;
55+
56+
if (channel >= NEORV32_PWM_CHANNELS) {
57+
LOG_ERR("invalid PWM channel %u", channel);
58+
return -EINVAL;
59+
}
60+
61+
if (pulse_cycles == 0U) {
62+
/* Constant inactive level */
63+
if ((flags & PWM_POLARITY_INVERTED) != 0U) {
64+
cfg |= NEORV32_PWM_CFG_POL;
65+
}
66+
} else if (pulse_cycles == period_cycles) {
67+
/* Constant active level */
68+
if ((flags & PWM_POLARITY_INVERTED) == 0U) {
69+
cfg |= NEORV32_PWM_CFG_POL;
70+
}
71+
} else {
72+
/* PWM enabled */
73+
if ((flags & PWM_POLARITY_INVERTED) != 0U) {
74+
cfg |= NEORV32_PWM_CFG_POL;
75+
}
76+
77+
for (prsc = 0; prsc < ARRAY_SIZE(prsc_tbl); prsc++) {
78+
if (period_cycles / prsc_tbl[prsc] <= steps * (1U + cdiv_max)) {
79+
break;
80+
}
81+
}
82+
83+
cdiv = DIV_ROUND_CLOSEST(DIV_ROUND_CLOSEST(period_cycles, prsc_tbl[prsc]), steps) -
84+
1U;
85+
86+
duty = CLAMP(DIV_ROUND_CLOSEST((uint64_t)(pulse_cycles * steps), period_cycles),
87+
1U, steps - 1U);
88+
89+
cfg |= NEORV32_PWM_CFG_EN | FIELD_PREP(NEORV32_PWM_CFG_PRSC, prsc) |
90+
FIELD_PREP(NEORV32_PWM_CFG_CDIV, cdiv) |
91+
FIELD_PREP(NEORV32_PWM_CFG_DUTY, duty);
92+
}
93+
94+
neorv32_pwm_write_channel_cfg(dev, channel, cfg);
95+
96+
return 0;
97+
}
98+
99+
static int neorv32_pwm_get_cycles_per_sec(const struct device *dev, uint32_t channel,
100+
uint64_t *cycles)
101+
{
102+
const struct neorv32_pwm_config *config = dev->config;
103+
uint32_t clk;
104+
int err;
105+
106+
if (channel >= NEORV32_PWM_CHANNELS) {
107+
LOG_ERR("invalid PWM channel %u", channel);
108+
return -EINVAL;
109+
}
110+
111+
err = syscon_read_reg(config->syscon, NEORV32_SYSINFO_CLK, &clk);
112+
if (err < 0) {
113+
LOG_ERR("failed to determine clock rate (err %d)", err);
114+
return -EIO;
115+
}
116+
117+
*cycles = clk;
118+
119+
return 0;
120+
}
121+
122+
static int neorv32_pwm_init(const struct device *dev)
123+
{
124+
const struct neorv32_pwm_config *config = dev->config;
125+
uint32_t features;
126+
int err;
127+
128+
if (!device_is_ready(config->syscon)) {
129+
LOG_ERR("syscon device not ready");
130+
return -EINVAL;
131+
}
132+
133+
err = syscon_read_reg(config->syscon, NEORV32_SYSINFO_SOC, &features);
134+
if (err < 0) {
135+
LOG_ERR("failed to determine implemented features (err %d)", err);
136+
return -EIO;
137+
}
138+
139+
if ((features & NEORV32_SYSINFO_SOC_IO_PWM) == 0) {
140+
LOG_ERR("neorv32 pwm not supported");
141+
return -ENODEV;
142+
}
143+
144+
return 0;
145+
}
146+
147+
static DEVICE_API(pwm, neorv32_pwm_driver_api) = {
148+
.set_cycles = neorv32_pwm_set_cycles,
149+
.get_cycles_per_sec = neorv32_pwm_get_cycles_per_sec,
150+
};
151+
152+
#define NEORV32_PWM_INIT(n) \
153+
static const struct neorv32_pwm_config neorv32_pwm_##n##_config = { \
154+
.syscon = DEVICE_DT_GET(DT_INST_PHANDLE(n, syscon)), \
155+
.base = DT_INST_REG_ADDR(n), \
156+
}; \
157+
\
158+
DEVICE_DT_INST_DEFINE(n, neorv32_pwm_init, NULL, NULL, &neorv32_pwm_##n##_config, \
159+
POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, &neorv32_pwm_driver_api);
160+
161+
DT_INST_FOREACH_STATUS_OKAY(NEORV32_PWM_INIT)

0 commit comments

Comments
 (0)