1 /*
2 * Copyright (c) 2023 SLB
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT infineon_xmc4xxx_ccu8_pwm
8
9 #include <soc.h>
10 #include <zephyr/dt-bindings/pwm/pwm.h>
11 #include <zephyr/drivers/pwm.h>
12 #include <zephyr/drivers/pinctrl.h>
13
14 #include <xmc_ccu8.h>
15 #include <xmc_scu.h>
16
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(pwm_xmc4xxx_ccu8, CONFIG_PWM_LOG_LEVEL);
19
20 #define NUM_SLICES 4
21 #define NUM_CHANNELS (NUM_SLICES * 2)
22 #define MAX_DEAD_TIME_VALUE 255
23 #define MAX_SLICE_PRESCALER 15
24 #define MAX_DEADTIME_PRESCALER 3
25 #define SLICE_ADDR_FROM_MODULE(module_ptr, idx) ((uint32_t)(module_ptr) + ((idx) + 1) * 0x100)
26
27 struct pwm_xmc4xxx_ccu8_config {
28 XMC_CCU8_MODULE_t *ccu8;
29 const struct pinctrl_dev_config *pcfg;
30 const uint8_t slice_prescaler[NUM_SLICES];
31 const uint8_t slice_deadtime_prescaler[NUM_SLICES];
32 const uint32_t deadtime_high_ns[NUM_CHANNELS];
33 const uint32_t deadtime_low_ns[NUM_CHANNELS];
34 };
35
pwm_xmc4xxx_ccu8_init(const struct device * dev)36 static int pwm_xmc4xxx_ccu8_init(const struct device *dev)
37 {
38 const struct pwm_xmc4xxx_ccu8_config *config = dev->config;
39
40 /* enables the CCU8 clock and ungates clock to CCU8x */
41 XMC_CCU8_EnableModule(config->ccu8);
42 XMC_CCU8_StartPrescaler(config->ccu8);
43
44 for (int i = 0; i < NUM_SLICES; i++) {
45 XMC_CCU8_SLICE_t *slice;
46 XMC_CCU8_SLICE_DEAD_TIME_CONFIG_t deadtime_conf = {0};
47 XMC_CCU8_SLICE_COMPARE_CONFIG_t slice_conf = {
48 .prescaler_initval = config->slice_prescaler[i],
49 .invert_out1 = 1,
50 .invert_out3 = 1,
51 };
52
53 if (config->slice_prescaler[i] > MAX_SLICE_PRESCALER) {
54 LOG_ERR("Invalid slice_prescaler value %d. Range [0, 15]",
55 config->slice_prescaler[i]);
56 return -EINVAL;
57 }
58
59 if (config->slice_deadtime_prescaler[i] > MAX_DEADTIME_PRESCALER) {
60 LOG_ERR("Invalid dead time prescaler value %d. Range [0, 3]",
61 config->slice_deadtime_prescaler[i]);
62 return -EINVAL;
63 }
64
65 slice = (XMC_CCU8_SLICE_t *)SLICE_ADDR_FROM_MODULE(config->ccu8, i);
66 XMC_CCU8_SLICE_CompareInit(slice, &slice_conf);
67
68 deadtime_conf.div = config->slice_deadtime_prescaler[i];
69 if (config->deadtime_high_ns[2*i] > 0 || config->deadtime_low_ns[2*i] > 0) {
70 deadtime_conf.enable_dead_time_channel1 = 1;
71 }
72 deadtime_conf.channel1_st_path = config->deadtime_high_ns[2*i] > 0;
73 deadtime_conf.channel1_inv_st_path = config->deadtime_low_ns[2*i] > 0;
74
75 if (config->deadtime_high_ns[2*i + 1] > 0 || config->deadtime_low_ns[2*i + 1] > 0) {
76 deadtime_conf.enable_dead_time_channel2 = 1;
77 }
78 deadtime_conf.channel2_st_path = config->deadtime_high_ns[2*i + 1] > 0;
79 deadtime_conf.channel2_inv_st_path = config->deadtime_low_ns[2*i + 1] > 0;
80
81 XMC_CCU8_SLICE_DeadTimeInit(slice, &deadtime_conf);
82 }
83
84 return pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
85 }
86
pwm_xmc4xxx_ccu8_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)87 static int pwm_xmc4xxx_ccu8_set_cycles(const struct device *dev, uint32_t channel,
88 uint32_t period_cycles, uint32_t pulse_cycles,
89 pwm_flags_t flags)
90 {
91 const struct pwm_xmc4xxx_ccu8_config *config = dev->config;
92 XMC_CCU8_SLICE_t *slice;
93 uint32_t high_deadtime_value = 0, low_deadtime_value = 0;
94 uint64_t cycles;
95 int slice_idx = channel / 2;
96
97 if (channel >= NUM_CHANNELS) {
98 return -EINVAL;
99 }
100
101 if (period_cycles == 0 || period_cycles > UINT16_MAX + 1 || pulse_cycles > UINT16_MAX) {
102 return -EINVAL;
103 }
104
105 slice = (XMC_CCU8_SLICE_t *)SLICE_ADDR_FROM_MODULE(config->ccu8, slice_idx);
106 slice->PRS = period_cycles - 1;
107
108 if (channel & 0x1) {
109 slice->CR2S = period_cycles - pulse_cycles;
110 } else {
111 slice->CR1S = period_cycles - pulse_cycles;
112 }
113 slice->PSL = flags & PWM_POLARITY_INVERTED;
114
115 /* set channel dead time */
116 cycles = XMC_SCU_CLOCK_GetCcuClockFrequency() >> config->slice_prescaler[slice_idx];
117 cycles >>= config->slice_deadtime_prescaler[slice_idx];
118 high_deadtime_value = config->deadtime_high_ns[channel] * cycles / NSEC_PER_SEC;
119 low_deadtime_value = config->deadtime_low_ns[channel] * cycles / NSEC_PER_SEC;
120
121 if (high_deadtime_value > MAX_DEAD_TIME_VALUE || low_deadtime_value > MAX_DEAD_TIME_VALUE) {
122 return -EINVAL;
123 }
124
125 XMC_CCU8_SLICE_SetDeadTimeValue(slice, channel & 0x1, high_deadtime_value,
126 low_deadtime_value);
127
128 XMC_CCU8_EnableShadowTransfer(config->ccu8, BIT(slice_idx * 4));
129
130 /* start if not already running */
131 XMC_CCU8_EnableClock(config->ccu8, slice_idx);
132 XMC_CCU8_SLICE_StartTimer(slice);
133
134 return 0;
135 }
136
pwm_xmc4xxx_ccu8_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)137 static int pwm_xmc4xxx_ccu8_get_cycles_per_sec(const struct device *dev, uint32_t channel,
138 uint64_t *cycles)
139 {
140 const struct pwm_xmc4xxx_ccu8_config *config = dev->config;
141
142 if (channel >= NUM_CHANNELS) {
143 return -EINVAL;
144 }
145
146 *cycles = XMC_SCU_CLOCK_GetCcuClockFrequency() >> config->slice_prescaler[channel / 2];
147
148 return 0;
149 }
150
151 static const struct pwm_driver_api pwm_xmc4xxx_ccu8_driver_api = {
152 .set_cycles = pwm_xmc4xxx_ccu8_set_cycles,
153 .get_cycles_per_sec = pwm_xmc4xxx_ccu8_get_cycles_per_sec,
154 };
155
156 #define PWM_XMC4XXX_CCU8_INIT(n) \
157 PINCTRL_DT_INST_DEFINE(n); \
158 \
159 static const struct pwm_xmc4xxx_ccu8_config config##n = { \
160 .ccu8 = (CCU8_GLOBAL_TypeDef *)DT_INST_REG_ADDR(n), \
161 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
162 .slice_prescaler = DT_INST_PROP(n, slice_prescaler), \
163 .slice_deadtime_prescaler = DT_INST_PROP(n, slice_deadtime_prescaler), \
164 .deadtime_high_ns = DT_INST_PROP(n, channel_deadtime_high), \
165 .deadtime_low_ns = DT_INST_PROP(n, channel_deadtime_low), \
166 }; \
167 \
168 DEVICE_DT_INST_DEFINE(n, pwm_xmc4xxx_ccu8_init, NULL, NULL, &config##n, POST_KERNEL, \
169 CONFIG_PWM_INIT_PRIORITY, &pwm_xmc4xxx_ccu8_driver_api);
170
171 DT_INST_FOREACH_STATUS_OKAY(PWM_XMC4XXX_CCU8_INIT)
172