[PATCH] DMA : Added DMA API for Raspberrypi

Pavel Pisa ppisa4lists at pikron.com
Fri Aug 26 00:55:38 UTC 2016


Hello Mudit Jain,

I have tried to test the DMA API code

I have setup temporary branch on my RTEMS tests repo

  https://github.com/ppisa/rtems/tree/rtems-rpi-devel-dma-test

I have applied patches extracted from your GitHub repository

  Re: [PATCH] Mailbox : Extending functionality
  https://github.com/ppisa/rtems/commit/640071d0e16f0a29019e09c0308a364373b4b0e1

    with changes in alignment and padding which I have described
    in review

  [PATCH] DMA : Added DMA API for Raspberrypi
  https://github.com/ppisa/rtems/commit/d1f1ed30f918789b3ed91771ab743c7a1e6bf649

    I have added missing defines to
      c/src/lib/libbsp/arm/raspberrypi/include/irq.h
    which has been probably lost during your rebasing to master.
    Without these code cannot compile.

My patches

  arm/raspberrypi: DMA API: correct obvious errors 
  https://github.com/ppisa/rtems/commit/ec34b48b9b87462276f93ab07f7522b84c1819bd

    Correct your code to at least initialization call suceed

  arm/raspberrypi: DMA API: ensure that channel is enabled when it is configured.
  https://github.com/ppisa/rtems/commit/ec34b48b9b87462276f93ab07f7522b84c1819bd

     Because even after above correction I have not observed channel activity,
     I have added code to ensure that channel is enabled in a global enable register      

  arm/raspberrypi: DMA API: add simple memory to memory transfer test
  https://github.com/ppisa/rtems/commit/6c7d9463c83ee2d084a88235dbdaabd32b60b1a0

    Quite dirty test integrated into RTEMS samples ticker application
    to check if DMA API works.

System does not crash/stuck during testing, operations returns success status
but even after more ticks there data do not appear in destination position
albeit DMA request 0 should be permanently assigned according to the manual
so data should be copied.
    
rpi_dma_init ...
rpi_dma_init 0
rpi_dma_allocate ...
rpi_dma_allocate 0
rpi_dma_setup_src ...
rpi_dma_setup_src 0
rpi_dma_setup_dst ...
rpi_dma_setup_dst 0
rpi_dma_setup_intr ...
rpi_dma_setup_intr 0
rpi_dma_start ...
rpi_dma_start 0

dma test result
11>00 22>00 33>00 44>00 55>00 66>00 77>00 88>00 
simple mem2mem DMA test has FAILED

It is important that at least some functional tests
run to prove that code development heads in the right
direction.

Please, check if I use API intended way and try to help us
to make code more acceptable for mainline. We need to
declare the state for project evaluation forms.

See see remarks inlined in the following DMA patch


