summaryrefslogtreecommitdiff
path: root/tests/qtest
diff options
context:
space:
mode:
authorAndrey Makarov <ph.makarov@gmail.com>2022-07-16 14:32:10 +0300
committerPeter Maydell <peter.maydell@linaro.org>2022-07-18 13:25:13 +0100
commit004c8a8bc569c8b18fca6fc90ffe3223daaf17b7 (patch)
treefb0987e16733a9f93cf7561b9d30c27699096181 /tests/qtest
parent53ae2fdef1f5661cbaa2ea571c517f98e6041cb8 (diff)
Align Raspberry Pi DMA interrupts with Linux DTS
There is nothing in the specs on DMA engine interrupt lines: it should have been in the "BCM2835 ARM Peripherals" datasheet but the appropriate "ARM peripherals interrupt table" (p.113) is nearly empty. All Raspberry Pi models 1-3 (based on bcm2835) have Linux device tree (arch/arm/boot/dts/bcm2835-common.dtsi +25): /* dma channel 11-14 share one irq */ This information is repeated in the driver code (drivers/dma/bcm2835-dma.c +1344): /* * in case of channel >= 11 * use the 11th interrupt and that is shared */ In this patch channels 0--10 and 11--14 are handled separately. Signed-off-by: Andrey Makarov <andrey.makarov@auriga.com> Message-id: 20220716113210.349153-1-andrey.makarov@auriga.com [PMM: fixed checkpatch nits] Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'tests/qtest')
-rw-r--r--tests/qtest/bcm2835-dma-test.c118
-rw-r--r--tests/qtest/meson.build3
2 files changed, 120 insertions, 1 deletions
diff --git a/tests/qtest/bcm2835-dma-test.c b/tests/qtest/bcm2835-dma-test.c
new file mode 100644
index 0000000000..8293d822b9
--- /dev/null
+++ b/tests/qtest/bcm2835-dma-test.c
@@ -0,0 +1,118 @@
+/*
+ * 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;
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 31287a9173..3a474010e4 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -218,7 +218,8 @@ qtests_aarch64 = \
['arm-cpu-features',
'numa-test',
'boot-serial-test',
- 'migration-test']
+ 'migration-test',
+ 'bcm2835-dma-test']
qtests_s390x = \
(slirp.found() ? ['pxe-test', 'test-netfilter'] : []) + \