// Copyright 2016 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

// Unlike the bcm283x, the allwinner CPUs do not have a "clear bit" and "set
// bit" registers, they only have the data register. Also, allwinner CPUs do
// not support linked lists of DMA buffers. On the other hand, the Allwinner DMA
// controller supports 8 bits transfers instead of 32-128 bits that the bcm283x
// DMA controller supports.
//
// This means that only 8 bits can be used per sample, and only one stream is
// necessary. This results in 1/8th th memory usage than on the bcm283x. The
// drawback is that a block of 8 contiguous GPIO pins must be dedicated to the
// stream.

package allwinner

import (
	"errors"
	"fmt"
	"log"
	"os"

	"periph.io/x/conn/v3/driver/driverreg"
	"periph.io/x/host/v3/pmem"
)

// dmaMap represents the DMA memory mapped CPU registers.
//
// This map is specific to the currently supported CPUs and will have to be
// adapted as more CPUs are supported. In particular the number of physical
// channels varies across different CPUs.
//
// Note that we modify the DMA controllers without telling the kernel driver.
// The driver keeps its own table of which DMA channel is available so this
// code could effectively crash the whole system. It practice this works.
// #everythingisfine
type dmaMap struct {
	irqEn       dmaR8Irq                // DMA_IRQ_EN_REG
	irqPendStas dmaR8PendingIrq         // DMA_IRQ_PEND_STAS_REG
	reserved0   [(0x100 - 8) / 4]uint32 //
	normal      [8]dmaR8NormalGroup     // 0x100 The "8" "normal" DMA channels (only one active at a time so there's effectively one)
	reserved1   [0x100 / 4]uint32       //
	dedicated   [8]dmaDedicatedGroup    // 0x300 The 8 "dedicated" (as in actually existing) DMA channels
}

func (d *dmaMap) getDedicated() int {
	for i := len(d.dedicated) - 1; i >= 0; i-- {
		if d.dedicated[i].isAvailable() {
			return i
		}
	}
	return -1
}

// dmaNormalGroup is the control registers for the first block of 8 DMA
// controllers.
//
// They can be intentionally slowed down, unlike the dedicated DMA ones.
//
// The big caveat is that only one controller can be active at a time and the
// execution sequence is in accordance with the priority level. This means that
// two normal DMA cannot be used to do simultaneous read and write. This
// feature is critical for bus bitbanging.
type dmaR8NormalGroup struct {
	cfg         ndmaR8Cfg // NDMA_CTRL_REG
	srcAddr     uint32    // NDMA_SRC_ADDR_REG
	dstAddr     uint32    // NDMA_DEST_ADDR_REG
	byteCounter uint32    // NDMA_BC_REG
	reserved    [4]uint32 //
}

func (d *dmaR8NormalGroup) isAvailable() bool {
	return d.cfg == 0 && d.srcAddr == 0 && d.dstAddr == 0 && d.byteCounter == 0
}

func (d *dmaR8NormalGroup) release() error {
	d.srcAddr = 0
	d.dstAddr = 0
	d.byteCounter = 0
	d.cfg = ndmaLoad
	//drvDMA.dmaMemory.irqEn &^= ...
	//drvDMA.dmaMemory.irqPendStas &^= ...
	return nil
}

// dmaNormalGroup is the control registers for the second block of 8 DMA
// controllers.
//
// They support different DReq and can do non-linear streaming.
type dmaDedicatedGroup struct {
	cfg         ddmaR8Cfg   // DDMA_CTRL_REG
	srcAddr     uint32      // DDMA_SRC_ADDR_REG
	dstAddr     uint32      // DDMA_DEST_ADDR_REG
	byteCounter uint32      // DDMA_BC_REG (24 bits)
	reserved0   [2]uint32   //
	param       ddmaR8Param // DDMA_PARA_REG (dedicated DMA only)
	reserved1   uint32      //
}

func (d *dmaDedicatedGroup) isAvailable() bool {
	return d.cfg == 0 && d.srcAddr == 0 && d.dstAddr == 0 && d.byteCounter == 0 && d.param == 0
}