On Saturday 20 of August 2016 17:33:18 Mudit Jain wrote:
> Brief API Description
>
>  rpi_dma_init -> Initializes the channel structure
>  and its members for that particular channel, gets
>  the physical address of the control block structure
>  and installs the interrupt handler.
>
>  rpi_dma_allocate -> Allocates the channel if it is
>  not busy
>
>  rpi_dma_setup_dst & rpi_dma_setup_src -> These setup
>  src and dst parameters like dreq, width. Basically they
>  configure the info section of the control block structure
>  of that particular channel.
>
>  rpi_dma_setup_intr -> These will assign the handler
>  function for channel interrupt. This basically again
>  updates the channel structure with that function.
>  Note : rpi_dma_intr is the callback function for the
>  interrupt controller and calls this function internally.
>
>  rpi_dma_start -> Here you provide the virtual address,
>  the channel to be used and the width. Internally, it
>  converts the address to physical address and configures
>  the control block for that channel.
>  It flushes and invalidates the cache and writes the physical
>  address of the control block to a particular register
>  corresponding to the channel.
>
>  rpi_dma_free -> Frees the channel
> ---
>  c/src/lib/libbsp/arm/raspberrypi/Makefile.am   |   4 +
>  c/src/lib/libbsp/arm/raspberrypi/include/dma.h | 200 ++++++++
>  c/src/lib/libbsp/arm/raspberrypi/misc/dma.c    | 618
> +++++++++++++++++++++++++ 3 files changed, 822 insertions(+)
>  create mode 100644 c/src/lib/libbsp/arm/raspberrypi/include/dma.h
>  create mode 100644 c/src/lib/libbsp/arm/raspberrypi/misc/dma.c
>
> diff --git a/c/src/lib/libbsp/arm/raspberrypi/Makefile.am
> b/c/src/lib/libbsp/arm/raspberrypi/Makefile.am index 4b111ad..5b0e92e
> 100644
> --- a/c/src/lib/libbsp/arm/raspberrypi/Makefile.am
> +++ b/c/src/lib/libbsp/arm/raspberrypi/Makefile.am
> @@ -51,6 +51,7 @@ include_bsp_HEADERS += include/rpi-gpio.h
>  include_bsp_HEADERS += include/i2c.h
>  include_bsp_HEADERS += include/spi.h
>  include_bsp_HEADERS += include/mailbox.h
> +include_bsp_HEADERS += include/dma.h
>  include_bsp_HEADERS += include/vc.h
>  include_bsp_HEADERS += include/rpi-fb.h
>  include_bsp_HEADERS += console/fbcons.h
> @@ -124,6 +125,9 @@ libbsp_a_SOURCES += console/fb.c
>  libbsp_a_SOURCES += console/fbcons.c
>  libbsp_a_SOURCES += console/outch.c
>
> +# DMA
> +libbsp_a_SOURCES += misc/dma.c
> +
>  # Mailbox
>  libbsp_a_SOURCES += misc/mailbox.c
>
> diff --git a/c/src/lib/libbsp/arm/raspberrypi/include/dma.h
> b/c/src/lib/libbsp/arm/raspberrypi/include/dma.h new file mode 100644
> index 0000000..c49a575
> --- /dev/null
> +++ b/c/src/lib/libbsp/arm/raspberrypi/include/dma.h
> @@ -0,0 +1,200 @@
> +/**
> + * @file
> + *
> + * @ingroup raspberrypi_dma
> + *
> + * @brief Direct memory access (DMA) support.
> + */
> +
> +#ifndef LIBBSP_ARM_RASPBERRYPI_DMA_H
> +#define LIBBSP_ARM_RASPBERRYPI_DMA_H
> +
> +#define BCM_DMA_BLOCK_SIZE 512
> +
> +/* DMA0-DMA15 but DMA15 is special */
> +#define BCM_DMA_CH_MAX 12
> +
> +/* request CH for any nubmer */
> +#define BCM_DMA_CH_INVALID ( -1 )
> +#define BCM_DMA_CH_ANY ( -1 )
> +
> +/* Peripheral DREQ Signals (4.2.1.3) */
> +#define BCM_DMA_DREQ_NONE 0
> +#define BCM_DMA_DREQ_EMMC 11
> +#define BCM_DMA_DREQ_SDHOST 13
> +
> +#define BCM_DMA_SAME_ADDR 0
> +#define BCM_DMA_INC_ADDR 1
> +
> +#define BCM_DMA_32BIT 0
> +#define BCM_DMA_128BIT 1
> +
> +/*
> + * Defines for converting physical address to VideoCore bus address and
> back + */
> +#define BCM2835_VCBUS_SDRAM_CACHED 0x40000000
> +#define BCM2835_VCBUS_IO_BASE 0x7E000000
> +#define BCM2835_VCBUS_SDRAM_UNCACHED 0xC0000000
> +
> +#ifdef SOC_BCM2836
> +#define BCM2835_VCBUS_SDRAM_BASE BCM2835_VCBUS_SDRAM_UNCACHED
> +#else
> +#define BCM2835_VCBUS_SDRAM_BASE BCM2835_VCBUS_SDRAM_CACHED
> +#endif
> +#define BCM2835_ARM_IO_SIZE 0x01000000
> +
> +/*
> + * Convert physical address to VC bus address. Should be used
> + * when submitting address over mailbox interface
> + */
> +#define PHYS_TO_VCBUS( pa ) ( ( pa ) + BCM2835_VCBUS_SDRAM_BASE )
> +
> +/* Check whether pa bellong top IO window */
> +#define BCM2835_ARM_IS_IO( pa ) ( ( ( pa ) >= RPI_PERIPHERAL_BASE ) && \
> +                                  ( ( pa ) < RPI_PERIPHERAL_BASE + \
> +                                    BCM2835_ARM_IO_SIZE ) )
> +
> +/*
> + * Convert physical address in IO space to VC bus address.
> + */
> +#define IO_TO_VCBUS( pa ) ( ( ( pa ) - RPI_PERIPHERAL_BASE ) + \
> +                            BCM2835_VCBUS_IO_BASE )
> +
> +/*
> + * Convert address from VC bus space to physical. Should be used
> + * when address is returned by VC over mailbox interface. e.g.
> + * framebuffer base
> + */
> +#define VCBUS_TO_PHYS( vca ) ( ( vca ) & ~( BCM2835_VCBUS_SDRAM_BASE ) )
> +
> +struct bus_dmamap {
> +  void *buffer_begin;
> +  uint32_t buffer_size;
> +};
> +
> +typedef struct bus_dmamap *bus_dmamap_t;
> +
> +/*
> + *	bus_dma_segment_t
> + *
> + *	Describes a single contiguous DMA transaction.  Values
> + *	are suitable for programming into DMA registers.
> + */
> +typedef struct bus_dma_segment {
> +  unsigned int ds_addr;         /* DMA address */
> +  unsigned int ds_len;          /* length of transfer */
> +} bus_dma_segment_t;
> +
> +/* DMA Control Block - 256bit aligned (p.40) */
> +struct bcm_dma_cb {
> +  uint32_t info;                /* Transfer Information */
> +  uint32_t src;                 /* Source Address */
> +  uint32_t dst;                 /* Destination Address */
> +  uint32_t len;                 /* Transfer Length */
> +  uint32_t stride;              /* 2D Mode Stride */
> +  uint32_t next;                /* Next Control Block Address */
> +  uint32_t rsvd1;               /* Reserved */
> +  uint32_t rsvd2;               /* Reserved */
> +};

