xref: /qemu/tests/qtest/bcm2835-dma-test.c (revision 940bb5fa9ca9f71fcc0d06e9de9ac3ab7415d0f2)
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 Transfer 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  }
119