func (d *dmaDedicatedGroup) set(srcAddr, dstAddr, l uint32, srcIO, dstIO bool, src ddmaR8Cfg) {
	d.srcAddr = srcAddr
	d.dstAddr = dstAddr
	d.byteCounter = l
	// TODO(maruel): Slow down the clock by another 2*250x
	//d.param = ddmaR8Param(250 | 250<<16)
	d.param = ddmaR8Param(1<<24 | 1<<8 | 1)
	// All these have value 0. This statement only exist for documentation.
	cfg := ddmaDstWidth8 | ddmaDstBurst1 | ddmaDstLinear | ddmaSrcWidth8 | ddmaSrcLinear | ddmaSrcBurst1
	cfg |= src | ddmaBCRemain
	if srcIO {
		cfg |= ddmaSrcIOMode
	} else if dstIO {
		cfg |= ddmaDstIOMode
	}
	d.cfg = ddmaLoad | cfg
	for i := 0; d.cfg&ddmaLoad != 0 && i < 100000; i++ {
	}
	if d.cfg&ddmaLoad != 0 {
		log.Printf("failed to load DDMA: %# v\n", d)
	}
}

func (d *dmaDedicatedGroup) release() error {
	d.param = 0
	d.srcAddr = 0
	d.dstAddr = 0
	d.byteCounter = 0
	d.cfg = ddmaLoad
	//drvDMA.dmaMemory.irqEn &^= ...
	//drvDMA.dmaMemory.irqPendStas &^= ...
	return nil
}

const (
	// 31 reserved
	dma7QueueEndIrq   dmaA64Irq = 1 << 30 // DMA7_END_IRQ_EN; DMA 7 Queue End Transfer Interrupt Enable.
	dma7PackageEndIrq dmaA64Irq = 1 << 29 // DMA7_PKG_IRQ_EN; DMA 7 Package End Transfer Interrupt Enable.
	dma7HalfIrq       dmaA64Irq = 1 << 28 // DMA7_HLAF_IRQ_EN; DMA 7 Half Package Transfer Interrupt Enable.
	// ...
	// 3 reserved
	dma0QueueEndIrq   dmaA64Irq = 1 << 2 // DMA0_END_IRQ_EN; DMA 0 Queue End Transfer Interrupt Enable.
	dma0PackageEndIrq dmaA64Irq = 1 << 1 // DMA0_PKG_IRQ_EN; DMA 0 Package End Transfer Interrupt Enable.
	dma0HalfIrq       dmaA64Irq = 1 << 0 // DMA0_HLAF_IRQ_EN; DMA 0 Half Package Transfer Interrupt Enable.
)

// DMA_IRQ_EN_REG
// A64: Page 199-201.
type dmaA64Irq uint32

const (
	ddma7EndIrq   dmaR8Irq = 1 << 31 // DDMA7_END_IRQ_EN
	ddma7HalfIreq dmaR8Irq = 1 << 30 // DDMA7_HF_IRQ_EN
	// ...
	ddma0EndIrq   dmaR8Irq = 1 << 17 // DDMA0_END_IRQ_EN
	ddma0HalfIreq dmaR8Irq = 1 << 16 // DDMA0_HF_IRQ_EN
	ndma7EndIrq   dmaR8Irq = 1 << 15 // NDMA7_END_IRQ_EN
	ndma7HalfIreq dmaR8Irq = 1 << 16 // NDDMA7_HF_IRQ_EN
	// ...
	ndma0EndIrq dmaR8Irq = 1 << 1 // NDMA0_END_IRQ_EN
	ndma0HFIreq dmaR8Irq = 1 << 0 // NDMA0_HF_IRQ_EN
)

// DMA_IRQ_EN_REG
// R8: Page 124-126.
type dmaR8Irq uint32

const (
	// 31 reserved
	dma7QueueEndIrqPend   dmaA64PendingIrq = 1 << 30 // DMA7_QUEUE_IRQ_PEND; DMA 7 Queue End Transfer Interrupt Pending. Set 1 to the bit will clear it.
	dma7PackageEndIrqPend dmaA64PendingIrq = 1 << 29 // DMA7_PKG_IRQ_PEND; DMA 7 Package End Transfer Interrupt Pending. Set 1 to the bit will clear it.
	dma7HalfIrqPend       dmaA64PendingIrq = 1 << 28 // DMA7_HLAF_IRQ_PEND; DMA 7 Half Package Transfer Interrupt Pending. Set 1 to the bit will clear it.
	// ...
	// 3 reserved
	dma0QueueEndIrqPend   dmaA64PendingIrq = 1 << 2 // DMA0_QUEUE_IRQ_PEND; DMA 0 Queue End Transfer Interrupt Pending. Set 1 to the bit will clear it.
	dma0PackageEndIrqPend dmaA64PendingIrq = 1 << 1 // DMA0_PKG_IRQ_PEND; DMA 0 Package End Transfer Interrupt Pending. Set 1 to the bit will clear it.
	dma0HalfIrqPend       dmaA64PendingIrq = 1 << 0 // DMA0_HLAF_IRQ_PEND; DMA 0 Half Package Transfer Interrupt Pending. Set 1 to the bit will clear it.
)

