DMAManager SWIs
---------------

SWI DMA_RegisterChannel extended as follows:
  R0 bits 0-3 = post-transfer channel delay
  R0 bit 4 set => disable burst transfers
  R0 bit 5 set => disable DMA request synchronisation to clock
  R6 = peripheral read/receive physical address
  R7 = peripheral write/transmit physical address
  (R6,R7 ignored for IOMD implementations and memory-to-memory DMA, but
  otherwise required for the 7200 and 9200)

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. (This has always been
the case.)

SWI DMA_AllocateLogicalChannels (SWI &46147)

On entry:  R0 = number of logical channels to allocate
On exit:   R0 = number of first logical channel (any others are in a contiguous
                range above this number)

Use:       Since DMA controllers can now be added by third parties, a method
           of allocating logical channel numbers is required - this is what
           this SWI achieves. The range of logical channels from &1000 upwards
           is reserved for dynamic allocation, and this SWI will allocate one
           or more channels from this range. The logical channels are
           guaranteed to be unique within this session, even across
           reinitialisations of the DMAManager.

Possible 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.



Three types of devices (see Kernel.Docs.HAL.NewAPI) are defined: the DMA
controller, and two types of 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; }
      (*Enumerate)(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 = &302.
  Address is not used.
  Controllers are activated/deactivated on module initialisation/finalisation.
  dev.Reset is called on software-initiated OS resets.
  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.

Enumerate
  Returns a static list of available physical DMA channel devices.

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 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 controller

  Recommended DMA priorities:
    High: sound DMA
    Medium: other device DMA
    Low: memory-to-memory DMA

Deallocate
  The partner of Allocate, this lets the device know that a particular
  logical channel is no longer being used.



DMA channel device - buffer type
--------------------------------

struct dmachannel
{
  /* Public interface */
  struct device dev;
  
  uint32_t (*Features)(struct dmachannel *);
  dmacontroller *controller;
  void (*Abort)(struct dmachannel *);
  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 (*IRQClear)(struct dmachannel *);
  uint32_t (*Status)(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 *);
}

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 = &303.
  Address is not used.
  Channels are activated/deactivated around each transfer (and are deactivated
  when a transfer is suspended).
  
  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 (but not on OS
  resets). After this call, Status must report an over/underrun condition.

Features
  Returns a flag word:
    all bits reserved, should be zero

controller
  A pointer to this channel's DMA controller device.

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)
    bits 1-5   => transfer unit width, typically 1, 2 or 4 bytes
                  (16 bytes can also be used for IOMD)
    bits 6-8   => cycle speed, on an arbitrary scale from 0-7
                  (0-3 maps to A-D cycles on IOMD)
    bits 9-12  => minimum delay required after a DMA transfer before the same
                  physical channel is reused (required for 7200 and 9200)
    bit 13 set => disable burst transfers for this channel
    bit 14 set => bypass synchronisation to clock before initiating transfer
                  (for synchronous peripherals - currently 7200 only)
    other bits reserved, value must be ignored
  address: sets the physical address of the peripheral to DMA to/from
    (should be ignored by memory-to-memory channels)

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
  flags bit 0 set => stop and raise the TC signal when this transfer completes
                     (mandatory if the DMA controller has no interrupt of
                     its own)
  address and length should be multiples of the transfer unit size, and must
  not cross a page boundary

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.

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.

IRQClear
  Clears the interrupt for this physical DMA channel (unless some other action
  carried out during or since TestIRQ has already done so).

Status
  Returns status bits for the current channel:
  bit 0 clr => there are transfers programmed that are waiting to begin
        set => there are no transfers programmed that are waiting to begin
               A double-buffered controller may exist as
               II: Both buffers are unprogrammed and idle, both transfers
                   have completed and an over/underrun condition exists
               AP/PA: One buffer is active doing a transfer, the other buffer
                   is programmed ready and waiting to begin
               AI/IA: One buffer is active doing a transfer, the other buffer
                   is unprogrammed and idle
               AA: Both buffers are active, this is of course impossible
               the II/AI/IA conditions set this flag bit since they are
               the only ones where there are no pending programmed transfers.
               For a single-buffered controller, any programmed channel must
               immediately transition to active. Since there is only one buffer
               it can never be in state 'P', so this flag bit is always set.
  bit 1 clr => no over/underrun condition exists
        set => channel has not yet been used, or is in over/underrun state
               Set when both buffers are idle for double-buffered DMA
               controllers.
               Set on every interrupt for single-buffered DMA controllers.
  bit 2 clr => channel does not need reprogramming via SetCurrentTransfer
        set => channel entered over/underrun state before last call to
               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
               would be expected from the last time the next buffer was
               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 SetCurrentTransfer. This bit must be clear if
               the last transfer was programmed using SetCurrentTransfer, or if
               bit 1 is clear.
  other bits reserved, should be zero.

SetCurrentTransfer2 / SetNextTransfer2 / TransferState2
  are same as above but with two address arguments and one length argument. For
  use in memory-to-memory transfers only.



DMA channel device - list type
------------------------------

struct dmalistchannel
{
  /* Public interface */
  struct device dev;
  
  struct dmalistchannelfeatures *(*Features)(struct dmachannel *);
  dmacontroller *controller;
  void (*Abort)(struct dmachannel *);
  void (*SetOptions)(struct dmachannel *, uint32_t flags, uint32_t address);
  void (*SetListTransfer)(struct dmachannel *, uint32_t blockphy,
      void *blocklog, uint32_t nentries, uint32_t length);
  uint32_t (*ListTransferProgress)(struct dmachannel *);
  uint32_t (*ListTransferStatus)(struct dmachannel *);
  uint32_t (*CurtailListTransfer)(struct dmachannel *, uint32_t minimum);
}

