drivers: pwm: Add driver for xmc4xxx using ccu8 module

Adds driver for pwm on xmc4xxx using Capture Compare Unit 8 (CCU8)
module. There are two CCU8 nodes with each one having four slices.
Each slice has two output channels.

Unlike CCU4, this module can generate complementary high-side/low-side
signals for each output channel. A variable dead time can be added
during the off to on transitions to make sure that the
high-side/low-side signals are not on at the same time.

Signed-off-by: Andriy Gelman <andriy.gelman@gmail.com>
This commit is contained in:
Andriy Gelman 2023-06-12 17:43:50 -04:00 committed by Carles Cufí
parent 23b6e4f507
commit d8f955e375
12 changed files with 612 additions and 0 deletions

View File

@ -43,3 +43,15 @@
drive-push-pull;
hwctrl = "disabled";
};
&pwm_out_p5_9_ccu80_ch4_high {
drive-strength = "strong-medium-edge";
drive-push-pull;
hwctrl = "disabled";
};
&pwm_out_p5_8_ccu80_ch0_low {
drive-strength = "strong-medium-edge";
drive-push-pull;
hwctrl = "disabled";
};

View File

@ -9,6 +9,7 @@
#include <infineon/xmc4700_F144x2048.dtsi>
#include <infineon/xmc4700_F144x2048-intc.dtsi>
#include <zephyr/dt-bindings/pwm/pwm.h>
#include "xmc47_relax_kit-pinctrl.dtsi"
#include "arduino_r3_connector.dtsi"
@ -19,6 +20,7 @@
aliases {
led0 = &led1;
die-temp0 = &die_temp;
pwm-led0 = &pwm_led1;
};
leds {
@ -32,6 +34,18 @@
};
};
pwmleds {
compatible = "pwm-leds";
pwm_led1: pwm_led1 {
pwms = <&pwm_ccu80 4 PWM_SEC(1) PWM_POLARITY_NORMAL>;
label = "PWM LED1";
};
pwm_led2: pwm_led2 {
pwms = <&pwm_ccu80 0 PWM_SEC(1) PWM_POLARITY_NORMAL>;
label = "PWM LED2";
};
};
chosen {
zephyr,sram = &psram1;
zephyr,flash = &flash0;
@ -135,3 +149,14 @@
&gpio5 {
status = "okay";
};
/* this example is not using the high-side/low-side signals of the same channel */
/* the PWM signals are only used for the blink led example */
&pwm_ccu80 {
slice-prescaler = <15 15 15 15>;
slice-deadtime-prescaler = <3 3 3 3>;
channel-deadtime-high = <0 0 0 0 0 0 0 0>;
channel-deadtime-low = <0 0 0 0 0 0 0 0>;
pinctrl-0 = <&pwm_out_p5_9_ccu80_ch4_high &pwm_out_p5_8_ccu80_ch0_low>;
pinctrl-names = "default";
};

View File

@ -35,6 +35,7 @@ zephyr_library_sources_ifdef(CONFIG_PWM_RPI_PICO pwm_rpi_pico.c)
zephyr_library_sources_ifdef(CONFIG_PWM_BBLED_XEC pwm_mchp_xec_bbled.c)
zephyr_library_sources_ifdef(CONFIG_PWM_INTEL_BLINKY pwm_intel_blinky.c)
zephyr_library_sources_ifdef(CONFIG_PWM_XMC4XXX_CCU4 pwm_xmc4xxx_ccu4.c)
zephyr_library_sources_ifdef(CONFIG_PWM_XMC4XXX_CCU8 pwm_xmc4xxx_ccu8.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE pwm_handlers.c)
zephyr_library_sources_ifdef(CONFIG_PWM_CAPTURE pwm_capture.c)

View File

@ -91,4 +91,6 @@ source "drivers/pwm/Kconfig.intel_blinky"
source "drivers/pwm/Kconfig.xmc4xxx_ccu4"
source "drivers/pwm/Kconfig.xmc4xxx_ccu8"
endif # PWM

View File

@ -0,0 +1,9 @@
# Copyright (c) 2023 SLB
# SPDX-License-Identifier: Apache-2.0
config PWM_XMC4XXX_CCU8
bool "Infineon XMC4XXX CCU4 driver"
default y
depends on DT_HAS_INFINEON_XMC4XXX_CCU8_PWM_ENABLED
help
Enables Infineon XMC4XXX CCU8 PWM driver.

View File

@ -0,0 +1,171 @@
/*
* Copyright (c) 2023 SLB
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT infineon_xmc4xxx_ccu8_pwm
#include <soc.h>
#include <zephyr/dt-bindings/pwm/pwm.h>
#include <zephyr/drivers/pwm.h>
#include <zephyr/drivers/pinctrl.h>
#include <xmc_ccu8.h>
#include <xmc_scu.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(pwm_xmc4xxx_ccu8, CONFIG_PWM_LOG_LEVEL);
#define NUM_SLICES 4
#define NUM_CHANNELS (NUM_SLICES * 2)
#define MAX_DEAD_TIME_VALUE 255
#define MAX_SLICE_PRESCALER 15
#define MAX_DEADTIME_PRESCALER 3
#define SLICE_ADDR_FROM_MODULE(module_ptr, idx) ((uint32_t)(module_ptr) + ((idx) + 1) * 0x100)
struct pwm_xmc4xxx_ccu8_config {
XMC_CCU8_MODULE_t *ccu8;
const struct pinctrl_dev_config *pcfg;
const uint8_t slice_prescaler[NUM_SLICES];
const uint8_t slice_deadtime_prescaler[NUM_SLICES];
const uint32_t deadtime_high_ns[NUM_CHANNELS];
const uint32_t deadtime_low_ns[NUM_CHANNELS];
};
static int pwm_xmc4xxx_ccu8_init(const struct device *dev)
{
const struct pwm_xmc4xxx_ccu8_config *config = dev->config;
/* enables the CCU8 clock and ungates clock to CCU8x */
XMC_CCU8_EnableModule(config->ccu8);
XMC_CCU8_StartPrescaler(config->ccu8);
for (int i = 0; i < NUM_SLICES; i++) {
XMC_CCU8_SLICE_t *slice;
XMC_CCU8_SLICE_DEAD_TIME_CONFIG_t deadtime_conf = {0};
XMC_CCU8_SLICE_COMPARE_CONFIG_t slice_conf = {
.prescaler_initval = config->slice_prescaler[i],
.invert_out1 = 1,
.invert_out3 = 1,
};
if (config->slice_prescaler[i] > MAX_SLICE_PRESCALER) {
LOG_ERR("Invalid slice_prescaler value %d. Range [0, 15]",
config->slice_prescaler[i]);
return -EINVAL;
}
if (config->slice_deadtime_prescaler[i] > MAX_DEADTIME_PRESCALER) {
LOG_ERR("Invalid dead time prescaler value %d. Range [0, 3]",
config->slice_deadtime_prescaler[i]);
return -EINVAL;
}
slice = (XMC_CCU8_SLICE_t *)SLICE_ADDR_FROM_MODULE(config->ccu8, i);
XMC_CCU8_SLICE_CompareInit(slice, &slice_conf);
deadtime_conf.div = config->slice_deadtime_prescaler[i];
if (config->deadtime_high_ns[2*i] > 0 || config->deadtime_low_ns[2*i] > 0) {
deadtime_conf.enable_dead_time_channel1 = 1;
}
deadtime_conf.channel1_st_path = config->deadtime_high_ns[2*i] > 0;
deadtime_conf.channel1_inv_st_path = config->deadtime_low_ns[2*i] > 0;
if (config->deadtime_high_ns[2*i + 1] > 0 || config->deadtime_low_ns[2*i + 1] > 0) {
deadtime_conf.enable_dead_time_channel2 = 1;
}
deadtime_conf.channel2_st_path = config->deadtime_high_ns[2*i + 1] > 0;
deadtime_conf.channel2_inv_st_path = config->deadtime_low_ns[2*i + 1] > 0;
XMC_CCU8_SLICE_DeadTimeInit(slice, &deadtime_conf);
}
return pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
}
static int pwm_xmc4xxx_ccu8_set_cycles(const struct device *dev, uint32_t channel,
uint32_t period_cycles, uint32_t pulse_cycles,
pwm_flags_t flags)
{
const struct pwm_xmc4xxx_ccu8_config *config = dev->config;
XMC_CCU8_SLICE_t *slice;
uint32_t high_deadtime_value = 0, low_deadtime_value = 0;
uint64_t cycles;
int slice_idx = channel / 2;
if (channel >= NUM_CHANNELS) {
return -EINVAL;
}
if (period_cycles == 0 || period_cycles > UINT16_MAX + 1 || pulse_cycles > UINT16_MAX) {
return -EINVAL;
}
slice = (XMC_CCU8_SLICE_t *)SLICE_ADDR_FROM_MODULE(config->ccu8, slice_idx);
slice->PRS = period_cycles - 1;
if (channel & 0x1) {
slice->CR2S = period_cycles - pulse_cycles;
} else {
slice->CR1S = period_cycles - pulse_cycles;
}
slice->PSL = flags & PWM_POLARITY_INVERTED;
/* set channel dead time */
cycles = XMC_SCU_CLOCK_GetCcuClockFrequency() >> config->slice_prescaler[slice_idx];
cycles >>= config->slice_deadtime_prescaler[slice_idx];
high_deadtime_value = config->deadtime_high_ns[channel] * cycles / NSEC_PER_SEC;
low_deadtime_value = config->deadtime_low_ns[channel] * cycles / NSEC_PER_SEC;
if (high_deadtime_value > MAX_DEAD_TIME_VALUE || low_deadtime_value > MAX_DEAD_TIME_VALUE) {
return -EINVAL;
}
XMC_CCU8_SLICE_SetDeadTimeValue(slice, channel & 0x1, high_deadtime_value,
low_deadtime_value);
XMC_CCU8_EnableShadowTransfer(config->ccu8, BIT(slice_idx * 4));
/* start if not already running */
XMC_CCU8_EnableClock(config->ccu8, slice_idx);
XMC_CCU8_SLICE_StartTimer(slice);
return 0;
}
static int pwm_xmc4xxx_ccu8_get_cycles_per_sec(const struct device *dev, uint32_t channel,
uint64_t *cycles)
{
const struct pwm_xmc4xxx_ccu8_config *config = dev->config;
if (channel >= NUM_CHANNELS) {
return -EINVAL;
}
*cycles = XMC_SCU_CLOCK_GetCcuClockFrequency() >> config->slice_prescaler[channel / 2];
return 0;
}
static const struct pwm_driver_api pwm_xmc4xxx_ccu8_driver_api = {
.set_cycles = pwm_xmc4xxx_ccu8_set_cycles,
.get_cycles_per_sec = pwm_xmc4xxx_ccu8_get_cycles_per_sec,
};
#define PWM_XMC4XXX_CCU8_INIT(n) \
PINCTRL_DT_INST_DEFINE(n); \
\
static const struct pwm_xmc4xxx_ccu8_config config##n = { \
.ccu8 = (CCU8_GLOBAL_TypeDef *)DT_INST_REG_ADDR(n), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.slice_prescaler = DT_INST_PROP(n, slice_prescaler), \
.slice_deadtime_prescaler = DT_INST_PROP(n, slice_deadtime_prescaler), \
.deadtime_high_ns = DT_INST_PROP(n, channel_deadtime_high), \
.deadtime_low_ns = DT_INST_PROP(n, channel_deadtime_low), \
}; \
\
DEVICE_DT_INST_DEFINE(n, pwm_xmc4xxx_ccu8_init, NULL, NULL, &config##n, POST_KERNEL, \
CONFIG_PWM_INIT_PRIORITY, &pwm_xmc4xxx_ccu8_driver_api);
DT_INST_FOREACH_STATUS_OKAY(PWM_XMC4XXX_CCU8_INIT)