// DMA_IRQ_PEND_REG0
// A64: Page 201-203.
type dmaA64PendingIrq uint32

const (
	ddma7EndIrqPend   dmaR8PendingIrq = 1 << 31 // DDMA7_END_IRQ_PEND
	ddma7HalfIreqPend dmaR8PendingIrq = 1 << 30 // DDMA7_HF_IRQ_PEND
	// ...
	ddma0EndIrqPend   dmaR8PendingIrq = 1 << 17 // DDMA0_END_IRQ_PEND
	ddma0HalfIreqPend dmaR8PendingIrq = 1 << 16 // DDMA0_HF_IRQ_PEND
	ndma7EndIrqPend   dmaR8PendingIrq = 1 << 15 // NDMA7_END_IRQ_PEND
	ndma7HalfIreqPend dmaR8PendingIrq = 1 << 16 // NDDMA7_HF_IRQ_PEND
	// ...
	ndma0EndIrqPend   dmaR8PendingIrq = 1 << 1 // NDMA0_END_IRQ_PEND
	ndma0HalfIreqPend dmaR8PendingIrq = 1 << 0 // NDMA0_HF_IRQ_PEND
)

// DMA_IRQ_PEND_STAS_REG
// R8: Page 126-129.
type dmaR8PendingIrq uint32

