Commit 58777373 authored by Ben Avison's avatar Ben Avison
Browse files

Just enough support for floppy DMA transfer to work on Tungsten.

Detail:
  * Supports DMA controllers with no interrupts (ie those that need a bounce
    buffer).
  * Restructured interface to HAL to separate the concepts of a DMA controller
    device and a DMA channel device.
  * Bugfixes to handling of single-buffered channels.
  * Updated Doc.HAL_DMAAPI.
Admin:
  Tested with Kevin's ADFS.

Version 0.15, 4.4.2.3. Tagged as 'DMA-0_15-4_4_2_3'
parent 50188fde
DMAManager SWIs
---------------
SWI DMA_RegisterChannel extended as follows:
R0 bits 0-3 = post-transfer channel delay
R0 bit 4 set => disable burst transfers
......@@ -10,66 +13,162 @@ SWI DMA_RegisterChannel extended as follows:
SWI DMA_ExamineTransfer may not be able to give an accurate byte count for
some DMA controllers; however the byte count returned is guaranteed not to
exceed the true count. If you need a guaranteed accurate value, you must call
it between DMA_SuspendTransfer and DMA_ResumeTransfer.
it between DMA_SuspendTransfer and DMA_ResumeTransfer. (This has always been
the case.)
Not yet implemented:
Future enhancement:
> SWIs DMA_ClaimChannel and DMA_ReleaseChannel reinstated, but modified so
> as to take the logical channel as a parameter and not returning any
> information about the physical channel. These allow a logical channel to
> temporarily gain sole use of a physical channel, typically to guarantee
> reception of incoming data.
int HAL_DMAActivate(struct device *);
void HAL_DMADeactivate(struct device *);
void HAL_DMAReset(struct device *);
int HAL_DMASleep(struct device *, state);
int HAL_DMADevice;
bool HAL_DMATestIRQ(struct device *)
Standard functions as defined in Docs.HAL.NewAPI
Only intno=0 is implemented
__value_in_regs struct { struct device **channel, unsigned int count }
HAL_DMAEnumeratePhysical(struct device *);
First parameter is ignored
Returns a static list of available physical DMA channel HAL devices
count may be 0 to indicate that no DMA facility is available
__value_in_regs struct { unsigned int *channel, unsigned int count }
HAL_DMAEnumerateLogical(struct device *);
First parameter is ignored
Returns a static list of logical DMA channels supported on the current
hardware
struct device *HAL_DMAAllocate(struct device *, unsigned int channel);
First parameter is ignored
Two types of devices (see Kernel.Docs.HAL.NewAPI) are defined: the DMA
controller, and the DMA (physical) channel.
DMA controller device
---------------------
struct dmacontroller
{
/* Public interface */
struct device dev;
uint32_t (*Features)(struct dmacontroller *);
__value_in_regs struct { struct dmachannel **channel; uint32_t count; }
(*EnumeratePhysical)(struct dmacontroller *);
__value_in_regs struct { uint32_t *channel; uint32_t count; }
(*EnumerateLogical)(struct dmacontroller *);
struct dmachannel *(*Allocate)(struct dmacontroller *, uint32_t channel);
void (*Deallocate)(struct dmacontroller *, uint32_t channel);
}
dev.type
dev.id
dev.location
dev.version
dev.description
dev.address
dev.Activate
dev.Deactivate
dev.Reset
dev.Sleep
dev.devicenumber
dev.TestIRQ
Standard functions as defined in Docs.HAL.NewAPI.
Type = &301.
Address is not used.
Controllers are activated/deactivated on module initialisation/finalisation.
Interrupts are ignored - provide them using the DMA channel device instead.
Features
Returns a flags word indicating the capabilities of the controller.
Currently no bits defined, all bits should be zero.
EnumeratePhysical
Returns a static list of available physical DMA channel devices.
EnumerateLogical
Returns a static list of logical DMA channels supported by the controller.
Allocate
Returns pointer to the physical DMA channel struct to associate with the
given logical DMA channel. If the hardware requires a particular logical-
physical mapping, this will be obeyed; otherwise one will be allocated at
the whim of the HAL (typically: physical channels grouped according to
the whim of the software (typically: physical channels grouped according to
priority of logical channel, then within each group logical channels are
allocated on a one-to-one mapping until no physical channels remain,
after which logical channels are arbitrarily doubled up)
Return value NULL => this logical channel not supported on this hardware
after which logical channels are arbitrarily doubled up).
Return value NULL => this logical channel not supported on this controller
DMA priorities:
Recommended DMA priorities:
High: sound DMA
Medium: other device DMA
Low: memory-to-memory DMA
void HAL_DMADeallocate(struct device *, unsigned int channel);
First parameter is ignored
The partner of HAL_DMAAllocate(), this lets the HAL know that a
particular logical channel is no longer being used
Deallocate
The partner of Allocate, this lets the device know that a particular
logical channel is no longer being used.
DMA channel device
------------------
struct dmachannel
{
/* Public interface */
struct device dev;
uint32_t (*Features)(struct dmachannel *);
void (*Abort)(struct device *);
void (*SetOptions)(struct dmachannel *, uint32_t flags, uint32_t address);
void (*SetCurrentTransfer)(struct dmachannel *, uint32_t address,
uint32_t length, uint32_t flags);
void (*SetNextTransfer)(struct dmachannel *, uint32_t address,
uint32_t length, uint32_t flags);
__value_in_regs struct { uint32_t address; uint32_t length; }
(*TransferState)(struct dmachannel *);
void (*SetCurrentTransfer2)(struct dmachannel *, uint32_t srcaddress,
uint32_t dstaddress, uint32_t length, uint32_t flags);
void (*SetNextTransfer2)(struct dmachannel *, uint32_t srcaddress,
uint32_t dstaddress, uint32_t length, uint32_t flags);
__value_in_regs struct { uint32_t srcaddress; uint32_t dstaddress;
uint32_t length; } (*TransferState2)(struct dmachannel *);
void (*IRQClear)(struct dmachannel *);
uint32_t (*Status)(struct dmachannel *);
}
unsigned int HAL_DMAFeatures(struct device *);
dev.type
dev.id
dev.location
dev.version
dev.description
dev.address
dev.Activate
dev.Deactivate
dev.Reset
dev.Sleep
dev.devicenumber
dev.TestIRQ
Standard functions as defined in Docs.HAL.NewAPI.
Type = &302.
Address is not used.
Channels are activated/deactivated around each transfer.
dev.Activate enables this physical DMA channel, so that it can generate
interrupts (typically this involves unmasking the DMA request line and
unmasking a bit in an interrupt control register).
dev.Deactivate must block until the transfer on this channel can safely be
interrupted (due to Service_PagesUnsafe or SWI DMA_SuspendTransfer),
then disable DMA at the DMA request line and interrupt generation levels.
After it returns, the values returned from TransferState and Status must
reflect the true state of the hardware - ie they must be suitable to use for
resumption of the transfer using SetCurrentTransfer.
dev.Reset is called by the DMAManager between transfers (as well as by the
kernel on software-initiated resets). After this call, Status must report an
over/underrun condition.
Features
Returns a flag word:
bit 0 set => DMA transfers cannot be paused, so if *any* of the scatter
list entries is for an unsafe page when the transfer is
activated, then the whole transfer must be delayed
other bits reserved, should be zero
void HAL_DMASetOptions(struct device *, unsigned int flags,
unsigned int address);
Abort
This should be considered a "forced" version of dev.Deactivate, and is only
used for SWI DMA_TerminateTransfer. The call must not block, but the values
subsequently returned from TransferState and Status are allowed to report
inaccurate results, providing they underestimate the progress of the
transfer.
SetOptions
|flags| sets the following aspects of the next transfer on this channel:
bit 0 set => memory to device, clear => device to memory
(ignored for any memory-to-memory channel)
......@@ -86,8 +185,7 @@ unsigned int address);
|address| sets the physical address of the peripheral to DMA to/from
(should be ignored by memory-to-memory channels)
void HAL_DMASetCurrentTransfer(struct device *, unsigned int address,
unsigned int length, unsigned int flags);
SetCurrentTransfer
This sets up the first transfer to be executed for this channel
address = physical address to start transfer from
length = number of bytes to transfer
......@@ -97,40 +195,32 @@ unsigned int length, unsigned int flags);
address and length should be multiples of the transfer unit size, and must
not cross a page boundary
void HAL_DMASetNextTransfer(struct device *, unsigned int address,
unsigned int length, unsigned int flags);
SetNextTransfer
This sets up the next transfer to be executed for this channel, using similar
parameters. This will only be called for double-buffered channels.
__value_in_regs struct { unsigned int address, unsigned int length }
HAL_DMATransferState(struct device *);
TransferState
This reads the progress of the current transfer on this channel.
If the DMA controller will not report accurate progress, report values
corresponding to a pessimistic amount of progress.
HAL_DMASetCurrentTransfer2() / HAL_SetNextTransfer2 / HAL_DMATransferState2()
are same as above but with two address arguments and one length argument. For
use in memory-to-memory transfers only. DMA activity where the memory read has
completed but the memory write has not will not be counted as having occurred -
this may involve winding back the read address
void HAL_DMAIRQEnable(struct device *);
Enables interrupt generation for this physical DMA channel
void HAL_DMAIRQDisable(struct device *);
Disables interrupt generation for this physical DMA channel
SetCurrentTransfer2 / SetNextTransfer2 / TransferState2
are same as above but with two address arguments and one length argument. For
use in memory-to-memory transfers only.
void HAL_DMAIRQClear(struct device *);
IRQClear
Clears the interrupt for this physical DMA channel (unless some other action
carried out during or since since HAL_DMATestIRQ has already done so)
carried out during or since since TestIRQ has already done so).
unsigned int HAL_DMAStatus(struct device *);
Status
Returns status bits for the current channel:
bit 0 set => channel is attempting to raise an interrupt
bit 1 set => channel is in over/underrun state (assumed to happen on every
interrupt for single-buffered DMA controllers)
bit 0 set => there are no transfers programmed but not yet started
(for a double-buffered controller, this is set when interrupting
or for a single-buffered controller, this is always set)
bit 1 set => channel has not yet been used, or is in over/underrun state
(set on every interrupt for single-buffered DMA controllers)
bit 2 set => channel entered over/underrun state before last call to
HAL_DMASetNextTransfer had effect (devices may determine this by
SetNextTransfer had effect (devices may determine this by
monitoring any handle they may have on the physical double
buffers, or failing this by calculating the final value of the
current pointer of the current buffer, and comparing it to what
......@@ -138,38 +228,6 @@ unsigned int HAL_DMAStatus(struct device *);
programmed - but note that this latter algorithm will fail if a
circular transfer from a single-entry scatter list is in use)
so the DMA manager needs to program the same data again, but
this time using HAL_DMASetCurrentTransfer. This bit must be
clear if the last transfer was programmed using
HAL_DMASetCurrentTransfer, or if bit 1 is clear.
void HAL_DMAEnable(struct device *);
Enables this physical DMA channel, so that it can generate interrupts
(typically this involves unmasking the DMA request line and unmasking a
bit in an interrupt control register).
void HAL_DMADisable(struct device *);
Blocks until the transfer on this channel can safely be interrupted (due to
Service_PagesUnsafe or SWIs DMATerminateTransfer or DMA_SuspendTransfer),
then disables DMA at the DMA request line and interrupt generation levels.
After this call returns, the values returned from HAL_DMATransferState and
HAL_DMAStatus must reflect the true state of the hardware - ie they must
be suitable to use for possible resumption of the transfer using
HAL_DMASetCurrentTransfer.
void HAL_DMAChannelReset(struct device *);
Restores any hardware state machine to its default state. For example, after
this call, HAL_DMAStatus must report an over/underrun condition.
struct dmachannel
{
/* Public interface */
struct device dev;
/* List of public function pointers as described above */
/* Private interface */
struct dmacontroller *controller; /* stuff common to all channels */
/* Addresses of all registers relevant to this channel */
}
this time using SetCurrentTransfer. This bit must be clear if
the last transfer was programmed using SetCurrentTransfer, or if
bit 1 is clear.
......@@ -12,3 +12,4 @@ E09:Not a suspended DMA transfer
E10:DMA manager cannot accept zero length or sync gap
E11:DMA transfer terminated
E12:Hardware does not support IO DMA
E13:Transfer too long
......@@ -7,19 +7,17 @@
GBLS Module_MinorVersion
GBLS Module_Date
GBLS Module_FullVersion
GBLS Module_ApplicationDate2
GBLS Module_ApplicationDate4
GBLS Module_ApplicationDate
GBLS Module_HelpVersion
GBLS Module_ComponentName
GBLS Module_ComponentPath
Module_MajorVersion SETS "0.15"
Module_Version SETA 15
Module_MinorVersion SETS "4.4.2.2"
Module_Date SETS "03 Oct 2002"
Module_ApplicationDate2 SETS "03-Oct-02"
Module_ApplicationDate4 SETS "03-Oct-2002"
Module_MinorVersion SETS "4.4.2.3"
Module_Date SETS "15 Oct 2002"
Module_ApplicationDate SETS "15-Oct-02"
Module_ComponentName SETS "DMA"
Module_ComponentPath SETS "RiscOS/Sources/HWSupport/DMA"
Module_FullVersion SETS "0.15 (4.4.2.2)"
Module_HelpVersion SETS "0.15 (03 Oct 2002) 4.4.2.2"
Module_FullVersion SETS "0.15 (4.4.2.3)"
Module_HelpVersion SETS "0.15 (15 Oct 2002) 4.4.2.3"
END
......@@ -5,20 +5,19 @@
*
*/
#define Module_MajorVersion_CMHG 0.15
#define Module_MinorVersion_CMHG 4.4.2.2
#define Module_Date_CMHG 03 Oct 2002
#define Module_MinorVersion_CMHG 4.4.2.3
#define Module_Date_CMHG 15 Oct 2002
#define Module_MajorVersion "0.15"
#define Module_Version 15
#define Module_MinorVersion "4.4.2.2"
#define Module_Date "03 Oct 2002"
#define Module_MinorVersion "4.4.2.3"
#define Module_Date "15 Oct 2002"
#define Module_ApplicationDate2 "03-Oct-02"
#define Module_ApplicationDate4 "03-Oct-2002"
#define Module_ApplicationDate "15-Oct-02"
#define Module_ComponentName "DMA"
#define Module_ComponentPath "RiscOS/Sources/HWSupport/DMA"
#define Module_FullVersion "0.15 (4.4.2.2)"
#define Module_HelpVersion "0.15 (03 Oct 2002) 4.4.2.2"
#define Module_FullVersion "0.15 (4.4.2.3)"
#define Module_HelpVersion "0.15 (15 Oct 2002) 4.4.2.3"
#define Module_LibraryVersionInfo "0:15"
......@@ -13,7 +13,7 @@
; limitations under the License.
;
; Public interface (ie interface to DMAManager) of a DMA HAL device
; Public interface (ie interface to DMAManager) of DMA HAL devices
GET hdr:HALDevice
......@@ -24,26 +24,33 @@ OldOpt SETA {OPT}
GBLL Included_Hdr_DMADevice
Included_Hdr_DMADevice SETL {TRUE}
; Device for each DMA controller
^ 0
# HALDeviceSize
HALDevice_DMACFeatures # 4
HALDevice_DMACEnumeratePhysical # 4
HALDevice_DMACEnumerateLogical # 4
HALDevice_DMACAllocate # 4
HALDevice_DMACDeallocate # 4
HALDevice_DMAC_Size * :INDEX: @
; Device for each physical DMA channel
^ 0
# HALDeviceSize
HALDevice_DMA_EnumeratePhysical # 4
HALDevice_DMA_EnumerateLogical # 4
HALDevice_DMA_Allocate # 4
HALDevice_DMA_Deallocate # 4
HALDevice_DMA_Features # 4
HALDevice_DMA_SetOptions # 4
HALDevice_DMA_SetCurrentTransfer # 4
HALDevice_DMA_SetNextTransfer # 4
HALDevice_DMA_TransferState # 4
HALDevice_DMA_SetCurrentTransfer2 # 4
HALDevice_DMA_SetNextTransfer2 # 4
HALDevice_DMA_TransferState2 # 4
HALDevice_DMA_IRQClear # 4
HALDevice_DMA_Status # 4
HALDevice_DMA_Enable # 4
HALDevice_DMA_Disable # 4
HALDevice_DMA_ChannelReset # 4
HALDeviceDMASize * :INDEX: @
HALDevice_DMAFeatures # 4
HALDevice_DMAAbort # 4
HALDevice_DMASetOptions # 4
HALDevice_DMASetCurrentTransfer # 4
HALDevice_DMASetNextTransfer # 4
HALDevice_DMATransferState # 4
HALDevice_DMASetCurrentTransfer2 # 4
HALDevice_DMASetNextTransfer2 # 4
HALDevice_DMATransferState2 # 4
HALDevice_DMAIRQClear # 4
HALDevice_DMAStatus # 4
HALDevice_DMA_Size * :INDEX: @
DMAFeaturesFlag_CantSuspend * 1 :SHL: 0 ; don't activate transfer until all of the scatter list is on safe pages
......@@ -59,8 +66,8 @@ DMASetOptionsFlag_NoClockSync * 1 :SHL: 14
DMASetTransferFlag_Stop * 1 :SHL: 0 ; raise TC after this transfer
DMAStatusFlag_Interrupting * 1 :SHL: 0 ; this channel is trying to generate an interrupt
DMAStatusFlag_Overrun * 1 :SHL: 1 ; this channel is over/underrunning
DMAStatusFlag_NoUnstarted * 1 :SHL: 0 ; there are no transfers programmed but not yet started
DMAStatusFlag_Overrun * 1 :SHL: 1 ; this channel has not yet been used, or is over/underrunning
DMAStatusFlag_EarlyOverrun * 1 :SHL: 2 ; this channel over/underran during the last interrupt routine
]
......
This diff is collapsed.
......@@ -53,6 +53,9 @@ $label
AddError DMA_ZeroLength, "E10"
AddError DMA_Terminated, "E11"
AddError DMA_BadHard, "E12"
[ HAL
AddError DMA_TransferTooLong, "E13"
]
|
^ ErrorBase_DMA
......@@ -71,6 +74,9 @@ $label
AddError DMA_ZeroLength, "DMA manager cannot accept zero length or sync gap"
AddError DMA_Terminated, "DMA transfer terminated"
AddError DMA_BadHard, "Hardware does not support IO DMA"
[ HAL
AdError DMA_TransferTooLong, "Transfer too long"
]
]
END
......@@ -59,6 +59,14 @@ vector_Start # 4 ; Called before a DMA is started
vector_Completed # 4 ; Called when a DMA is completed.
vector_DMASync # 4 ; Called after every N bytes transferred.
; Page table entry.
^ 0
ptab_Len # 4 ; Length of transfer in this page.
ptab_Logical # 4 ; Logical address to start transfer.
ptab_Physical # 4 ; Corresponding physical address.
PTABSize * :INDEX:@
; Physical channel DMA queue.
^ 0
dmaq_Head # 4 ; Pointer to DMA queue head.
......@@ -67,6 +75,9 @@ dmaq_Active # 4 ; Pointer to DMA active on physi
dmaq_LastBuff # 4 ; Pointer to last buffer programmed.
[ HAL ; extends structure to hold all our info pertaining to a physical channel
dmaq_DMADevice # 4 ; Pointer to HAL device for this physical channel.
dmaq_DeviceFeatures # 4 ; Flags word returned from Features call.
dmaq_Usage # 4 ; Number of logical channels using this physical channel.
dmaq_BounceBuff # PTABSize ; Fake page table entry pointing at bounce buffer.
# (((:INDEX:@)+3):AND::NOT:15)+12-(:INDEX:@)
dmaq_Trampoline # 48 ; Qword-aligned device vector trampoline for physical channel.
# (((:INDEX:@)+15):AND::NOT:15)-(:INDEX:@)
......@@ -146,7 +157,9 @@ block_Data # NoPhysicalChannels * DMARSize
BlockSize * :INDEX:@
[ :LNOT: HAL
[ HAL
BOUNCEBUFFSIZE * 1:SHL:16
|
IOMD_IOxCURA * IOMD_IO0CURA-IOMD_IO0CURA
IOMD_IOxENDA * IOMD_IO0ENDA-IOMD_IO0CURA
IOMD_IOxCURB * IOMD_IO0CURB-IOMD_IO0CURA
......@@ -158,14 +171,6 @@ IOMD_IOxST * IOMD_IO0ST-IOMD_IO0CURA
PAGESHIFT * 12
PAGESIZE * 1:SHL:PAGESHIFT
; Page table entry.
^ 0
ptab_Len # 4 ; Length of transfer in this page.
ptab_Logical # 4 ; Logical address to start transfer.
ptab_Physical # 4 ; Corresponding physical address.
PTABSize * :INDEX:@
ptabf_Unsafe * &80000000 ; Combined with ptab_Len to mark a page as unsafe.
Memory_LogicalGiven * 1:SHL:9 ; OS_Memory flags.
......
......@@ -43,6 +43,7 @@ Module_BaseAddr
GET Hdr:DDVMacros
[ HAL
GET Hdr:DMADevice
GET Hdr:PCI
]
GET VersionASM
......
......@@ -35,6 +35,9 @@ Title DCB "DMAManager",0
Helpstr DCB "DMAManager",9,"$Module_HelpVersion",0
ALIGN
; GET Hdr:Debug
; InsertDebugRoutines
ModFlags
[ No32bitCode
DCD 0
......@@ -101,16 +104,17 @@ InitWS
STR r0, ChannelBlock ; Don't free anything silly in error cases.
CallHAL DMA_EnumeratePhysical
TEQ r0, #0
MOVVS r1, #0
TEQ r1, #0
ADREQ r0, ErrorBlock_DMA_BadHard
PullEnv EQ
DoError EQ ; Error if hardware doesn't do DMA.
STR r0, DMADevices
STR r1, PhysicalChannels
ASSERT DMAQSize=80
ASSERT DMAQSize=96
MOV r0, #ModHandReason_Claim ; Create and initialise DMA queues.
MOV r3, r1, LSL #6
ADD r3, r3, r1, LSL #4
ADD r3, r3, r1, LSL #5
SWI XOS_Module
EXIT VS
STR r2, DMAQueues
......@@ -119,9 +123,9 @@ InitWS
STRCS r4, [r2, r3]
BCS %BT10
LDR r0, DMADevices
ASSERT DMAQSize=80
ASSERT DMAQSize=96
MOV r3, r1, LSL #6
ADD r3, r3, r1, LSL #4
ADD r3, r3, r1, LSL #5
SUB r3, r3, #DMAQSize - dmaq_DMADevice
15 LDR lr, [r0], #4
STR lr, [r2, r3]
......@@ -134,7 +138,7 @@ InitWS
ASSERT LCBSize=28
MOV r0, #ModHandReason_Claim ; Create and initialise logical channel table.
MOV r3, r1, LSL #5
SUB r3, r3, r3, LSL #2
SUB r3, r3, r1, LSL #2
SWI XOS_Module
EXIT VS
STR r2, ChannelBlock
......@@ -185,8 +189,27 @@ InitHardware
; Check for bad hardware now done in InitWS
LDR r4, DMADevices
LDR r5, PhysicalChannels
LDR r6, DMAQueues
Push "r12"
LDR r0, [r4]
MOV lr, pc
LDR pc, [r0, #HALDevice_Activate] ; activate controller (bodge)
Pull "r12"
01 LDR r0, [r4], #4
CallHAL Activate
CallHAL DMA_Features
STR r0, [r6, #dmaq_DeviceFeatures]
; SWI XOS_WriteS
; = "Found ", 0
; ALIGN
; LDR r0, [r4, #-4]
; LDR r0, [r0, #HALDevice_Description]
; SWI XOS_Write0
; SWI XOS_NewLine
ADD r6, r6, #DMAQSize
SUBS r5, r5, #1
BGT %BT01
|
......@@ -321,6 +344,8 @@ ReleaseVectors
[ HAL
Entry ; r0-r6 trashable
LDR r1, DMAQueues
TEQ r1, #0
EXIT EQ
ADD r1, r1, #dmaq_Trampoline
LDR r3, DMADevices
LDR r4, PhysicalChannels
......@@ -412,13 +437,11 @@ Service2
TEQ r1, #Service_PreReset
BNE %FT10
Entry "r0-r5"
LDR r4, DMADevices
LDR r5, PhysicalChannels
01 LDR r0, [r4], #4
CallHAL Reset
SUBS r5, r5, #1
BGT %BT01
Entry "r0-r3"
LDR r0, DMADevices
LDR r0, [r0] ; reset controller (bodge)
MOV lr, pc
LDR pc, [r0, #HALDevice_Reset]
EXIT
10
]
......@@ -477,11 +500,24 @@ ShutDown
[ HAL
LDR r4, DMADevices
LDR r5, PhysicalChannels
01 LDR r0, [r4], #4
CallHAL Deactivate
LDR r6, DMAQueues
TEQ r6, #0
BEQ %FT02
Push "r12"
LDR r0, [r4]
MOV lr, pc
LDR pc, [r0, #HALDevice_Deactivate] ; deactivate controller (bodge)
Pull "r12"
01 LDR r0, [r6, #dmaq_BounceBuff + ptab_Logical]
TEQ r0, #0
SWINE XPCI_RAMFree
ADD r6, r6, #DMAQSize
SUBS r5, r5, #1
BGT %BT01