/* * QTest testcase for BCM283x DMA engine (on Raspberry Pi 3) * and its interrupts coming to Interrupt Controller. * * Copyright (c) 2022 Auriga LLC * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" #include "libqtest-single.h" /* Offsets in raspi3b platform: */ #define RASPI3_DMA_BASE 0x3f007000 #define RASPI3_IC_BASE 0x3f00b200 /* Used register/fields definitions */ /* DMA engine registers: */ #define BCM2708_DMA_CS 0 #define BCM2708_DMA_ACTIVE (1 << 0) #define BCM2708_DMA_INT (1 << 2) #define BCM2708_DMA_ADDR 0x04 #define BCM2708_DMA_INT_STATUS 0xfe0 /* DMA Trasfer Info fields: */ #define BCM2708_DMA_INT_EN (1 << 0) #define BCM2708_DMA_D_INC (1 << 4) #define BCM2708_DMA_S_INC (1 << 8) /* Interrupt controller registers: */ #define IRQ_PENDING_BASIC 0x00 #define IRQ_GPU_PENDING1_AGGR (1 << 8) #define IRQ_PENDING_1 0x04 #define IRQ_ENABLE_1 0x10 /* Data for the test: */ #define SCB_ADDR 256 #define S_ADDR 32 #define D_ADDR 64 #define TXFR_LEN 32 const uint32_t check_data = 0x12345678; static void bcm2835_dma_test_interrupt(int dma_c, int irq_line) { uint64_t dma_base = RASPI3_DMA_BASE + dma_c * 0x100; int gpu_irq_line = 16 + irq_line; /* Check that interrupts are silent by default: */ writel(RASPI3_IC_BASE + IRQ_ENABLE_1, 1 << gpu_irq_line); int isr = readl(dma_base + BCM2708_DMA_INT_STATUS); g_assert_cmpint(isr, ==, 0); uint32_t reg0 = readl(dma_base + BCM2708_DMA_CS); g_assert_cmpint(reg0, ==, 0); uint32_t ic_pending = readl(RASPI3_IC_BASE + IRQ_PENDING_BASIC); g_assert_cmpint(ic_pending, ==, 0); uint32_t gpu_pending1 = readl(RASPI3_IC_BASE + IRQ_PENDING_1); g_assert_cmpint(gpu_pending1, ==, 0); /* Prepare Control Block: */ writel(SCB_ADDR + 0, BCM2708_DMA_S_INC | BCM2708_DMA_D_INC | BCM2708_DMA_INT_EN); /* transfer info */ writel(SCB_ADDR + 4, S_ADDR); /* source address */ writel(SCB_ADDR + 8, D_ADDR); /* destination address */ writel(SCB_ADDR + 12, TXFR_LEN); /* transfer length */ writel(dma_base + BCM2708_DMA_ADDR, SCB_ADDR); writel(S_ADDR, check_data); for (int word = S_ADDR + 4; word < S_ADDR + TXFR_LEN; word += 4) { writel(word, ~check_data); } /* Perform the transfer: */ writel(dma_base + BCM2708_DMA_CS, BCM2708_DMA_ACTIVE); /* Check that destination == source: */ uint32_t data = readl(D_ADDR); g_assert_cmpint(data, ==, check_data); for (int word = D_ADDR + 4; word < D_ADDR + TXFR_LEN; word += 4) { data = readl(word); g_assert_cmpint(data, ==, ~check_data); } /* Check that interrupt status is set both in DMA and IC controllers: */ isr = readl(RASPI3_DMA_BASE + BCM2708_DMA_INT_STATUS); g_assert_cmpint(isr, ==, 1 << dma_c); ic_pending = readl(RASPI3_IC_BASE + IRQ_PENDING_BASIC); g_assert_cmpint(ic_pending, ==, IRQ_GPU_PENDING1_AGGR); gpu_pending1 = readl(RASPI3_IC_BASE + IRQ_PENDING_1); g_assert_cmpint(gpu_pending1, ==, 1 << gpu_irq_line); /* Clean up, clear interrupt: */ writel(dma_base + BCM2708_DMA_CS, BCM2708_DMA_INT); } static void bcm2835_dma_test_interrupts(void) { /* DMA engines 0--10 have separate IRQ lines, 11--14 - only one: */ bcm2835_dma_test_interrupt(0, 0); bcm2835_dma_test_interrupt(10, 10); bcm2835_dma_test_interrupt(11, 11); bcm2835_dma_test_interrupt(14, 11); } int main(int argc, char **argv) { int ret; g_test_init(&argc, &argv, NULL); qtest_add_func("/bcm2835/dma/test_interrupts", bcm2835_dma_test_interrupts); qtest_start("-machine raspi3b"); ret = g_test_run(); qtest_end(); return ret; }