const (
	ndmaLoad       ndmaR8Cfg = 1 << 31 // NDMA_LOAD
	ndmaContinuous ndmaR8Cfg = 1 << 30 // NDMA_CONTI_EN Continuous mode
	ndmaWaitClk0   ndmaR8Cfg = 0 << 27 // NDMA_WAIT_STATE Number of clock to wait for
	ndmaWaitClk2   ndmaR8Cfg = 1 << 27 // 2(n+1)
	ndmaWaitClk6   ndmaR8Cfg = 2 << 27 //
	ndmaWaitClk8   ndmaR8Cfg = 3 << 27 //
	ndmaWaitClk10  ndmaR8Cfg = 4 << 27 //
	ndmaWaitClk12  ndmaR8Cfg = 5 << 27 //
	ndmaWaitClk14  ndmaR8Cfg = 6 << 27 //
	ndmaWaitClk16  ndmaR8Cfg = 7 << 27 //
	ndmaDstWidth32 ndmaR8Cfg = 2 << 25 // NDMA_DST_DATA_WIDTH
	ndmaDstWidth16 ndmaR8Cfg = 1 << 25 //
	ndmaDstWidth8  ndmaR8Cfg = 0 << 25 //
	ndmaDstBurst8  ndmaR8Cfg = 2 << 23 // NDMA_DST_BST_LEN
	ndmaDstBurst4  ndmaR8Cfg = 1 << 23 //
	ndmaDstBurst1  ndmaR8Cfg = 0 << 23 //
	// 22 reserved NDMA_CFG_DST_NON_SECURE ?
	ndmaDstAddrNoInc  ndmaR8Cfg = 1 << 21  // NDMA_DST_ADDR_TYPE
	ndmaDstDrqIRTX    ndmaR8Cfg = 0 << 16  // NDMA_DST_DRQ_TYPE
	ndmaDstDrqUART1TX ndmaR8Cfg = 9 << 16  //
	ndmaDstDrqUART3TX ndmaR8Cfg = 11 << 16 //
	ndmaDstDrqAudio   ndmaR8Cfg = 19 << 16 // 24.576MHz (Page 53)
	ndmaDstDrqSRAM    ndmaR8Cfg = 21 << 16 //
	ndmaDstDrqSPI0TX  ndmaR8Cfg = 24 << 16 //
	ndmaDstDrqSPI1TX  ndmaR8Cfg = 25 << 16 //
	ndmaDstDrqSPI2TX  ndmaR8Cfg = 26 << 16 //
	ndmaDstDrqUSB1    ndmaR8Cfg = 27 << 16 // 480MHz
	ndmaDstDrqUSB2    ndmaR8Cfg = 28 << 16 //
	ndmaDstDrqUSB3    ndmaR8Cfg = 29 << 16 //
	ndmaDstDrqUSB4    ndmaR8Cfg = 30 << 16 //
	ndmaDstDrqUSB5    ndmaR8Cfg = 31 << 16 //
	ndmaBCRemain      ndmaR8Cfg = 1 << 15  // BC_MODE_SEL
	// 14:11 reserved
	ndmaSrcWidth32 ndmaR8Cfg = 2 << 9 // NDMA_SRC_DATA_WIDTH
	ndmaSrcWidth16 ndmaR8Cfg = 1 << 9 //
	ndmaSrcWidth8  ndmaR8Cfg = 0 << 9 //
	ndmaSrcBurst8  ndmaR8Cfg = 2 << 7 // NDMA_SRC_BST_LEN
	ndmaSrcBurst4  ndmaR8Cfg = 1 << 7 //
	ndmaSrcBurst1  ndmaR8Cfg = 0 << 7 //
	// 6 reserved NDMA_CFG_SRC_NON_SECURE ?
	ndmaSrcAddrNoInc  ndmaR8Cfg = 1 << 5  // NDMA_SRC_ADDR_TYPE
	ndmaSrcDrqIRTX    ndmaR8Cfg = 0 << 0  // NDMA_SRC_DRQ_TYPE
	ndmaSrcDrqUART1RX ndmaR8Cfg = 9 << 0  //
	ndmaSrcDrqUART3RX ndmaR8Cfg = 11 << 0 //
	ndmaSrcDrqAudio   ndmaR8Cfg = 19 << 0 // 24.576MHz (Page 53)
	ndmaSrcDrqSRAM    ndmaR8Cfg = 21 << 0 //
	ndmaSrcDrqSDRAM   ndmaR8Cfg = 22 << 0 // 0~400MHz
	ndmaSrcDrqTPAD    ndmaR8Cfg = 23 << 0 //
	ndmaSrcDrqSPI0RX  ndmaR8Cfg = 24 << 0 //
	ndmaSrcDrqSPI1RX  ndmaR8Cfg = 25 << 0 //
	ndmaSrcDrqSPI2RX  ndmaR8Cfg = 26 << 0 //
	ndmaSrcDrqUSB1    ndmaR8Cfg = 27 << 0 // 480MHz
	ndmaSrcDrqUSB2    ndmaR8Cfg = 28 << 0 //
	ndmaSrcDrqUSB3    ndmaR8Cfg = 29 << 0 //
	ndmaSrcDrqUSB4    ndmaR8Cfg = 30 << 0 //
	ndmaSrcDrqUSB5    ndmaR8Cfg = 31 << 0 //
)

// NDMA_CTRL_REG
// R8: Page 129-131.
type ndmaR8Cfg uint32

