2022-07-16 13:32:10 +02:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
|
2023-07-14 13:33:27 +02:00
|
|
|
/* DMA Transfer Info fields: */
|
2022-07-16 13:32:10 +02:00
|
|
|
#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;
|
|
|
|
}
|