Quite much of internal functionality and implementation is exposed
to public API (but not critical for now)

> +/* DMA Channel Structure */
> +struct bcm_dma_ch {
> +  int ch;
> +  uint32_t flags;
> +  struct bcm_dma_cb *cb;
> +  uint32_t vc_cb;
> +  bus_dmamap_t dma_map;
> +  void ( *intr_func )(
> +    int,
> +    void *
> +  );
> +  void *intr_arg;
> +  Atomic_Flag dma_lock;
> +};
> +
> +/* API */
> +
> +/*
> + * Allocate DMA channel for further use.
> + */
> +rtems_status_code rpi_dma_allocate( int req_ch );
> +
> +/*
> + * Frees allocated channel.
> + */
> +rtems_status_code rpi_dma_free( int ch );
> +
> +/*
> + * Assign handler function for channel interrupt
> + */
> +rtems_status_code rpi_dma_setup_intr( int ch, void ( *func )(
> +    int,
> +    void *
> +  ), void *arg );
> +
> +/*
> + * Setup DMA source parameters
> + *     ch - channel number
> + *     dreq - hardware DREQ # or BCM_DMA_DREQ_NONE if
> + *         source is physical memory
> + *     inc_addr - BCM_DMA_INC_ADDR if source address
> + *         should be increased after each access or
> + *         BCM_DMA_SAME_ADDR if address should remain
> + *         the same
> + *     width - size of read operation, BCM_DMA_32BIT
> + *         for 32bit bursts, BCM_DMA_128BIT for 128 bits
> + */
> +rtems_status_code rpi_dma_setup_src(
> +  int ch,
> +  int dreq,
> +  int inc_addr,
> +  int width
> +);
> +
> +/*
> + * Setup DMA destination parameters
> + *     ch - channel number
> + *     dreq - hardware DREQ # or BCM_DMA_DREQ_NONE if
> + *         destination is physical memory
> + *     inc_addr - BCM_DMA_INC_ADDR if source address
> + *         should be increased after each access or
> + *         BCM_DMA_SAME_ADDR if address should remain
> + *         the same
> + *     width - size of write operation, BCM_DMA_32BIT
> + *         for 32bit bursts, BCM_DMA_128BIT for 128 bits
> + */
> +rtems_status_code rpi_dma_setup_dst(
> +  int ch,
> +  int dreq,
> +  int inc_addr,
> +  int width
> +);
> +
> +/*
> + * Start DMA transaction
> + *     ch - channel number
> + *     src, dst - source and destination address in
> + *         ARM physical memory address space.
> + *     len - amount of bytes to be transferred
> + */
> +rtems_status_code rpi_dma_start(
> +  int        ch,
> +  vm_paddr_t src,
> +  vm_paddr_t dst,
> +  int        len
> +);
> +
> +/*
> + * Initializes the DMA
> + */
> +int rpi_dma_init( int ch );
> +
> +/*
> + * Get length requested for DMA transaction
> + *     ch - channel number
> + */
> +uint32_t rpi_dma_length( int ch );
> +
> +static void rpi_dma_intr( void *arg );
The static internal only function in public header
leads to warning if header is included in another source/API user.
If declaration is required due to functions order in C file
then add static declaration at start of that file.

