Skip to content

Commit 51d1708

Browse files
diegosueiroMaureenHelm
authored andcommitted
drivers: add i.MX PWM driver
Adds the PWM shim driver for i.MX Signed-off-by: Diego Sueiro <[email protected]>
1 parent c245430 commit 51d1708

File tree

4 files changed

+234
-0
lines changed

4 files changed

+234
-0
lines changed

drivers/pwm/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ zephyr_library_sources_ifdef(CONFIG_PWM_STM32 pwm_stm32.c)
77
zephyr_library_sources_ifdef(CONFIG_PWM_NRF5_SW pwm_nrf5_sw.c)
88
zephyr_library_sources_ifdef(CONFIG_PWM_NRFX pwm_nrfx.c)
99
zephyr_library_sources_ifdef(CONFIG_PWM_MCUX_FTM pwm_mcux_ftm.c)
10+
zephyr_library_sources_ifdef(CONFIG_PWM_IMX pwm_imx.c)
1011
zephyr_library_sources_ifdef(CONFIG_PWM_LED_ESP32 pwm_led_esp32.c)
1112

1213
zephyr_library_sources_ifdef(CONFIG_USERSPACE pwm_handlers.c)

drivers/pwm/Kconfig

+5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ config PWM_2
4040
config PWM_3
4141
bool "Enable PWM port 3"
4242

43+
config PWM_4
44+
bool "Enable PWM port 4"
45+
4346
source "drivers/pwm/Kconfig.pca9685"
4447

4548
source "drivers/pwm/Kconfig.qmsi"
@@ -54,6 +57,8 @@ source "drivers/pwm/Kconfig.nrfx"
5457

5558
source "drivers/pwm/Kconfig.mcux_ftm"
5659

60+
source "drivers/pwm/Kconfig.imx"
61+
5762
source "drivers/pwm/Kconfig.esp32"
5863

5964
endif # PWM

drivers/pwm/Kconfig.imx

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Kconfig - i.MX PWM Config
2+
#
3+
# Copyright (c) 2018, Diego Sueiro
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
#
7+
8+
menuconfig PWM_IMX
9+
bool
10+
prompt "i.MX PWM Driver"
11+
depends on PWM
12+
help
13+
Enable support for i.MX pwm driver.
14+
15+
config PWM_PWMSWR_LOOP
16+
int "Loop count for PWM Software Reset"
17+
default 5
18+
depends on PWM_IMX
19+
help
20+
Loop count for PWM Software Reset when disabling PWM channel.

drivers/pwm/pwm_imx.c

