]>
Commit | Line | Data |
---|---|---|
98e5d7a2 VG |
1 | /* |
2 | * QEMU model of the Xilinx ZynqMP CAN controller. | |
3 | * This implementation is based on the following datasheet: | |
4 | * https://www.xilinx.com/support/documentation/user_guides/ug1085-zynq-ultrascale-trm.pdf | |
5 | * | |
6 | * Copyright (c) 2020 Xilinx Inc. | |
7 | * | |
8 | * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com> | |
9 | * | |
10 | * Based on QEMU CAN Device emulation implemented by Jin Yang, Deniz Eren and | |
11 | * Pavel Pisa | |
12 | * | |
13 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
14 | * of this software and associated documentation files (the "Software"), to deal | |
15 | * in the Software without restriction, including without limitation the rights | |
16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
17 | * copies of the Software, and to permit persons to whom the Software is | |
18 | * furnished to do so, subject to the following conditions: | |
19 | * | |
20 | * The above copyright notice and this permission notice shall be included in | |
21 | * all copies or substantial portions of the Software. | |
22 | * | |
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
26 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
29 | * THE SOFTWARE. | |
30 | */ | |
31 | ||
32 | #include "qemu/osdep.h" | |
33 | #include "hw/sysbus.h" | |
34 | #include "hw/register.h" | |
35 | #include "hw/irq.h" | |
36 | #include "qapi/error.h" | |
37 | #include "qemu/bitops.h" | |
38 | #include "qemu/log.h" | |
39 | #include "qemu/cutils.h" | |
98e5d7a2 VG |
40 | #include "migration/vmstate.h" |
41 | #include "hw/qdev-properties.h" | |
42 | #include "net/can_emu.h" | |
43 | #include "net/can_host.h" | |
44 | #include "qemu/event_notifier.h" | |
45 | #include "qom/object_interfaces.h" | |
46 | #include "hw/net/xlnx-zynqmp-can.h" | |
47 | #include "trace.h" | |
48 | ||
49 | #ifndef XLNX_ZYNQMP_CAN_ERR_DEBUG | |
50 | #define XLNX_ZYNQMP_CAN_ERR_DEBUG 0 | |
51 | #endif | |
52 | ||
53 | #define MAX_DLC 8 | |
54 | #undef ERROR | |
55 | ||
56 | REG32(SOFTWARE_RESET_REGISTER, 0x0) | |
57 | FIELD(SOFTWARE_RESET_REGISTER, CEN, 1, 1) | |
58 | FIELD(SOFTWARE_RESET_REGISTER, SRST, 0, 1) | |
59 | REG32(MODE_SELECT_REGISTER, 0x4) | |
60 | FIELD(MODE_SELECT_REGISTER, SNOOP, 2, 1) | |
61 | FIELD(MODE_SELECT_REGISTER, LBACK, 1, 1) | |
62 | FIELD(MODE_SELECT_REGISTER, SLEEP, 0, 1) | |
63 | REG32(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, 0x8) | |
64 | FIELD(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, BRP, 0, 8) | |
65 | REG32(ARBITRATION_PHASE_BIT_TIMING_REGISTER, 0xc) | |
66 | FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, SJW, 7, 2) | |
67 | FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS2, 4, 3) | |
68 | FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS1, 0, 4) | |
69 | REG32(ERROR_COUNTER_REGISTER, 0x10) | |
70 | FIELD(ERROR_COUNTER_REGISTER, REC, 8, 8) | |
71 | FIELD(ERROR_COUNTER_REGISTER, TEC, 0, 8) | |
72 | REG32(ERROR_STATUS_REGISTER, 0x14) | |
73 | FIELD(ERROR_STATUS_REGISTER, ACKER, 4, 1) | |
74 | FIELD(ERROR_STATUS_REGISTER, BERR, 3, 1) | |
75 | FIELD(ERROR_STATUS_REGISTER, STER, 2, 1) | |
76 | FIELD(ERROR_STATUS_REGISTER, FMER, 1, 1) | |
77 | FIELD(ERROR_STATUS_REGISTER, CRCER, 0, 1) | |
78 | REG32(STATUS_REGISTER, 0x18) | |
79 | FIELD(STATUS_REGISTER, SNOOP, 12, 1) | |
80 | FIELD(STATUS_REGISTER, ACFBSY, 11, 1) | |
81 | FIELD(STATUS_REGISTER, TXFLL, 10, 1) | |
82 | FIELD(STATUS_REGISTER, TXBFLL, 9, 1) | |
83 | FIELD(STATUS_REGISTER, ESTAT, 7, 2) | |
84 | FIELD(STATUS_REGISTER, ERRWRN, 6, 1) | |
85 | FIELD(STATUS_REGISTER, BBSY, 5, 1) | |
86 | FIELD(STATUS_REGISTER, BIDLE, 4, 1) | |
87 | FIELD(STATUS_REGISTER, NORMAL, 3, 1) | |
88 | FIELD(STATUS_REGISTER, SLEEP, 2, 1) | |
89 | FIELD(STATUS_REGISTER, LBACK, 1, 1) | |
90 | FIELD(STATUS_REGISTER, CONFIG, 0, 1) | |
91 | REG32(INTERRUPT_STATUS_REGISTER, 0x1c) | |
92 | FIELD(INTERRUPT_STATUS_REGISTER, TXFEMP, 14, 1) | |
93 | FIELD(INTERRUPT_STATUS_REGISTER, TXFWMEMP, 13, 1) | |
94 | FIELD(INTERRUPT_STATUS_REGISTER, RXFWMFLL, 12, 1) | |
95 | FIELD(INTERRUPT_STATUS_REGISTER, WKUP, 11, 1) | |
96 | FIELD(INTERRUPT_STATUS_REGISTER, SLP, 10, 1) | |
97 | FIELD(INTERRUPT_STATUS_REGISTER, BSOFF, 9, 1) | |
98 | FIELD(INTERRUPT_STATUS_REGISTER, ERROR, 8, 1) | |
99 | FIELD(INTERRUPT_STATUS_REGISTER, RXNEMP, 7, 1) | |
100 | FIELD(INTERRUPT_STATUS_REGISTER, RXOFLW, 6, 1) | |
101 | FIELD(INTERRUPT_STATUS_REGISTER, RXUFLW, 5, 1) | |
102 | FIELD(INTERRUPT_STATUS_REGISTER, RXOK, 4, 1) | |
103 | FIELD(INTERRUPT_STATUS_REGISTER, TXBFLL, 3, 1) | |
104 | FIELD(INTERRUPT_STATUS_REGISTER, TXFLL, 2, 1) | |
105 | FIELD(INTERRUPT_STATUS_REGISTER, TXOK, 1, 1) | |
106 | FIELD(INTERRUPT_STATUS_REGISTER, ARBLST, 0, 1) | |
107 | REG32(INTERRUPT_ENABLE_REGISTER, 0x20) | |
108 | FIELD(INTERRUPT_ENABLE_REGISTER, ETXFEMP, 14, 1) | |
109 | FIELD(INTERRUPT_ENABLE_REGISTER, ETXFWMEMP, 13, 1) | |
110 | FIELD(INTERRUPT_ENABLE_REGISTER, ERXFWMFLL, 12, 1) | |
111 | FIELD(INTERRUPT_ENABLE_REGISTER, EWKUP, 11, 1) | |
112 | FIELD(INTERRUPT_ENABLE_REGISTER, ESLP, 10, 1) | |
113 | FIELD(INTERRUPT_ENABLE_REGISTER, EBSOFF, 9, 1) | |
114 | FIELD(INTERRUPT_ENABLE_REGISTER, EERROR, 8, 1) | |
115 | FIELD(INTERRUPT_ENABLE_REGISTER, ERXNEMP, 7, 1) | |
116 | FIELD(INTERRUPT_ENABLE_REGISTER, ERXOFLW, 6, 1) | |
117 | FIELD(INTERRUPT_ENABLE_REGISTER, ERXUFLW, 5, 1) | |
118 | FIELD(INTERRUPT_ENABLE_REGISTER, ERXOK, 4, 1) | |
119 | FIELD(INTERRUPT_ENABLE_REGISTER, ETXBFLL, 3, 1) | |
120 | FIELD(INTERRUPT_ENABLE_REGISTER, ETXFLL, 2, 1) | |
121 | FIELD(INTERRUPT_ENABLE_REGISTER, ETXOK, 1, 1) | |
122 | FIELD(INTERRUPT_ENABLE_REGISTER, EARBLST, 0, 1) | |
123 | REG32(INTERRUPT_CLEAR_REGISTER, 0x24) | |
124 | FIELD(INTERRUPT_CLEAR_REGISTER, CTXFEMP, 14, 1) | |
125 | FIELD(INTERRUPT_CLEAR_REGISTER, CTXFWMEMP, 13, 1) | |
126 | FIELD(INTERRUPT_CLEAR_REGISTER, CRXFWMFLL, 12, 1) | |
127 | FIELD(INTERRUPT_CLEAR_REGISTER, CWKUP, 11, 1) | |
128 | FIELD(INTERRUPT_CLEAR_REGISTER, CSLP, 10, 1) | |
129 | FIELD(INTERRUPT_CLEAR_REGISTER, CBSOFF, 9, 1) | |
130 | FIELD(INTERRUPT_CLEAR_REGISTER, CERROR, 8, 1) | |
131 | FIELD(INTERRUPT_CLEAR_REGISTER, CRXNEMP, 7, 1) | |
132 | FIELD(INTERRUPT_CLEAR_REGISTER, CRXOFLW, 6, 1) | |
133 | FIELD(INTERRUPT_CLEAR_REGISTER, CRXUFLW, 5, 1) | |
134 | FIELD(INTERRUPT_CLEAR_REGISTER, CRXOK, 4, 1) | |
135 | FIELD(INTERRUPT_CLEAR_REGISTER, CTXBFLL, 3, 1) | |
136 | FIELD(INTERRUPT_CLEAR_REGISTER, CTXFLL, 2, 1) | |
137 | FIELD(INTERRUPT_CLEAR_REGISTER, CTXOK, 1, 1) | |
138 | FIELD(INTERRUPT_CLEAR_REGISTER, CARBLST, 0, 1) | |
139 | REG32(TIMESTAMP_REGISTER, 0x28) | |
140 | FIELD(TIMESTAMP_REGISTER, CTS, 0, 1) | |
141 | REG32(WIR, 0x2c) | |
142 | FIELD(WIR, EW, 8, 8) | |
143 | FIELD(WIR, FW, 0, 8) | |
144 | REG32(TXFIFO_ID, 0x30) | |
145 | FIELD(TXFIFO_ID, IDH, 21, 11) | |
146 | FIELD(TXFIFO_ID, SRRRTR, 20, 1) | |
147 | FIELD(TXFIFO_ID, IDE, 19, 1) | |
148 | FIELD(TXFIFO_ID, IDL, 1, 18) | |
149 | FIELD(TXFIFO_ID, RTR, 0, 1) | |
150 | REG32(TXFIFO_DLC, 0x34) | |
151 | FIELD(TXFIFO_DLC, DLC, 28, 4) | |
152 | REG32(TXFIFO_DATA1, 0x38) | |
153 | FIELD(TXFIFO_DATA1, DB0, 24, 8) | |
154 | FIELD(TXFIFO_DATA1, DB1, 16, 8) | |
155 | FIELD(TXFIFO_DATA1, DB2, 8, 8) | |
156 | FIELD(TXFIFO_DATA1, DB3, 0, 8) | |
157 | REG32(TXFIFO_DATA2, 0x3c) | |
158 | FIELD(TXFIFO_DATA2, DB4, 24, 8) | |
159 | FIELD(TXFIFO_DATA2, DB5, 16, 8) | |
160 | FIELD(TXFIFO_DATA2, DB6, 8, 8) | |
161 | FIELD(TXFIFO_DATA2, DB7, 0, 8) | |
162 | REG32(TXHPB_ID, 0x40) | |
163 | FIELD(TXHPB_ID, IDH, 21, 11) | |
164 | FIELD(TXHPB_ID, SRRRTR, 20, 1) | |
165 | FIELD(TXHPB_ID, IDE, 19, 1) | |
166 | FIELD(TXHPB_ID, IDL, 1, 18) | |
167 | FIELD(TXHPB_ID, RTR, 0, 1) | |
168 | REG32(TXHPB_DLC, 0x44) | |
169 | FIELD(TXHPB_DLC, DLC, 28, 4) | |
170 | REG32(TXHPB_DATA1, 0x48) | |
171 | FIELD(TXHPB_DATA1, DB0, 24, 8) | |
172 | FIELD(TXHPB_DATA1, DB1, 16, 8) | |
173 | FIELD(TXHPB_DATA1, DB2, 8, 8) | |
174 | FIELD(TXHPB_DATA1, DB3, 0, 8) | |
175 | REG32(TXHPB_DATA2, 0x4c) | |
176 | FIELD(TXHPB_DATA2, DB4, 24, 8) | |
177 | FIELD(TXHPB_DATA2, DB5, 16, 8) | |
178 | FIELD(TXHPB_DATA2, DB6, 8, 8) | |
179 | FIELD(TXHPB_DATA2, DB7, 0, 8) | |
180 | REG32(RXFIFO_ID, 0x50) | |
181 | FIELD(RXFIFO_ID, IDH, 21, 11) | |
182 | FIELD(RXFIFO_ID, SRRRTR, 20, 1) | |
183 | FIELD(RXFIFO_ID, IDE, 19, 1) | |
184 | FIELD(RXFIFO_ID, IDL, 1, 18) | |
185 | FIELD(RXFIFO_ID, RTR, 0, 1) | |
186 | REG32(RXFIFO_DLC, 0x54) | |
187 | FIELD(RXFIFO_DLC, DLC, 28, 4) | |
188 | FIELD(RXFIFO_DLC, RXT, 0, 16) | |
189 | REG32(RXFIFO_DATA1, 0x58) | |
190 | FIELD(RXFIFO_DATA1, DB0, 24, 8) | |
191 | FIELD(RXFIFO_DATA1, DB1, 16, 8) | |
192 | FIELD(RXFIFO_DATA1, DB2, 8, 8) | |
193 | FIELD(RXFIFO_DATA1, DB3, 0, 8) | |
194 | REG32(RXFIFO_DATA2, 0x5c) | |
195 | FIELD(RXFIFO_DATA2, DB4, 24, 8) | |
196 | FIELD(RXFIFO_DATA2, DB5, 16, 8) | |
197 | FIELD(RXFIFO_DATA2, DB6, 8, 8) | |
198 | FIELD(RXFIFO_DATA2, DB7, 0, 8) | |
199 | REG32(AFR, 0x60) | |
200 | FIELD(AFR, UAF4, 3, 1) | |
201 | FIELD(AFR, UAF3, 2, 1) | |
202 | FIELD(AFR, UAF2, 1, 1) | |
203 | FIELD(AFR, UAF1, 0, 1) | |
204 | REG32(AFMR1, 0x64) | |
205 | FIELD(AFMR1, AMIDH, 21, 11) | |
206 | FIELD(AFMR1, AMSRR, 20, 1) | |
207 | FIELD(AFMR1, AMIDE, 19, 1) | |
208 | FIELD(AFMR1, AMIDL, 1, 18) | |
209 | FIELD(AFMR1, AMRTR, 0, 1) | |
210 | REG32(AFIR1, 0x68) | |
211 | FIELD(AFIR1, AIIDH, 21, 11) | |
212 | FIELD(AFIR1, AISRR, 20, 1) | |
213 | FIELD(AFIR1, AIIDE, 19, 1) | |
214 | FIELD(AFIR1, AIIDL, 1, 18) | |
215 | FIELD(AFIR1, AIRTR, 0, 1) | |
216 | REG32(AFMR2, 0x6c) | |
217 | FIELD(AFMR2, AMIDH, 21, 11) | |
218 | FIELD(AFMR2, AMSRR, 20, 1) | |
219 | FIELD(AFMR2, AMIDE, 19, 1) | |
220 | FIELD(AFMR2, AMIDL, 1, 18) | |
221 | FIELD(AFMR2, AMRTR, 0, 1) | |
222 | REG32(AFIR2, 0x70) | |
223 | FIELD(AFIR2, AIIDH, 21, 11) | |
224 | FIELD(AFIR2, AISRR, 20, 1) | |
225 | FIELD(AFIR2, AIIDE, 19, 1) | |
226 | FIELD(AFIR2, AIIDL, 1, 18) | |
227 | FIELD(AFIR2, AIRTR, 0, 1) | |
228 | REG32(AFMR3, 0x74) | |
229 | FIELD(AFMR3, AMIDH, 21, 11) | |
230 | FIELD(AFMR3, AMSRR, 20, 1) | |
231 | FIELD(AFMR3, AMIDE, 19, 1) | |
232 | FIELD(AFMR3, AMIDL, 1, 18) | |
233 | FIELD(AFMR3, AMRTR, 0, 1) | |
234 | REG32(AFIR3, 0x78) | |
235 | FIELD(AFIR3, AIIDH, 21, 11) | |
236 | FIELD(AFIR3, AISRR, 20, 1) | |
237 | FIELD(AFIR3, AIIDE, 19, 1) | |
238 | FIELD(AFIR3, AIIDL, 1, 18) | |
239 | FIELD(AFIR3, AIRTR, 0, 1) | |
240 | REG32(AFMR4, 0x7c) | |
241 | FIELD(AFMR4, AMIDH, 21, 11) | |
242 | FIELD(AFMR4, AMSRR, 20, 1) | |
243 | FIELD(AFMR4, AMIDE, 19, 1) | |
244 | FIELD(AFMR4, AMIDL, 1, 18) | |
245 | FIELD(AFMR4, AMRTR, 0, 1) | |
246 | REG32(AFIR4, 0x80) | |
247 | FIELD(AFIR4, AIIDH, 21, 11) | |
248 | FIELD(AFIR4, AISRR, 20, 1) | |
249 | FIELD(AFIR4, AIIDE, 19, 1) | |
250 | FIELD(AFIR4, AIIDL, 1, 18) | |
251 | FIELD(AFIR4, AIRTR, 0, 1) | |
252 | ||
253 | static void can_update_irq(XlnxZynqMPCANState *s) | |
254 | { | |
255 | uint32_t irq; | |
256 | ||
257 | /* Watermark register interrupts. */ | |
258 | if ((fifo32_num_free(&s->tx_fifo) / CAN_FRAME_SIZE) > | |
259 | ARRAY_FIELD_EX32(s->regs, WIR, EW)) { | |
260 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXFWMEMP, 1); | |
261 | } | |
262 | ||
263 | if ((fifo32_num_used(&s->rx_fifo) / CAN_FRAME_SIZE) > | |
264 | ARRAY_FIELD_EX32(s->regs, WIR, FW)) { | |
265 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFWMFLL, 1); | |
266 | } | |
267 | ||
268 | /* RX Interrupts. */ | |
269 | if (fifo32_num_used(&s->rx_fifo) >= CAN_FRAME_SIZE) { | |
270 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXNEMP, 1); | |
271 | } | |
272 | ||
273 | /* TX interrupts. */ | |
274 | if (fifo32_is_empty(&s->tx_fifo)) { | |
275 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXFEMP, 1); | |
276 | } | |
277 | ||
278 | if (fifo32_is_full(&s->tx_fifo)) { | |
279 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXFLL, 1); | |
280 | } | |
281 | ||
282 | if (fifo32_is_full(&s->txhpb_fifo)) { | |
283 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXBFLL, 1); | |
284 | } | |
285 | ||
286 | irq = s->regs[R_INTERRUPT_STATUS_REGISTER]; | |
287 | irq &= s->regs[R_INTERRUPT_ENABLE_REGISTER]; | |
288 | ||
289 | trace_xlnx_can_update_irq(s->regs[R_INTERRUPT_STATUS_REGISTER], | |
290 | s->regs[R_INTERRUPT_ENABLE_REGISTER], irq); | |
291 | qemu_set_irq(s->irq, irq); | |
292 | } | |
293 | ||
294 | static void can_ier_post_write(RegisterInfo *reg, uint64_t val) | |
295 | { | |
296 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | |
297 | ||
298 | can_update_irq(s); | |
299 | } | |
300 | ||
301 | static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val) | |
302 | { | |
303 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | |
304 | ||
305 | s->regs[R_INTERRUPT_STATUS_REGISTER] &= ~val; | |
306 | can_update_irq(s); | |
307 | ||
308 | return 0; | |
309 | } | |
310 | ||
311 | static void can_config_reset(XlnxZynqMPCANState *s) | |
312 | { | |
313 | /* Reset all the configuration registers. */ | |
314 | register_reset(&s->reg_info[R_SOFTWARE_RESET_REGISTER]); | |
315 | register_reset(&s->reg_info[R_MODE_SELECT_REGISTER]); | |
316 | register_reset( | |
317 | &s->reg_info[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER]); | |
318 | register_reset(&s->reg_info[R_ARBITRATION_PHASE_BIT_TIMING_REGISTER]); | |
319 | register_reset(&s->reg_info[R_STATUS_REGISTER]); | |
320 | register_reset(&s->reg_info[R_INTERRUPT_STATUS_REGISTER]); | |
321 | register_reset(&s->reg_info[R_INTERRUPT_ENABLE_REGISTER]); | |
322 | register_reset(&s->reg_info[R_INTERRUPT_CLEAR_REGISTER]); | |
323 | register_reset(&s->reg_info[R_WIR]); | |
324 | } | |
325 | ||
326 | static void can_config_mode(XlnxZynqMPCANState *s) | |
327 | { | |
328 | register_reset(&s->reg_info[R_ERROR_COUNTER_REGISTER]); | |
329 | register_reset(&s->reg_info[R_ERROR_STATUS_REGISTER]); | |
330 | ||
331 | /* Put XlnxZynqMPCAN in configuration mode. */ | |
332 | ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 1); | |
333 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, 0); | |
334 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, 0); | |
335 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, BSOFF, 0); | |
336 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ERROR, 0); | |
337 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 0); | |
338 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 0); | |
339 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 0); | |
340 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ARBLST, 0); | |
341 | ||
342 | can_update_irq(s); | |
343 | } | |
344 | ||
345 | static void update_status_register_mode_bits(XlnxZynqMPCANState *s) | |
346 | { | |
347 | bool sleep_status = ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP); | |
348 | bool sleep_mode = ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SLEEP); | |
349 | /* Wake up interrupt bit. */ | |
350 | bool wakeup_irq_val = sleep_status && (sleep_mode == 0); | |
351 | /* Sleep interrupt bit. */ | |
352 | bool sleep_irq_val = sleep_mode && (sleep_status == 0); | |
353 | ||
354 | /* Clear previous core mode status bits. */ | |
355 | ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 0); | |
356 | ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 0); | |
357 | ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 0); | |
358 | ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 0); | |
359 | ||
360 | /* set current mode bit and generate irqs accordingly. */ | |
361 | if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, LBACK)) { | |
362 | ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 1); | |
363 | } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SLEEP)) { | |
364 | ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 1); | |
365 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, | |
366 | sleep_irq_val); | |
367 | } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SNOOP)) { | |
368 | ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 1); | |
369 | } else { | |
370 | /* | |
371 | * If all bits are zero then XlnxZynqMPCAN is set in normal mode. | |
372 | */ | |
373 | ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 1); | |
374 | /* Set wakeup interrupt bit. */ | |
375 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, | |
376 | wakeup_irq_val); | |
377 | } | |
378 | ||
379 | can_update_irq(s); | |
380 | } | |
381 | ||
382 | static void can_exit_sleep_mode(XlnxZynqMPCANState *s) | |
383 | { | |
384 | ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, 0); | |
385 | update_status_register_mode_bits(s); | |
386 | } | |
387 | ||
388 | static void generate_frame(qemu_can_frame *frame, uint32_t *data) | |
389 | { | |
390 | frame->can_id = data[0]; | |
391 | frame->can_dlc = FIELD_EX32(data[1], TXFIFO_DLC, DLC); | |
392 | ||
393 | frame->data[0] = FIELD_EX32(data[2], TXFIFO_DATA1, DB3); | |
394 | frame->data[1] = FIELD_EX32(data[2], TXFIFO_DATA1, DB2); | |
395 | frame->data[2] = FIELD_EX32(data[2], TXFIFO_DATA1, DB1); | |
396 | frame->data[3] = FIELD_EX32(data[2], TXFIFO_DATA1, DB0); | |
397 | ||
398 | frame->data[4] = FIELD_EX32(data[3], TXFIFO_DATA2, DB7); | |
399 | frame->data[5] = FIELD_EX32(data[3], TXFIFO_DATA2, DB6); | |
400 | frame->data[6] = FIELD_EX32(data[3], TXFIFO_DATA2, DB5); | |
401 | frame->data[7] = FIELD_EX32(data[3], TXFIFO_DATA2, DB4); | |
402 | } | |
403 | ||
404 | static bool tx_ready_check(XlnxZynqMPCANState *s) | |
405 | { | |
406 | if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) { | |
407 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
408 | ||
409 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer data while" | |
410 | " data while controller is in reset mode.\n", | |
411 | path); | |
412 | return false; | |
413 | } | |
414 | ||
415 | if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { | |
416 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
417 | ||
418 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer" | |
419 | " data while controller is in configuration mode. Reset" | |
420 | " the core so operations can start fresh.\n", | |
421 | path); | |
422 | return false; | |
423 | } | |
424 | ||
425 | if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) { | |
426 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
427 | ||
428 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer" | |
429 | " data while controller is in SNOOP MODE.\n", | |
430 | path); | |
431 | return false; | |
432 | } | |
433 | ||
434 | return true; | |
435 | } | |
436 | ||
437 | static void transfer_fifo(XlnxZynqMPCANState *s, Fifo32 *fifo) | |
438 | { | |
439 | qemu_can_frame frame; | |
440 | uint32_t data[CAN_FRAME_SIZE]; | |
441 | int i; | |
442 | bool can_tx = tx_ready_check(s); | |
443 | ||
444 | if (!can_tx) { | |
445 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
446 | ||
447 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is not enabled for data" | |
448 | " transfer.\n", path); | |
449 | can_update_irq(s); | |
450 | return; | |
451 | } | |
452 | ||
453 | while (!fifo32_is_empty(fifo)) { | |
454 | for (i = 0; i < CAN_FRAME_SIZE; i++) { | |
455 | data[i] = fifo32_pop(fifo); | |
456 | } | |
457 | ||
458 | if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) { | |
459 | /* | |
460 | * Controller is in loopback. In Loopback mode, the CAN core | |
461 | * transmits a recessive bitstream on to the XlnxZynqMPCAN Bus. | |
462 | * Any message transmitted is looped back to the RX line and | |
463 | * acknowledged. The XlnxZynqMPCAN core receives any message | |
464 | * that it transmits. | |
465 | */ | |
466 | if (fifo32_is_full(&s->rx_fifo)) { | |
467 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 1); | |
468 | } else { | |
469 | for (i = 0; i < CAN_FRAME_SIZE; i++) { | |
470 | fifo32_push(&s->rx_fifo, data[i]); | |
471 | } | |
472 | ||
473 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 1); | |
474 | } | |
475 | } else { | |
476 | /* Normal mode Tx. */ | |
477 | generate_frame(&frame, data); | |
478 | ||
479 | trace_xlnx_can_tx_data(frame.can_id, frame.can_dlc, | |
480 | frame.data[0], frame.data[1], | |
481 | frame.data[2], frame.data[3], | |
482 | frame.data[4], frame.data[5], | |
483 | frame.data[6], frame.data[7]); | |
484 | can_bus_client_send(&s->bus_client, &frame, 1); | |
485 | } | |
486 | } | |
487 | ||
488 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 1); | |
489 | ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, TXBFLL, 0); | |
490 | ||
491 | if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) { | |
492 | can_exit_sleep_mode(s); | |
493 | } | |
494 | ||
495 | can_update_irq(s); | |
496 | } | |
497 | ||
498 | static uint64_t can_srr_pre_write(RegisterInfo *reg, uint64_t val) | |
499 | { | |
500 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | |
501 | ||
502 | ARRAY_FIELD_DP32(s->regs, SOFTWARE_RESET_REGISTER, CEN, | |
503 | FIELD_EX32(val, SOFTWARE_RESET_REGISTER, CEN)); | |
504 | ||
505 | if (FIELD_EX32(val, SOFTWARE_RESET_REGISTER, SRST)) { | |
506 | trace_xlnx_can_reset(val); | |
507 | ||
508 | /* First, core will do software reset then will enter in config mode. */ | |
509 | can_config_reset(s); | |
510 | } | |
511 | ||
512 | if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { | |
513 | can_config_mode(s); | |
514 | } else { | |
515 | /* | |
516 | * Leave config mode. Now XlnxZynqMPCAN core will enter normal, | |
517 | * sleep, snoop or loopback mode depending upon LBACK, SLEEP, SNOOP | |
518 | * register states. | |
519 | */ | |
520 | ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0); | |
521 | ||
522 | ptimer_transaction_begin(s->can_timer); | |
523 | ptimer_set_count(s->can_timer, 0); | |
524 | ptimer_transaction_commit(s->can_timer); | |
525 | ||
526 | /* XlnxZynqMPCAN is out of config mode. It will send pending data. */ | |
527 | transfer_fifo(s, &s->txhpb_fifo); | |
528 | transfer_fifo(s, &s->tx_fifo); | |
529 | } | |
530 | ||
531 | update_status_register_mode_bits(s); | |
532 | ||
533 | return s->regs[R_SOFTWARE_RESET_REGISTER]; | |
534 | } | |
535 | ||
536 | static uint64_t can_msr_pre_write(RegisterInfo *reg, uint64_t val) | |
537 | { | |
538 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | |
539 | uint8_t multi_mode; | |
540 | ||
541 | /* | |
542 | * Multiple mode set check. This is done to make sure user doesn't set | |
543 | * multiple modes. | |
544 | */ | |
545 | multi_mode = FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK) + | |
546 | FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP) + | |
547 | FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP); | |
548 | ||
549 | if (multi_mode > 1) { | |
550 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
551 | ||
552 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to config" | |
553 | " several modes simultaneously. One mode will be selected" | |
554 | " according to their priority: LBACK > SLEEP > SNOOP.\n", | |
555 | path); | |
556 | } | |
557 | ||
558 | if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) { | |
559 | /* We are in configuration mode, any mode can be selected. */ | |
560 | s->regs[R_MODE_SELECT_REGISTER] = val; | |
561 | } else { | |
562 | bool sleep_mode_bit = FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP); | |
563 | ||
564 | ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, sleep_mode_bit); | |
565 | ||
566 | if (FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK)) { | |
567 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
568 | ||
569 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to set" | |
570 | " LBACK mode without setting CEN bit as 0.\n", | |
571 | path); | |
572 | } else if (FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP)) { | |
573 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
574 | ||
575 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to set" | |
576 | " SNOOP mode without setting CEN bit as 0.\n", | |
577 | path); | |
578 | } | |
579 | ||
580 | update_status_register_mode_bits(s); | |
581 | } | |
582 | ||
583 | return s->regs[R_MODE_SELECT_REGISTER]; | |
584 | } | |
585 | ||
586 | static uint64_t can_brpr_pre_write(RegisterInfo *reg, uint64_t val) | |
587 | { | |
588 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | |
589 | ||
590 | /* Only allow writes when in config mode. */ | |
591 | if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) { | |
592 | return s->regs[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER]; | |
593 | } | |
594 | ||
595 | return val; | |
596 | } | |
597 | ||
598 | static uint64_t can_btr_pre_write(RegisterInfo *reg, uint64_t val) | |
599 | { | |
600 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | |
601 | ||
602 | /* Only allow writes when in config mode. */ | |
603 | if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) { | |
604 | return s->regs[R_ARBITRATION_PHASE_BIT_TIMING_REGISTER]; | |
605 | } | |
606 | ||
607 | return val; | |
608 | } | |
609 | ||
610 | static uint64_t can_tcr_pre_write(RegisterInfo *reg, uint64_t val) | |
611 | { | |
612 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | |
613 | ||
614 | if (FIELD_EX32(val, TIMESTAMP_REGISTER, CTS)) { | |
615 | ptimer_transaction_begin(s->can_timer); | |
616 | ptimer_set_count(s->can_timer, 0); | |
617 | ptimer_transaction_commit(s->can_timer); | |
618 | } | |
619 | ||
620 | return 0; | |
621 | } | |
622 | ||
623 | static void update_rx_fifo(XlnxZynqMPCANState *s, const qemu_can_frame *frame) | |
624 | { | |
625 | bool filter_pass = false; | |
626 | uint16_t timestamp = 0; | |
627 | ||
628 | /* If no filter is enabled. Message will be stored in FIFO. */ | |
629 | if (!((ARRAY_FIELD_EX32(s->regs, AFR, UAF1)) | | |
630 | (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) | | |
631 | (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) | | |
632 | (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)))) { | |
633 | filter_pass = true; | |
634 | } | |
635 | ||
636 | /* | |
637 | * Messages that pass any of the acceptance filters will be stored in | |
638 | * the RX FIFO. | |
639 | */ | |
640 | if (ARRAY_FIELD_EX32(s->regs, AFR, UAF1)) { | |
641 | uint32_t id_masked = s->regs[R_AFMR1] & frame->can_id; | |
642 | uint32_t filter_id_masked = s->regs[R_AFMR1] & s->regs[R_AFIR1]; | |
643 | ||
644 | if (filter_id_masked == id_masked) { | |
645 | filter_pass = true; | |
646 | } | |
647 | } | |
648 | ||
649 | if (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) { | |
650 | uint32_t id_masked = s->regs[R_AFMR2] & frame->can_id; | |
651 | uint32_t filter_id_masked = s->regs[R_AFMR2] & s->regs[R_AFIR2]; | |
652 | ||
653 | if (filter_id_masked == id_masked) { | |
654 | filter_pass = true; | |
655 | } | |
656 | } | |
657 | ||
658 | if (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) { | |
659 | uint32_t id_masked = s->regs[R_AFMR3] & frame->can_id; | |
660 | uint32_t filter_id_masked = s->regs[R_AFMR3] & s->regs[R_AFIR3]; | |
661 | ||
662 | if (filter_id_masked == id_masked) { | |
663 | filter_pass = true; | |
664 | } | |
665 | } | |
666 | ||
667 | if (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) { | |
668 | uint32_t id_masked = s->regs[R_AFMR4] & frame->can_id; | |
669 | uint32_t filter_id_masked = s->regs[R_AFMR4] & s->regs[R_AFIR4]; | |
670 | ||
671 | if (filter_id_masked == id_masked) { | |
672 | filter_pass = true; | |
673 | } | |
674 | } | |
675 | ||
676 | if (!filter_pass) { | |
677 | trace_xlnx_can_rx_fifo_filter_reject(frame->can_id, frame->can_dlc); | |
678 | return; | |
679 | } | |
680 | ||
681 | /* Store the message in fifo if it passed through any of the filters. */ | |
682 | if (filter_pass && frame->can_dlc <= MAX_DLC) { | |
683 | ||
684 | if (fifo32_is_full(&s->rx_fifo)) { | |
685 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 1); | |
686 | } else { | |
687 | timestamp = CAN_TIMER_MAX - ptimer_get_count(s->can_timer); | |
688 | ||
689 | fifo32_push(&s->rx_fifo, frame->can_id); | |
690 | ||
691 | fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DLC_DLC_SHIFT, | |
692 | R_RXFIFO_DLC_DLC_LENGTH, | |
693 | frame->can_dlc) | | |
694 | deposit32(0, R_RXFIFO_DLC_RXT_SHIFT, | |
695 | R_RXFIFO_DLC_RXT_LENGTH, | |
696 | timestamp)); | |
697 | ||
698 | /* First 32 bit of the data. */ | |
fb96d131 AK |
699 | fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DATA1_DB3_SHIFT, |
700 | R_RXFIFO_DATA1_DB3_LENGTH, | |
98e5d7a2 | 701 | frame->data[0]) | |
fb96d131 AK |
702 | deposit32(0, R_RXFIFO_DATA1_DB2_SHIFT, |
703 | R_RXFIFO_DATA1_DB2_LENGTH, | |
98e5d7a2 | 704 | frame->data[1]) | |
fb96d131 AK |
705 | deposit32(0, R_RXFIFO_DATA1_DB1_SHIFT, |
706 | R_RXFIFO_DATA1_DB1_LENGTH, | |
98e5d7a2 | 707 | frame->data[2]) | |
fb96d131 AK |
708 | deposit32(0, R_RXFIFO_DATA1_DB0_SHIFT, |
709 | R_RXFIFO_DATA1_DB0_LENGTH, | |
98e5d7a2 VG |
710 | frame->data[3])); |
711 | /* Last 32 bit of the data. */ | |
fb96d131 AK |
712 | fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DATA2_DB7_SHIFT, |
713 | R_RXFIFO_DATA2_DB7_LENGTH, | |
98e5d7a2 | 714 | frame->data[4]) | |
fb96d131 AK |
715 | deposit32(0, R_RXFIFO_DATA2_DB6_SHIFT, |
716 | R_RXFIFO_DATA2_DB6_LENGTH, | |
98e5d7a2 | 717 | frame->data[5]) | |
fb96d131 AK |
718 | deposit32(0, R_RXFIFO_DATA2_DB5_SHIFT, |
719 | R_RXFIFO_DATA2_DB5_LENGTH, | |
98e5d7a2 | 720 | frame->data[6]) | |
fb96d131 AK |
721 | deposit32(0, R_RXFIFO_DATA2_DB4_SHIFT, |
722 | R_RXFIFO_DATA2_DB4_LENGTH, | |
98e5d7a2 VG |
723 | frame->data[7])); |
724 | ||
725 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 1); | |
726 | trace_xlnx_can_rx_data(frame->can_id, frame->can_dlc, | |
727 | frame->data[0], frame->data[1], | |
728 | frame->data[2], frame->data[3], | |
729 | frame->data[4], frame->data[5], | |
730 | frame->data[6], frame->data[7]); | |
731 | } | |
732 | ||
733 | can_update_irq(s); | |
734 | } | |
735 | } | |
736 | ||
737 | static uint64_t can_rxfifo_pre_read(RegisterInfo *reg, uint64_t val) | |
738 | { | |
739 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | |
740 | ||
741 | if (!fifo32_is_empty(&s->rx_fifo)) { | |
742 | val = fifo32_pop(&s->rx_fifo); | |
743 | } else { | |
744 | ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXUFLW, 1); | |
745 | } | |
746 | ||
747 | can_update_irq(s); | |
748 | return val; | |
749 | } | |
750 | ||
751 | static void can_filter_enable_post_write(RegisterInfo *reg, uint64_t val) | |
752 | { | |
753 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | |
754 | ||
755 | if (ARRAY_FIELD_EX32(s->regs, AFR, UAF1) && | |
756 | ARRAY_FIELD_EX32(s->regs, AFR, UAF2) && | |
757 | ARRAY_FIELD_EX32(s->regs, AFR, UAF3) && | |
758 | ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) { | |
759 | ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ACFBSY, 1); | |
760 | } else { | |
761 | ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ACFBSY, 0); | |
762 | } | |
763 | } | |
764 | ||
765 | static uint64_t can_filter_mask_pre_write(RegisterInfo *reg, uint64_t val) | |
766 | { | |
767 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | |
768 | uint32_t reg_idx = (reg->access->addr) / 4; | |
769 | uint32_t filter_number = (reg_idx - R_AFMR1) / 2; | |
770 | ||
771 | /* modify an acceptance filter, the corresponding UAF bit should be '0'. */ | |
772 | if (!(s->regs[R_AFR] & (1 << filter_number))) { | |
773 | s->regs[reg_idx] = val; | |
774 | ||
775 | trace_xlnx_can_filter_mask_pre_write(filter_number, s->regs[reg_idx]); | |
776 | } else { | |
777 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
778 | ||
779 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d" | |
780 | " mask is not set as corresponding UAF bit is not 0.\n", | |
781 | path, filter_number + 1); | |
782 | } | |
783 | ||
784 | return s->regs[reg_idx]; | |
785 | } | |
786 | ||
787 | static uint64_t can_filter_id_pre_write(RegisterInfo *reg, uint64_t val) | |
788 | { | |
789 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | |
790 | uint32_t reg_idx = (reg->access->addr) / 4; | |
791 | uint32_t filter_number = (reg_idx - R_AFIR1) / 2; | |
792 | ||
793 | if (!(s->regs[R_AFR] & (1 << filter_number))) { | |
794 | s->regs[reg_idx] = val; | |
795 | ||
796 | trace_xlnx_can_filter_id_pre_write(filter_number, s->regs[reg_idx]); | |
797 | } else { | |
798 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
799 | ||
800 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d" | |
801 | " id is not set as corresponding UAF bit is not 0.\n", | |
802 | path, filter_number + 1); | |
803 | } | |
804 | ||
805 | return s->regs[reg_idx]; | |
806 | } | |
807 | ||
808 | static void can_tx_post_write(RegisterInfo *reg, uint64_t val) | |
809 | { | |
810 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque); | |
811 | ||
812 | bool is_txhpb = reg->access->addr > A_TXFIFO_DATA2; | |
813 | ||
814 | bool initiate_transfer = (reg->access->addr == A_TXFIFO_DATA2) || | |
815 | (reg->access->addr == A_TXHPB_DATA2); | |
816 | ||
817 | Fifo32 *f = is_txhpb ? &s->txhpb_fifo : &s->tx_fifo; | |
818 | ||
819 | if (!fifo32_is_full(f)) { | |
820 | fifo32_push(f, val); | |
821 | } else { | |
822 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
823 | ||
824 | qemu_log_mask(LOG_GUEST_ERROR, "%s: TX FIFO is full.\n", path); | |
825 | } | |
826 | ||
827 | /* Initiate the message send if TX register is written. */ | |
828 | if (initiate_transfer && | |
829 | ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) { | |
830 | transfer_fifo(s, f); | |
831 | } | |
832 | ||
833 | can_update_irq(s); | |
834 | } | |
835 | ||
836 | static const RegisterAccessInfo can_regs_info[] = { | |
837 | { .name = "SOFTWARE_RESET_REGISTER", | |
838 | .addr = A_SOFTWARE_RESET_REGISTER, | |
839 | .rsvd = 0xfffffffc, | |
840 | .pre_write = can_srr_pre_write, | |
841 | },{ .name = "MODE_SELECT_REGISTER", | |
842 | .addr = A_MODE_SELECT_REGISTER, | |
843 | .rsvd = 0xfffffff8, | |
844 | .pre_write = can_msr_pre_write, | |
845 | },{ .name = "ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER", | |
846 | .addr = A_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, | |
847 | .rsvd = 0xffffff00, | |
848 | .pre_write = can_brpr_pre_write, | |
849 | },{ .name = "ARBITRATION_PHASE_BIT_TIMING_REGISTER", | |
850 | .addr = A_ARBITRATION_PHASE_BIT_TIMING_REGISTER, | |
851 | .rsvd = 0xfffffe00, | |
852 | .pre_write = can_btr_pre_write, | |
853 | },{ .name = "ERROR_COUNTER_REGISTER", | |
854 | .addr = A_ERROR_COUNTER_REGISTER, | |
855 | .rsvd = 0xffff0000, | |
856 | .ro = 0xffffffff, | |
857 | },{ .name = "ERROR_STATUS_REGISTER", | |
858 | .addr = A_ERROR_STATUS_REGISTER, | |
859 | .rsvd = 0xffffffe0, | |
860 | .w1c = 0x1f, | |
861 | },{ .name = "STATUS_REGISTER", .addr = A_STATUS_REGISTER, | |
862 | .reset = 0x1, | |
863 | .rsvd = 0xffffe000, | |
864 | .ro = 0x1fff, | |
865 | },{ .name = "INTERRUPT_STATUS_REGISTER", | |
866 | .addr = A_INTERRUPT_STATUS_REGISTER, | |
867 | .reset = 0x6000, | |
868 | .rsvd = 0xffff8000, | |
869 | .ro = 0x7fff, | |
870 | },{ .name = "INTERRUPT_ENABLE_REGISTER", | |
871 | .addr = A_INTERRUPT_ENABLE_REGISTER, | |
872 | .rsvd = 0xffff8000, | |
873 | .post_write = can_ier_post_write, | |
874 | },{ .name = "INTERRUPT_CLEAR_REGISTER", | |
875 | .addr = A_INTERRUPT_CLEAR_REGISTER, | |
876 | .rsvd = 0xffff8000, | |
877 | .pre_write = can_icr_pre_write, | |
878 | },{ .name = "TIMESTAMP_REGISTER", | |
879 | .addr = A_TIMESTAMP_REGISTER, | |
880 | .rsvd = 0xfffffffe, | |
881 | .pre_write = can_tcr_pre_write, | |
882 | },{ .name = "WIR", .addr = A_WIR, | |
883 | .reset = 0x3f3f, | |
884 | .rsvd = 0xffff0000, | |
885 | },{ .name = "TXFIFO_ID", .addr = A_TXFIFO_ID, | |
886 | .post_write = can_tx_post_write, | |
887 | },{ .name = "TXFIFO_DLC", .addr = A_TXFIFO_DLC, | |
888 | .rsvd = 0xfffffff, | |
889 | .post_write = can_tx_post_write, | |
890 | },{ .name = "TXFIFO_DATA1", .addr = A_TXFIFO_DATA1, | |
891 | .post_write = can_tx_post_write, | |
892 | },{ .name = "TXFIFO_DATA2", .addr = A_TXFIFO_DATA2, | |
893 | .post_write = can_tx_post_write, | |
894 | },{ .name = "TXHPB_ID", .addr = A_TXHPB_ID, | |
895 | .post_write = can_tx_post_write, | |
896 | },{ .name = "TXHPB_DLC", .addr = A_TXHPB_DLC, | |
897 | .rsvd = 0xfffffff, | |
898 | .post_write = can_tx_post_write, | |
899 | },{ .name = "TXHPB_DATA1", .addr = A_TXHPB_DATA1, | |
900 | .post_write = can_tx_post_write, | |
901 | },{ .name = "TXHPB_DATA2", .addr = A_TXHPB_DATA2, | |
902 | .post_write = can_tx_post_write, | |
903 | },{ .name = "RXFIFO_ID", .addr = A_RXFIFO_ID, | |
904 | .ro = 0xffffffff, | |
905 | .post_read = can_rxfifo_pre_read, | |
906 | },{ .name = "RXFIFO_DLC", .addr = A_RXFIFO_DLC, | |
907 | .rsvd = 0xfff0000, | |
908 | .post_read = can_rxfifo_pre_read, | |
909 | },{ .name = "RXFIFO_DATA1", .addr = A_RXFIFO_DATA1, | |
910 | .post_read = can_rxfifo_pre_read, | |
911 | },{ .name = "RXFIFO_DATA2", .addr = A_RXFIFO_DATA2, | |
912 | .post_read = can_rxfifo_pre_read, | |
913 | },{ .name = "AFR", .addr = A_AFR, | |
914 | .rsvd = 0xfffffff0, | |
915 | .post_write = can_filter_enable_post_write, | |
916 | },{ .name = "AFMR1", .addr = A_AFMR1, | |
917 | .pre_write = can_filter_mask_pre_write, | |
918 | },{ .name = "AFIR1", .addr = A_AFIR1, | |
919 | .pre_write = can_filter_id_pre_write, | |
920 | },{ .name = "AFMR2", .addr = A_AFMR2, | |
921 | .pre_write = can_filter_mask_pre_write, | |
922 | },{ .name = "AFIR2", .addr = A_AFIR2, | |
923 | .pre_write = can_filter_id_pre_write, | |
924 | },{ .name = "AFMR3", .addr = A_AFMR3, | |
925 | .pre_write = can_filter_mask_pre_write, | |
926 | },{ .name = "AFIR3", .addr = A_AFIR3, | |
927 | .pre_write = can_filter_id_pre_write, | |
928 | },{ .name = "AFMR4", .addr = A_AFMR4, | |
929 | .pre_write = can_filter_mask_pre_write, | |
930 | },{ .name = "AFIR4", .addr = A_AFIR4, | |
931 | .pre_write = can_filter_id_pre_write, | |
932 | } | |
933 | }; | |
934 | ||
935 | static void xlnx_zynqmp_can_ptimer_cb(void *opaque) | |
936 | { | |
937 | /* No action required on the timer rollover. */ | |
938 | } | |
939 | ||
940 | static const MemoryRegionOps can_ops = { | |
941 | .read = register_read_memory, | |
942 | .write = register_write_memory, | |
943 | .endianness = DEVICE_LITTLE_ENDIAN, | |
944 | .valid = { | |
945 | .min_access_size = 4, | |
946 | .max_access_size = 4, | |
947 | }, | |
948 | }; | |
949 | ||
950 | static void xlnx_zynqmp_can_reset_init(Object *obj, ResetType type) | |
951 | { | |
952 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj); | |
953 | unsigned int i; | |
954 | ||
955 | for (i = R_RXFIFO_ID; i < ARRAY_SIZE(s->reg_info); ++i) { | |
956 | register_reset(&s->reg_info[i]); | |
957 | } | |
958 | ||
959 | ptimer_transaction_begin(s->can_timer); | |
960 | ptimer_set_count(s->can_timer, 0); | |
961 | ptimer_transaction_commit(s->can_timer); | |
962 | } | |
963 | ||
964 | static void xlnx_zynqmp_can_reset_hold(Object *obj) | |
965 | { | |
966 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj); | |
967 | unsigned int i; | |
968 | ||
969 | for (i = 0; i < R_RXFIFO_ID; ++i) { | |
970 | register_reset(&s->reg_info[i]); | |
971 | } | |
972 | ||
973 | /* | |
974 | * Reset FIFOs when CAN model is reset. This will clear the fifo writes | |
975 | * done by post_write which gets called from register_reset function, | |
976 | * post_write handle will not be able to trigger tx because CAN will be | |
977 | * disabled when software_reset_register is cleared first. | |
978 | */ | |
979 | fifo32_reset(&s->rx_fifo); | |
980 | fifo32_reset(&s->tx_fifo); | |
981 | fifo32_reset(&s->txhpb_fifo); | |
982 | } | |
983 | ||
984 | static bool xlnx_zynqmp_can_can_receive(CanBusClientState *client) | |
985 | { | |
986 | XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState, | |
987 | bus_client); | |
988 | ||
989 | if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) { | |
990 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
991 | ||
992 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is in reset state.\n", | |
993 | path); | |
994 | return false; | |
995 | } | |
996 | ||
997 | if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) == 0) { | |
998 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
999 | ||
1000 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is disabled. Incoming" | |
1001 | " messages will be discarded.\n", path); | |
1002 | return false; | |
1003 | } | |
1004 | ||
1005 | return true; | |
1006 | } | |
1007 | ||
1008 | static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client, | |
1009 | const qemu_can_frame *buf, size_t buf_size) { | |
1010 | XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState, | |
1011 | bus_client); | |
1012 | const qemu_can_frame *frame = buf; | |
1013 | ||
1014 | if (buf_size <= 0) { | |
1015 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
1016 | ||
1017 | qemu_log_mask(LOG_GUEST_ERROR, "%s: Error in the data received.\n", | |
1018 | path); | |
1019 | return 0; | |
1020 | } | |
1021 | ||
1022 | if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) { | |
1023 | /* Snoop Mode: Just keep the data. no response back. */ | |
1024 | update_rx_fifo(s, frame); | |
1025 | } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP))) { | |
1026 | /* | |
1027 | * XlnxZynqMPCAN is in sleep mode. Any data on bus will bring it to wake | |
1028 | * up state. | |
1029 | */ | |
1030 | can_exit_sleep_mode(s); | |
1031 | update_rx_fifo(s, frame); | |
1032 | } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) == 0) { | |
1033 | update_rx_fifo(s, frame); | |
1034 | } else { | |
1035 | /* | |
1036 | * XlnxZynqMPCAN will not participate in normal bus communication | |
1037 | * and will not receive any messages transmitted by other CAN nodes. | |
1038 | */ | |
1039 | trace_xlnx_can_rx_discard(s->regs[R_STATUS_REGISTER]); | |
1040 | } | |
1041 | ||
1042 | return 1; | |
1043 | } | |
1044 | ||
1045 | static CanBusClientInfo can_xilinx_bus_client_info = { | |
1046 | .can_receive = xlnx_zynqmp_can_can_receive, | |
1047 | .receive = xlnx_zynqmp_can_receive, | |
1048 | }; | |
1049 | ||
1050 | static int xlnx_zynqmp_can_connect_to_bus(XlnxZynqMPCANState *s, | |
1051 | CanBusState *bus) | |
1052 | { | |
1053 | s->bus_client.info = &can_xilinx_bus_client_info; | |
1054 | ||
1055 | if (can_bus_insert_client(bus, &s->bus_client) < 0) { | |
1056 | return -1; | |
1057 | } | |
1058 | return 0; | |
1059 | } | |
1060 | ||
1061 | static void xlnx_zynqmp_can_realize(DeviceState *dev, Error **errp) | |
1062 | { | |
1063 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(dev); | |
1064 | ||
1065 | if (s->canbus) { | |
1066 | if (xlnx_zynqmp_can_connect_to_bus(s, s->canbus) < 0) { | |
1067 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); | |
1068 | ||
1069 | error_setg(errp, "%s: xlnx_zynqmp_can_connect_to_bus" | |
1070 | " failed.", path); | |
1071 | return; | |
1072 | } | |
1073 | } | |
1074 | ||
1075 | /* Create RX FIFO, TXFIFO, TXHPB storage. */ | |
1076 | fifo32_create(&s->rx_fifo, RXFIFO_SIZE); | |
1077 | fifo32_create(&s->tx_fifo, RXFIFO_SIZE); | |
1078 | fifo32_create(&s->txhpb_fifo, CAN_FRAME_SIZE); | |
1079 | ||
1080 | /* Allocate a new timer. */ | |
1081 | s->can_timer = ptimer_init(xlnx_zynqmp_can_ptimer_cb, s, | |
9598c1bb | 1082 | PTIMER_POLICY_LEGACY); |
98e5d7a2 VG |
1083 | |
1084 | ptimer_transaction_begin(s->can_timer); | |
1085 | ||
1086 | ptimer_set_freq(s->can_timer, s->cfg.ext_clk_freq); | |
1087 | ptimer_set_limit(s->can_timer, CAN_TIMER_MAX, 1); | |
1088 | ptimer_run(s->can_timer, 0); | |
1089 | ptimer_transaction_commit(s->can_timer); | |
1090 | } | |
1091 | ||
1092 | static void xlnx_zynqmp_can_init(Object *obj) | |
1093 | { | |
1094 | XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj); | |
1095 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); | |
1096 | ||
1097 | RegisterInfoArray *reg_array; | |
1098 | ||
1099 | memory_region_init(&s->iomem, obj, TYPE_XLNX_ZYNQMP_CAN, | |
1100 | XLNX_ZYNQMP_CAN_R_MAX * 4); | |
1101 | reg_array = register_init_block32(DEVICE(obj), can_regs_info, | |
1102 | ARRAY_SIZE(can_regs_info), | |
1103 | s->reg_info, s->regs, | |
1104 | &can_ops, | |
1105 | XLNX_ZYNQMP_CAN_ERR_DEBUG, | |
1106 | XLNX_ZYNQMP_CAN_R_MAX * 4); | |
1107 | ||
1108 | memory_region_add_subregion(&s->iomem, 0x00, ®_array->mem); | |
1109 | sysbus_init_mmio(sbd, &s->iomem); | |
1110 | sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); | |
1111 | } | |
1112 | ||
1113 | static const VMStateDescription vmstate_can = { | |
1114 | .name = TYPE_XLNX_ZYNQMP_CAN, | |
1115 | .version_id = 1, | |
1116 | .minimum_version_id = 1, | |
1117 | .fields = (VMStateField[]) { | |
1118 | VMSTATE_FIFO32(rx_fifo, XlnxZynqMPCANState), | |
1119 | VMSTATE_FIFO32(tx_fifo, XlnxZynqMPCANState), | |
1120 | VMSTATE_FIFO32(txhpb_fifo, XlnxZynqMPCANState), | |
1121 | VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCANState, XLNX_ZYNQMP_CAN_R_MAX), | |
1122 | VMSTATE_PTIMER(can_timer, XlnxZynqMPCANState), | |
1123 | VMSTATE_END_OF_LIST(), | |
1124 | } | |
1125 | }; | |
1126 | ||
1127 | static Property xlnx_zynqmp_can_properties[] = { | |
1128 | DEFINE_PROP_UINT32("ext_clk_freq", XlnxZynqMPCANState, cfg.ext_clk_freq, | |
1129 | CAN_DEFAULT_CLOCK), | |
1130 | DEFINE_PROP_LINK("canbus", XlnxZynqMPCANState, canbus, TYPE_CAN_BUS, | |
1131 | CanBusState *), | |
1132 | DEFINE_PROP_END_OF_LIST(), | |
1133 | }; | |
1134 | ||
1135 | static void xlnx_zynqmp_can_class_init(ObjectClass *klass, void *data) | |
1136 | { | |
1137 | DeviceClass *dc = DEVICE_CLASS(klass); | |
1138 | ResettableClass *rc = RESETTABLE_CLASS(klass); | |
1139 | ||
1140 | rc->phases.enter = xlnx_zynqmp_can_reset_init; | |
1141 | rc->phases.hold = xlnx_zynqmp_can_reset_hold; | |
1142 | dc->realize = xlnx_zynqmp_can_realize; | |
1143 | device_class_set_props(dc, xlnx_zynqmp_can_properties); | |
1144 | dc->vmsd = &vmstate_can; | |
1145 | } | |
1146 | ||
1147 | static const TypeInfo can_info = { | |
1148 | .name = TYPE_XLNX_ZYNQMP_CAN, | |
1149 | .parent = TYPE_SYS_BUS_DEVICE, | |
1150 | .instance_size = sizeof(XlnxZynqMPCANState), | |
1151 | .class_init = xlnx_zynqmp_can_class_init, | |
1152 | .instance_init = xlnx_zynqmp_can_init, | |
1153 | }; | |
1154 | ||
1155 | static void can_register_types(void) | |
1156 | { | |
1157 | type_register_static(&can_info); | |
1158 | } | |
1159 | ||
1160 | type_init(can_register_types) |