qemu/tests/qtest/bcm2835-dma-test.c
<<
>>
Prefs
   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 Trasfer 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
  44const uint32_t check_data = 0x12345678;
  45
  46static 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
  99static 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
 108int 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