Andrey Makarov | 004c8a8 | 2022-07-16 14:32:10 +0300 | [diff] [blame] | 1 | /* |
| 2 | * QTest testcase for BCM283x DMA engine (on Raspberry Pi 3) |
| 3 | * and its interrupts coming to Interrupt Controller. |
| 4 | * |
| 5 | * Copyright (c) 2022 Auriga LLC |
| 6 | * |
| 7 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 8 | */ |
| 9 | |
| 10 | #include "qemu/osdep.h" |
| 11 | #include "libqtest-single.h" |
| 12 | |
| 13 | /* Offsets in raspi3b platform: */ |
| 14 | #define RASPI3_DMA_BASE 0x3f007000 |
| 15 | #define RASPI3_IC_BASE 0x3f00b200 |
| 16 | |
| 17 | /* Used register/fields definitions */ |
| 18 | |
| 19 | /* DMA engine registers: */ |
| 20 | #define BCM2708_DMA_CS 0 |
| 21 | #define BCM2708_DMA_ACTIVE (1 << 0) |
| 22 | #define BCM2708_DMA_INT (1 << 2) |
| 23 | |
| 24 | #define BCM2708_DMA_ADDR 0x04 |
| 25 | |
| 26 | #define BCM2708_DMA_INT_STATUS 0xfe0 |
| 27 | |
| 28 | /* DMA Trasfer Info fields: */ |
| 29 | #define BCM2708_DMA_INT_EN (1 << 0) |
| 30 | #define BCM2708_DMA_D_INC (1 << 4) |
| 31 | #define BCM2708_DMA_S_INC (1 << 8) |
| 32 | |
| 33 | /* Interrupt controller registers: */ |
| 34 | #define IRQ_PENDING_BASIC 0x00 |
| 35 | #define IRQ_GPU_PENDING1_AGGR (1 << 8) |
| 36 | #define IRQ_PENDING_1 0x04 |
| 37 | #define IRQ_ENABLE_1 0x10 |
| 38 | |
| 39 | /* Data for the test: */ |
| 40 | #define SCB_ADDR 256 |
| 41 | #define S_ADDR 32 |
| 42 | #define D_ADDR 64 |
| 43 | #define TXFR_LEN 32 |
| 44 | const uint32_t check_data = 0x12345678; |
| 45 | |
| 46 | static void bcm2835_dma_test_interrupt(int dma_c, int irq_line) |
| 47 | { |
| 48 | uint64_t dma_base = RASPI3_DMA_BASE + dma_c * 0x100; |
| 49 | int gpu_irq_line = 16 + irq_line; |
| 50 | |
| 51 | /* Check that interrupts are silent by default: */ |
| 52 | writel(RASPI3_IC_BASE + IRQ_ENABLE_1, 1 << gpu_irq_line); |
| 53 | int isr = readl(dma_base + BCM2708_DMA_INT_STATUS); |
| 54 | g_assert_cmpint(isr, ==, 0); |
| 55 | uint32_t reg0 = readl(dma_base + BCM2708_DMA_CS); |
| 56 | g_assert_cmpint(reg0, ==, 0); |
| 57 | uint32_t ic_pending = readl(RASPI3_IC_BASE + IRQ_PENDING_BASIC); |
| 58 | g_assert_cmpint(ic_pending, ==, 0); |
| 59 | uint32_t gpu_pending1 = readl(RASPI3_IC_BASE + IRQ_PENDING_1); |
| 60 | g_assert_cmpint(gpu_pending1, ==, 0); |
| 61 | |
| 62 | /* Prepare Control Block: */ |
| 63 | writel(SCB_ADDR + 0, BCM2708_DMA_S_INC | BCM2708_DMA_D_INC | |
| 64 | BCM2708_DMA_INT_EN); /* transfer info */ |
| 65 | writel(SCB_ADDR + 4, S_ADDR); /* source address */ |
| 66 | writel(SCB_ADDR + 8, D_ADDR); /* destination address */ |
| 67 | writel(SCB_ADDR + 12, TXFR_LEN); /* transfer length */ |
| 68 | writel(dma_base + BCM2708_DMA_ADDR, SCB_ADDR); |
| 69 | |
| 70 | writel(S_ADDR, check_data); |
| 71 | for (int word = S_ADDR + 4; word < S_ADDR + TXFR_LEN; word += 4) { |
| 72 | writel(word, ~check_data); |
| 73 | } |
| 74 | /* Perform the transfer: */ |
| 75 | writel(dma_base + BCM2708_DMA_CS, BCM2708_DMA_ACTIVE); |
| 76 | |
| 77 | /* Check that destination == source: */ |
| 78 | uint32_t data = readl(D_ADDR); |
| 79 | g_assert_cmpint(data, ==, check_data); |
| 80 | for (int word = D_ADDR + 4; word < D_ADDR + TXFR_LEN; word += 4) { |
| 81 | data = readl(word); |
| 82 | g_assert_cmpint(data, ==, ~check_data); |
| 83 | } |
| 84 | |
| 85 | /* Check that interrupt status is set both in DMA and IC controllers: */ |
| 86 | isr = readl(RASPI3_DMA_BASE + BCM2708_DMA_INT_STATUS); |
| 87 | g_assert_cmpint(isr, ==, 1 << dma_c); |
| 88 | |
| 89 | ic_pending = readl(RASPI3_IC_BASE + IRQ_PENDING_BASIC); |
| 90 | g_assert_cmpint(ic_pending, ==, IRQ_GPU_PENDING1_AGGR); |
| 91 | |
| 92 | gpu_pending1 = readl(RASPI3_IC_BASE + IRQ_PENDING_1); |
| 93 | g_assert_cmpint(gpu_pending1, ==, 1 << gpu_irq_line); |
| 94 | |
| 95 | /* Clean up, clear interrupt: */ |
| 96 | writel(dma_base + BCM2708_DMA_CS, BCM2708_DMA_INT); |
| 97 | } |
| 98 | |
| 99 | static void bcm2835_dma_test_interrupts(void) |
| 100 | { |
| 101 | /* DMA engines 0--10 have separate IRQ lines, 11--14 - only one: */ |
| 102 | bcm2835_dma_test_interrupt(0, 0); |
| 103 | bcm2835_dma_test_interrupt(10, 10); |
| 104 | bcm2835_dma_test_interrupt(11, 11); |
| 105 | bcm2835_dma_test_interrupt(14, 11); |
| 106 | } |
| 107 | |
| 108 | int main(int argc, char **argv) |
| 109 | { |
| 110 | int ret; |
| 111 | g_test_init(&argc, &argv, NULL); |
| 112 | qtest_add_func("/bcm2835/dma/test_interrupts", |
| 113 | bcm2835_dma_test_interrupts); |
| 114 | qtest_start("-machine raspi3b"); |
| 115 | ret = g_test_run(); |
| 116 | qtest_end(); |
| 117 | return ret; |
| 118 | } |