github.com/f-secure-foundry/tamago@v0.0.0-20220307101044-d73fcdd7f11b/soc/bcm2835/dma.go (about)

     1  // BCM2835 SoC DMA support
     2  // https://github.com/f-secure-foundry/tamago
     3  //
     4  // Copyright (c) the bcm2835 package authors
     5  //
     6  // Use of this source code is governed by the license
     7  // that can be found in the LICENSE file.
     8  
     9  package bcm2835
    10  
    11  import (
    12  	"encoding/binary"
    13  	"fmt"
    14  	"runtime"
    15  	"sync"
    16  
    17  	"github.com/f-secure-foundry/tamago/dma"
    18  	"github.com/f-secure-foundry/tamago/internal/reg"
    19  )
    20  
    21  //
    22  // There are 16 DMA channels with identical register layout.
    23  //
    24  // The 16th channel (channel 15) is in a non-contiguous register
    25  // space.
    26  //
    27  // See BCM2835-ARM-Periperals.pdf for detailed register usage
    28  //
    29  
    30  // Channel base addresses
    31  const (
    32  	// Base address of the first channel.
    33  	DMA_CH_BASE0 = 0x7000
    34  
    35  	// Base address of the 16th channel (ch 15).
    36  	//
    37  	// Channel 15 is register space is non-contiguous with other
    38  	// channels.
    39  	DMA_CH_BASE15 = 0x5000
    40  
    41  	// Offset of each channel's registers from previous.
    42  	DMA_CH_SPAN = 0x100
    43  )
    44  
    45  // Layout of registers for each channel
    46  const (
    47  	// Control and Status
    48  	DMA_CH_REG_CS = 0x00
    49  
    50  	// Control Block Address
    51  	DMA_CH_REG_CONBLK_AD = 0x04
    52  
    53  	// Debug
    54  	DMA_CH_REG_DEBUG = 0x20
    55  )
    56  
    57  // DMA Control and Status flags
    58  //
    59  // These are a mix of read-only, write-only, read/write
    60  const (
    61  	// Activate (start) the DMA transfer
    62  	DMA_CS_ACTIVE = 1 << 0
    63  
    64  	// Indicates the DMA transfer is complete
    65  	DMA_CS_END = 1 << 1
    66  
    67  	// Interrupt Status
    68  	DMA_CS_INT = 1 << 2
    69  
    70  	// DREQ Status
    71  	DMA_CS_DREQ = 1 << 3
    72  
    73  	// Indicates the DMA transfer has been paused
    74  	DMA_CS_PAUSED = 1 << 4
    75  
    76  	// Indicates DMA is paused due to DREQ
    77  	DMA_CS_DREQ_STOPS_DMA = 1 << 5
    78  
    79  	// Indicates if the DMA is waiting for outstanding writes
    80  	DMA_CS_WAITING_FOR_OUTSTANDING_WRITES = 1 << 6
    81  
    82  	// Indicates if a DMA error occured
    83  	DMA_CS_ERROR = 1 << 8
    84  
    85  	// Sets Priority on the AXI bus
    86  	DMA_CS_PRIORITY_SHIFT = 16
    87  	DMA_CS_PRIORITY_MASK  = 0xF
    88  
    89  	// Sets the Panic priority on the AXI bus
    90  	DMA_CS_PANIC_PRIORITY_SHIFT = 20
    91  	DMA_CS_PANIC_PRIORITY_MASK  = 0xF
    92  
    93  	// Limits outstanding writes and waits for completion at end of transfer
    94  	DMA_CS_WAIT_FOR_OUTSTANDING_WRITES = 1 << 28
    95  
    96  	// Disables debug pause
    97  	DMA_CS_DISDEBUG = 1 << 29
    98  
    99  	// Aborts the current control block (transfer segment)
   100  	DMA_CS_ABORT = 1 << 30
   101  
   102  	// Resets the DMA channel
   103  	DMA_CS_RESET = 1 << 31
   104  )
   105  
   106  // DMA Transfer Information control flags
   107  const (
   108  	// Interrupt Enable (signal on transfer complete)
   109  	DMA_TI_INTEN = 0x1 << 0
   110  
   111  	// Use two-dimensional (striped) mode
   112  	DMA_TI_TDMODE = 0x1 << 1
   113  
   114  	// Wait for write response
   115  	DMA_TI_WAITRESP = 0x1 << 3
   116  
   117  	// Increment destination address
   118  	DMA_TI_DEST_INC = 0x1 << 4
   119  
   120  	// Destination transfer width (1 = 128bit, 0 = 32bit)
   121  	DMA_TI_DEST_WIDTH = 0x1 << 5
   122  
   123  	// Control writes with DREQ (allow peripheral to control speed)
   124  	DMA_TI_DEST_DREQ = 0x1 << 6
   125  
   126  	// Ignore writes (do not perform writes)
   127  	DMA_TI_DEST_IGNORE = 0x1 << 7
   128  
   129  	// Increment source address
   130  	DMA_TI_SRC_INC = 0x1 << 8
   131  
   132  	// Source transfer width (1 = 128bit, 0 = 32bit)
   133  	DMA_TI_SRC_WIDTH = 0x1 << 9
   134  
   135  	// Control reads with DREQ (allow peripheral to control speed)
   136  	DMA_TI_SRC_DREQ = 0x1 << 10
   137  
   138  	// Ignore reads (do not perform reads)
   139  	DMA_TI_SRC_IGNORE = 0x1 << 11
   140  
   141  	// 4-bit burst length requested
   142  	DMA_TI_BURST_LENGTH_SHIFT = 12
   143  	DMA_TI_BURST_LENGTH_MASK  = 0xF
   144  
   145  	// Peripheral ID for DREQ rate control
   146  	DMA_TI_BURST_PERMAP_SHIFT = 16
   147  	DMA_TI_BURST_PREMAP_MASK  = 0x1F
   148  
   149  	// Add wait cycles between each DMA read or write
   150  	DMA_TI_WAITS_SHIFT = 21
   151  	DMA_TI_WAITS_MASK  = 0x1F
   152  
   153  	// Don't do wide bursts
   154  	DMA_TI_NO_WIDE_BURSTS = 0x1 << 26
   155  )
   156  
   157  // DMA Debug flags
   158  const (
   159  	// Indicates AXI read last signal was not set when expected
   160  	DMA_DEBUG_READ_LAST_NOT_SET_ERROR = 0x1 << 0
   161  
   162  	// Indicates FIFO error
   163  	DMA_DEBUG_FIFO_ERROR = 0x1 << 1
   164  
   165  	// Indicates read error
   166  	DMA_DEBUG_READ_ERROR = 0x1 << 2
   167  
   168  	// Indicates outstanding writes
   169  	DMA_DEBUG_OUTSTANDING_WRITES_SHIFT = 4
   170  	DMA_DEBUG_OUTSTANDING_WRITE_MASK   = 0xF
   171  
   172  	// Gets the AXI ID of this channel
   173  	DMA_DEBUG_DMA_ID_SHIFT = 8
   174  	DMA_DEBUG_DMA_ID_MASK  = 0xFF
   175  
   176  	// Gets the DMA engine state
   177  	DMA_DEBUG_DMA_STATE_SHIFT = 16
   178  	DMA_DEBUG_DMA_STATE_MASK  = 0xFF
   179  
   180  	// Gets the DMA version
   181  	DMA_DEBUG_VERSION_SHIFT = 25
   182  	DMA_DEBUG_VERSION_MASK  = 0x7
   183  
   184  	// Indicates if this is a reduced performance channel
   185  	DMA_DEBUG_LITE = 1 << 28
   186  )
   187  
   188  type DMAController struct {
   189  	sync.Mutex
   190  	channels []*DMAChannel
   191  	region   *dma.Region
   192  }
   193  
   194  // DMAChannel controls a specific DMA channel on the DMA controller
   195  type DMAChannel struct {
   196  	index     int
   197  	allocated bool
   198  
   199  	// base is the absolute base address of this channel's registers,
   200  	// already offset by the peripheral base address.
   201  	base uint32
   202  
   203  	ctrlr *DMAController
   204  }
   205  
   206  // Access to the debug flag bitfield
   207  type DMADebugInfo uint32
   208  
   209  // Access to the status flag bitfield
   210  type DMAStatus uint32
   211  
   212  // DMA provides access to the DMA controller
   213  var DMA = DMAController{}
   214  
   215  // Initialize the controller.
   216  //
   217  // The given region provides a memory region dedicated for
   218  // DMA transfer purposes
   219  func (c *DMAController) Init(rgn *dma.Region) {
   220  	c.region = rgn
   221  	c.channels = make([]*DMAChannel, 16)
   222  
   223  	// VideoCore reserves some DMA channels, only mark available
   224  	// channels the VideoCore is making available to the CPU
   225  	available := CPUAvailableDMAChannels()
   226  
   227  	for i := range c.channels {
   228  		c.channels[i] = &DMAChannel{
   229  			index:     i,
   230  			allocated: (available & (1 << i)) != 0,
   231  			base:      peripheralBase + DMA_CH_BASE0 + uint32(i)*DMA_CH_SPAN,
   232  			ctrlr:     c,
   233  		}
   234  	}
   235  
   236  	// Channel 15 is a special case
   237  	c.channels[15].base = peripheralBase + DMA_CH_BASE15
   238  }
   239  
   240  // AllocChannel provides exclusive use of a DMA channel
   241  func (c *DMAController) AllocChannel() (*DMAChannel, error) {
   242  	c.Lock()
   243  	defer c.Unlock()
   244  
   245  	for _, ch := range c.channels {
   246  		if !ch.allocated {
   247  			ch.allocated = true
   248  			return ch, nil
   249  		}
   250  	}
   251  
   252  	return nil, fmt.Errorf("no DMA channels available")
   253  }
   254  
   255  // FreeChannel surrenders exclusive use of a DMA channel
   256  func (c *DMAController) FreeChannel(ch *DMAChannel) error {
   257  	c.Lock()
   258  	defer c.Unlock()
   259  
   260  	if !ch.allocated {
   261  		return fmt.Errorf("attempt to free unallocated channel %d", ch.index)
   262  	}
   263  
   264  	ch.allocated = false
   265  	return nil
   266  }
   267  
   268  // Debug state for the channel
   269  func (ch *DMAChannel) DebugInfo() DMADebugInfo {
   270  	return DMADebugInfo(reg.Read(ch.base + DMA_CH_REG_DEBUG))
   271  }
   272  
   273  // Status of the channel
   274  func (ch *DMAChannel) Status() DMAStatus {
   275  	return DMAStatus(reg.Read(ch.base + DMA_CH_REG_CS))
   276  }
   277  
   278  // Do a RAM to RAM transfer.
   279  //
   280  // This method blocks until the transfer is complete, but does allow the
   281  // Go scheduler to schedule other Go routines.
   282  func (ch *DMAChannel) Copy(from uint32, size int, to uint32) {
   283  	cbAddr, cb := ch.ctrlr.region.Reserve(8*4, 64)
   284  	defer ch.ctrlr.region.Release(cbAddr)
   285  
   286  	conv := binary.LittleEndian
   287  
   288  	conv.PutUint32(cb[0:], DMA_TI_SRC_INC|DMA_TI_DEST_INC)
   289  	conv.PutUint32(cb[4:], from)
   290  	conv.PutUint32(cb[8:], to)
   291  	conv.PutUint32(cb[12:], uint32(size))
   292  	conv.PutUint32(cb[16:], 0)
   293  	conv.PutUint32(cb[20:], 0)
   294  	conv.PutUint32(cb[24:], 0)
   295  	conv.PutUint32(cb[28:], 0)
   296  
   297  	reg.Write(ch.base+DMA_CH_REG_CS, DMA_CS_RESET)
   298  	reg.Write(ch.base+DMA_CH_REG_DEBUG, 0x7) // Clear Errors
   299  	reg.Write(ch.base+DMA_CH_REG_CONBLK_AD, cbAddr)
   300  	reg.Write(ch.base+DMA_CH_REG_CS, DMA_CS_ACTIVE)
   301  
   302  	for (reg.Read(ch.base+DMA_CH_REG_CS) & DMA_CS_END) == 0 {
   303  		runtime.Gosched()
   304  	}
   305  }
   306  
   307  // Indicates the last AXI read signal was not set when expected
   308  func (i DMADebugInfo) ReadLastNotSetError() bool {
   309  	return (i & DMA_DEBUG_READ_LAST_NOT_SET_ERROR) != 0
   310  }
   311  
   312  // Indicates a FIFO error condition
   313  func (i DMADebugInfo) FIFOError() bool {
   314  	return (i & DMA_DEBUG_FIFO_ERROR) != 0
   315  }
   316  
   317  // Indicates a read error
   318  func (i DMADebugInfo) ReadError() bool {
   319  	return (i & DMA_DEBUG_READ_ERROR) != 0
   320  }
   321  
   322  // Currently outstanding writes
   323  func (i DMADebugInfo) OutstandingWrites() int {
   324  	return int(i>>DMA_DEBUG_OUTSTANDING_WRITES_SHIFT) & DMA_DEBUG_OUTSTANDING_WRITE_MASK
   325  }
   326  
   327  // AXI ID of this channel
   328  func (i DMADebugInfo) ID() int {
   329  	return int(i>>DMA_DEBUG_DMA_ID_SHIFT) & DMA_DEBUG_DMA_ID_MASK
   330  }
   331  
   332  // State Machine State
   333  func (i DMADebugInfo) State() int {
   334  	return int(i>>DMA_DEBUG_DMA_STATE_SHIFT) & DMA_DEBUG_DMA_STATE_MASK
   335  }
   336  
   337  // Version
   338  func (i DMADebugInfo) Version() int {
   339  	return int(i>>DMA_DEBUG_VERSION_SHIFT) & DMA_DEBUG_VERSION_MASK
   340  }
   341  
   342  // Indicates if reduced performance 'lite' engine
   343  func (i DMADebugInfo) Lite() bool {
   344  	return (i & DMA_DEBUG_LITE) != 0
   345  }
   346  
   347  // Compat representation of the DMA debug status for debugging
   348  func (i DMADebugInfo) String() string {
   349  	return fmt.Sprintf(
   350  		"[E:%s%s%s, O:%d, ID:%d, S:%x, V:%d, L:%s]",
   351  		boolToStr(i.ReadLastNotSetError(), "L", "l"),
   352  		boolToStr(i.FIFOError(), "F", "f"),
   353  		boolToStr(i.ReadError(), "R", "r"),
   354  		i.OutstandingWrites(),
   355  		i.ID(),
   356  		i.State(),
   357  		i.Version(),
   358  		boolToStr(i.Lite(), "T", "F"),
   359  	)
   360  }
   361  
   362  // Indicates if a transfer is active
   363  func (s DMAStatus) Active() bool {
   364  	return (uint32(s) & DMA_CS_ACTIVE) != 0
   365  }
   366  
   367  // Indicates if the transfer is complete
   368  func (s DMAStatus) End() bool {
   369  	return (uint32(s) & DMA_CS_END) != 0
   370  }
   371  
   372  // Gets the interrupt status
   373  func (s DMAStatus) Int() bool {
   374  	return (uint32(s) & DMA_CS_INT) != 0
   375  }
   376  
   377  // Gets the DREQ state
   378  func (s DMAStatus) DReq() bool {
   379  	return (uint32(s) & DMA_CS_DREQ) != 0
   380  }
   381  
   382  // Indicates if the transfer is currently paused
   383  func (s DMAStatus) Paused() bool {
   384  	return (uint32(s) & DMA_CS_PAUSED) != 0
   385  }
   386  
   387  // Indicates if the transfer is currently paused due to DREQ state
   388  func (s DMAStatus) DReqStopsDMA() bool {
   389  	return (uint32(s) & DMA_CS_DREQ_STOPS_DMA) != 0
   390  }
   391  
   392  // Indicates if the transfer is waiting for last write to complete
   393  func (s DMAStatus) WaitingForOutstandingWrites() bool {
   394  	return (uint32(s) & DMA_CS_WAITING_FOR_OUTSTANDING_WRITES) != 0
   395  }
   396  
   397  // Indicates if there is an error state on the channel
   398  func (s DMAStatus) Error() bool {
   399  	return (uint32(s) & DMA_CS_ERROR) != 0
   400  }
   401  
   402  // Gets the AXI priority level of the channel
   403  func (s DMAStatus) Priority() int {
   404  	return int((uint32(s) & DMA_CS_PRIORITY_MASK) >> DMA_CS_PRIORITY_SHIFT)
   405  }
   406  
   407  // Gets the AXI panic priority level of the channel
   408  func (s DMAStatus) PanicPriority() int {
   409  	return int((uint32(s) & DMA_CS_PANIC_PRIORITY_MASK) >> DMA_CS_PANIC_PRIORITY_SHIFT)
   410  }
   411  
   412  // Indicates if the channel will wait for all writes to complete
   413  func (s DMAStatus) WaitForOutstandingWrites() bool {
   414  	return (uint32(s) & DMA_CS_WAIT_FOR_OUTSTANDING_WRITES) != 0
   415  }
   416  
   417  // Indicates if the debug pause signal is disabled
   418  func (s DMAStatus) DisableDebug() bool {
   419  	return (uint32(s) & DMA_CS_DISDEBUG) != 0
   420  }
   421  
   422  // Compat representation of the DMA channel status for debugging
   423  func (s DMAStatus) String() string {
   424  	return fmt.Sprintf(
   425  		"[F:%s%s%s%s%s%s%s%s%s%s, P:%d, PP:%d]",
   426  		boolToStr(s.Active(), "A", "a"),
   427  		boolToStr(s.End(), "E", "e"),
   428  		boolToStr(s.Int(), "I", "i"),
   429  		boolToStr(s.DReq(), "D", "d"),
   430  		boolToStr(s.Paused(), "P", "p"),
   431  		boolToStr(s.DReqStopsDMA(), "S", "s"),
   432  		boolToStr(s.WaitingForOutstandingWrites(), "W", "w"),
   433  		boolToStr(s.Error(), "E", "E"),
   434  		boolToStr(s.WaitForOutstandingWrites(), "W", "w"),
   435  		boolToStr(s.DisableDebug(), "D", "d"),
   436  		s.Priority(),
   437  		s.PanicPriority(),
   438  	)
   439  }
   440  
   441  func boolToStr(val bool, ifTrue string, ifFalse string) string {
   442  	if val {
   443  		return ifTrue
   444  	}
   445  
   446  	return ifFalse
   447  }