View File

@ -242,4 +242,104 @@
/omit-if-no-ref/ pwm_out_p3_6_ccu42_ch0: pwm_out_p3_6_ccu42_ch0 {
pinmux = <XMC4XXX_PINMUX_SET(3, 6, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_0_ccu80_ch2_low: pwm_out_p0_0_ccu80_ch2_low {
pinmux = <XMC4XXX_PINMUX_SET(0, 0, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_1_ccu80_ch1_low: pwm_out_p0_1_ccu80_ch1_low {
pinmux = <XMC4XXX_PINMUX_SET(0, 1, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_2_ccu80_ch0_low: pwm_out_p0_2_ccu80_ch0_low {
pinmux = <XMC4XXX_PINMUX_SET(0, 2, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_3_ccu80_ch2_high: pwm_out_p0_3_ccu80_ch2_high {
pinmux = <XMC4XXX_PINMUX_SET(0, 3, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_4_ccu80_ch1_high: pwm_out_p0_4_ccu80_ch1_high {
pinmux = <XMC4XXX_PINMUX_SET(0, 4, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_5_ccu80_ch0_high: pwm_out_p0_5_ccu80_ch0_high {
pinmux = <XMC4XXX_PINMUX_SET(0, 5, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_6_ccu80_ch3_high: pwm_out_p0_6_ccu80_ch3_high {
pinmux = <XMC4XXX_PINMUX_SET(0, 6, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_9_ccu80_ch1_high: pwm_out_p0_9_ccu80_ch1_high {
pinmux = <XMC4XXX_PINMUX_SET(0, 9, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_10_ccu80_ch0_high: pwm_out_p0_10_ccu80_ch0_high {
pinmux = <XMC4XXX_PINMUX_SET(0, 10, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_11_ccu80_ch3_low: pwm_out_p0_11_ccu80_ch3_low {
pinmux = <XMC4XXX_PINMUX_SET(0, 11, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_4_ccu80_ch3_low: pwm_out_p1_4_ccu80_ch3_low {
pinmux = <XMC4XXX_PINMUX_SET(1, 4, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_4_ccu81_ch2_high: pwm_out_p1_4_ccu81_ch2_high {
pinmux = <XMC4XXX_PINMUX_SET(1, 4, 4)>;
};
/omit-if-no-ref/ pwm_out_p1_5_ccu80_ch2_low: pwm_out_p1_5_ccu80_ch2_low {
pinmux = <XMC4XXX_PINMUX_SET(1, 5, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_5_ccu81_ch1_high: pwm_out_p1_5_ccu81_ch1_high {
pinmux = <XMC4XXX_PINMUX_SET(1, 5, 4)>;
};
/omit-if-no-ref/ pwm_out_p1_10_ccu81_ch2_low: pwm_out_p1_10_ccu81_ch2_low {
pinmux = <XMC4XXX_PINMUX_SET(1, 10, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_11_ccu81_ch1_low: pwm_out_p1_11_ccu81_ch1_low {
pinmux = <XMC4XXX_PINMUX_SET(1, 11, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_12_ccu81_ch0_low: pwm_out_p1_12_ccu81_ch0_low {
pinmux = <XMC4XXX_PINMUX_SET(1, 12, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_13_ccu81_ch2_high: pwm_out_p1_13_ccu81_ch2_high {
pinmux = <XMC4XXX_PINMUX_SET(1, 13, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_14_ccu81_ch1_high: pwm_out_p1_14_ccu81_ch1_high {
pinmux = <XMC4XXX_PINMUX_SET(1, 14, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_15_ccu81_ch0_high: pwm_out_p1_15_ccu81_ch0_high {
pinmux = <XMC4XXX_PINMUX_SET(1, 15, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_0_ccu81_ch2_low: pwm_out_p2_0_ccu81_ch2_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 0, 2)>;
};
/omit-if-no-ref/ pwm_out_p2_1_ccu81_ch1_low: pwm_out_p2_1_ccu81_ch1_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 1, 2)>;
};
/omit-if-no-ref/ pwm_out_p2_2_ccu81_ch0_low: pwm_out_p2_2_ccu81_ch0_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 2, 2)>;
};
/omit-if-no-ref/ pwm_out_p2_6_ccu80_ch1_low: pwm_out_p2_6_ccu80_ch1_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 6, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_7_ccu80_ch0_low: pwm_out_p2_7_ccu80_ch0_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 7, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_8_ccu80_ch3_high: pwm_out_p2_8_ccu80_ch3_high {
pinmux = <XMC4XXX_PINMUX_SET(2, 8, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_9_ccu80_ch2_high: pwm_out_p2_9_ccu80_ch2_high {
pinmux = <XMC4XXX_PINMUX_SET(2, 9, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_14_ccu80_ch2_low: pwm_out_p2_14_ccu80_ch2_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 14, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_15_ccu80_ch1_low: pwm_out_p2_15_ccu80_ch1_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 15, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_0_ccu81_ch3_low: pwm_out_p5_0_ccu81_ch3_low {
pinmux = <XMC4XXX_PINMUX_SET(5, 0, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_1_ccu81_ch3_high: pwm_out_p5_1_ccu81_ch3_high {
pinmux = <XMC4XXX_PINMUX_SET(5, 1, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_2_ccu81_ch2_low: pwm_out_p5_2_ccu81_ch2_low {
pinmux = <XMC4XXX_PINMUX_SET(5, 2, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_7_ccu81_ch0_high: pwm_out_p5_7_ccu81_ch0_high {
pinmux = <XMC4XXX_PINMUX_SET(5, 7, 3)>;
};
};

View File

@ -811,4 +811,140 @@
/omit-if-no-ref/ pwm_out_p6_5_ccu43_ch0: pwm_out_p6_5_ccu43_ch0 {
pinmux = <XMC4XXX_PINMUX_SET(6, 5, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_0_ccu80_ch4_low: pwm_out_p0_0_ccu80_ch4_low {
pinmux = <XMC4XXX_PINMUX_SET(0, 0, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_1_ccu80_ch2_low: pwm_out_p0_1_ccu80_ch2_low {
pinmux = <XMC4XXX_PINMUX_SET(0, 1, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_2_ccu80_ch0_low: pwm_out_p0_2_ccu80_ch0_low {
pinmux = <XMC4XXX_PINMUX_SET(0, 2, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_3_ccu80_ch4_high: pwm_out_p0_3_ccu80_ch4_high {
pinmux = <XMC4XXX_PINMUX_SET(0, 3, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_4_ccu80_ch2_high: pwm_out_p0_4_ccu80_ch2_high {
pinmux = <XMC4XXX_PINMUX_SET(0, 4, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_5_ccu80_ch0_high: pwm_out_p0_5_ccu80_ch0_high {
pinmux = <XMC4XXX_PINMUX_SET(0, 5, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_6_ccu80_ch6_high: pwm_out_p0_6_ccu80_ch6_high {
pinmux = <XMC4XXX_PINMUX_SET(0, 6, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_9_ccu80_ch3_high: pwm_out_p0_9_ccu80_ch3_high {
pinmux = <XMC4XXX_PINMUX_SET(0, 9, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_10_ccu80_ch1_high: pwm_out_p0_10_ccu80_ch1_high {
pinmux = <XMC4XXX_PINMUX_SET(0, 10, 3)>;
};
/omit-if-no-ref/ pwm_out_p0_11_ccu80_ch6_low: pwm_out_p0_11_ccu80_ch6_low {
pinmux = <XMC4XXX_PINMUX_SET(0, 11, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_4_ccu80_ch7_low: pwm_out_p1_4_ccu80_ch7_low {
pinmux = <XMC4XXX_PINMUX_SET(1, 4, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_4_ccu81_ch4_high: pwm_out_p1_4_ccu81_ch4_high {
pinmux = <XMC4XXX_PINMUX_SET(1, 4, 4)>;
};
/omit-if-no-ref/ pwm_out_p1_5_ccu80_ch5_low: pwm_out_p1_5_ccu80_ch5_low {
pinmux = <XMC4XXX_PINMUX_SET(1, 5, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_5_ccu81_ch2_high: pwm_out_p1_5_ccu81_ch2_high {
pinmux = <XMC4XXX_PINMUX_SET(1, 5, 4)>;
};
/omit-if-no-ref/ pwm_out_p1_10_ccu81_ch4_low: pwm_out_p1_10_ccu81_ch4_low {
pinmux = <XMC4XXX_PINMUX_SET(1, 10, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_11_ccu81_ch2_low: pwm_out_p1_11_ccu81_ch2_low {
pinmux = <XMC4XXX_PINMUX_SET(1, 11, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_12_ccu81_ch0_low: pwm_out_p1_12_ccu81_ch0_low {
pinmux = <XMC4XXX_PINMUX_SET(1, 12, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_13_ccu81_ch4_high: pwm_out_p1_13_ccu81_ch4_high {
pinmux = <XMC4XXX_PINMUX_SET(1, 13, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_14_ccu81_ch2_high: pwm_out_p1_14_ccu81_ch2_high {
pinmux = <XMC4XXX_PINMUX_SET(1, 14, 3)>;
};
/omit-if-no-ref/ pwm_out_p1_15_ccu81_ch0_high: pwm_out_p1_15_ccu81_ch0_high {
pinmux = <XMC4XXX_PINMUX_SET(1, 15, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_0_ccu81_ch4_low: pwm_out_p2_0_ccu81_ch4_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 0, 2)>;
};
/omit-if-no-ref/ pwm_out_p2_1_ccu81_ch2_low: pwm_out_p2_1_ccu81_ch2_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 1, 2)>;
};
/omit-if-no-ref/ pwm_out_p2_2_ccu81_ch0_low: pwm_out_p2_2_ccu81_ch0_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 2, 2)>;
};
/omit-if-no-ref/ pwm_out_p2_6_ccu80_ch3_low: pwm_out_p2_6_ccu80_ch3_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 6, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_7_ccu80_ch1_low: pwm_out_p2_7_ccu80_ch1_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 7, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_8_ccu80_ch7_high: pwm_out_p2_8_ccu80_ch7_high {
pinmux = <XMC4XXX_PINMUX_SET(2, 8, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_9_ccu80_ch5_high: pwm_out_p2_9_ccu80_ch5_high {
pinmux = <XMC4XXX_PINMUX_SET(2, 9, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_11_ccu80_ch5_high: pwm_out_p2_11_ccu80_ch5_high {
pinmux = <XMC4XXX_PINMUX_SET(2, 11, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_12_ccu81_ch7_low: pwm_out_p2_12_ccu81_ch7_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 12, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_14_ccu80_ch4_low: pwm_out_p2_14_ccu80_ch4_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 14, 3)>;
};
/omit-if-no-ref/ pwm_out_p2_15_ccu80_ch2_low: pwm_out_p2_15_ccu80_ch2_low {
pinmux = <XMC4XXX_PINMUX_SET(2, 15, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_0_ccu81_ch7_low: pwm_out_p5_0_ccu81_ch7_low {
pinmux = <XMC4XXX_PINMUX_SET(5, 0, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_1_ccu81_ch7_high: pwm_out_p5_1_ccu81_ch7_high {
pinmux = <XMC4XXX_PINMUX_SET(5, 1, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_2_ccu81_ch5_low: pwm_out_p5_2_ccu81_ch5_low {
pinmux = <XMC4XXX_PINMUX_SET(5, 2, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_3_ccu81_ch5_high: pwm_out_p5_3_ccu81_ch5_high {
pinmux = <XMC4XXX_PINMUX_SET(5, 3, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_4_ccu81_ch3_low: pwm_out_p5_4_ccu81_ch3_low {
pinmux = <XMC4XXX_PINMUX_SET(5, 4, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_5_ccu81_ch3_high: pwm_out_p5_5_ccu81_ch3_high {
pinmux = <XMC4XXX_PINMUX_SET(5, 5, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_6_ccu81_ch1_low: pwm_out_p5_6_ccu81_ch1_low {
pinmux = <XMC4XXX_PINMUX_SET(5, 6, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_7_ccu81_ch1_high: pwm_out_p5_7_ccu81_ch1_high {
pinmux = <XMC4XXX_PINMUX_SET(5, 7, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_8_ccu80_ch0_low: pwm_out_p5_8_ccu80_ch0_low {
pinmux = <XMC4XXX_PINMUX_SET(5, 8, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_9_ccu80_ch4_high: pwm_out_p5_9_ccu80_ch4_high {
pinmux = <XMC4XXX_PINMUX_SET(5, 9, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_10_ccu80_ch2_high: pwm_out_p5_10_ccu80_ch2_high {
pinmux = <XMC4XXX_PINMUX_SET(5, 10, 3)>;
};
/omit-if-no-ref/ pwm_out_p5_11_ccu80_ch0_high: pwm_out_p5_11_ccu80_ch0_high {
pinmux = <XMC4XXX_PINMUX_SET(5, 11, 3)>;
};
/omit-if-no-ref/ pwm_out_p6_0_ccu81_ch6_low: pwm_out_p6_0_ccu81_ch6_low {
pinmux = <XMC4XXX_PINMUX_SET(6, 0, 3)>;
};
/omit-if-no-ref/ pwm_out_p6_1_ccu81_ch6_high: pwm_out_p6_1_ccu81_ch6_high {
pinmux = <XMC4XXX_PINMUX_SET(6, 1, 3)>;
};
};

View File

@ -215,6 +215,20 @@
#pwm-cells = <3>;
status = "disabled";
};
pwm_ccu80: ccu80@40020000 {
compatible = "infineon,xmc4xxx-ccu8-pwm";
reg = <0x40020000 0x4000>;
#pwm-cells = <3>;
status = "disabled";
};
pwm_ccu81: ccu81@40024000 {
compatible = "infineon,xmc4xxx-ccu8-pwm";
reg = <0x40024000 0x4000>;
#pwm-cells = <3>;
status = "disabled";
};
};
};

View File

@ -0,0 +1,126 @@
# Copyright (c) 2023 SLB
# SPDX-License-Identifier: Apache-2.0
description: |
Infineon XMC4XXX PWM Capture Compare Unit 8 (CCU8) module
The PWM CCU8 module can automatically generate a high-side
and a low-side PWM signal, where the two signals are complementary
to each other.
The module supports adding a dead time between the high-side and
low-side PWM signals.
The dead time ensures that there is a delay before the PWM state
transitions from 0 to 1, preventing the high-side and low-side
switches from being on simultaneously.
There are two CCU8 modules with DTS node labels: pwm_ccu80 and
pwm_ccu81. Each module has four slices, and each slice has
two channels. A channel consists of a corresponding high-side
and low-side PWM signal.
The CCU8 modules use the CCU clock source. Each slice applies
a separate prescaler to divide the clock. The clock divider is
defined by the 'slice-prescaler' property. Additionally, each
slice has a dead time prescaler, which divides the slice clock
for the dead time counter.
Device tree example:
A node can define a 'pwm' field, usually referenced in a 'pwms'
property, where the entries include the PWM module phandle,
channel number, pulse period (in nanoseconds or set using
PWM_XX() macros), and a channel
flag (PWM_POLARITY_NORMAL/PWM_POLARITY_INVERTED).
The 'pwm_ccu8' node must define the following fields:
&pwm_ccu80 {
slice-prescaler = <15 15 15 15>;
slice-deadtime-prescaler = <3 3 3 3>;
channel-deadtime-high = <0 0 0 0 PWM_MSEC(100) 0 0 0>;
channel-deadtime-low = <0 0 0 0 PWM_MSEC(100) 0 0 0>;
pinctrl-0 = <&pwm_out_p5_9_ccu80_ch4_high &pwm_out_p0_0_ccu80_ch4_low>;
pinctrl-names = "default";
};
This will configure channel 4 with a 100msec deadtime on the high
and low side PWM signals.
Another node can reference the PWM as follows:
&test_node {
...
pwms = <&pwm_ccu80 0 PWM_SEC(1) PWM_POLARITY_NORMAL>;
...
};
The 'pwm_out_p{PORT}_{PIN}_ccu8{MODULE_IDX}_ch{CHANNEL_IDX}_{HIGH_LOW}'
format is used for CCU8 pinctrl nodes. 'MODULE_IDX' and 'CHANNEL_IDX'
refer to a specific 'pwm_ccu8x' module and channel, respectively.
'PORT/PIN' defines the GPIO that the channel connects to.
'HIGH_LOW' indicates whether the pin is for the high or low-side signal.
It's not necessary to specify both the high and low pinctrls. Only the low-side
signal can, for example, be used as PWM, but note that the duty cycle of the
low signal will be (1 - duty) as set via the API.
Note that a slice has two channels. Channels 0/1 are in slice 0,
channels 2/3 are in slice 1, and so on. Each channel can have its own
duty cycle and high/low dead times. But the pulse duration applies to
both channels. Thus, when using the PWM control api to modify the pulse width
on a channel 0, it will also be updated for channel 1 since they are
in the same slice.
compatible: "infineon,xmc4xxx-ccu8-pwm"
include:
- name: base.yaml
- name: pwm-controller.yaml
- name: pinctrl-device.yaml
properties:
reg:
required: true
pinctrl-0:
required: true
pinctrl-names:
required: true
slice-prescaler:
type: array
required: true
description: |
Defines the clock divider for each slice.
The entry in the array will divide CCU clock by (2 << value).
The range for the prescaler values is [0, 15].
Reducing prescaler value will improve resolution but decrease the maximum period.
slice-deadtime-prescaler:
type: array
required: true
description: |
Defines the clock divider for dead time counter for each slice.
The range for the values is [0, 3].
Reducing prescaler value will improve dead time resolution but decrease the
maximum dead time.
channel-deadtime-high:
type: array
required: true
description: |
Defines the dead time in nanoseconds for the high-side PWM signal for each channel.
channel-deadtime-low:
type: array
required: true
description: |
Defines the dead time in nanoseconds for the low-side PWM signal for each channel.
"#pwm-cells":
const: 3
pwm-cells:
- channel
- period
- flags

View File

@ -0,0 +1,13 @@
/*
* Copyright (c) 2023 SLB
*
* SPDX-License-Identifier: Apache-2.0
*/
&pwm_led1 {
status = "okay";
};
&pwm_ccu80 {
status = "okay";
};

View File

@ -34,6 +34,9 @@ void z_arm_platform_init(void)
XMC_SCU_CLOCK_SetSleepConfig(XMC_SCU_CLOCK_SLEEP_MODE_CONFIG_SYSCLK_FPLL
#ifdef CONFIG_PWM_XMC4XXX_CCU4
| XMC_SCU_CLOCK_SLEEP_MODE_CONFIG_ENABLE_CCU
#endif
#ifdef CONFIG_PWM_XMC4XXX_CCU8
| XMC_SCU_CLOCK_SLEEP_MODE_CONFIG_ENABLE_CCU
#endif
);