* arch/arm/src/sama5/sam_xdmac.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/mutex.h>
#include "arm_internal.h"
#include "sched/sched.h"
#include "chip.h"
#include "sam_dmac.h"
#include "sam_periphclks.h"
#include "sam_memories.h"
#include "hardware/sam_pmc.h"
#include "hardware/sam_xdmac.h"
* Pre-processor Definitions
****************************************************************************/
* of 8 DMA Channels each.
*/
#if SAM_NDMAC < 1
# undef CONFIG_SAMA5_XDMAC1
# undef CONFIG_SAMA5_XDMAC0
#elif SAM_NDMAC < 2
# undef CONFIG_SAMA5_XDMAC1
#endif
#if defined(CONFIG_SAMA5_XDMAC0) || defined(CONFIG_SAMA5_XDMAC1)
* enabled
*/
#ifndef CONFIG_ARCH_DMA
# warning "SAMA5 DMA enabled but CONFIG_ARCH_DMA disabled"
#endif
#ifndef CONFIG_SAMA5_NLLDESC
# define CONFIG_SAMA5_NLLDESC SAM_NDMACHAN
#endif
#if CONFIG_SAMA5_NLLDESC < SAM_NDMACHAN
# warning "At least SAM_NDMACHAN descriptors must be allocated"
# undef CONFIG_SAMA5_NLLDESC
# define CONFIG_SAMA5_NLLDESC SAM_NDMACHAN
#endif
* Private Types
****************************************************************************/
struct sam_pidmap_s
{
uint8_t pid;
uint8_t pchan;
};
struct sam_xdmach_s
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
uint8_t xdmac;
#endif
uint8_t chan;
bool inuse;
bool rx;
uint32_t flags;
uint32_t base;
uint32_t cc;
dma_callback_t callback;
void *arg;
uint32_t rxaddr;
size_t rxsize;
struct chnext_view1_s *llhead;
struct chnext_view1_s *lltail;
};
struct sam_xdmac_s
{
mutex_t chlock;
sem_t dsem;
uint32_t base;
struct chnext_view1_s *descr;
struct sam_xdmach_s *xdmach;
};
* Private Data
****************************************************************************/
* before each DMA transfer in order to map the peripheral IDs to the
* correct channel. This must be done because the channel can change with
* the direction of the transfer.
*/
#ifdef CONFIG_SAMA5_XDMAC0
static const struct sam_pidmap_s g_xdmac0_rxchan[] =
{
#if defined(ATSAMA5D2)
{ SAM_PID_TWI0, XDMAC0_CH_TWI0_RX },
{ SAM_PID_TWI1, XDMAC0_CH_TWI1_RX },
{ SAM_PID_QSPI0, XDMAC0_CH_QSPI0_RX },
{ SAM_PID_SPI0, XDMAC0_CH_SPI0_RX },
{ SAM_PID_SPI1, XDMAC0_CH_SPI1_RX },
{ SAM_PID_FLEXCOM0, XDMAC0_CH_FLEXCOM0_RX },
{ SAM_PID_FLEXCOM1, XDMAC0_CH_FLEXCOM1_RX },
{ SAM_PID_FLEXCOM2, XDMAC0_CH_FLEXCOM2_RX },
{ SAM_PID_FLEXCOM3, XDMAC0_CH_FLEXCOM3_RX },
{ SAM_PID_FLEXCOM4, XDMAC0_CH_FLEXCOM4_RX },
{ SAM_PID_SSC0, XDMAC0_CH_SSC0_RX },
{ SAM_PID_SSC1, XDMAC0_CH_SSC1_RX },
{ SAM_PID_ADC, XDMAC0_CH_ADC_RX },
{ SAM_PID_AES, XDMAC0_CH_AES_RX },
{ SAM_PID_TDES, XDMAC0_CH_TDES_RX },
{ SAM_PID_I2SC0, XDMAC0_CH_I2SC0_RX },
{ SAM_PID_I2SC1, XDMAC0_CH_I2SC1_RX },
{ SAM_PID_UART0, XDMAC0_CH_UART0_RX },
{ SAM_PID_UART1, XDMAC0_CH_UART1_RX },
{ SAM_PID_UART2, XDMAC0_CH_UART2_RX },
{ SAM_PID_UART3, XDMAC0_CH_UART3_RX },
{ SAM_PID_UART4, XDMAC0_CH_UART4_RX },
{ SAM_PID_TC0, XDMAC0_CH_TC0_RX },
{ SAM_PID_TC1, XDMAC0_CH_TC1_RX },
{ SAM_PID_QSPI1, XDMAC0_CH_QSPI1_RX },
{ SAM_PID_PDMIC, XDMAC0_CH_PDMIC_RX },
#elif defined(ATSAMA5D4)
{ SAM_PID_HSMCI0, XDMAC0_CH_HSMCI0 },
{ SAM_PID_HSMCI1, XDMAC0_CH_HSMCI1 },
{ SAM_PID_TWI0, XDMAC0_CH_TWI0_RX },
{ SAM_PID_TWI1, XDMAC0_CH_TWI1_RX },
{ SAM_PID_TWI2, XDMAC0_CH_TWI2_RX },
{ SAM_PID_TWI3, XDMAC0_CH_TWI3_RX },
{ SAM_PID_SPI0, XDMAC0_CH_SPI0_RX },
{ SAM_PID_SPI1, XDMAC0_CH_SPI1_RX },
{ SAM_PID_SPI2, XDMAC0_CH_SPI2_RX },
{ SAM_PID_USART2, XDMAC0_CH_USART2_RX },
{ SAM_PID_USART3, XDMAC0_CH_USART3_RX },
{ SAM_PID_USART4, XDMAC0_CH_USART4_RX },
{ SAM_PID_UART0, XDMAC0_CH_UART0_RX },
{ SAM_PID_UART1, XDMAC0_CH_UART1_RX },
{ SAM_PID_SSC0, XDMAC0_CH_SSC0_RX },
{ SAM_PID_SSC1, XDMAC0_CH_SSC1_RX },
{ SAM_PID_DBGU, XDMAC0_CH_DBGU_RX },
{ SAM_PID_ADC, XDMAC0_CH_ADC_RX },
{ SAM_PID_SMD, XDMAC0_CH_SMD_RX },
{ SAM_PID_USART0, XDMAC0_CH_USART0_RX },
{ SAM_PID_USART1, XDMAC0_CH_USART1_RX },
{ SAM_PID_AES, XDMAC0_CH_AES_RX },
{ SAM_PID_TDES, XDMAC0_CH_TDES_RX },
#endif
};
#define NXDMAC0_RXCHANNELS (sizeof(g_xdmac0_rxchan) / sizeof(struct sam_pidmap_s))
static const struct sam_pidmap_s g_xdmac0_txchan[] =
{
#if defined(ATSAMA5D2)
{ SAM_PID_TWI0, XDMAC0_CH_TWI0_TX },
{ SAM_PID_TWI1, XDMAC0_CH_TWI1_TX },
{ SAM_PID_QSPI0, XDMAC0_CH_QSPI0_TX },
{ SAM_PID_SPI0, XDMAC0_CH_SPI0_TX },
{ SAM_PID_SPI1, XDMAC0_CH_SPI1_TX },
{ SAM_PID_PWM, XDMAC0_CH_PWM_TX },
{ SAM_PID_FLEXCOM0, XDMAC0_CH_FLEXCOM0_TX },
{ SAM_PID_FLEXCOM1, XDMAC0_CH_FLEXCOM1_TX },
{ SAM_PID_FLEXCOM2, XDMAC0_CH_FLEXCOM2_TX },
{ SAM_PID_FLEXCOM3, XDMAC0_CH_FLEXCOM3_TX },
{ SAM_PID_FLEXCOM4, XDMAC0_CH_FLEXCOM4_TX },
{ SAM_PID_SSC0, XDMAC0_CH_SSC0_TX },
{ SAM_PID_SSC1, XDMAC0_CH_SSC1_TX },
{ SAM_PID_AES, XDMAC0_CH_AES_TX },
{ SAM_PID_TDES, XDMAC0_CH_TDES_TX },
{ SAM_PID_SHA, XDMAC0_CH_SHA_TX },
{ SAM_PID_I2SC0, XDMAC0_CH_I2SC0_TX },
{ SAM_PID_I2SC1, XDMAC0_CH_I2SC1_TX },
{ SAM_PID_UART0, XDMAC0_CH_UART0_TX },
{ SAM_PID_UART1, XDMAC0_CH_UART1_TX },
{ SAM_PID_UART2, XDMAC0_CH_UART2_TX },
{ SAM_PID_UART3, XDMAC0_CH_UART3_TX },
{ SAM_PID_UART4, XDMAC0_CH_UART4_TX },
{ SAM_PID_CLASSD, XDMAC0_CH_CLASSD_TX },
{ SAM_PID_QSPI1, XDMAC0_CH_QSPI1_TX },
#elif defined(ATSAMA5D4)
{ SAM_PID_HSMCI0, XDMAC0_CH_HSMCI0 },
{ SAM_PID_HSMCI1, XDMAC0_CH_HSMCI1 },
{ SAM_PID_TWI0, XDMAC0_CH_TWI0_TX },
{ SAM_PID_TWI1, XDMAC0_CH_TWI1_TX },
{ SAM_PID_TWI2, XDMAC0_CH_TWI2_TX },
{ SAM_PID_TWI3, XDMAC0_CH_TWI3_TX },
{ SAM_PID_SPI0, XDMAC0_CH_SPI0_TX },
{ SAM_PID_SPI1, XDMAC0_CH_SPI1_TX },
{ SAM_PID_SPI2, XDMAC0_CH_SPI2_TX },
{ SAM_PID_USART2, XDMAC0_CH_USART2_TX },
{ SAM_PID_USART3, XDMAC0_CH_USART3_TX },
{ SAM_PID_USART4, XDMAC0_CH_USART4_TX },
{ SAM_PID_UART0, XDMAC0_CH_UART0_TX },
{ SAM_PID_UART1, XDMAC0_CH_UART1_TX },
{ SAM_PID_SSC0, XDMAC0_CH_SSC0_TX },
{ SAM_PID_SSC1, XDMAC0_CH_SSC1_TX },
{ SAM_PID_DBGU, XDMAC0_CH_DBGU_TX },
{ SAM_PID_SMD, XDMAC0_CH_SMD_TX },
{ SAM_PID_USART0, XDMAC0_CH_USART0_TX },
{ SAM_PID_USART1, XDMAC0_CH_USART1_TX },
{ SAM_PID_AES, XDMAC0_CH_AES_TX },
{ SAM_PID_TDES, XDMAC0_CH_TDES_TX },
{ SAM_PID_SHA, XDMAC0_CH_SHA_TX },
#endif
};
#define NXDMAC0_TXCHANNELS (sizeof(g_xdmac0_txchan) / sizeof(struct sam_pidmap_s))
#endif
#ifdef CONFIG_SAMA5_XDMAC1
static const struct sam_pidmap_s g_xdmac1_rxchan[] =
{
#if defined(ATSAMA5D2)
{ SAM_PID_TWI0, XDMAC1_CH_TWI0_RX },
{ SAM_PID_TWI1, XDMAC1_CH_TWI1_RX },
{ SAM_PID_QSPI0, XDMAC1_CH_QSPI0_RX },
{ SAM_PID_SPI0, XDMAC1_CH_SPI0_RX },
{ SAM_PID_SPI1, XDMAC1_CH_SPI1_RX },
{ SAM_PID_FLEXCOM0, XDMAC1_CH_FLEXCOM0_RX },
{ SAM_PID_FLEXCOM1, XDMAC1_CH_FLEXCOM1_RX },
{ SAM_PID_FLEXCOM2, XDMAC1_CH_FLEXCOM2_RX },
{ SAM_PID_FLEXCOM3, XDMAC1_CH_FLEXCOM3_RX },
{ SAM_PID_FLEXCOM4, XDMAC1_CH_FLEXCOM4_RX },
{ SAM_PID_SSC0, XDMAC1_CH_SSC0_RX },
{ SAM_PID_SSC1, XDMAC1_CH_SSC1_RX },
{ SAM_PID_ADC, XDMAC1_CH_ADC_RX },
{ SAM_PID_AES, XDMAC1_CH_AES_RX },
{ SAM_PID_TDES, XDMAC1_CH_TDES_RX },
{ SAM_PID_I2SC0, XDMAC1_CH_I2SC0_RX },
{ SAM_PID_I2SC1, XDMAC1_CH_I2SC1_RX },
{ SAM_PID_UART0, XDMAC1_CH_UART0_RX },
{ SAM_PID_UART1, XDMAC1_CH_UART1_RX },
{ SAM_PID_UART2, XDMAC1_CH_UART2_RX },
{ SAM_PID_UART3, XDMAC1_CH_UART3_RX },
{ SAM_PID_UART4, XDMAC1_CH_UART4_RX },
{ SAM_PID_TC0, XDMAC1_CH_TC0_RX },
{ SAM_PID_TC1, XDMAC1_CH_TC1_RX },
{ SAM_PID_QSPI1, XDMAC1_CH_QSPI1_RX },
{ SAM_PID_PDMIC, XDMAC1_CH_PDMIC_RX },
#elif defined(ATSAMA5D4)
{ SAM_PID_HSMCI0, XDMAC1_CH_HSMCI0 },
{ SAM_PID_HSMCI1, XDMAC1_CH_HSMCI1 },
{ SAM_PID_TWI0, XDMAC1_CH_TWI0_RX },
{ SAM_PID_TWI1, XDMAC1_CH_TWI1_RX },
{ SAM_PID_TWI2, XDMAC1_CH_TWI2_RX },
{ SAM_PID_TWI3, XDMAC1_CH_TWI3_RX },
{ SAM_PID_SPI0, XDMAC1_CH_SPI0_RX },
{ SAM_PID_SPI1, XDMAC1_CH_SPI1_RX },
{ SAM_PID_SPI2, XDMAC1_CH_SPI2_RX },
{ SAM_PID_USART2, XDMAC1_CH_USART2_RX },
{ SAM_PID_USART3, XDMAC1_CH_USART3_RX },
{ SAM_PID_USART4, XDMAC1_CH_USART4_RX },
{ SAM_PID_UART0, XDMAC1_CH_UART0_RX },
{ SAM_PID_UART1, XDMAC1_CH_UART1_RX },
{ SAM_PID_SSC0, XDMAC1_CH_SSC0_RX },
{ SAM_PID_SSC1, XDMAC1_CH_SSC1_RX },
{ SAM_PID_DBGU, XDMAC1_CH_DBGU_RX },
{ SAM_PID_ADC, XDMAC1_CH_ADC_RX },
{ SAM_PID_SMD, XDMAC1_CH_SMD_RX },
#endif
};
#define NXDMAC1_RXCHANNELS (sizeof(g_xdmac1_rxchan) / sizeof(struct sam_pidmap_s))
static const struct sam_pidmap_s g_xdmac1_txchan[] =
{
#if defined(ATSAMA5D2)
{ SAM_PID_TWI0, XDMAC1_CH_TWI0_TX },
{ SAM_PID_TWI1, XDMAC1_CH_TWI1_TX },
{ SAM_PID_QSPI0, XDMAC1_CH_QSPI0_TX },
{ SAM_PID_SPI0, XDMAC1_CH_SPI0_TX },
{ SAM_PID_SPI1, XDMAC1_CH_SPI1_TX },
{ SAM_PID_PWM, XDMAC1_CH_PWM_TX },
{ SAM_PID_FLEXCOM0, XDMAC1_CH_FLEXCOM0_TX },
{ SAM_PID_FLEXCOM1, XDMAC1_CH_FLEXCOM1_TX },
{ SAM_PID_FLEXCOM2, XDMAC1_CH_FLEXCOM2_TX },
{ SAM_PID_FLEXCOM3, XDMAC1_CH_FLEXCOM3_TX },
{ SAM_PID_FLEXCOM4, XDMAC1_CH_FLEXCOM4_TX },
{ SAM_PID_SSC0, XDMAC1_CH_SSC0_TX },
{ SAM_PID_SSC1, XDMAC1_CH_SSC1_TX },
{ SAM_PID_AES, XDMAC1_CH_AES_TX },
{ SAM_PID_TDES, XDMAC1_CH_TDES_TX },
{ SAM_PID_SHA, XDMAC1_CH_SHA_TX },
{ SAM_PID_I2SC0, XDMAC1_CH_I2SC0_TX },
{ SAM_PID_I2SC1, XDMAC1_CH_I2SC1_TX },
{ SAM_PID_UART0, XDMAC1_CH_UART0_TX },
{ SAM_PID_UART1, XDMAC1_CH_UART1_TX },
{ SAM_PID_UART2, XDMAC1_CH_UART2_TX },
{ SAM_PID_UART3, XDMAC1_CH_UART3_TX },
{ SAM_PID_UART4, XDMAC1_CH_UART4_TX },
{ SAM_PID_CLASSD, XDMAC1_CH_CLASSD_TX },
{ SAM_PID_QSPI1, XDMAC1_CH_QSPI1_TX },
#elif defined(ATSAMA5D4)
{ SAM_PID_HSMCI0, XDMAC1_CH_HSMCI0 },
{ SAM_PID_HSMCI1, XDMAC1_CH_HSMCI1 },
{ SAM_PID_TWI0, XDMAC1_CH_TWI0_TX },
{ SAM_PID_TWI1, XDMAC1_CH_TWI1_TX },
{ SAM_PID_TWI2, XDMAC1_CH_TWI2_TX },
{ SAM_PID_TWI3, XDMAC1_CH_TWI3_TX },
{ SAM_PID_SPI0, XDMAC1_CH_SPI0_TX },
{ SAM_PID_SPI1, XDMAC1_CH_SPI1_TX },
{ SAM_PID_SPI2, XDMAC1_CH_SPI2_TX },
{ SAM_PID_USART2, XDMAC1_CH_USART2_TX },
{ SAM_PID_USART3, XDMAC1_CH_USART3_TX },
{ SAM_PID_USART4, XDMAC1_CH_USART4_TX },
{ SAM_PID_UART0, XDMAC1_CH_UART0_TX },
{ SAM_PID_UART1, XDMAC1_CH_UART1_TX },
{ SAM_PID_SSC0, XDMAC1_CH_SSC0_TX },
{ SAM_PID_SSC1, XDMAC1_CH_SSC1_TX },
{ SAM_PID_DBGU, XDMAC1_CH_DBGU_TX },
{ SAM_PID_SMD, XDMAC1_CH_SMD_TX },
#endif
};
#define NXDMAC1_TXCHANNELS (sizeof(g_xdmac1_txchan) / sizeof(struct sam_pidmap_s))
#endif
#ifdef CONFIG_SAMA5_XDMAC0
struct chnext_view1_s g_desc0[CONFIG_SAMA5_NLLDESC];
static struct sam_xdmach_s g_xdmach0[SAM_NDMACHAN] =
{
#if SAM_NDMACHAN > 0
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 0,
.base = SAM_XDMAC0_CH0_BASE,
},
#endif
#if SAM_NDMACHAN > 1
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 1,
.base = SAM_XDMAC0_CH1_BASE,
},
#endif
#if SAM_NDMACHAN > 2
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 2,
.base = SAM_XDMAC0_CH2_BASE,
},
#endif
#if SAM_NDMACHAN > 3
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 3,
.base = SAM_XDMAC0_CH3_BASE,
},
#endif
#if SAM_NDMACHAN > 4
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 4,
.base = SAM_XDMAC0_CH4_BASE,
},
#endif
#if SAM_NDMACHAN > 5
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 5,
.base = SAM_XDMAC0_CH5_BASE,
},
#endif
#if SAM_NDMACHAN > 6
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 6,
.base = SAM_XDMAC0_CH6_BASE,
},
#endif
#if SAM_NDMACHAN > 7
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 7,
.base = SAM_XDMAC0_CH7_BASE,
},
#endif
#if SAM_NDMACHAN > 8
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 8,
.base = SAM_XDMAC0_CH8_BASE,
},
#endif
#if SAM_NDMACHAN > 9
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 9,
.base = SAM_XDMAC0_CH9_BASE,
},
#endif
#if SAM_NDMACHAN > 10
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 10,
.base = SAM_XDMAC0_CH10_BASE,
},
#endif
#if SAM_NDMACHAN > 11
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 11,
.base = SAM_XDMAC0_CH11_BASE,
},
#endif
#if SAM_NDMACHAN > 12
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 12,
.base = SAM_XDMAC0_CH12_BASE,
},
#endif
#if SAM_NDMACHAN > 13
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 13,
.base = SAM_XDMAC0_CH13_BASE,
},
#endif
#if SAM_NDMACHAN > 14
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 14,
.base = SAM_XDMAC0_CH14_BASE,
},
#endif
#if SAM_NDMACHAN > 15
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 0,
#endif
.chan = 15,
.base = SAM_XDMAC0_CH15_BASE,
}
#endif
};
static struct sam_xdmac_s g_xdmac0 =
{
.chlock = NXMUTEX_INITIALIZER,
.dsem = SEM_INITIALIZER(SAM_NDMACHAN),
.base = SAM_XDMAC0_VBASE,
.descr = g_desc0,
.xdmach = g_xdmach0,
};
#endif
#ifdef CONFIG_SAMA5_XDMAC1
struct chnext_view1_s g_desc1[CONFIG_SAMA5_NLLDESC];
static struct sam_xdmach_s g_xdmach1[SAM_NDMACHAN] =
{
#if SAM_NDMACHAN > 0
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 0,
.base = SAM_XDMAC1_CH0_BASE,
},
#endif
#if SAM_NDMACHAN > 1
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 1,
.base = SAM_XDMAC1_CH1_BASE,
},
#endif
#if SAM_NDMACHAN > 2
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 2,
.base = SAM_XDMAC1_CH2_BASE,
},
#endif
#if SAM_NDMACHAN > 3
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 3,
.base = SAM_XDMAC1_CH3_BASE,
},
#endif
#if SAM_NDMACHAN > 4
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 4,
.base = SAM_XDMAC1_CH4_BASE,
},
#endif
#if SAM_NDMACHAN > 5
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 5,
.base = SAM_XDMAC1_CH5_BASE,
},
#endif
#if SAM_NDMACHAN > 6
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 6,
.base = SAM_XDMAC1_CH6_BASE,
},
#endif
#if SAM_NDMACHAN > 7
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 7,
.base = SAM_XDMAC1_CH7_BASE,
},
#endif
#if SAM_NDMACHAN > 8
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 8,
.base = SAM_XDMAC1_CH8_BASE,
},
#endif
#if SAM_NDMACHAN > 9
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 9,
.base = SAM_XDMAC1_CH9_BASE,
},
#endif
#if SAM_NDMACHAN > 10
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 10,
.base = SAM_XDMAC1_CH10_BASE,
},
#endif
#if SAM_NDMACHAN > 11
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 11,
.base = SAM_XDMAC1_CH11_BASE,
},
#endif
#if SAM_NDMACHAN > 12
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 12,
.base = SAM_XDMAC1_CH12_BASE,
},
#endif
#if SAM_NDMACHAN > 13
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 13,
.base = SAM_XDMAC1_CH13_BASE,
},
#endif
#if SAM_NDMACHAN > 14
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 14,
.base = SAM_XDMAC1_CH14_BASE,
},
#endif
#if SAM_NDMACHAN > 15
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
.xdmac = 1,
#endif
.chan = 15,
.base = SAM_XDMAC1_CH15_BASE,
}
#endif
};
static struct sam_xdmac_s g_xdmac1 =
{
.chlock = NXMUTEX_INITIALIZER,
.dsem = SEM_INITIALIZER(SAM_NDMACHAN),
.base = SAM_XDMAC1_VBASE,
.descr = g_desc1,
.xdmach = g_xdmach1,
};
#endif
* Private Functions
****************************************************************************/
* Name: sam_getdmac
*
* Description:
* Read a global XDMAC register
*
****************************************************************************/
static inline uint32_t sam_getdmac(struct sam_xdmac_s *xdmac,
unsigned int offset)
{
return getreg32(xdmac->base + offset);
}
* Name: sam_putdmac
*
* Description:
* Write a value to a global XDMAC register
*
****************************************************************************/
static inline void sam_putdmac(struct sam_xdmac_s *xdmac, uint32_t value,
unsigned int offset)
{
putreg32(value, xdmac->base + offset);
}
* Name: sam_getdmach
*
* Description:
* Read a XDMAC channel register
*
****************************************************************************/
static inline uint32_t sam_getdmach(struct sam_xdmach_s *xdmach,
unsigned int offset)
{
return getreg32(xdmach->base + offset);
}
* Name: sam_putdmach
*
* Description:
* Write a value to a XDMAC channel register
*
****************************************************************************/
static inline void sam_putdmach(struct sam_xdmach_s *xdmach, uint32_t value,
unsigned int offset)
{
putreg32(value, xdmach->base + offset);
}
* Name: sam_controller
*
* Description:
* Given a DMA channel instance, return a pointer to the parent DMA
* controller instance.
*
****************************************************************************/
static inline struct sam_xdmac_s *sam_controller(struct sam_xdmach_s *xdmach)
{
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
return xdmach->xdmac ? &g_xdmac1 : &g_xdmac0;
#elif defined(CONFIG_SAMA5_XDMAC0)
return &g_xdmac0;
#else
return &g_xdmac1;
#endif
}
* Name: sam_channel, sam_source_channel, and sam_sink_channel
*
* Description:
* Return the RX or TX channel associated with a PID. As a clarification:
*
* The source channel refers to the source of data for the DMA. This is,
* either (1) memory for a TX DMA or (2) a peripheral register for an RX
* DMA.
*
* The sink channel is the recipient of the DMA data. This is either
* (1) memory for an RX DMA, or (2) a peripheral register for a TX DMA.
*
****************************************************************************/
static uint8_t sam_channel(uint8_t pid, const struct sam_pidmap_s *table,
unsigned int nentries)
{
int i;
* all of the table entries have been examined without finding the PID.
*/
for (i = 0; i < nentries; i++, table++)
{
if (table->pid == pid)
{
return table->pchan;
}
}
dmaerr("ERROR: No channel found for pid %d\n", pid);
DEBUGPANIC();
return 0x3f;
}
static uint32_t sam_source_channel(struct sam_xdmach_s *xdmach, uint8_t pid)
{
const struct sam_pidmap_s *table;
unsigned int nentries;
#ifdef CONFIG_SAMA5_XDMAC0
#ifdef CONFIG_SAMA5_XDMAC1
if (xdmach->xdmac == 0)
#endif
{
table = g_xdmac0_rxchan;
nentries = NXDMAC0_RXCHANNELS;
}
#endif
#ifdef CONFIG_SAMA5_XDMAC1
#ifdef CONFIG_SAMA5_XDMAC0
else
#endif
{
table = g_xdmac1_rxchan;
nentries = NXDMAC1_RXCHANNELS;
}
#endif
return (uint32_t)sam_channel(pid, table, nentries);
}
static uint32_t sam_sink_channel(struct sam_xdmach_s *xdmach, uint8_t pid)
{
const struct sam_pidmap_s *table;
unsigned int nentries;
#ifdef CONFIG_SAMA5_XDMAC0
#ifdef CONFIG_SAMA5_XDMAC1
if (xdmach->xdmac == 0)
#endif
{
table = g_xdmac0_txchan;
nentries = NXDMAC0_TXCHANNELS;
}
#endif
#ifdef CONFIG_SAMA5_XDMAC1
#ifdef CONFIG_SAMA5_XDMAC0
else
#endif
{
table = g_xdmac1_txchan;
nentries = NXDMAC1_TXCHANNELS;
}
#endif
return (uint32_t)sam_channel(pid, table, nentries);
}
* Name: sam_maxtransfer
*
* Description:
* Maximum number of bytes that can be sent in one transfer
*
****************************************************************************/
static size_t sam_maxtransfer(struct sam_xdmach_s *xdmach)
{
unsigned int xfrwidth;
size_t maxtransfer;
xfrwidth = (xdmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) >>
DMACH_FLAG_PERIPHWIDTH_SHIFT;
switch (xfrwidth)
{
default:
case 0:
maxtransfer = XDMACH_CUBC_UBLEN_MAX;
break;
case 1:
maxtransfer = 2 * XDMACH_CUBC_UBLEN_MAX;
break;
case 2:
maxtransfer = 4 * XDMACH_CUBC_UBLEN_MAX;
break;
case 3:
maxtransfer = 8 * XDMACH_CUBC_UBLEN_MAX;
break;
}
return maxtransfer;
}
* Name: sam_ntxtransfers
*
* Description:
* Number of TX transfers via DMA
*
****************************************************************************/
static uint32_t sam_ntxtransfers(struct sam_xdmach_s *xdmach,
uint32_t dmasize)
{
unsigned int srcwidth;
* chunk size). BTSIZE is "the number of transfers to be performed, that
* is, for writes it refers to the number of source width transfers
* to perform when XDMAC is flow controller. For Reads, BTSIZE refers to
* the number of transfers completed on the Source Interface. ..."
*/
srcwidth = (xdmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) >>
DMACH_FLAG_PERIPHWIDTH_SHIFT;
switch (srcwidth)
{
default:
case 0:
break;
case 1:
dmasize = (dmasize + 1) >> 1;
break;
case 2:
dmasize = (dmasize + 3) >> 2;
break;
case 3:
dmasize = (dmasize + 7) >> 3;
break;
}
return dmasize;
}
* Name: sam_cubc
*
* Description:
* Calculate the CUBC transfer size
*
****************************************************************************/
static inline uint32_t sam_cubc(struct sam_xdmach_s *xdmach,
uint32_t dmasize)
{
uint32_t ntransfers;
* be performed, that is, the number of source width transfers to perform.
*/
ntransfers = sam_ntxtransfers(xdmach, dmasize);
DEBUGASSERT(ntransfers <= XDMACH_CUBC_UBLEN_MAX);
return (ntransfers << XDMACH_CUBC_UBLEN_SHIFT);
}
* Name: sam_txcc
*
* Description:
* Decode the flags to get the correct Channel Control (CC) Register bit
* settings for a transmit (memory to peripheral) transfer.
*
* Single Block:
* 1. Clear TYPE for a memory to memory transfer, otherwise set
* this bit for memory to/from peripheral transfer.
* 2. Program MBSIZE to the memory burst size used.
* 3. Program SAM/DAM to the memory addressing scheme.
* 4. Program SYNC to select the peripheral transfer direction.
* 5. Set PROT to activate a secure channel.
* 6. Program CSIZE to configure the channel chunk size (only
* relevant for peripheral synchronized transfer).
* 7. Program DWIDTH to configure the transfer data width.
* 8. Program SIF, DIF to configure the master interface
* used to read data and write data respectively.
* 9. Program PERID to select the active hardware request line
* (only relevant for a peripheral synchronized transfer).
* 10. Set SWREQ to use software request (only relevant for a
* peripheral synchronized transfer).
*
****************************************************************************/
static inline uint32_t sam_txcc(struct sam_xdmach_s *xdmach)
{
uint32_t regval = 0;
uint32_t field;
* this bit for memory to/from peripheral transfer.
*
* By convention, TX refers to a memory to peripheral transfer. So the
* source is memory. Is the "peripheral" destination a peripheral? or
* it is really memory?
*/
if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0)
{
regval |= XDMACH_CC_TYPE;
}
*
* NOTE: This assumes the same encoding in the DMACH flags as in the CC
* register MBSIZE field.
*/
field = (xdmach->flags & DMACH_FLAG_MEMBURST_MASK) >>
DMACH_FLAG_MEMBURST_SHIFT;
regval |= (field << XDMACH_CC_MBSIZE_SHIFT);
*
* NOTE: This assumes that 0 means non-incrementing.
* TX -> Source is memory.
*/
if ((xdmach->flags & DMACH_FLAG_MEMINCREMENT) != 0)
{
regval |= XDMACH_CC_SAM_INCR;
}
if ((xdmach->flags & DMACH_FLAG_PERIPHINCREMENT) != 0)
{
regval |= XDMACH_CC_DAM_INCR;
}
*
* TX -> Memory to peripheral
*/
regval |= XDMACH_CC_DSYNC;
#if 0
regval |= XDMACH_CC_PROT;
#endif
* relevant for peripheral synchronized transfer).
*/
if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0)
{
* NOTE that we assume that encoding in the XDMACH flags is the same
* as in the CC register CSIZE field.
*/
field = (xdmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE_MASK) >>
DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT;
regval |= (field << XDMACH_CC_CSIZE_SHIFT);
}
*
* NOTE that we assume that encoding in the XDMACH flags is the same as in
* the CC register CSIZE field.
*/
field = (xdmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) >>
DMACH_FLAG_PERIPHWIDTH_SHIFT;
regval |= (field << XDMACH_CC_DWIDTH_SHIFT);
* used to read data and write data respectively.
*
* TX -> Source is memory
*/
if ((xdmach->flags & DMACH_FLAG_MEMAHB_MASK) == DMACH_FLAG_MEMAHB_AHB_IF1)
{
regval |= XDMACH_CC_SIF;
}
if ((xdmach->flags & DMACH_FLAG_PERIPHAHB_MASK) ==
DMACH_FLAG_PERIPHAHB_AHB_IF1)
{
regval |= XDMACH_CC_DIF;
}
* (only relevant for a peripheral synchronized transfer).
*/
if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0)
{
int pid;
pid = (xdmach->flags & DMACH_FLAG_PERIPHPID_MASK) >>
DMACH_FLAG_PERIPHPID_SHIFT;
field = sam_sink_channel(xdmach, pid);
regval |= (field << XDMACH_CC_PERID_SHIFT);
#if 0
* peripheral synchronized transfer).
*/
regval |= XDMACH_CC_SWREQ;
#endif
}
return regval;
}
* Name: sam_rxcc
*
* Description:
* Decode the flags to get the correct Channel Control (CC) Register bit
* settings for a receive (peripheral to memory) transfer.
*
* 1. Clear TYPE for a memory to memory transfer, otherwise set
* this bit for memory to/from peripheral transfer.
* 2. Program MBSIZE to the memory burst size used.
* 3. Program SAM/DAM to the memory addressing scheme.
* 4. Program SYNC to select the peripheral transfer direction.
* 5. Set PROT to activate a secure channel.
* 6. Program CSIZE to configure the channel chunk size (only
* relevant for peripheral synchronized transfer).
* 7. Program DWIDTH to configure the transfer data width.
* 8. Program SIF, DIF to configure the master interface
* used to read data and write data respectively.
* 9. Program PERID to select the active hardware request line
* (only relevant for a peripheral synchronized transfer).
* 10. Set SWREQ to use software request (only relevant for a
* peripheral synchronized transfer).
*
****************************************************************************/
static inline uint32_t sam_rxcc(struct sam_xdmach_s *xdmach)
{
uint32_t regval = 0;
uint32_t field;
* this bit for memory to/from peripheral transfer.
*
* By convention, RX refers to a peripheral to memory transfer. So the
* source is peripheral. Is the "peripheral" source a peripheral? or
* is it also memory?
*/
if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0)
{
regval |= XDMACH_CC_TYPE;
}
*
* NOTE: This assumes the same encoding in the DMACH flags as in the CC
* register MBSIZE field.
*/
field = (xdmach->flags & DMACH_FLAG_MEMBURST_MASK) >>
DMACH_FLAG_MEMBURST_SHIFT;
regval |= (field << XDMACH_CC_MBSIZE_SHIFT);
*
* NOTE: This assumes that 0 means non-incrementing.
* RX -> Source is peripheral.
*/
if ((xdmach->flags & DMACH_FLAG_PERIPHINCREMENT) != 0)
{
regval |= XDMACH_CC_SAM_INCR;
}
if ((xdmach->flags & DMACH_FLAG_MEMINCREMENT) != 0)
{
regval |= XDMACH_CC_DAM_INCR;
}
*
* RX -> Peripheral to memory (DSYNC == 0)
*/
#if 0
regval |= XDMACH_CC_PROT;
#endif
* relevant for peripheral synchronized transfer).
*/
if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0)
{
* NOTE that we assume that encoding in the XDMACH flags is the same
* as in the CC register CSIZE field.
*/
field = (xdmach->flags & DMACH_FLAG_PERIPHCHUNKSIZE_MASK) >>
DMACH_FLAG_PERIPHCHUNKSIZE_SHIFT;
regval |= (field << XDMACH_CC_CSIZE_SHIFT);
}
*
* NOTE that we assume that encoding in the XDMACH flags is the same as in
* the CC register CSIZE field.
*/
field = (xdmach->flags & DMACH_FLAG_PERIPHWIDTH_MASK) >>
DMACH_FLAG_PERIPHWIDTH_SHIFT;
regval |= (field << XDMACH_CC_DWIDTH_SHIFT);
* used to read data and write data respectively.
*
* RX -> Source is peripheral
*/
if ((xdmach->flags & DMACH_FLAG_PERIPHAHB_MASK) ==
DMACH_FLAG_PERIPHAHB_AHB_IF1)
{
regval |= XDMACH_CC_SIF;
}
if ((xdmach->flags & DMACH_FLAG_MEMAHB_MASK) == DMACH_FLAG_MEMAHB_AHB_IF1)
{
regval |= XDMACH_CC_DIF;
}
* (only relevant for a peripheral synchronized transfer).
*/
if ((xdmach->flags & DMACH_FLAG_PERIPHISPERIPH) != 0)
{
int pid;
pid = (xdmach->flags & DMACH_FLAG_PERIPHPID_MASK) >>
DMACH_FLAG_PERIPHPID_SHIFT;
field = sam_source_channel(xdmach, pid);
regval |= (field << XDMACH_CC_PERID_SHIFT);
#if 0
* peripheral synchronized transfer).
*/
regval |= XDMACH_CC_SWREQ;
#endif
}
return regval;
}
* Name: sam_allocdesc
*
* Description:
* Allocate and add one descriptor to the DMA channel's link list.
*
* NOTE: link list entries are freed by the DMA interrupt handler.
* However, since the setting/clearing of the 'in use' indication is
* atomic, no special actions need be performed. It would be a good thing
* to add logic to handle the case where all of the entries are exhausted
* and we could wait for some to be freed by the interrupt handler.
*
****************************************************************************/
static struct chnext_view1_s *
sam_allocdesc(struct sam_xdmach_s *xdmach, struct chnext_view1_s *prev,
uint32_t csa, uint32_t cda, uint32_t cubc)
{
struct sam_xdmac_s *xdmac = sam_controller(xdmach);
struct chnext_view1_s *descr = NULL;
int i;
int ret;
* Obviously setting it to zero would break that usage.
*/
#ifdef CONFIG_DEBUG_FEATURES
if (csa != 0)
#endif
{
* there is at least one free descriptor in the table and it is ours.
*/
ret = nxsem_wait_uninterruptible(&xdmac->dsem);
if (ret < 0)
{
return NULL;
}
* with csa == 0. That csa field is set to zero by the DMA transfer
* complete interrupt handler. The following should be safe because
* that is an atomic operation.
*/
ret = nxmutex_lock(&xdmac->chlock);
if (ret < 0)
{
nxsem_post(&xdmac->dsem);
return NULL;
}
for (i = 0; i < CONFIG_SAMA5_NLLDESC; i++)
{
if (xdmac->descr[i].csa == 0)
{
descr = &xdmac->descr[i];
descr->cnda = 0;
descr->cubc = cubc;
descr->csa = csa;
descr->cda = cda;
if (!prev)
{
* the list
*/
DEBUGASSERT(xdmach->llhead == NULL &&
xdmach->lltail == NULL);
xdmach->llhead = descr;
}
else
{
DEBUGASSERT(xdmach->llhead != NULL &&
xdmach->lltail == prev);
* cue that we are going to do the link list transfer.
*
* Set the NDE field in the previous descriptor; Clear the
* NDE field in the final descriptor.
*/
prev->cubc |= CHNEXT_UBC_NDE;
* REVISIT: This assumes that the next description is
* fetched via AHB IF0.
*/
prev->cnda = (uint32_t)sam_physramaddr((uintptr_t)descr);
}
xdmach->lltail = descr;
* that hardware will be accessing the descriptor via DMA.
*/
up_clean_dcache((uintptr_t)descr,
(uintptr_t)descr +
sizeof(struct chnext_view1_s));
break;
}
}
* search loop should always be successful.
*/
nxmutex_unlock(&xdmac->chlock);
DEBUGASSERT(descr != NULL);
}
return descr;
}
* Name: sam_freelinklist
*
* Description:
* Free all descriptors in the DMA channel's link list.
*
* NOTE: Called from the DMA interrupt handler.
*
****************************************************************************/
static void sam_freelinklist(struct sam_xdmach_s *xdmach)
{
struct sam_xdmac_s *xdmac = sam_controller(xdmach);
struct chnext_view1_s *descr;
uintptr_t paddr;
* channel
*/
descr = xdmach->llhead;
xdmach->llhead = NULL;
xdmach->lltail = NULL;
while (descr != NULL)
{
DEBUGASSERT(descr->csa != 0);
paddr = descr->cnda;
* semaphore count.
*/
memset(descr, 0, sizeof(struct chnext_view1_s));
nxsem_post(&xdmac->dsem);
descr = (struct chnext_view1_s *)sam_virtramaddr(paddr);
}
}
* Name: sam_txbuffer
*
* Description:
* Configure DMA for transmit of one buffer (memory to peripheral). This
* function may be called multiple times to handle large and/or dis-
* continuous transfers.
*
****************************************************************************/
static int sam_txbuffer(struct sam_xdmach_s *xdmach, uint32_t paddr,
uint32_t maddr, size_t nbytes)
{
uint32_t cubc;
* calculated CC register value. Otherwise, create the CC register value
* from the properties of the transfer.
*/
if (!xdmach->llhead)
{
xdmach->cc = sam_txcc(xdmach);
}
cubc = sam_cubc(xdmach, nbytes);
cubc |= (CHNEXT_UBC_NVIEW_1 | CHNEXT_UBC_NSEN);
if (!sam_allocdesc(xdmach, xdmach->lltail, maddr, paddr, cubc))
{
return -ENOMEM;
}
return OK;
}
* Name: sam_rxbuffer
*
* Description:
* Configure DMA for receipt of one buffer (peripheral to memory). This
* function may be called multiple times to handle large and/or dis-
* continuous transfers.
*
****************************************************************************/
static int sam_rxbuffer(struct sam_xdmach_s *xdmach, uint32_t paddr,
uint32_t maddr, size_t nbytes)
{
uint32_t cubc;
* calculated CC register value. Otherwise, create the CC register value
* from the properties of the transfer.
*/
if (!xdmach->llhead)
{
xdmach->cc = sam_rxcc(xdmach);
}
cubc = sam_cubc(xdmach, nbytes);
cubc |= (CHNEXT_UBC_NVIEW_1 | CHNEXT_UBC_NSEN);
if (!sam_allocdesc(xdmach, xdmach->lltail, paddr, maddr, cubc))
{
return -ENOMEM;
}
return OK;
}
* Name: sam_single
*
* Description:
* Start a single buffer DMA.
*
****************************************************************************/
static inline int sam_single(struct sam_xdmach_s *xdmach)
{
struct sam_xdmac_s *xdmac = sam_controller(xdmach);
struct chnext_view1_s *llhead = xdmach->llhead;
* free channel.
*
* In this implementation, the free channel is assigned in a different
* manner.
*/
* reading the XDMAC Channel Interrupt Status Register (CIS).
*/
sam_getdmach(xdmach, SAM_XDMACH_CIS_OFFSET);
* Register.
*/
DEBUGASSERT(llhead != NULL && llhead->csa != 0);
sam_putdmach(xdmach, llhead->csa, SAM_XDMACH_CSA_OFFSET);
* Address Register (CDA).
*/
sam_putdmach(xdmach, llhead->cda, SAM_XDMACH_CDA_OFFSET);
* Register with the number of data.
*/
sam_putdmach(xdmach, llhead->cubc, SAM_XDMACH_CUBC_OFFSET);
sam_putdmach(xdmach, xdmach->cc, SAM_XDMACH_CC_OFFSET);
*
* XDMAC Channel Next Descriptor Control (CNDC) Register
* XDMAC Channel Block Control (CBC) Register
* XDMAC Channel Data Stride Memory Set Pattern (CDSMSP) Register
* XDMAC Channel Source Microblock Stride (CSUS) Register
* XDMAC Channel Destination Microblock Stride (CDUS)Register
*
* This respectively indicates that the linked list is disabled, there is
* only one block and striding is disabled
*/
sam_putdmach(xdmach, 0, SAM_XDMACH_CNDC_OFFSET);
sam_putdmach(xdmach, 0, SAM_XDMACH_CBC_OFFSET);
sam_putdmach(xdmach, 0, SAM_XDMACH_CDSMSP_OFFSET);
sam_putdmach(xdmach, 0, SAM_XDMACH_CSUS_OFFSET);
sam_putdmach(xdmach, 0, SAM_XDMACH_CDUS_OFFSET);
* interrupt bit in the XDMAC Channel Interrupt Enable (CIE) Register.
*/
sam_putdmach(xdmach, XDMAC_CHINT_BI | XDMAC_CHINT_ERRORS,
SAM_XDMACH_CIE_OFFSET);
* bit in the XDMAC Global Interrupt Enable (GIE) Register.
*/
sam_putdmac(xdmac, XDMAC_CHAN(xdmach->chan), SAM_XDMAC_GIE_OFFSET);
* XDMAC Global Channel Enable (GE) Register. The channel bit will
* be set in the GS register by hardware.
*/
sam_putdmac(xdmac, XDMAC_CHAN(xdmach->chan), SAM_XDMAC_GE_OFFSET);
* corresponding "End of Block Interrupt" Status bit in the channel
* CIS register and generates an global interrupt for the channel.
* The channel bit will be cleared in the GS register by hardware.
*/
return OK;
}
* Name: sam_multiple
*
* Description:
* Start a multiple buffer DMA.
*
****************************************************************************/
static inline int sam_multiple(struct sam_xdmach_s *xdmach)
{
struct sam_xdmac_s *xdmac = sam_controller(xdmach);
struct chnext_view1_s *llhead = xdmach->llhead;
uintptr_t paddr;
uint32_t regval;
DEBUGASSERT(llhead != NULL && llhead->csa != 0);
* free channel.
*
* In this implementation, the free channel is assigned in a different
* manner.
*/
* reading the XDMAC Channel Interrupt Status Register (CIS).
*/
sam_getdmach(xdmach, SAM_XDMACH_CIS_OFFSET);
* descriptor view is programmable on a per descriptor basis. The
* linked list items structure must be word aligned. CUBC NDE
* must be configured to 0 in the last descriptor to terminate the
* list.
*
* This was done during the RX/TX setup phases of the DMA transfer.
*/
* Control (CC) Register.
*/
sam_putdmach(xdmach, xdmach->cc, SAM_XDMACH_CC_OFFSET);
* (CNDA) Register with the first descriptor address and bit NDAIF
* with the master interface identifier.
*
* REVISIT: Using NDAIF=0. Is that correct?
*/
paddr = sam_physramaddr((uintptr_t)llhead);
sam_putdmach(xdmach, (uint32_t)paddr, SAM_XDMACH_CNDA_OFFSET);
*
* a. Set NDE to enable the descriptor fetch.
* b. Set NDSUP to update the source address at the descriptor fetch
* time, otherwise clear this bit.
* c. Set NDDUP to update the destination address at the descriptor
* fetch time, otherwise clear this bit.
* d. Program the NDVIEW field to define the length of the first
* descriptor.
*/
regval = (XDMACH_CNDC_NDE | XDMACH_CNDC_NDVIEW_NDV2);
*
* TYPE = 0 -> memory-to-memory
* TYPE = 1 && DSYNC = 1 -> memory-to-peripheral
*/
if ((xdmach->cc & XDMACH_CC_TYPE) == 0 ||
(xdmach->cc & XDMACH_CC_DSYNC) != 0)
{
regval |= XDMACH_CNDC_NDSUP;
}
*
* TYPE = 0 -> memory-to-memory
* TYPE = 1 && DSYNC = 0 -> peripheral-to-memory
*/
if ((xdmach->cc & XDMACH_CC_TYPE) == 0 ||
(xdmach->cc & XDMACH_CC_TYPE) == 0)
{
regval |= XDMACH_CNDC_NDDUP;
}
sam_putdmach(xdmach, regval, SAM_XDMACH_CNDC_OFFSET);
* CIE register.
*/
sam_putdmach(xdmach, XDMAC_CHINT_LI | XDMAC_CHINT_ERRORS,
SAM_XDMACH_CIE_OFFSET);
* bit in the XDMAC Global Interrupt Enable (GIE) Register.
*/
sam_putdmac(xdmac, XDMAC_CHAN(xdmach->chan), SAM_XDMAC_GIE_OFFSET);
* XDMAC Global Channel Enable (GE) Register. The channel bit will
* be set in the GS register by hardware.
*/
sam_putdmac(xdmac, XDMAC_CHAN(xdmach->chan), SAM_XDMAC_GE_OFFSET);
* corresponding "End of Block Interrupt" Status bit in the channel
* CIS register and generates an global interrupt for the channel.
* The channel bit will be cleared in the GS register by hardware.
*/
return OK;
}
* Name: sam_dmaterminate
*
* Description:
* Terminate the DMA transfer and disable the DMA channel
*
****************************************************************************/
static void sam_dmaterminate(struct sam_xdmach_s *xdmach, int result)
{
struct sam_xdmac_s *xdmac = sam_controller(xdmach);
uint32_t chanbit = XDMAC_CHAN(xdmach->chan);
sam_putdmac(xdmac, chanbit, SAM_XDMAC_GID_OFFSET);
sam_putdmach(xdmach, XDMAC_CHINT_ALL, SAM_XDMACH_CID_OFFSET);
* associated bit in the Global Channel Enable (GE) Register. The hardware
* then disables a channel on transfer completion by clearing channel bit
* in the GS register. To disable a channel, setting the channel bit in the
* GD register and poll the channel bit in GS register to determine when
* the channel has been disabled.
*/
sam_putdmac(xdmac, chanbit, SAM_XDMAC_GD_OFFSET);
while ((sam_getdmac(xdmac, SAM_XDMAC_GS_OFFSET) & chanbit) != 0);
sam_freelinklist(xdmach);
* to force reloads from memory.
*/
if (xdmach->rx)
{
up_invalidate_dcache(xdmach->rxaddr, xdmach->rxaddr + xdmach->rxsize);
}
if (xdmach->callback)
{
xdmach->callback((DMA_HANDLE)xdmach, xdmach->arg, result);
}
xdmach->callback = NULL;
xdmach->arg = NULL;
}
* Name: sam_xdmac_interrupt
*
* Description:
* DMA interrupt handler
*
****************************************************************************/
static int sam_xdmac_interrupt(int irq, void *context, void *arg)
{
struct sam_xdmac_s *xdmac = (struct sam_xdmac_s *)arg;
struct sam_xdmach_s *xdmach;
unsigned int chndx;
uint32_t gpending;
uint32_t chpending;
uint32_t bit;
DEBUGASSERT(xdmac != NULL);
gpending = sam_getdmac(xdmac, SAM_XDMAC_GIS_OFFSET) &
sam_getdmac(xdmac, SAM_XDMAC_GIM_OFFSET);
for (chndx = 0; chndx < SAM_NDMACHAN && gpending != 0; chndx++)
{
bit = XDMAC_CHAN(chndx);
if ((gpending & bit) != 0)
{
xdmach = &xdmac->xdmach[chndx];
chpending = sam_getdmach(xdmach, SAM_XDMACH_CIS_OFFSET) &
sam_getdmach(xdmach, SAM_XDMACH_CIM_OFFSET);
if ((chpending & XDMAC_CHINT_ERRORS) != 0)
{
dmaerr("ERROR: DMA failed: %08" PRIx32 "\n", chpending);
sam_dmaterminate(xdmach, -EIO);
}
else if ((chpending & (XDMAC_CHINT_BI | XDMAC_CHINT_LI)) != 0)
{
sam_dmaterminate(xdmach, OK);
}
else
{
dmaerr("ERROR: Unexpected interrupt: %08" PRIx32 "\n",
chpending);
DEBUGPANIC();
}
gpending &= !bit;
}
}
return OK;
}
* Name: sam_dmainitialize
*
* Description:
* Initialize the DMA subsystem
*
* Returned Value:
* None
*
****************************************************************************/
void sam_dmainitialize(struct sam_xdmac_s *xdmac)
{
sam_putdmac(xdmac, XDMAC_CHAN_ALL, SAM_XDMAC_GID_OFFSET);
sam_putdmac(xdmac, XDMAC_CHAN_ALL, SAM_XDMAC_GD_OFFSET);
}
* Public Functions
****************************************************************************/
* Name: arm_dma_initialize
*
* Description:
* Initialize the DMA subsystem
*
* Returned Value:
* None
*
****************************************************************************/
void weak_function arm_dma_initialize(void)
{
#ifdef CONFIG_SAMA5_XDMAC0
dmainfo("Initialize XDMAC0\n");
sam_xdmac0_enableclk();
irq_attach(SAM_IRQ_XDMAC0, sam_xdmac_interrupt, &g_xdmac0);
sam_dmainitialize(&g_xdmac0);
up_enable_irq(SAM_IRQ_XDMAC0);
#endif
#ifdef CONFIG_SAMA5_XDMAC1
dmainfo("Initialize XDMAC1\n");
sam_xdmac1_enableclk();
irq_attach(SAM_IRQ_XDMAC1, sam_xdmac_interrupt, &g_xdmac1);
sam_dmainitialize(&g_xdmac1);
up_enable_irq(SAM_IRQ_XDMAC1);
#endif
}
* Name: sam_dmachannel
*
* Allocate a DMA channel. This function sets aside a DMA channel then
* gives the caller exclusive access to the DMA channel.
*
* The naming convention in all of the DMA interfaces is that one side is
* the 'peripheral' and the other is 'memory'. However, the interface
* could still be used if, for example, both sides were memory although
* the naming would be awkward.
*
* Returned Value:
* If a DMA channel is available, this function returns a non-NULL, void*
* DMA channel handle. NULL is returned on any failure.
*
****************************************************************************/
DMA_HANDLE sam_dmachannel(uint8_t dmacno, uint32_t chflags)
{
struct sam_xdmac_s *xdmac;
struct sam_xdmach_s *xdmach;
unsigned int chndx;
int ret;
#ifdef CONFIG_SAMA5_XDMAC0
if (dmacno == 0)
{
xdmac = &g_xdmac0;
}
else
#endif
#ifdef CONFIG_SAMA5_XDMAC1
if (dmacno == 1)
{
xdmac = &g_xdmac1;
}
else
#endif
{
dmaerr("ERROR: Bad XDMAC number: %d\n", dmacno);
DEBUGPANIC();
return NULL;
}
* size.
*/
xdmach = NULL;
ret = nxmutex_lock(&xdmac->chlock);
if (ret < 0)
{
return NULL;
}
for (chndx = 0; chndx < SAM_NDMACHAN; chndx++)
{
struct sam_xdmach_s *candidate = &xdmac->xdmach[chndx];
if (!candidate->inuse)
{
xdmach = candidate;
xdmach->inuse = true;
* Channel Interrupt Status (CIS) Register
*/
sam_getdmach(xdmach, SAM_XDMACH_CIS_OFFSET);
* Channel Disable (GD) Register
*/
sam_putdmac(xdmac, XDMAC_CHAN(chndx), SAM_XDMAC_GD_OFFSET);
xdmach->flags = chflags;
break;
}
}
nxmutex_unlock(&xdmac->chlock);
if (xdmach)
{
dmainfo("XDMAC%d CH%d: chflags: %08x returning xdmach: %p\n",
(int)dmacno, xdmach->chan, (int)chflags, xdmach);
}
else
{
dmaerr("ERROR: Failed allocate XDMAC%d channel\n", (int)dmacno);
}
return (DMA_HANDLE)xdmach;
}
* Name: sam_dmaconfig
*
* Description:
* There are two channel usage models: (1) The channel is allocated and
* configured in one step. This is the typical case where a DMA channel
* performs a constant role. The alternative is (2) where the DMA channel
* is reconfigured on the fly.
* In this case, the chflags provided to sam_dmachannel are not used and
* sam_dmaconfig() is called before each DMA to configure the DMA channel
* appropriately.
*
* Returned Value:
* None
*
****************************************************************************/
void sam_dmaconfig(DMA_HANDLE handle, uint32_t chflags)
{
struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
xdmach->flags = chflags;
#if defined(CONFIG_SAMA5_XDMAC0) && defined(CONFIG_SAMA5_XDMAC1)
dmainfo("XDMAC%d CH%d: chflags: %08x\n",
xdmach->xdmac, xdmach->chan, (int)chflags);
#elif defined(CONFIG_SAMA5_XDMAC0)
dmainfo("XDMAC0 CH%d: chflags: %08x\n",
xdmach->chan, (int)chflags);
#else
dmainfo("XDMAC1 CH%d: chflags: %08x\n",
xdmach->chan, (int)chflags);
#endif
}
* Name: sam_dmafree
*
* Description:
* Release a DMA channel. NOTE: The 'handle' used in this argument must
* NEVER be used again until sam_dmachannel() is called again to re-gain
* a valid handle.
*
* Returned Value:
* None
*
****************************************************************************/
void sam_dmafree(DMA_HANDLE handle)
{
struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
struct sam_xdmac_s *xdmac;
dmainfo("xdmach: %p\n", xdmach);
DEBUGASSERT((xdmach != NULL) && (xdmach->inuse));
xdmac = sam_controller(xdmach);
* Global Channel Disable (GD) Register
*/
sam_putdmac(xdmac, XDMAC_CHAN(xdmach->chan), SAM_XDMAC_GD_OFFSET);
* operation and so should be safe.
*/
xdmach->flags = 0;
xdmach->inuse = false;
}
* Name: sam_dmatxsetup
*
* Description:
* Configure DMA for transmit of one buffer (memory to peripheral). This
* function may be called multiple times to handle large and/or dis-
* continuous transfers. Calls to sam_dmatxsetup() and sam_dmarxsetup()
* must not be intermixed on the same transfer, however.
*
****************************************************************************/
int sam_dmatxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
size_t nbytes)
{
struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
size_t maxtransfer;
size_t remaining;
int ret = OK;
dmainfo("xdmach: %p paddr: %08x maddr: %08x nbytes: %d\n",
xdmach, (int)paddr, (int)maddr, (int)nbytes);
DEBUGASSERT(xdmach);
dmainfo("llhead: %p lltail: %p\n", xdmach->llhead, xdmach->lltail);
* transfers and the number of bytes per transfer.
*/
maxtransfer = sam_maxtransfer(xdmach);
remaining = nbytes;
while (remaining > maxtransfer)
{
ret = sam_txbuffer(xdmach, paddr, maddr, maxtransfer);
if (ret == OK)
{
remaining -= maxtransfer;
* to do so).
*/
if ((xdmach->flags & DMACH_FLAG_PERIPHINCREMENT) != 0)
{
paddr += maxtransfer;
}
if ((xdmach->flags & DMACH_FLAG_MEMINCREMENT) != 0)
{
maddr += maxtransfer;
}
}
}
if (ret == OK && remaining > 0)
{
ret = sam_txbuffer(xdmach, paddr, maddr, remaining);
}
* that this was not an RX transfer.
*/
xdmach->rx = false;
up_clean_dcache(maddr, maddr + nbytes);
return ret;
}
* Name: sam_dmarxsetup
*
* Description:
* Configure DMA for receipt of one buffer (peripheral to memory). This
* function may be called multiple times to handle large and/or dis-
* continuous transfers. Calls to sam_dmatxsetup() and sam_dmarxsetup()
* must not be intermixed on the same transfer, however.
*
****************************************************************************/
int sam_dmarxsetup(DMA_HANDLE handle, uint32_t paddr, uint32_t maddr,
size_t nbytes)
{
struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
size_t maxtransfer;
size_t remaining;
int ret = OK;
dmainfo("xdmach: %p paddr: %08x maddr: %08x nbytes: %d\n",
xdmach, (int)paddr, (int)maddr, (int)nbytes);
DEBUGASSERT(xdmach);
dmainfo("llhead: %p lltail: %p\n", xdmach->llhead, xdmach->lltail);
* transfers and the number of bytes per transfer.
*/
maxtransfer = sam_maxtransfer(xdmach);
remaining = nbytes;
while (remaining > maxtransfer)
{
ret = sam_rxbuffer(xdmach, paddr, maddr, maxtransfer);
if (ret == OK)
{
remaining -= maxtransfer;
* to do so).
*/
if ((xdmach->flags & DMACH_FLAG_PERIPHINCREMENT) != 0)
{
paddr += maxtransfer;
}
if ((xdmach->flags & DMACH_FLAG_MEMINCREMENT) != 0)
{
maddr += maxtransfer;
}
}
}
if (ret == OK && remaining > 0)
{
ret = sam_rxbuffer(xdmach, paddr, maddr, remaining);
}
* that this was an RX transfer and will invalidate the cache.
*/
xdmach->rx = true;
xdmach->rxaddr = maddr;
xdmach->rxsize = (xdmach->flags & DMACH_FLAG_MEMINCREMENT) != 0 ?
nbytes : sizeof(uint32_t);
up_clean_dcache(maddr, maddr + nbytes);
return ret;
}
* Name: sam_dmastart
*
* Description:
* Start the DMA transfer
*
****************************************************************************/
int sam_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg)
{
struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
int ret = -EINVAL;
dmainfo("xdmach: %p callback: %p arg: %p\n", xdmach, callback, arg);
DEBUGASSERT(xdmach != NULL);
* link list).
*/
if (xdmach->llhead)
{
* completes
*/
xdmach->callback = callback;
xdmach->arg = arg;
* invalidate the data cache to force reloading from memory when the
* DMA completes.
*/
if (xdmach->rx)
{
up_flush_dcache(xdmach->rxaddr, xdmach->rxaddr + xdmach->rxsize);
}
if (xdmach->llhead == xdmach->lltail)
{
ret = sam_single(xdmach);
}
else
{
ret = sam_multiple(xdmach);
}
}
return ret;
}
* Name: sam_dmastop
*
* Description:
* Cancel the DMA. After sam_dmastop() is called, the DMA channel is
* reset and sam_dmarx/txsetup() must be called before sam_dmastart() can
* be called again
*
****************************************************************************/
void sam_dmastop(DMA_HANDLE handle)
{
struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
irqstate_t flags;
dmainfo("xdmach: %p\n", xdmach);
DEBUGASSERT(xdmach != NULL);
flags = enter_critical_section();
sam_dmaterminate(xdmach, -EINTR);
leave_critical_section(flags);
}
* Name: sam_dmasample
*
* Description:
* Sample DMA register contents
*
* Assumptions:
* - DMA handle allocated by sam_dmachannel()
*
****************************************************************************/
#ifdef CONFIG_DEBUG_DMA_INFO
void sam_dmasample(DMA_HANDLE handle, struct sam_dmaregs_s *regs)
{
struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
struct sam_xdmac_s *xdmac = sam_controller(xdmach);
irqstate_t flags;
* that should be okay IF interrupts are enabled when this function is
* called. But there is a race condition where this instrumentation could
* cause lost interrupts.
*/
flags = enter_critical_section();
regs->gtype = sam_getdmac(xdmac, SAM_XDMAC_GTYPE_OFFSET);
regs->gcfg = sam_getdmac(xdmac, SAM_XDMAC_GCFG_OFFSET);
regs->gwac = sam_getdmac(xdmac, SAM_XDMAC_GWAC_OFFSET);
regs->gim = sam_getdmac(xdmac, SAM_XDMAC_GIM_OFFSET);
regs->gis = sam_getdmac(xdmac, SAM_XDMAC_GIS_OFFSET);
regs->gs = sam_getdmac(xdmac, SAM_XDMAC_GS_OFFSET);
regs->grs = sam_getdmac(xdmac, SAM_XDMAC_GRS_OFFSET);
regs->gws = sam_getdmac(xdmac, SAM_XDMAC_GWS_OFFSET);
regs->gsws = sam_getdmac(xdmac, SAM_XDMAC_GSWS_OFFSET);
regs->cim = sam_getdmach(xdmach, SAM_XDMACH_CIM_OFFSET);
regs->cis = sam_getdmach(xdmach, SAM_XDMACH_CIS_OFFSET);
regs->csa = sam_getdmach(xdmach, SAM_XDMACH_CSA_OFFSET);
regs->cda = sam_getdmach(xdmach, SAM_XDMACH_CDA_OFFSET);
regs->cnda = sam_getdmach(xdmach, SAM_XDMACH_CNDA_OFFSET);
regs->cndc = sam_getdmach(xdmach, SAM_XDMACH_CNDC_OFFSET);
regs->cubc = sam_getdmach(xdmach, SAM_XDMACH_CUBC_OFFSET);
regs->cbc = sam_getdmach(xdmach, SAM_XDMACH_CBC_OFFSET);
regs->cc = sam_getdmach(xdmach, SAM_XDMACH_CC_OFFSET);
regs->cdsmsp = sam_getdmach(xdmach, SAM_XDMACH_CDSMSP_OFFSET);
regs->csus = sam_getdmach(xdmach, SAM_XDMACH_CSUS_OFFSET);
regs->cdus = sam_getdmach(xdmach, SAM_XDMACH_CDUS_OFFSET);
leave_critical_section(flags);
}
#endif
* Name: sam_dmadump
*
* Description:
* Dump previously sampled DMA register contents
*
* Assumptions:
* - DMA handle allocated by sam_dmachannel()
*
****************************************************************************/
#ifdef CONFIG_DEBUG_DMA_INFO
void sam_dmadump(DMA_HANDLE handle, const struct sam_dmaregs_s *regs,
const char *msg)
{
struct sam_xdmach_s *xdmach = (struct sam_xdmach_s *)handle;
struct sam_xdmac_s *xdmac = sam_controller(xdmach);
dmainfo("%s\n", msg);
dmainfo(" DMA Global Registers:\n");
dmainfo(" GTYPE[%08x]: %08x\n",
xdmac->base + SAM_XDMAC_GTYPE_OFFSET, regs->gtype);
dmainfo(" GCFG[%08x]: %08x\n",
xdmac->base + SAM_XDMAC_GCFG_OFFSET, regs->gcfg);
dmainfo(" GWAC[%08x]: %08x\n",
xdmac->base + SAM_XDMAC_GWAC_OFFSET, regs->gwac);
dmainfo(" GIM[%08x]: %08x\n",
xdmac->base + SAM_XDMAC_GIM_OFFSET, regs->gim);
dmainfo(" GIS[%08x]: %08x\n",
xdmac->base + SAM_XDMAC_GIS_OFFSET, regs->gis);
dmainfo(" GS[%08x]: %08x\n",
xdmac->base + SAM_XDMAC_GS_OFFSET, regs->gs);
dmainfo(" GRS[%08x]: %08x\n",
xdmac->base + SAM_XDMAC_GRS_OFFSET, regs->grs);
dmainfo(" GWS[%08x]: %08x\n",
xdmac->base + SAM_XDMAC_GWS_OFFSET, regs->gws);
dmainfo(" GSWS[%08x]: %08x\n",
xdmac->base + SAM_XDMAC_GSWS_OFFSET, regs->gsws);
dmainfo(" DMA Channel Registers:\n");
dmainfo(" CIM[%08x]: %08x\n",
xdmach->base + SAM_XDMACH_CIM_OFFSET, regs->cim);
dmainfo(" CIS[%08x]: %08x\n",
xdmach->base + SAM_XDMACH_CIS_OFFSET, regs->cis);
dmainfo(" CSA[%08x]: %08x\n",
xdmach->base + SAM_XDMACH_CSA_OFFSET, regs->csa);
dmainfo(" CDA[%08x]: %08x\n",
xdmach->base + SAM_XDMACH_CDA_OFFSET, regs->cda);
dmainfo(" CNDA[%08x]: %08x\n",
xdmach->base + SAM_XDMACH_CNDA_OFFSET, regs->cnda);
dmainfo(" CNDC[%08x]: %08x\n",
xdmach->base + SAM_XDMACH_CNDC_OFFSET, regs->cndc);
dmainfo(" CUBC[%08x]: %08x\n",
xdmach->base + SAM_XDMACH_CUBC_OFFSET, regs->cubc);
dmainfo(" CBC[%08x]: %08x\n",
xdmach->base + SAM_XDMACH_CBC_OFFSET, regs->cbc);
dmainfo(" CC[%08x]: %08x\n",
xdmach->base + SAM_XDMACH_CC_OFFSET, regs->cc);
dmainfo(" CDSMSP[%08x]: %08x\n",
xdmach->base + SAM_XDMACH_CDSMSP_OFFSET, regs->cdsmsp);
dmainfo(" CSUS[%08x]: %08x\n",
xdmach->base + SAM_XDMACH_CSUS_OFFSET, regs->csus);
dmainfo(" CDUS[%08x]: %08x\n",
xdmach->base + SAM_XDMACH_CDUS_OFFSET, regs->cdus);
}
#endif
#endif