2019-12-27 17:23:08 +00:00
|
|
|
// 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"
|
|
|
|
|
2021-09-01 19:34:31 +00:00
|
|
|
"periph.io/x/conn/v3/driver/driverreg"
|
|
|
|
"periph.io/x/host/v3/pmem"
|
2019-12-27 17:23:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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.
|
2021-09-01 19:34:31 +00:00
|
|
|
driverreg.MustRegister(&drvDMA)
|
2019-12-27 17:23:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var drvDMA driverDMA
|