> +#endif /* LIBBSP_ARM_RASPBERRYPI_DMA_H */
> diff --git a/c/src/lib/libbsp/arm/raspberrypi/misc/dma.c
> b/c/src/lib/libbsp/arm/raspberrypi/misc/dma.c new file mode 100644
> index 0000000..4fbf73b
> --- /dev/null
> +++ b/c/src/lib/libbsp/arm/raspberrypi/misc/dma.c
> @@ -0,0 +1,618 @@
> +/**
> + * @file
> + *
> + * @ingroup rpi_dma
> + *
> + * @brief Direct memory access (DMA) support.
> + */
> +
> +#include <bsp.h>
> +#include <bsp/raspberrypi.h>
> +#include <bsp/dma.h>
> +#include <bsp/irq.h>
> +#include <stdlib.h>
> +#include <errno.h>
> +
> +/* private flags */
> +#define BCM_DMA_CH_USED 0x00000001
> +#define BCM_DMA_CH_FREE 0x40000000
> +#define BCM_DMA_CH_UNMAP 0x80000000
> +
> +/* Register Map (4.2.1.2) */
> +#define BCM_DMA_CS( n ) ( 0x100 * ( n ) + 0x00 )
> +/* Different States */
> +#define         CS_ACTIVE ( 1 << 0 )
> +#define         CS_END ( 1 << 1 )
> +#define         CS_INT ( 1 << 2 )
> +#define         CS_DREQ ( 1 << 3 )
> +#define         CS_ISPAUSED ( 1 << 4 )
> +#define         CS_ISHELD ( 1 << 5 )
> +#define         CS_ISWAIT ( 1 << 6 )
> +#define         CS_ERR ( 1 << 8 )
> +#define         CS_WAITWRT ( 1 << 28 )
> +#define         CS_DISDBG ( 1 << 29 )
> +#define         CS_ABORT ( 1 << 30 )
> +#define         CS_RESET ( 1U << 31 )
> +#define BCM_DMA_CBADDR( n ) ( 0x100 * ( n ) + 0x04 )
> +#define BCM_DMA_INFO( n ) ( 0x100 * ( n ) + 0x08 )
> +#define         INFO_INT_EN ( 1 << 0 )
> +#define         INFO_TDMODE ( 1 << 1 )
> +#define         INFO_WAIT_RESP ( 1 << 3 )
> +#define         INFO_D_INC ( 1 << 4 )
> +#define         INFO_D_WIDTH ( 1 << 5 )
> +#define         INFO_D_DREQ ( 1 << 6 )
> +#define         INFO_S_INC ( 1 << 8 )
> +#define         INFO_S_WIDTH ( 1 << 9 )
> +#define         INFO_S_DREQ ( 1 << 10 )
> +#define         INFO_WAITS_SHIFT ( 21 )
> +#define         INFO_PERMAP_SHIFT ( 16 )
> +#define         INFO_PERMAP_MASK ( 0x1f << INFO_PERMAP_SHIFT )
> +
> +#define BCM_DMA_SRC( n ) ( 0x100 * ( n ) + 0x0C )
> +#define BCM_DMA_DST( n ) ( 0x100 * ( n ) + 0x10 )
> +#define BCM_DMA_LEN( n ) ( 0x100 * ( n ) + 0x14 )
> +#define BCM_DMA_STRIDE( n ) ( 0x100 * ( n ) + 0x18 )
> +#define BCM_DMA_CBNEXT( n ) ( 0x100 * ( n ) + 0x1C )
> +#define BCM_DMA_DEBUG( n ) ( 0x100 * ( n ) + 0x20 )
> +#define         DEBUG_ERROR_MASK ( 7 )
> +
> +#define BCM_DMA_INT_STATUS 0xfe0
> +#define BCM_DMA_ENABLE 0xff0
> +
> +#define BCM_PAGE_SIZE 4096
> +#define BCM_PAGE_MASK ( BCM_PAGE_SIZE - 1 )
> +/**
> + * @brief Table that indicates if a channel is currently occupied.
> + */
> +static struct bcm_dma_ch bcm_dma_ch[ BCM_DMA_CH_MAX ];
> +
> +rtems_status_code rpi_dma_reset( int ch );
> +
> +int bus_dmamap_load_buffer(
> +  bus_dma_segment_t segs[],
> +  void             *buf,
> +  unsigned int      buflen,
> +  int               flags,
> +  vm_offset_t      *lastaddrp,
> +  int              *segp,
> +  int               first
> +);
> +
> +static void rpi_dmamap_cb(
> +  void              *arg,
> +  bus_dma_segment_t *segs,
> +  int                nseg,
> +  int                err
> +)
> +{
> +  unsigned int *addr;
> +
> +  if ( err )
> +    return;
> +
> +  addr = (unsigned int *) arg;
> +  *addr = PHYS_TO_VCBUS( segs[ 0 ].ds_addr );
> +}
> +
> +rtems_status_code rpi_dma_reset( int ch )
> +{
> +  struct bcm_dma_cb *cb;
> +  uint32_t           cs; /* Current State */
> +  int                count;
> +
> +  /* Check the channel number provided */
> +  if ( ch < 0 || ch >= BCM_DMA_CH_MAX )
> +    return ( RTEMS_INVALID_ID );
> +
> +  /* Current State of the channel */
> +  cs = BCM2835_REG( BCM_DMA_CS( ch ) );
> +
> +  if ( cs & CS_ACTIVE ) {
> +    /* Pause current task */
> +    BCM2835_REG( BCM_DMA_CS( ch ) ) = 0;
> +
> +    /* Wait */
> +    count = 1000;
> +
> +    do {
> +      cs = BCM2835_REG( BCM_DMA_CS( ch ) );
> +    } while ( !( cs & CS_ISPAUSED ) && ( count-- > 0 ) );
> +
> +    if ( !( cs & CS_ISPAUSED ) ) {
> +      return ( RTEMS_RESOURCE_IN_USE );
> +    }
> +
> +    /* Clear the next control block address */
> +    BCM2835_REG( BCM_DMA_CBNEXT( ch ) ) = 0;
> +
> +    /* Complete everything, clear interrupt */
> +    BCM2835_REG( BCM_DMA_CS( ch ) ) = CS_ABORT | CS_INT | CS_END |
> CS_ACTIVE; +  }
> +
> +  /* Clear control blocks */
> +  BCM2835_REG( BCM_DMA_CBADDR( ch ) ) = 0;
> +  BCM2835_REG( BCM_DMA_CBNEXT( ch ) ) = 0;
> +
> +  /* Reset control block */
> +  cb = bcm_dma_ch[ ch ].cb;
> +  bzero( cb, sizeof( *cb ) );
> +  cb->info = INFO_WAIT_RESP;
> +
> +  return RTEMS_SUCCESSFUL;
> +}
> +
> +/*
> + * Allocate DMA channel for further use
> + */
> +rtems_status_code rpi_dma_allocate( int req_ch )
> +{
> +  /* Check the channel number provided */
> +  if ( req_ch >= BCM_DMA_CH_MAX )
> +    return ( RTEMS_INVALID_ID );
> +
> +  if ( _Atomic_Flag_test_and_set( &bcm_dma_ch[ req_ch ].dma_lock,
> +         ATOMIC_ORDER_ACQUIRE ) != 0 ) {
> +    return RTEMS_RESOURCE_IN_USE;
> +  }
> +
> +  /* Check whether a channel is in use or not and allocate accordingly */
> +  if ( bcm_dma_ch[ req_ch ].flags & BCM_DMA_CH_FREE ) {
> +    bcm_dma_ch[ req_ch ].flags &= ~BCM_DMA_CH_FREE;
> +    bcm_dma_ch[ req_ch ].flags |= BCM_DMA_CH_USED;

May it be too complex flags, only one flag could be enough.
BCM_DMA_CH_FREE is probably redundant. But no problem for now.

> +  } else {
> +    return ( RTEMS_RESOURCE_IN_USE );
> +  }
> +
> +  _Atomic_Flag_clear( &bcm_dma_ch[ req_ch ].dma_lock, ATOMIC_ORDER_RELEASE
> ); +
> +  return ( RTEMS_SUCCESSFUL );
> +}
> +
> +/*
> + * Frees allocated channel. Returns 0 on success, -1 otherwise
> + */
> +rtems_status_code rpi_dma_free( int ch )
> +{
> +  rtems_status_code status_code = RTEMS_SUCCESSFUL;
> +
> +  if ( _Atomic_Flag_test_and_set( &bcm_dma_ch[ ch ].dma_lock,
> +         ATOMIC_ORDER_ACQUIRE ) != 0 ) {
> +    return RTEMS_RESOURCE_IN_USE;
> +  }
> +
> +  /* Check the channel number provided */
> +  if ( ch < 0 || ch >= BCM_DMA_CH_MAX )
> +    return ( RTEMS_INVALID_ID );
> +
> +  /* Check whether the channel is in use or not and free accordingly */
> +  if ( bcm_dma_ch[ ch ].flags & BCM_DMA_CH_USED ) {
> +    bcm_dma_ch[ ch ].flags |= BCM_DMA_CH_FREE;
> +    bcm_dma_ch[ ch ].flags &= ~BCM_DMA_CH_USED;
> +    bcm_dma_ch[ ch ].intr_func = NULL;
> +    bcm_dma_ch[ ch ].intr_arg = NULL;
> +
> +    /* Reset DMA */
> +    status_code = rpi_dma_reset( ch );
> +  }
> +
> +  status_code = rtems_interrupt_handler_remove( BCM2835_IRQ_ID_DMA_CH0 +
> ch, +    rpi_dma_intr,
> +    NULL );
> +
> +  _Atomic_Flag_clear( &bcm_dma_ch[ ch ].dma_lock, ATOMIC_ORDER_RELEASE );
> +
> +  return ( status_code );
> +}
> +
> +/*
> + * Assign handler function for channel interrupt
> + */
> +rtems_status_code rpi_dma_setup_intr(
> +  int   ch,
> +  void  ( *func )(
> +    int,
> +    void *
> +  ),
> +  void *arg
> +)
> +{
> +  struct bcm_dma_cb *cb;
> +
> +  /* Check the channel number provided */
> +  if ( ch < 0 || ch > -BCM_DMA_CH_MAX )

The condition is permanently false due to superfluous minus sign.

> +    return ( RTEMS_INVALID_ID );
> +
> +  /* Check whether the channel is in use */
> +  if ( !( bcm_dma_ch[ ch ].flags & BCM_DMA_CH_USED ) )
> +    return ( RTEMS_RESOURCE_IN_USE );
The error code RTEMS_RESOURCE_IN_USE is not intuitive
in this place. It should be something like, you are attempting
to access channel which is not initialized/allocated
If RTEMS status codes are used then

  RTEMS_INCORRECT_STATE

is more appropriate.


> +  /* Update the software context */
> +  bcm_dma_ch[ ch ].intr_func = func;
> +  bcm_dma_ch[ ch ].intr_arg = arg;
> +  cb = bcm_dma_ch[ ch ].cb;
> +  cb->info |= INFO_INT_EN;
> +
> +  return ( RTEMS_SUCCESSFUL );
> +}
> +
> +/*
> + * Setup DMA source parameters
> + *     ch - channel number
> + *     dreq - hardware DREQ # or BCM_DMA_DREQ_NONE if
> + *         source is physical memory
> + *     inc_addr - BCM_DMA_INC_ADDR if source address
> + *         should be increased after each access or
> + *         BCM_DMA_SAME_ADDR if address should remain
> + *         the same
> + *     width - size of read operation, BCM_DMA_32BIT
> + *         for 32bit bursts, BCM_DMA_128BIT for 128 bits
> + *
> + */
> +rtems_status_code rpi_dma_setup_src(
> +  int ch,
> +  int dreq,
> +  int inc_addr,
> +  int width
> +)
> +{
> +  uint32_t info;
> +
> +  /* Check the channel number provided */
> +  if ( ch < 0 || ch >= BCM_DMA_CH_MAX )
> +    return ( RTEMS_INVALID_ID );
> +
> +  /* Check whether the channel is in use */
> +  if ( !( bcm_dma_ch[ ch ].flags & BCM_DMA_CH_USED ) )
> +    return ( RTEMS_RESOURCE_IN_USE );
> +
> +  /* Configure the info field */
> +  info = bcm_dma_ch[ ch ].cb->info;
> +  info &= ~INFO_PERMAP_MASK;
> +  info |= ( dreq << INFO_PERMAP_SHIFT ) & INFO_PERMAP_MASK;
> +
> +  if ( dreq )
> +    info |= INFO_S_DREQ;
> +  else
> +    info &= ~INFO_S_DREQ;
> +
> +  if ( width == BCM_DMA_128BIT )
> +    info |= INFO_S_WIDTH;
> +  else
> +    info &= ~INFO_S_WIDTH;
> +
> +  if ( inc_addr == BCM_DMA_INC_ADDR )
> +    info |= INFO_S_INC;
> +  else
> +    info &= ~INFO_S_INC;
> +
> +  /* Update the info field in the CB struture of the given channel */
> +  bcm_dma_ch[ ch ].cb->info = info;
> +
> +  return ( RTEMS_SUCCESSFUL );
> +}
> +
> +/*
> + * Setup DMA destination parameters
> + *     ch - channel number
> + *     dreq - hardware DREQ # or BCM_DMA_DREQ_NONE if
> + *         destination is physical memory
> + *     inc_addr - BCM_DMA_INC_ADDR if source address
> + *         should be increased after each access or
> + *         BCM_DMA_SAME_ADDR if address should remain
> + *         the same
> + *     width - size of write operation, BCM_DMA_32BIT
> + *         for 32bit bursts, BCM_DMA_128BIT for 128 bits
> + */
> +rtems_status_code rpi_dma_setup_dst(
> +  int ch,
> +  int dreq,
> +  int inc_addr,
> +  int width
> +)
> +{
> +  uint32_t info;
> +
> +  /* Check the channel number provided */
> +  if ( ch < 0 || ch >= BCM_DMA_CH_MAX )
> +    return ( RTEMS_INVALID_ID );
> +
> +  /* Check whether the channel is in use */
> +  if ( !( bcm_dma_ch[ ch ].flags & BCM_DMA_CH_USED ) )
> +    return ( RTEMS_RESOURCE_IN_USE );
> +
> +  /* Configure the info field */
> +  info = bcm_dma_ch[ ch ].cb->info;
> +  info &= ~INFO_PERMAP_MASK;
> +  info |= ( dreq << INFO_PERMAP_SHIFT ) & INFO_PERMAP_MASK;
> +
> +  if ( dreq )
> +    info |= INFO_D_DREQ;
> +  else
> +    info &= ~INFO_D_DREQ;
> +
> +  if ( width == BCM_DMA_128BIT )
> +    info |= INFO_D_WIDTH;
> +  else
> +    info &= ~INFO_D_WIDTH;
> +
> +  if ( inc_addr == BCM_DMA_INC_ADDR )
> +    info |= INFO_D_INC;
> +  else
> +    info &= ~INFO_D_INC;
> +
> +  bcm_dma_ch[ ch ].cb->info = info;
> +
> +  return RTEMS_SUCCESSFUL;
> +}
> +
> +/*
> + * Start DMA transaction
> + *     ch - channel number
> + *     src, dst - source and destination address in
> + *         ARM physical memory address space.
> + *     len - amount of bytes to be transferred
> + */
> +rtems_status_code rpi_dma_start(
> +  int        ch,
> +  vm_paddr_t src,
> +  vm_paddr_t dst,
> +  int        len
> +)
> +{
> +  struct bcm_dma_cb *cb;
> +
> +  /* Check the channel number provided */
> +  if ( ch < 0 || ch >= BCM_DMA_CH_MAX )
> +    return RTEMS_INVALID_ID;
> +
> +  /* Check whether the channel is in use */
> +  if ( !( bcm_dma_ch[ ch ].flags & BCM_DMA_CH_USED ) )
> +    return RTEMS_RESOURCE_IN_USE;
> +
> +  cb = bcm_dma_ch[ ch ].cb;
> +
> +  /*
> +   *  Checking whether the pointer address belongs to the
> +   *  top IO window. If it does converting physical address
> +   *  in IO space to VC bus address. If it does not then
> +   *  convert physical address to VC bus address.
> +   */
> +  if ( BCM2835_ARM_IS_IO( src ) )
> +    cb->src = IO_TO_VCBUS( src );
> +  else
> +    /* While submitting address over mailbox interface */
> +    cb->src = PHYS_TO_VCBUS( src );
> +
> +  if ( BCM2835_ARM_IS_IO( dst ) )
> +    cb->dst = IO_TO_VCBUS( src );
> +  else
> +    cb->dst = PHYS_TO_VCBUS( dst );
> +
> +  cb->len = len;
> +
> +  /* Cache coherency */
> +  rtems_cache_flush_multiple_data_lines(
> +    (void *) bcm_dma_ch[ ch ].dma_map->buffer_begin,
> +    bcm_dma_ch[ ch ].dma_map->buffer_size );
> +
> +  rtems_cache_invalidate_multiple_data_lines(
> +    (void *) bcm_dma_ch[ ch ].dma_map->buffer_begin,
> +    bcm_dma_ch[ ch ].dma_map->buffer_size );
> +
> +  /* Write the physical address of the control block into the register */
> +  BCM2835_REG( BCM_DMA_CBADDR( ch ) ) = bcm_dma_ch[ ch ].vc_cb;
> +
> +  /* Change the state of the channel */
> +  BCM2835_REG( BCM_DMA_CS( ch ) ) = CS_ACTIVE;
> +
> +  return RTEMS_SUCCESSFUL;
> +}
> +
> +/*
> + * Get length requested for DMA transaction
> + *     ch - channel number
> + *
> + * Returns size of transaction, 0 if channel is invalid
> + */
> +uint32_t rpi_dma_length( int ch )
> +{
> +  struct bcm_dma_cb *cb;
> +
> +  /* Check the channel number provided */
> +  if ( ch < 0 || ch >= BCM_DMA_CH_MAX )
> +    return RTEMS_INVALID_ID;
> +
> +  /* Check whether the channel is in use */
> +  if ( !( bcm_dma_ch[ ch ].flags & BCM_DMA_CH_USED ) )
> +    return RTEMS_RESOURCE_IN_USE;
> +
> +  cb = bcm_dma_ch[ ch ].cb;
> +
> +  return ( cb->len );
> +}
> +
> +/* Utility function for rpi_dma_init */
> +int bus_dmamap_load_buffer(
> +  bus_dma_segment_t segs[],
> +  void             *buf,
> +  unsigned int      buflen,
> +  int               flags,
> +  vm_offset_t      *lastaddrp,
> +  int              *segp,
> +  int               first
> +)
> +{
> +  unsigned int sgsize;
> +  unsigned int curaddr, lastaddr, bmask;
> +  vm_offset_t  vaddr = (vm_offset_t) buf;
> +  int          seg;
> +
> +  lastaddr = *lastaddrp;
> +  bmask = ~( -1 );
> +
> +  for ( seg = *segp; buflen > 0; ) {
> +    /*
> +     * Get the physical address for this segment.
> +     */
> +    curaddr = vaddr;
> +
> +    /*
> +     * Compute the segment size, and adjust counts.
> +     */
> +    sgsize = BCM_PAGE_SIZE - ( (u_long) curaddr & BCM_PAGE_MASK );
> +
> +    if ( sgsize > sizeof( struct bcm_dma_cb ) )
> +      sgsize = sizeof( struct bcm_dma_cb );
> +
> +    if ( buflen < sgsize )
> +      sgsize = buflen;
> +
> +    /*
> +     * Insert chunk into a segment, coalescing with
> +     * the previous segment if possible.
> +     */
> +    if ( first ) {
> +      segs[ seg ].ds_addr = curaddr;
> +      segs[ seg ].ds_len = sgsize;
> +      first = 0;
> +    } else {
> +      if ( curaddr == lastaddr &&
> +           ( segs[ seg ].ds_len + sgsize ) <= sizeof( struct bcm_dma_cb )
> && +           ( segs[ seg ].ds_addr & bmask ) == ( curaddr & bmask ) ) +  
>      segs[ seg ].ds_len += sgsize;
> +      else {
> +        if ( ++seg >= 1 )
> +          break;
> +
> +        segs[ seg ].ds_addr = curaddr;
> +        segs[ seg ].ds_len = sgsize;
> +      }
> +    }
> +
> +    lastaddr = curaddr + sgsize;
> +    vaddr += sgsize;
> +    buflen -= sgsize;
> +  }
> +
> +  *segp = seg;
> +  *lastaddrp = lastaddr;
> +
> +  return ( buflen != 0 ? 27 : 0 );
> +}
> +
> +int rpi_dma_init( int channel )
> +{
> +  struct bcm_dma_ch *ch;
> +  void              *cb_virt;
> +  vm_paddr_t         cb_phys = 0;
> +  int                error, nsegs;
> +  vm_offset_t        lastaddr;
> +  bus_dma_segment_t  dm_segments[ 1 ];
> +
> +  /* Setup Initial Setting */
> +
> +  /* Check the channel number provided */
> +  if ( channel < 0 || channel >= BCM_DMA_CH_MAX )
> +    return ( RTEMS_INVALID_ID );
> +
> +  /* Check whether the channel is in use */
> +  if ( !( bcm_dma_ch[ channel ].flags & BCM_DMA_CH_USED ) )
> +    return ( RTEMS_RESOURCE_IN_USE );

This condition is probably inverted. It prevents initialization
by rpi_dma_init() to succeed before rpi_dma_allocate().
But order rpi_dma_init() the first and  rpi_dma_allocate()
next is in your API documentation. It can be vice versa
but in such case repeating rpi_dma_init call would leak
resources which are not released by rpi_dma_free().

> +
> +  ch = &bcm_dma_ch[ channel ];
> +
> +  bzero( ch, sizeof( struct bcm_dma_ch ) );
> +  ch->ch = channel;
> +  ch->flags = BCM_DMA_CH_UNMAP;
> +
> +  ch->dma_map = malloc( sizeof( struct bus_dmamap ) );
> +
> +  if ( ch->dma_map == NULL ) {
> +    return ENOMEM;
> +  }
> +
> +  /* Alignment = 1 , Boundary = 0 */
> +  cb_virt = rtems_cache_aligned_malloc(
> +    sizeof( struct bcm_dma_cb ) );
> +
> +  if ( cb_virt == NULL ) {
> +    free( ch->dma_map );
> +
> +    return ENOMEM;
> +  }
> +
> +  ch->dma_map->buffer_begin = cb_virt;
> +  ch->dma_map->buffer_size = sizeof( struct bcm_dma_cb );
> +
> +  memset( cb_virt, 0, sizeof( struct bcm_dma_cb ) );
> +
> +  /*
> +   * Least alignment for busdma-allocated stuff is cache
> +   * line size, so just make sure nothing stupid happened
> +   * and we got properly aligned address
> +   */
> +  if ( (unsigned long int) cb_virt & 0x1f )
> +    return RTEMS_UNSATISFIED;
> +
> +  lastaddr = (vm_offset_t) 0;
> +  nsegs = 0;
> +
> +  error = bus_dmamap_load_buffer( dm_segments, cb_virt,
> +    sizeof( struct bcm_dma_cb ), 0x00, &lastaddr, &nsegs, 1 );

I am not sure if bus_dmamap_load_buffer is the right operation there.
It seems to be intended to setup scatter gather transfer operation
or buffer when translation from virtual addresses to physical
is computed on page by page basis.

RTEMS uses linear identity mapping so only translate from physical
to DMA device bus address is needed and you API supports only
single linear block/peripheral reg to linear block/peripheral reg
transfers. And I do not know why buffers should be prepared 
before transfer size is provided. The preallocation could have
only purpose for case that normal data segment has insufficient
caching.
So the concepts are mismatched there.

> +  if ( error == 0 )
> +    rpi_dmamap_cb( &cb_phys, dm_segments, nsegs + 1, 0 );
> +  else
> +    rpi_dmamap_cb( &cb_phys, NULL, 0, error );
> +
> +  ch->cb = cb_virt;
> +  ch->vc_cb = cb_phys;
> +  ch->flags = BCM_DMA_CH_FREE;
> +  ch->cb->info = INFO_WAIT_RESP;
> +
> +  rtems_interrupt_handler_install( BCM2835_IRQ_ID_DMA_CH0 + channel,
> +    "DMA copy",
> +    RTEMS_INTERRUPT_UNIQUE,
> +    rpi_dma_intr,
> +    NULL );
> +
> +  /* reset DMA */
> +  BCM2835_REG( BCM_DMA_CS( channel ) ) = CS_RESET;
> +
> +  return 0;
> +}
> +
> +static void rpi_dma_intr( void *arg )
> +{
> +  struct bcm_dma_ch *ch = (struct bcm_dma_ch *) arg;
> +  uint32_t           cs, debug;
> +
> +  cs = BCM2835_REG( BCM_DMA_CS( ch->ch ) );
> +
> +  if ( !( cs & ( CS_INT | CS_ERR ) ) ) {
> +    return;
> +  }
> +
> +  /* Check whether the channel is in use */
> +  if ( !( ch->flags & BCM_DMA_CH_USED ) ) {
> +    return;
> +  }
> +
> +  if ( cs & CS_ERR ) {
> +    debug = BCM2835_REG( BCM_DMA_DEBUG( ch->ch ) );
> +    BCM2835_REG( BCM_DMA_DEBUG( ch->ch ) ) = debug & DEBUG_ERROR_MASK;
> +    rpi_dma_reset( ch->ch );
> +  }
> +
> +  if ( cs & CS_INT ) {
> +    /* acknowledge interrupt */
> +    BCM2835_REG( BCM_DMA_CS( ch->ch ) ) = CS_INT | CS_END;
> +
> +    /* Note : BUS DMA sync : POST write so cache coherency not required.
> */ +
> +    /* save callback function and argument */
> +    if ( ch->intr_func )
> +      ch->intr_func( ch->ch, ch->intr_arg );
> +  }
> +}
> +

Please look at code and tests what can be done to make it functional.

Best wishes,

              Pavel


More information about the devel mailing list