const (
	ddmaLoad       ddmaR8Cfg = 1 << 31 // DDMA_LOAD
	ddmaBusy       ddmaR8Cfg = 1 << 30 // DDMA_BSY_STA
	ddmaContinuous ddmaR8Cfg = 1 << 29 // DDMA_CONTI_MODE_EN
	// 28:27 reserved  28 = DDMA_CFG_DST_NON_SECURE ?
	ddmaDstWidth32     ddmaR8Cfg = 2 << 25  // DDMA_DST_DATA_WIDTH
	ddmaDstWidth16     ddmaR8Cfg = 1 << 25  //
	ddmaDstWidth8      ddmaR8Cfg = 0 << 25  //
	ddmaDstBurst8      ddmaR8Cfg = 2 << 23  // DDMA_DST_BST_LEN
	ddmaDstBurst4      ddmaR8Cfg = 1 << 23  //
	ddmaDstBurst1      ddmaR8Cfg = 0 << 23  //
	ddmaDstVertical    ddmaR8Cfg = 3 << 21  // DDMA_ADDR_MODE; no idea what it's use it. It's not explained in the datasheet ...
	ddmaDstHorizontal  ddmaR8Cfg = 2 << 21  // ... and the official drivers/dma/sun6i-dma.c driver doesn't use it
	ddmaDstIOMode      ddmaR8Cfg = 1 << 21  // Non incrementing
	ddmaDstLinear      ddmaR8Cfg = 0 << 21  // Normal incrementing position
	ddmaDstDrqSRAM     ddmaR8Cfg = 0 << 16  // DDMA_DST_DRQ_SEL
	ddmaDstDrqSDRAM    ddmaR8Cfg = 1 << 16  // DDR ram speed
	ddmaDstDrqNAND     ddmaR8Cfg = 3 << 16  //
	ddmaDstDrqUSB0     ddmaR8Cfg = 4 << 16  //
	ddmaDstDrqSPI1TX   ddmaR8Cfg = 8 << 16  //
	ddmaDstDrqCryptoTX ddmaR8Cfg = 10 << 16 //
	ddmaDstDrqTCON0    ddmaR8Cfg = 14 << 16 //
	ddmaDstDrqSPI0TX   ddmaR8Cfg = 26 << 16 //
	ddmaDstDrqSPI2TX   ddmaR8Cfg = 28 << 16 //
	ddmaBCRemain       ddmaR8Cfg = 1 << 15  // BC_MODE_SEL
	// 14:11 reserved
	ddmaSrcWidth32    ddmaR8Cfg = 2 << 9 // DDMA_SRC_DATA_WIDTH
	ddmaSrcWidth16    ddmaR8Cfg = 1 << 9 //
	ddmaSrcWidth8     ddmaR8Cfg = 0 << 9 //
	ddmaSrcBurst8     ddmaR8Cfg = 2 << 7 // DDMA_SRC_BST_LEN
	ddmaSrcBurst4     ddmaR8Cfg = 1 << 7 //
	ddmaSrcBurst1     ddmaR8Cfg = 0 << 7 //
	ddmaSrcVertical   ddmaR8Cfg = 3 << 5 // DDMA_SRC_ADDR_MODE
	ddmaSrcHorizontal ddmaR8Cfg = 2 << 5 //
	ddmaSrcIOMode     ddmaR8Cfg = 1 << 5 // Non incrementing
	ddmaSrcLinear     ddmaR8Cfg = 0 << 5 // Normal incrementing position
	// 4:0 drq
	ddmaSrcDrqSRAM     ddmaR8Cfg = 0 << 0  // DDMA_SRC_DRQ_TYPE
	ddmaSrcDrqSDRAM    ddmaR8Cfg = 1 << 0  //
	ddmaSrcDrqNAND     ddmaR8Cfg = 3 << 0  //
	ddmaSrcDrqUSB0     ddmaR8Cfg = 4 << 0  //
	ddmaSrcDrqSPI1RX   ddmaR8Cfg = 9 << 0  //
	ddmaSrcDrqCryptoRX ddmaR8Cfg = 11 << 0 //
	ddmaSrcDrqSPI0RX   ddmaR8Cfg = 27 << 0 //
	ddmaSrcDrqSPI2RX   ddmaR8Cfg = 29 << 0 //
)

// DDMA_CFG_REG
// R8: Page 131-134.
type ddmaR8Cfg uint32

const (
	// For each value, N+1 is actually used.
	ddmaDstBlkSizeMask      ddmaR8Param = 0xFF << 24 // DEST_DATA_BLK_SIZE
	ddmaDstWaitClkCycleMask ddmaR8Param = 0xFF << 16 // DEST_WAIT_CLK_CYC
	ddmaSrcBlkSizeMask      ddmaR8Param = 0xFF << 8  // SRC_DATA_BLK_SIZE
	ddmaSrcWaitClkCycleMask ddmaR8Param = 0xFF << 0  // SRC_WAIT_CLK_CYC
)

// DDMA_PARA_REG
// R8: Page 134.
type ddmaR8Param uint32