+208
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/*
2+
* Copyright (c) 2018, Diego Sueiro <[email protected]>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <errno.h>
8+
#include <pwm.h>
9+
#include <soc.h>
10+
#include <device_imx.h>
11+
12+
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_PWM_LEVEL
13+
#include <logging/sys_log.h>
14+
15+
#define PWM_PWMSR_FIFOAV_4WORDS 0x4
16+
17+
#define PWM_PWMCR_SWR(x) (((uint32_t)(((uint32_t)(x)) \
18+
<<PWM_PWMCR_SWR_SHIFT))&PWM_PWMCR_SWR_MASK)
19+
20+
#define DEV_CFG(dev) \
21+
((const struct imx_pwm_config * const)(dev)->config->config_info)
22+
#define DEV_DATA(dev) \
23+
((struct imx_pwm_data * const)(dev)->driver_data)
24+
#define DEV_BASE(dev) \
25+
((PWM_Type *)(DEV_CFG(dev))->base)
26+
27+
struct imx_pwm_config {
28+
PWM_Type *base;
29+
u16_t prescaler;
30+
};
31+
32+
struct imx_pwm_data {
33+
u32_t period_cycles;
34+
};
35+
36+
37+
static bool imx_pwm_is_enabled(PWM_Type *base)
38+
{
39+
return PWM_PWMCR_REG(base) & PWM_PWMCR_EN_MASK;
40+
}
41+
42+
static int imx_pwm_get_cycles_per_sec(struct device *dev, u32_t pwm,
43+
u64_t *cycles)
44+
{
45+
PWM_Type *base = DEV_BASE(dev);
46+
const struct imx_pwm_config *config = DEV_CFG(dev);
47+
48+
*cycles = get_pwm_clock_freq(base) >> config->prescaler;
49+
50+
return 0;
51+
}
52+
53+
static int imx_pwm_pin_set(struct device *dev, u32_t pwm,
54+
u32_t period_cycles, u32_t pulse_cycles)
55+
{
56+
PWM_Type *base = DEV_BASE(dev);
57+
const struct imx_pwm_config *config = DEV_CFG(dev);
58+
struct imx_pwm_data *data = DEV_DATA(dev);
59+
unsigned int period_ms;
60+
bool enabled = imx_pwm_is_enabled(base);
61+
int wait_count = 0, fifoav;
62+
u32_t cr, sr;
63+
64+
65+
if ((period_cycles == 0) || (pulse_cycles > period_cycles)) {
66+
SYS_LOG_ERR("Invalid combination: period_cycles=%d, "
67+
"pulse_cycles=%d", period_cycles, pulse_cycles);
68+
return -EINVAL;
69+
}
70+
71+
SYS_LOG_DBG("enabled=%d, pulse_cycles=%d, period_cycles=%d,"
72+
" duty_cycle=%d\n", enabled, pulse_cycles, period_cycles,
73+
(100 * pulse_cycles / period_cycles));
74+
75+
/*
76+
* i.MX PWMv2 has a 4-word sample FIFO.
77+
* In order to avoid FIFO overflow issue, we do software reset
78+
* to clear all sample FIFO if the controller is disabled or
79+
* wait for a full PWM cycle to get a relinquished FIFO slot
80+
* when the controller is enabled and the FIFO is fully loaded.
81+
*/
82+
if (enabled) {
83+
sr = PWM_PWMSR_REG(base);
84+
fifoav = PWM_PWMSR_FIFOAV(sr);
85+
if (fifoav == PWM_PWMSR_FIFOAV_4WORDS) {
86+
period_ms = (get_pwm_clock_freq(base) >>
87+
config->prescaler) * MSEC_PER_SEC;
88+
k_sleep(K_MSEC(period_ms));
89+
90+
sr = PWM_PWMSR_REG(base);
91+
if (fifoav == PWM_PWMSR_FIFOAV(sr)) {
92+
SYS_LOG_WRN("there is no free FIFO slot\n");
93+
}
94+
}
95+
} else {
96+
PWM_PWMCR_REG(base) = PWM_PWMCR_SWR(1);
97+
do {
98+
k_sleep(1);
99+
cr = PWM_PWMCR_REG(base);
100+
} while ((PWM_PWMCR_SWR(cr)) &&
101+
(++wait_count < CONFIG_PWM_PWMSWR_LOOP));
102+
103+
if (PWM_PWMCR_SWR(cr)) {
104+
SYS_LOG_WRN("software reset timeout\n");
105+
}
106+
107+
}
108+
109+
/*
110+
* according to imx pwm RM, the real period value should be
111+
* PERIOD value in PWMPR plus 2.
112+
*/
113+
if (period_cycles > 2) {
114+
period_cycles -= 2;
115+
} else {
116+
return -EINVAL;
117+
}
118+
119+
PWM_PWMSAR_REG(base) = pulse_cycles;
120+
121+
if (data->period_cycles != period_cycles) {
122+
SYS_LOG_WRN("Changing period cycles from %d to %d in %s",
123+
data->period_cycles, period_cycles,
124+
dev->config->name);
125+
126+
data->period_cycles = period_cycles;
127+
PWM_PWMPR_REG(base) = period_cycles;
128+
}
129+
130+
cr = PWM_PWMCR_EN_MASK | PWM_PWMCR_PRESCALER(config->prescaler) |
131+
PWM_PWMCR_DOZEN_MASK | PWM_PWMCR_WAITEN_MASK |
132+
PWM_PWMCR_DBGEN_MASK | PWM_PWMCR_CLKSRC(2);
133+
134+
PWM_PWMCR_REG(base) = cr;
135+
136+
return 0;
137+
}
138+
139+
static int imx_pwm_init(struct device *dev)
140+
{
141+
struct imx_pwm_data *data = DEV_DATA(dev);
142+
PWM_Type *base = DEV_BASE(dev);
143+
144+
PWM_PWMPR_REG(base) = data->period_cycles;
145+
146+
return 0;
147+
}
148+
149+
static const struct pwm_driver_api imx_pwm_driver_api = {
150+
.pin_set = imx_pwm_pin_set,
151+
.get_cycles_per_sec = imx_pwm_get_cycles_per_sec,
152+
};
153+
154+
#ifdef CONFIG_PWM_1
155+
static const struct imx_pwm_config imx_pwm_config_1 = {
156+
.base = (PWM_Type *)PWM_1_BASE_ADDRESS,
157+
.prescaler = PWM_1_PRESCALER,
158+
};
159+
160+
static struct imx_pwm_data imx_pwm_data_1;
161+
162+
DEVICE_AND_API_INIT(imx_pwm_1, PWM_1_LABEL, &imx_pwm_init,
163+
&imx_pwm_data_1, &imx_pwm_config_1,
164+
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
165+
&imx_pwm_driver_api);
166+
#endif /* CONFIG_PWM_1 */
167+
168+
#ifdef CONFIG_PWM_2
169+
static const struct imx_pwm_config imx_pwm_config_2 = {
170+
.base = (PWM_Type *)PWM_2_BASE_ADDRESS,
171+
.prescaler = PWM_2_PRESCALER,
172+
};
173+
174+
static struct imx_pwm_data imx_pwm_data_2;
175+
176+
DEVICE_AND_API_INIT(imx_pwm_2, PWM_2_LABEL, &imx_pwm_init,
177+
&imx_pwm_data_2, &imx_pwm_config_2,
178+
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
179+
&imx_pwm_driver_api);
180+
#endif /* CONFIG_PWM_2 */
181+
182+
#ifdef CONFIG_PWM_3
183+
static const struct imx_pwm_config imx_pwm_config_3 = {
184+
.base = (PWM_Type *)PWM_3_BASE_ADDRESS,
185+
.prescaler = PWM_3_PRESCALER,
186+
};
187+
188+
static struct imx_pwm_data imx_pwm_data_3;
189+
190+
DEVICE_AND_API_INIT(imx_pwm_3, PWM_3_LABEL, &imx_pwm_init,
191+
&imx_pwm_data_3, &imx_pwm_config_3,
192+
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
193+
&imx_pwm_driver_api);
194+
#endif /* CONFIG_PWM_3 */
195+
196+
#ifdef CONFIG_PWM_4
197+
static const struct imx_pwm_config imx_pwm_config_4 = {
198+
.base = (PWM_Type *)PWM_4_BASE_ADDRESS,
199+
.prescaler = PWM_4_PRESCALER,
200+
};
201+
202+
static struct imx_pwm_data imx_pwm_data_4;
203+
204+
DEVICE_AND_API_INIT(imx_pwm_4, PWM_4_LABEL, &imx_pwm_init,
205+
&imx_pwm_data_4, &imx_pwm_config_4,
206+
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
207+
&imx_pwm_driver_api);
208+
#endif /* CONFIG_PWM_4 */

0 commit comments

Comments
 (0)