| 1 | /* |
| 2 | * SPDX-License-Identifier: MIT |
| 3 | * |
| 4 | * QTests for the Xilinx Versal CANFD controller. |
| 5 | * |
| 6 | * Copyright (c) 2022 AMD Inc. |
| 7 | * |
| 8 | * Written-by: Vikram Garhwal<vikram.garhwal@amd.com> |
| 9 | * |
| 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 11 | * of this software and associated documentation files (the "Software"), to deal |
| 12 | * in the Software without restriction, including without limitation the rights |
| 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 14 | * copies of the Software, and to permit persons to whom the Software is |
| 15 | * furnished to do so, subject to the following conditions: |
| 16 | * |
| 17 | * The above copyright notice and this permission notice shall be included in |
| 18 | * all copies or substantial portions of the Software. |
| 19 | * |
| 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 23 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 26 | * THE SOFTWARE. |
| 27 | */ |
| 28 | |
| 29 | #include "qemu/osdep.h" |
| 30 | #include "libqtest.h" |
| 31 | |
| 32 | /* Base address. */ |
| 33 | #define CANFD0_BASE_ADDR 0xff060000 |
| 34 | #define CANFD1_BASE_ADDR 0xff070000 |
| 35 | |
| 36 | /* Register addresses. */ |
| 37 | #define R_SRR_OFFSET 0x00 |
| 38 | #define R_MSR_OFFSET 0x04 |
| 39 | #define R_FILTER_CONTROL_REGISTER 0xe0 |
| 40 | #define R_SR_OFFSET 0x18 |
| 41 | #define R_ISR_OFFSET 0x1c |
| 42 | #define R_IER_OFFSET 0x20 |
| 43 | #define R_ICR_OFFSET 0x24 |
| 44 | #define R_TX_READY_REQ_REGISTER 0x90 |
| 45 | #define RX_FIFO_STATUS_REGISTER 0xe8 |
| 46 | #define R_TXID_OFFSET 0x100 |
| 47 | #define R_TXDLC_OFFSET 0x104 |
| 48 | #define R_TXDATA1_OFFSET 0x108 |
| 49 | #define R_TXDATA2_OFFSET 0x10c |
| 50 | #define R_AFMR_REGISTER0 0xa00 |
| 51 | #define R_AFIR_REGISTER0 0xa04 |
| 52 | #define R_RX0_ID_OFFSET 0x2100 |
| 53 | #define R_RX0_DLC_OFFSET 0x2104 |
| 54 | #define R_RX0_DATA1_OFFSET 0x2108 |
| 55 | #define R_RX0_DATA2_OFFSET 0x210c |
| 56 | |
| 57 | /* CANFD modes. */ |
| 58 | #define SRR_CONFIG_MODE 0x00 |
| 59 | #define MSR_NORMAL_MODE 0x00 |
| 60 | #define MSR_LOOPBACK_MODE (1 << 1) |
| 61 | #define ENABLE_CANFD (1 << 1) |
| 62 | |
| 63 | /* CANFD status. */ |
| 64 | #define STATUS_CONFIG_MODE (1 << 0) |
| 65 | #define STATUS_NORMAL_MODE (1 << 3) |
| 66 | #define STATUS_LOOPBACK_MODE (1 << 1) |
| 67 | #define ISR_TXOK (1 << 1) |
| 68 | #define ISR_RXOK (1 << 4) |
| 69 | |
| 70 | #define ENABLE_ALL_FILTERS 0xffffffff |
| 71 | #define ENABLE_ALL_INTERRUPTS 0xffffffff |
| 72 | |
| 73 | /* We are sending one canfd message. */ |
| 74 | #define TX_READY_REG_VAL 0x1 |
| 75 | |
| 76 | #define FIRST_RX_STORE_INDEX 0x1 |
| 77 | #define STATUS_REG_MASK 0xf |
| 78 | #define DLC_FD_BIT_SHIFT 0x1b |
| 79 | #define DLC_FD_BIT_MASK 0xf8000000 |
| 80 | #define FIFO_STATUS_READ_INDEX_MASK 0x3f |
| 81 | #define FIFO_STATUS_FILL_LEVEL_MASK 0x7f00 |
| 82 | #define FILL_LEVEL_SHIFT 0x8 |
| 83 | |
| 84 | /* CANFD frame size ID, DLC and 16 DATA word. */ |
| 85 | #define CANFD_FRAME_SIZE 18 |
| 86 | /* CAN frame size ID, DLC and 2 DATA word. */ |
| 87 | #define CAN_FRAME_SIZE 4 |
| 88 | |
| 89 | /* Set the filters for CANFD controller. */ |
| 90 | static void enable_filters(QTestState *qts) |
| 91 | { |
| 92 | const uint32_t arr_afmr[32] = { 0xb423deaa, 0xa2a40bdc, 0x1b64f486, |
| 93 | 0x95c0d4ee, 0xe0c44528, 0x4b407904, |
| 94 | 0xd2673f46, 0x9fc638d6, 0x8844f3d8, |
| 95 | 0xa607d1e8, 0x67871bf4, 0xc2557dc, |
| 96 | 0x9ea5b53e, 0x3643c0cc, 0x5a05ea8e, |
| 97 | 0x83a46d84, 0x4a25c2b8, 0x93a66008, |
| 98 | 0x2e467470, 0xedc66118, 0x9086f9f2, |
| 99 | 0xfa23dd36, 0xb6654b90, 0xb221b8ca, |
| 100 | 0x3467d1e2, 0xa3a55542, 0x5b26a012, |
| 101 | 0x2281ea7e, 0xcea0ece8, 0xdc61e588, |
| 102 | 0x2e5676a, 0x16821320 }; |
| 103 | |
| 104 | const uint32_t arr_afir[32] = { 0xa833dfa1, 0x255a477e, 0x3a4bb1c5, |
| 105 | 0x8f560a6c, 0x27f38903, 0x2fecec4d, |
| 106 | 0xa014c66d, 0xec289b8, 0x7e52dead, |
| 107 | 0x82e94f3c, 0xcf3e3c5c, 0x66059871, |
| 108 | 0x3f213df4, 0x25ac3959, 0xa12e9bef, |
| 109 | 0xa3ad3af, 0xbafd7fe, 0xb3cb40fd, |
| 110 | 0x5d9caa81, 0x2ed61902, 0x7cd64a0, |
| 111 | 0x4b1fa538, 0x9b5ced8c, 0x150de059, |
| 112 | 0xd2794227, 0x635e820a, 0xbb6b02cf, |
| 113 | 0xbb58176, 0x570025bb, 0xa78d9658, |
| 114 | 0x49d735df, 0xe5399d2f }; |
| 115 | |
| 116 | /* Passing the respective array values to all the AFMR and AFIR pairs. */ |
| 117 | for (int i = 0; i < 32; i++) { |
| 118 | /* For CANFD0. */ |
| 119 | qtest_writel(qts, CANFD0_BASE_ADDR + R_AFMR_REGISTER0 + 8 * i, |
| 120 | arr_afmr[i]); |
| 121 | qtest_writel(qts, CANFD0_BASE_ADDR + R_AFIR_REGISTER0 + 8 * i, |
| 122 | arr_afir[i]); |
| 123 | |
| 124 | /* For CANFD1. */ |
| 125 | qtest_writel(qts, CANFD1_BASE_ADDR + R_AFMR_REGISTER0 + 8 * i, |
| 126 | arr_afmr[i]); |
| 127 | qtest_writel(qts, CANFD1_BASE_ADDR + R_AFIR_REGISTER0 + 8 * i, |
| 128 | arr_afir[i]); |
| 129 | } |
| 130 | |
| 131 | /* Enable all the pairs from AFR register. */ |
| 132 | qtest_writel(qts, CANFD0_BASE_ADDR + R_FILTER_CONTROL_REGISTER, |
| 133 | ENABLE_ALL_FILTERS); |
| 134 | qtest_writel(qts, CANFD1_BASE_ADDR + R_FILTER_CONTROL_REGISTER, |
| 135 | ENABLE_ALL_FILTERS); |
| 136 | } |
| 137 | |
| 138 | static void configure_canfd(QTestState *qts, uint8_t mode) |
| 139 | { |
| 140 | uint32_t status = 0; |
| 141 | |
| 142 | /* Put CANFD0 and CANFD1 in config mode. */ |
| 143 | qtest_writel(qts, CANFD0_BASE_ADDR + R_SRR_OFFSET, SRR_CONFIG_MODE); |
| 144 | qtest_writel(qts, CANFD1_BASE_ADDR + R_SRR_OFFSET, SRR_CONFIG_MODE); |
| 145 | |
| 146 | /* Write mode of operation in Mode select register. */ |
| 147 | qtest_writel(qts, CANFD0_BASE_ADDR + R_MSR_OFFSET, mode); |
| 148 | qtest_writel(qts, CANFD1_BASE_ADDR + R_MSR_OFFSET, mode); |
| 149 | |
| 150 | enable_filters(qts); |
| 151 | |
| 152 | /* Check here if CANFD0 and CANFD1 are in config mode. */ |
| 153 | status = qtest_readl(qts, CANFD0_BASE_ADDR + R_SR_OFFSET); |
| 154 | status = status & STATUS_REG_MASK; |
| 155 | g_assert_cmpint(status, ==, STATUS_CONFIG_MODE); |
| 156 | |
| 157 | status = qtest_readl(qts, CANFD1_BASE_ADDR + R_SR_OFFSET); |
| 158 | status = status & STATUS_REG_MASK; |
| 159 | g_assert_cmpint(status, ==, STATUS_CONFIG_MODE); |
| 160 | |
| 161 | qtest_writel(qts, CANFD1_BASE_ADDR + R_IER_OFFSET, ENABLE_ALL_INTERRUPTS); |
| 162 | qtest_writel(qts, CANFD1_BASE_ADDR + R_IER_OFFSET, ENABLE_ALL_INTERRUPTS); |
| 163 | |
| 164 | qtest_writel(qts, CANFD0_BASE_ADDR + R_SRR_OFFSET, ENABLE_CANFD); |
| 165 | qtest_writel(qts, CANFD1_BASE_ADDR + R_SRR_OFFSET, ENABLE_CANFD); |
| 166 | } |
| 167 | |
| 168 | static void generate_random_data(uint32_t *buf_tx, bool is_canfd_frame) |
| 169 | { |
| 170 | /* Generate random TX data for CANFD frame. */ |
| 171 | if (is_canfd_frame) { |
| 172 | for (int i = 0; i < CANFD_FRAME_SIZE - 2; i++) { |
| 173 | buf_tx[2 + i] = g_random_int(); |
| 174 | } |
| 175 | } else { |
| 176 | /* Generate random TX data for CAN frame. */ |
| 177 | for (int i = 0; i < CAN_FRAME_SIZE - 2; i++) { |
| 178 | buf_tx[2 + i] = g_random_int(); |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | static void read_data(QTestState *qts, uint64_t can_base_addr, uint32_t *buf_rx, |
| 184 | uint32_t frame_size) |
| 185 | { |
| 186 | uint32_t int_status; |
| 187 | uint32_t fifo_status_reg_value; |
| 188 | /* At which RX FIFO the received data is stored. */ |
| 189 | uint8_t store_ind = 0; |
| 190 | |
| 191 | /* Read the interrupt on CANFD rx. */ |
| 192 | int_status = qtest_readl(qts, can_base_addr + R_ISR_OFFSET) & ISR_RXOK; |
| 193 | |
| 194 | g_assert_cmpint(int_status, ==, ISR_RXOK); |
| 195 | |
| 196 | /* Find the fill level and read index. */ |
| 197 | fifo_status_reg_value = qtest_readl(qts, can_base_addr + |
| 198 | RX_FIFO_STATUS_REGISTER); |
| 199 | |
| 200 | store_ind = (fifo_status_reg_value & FIFO_STATUS_READ_INDEX_MASK) + |
| 201 | ((fifo_status_reg_value & FIFO_STATUS_FILL_LEVEL_MASK) >> |
| 202 | FILL_LEVEL_SHIFT); |
| 203 | |
| 204 | g_assert_cmpint(store_ind, ==, FIRST_RX_STORE_INDEX); |
| 205 | |
| 206 | /* Read the RX register data for CANFD. */ |
| 207 | buf_rx[0] = qtest_readl(qts, can_base_addr + R_RX0_ID_OFFSET); |
| 208 | buf_rx[1] = qtest_readl(qts, can_base_addr + R_RX0_DLC_OFFSET); |
| 209 | |
| 210 | for (int i = 0; i < frame_size - 2; i++) { |
| 211 | buf_rx[i + 2] = qtest_readl(qts, |
| 212 | can_base_addr + R_RX0_DATA1_OFFSET + 4 * i); |
| 213 | } |
| 214 | |
| 215 | /* Clear the RX interrupt. */ |
| 216 | qtest_writel(qts, CANFD1_BASE_ADDR + R_ICR_OFFSET, ISR_RXOK); |
| 217 | } |
| 218 | |
| 219 | static void write_data(QTestState *qts, uint64_t can_base_addr, |
| 220 | const uint32_t *buf_tx, bool is_canfd_frame) |
| 221 | { |
| 222 | /* Write the TX register data for CANFD. */ |
| 223 | qtest_writel(qts, can_base_addr + R_TXID_OFFSET, buf_tx[0]); |
| 224 | qtest_writel(qts, can_base_addr + R_TXDLC_OFFSET, buf_tx[1]); |
| 225 | |
| 226 | if (is_canfd_frame) { |
| 227 | for (int i = 0; i < CANFD_FRAME_SIZE - 2; i++) { |
| 228 | qtest_writel(qts, can_base_addr + R_TXDATA1_OFFSET + 4 * i, |
| 229 | buf_tx[2 + i]); |
| 230 | } |
| 231 | } else { |
| 232 | qtest_writel(qts, can_base_addr + R_TXDATA1_OFFSET, buf_tx[2]); |
| 233 | qtest_writel(qts, can_base_addr + R_TXDATA2_OFFSET, buf_tx[3]); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | static void send_data(QTestState *qts, uint64_t can_base_addr) |
| 238 | { |
| 239 | uint32_t int_status; |
| 240 | |
| 241 | qtest_writel(qts, can_base_addr + R_TX_READY_REQ_REGISTER, |
| 242 | TX_READY_REG_VAL); |
| 243 | |
| 244 | /* Read the interrupt on CANFD for tx. */ |
| 245 | int_status = qtest_readl(qts, can_base_addr + R_ISR_OFFSET) & ISR_TXOK; |
| 246 | |
| 247 | g_assert_cmpint(int_status, ==, ISR_TXOK); |
| 248 | |
| 249 | /* Clear the interrupt for tx. */ |
| 250 | qtest_writel(qts, CANFD0_BASE_ADDR + R_ICR_OFFSET, ISR_TXOK); |
| 251 | } |
| 252 | |
| 253 | static void match_rx_tx_data(const uint32_t *buf_tx, const uint32_t *buf_rx, |
| 254 | bool is_canfd_frame) |
| 255 | { |
| 256 | uint16_t size = 0; |
| 257 | uint8_t len = CAN_FRAME_SIZE; |
| 258 | |
| 259 | if (is_canfd_frame) { |
| 260 | len = CANFD_FRAME_SIZE; |
| 261 | } |
| 262 | |
| 263 | while (size < len) { |
| 264 | if (R_RX0_ID_OFFSET + 4 * size == R_RX0_DLC_OFFSET) { |
| 265 | g_assert_cmpint((buf_rx[size] & DLC_FD_BIT_MASK), ==, |
| 266 | (buf_tx[size] & DLC_FD_BIT_MASK)); |
| 267 | } else { |
| 268 | g_assert_cmpint(buf_rx[size], ==, buf_tx[size]); |
| 269 | } |
| 270 | |
| 271 | size++; |
| 272 | } |
| 273 | } |
| 274 | /* |
| 275 | * Xilinx CANFD supports both CAN and CANFD frames. This test will be |
| 276 | * transferring CAN frame i.e. 8 bytes of data from CANFD0 and CANFD1 through |
| 277 | * canbus. CANFD0 initiate the data transfer to can-bus, CANFD1 receives the |
| 278 | * data. Test compares the can frame data sent from CANFD0 and received on |
| 279 | * CANFD1. |
| 280 | */ |
| 281 | static void test_can_data_transfer(void) |
| 282 | { |
| 283 | uint32_t buf_tx[CAN_FRAME_SIZE] = { 0x5a5bb9a4, 0x80000000, |
| 284 | 0x12345678, 0x87654321 }; |
| 285 | uint32_t buf_rx[CAN_FRAME_SIZE] = { 0x00, 0x00, 0x00, 0x00 }; |
| 286 | uint32_t status = 0; |
| 287 | |
| 288 | generate_random_data(buf_tx, false); |
| 289 | |
| 290 | QTestState *qts = qtest_init("-machine xlnx-versal-virt" |
| 291 | " -object can-bus,id=canbus" |
| 292 | " -machine canbus0=canbus" |
| 293 | " -machine canbus1=canbus" |
| 294 | ); |
| 295 | |
| 296 | configure_canfd(qts, MSR_NORMAL_MODE); |
| 297 | |
| 298 | /* Check if CANFD0 and CANFD1 are in Normal mode. */ |
| 299 | status = qtest_readl(qts, CANFD0_BASE_ADDR + R_SR_OFFSET); |
| 300 | status = status & STATUS_REG_MASK; |
| 301 | g_assert_cmpint(status, ==, STATUS_NORMAL_MODE); |
| 302 | |
| 303 | status = qtest_readl(qts, CANFD1_BASE_ADDR + R_SR_OFFSET); |
| 304 | status = status & STATUS_REG_MASK; |
| 305 | g_assert_cmpint(status, ==, STATUS_NORMAL_MODE); |
| 306 | |
| 307 | write_data(qts, CANFD0_BASE_ADDR, buf_tx, false); |
| 308 | |
| 309 | send_data(qts, CANFD0_BASE_ADDR); |
| 310 | read_data(qts, CANFD1_BASE_ADDR, buf_rx, CAN_FRAME_SIZE); |
| 311 | match_rx_tx_data(buf_tx, buf_rx, false); |
| 312 | |
| 313 | qtest_quit(qts); |
| 314 | } |
| 315 | |
| 316 | /* |
| 317 | * This test will be transferring CANFD frame i.e. 64 bytes of data from CANFD0 |
| 318 | * and CANFD1 through canbus. CANFD0 initiate the data transfer to can-bus, |
| 319 | * CANFD1 receives the data. Test compares the CANFD frame data sent from CANFD0 |
| 320 | * with received on CANFD1. |
| 321 | */ |
| 322 | static void test_canfd_data_transfer(void) |
| 323 | { |
| 324 | uint32_t buf_tx[CANFD_FRAME_SIZE] = { 0x5a5bb9a4, 0xf8000000 }; |
| 325 | uint32_t buf_rx[CANFD_FRAME_SIZE] = { 0x00, 0x00, 0x00, 0x00 }; |
| 326 | uint32_t status = 0; |
| 327 | |
| 328 | generate_random_data(buf_tx, true); |
| 329 | |
| 330 | QTestState *qts = qtest_init("-machine xlnx-versal-virt" |
| 331 | " -object can-bus,id=canbus" |
| 332 | " -machine canbus0=canbus" |
| 333 | " -machine canbus1=canbus" |
| 334 | ); |
| 335 | |
| 336 | configure_canfd(qts, MSR_NORMAL_MODE); |
| 337 | |
| 338 | /* Check if CANFD0 and CANFD1 are in Normal mode. */ |
| 339 | status = qtest_readl(qts, CANFD0_BASE_ADDR + R_SR_OFFSET); |
| 340 | status = status & STATUS_REG_MASK; |
| 341 | g_assert_cmpint(status, ==, STATUS_NORMAL_MODE); |
| 342 | |
| 343 | status = qtest_readl(qts, CANFD1_BASE_ADDR + R_SR_OFFSET); |
| 344 | status = status & STATUS_REG_MASK; |
| 345 | g_assert_cmpint(status, ==, STATUS_NORMAL_MODE); |
| 346 | |
| 347 | write_data(qts, CANFD0_BASE_ADDR, buf_tx, true); |
| 348 | |
| 349 | send_data(qts, CANFD0_BASE_ADDR); |
| 350 | read_data(qts, CANFD1_BASE_ADDR, buf_rx, CANFD_FRAME_SIZE); |
| 351 | match_rx_tx_data(buf_tx, buf_rx, true); |
| 352 | |
| 353 | qtest_quit(qts); |
| 354 | } |
| 355 | |
| 356 | /* |
| 357 | * This test is performing loopback mode on CANFD0 and CANFD1. Data sent from |
| 358 | * TX of each CANFD0 and CANFD1 are compared with RX register data for |
| 359 | * respective CANFD Controller. |
| 360 | */ |
| 361 | static void test_can_loopback(void) |
| 362 | { |
| 363 | uint32_t buf_tx[CANFD_FRAME_SIZE] = { 0x5a5bb9a4, 0xf8000000 }; |
| 364 | uint32_t buf_rx[CANFD_FRAME_SIZE] = { 0x00, 0x00, 0x00, 0x00 }; |
| 365 | uint32_t status = 0; |
| 366 | |
| 367 | generate_random_data(buf_tx, true); |
| 368 | |
| 369 | QTestState *qts = qtest_init("-machine xlnx-versal-virt" |
| 370 | " -object can-bus,id=canbus" |
| 371 | " -machine canbus0=canbus" |
| 372 | " -machine canbus1=canbus" |
| 373 | ); |
| 374 | |
| 375 | configure_canfd(qts, MSR_LOOPBACK_MODE); |
| 376 | |
| 377 | /* Check if CANFD0 and CANFD1 are set in correct loopback mode. */ |
| 378 | status = qtest_readl(qts, CANFD0_BASE_ADDR + R_SR_OFFSET); |
| 379 | status = status & STATUS_REG_MASK; |
| 380 | g_assert_cmpint(status, ==, STATUS_LOOPBACK_MODE); |
| 381 | |
| 382 | status = qtest_readl(qts, CANFD1_BASE_ADDR + R_SR_OFFSET); |
| 383 | status = status & STATUS_REG_MASK; |
| 384 | g_assert_cmpint(status, ==, STATUS_LOOPBACK_MODE); |
| 385 | |
| 386 | write_data(qts, CANFD0_BASE_ADDR, buf_tx, true); |
| 387 | |
| 388 | send_data(qts, CANFD0_BASE_ADDR); |
| 389 | read_data(qts, CANFD0_BASE_ADDR, buf_rx, CANFD_FRAME_SIZE); |
| 390 | match_rx_tx_data(buf_tx, buf_rx, true); |
| 391 | |
| 392 | generate_random_data(buf_tx, true); |
| 393 | |
| 394 | write_data(qts, CANFD1_BASE_ADDR, buf_tx, true); |
| 395 | |
| 396 | send_data(qts, CANFD1_BASE_ADDR); |
| 397 | read_data(qts, CANFD1_BASE_ADDR, buf_rx, CANFD_FRAME_SIZE); |
| 398 | match_rx_tx_data(buf_tx, buf_rx, true); |
| 399 | |
| 400 | qtest_quit(qts); |
| 401 | } |
| 402 | |
| 403 | int main(int argc, char **argv) |
| 404 | { |
| 405 | g_test_init(&argc, &argv, NULL); |
| 406 | |
| 407 | qtest_add_func("/net/canfd/can_data_transfer", test_can_data_transfer); |
| 408 | qtest_add_func("/net/canfd/canfd_data_transfer", test_canfd_data_transfer); |
| 409 | qtest_add_func("/net/canfd/can_loopback", test_can_loopback); |
| 410 | |
| 411 | return g_test_run(); |
| 412 | } |