struct dmalistchannelfeatures
{
  uint32_t flags;
  uint32_t blocksize;
  uint32_t blockalign;
  uint32_t blockbound;
  uint32_t maxtransfers;
  uint32_t transferlimit;
  uint32_t transferbound;
}

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 = &304.
  Address is not used.
  Channels are activated/deactivated around each transfer (and are deactivated
  when a transfer is suspended).
  Interrupts are used to detect transfer termination, and to trigger sync
  callbacks. If the device provides no interrupt, the device is interrogated
  from a TickerV routine instead.

  dev.Activate, dev.Deactivate and dev.Reset are handled similarly to
  buffer-type DMA channel devices.

Features
  Returns a pointer to a static block describing the features of the channel.
  flags: a flags word with the following format:
    bit 1 set => channel does not support circular transfers
    bit 2 set => channel does not support infinite transfers
    bit 3 set => channel does not generate interrupts on completion of each
                 entry in the transfer descriptors block, so there is no
                 benefit to be gained by splitting descriptors at multiples of
                 the sync gap (sync gap splitting is done when this bit is
                 clear, to provide more accurate sync callbacks)
    other bits reserved, should be zero.
  blocksize: amount of memory to allocate for transfer descriptors block.
  blockalign: alignment of transfer descriptors block (0 if none).
  blocklimit: boundary limitation of transfer descriptors block (0 if none).
  maxtransfers: maximum number of entries to provide in the transfer
    descriptors block (note this may differ from blocksize/8 if postprocessing
    needs to be done before the block is presented to the hardware).
  transferlimit: maximum amount of data that can be transferred by a single
    entry in the transfer descriptors block.
  transferbound: boundary limitation of individual entries in the transfer
    descriptors block.

controller
  A pointer to this channel's DMA controller device.

Abort
  See equivalent entry for buffer-type DMA channel devices.

SetOptions
  See equivalent entry for buffer-type DMA channel devices, but note the
  following addition to the flags word:
    bit 15 set => transfer is circular

SetListTransfer
  Initiates the transfer of data according to a transfer descriptors block.
  blockphy: physical address of transfer descriptors block.
  blocklog: logical address of transfer descriptors block.
  nentries: number of entries in transfer descriptors block.
  length: total length to transfer, or 0 for infinite transfer.

ListTransferProgress
  Returns the number of bytes transferred since transfer was initiated. If this
  cannot be determined exactly, an underestimate should be returned.

ListTransferStatus
  Returns status flags for the current channel:
  bit 0 set => an error has occurred accessing memory (eg bus abort) since the
               transfer was initiated.
  bit 1 set => an error has occurred accessing the device (eg transfer size
               mismatch) since the transfer was initiated.
  other bits reserved, should be zero.

CurtailListTransfer
  This requests that a transfer in progress be shortened as much as possible
  subject to normal termination conditions (eg raising the TC line). The call
  need not block. For infinite transfers, there is a minimum requirement that
  this makes the transfer finite.
  minimum: the minimum number of bytes that must still be transferred (as
  returned from the DMASync callback).
  Returns the number of bytes that will have been transferred when the transfer
  terminates.



Memory barriers
---------------

DMAManager considers it to be the responsibility of the HAL device to ensure
that the correct data read/write barriers are used where appropriate. Typically
this means that:

* Whenever a memory -> device transfer is about to be started, a write barrier
  should be used to ensure that any buffered writes have reached RAM (pages
  involved in a DMA transfer will never be cacheable, but they will typically
  be bufferable).
* Whenever the progress of a device -> memory transfer is being reported (e.g.
  TransferState and Status calls), a read barrier should be used to ensure the
  CPU hasn't prefetched any stale data from the destination buffer.
* For list-based controllers, the transfer descriptor list will typically be
  located in bufferable memory, and so barriers will be needed whenever it is
  being accessed. E.g. a write barrier is needed after the list has been fully
  populated by the CPU, and if the DMA controller updates the descriptors with
  progress information then a read barrier should be used before the CPU reads
  from the descriptors.

The above covers situations where the HAL device should use barriers. The
following covers situations where the DMAManager client should use barriers:

* If DMA is being used to transfer code, an instruction synchronisation barrier
  (e.g. ARMv7 ISB instruction) may be required to ensure any stale prefetched
  instructions are flushed before the new code is executed. Calling
  OS_SynchroniseCodeAreas for the affected range is a safe and valid method of
  doing this. Other methods may fail on future systems (e.g. consider a DMA
  controller which is able to perform cache-coherent DMA to/from the CPU data
  cache, but not to the instruction cache).
* If the contents of a circular buffer are being updated on-the-fly by the CPU
  (e.g. audio playback as used by SoundDMA) then it is the responsibility of
  the client to ensure any freshly written data is flushed to RAM. However, in
  this case barriers are only likely to be needed in exceptional
  circumstances - e.g. if the CPU is repeatedly writing to the same location,
  and the hardware keeps merging the data into the write buffer but never
  flushing it to RAM.

For software running within RISC OS, OS_MMUControl 2 can be used to gain access
to barrier routines suitable for DMA. These routines aren't currently exposed
to the HAL, and so bespoke barrier routines will be required for code running
there.