// smokeTest allocates two physical pages, ask the DMA controller to copy the
// data from one page to another (with a small offset) and make sure the
// content is as expected.
//
// This should take a fraction of a second and will make sure the driver is
// usable.
func smokeTest() error {
	const size = 4096  // 4kb
	const holeSize = 1 // Minimum DMA alignment.

	alloc := func(s int) (pmem.Mem, error) {
		return pmem.Alloc(s)
	}

	copyMem := func(pDst, pSrc uint64) error {
		n := drvDMA.dmaMemory.getDedicated()
		if n == -1 {
			return errors.New("no channel available")
		}
		drvDMA.dmaMemory.irqEn &^= 3 << uint(2*n+16)
		drvDMA.dmaMemory.irqPendStas = 3 << uint(2*n+16)
		ch := &drvDMA.dmaMemory.dedicated[n]
		defer func() {
			_ = ch.release()
		}()
		ch.set(uint32(pSrc), uint32(pDst)+holeSize, 4096-2*holeSize, false, false, ddmaDstDrqSDRAM|ddmaSrcDrqSDRAM)

		for ch.cfg&ddmaBusy != 0 {
		}
		return nil
	}

	return pmem.TestCopy(size, holeSize, alloc, copyMem)
}

// driverDMA implements periph.Driver.
//
// It implements much more than the DMA controller, it also exposes the clocks,
// the PWM and PCM controllers.
type driverDMA struct {
	// dmaMemory is the memory map of the CPU DMA registers.
	dmaMemory *dmaMap
	// pwmMemory is the memory map of the CPU PWM registers.
	pwmMemory *pwmMap
	// spiMemory is the memory mapping for the spi CPU registers.
	spiMemory *spiMap
	// clockMemory is the memory mapping for the clock CPU registers.
	clockMemory *clockMap
	// timerMemory is the memory mapping for the timer CPU registers.
	timerMemory *timerMap
}

func (d *driverDMA) String() string {
	return "allwinner-dma"
}

func (d *driverDMA) Prerequisites() []string {
	return []string{"allwinner-gpio"}
}

func (d *driverDMA) After() []string {
	return nil
}

func (d *driverDMA) Init() (bool, error) {
	// dmaBaseAddr is the physical base address of the DMA registers.
	var dmaBaseAddr uint32
	// pwmBaseAddr is the physical base address of the PWM registers.
	var pwmBaseAddr uint32
	// spiBaseAddr is the physical base address of the clock registers.
	var spiBaseAddr uint32
	// clockBaseAddr is the physical base address of the clock registers.
	var clockBaseAddr uint32
	// timerBaseAddr is the physical base address of the timer registers.
	var timerBaseAddr uint32
	if IsA64() {
		// Page 198.
		dmaBaseAddr = 0x1C02000
		// Page 194.
		pwmBaseAddr = 0x1C21400
		// Page 161.
		timerBaseAddr = 0x1C20C00
		// Page 81.
		clockBaseAddr = 0x1C20000
		// Page Page 545.
		spiBaseAddr = 0x01C68000
	} else if IsR8() {
		// Page 124.
		dmaBaseAddr = 0x1C02000
		// Page 83.
		pwmBaseAddr = 0x1C20C00 + 0x200
		// Page 85.
		timerBaseAddr = 0x1C20C00
		// Page 57.
		clockBaseAddr = 0x1C20000
		// Page 151.
		spiBaseAddr = 0x01C05000
	} else {
		// H3
		// Page 194.
		//dmaBaseAddr = 0x1C02000
		// Page 187.
		//pwmBaseAddr = 0x1C21400
		// Page 154.
		//timerBaseAddr = 0x1C20C00
		return false, errors.New("unsupported CPU architecture")
	}

	if err := pmem.MapAsPOD(uint64(dmaBaseAddr), &d.dmaMemory); err != nil {
		if os.IsPermission(err) {
			return true, fmt.Errorf("need more access, try as root: %v", err)
		}
		return true, err
	}

	if err := pmem.MapAsPOD(uint64(pwmBaseAddr), &d.pwmMemory); err != nil {
		return true, err
	}
	if err := pmem.MapAsPOD(uint64(timerBaseAddr), &d.timerMemory); err != nil {
		return true, err
	}
	if err := pmem.MapAsPOD(uint64(clockBaseAddr), &d.clockMemory); err != nil {
		return true, err
	}
	if err := pmem.MapAsPOD(uint64(spiBaseAddr), &d.spiMemory); err != nil {
		return true, err
	}

	return true, smokeTest()
}

func (d *driverDMA) Close() error {
	// Stop DMA and PWM controllers.
	return nil
}

func init() {
	if false && isArm {
		// TODO(maruel): This is intense, wait to be sure it works.
		driverreg.MustRegister(&drvDMA)
	}
}

var drvDMA driverDMA
