github.com/usbarmory/tamago@v0.0.0-20240508072735-8612bbe1e454/soc/bcm2835/dma.go (about) 1 // BCM2835 SoC DMA support 2 // https://github.com/usbarmory/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/usbarmory/tamago/dma" 18 "github.com/usbarmory/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, uint32(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 }