1 /*
2 * Copyright (c) 2020 Henrik Brix Andersen <henrik@brixandersen.dk>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT xlnx_xps_timer_1_00_a_pwm
8
9 #include <zephyr/arch/cpu.h>
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/pwm.h>
12 #include <zephyr/sys/sys_io.h>
13 #include <zephyr/logging/log.h>
14
15 LOG_MODULE_REGISTER(xlnx_axi_timer_pwm, CONFIG_PWM_LOG_LEVEL);
16
17 /* AXI Timer v2.0 registers offsets (See Xilinx PG079 for details) */
18 #define TCSR0_OFFSET 0x00
19 #define TLR0_OFFSET 0x04
20 #define TCR0_OFFSET 0x08
21 #define TCSR1_OFFSET 0x10
22 #define TLR1_OFFSET 0x14
23 #define TCR1_OFFSET 0x18
24
25 /* TCSRx bit definitions */
26 #define TCSR_MDT BIT(0)
27 #define TCSR_UDT BIT(1)
28 #define TCSR_GENT BIT(2)
29 #define TCSR_CAPT BIT(3)
30 #define TCSR_ARHT BIT(4)
31 #define TCSR_LOAD BIT(5)
32 #define TCSR_ENIT BIT(6)
33 #define TCSR_ENT BIT(7)
34 #define TCSR_TINT BIT(8)
35 #define TCSR_PWMA BIT(9)
36 #define TCSR_ENALL BIT(10)
37 #define TCSR_CASC BIT(11)
38
39 /* Generate PWM mode, count-down, auto-reload */
40 #define TCSR_PWM (TCSR_UDT | TCSR_GENT | TCSR_ARHT | TCSR_PWMA)
41
42 struct xlnx_axi_timer_config {
43 mm_reg_t base;
44 uint32_t cycles_max;
45 uint32_t freq;
46 };
47
xlnx_axi_timer_read32(const struct device * dev,mm_reg_t offset)48 static inline uint32_t xlnx_axi_timer_read32(const struct device *dev,
49 mm_reg_t offset)
50 {
51 const struct xlnx_axi_timer_config *config = dev->config;
52
53 return sys_read32(config->base + offset);
54 }
55
xlnx_axi_timer_write32(const struct device * dev,uint32_t value,mm_reg_t offset)56 static inline void xlnx_axi_timer_write32(const struct device *dev,
57 uint32_t value,
58 mm_reg_t offset)
59 {
60 const struct xlnx_axi_timer_config *config = dev->config;
61
62 sys_write32(value, config->base + offset);
63 }
64
xlnx_axi_timer_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)65 static int xlnx_axi_timer_set_cycles(const struct device *dev, uint32_t channel,
66 uint32_t period_cycles,
67 uint32_t pulse_cycles, pwm_flags_t flags)
68 {
69 const struct xlnx_axi_timer_config *config = dev->config;
70 uint32_t tcsr0 = TCSR_PWM;
71 uint32_t tcsr1 = TCSR_PWM;
72 uint32_t tlr0;
73 uint32_t tlr1;
74
75 if (channel != 0) {
76 return -ENOTSUP;
77 }
78
79 LOG_DBG("period = 0x%08x, pulse = 0x%08x", period_cycles, pulse_cycles);
80
81 if (pulse_cycles == 0) {
82 LOG_DBG("setting constant inactive level");
83
84 if (flags & PWM_POLARITY_INVERTED) {
85 tcsr0 |= TCSR_ENT;
86 } else {
87 tcsr1 |= TCSR_ENT;
88 }
89 } else if (pulse_cycles == period_cycles) {
90 LOG_DBG("setting constant active level");
91
92 if (flags & PWM_POLARITY_INVERTED) {
93 tcsr1 |= TCSR_ENT;
94 } else {
95 tcsr0 |= TCSR_ENT;
96 }
97 } else {
98 LOG_DBG("setting normal pwm");
99
100 if (period_cycles < 2) {
101 LOG_ERR("period cycles too narrow");
102 return -ENOTSUP;
103 }
104
105 /* PWM_PERIOD = (TLR0 + 2) * AXI_CLOCK_PERIOD */
106 tlr0 = period_cycles - 2;
107
108 if (tlr0 > config->cycles_max) {
109 LOG_ERR("tlr0 out of range (0x%08x > 0x%08x)", tlr0,
110 config->cycles_max);
111 return -ENOTSUP;
112 }
113
114 if (flags & PWM_POLARITY_INVERTED) {
115 /*
116 * Since this is a single-channel PWM controller (with
117 * no other channels to phase align with) inverse
118 * polarity can be achieved simply by inverting the
119 * pulse.
120 */
121
122 if ((period_cycles - pulse_cycles) < 2) {
123 LOG_ERR("pulse cycles too narrow");
124 return -ENOTSUP;
125 }
126
127 /* PWM_HIGH_TIME = (TLR1 + 2) * AXI_CLOCK_PERIOD */
128 tlr1 = period_cycles - pulse_cycles - 2;
129 } else {
130 if (pulse_cycles < 2) {
131 LOG_ERR("pulse cycles too narrow");
132 return -ENOTSUP;
133 }
134
135 /* PWM_HIGH_TIME = (TLR1 + 2) * AXI_CLOCK_PERIOD */
136 tlr1 = pulse_cycles - 2;
137 }
138
139 LOG_DBG("tlr0 = 0x%08x, tlr1 = 0x%08x", tlr0, tlr1);
140
141 /* Stop both timers */
142 xlnx_axi_timer_write32(dev, TCSR_PWM, TCSR0_OFFSET);
143 xlnx_axi_timer_write32(dev, TCSR_PWM, TCSR1_OFFSET);
144
145 /* Load period cycles */
146 xlnx_axi_timer_write32(dev, tlr0, TLR0_OFFSET);
147 xlnx_axi_timer_write32(dev, TCSR_PWM | TCSR_LOAD, TCSR0_OFFSET);
148
149 /* Load pulse cycles */
150 xlnx_axi_timer_write32(dev, tlr1, TLR1_OFFSET);
151 xlnx_axi_timer_write32(dev, TCSR_PWM | TCSR_LOAD, TCSR1_OFFSET);
152
153 /* Start both timers */
154 tcsr1 |= TCSR_ENALL;
155 }
156
157 xlnx_axi_timer_write32(dev, tcsr0, TCSR0_OFFSET);
158 xlnx_axi_timer_write32(dev, tcsr1, TCSR1_OFFSET);
159
160 return 0;
161 }
162
xlnx_axi_timer_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)163 static int xlnx_axi_timer_get_cycles_per_sec(const struct device *dev,
164 uint32_t channel, uint64_t *cycles)
165 {
166 const struct xlnx_axi_timer_config *config = dev->config;
167
168 ARG_UNUSED(channel);
169
170 *cycles = config->freq;
171
172 return 0;
173 }
174
175 static const struct pwm_driver_api xlnx_axi_timer_driver_api = {
176 .set_cycles = xlnx_axi_timer_set_cycles,
177 .get_cycles_per_sec = xlnx_axi_timer_get_cycles_per_sec,
178 };
179
180 #define XLNX_AXI_TIMER_ASSERT_PROP_VAL(n, prop, val, str) \
181 BUILD_ASSERT(DT_INST_PROP(n, prop) == val, str)
182
183 #define XLNX_AXI_TIMER_INIT(n) \
184 XLNX_AXI_TIMER_ASSERT_PROP_VAL(n, xlnx_gen0_assert, 1, \
185 "xlnx,gen0-assert must be 1 for pwm"); \
186 XLNX_AXI_TIMER_ASSERT_PROP_VAL(n, xlnx_gen1_assert, 1, \
187 "xlnx,gen1-assert must be 1 for pwm"); \
188 XLNX_AXI_TIMER_ASSERT_PROP_VAL(n, xlnx_one_timer_only, 0, \
189 "xlnx,one-timer-only must be 0 for pwm"); \
190 \
191 static struct xlnx_axi_timer_config xlnx_axi_timer_config_##n = { \
192 .base = DT_INST_REG_ADDR(n), \
193 .freq = DT_INST_PROP(n, clock_frequency), \
194 .cycles_max = \
195 GENMASK(DT_INST_PROP(n, xlnx_count_width) - 1, 0), \
196 }; \
197 \
198 DEVICE_DT_INST_DEFINE(n, NULL, NULL, NULL, \
199 &xlnx_axi_timer_config_##n, \
200 POST_KERNEL, \
201 CONFIG_PWM_INIT_PRIORITY, \
202 &xlnx_axi_timer_driver_api)
203
204 DT_INST_FOREACH_STATUS_OKAY(XLNX_AXI_TIMER_INIT);
205