1004c8a8bSAndrey Makarov /* 2004c8a8bSAndrey Makarov * QTest testcase for BCM283x DMA engine (on Raspberry Pi 3) 3004c8a8bSAndrey Makarov * and its interrupts coming to Interrupt Controller. 4004c8a8bSAndrey Makarov * 5004c8a8bSAndrey Makarov * Copyright (c) 2022 Auriga LLC 6004c8a8bSAndrey Makarov * 7004c8a8bSAndrey Makarov * SPDX-License-Identifier: GPL-2.0-or-later 8004c8a8bSAndrey Makarov */ 9004c8a8bSAndrey Makarov 10004c8a8bSAndrey Makarov #include "qemu/osdep.h" 11004c8a8bSAndrey Makarov #include "libqtest-single.h" 12004c8a8bSAndrey Makarov 13004c8a8bSAndrey Makarov /* Offsets in raspi3b platform: */ 14004c8a8bSAndrey Makarov #define RASPI3_DMA_BASE 0x3f007000 15004c8a8bSAndrey Makarov #define RASPI3_IC_BASE 0x3f00b200 16004c8a8bSAndrey Makarov 17004c8a8bSAndrey Makarov /* Used register/fields definitions */ 18004c8a8bSAndrey Makarov 19004c8a8bSAndrey Makarov /* DMA engine registers: */ 20004c8a8bSAndrey Makarov #define BCM2708_DMA_CS 0 21004c8a8bSAndrey Makarov #define BCM2708_DMA_ACTIVE (1 << 0) 22004c8a8bSAndrey Makarov #define BCM2708_DMA_INT (1 << 2) 23004c8a8bSAndrey Makarov 24004c8a8bSAndrey Makarov #define BCM2708_DMA_ADDR 0x04 25004c8a8bSAndrey Makarov 26004c8a8bSAndrey Makarov #define BCM2708_DMA_INT_STATUS 0xfe0 27004c8a8bSAndrey Makarov 28*96420a30SMichael Tokarev /* DMA Transfer Info fields: */ 29004c8a8bSAndrey Makarov #define BCM2708_DMA_INT_EN (1 << 0) 30004c8a8bSAndrey Makarov #define BCM2708_DMA_D_INC (1 << 4) 31004c8a8bSAndrey Makarov #define BCM2708_DMA_S_INC (1 << 8) 32004c8a8bSAndrey Makarov 33004c8a8bSAndrey Makarov /* Interrupt controller registers: */ 34004c8a8bSAndrey Makarov #define IRQ_PENDING_BASIC 0x00 35004c8a8bSAndrey Makarov #define IRQ_GPU_PENDING1_AGGR (1 << 8) 36004c8a8bSAndrey Makarov #define IRQ_PENDING_1 0x04 37004c8a8bSAndrey Makarov #define IRQ_ENABLE_1 0x10 38004c8a8bSAndrey Makarov 39004c8a8bSAndrey Makarov /* Data for the test: */ 40004c8a8bSAndrey Makarov #define SCB_ADDR 256 41004c8a8bSAndrey Makarov #define S_ADDR 32 42004c8a8bSAndrey Makarov #define D_ADDR 64 43004c8a8bSAndrey Makarov #define TXFR_LEN 32 44004c8a8bSAndrey Makarov const uint32_t check_data = 0x12345678; 45004c8a8bSAndrey Makarov 46004c8a8bSAndrey Makarov static void bcm2835_dma_test_interrupt(int dma_c, int irq_line) 47004c8a8bSAndrey Makarov { 48004c8a8bSAndrey Makarov uint64_t dma_base = RASPI3_DMA_BASE + dma_c * 0x100; 49004c8a8bSAndrey Makarov int gpu_irq_line = 16 + irq_line; 50004c8a8bSAndrey Makarov 51004c8a8bSAndrey Makarov /* Check that interrupts are silent by default: */ 52004c8a8bSAndrey Makarov writel(RASPI3_IC_BASE + IRQ_ENABLE_1, 1 << gpu_irq_line); 53004c8a8bSAndrey Makarov int isr = readl(dma_base + BCM2708_DMA_INT_STATUS); 54004c8a8bSAndrey Makarov g_assert_cmpint(isr, ==, 0); 55004c8a8bSAndrey Makarov uint32_t reg0 = readl(dma_base + BCM2708_DMA_CS); 56004c8a8bSAndrey Makarov g_assert_cmpint(reg0, ==, 0); 57004c8a8bSAndrey Makarov uint32_t ic_pending = readl(RASPI3_IC_BASE + IRQ_PENDING_BASIC); 58004c8a8bSAndrey Makarov g_assert_cmpint(ic_pending, ==, 0); 59004c8a8bSAndrey Makarov uint32_t gpu_pending1 = readl(RASPI3_IC_BASE + IRQ_PENDING_1); 60004c8a8bSAndrey Makarov g_assert_cmpint(gpu_pending1, ==, 0); 61004c8a8bSAndrey Makarov 62004c8a8bSAndrey Makarov /* Prepare Control Block: */ 63004c8a8bSAndrey Makarov writel(SCB_ADDR + 0, BCM2708_DMA_S_INC | BCM2708_DMA_D_INC | 64004c8a8bSAndrey Makarov BCM2708_DMA_INT_EN); /* transfer info */ 65004c8a8bSAndrey Makarov writel(SCB_ADDR + 4, S_ADDR); /* source address */ 66004c8a8bSAndrey Makarov writel(SCB_ADDR + 8, D_ADDR); /* destination address */ 67004c8a8bSAndrey Makarov writel(SCB_ADDR + 12, TXFR_LEN); /* transfer length */ 68004c8a8bSAndrey Makarov writel(dma_base + BCM2708_DMA_ADDR, SCB_ADDR); 69004c8a8bSAndrey Makarov 70004c8a8bSAndrey Makarov writel(S_ADDR, check_data); 71004c8a8bSAndrey Makarov for (int word = S_ADDR + 4; word < S_ADDR + TXFR_LEN; word += 4) { 72004c8a8bSAndrey Makarov writel(word, ~check_data); 73004c8a8bSAndrey Makarov } 74004c8a8bSAndrey Makarov /* Perform the transfer: */ 75004c8a8bSAndrey Makarov writel(dma_base + BCM2708_DMA_CS, BCM2708_DMA_ACTIVE); 76004c8a8bSAndrey Makarov 77004c8a8bSAndrey Makarov /* Check that destination == source: */ 78004c8a8bSAndrey Makarov uint32_t data = readl(D_ADDR); 79004c8a8bSAndrey Makarov g_assert_cmpint(data, ==, check_data); 80004c8a8bSAndrey Makarov for (int word = D_ADDR + 4; word < D_ADDR + TXFR_LEN; word += 4) { 81004c8a8bSAndrey Makarov data = readl(word); 82004c8a8bSAndrey Makarov g_assert_cmpint(data, ==, ~check_data); 83004c8a8bSAndrey Makarov } 84004c8a8bSAndrey Makarov 85004c8a8bSAndrey Makarov /* Check that interrupt status is set both in DMA and IC controllers: */ 86004c8a8bSAndrey Makarov isr = readl(RASPI3_DMA_BASE + BCM2708_DMA_INT_STATUS); 87004c8a8bSAndrey Makarov g_assert_cmpint(isr, ==, 1 << dma_c); 88004c8a8bSAndrey Makarov 89004c8a8bSAndrey Makarov ic_pending = readl(RASPI3_IC_BASE + IRQ_PENDING_BASIC); 90004c8a8bSAndrey Makarov g_assert_cmpint(ic_pending, ==, IRQ_GPU_PENDING1_AGGR); 91004c8a8bSAndrey Makarov 92004c8a8bSAndrey Makarov gpu_pending1 = readl(RASPI3_IC_BASE + IRQ_PENDING_1); 93004c8a8bSAndrey Makarov g_assert_cmpint(gpu_pending1, ==, 1 << gpu_irq_line); 94004c8a8bSAndrey Makarov 95004c8a8bSAndrey Makarov /* Clean up, clear interrupt: */ 96004c8a8bSAndrey Makarov writel(dma_base + BCM2708_DMA_CS, BCM2708_DMA_INT); 97004c8a8bSAndrey Makarov } 98004c8a8bSAndrey Makarov 99004c8a8bSAndrey Makarov static void bcm2835_dma_test_interrupts(void) 100004c8a8bSAndrey Makarov { 101004c8a8bSAndrey Makarov /* DMA engines 0--10 have separate IRQ lines, 11--14 - only one: */ 102004c8a8bSAndrey Makarov bcm2835_dma_test_interrupt(0, 0); 103004c8a8bSAndrey Makarov bcm2835_dma_test_interrupt(10, 10); 104004c8a8bSAndrey Makarov bcm2835_dma_test_interrupt(11, 11); 105004c8a8bSAndrey Makarov bcm2835_dma_test_interrupt(14, 11); 106004c8a8bSAndrey Makarov } 107004c8a8bSAndrey Makarov 108004c8a8bSAndrey Makarov int main(int argc, char **argv) 109004c8a8bSAndrey Makarov { 110004c8a8bSAndrey Makarov int ret; 111004c8a8bSAndrey Makarov g_test_init(&argc, &argv, NULL); 112004c8a8bSAndrey Makarov qtest_add_func("/bcm2835/dma/test_interrupts", 113004c8a8bSAndrey Makarov bcm2835_dma_test_interrupts); 114004c8a8bSAndrey Makarov qtest_start("-machine raspi3b"); 115004c8a8bSAndrey Makarov ret = g_test_run(); 116004c8a8bSAndrey Makarov qtest_end(); 117004c8a8bSAndrey Makarov return ret; 118004c8a8bSAndrey Makarov } 119