rt-thread-official/bsp/AE210P/driver/dma/dmad.c

2514 lines
74 KiB
C

/*****************************************************************************
*
* Copyright Andes Technology Corporation 2007-2008
* All Rights Reserved.
*
* Revision History:
*
* Aug.21.2007 Created.
****************************************************************************/
/*****************************************************************************
*
* FILE NAME VERSION
*
* dmad.c
*
* DESCRIPTION
*
* DMA controller driver internal supplement library.
*
* DATA STRUCTURES
*
* None
*
* DEPENDENCIES
*
* dmad.h
*
****************************************************************************/
#include "dmad.h"
#include "cache.h"
#include "bsp_hal.h"
// #define DMAD_POLLING
#define DMAD_AHB_MAX_CHANNELS DMAC_MAX_CHANNELS
#define DMAD_APB_MAX_CHANNELS APBBR_DMA_MAX_CHANNELS
#define DMAD_DRB_POOL_SIZE 128 /* 64 // 128*/
//#define DMAD_HISR_PRIORITY 0 // 0: highest, 2: lowest
#define DMAD_HISR_STACK_SIZE 4096 // Please align to 32-bit
#ifdef CONFIG_PLAT_AG101P_4GB
#define NTC0_BONDER_START 0x00000000
#define NTC0_BONDER_END 0x40000000
#else
#define NTC0_BONDER_START 0x00000000
#define NTC0_BONDER_END 0x00400000
#endif
/*
* DMA module is shared between drivers and has no specific
* initialization entry point. For this reason, it's stack
* pool is declared in the global data or bss section.
*/
static uint32_t dmad_hisr_stack[DMAD_HISR_STACK_SIZE];
/* Driver data structure, one instance per system */
typedef struct DMAD_DATA_STRUCT{
/* Driver data initialization flag */
uint32_t init; /* init flag for this object */
uint32_t drq_pool_mutex_init; /* init flag for DMA queue pool access control object */
uint32_t ahb_lisr_registered; /* init flag for AHB DMA LISR */
uint32_t apb_lisr_registered; /* init flag for APB DMA LISR */
uint32_t hisr_registered; /* init flag for HISR */
/* DMA queue pool access control object */
hal_mutex_t drq_pool_mutex; /* Mutex for access control of DRQ (DMA Request Queue) pool between tasks */
/* DMA HISR resources */
hal_bh_t hisr; /* HISR kernel object, used to perform deffered tasks of DMA LISR */
uint32_t hisr_as; /* HISR activation state (for the single HISR to identify who activated it) */
} DMAD_DATA;
/* Driver data structure instance, one instance per system */
static DMAD_DATA dmad;
/* DMA request queue, one instance per channel */
typedef struct DMAD_DRQ_STRUCT{
uint32_t allocated; /* Flag to indicate the channel allocation status */
DMAD_DATA *dmad; /* Pointer to driver object (DMAD_DATA) */
uint32_t channel_base; /* DMA channel register base address */
hal_mutex_t drb_pool_mutex; /* Mutex for access control of DRB (DMA Request Block) pool between tasks */
DMAD_DRB drb_pool[DMAD_DRB_POOL_SIZE]; /* DRB (DMA Request Block) pool for this channel */
hal_semaphore_t drb_sem;
uint32_t fre_head; /* Free(un-allocated, completed) DRB linked list head */
uint32_t fre_tail; /* Free(un-allocated, completed) DRB linked list tail */
uint32_t rdy_head; /* Ready(allocated, un-submitted) DRB linked list head */
uint32_t rdy_tail; /* Ready(allocated, un-submitted) DRB linked list tail */
uint32_t sbt_head; /* Submitted DRB linked list head */
uint32_t sbt_tail; /* Submitted DRB linked list tail */
uint32_t cpl_head; /* Completed (those need to notify client) DRB linked list head */
uint32_t cpl_tail; /* Completed (those need to notify client) DRB linked list tail */
/*
* cache writeback function
*
* source destination writeback invalidate function
* ---------------------------------------------------------------------------------------------------
* memory -> memory v (for src data) v (for dest readback) NDS_DCache_Invalidate_Flush()
* device -> memory v (for invalidate) v (for dest readback) NDS_DCache_Invalidate_Flush()
* memory -> device v (for src data) x NDS_DCache_Flush()
* device -> device x x null
*/
void (*dc_writeback)(unsigned long start, unsigned long end);
void (*dc_invalidate)(unsigned long start, unsigned long end);
} DMAD_DRQ;
/* DMA queue for AHB DMA channels */
static DMAD_DRQ ahb_drq_pool[DMAD_AHB_MAX_CHANNELS];
/* DMA queue for APB DMA channels */
static DMAD_DRQ apb_drq_pool[DMAD_APB_MAX_CHANNELS];
/* AHB DMAC channel re-route table structure */
typedef struct _DMAD_AHB_CH_ROUTE {
// uint32_t dev_reqn; /* device req/gnt number */
uint32_t route_cr; /* routing control register address */
} DMAD_AHB_CH_ROUTE;
/* AHB DMAC channel re-route table. Indexed by AHB DMAC req/ack number. */
static DMAD_AHB_CH_ROUTE dmad_ahb_ch_route_table[] = {
{ 0 },
{ PMU_CFC_REQACK_CFG }, /* CFC REQ/ACK connection configuration register */
{ PMU_SSP1_REQACK_CFG }, /* SSP1 REQ/ACK connection configuration register */
{ PMU_UART1TX_REQACK_CFG }, /* UART1 TX REQ/ACK connection configuration register */
{ PMU_UART1RX_REQACK_CFG }, /* UART1 RX REQ/ACK connection configuration register */
{ PMU_UART2TX_REQACK_CFG }, /* UART2 TX REQ/ACK connection configuration register */
{ PMU_UART2RX_REQACK_CFG }, /* UART2 RX REQ/ACK connection configuration register */
{ PMU_SDC_REQACK_CFG }, /* SDC REQ/ACK connection configuration register */
{ PMU_I2SAC97_REQACK_CFG }, /* I2S/AC97 REQ/ACK connection configuration register */
{ 0 },
{ 0 },
{ PMU_USB_REQACK_CFG }, /* USB 2.0 REQ/ACK connection configuration register */
{ 0 },
{ 0 },
{ PMU_EXT0_REQACK_CFG }, /* External device0 REQ/ACK connection configuration register */
{ PMU_EXT1_REQACK_CFG }, /* External device1 REQ/ACK connection configuration register */
};
/* APB Bridge DMA request number re-route table */
typedef struct _DMAD_APB_REQN_ROUTE{
// uint32_t apb_reqn; /* APB device req/gnt number */
uint32_t ahb_reqn; /* AHB DMAC req/ack number */
uint32_t bus_sel; /* Address selection: APBBR_ADDRSEL_APB(0) or APBBR_ADDRSEL_AHB(1) */
} DMAD_APB_REQN_ROUTE;
/* APB Bridge DMA request number re-route table. Indexed by APB DMA req/gnt number. */
static DMAD_APB_REQN_ROUTE dmad_apb_reqn_route_table[] = {
{ 0x00, APBBR_ADDRSEL_AHB }, /* APBBR_REQN_NONE */
{ 0x01, APBBR_ADDRSEL_APB }, /* APBBR_REQN_CFC */
{ 0x02, APBBR_ADDRSEL_APB }, /* APBBR_REQN_SSP */
{ 0x00, APBBR_ADDRSEL_AHB }, /* APB reserved */
{ 0x05, APBBR_ADDRSEL_APB }, /* APBBR_REQN_BTUART (AHB TX reqn: 5, AHB RX reqn: 6) */
{ 0x07, APBBR_ADDRSEL_APB }, /* APBBR_REQN_SDC */
{ 0x08, APBBR_ADDRSEL_APB }, /* APBBR_REQN_I2SAC97 */
{ 0x00, APBBR_ADDRSEL_AHB }, /* APB reserved */
{ 0x03, APBBR_ADDRSEL_APB }, /* APBBR_REQN_STUART (AHB TX reqn: 3, AHB RX reqn: 4) */
{ 0x00, APBBR_ADDRSEL_AHB }, /* APB reserved (comment out following fields to save code size) */
{ 0x00, APBBR_ADDRSEL_AHB }, /* APB reserved */
{ 0x00, APBBR_ADDRSEL_AHB }, /* APB reserved */
{ 0x00, APBBR_ADDRSEL_AHB }, /* APB reserved */
{ 0x00, APBBR_ADDRSEL_AHB }, /* APB reserved */
{ 0x00, APBBR_ADDRSEL_AHB }, /* APB reserved */
{ 0x00, APBBR_ADDRSEL_AHB }, /* APB reserved */
};
/* AHB DMA Request number */
/* Used to record DMA request numbers in different platform */
typedef struct _APB_DMA_REQN {
uint32_t xc5_reqn;
uint32_t xc7_reqn;
} APB_DMA_REQN;
static APB_DMA_REQN apb_dma_reqn_table[] = {
{APBBR_REQN_NONE, APBBR_REQN_NONE},//APB_REQN_NONE
/* REQN in XC5 */
{XC5_APBBR_REQN_CFC, APBBR_REQN_RESERVED},//APB_REQN_CFC
{XC5_APBBR_REQN_SSP, APBBR_REQN_RESERVED},//APB_REQN_SSP
{XC5_APBBR_REQN_BTUART, APBBR_REQN_RESERVED},//APBBR_REQN_BTUART
{XC5_APBBR_REQN_I2SAC97, XC7_APBBR_REQN_I2SAC97},//APB_REQN_I2SAC97
{XC5_APBBR_REQN_STUART, APBBR_REQN_RESERVED},//APB_REQN_STUART
{XC5_APBBR_REQN_I2S, APBBR_REQN_RESERVED},//APB_REQN_I2S
{XC5_APBBR_REQN_SSP2, APBBR_REQN_RESERVED},//APB_REQN_SSP2
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_EXTREQ0},//APB_REQN_EXT0
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_EXTREQ1},//APB_REQN_EXT1
/* REQN in XC7 */
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_SSP1TX},//APB_REQN_SSP1TX
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_SSP1RX},//APB_REQN_SSP1RX
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_UART2TX},//APB_REQN_UART2TX
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_UART2RX},//APB_REQN_UART2RX
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_UART4TX},//APB_REQN_UART4TX
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_UART4RX},//APB_REQN_UART4RX
{XC5_APBBR_REQN_SDC, XC7_APBBR_REQN_SDC},//APB_REQN_SDC
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_SSP2TX},//APB_REQN_SSP2TX
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_SSP2RX},//APB_REQN_SSP2RX
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_USB_2_0},//APB_REQN_USB_2_0
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_USB_1_1_EP1},//APB_REQN_USB_1_1_EP1
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_USB_1_1_EP2},//APB_REQN_USB_1_1_EP2
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_USB_1_1_EP3},//APB_REQN_USB_1_1_EP3
{APBBR_REQN_RESERVED, XC7_APBBR_REQN_USB_1_1_EP4},//AHB_REQN_USB_1_1_EP4
{XC5_APBBR_REQN_MAX, XC7_APBBR_REQN_MAX},//APB_REQN_MAX
};
/* AHB DMA Request number */
/* Used to record DMA request numbers in different platform */
typedef struct _AHB_DMA_REQN {
uint32_t xc5_reqn;
uint32_t xc7_reqn;
} AHB_DMA_REQN;
static AHB_DMA_REQN ahb_dma_reqn_table[] = {
{AHB_REQN_NONE, AHB_REQN_NONE},//AHB_REQN_NONE
/* REQN in XC5 */
{XC5_AHB_REQN_CFC, AHB_REQN_RESERVED},//AHB_REQN_CFC
{XC5_AHB_REQN_SSP, AHB_REQN_RESERVED},//AHB_REQN_SSP
{XC5_AHB_REQN_UART1TX, AHB_REQN_RESERVED},//AHB_REQN_UART1TX
{XC5_AHB_REQN_UART1RX, AHB_REQN_RESERVED},//AHB_REQN_UART1RX
{XC5_AHB_REQN_I2SAC97, XC7_AHB_REQN_I2SAC97},//AHB_REQN_I2SAC97
{XC5_AHB_REQN_USB, AHB_REQN_RESERVED},//AHB_REQN_USB
{XC5_AHB_REQN_EXT0, XC7_AHB_REQN_EXTREQ0},//AHB_REQN_EXT0
{XC5_AHB_REQN_EXT1, XC7_AHB_REQN_EXTREQ1},//AHB_REQN_EXT1
/* REQN in XC7 */
{AHB_REQN_RESERVED, XC7_AHB_REQN_SSP1TX},//AHB_REQN_SSP1TX
{AHB_REQN_RESERVED, XC7_AHB_REQN_SSP1RX},//AHB_REQN_SSP1RX
{AHB_REQN_RESERVED, XC7_AHB_REQN_UART2TX},//AHB_REQN_UART2TX
{AHB_REQN_RESERVED, XC7_AHB_REQN_UART2RX},//AHB_REQN_UART2RX
{AHB_REQN_RESERVED, XC7_AHB_REQN_UART4TX},//AHB_REQN_UART4TX
{AHB_REQN_RESERVED, XC7_AHB_REQN_UART4RX},//AHB_REQN_UART4RX
{XC5_AHB_REQN_SDC, XC7_AHB_REQN_SDC},//AHB_REQN_SDC
{AHB_REQN_RESERVED, XC7_AHB_REQN_SSP2TX},//AHB_REQN_SSP2TX
{AHB_REQN_RESERVED, XC7_AHB_REQN_SSP2RX},//AHB_REQN_SSP2RX
{AHB_REQN_RESERVED, XC7_AHB_REQN_USB_2_0},//AHB_REQN_USB_2_0
{AHB_REQN_RESERVED, XC7_AHB_REQN_USB_1_1_EP1},//AHB_REQN_USB_1_1_EP1
{AHB_REQN_RESERVED, XC7_AHB_REQN_USB_1_1_EP2},//AHB_REQN_USB_1_1_EP2
{AHB_REQN_RESERVED, XC7_AHB_REQN_USB_1_1_EP3},//AHB_REQN_USB_1_1_EP3
{AHB_REQN_RESERVED, XC7_AHB_REQN_USB_1_1_EP4},//AHB_REQN_USB_1_1_EP4
};
/*****************************************************************************
* FUNCTION
*
* _dmad_get_reqn
*
* DESCRIPTION
*
* Get DMA request number from various platform.
*
* INPUTS
*
* dma_controller : (in) AHB or APB
* device : (in) Device and peripheral.
*
* OUTPUTS
*
* none
*
****************************************************************************/
uint32_t _dmad_get_reqn(uint32_t dma_controller, uint32_t device){
uint32_t reqn;
uint32_t platform_id = IN32(PMU_IDNMBR0);
if (dma_controller == DMAD_DMAC_APB_CORE){ /* APB */
if ((platform_id & PRODUCT_ID_MASK) == AG101P_EMERALD)
reqn = apb_dma_reqn_table[device].xc7_reqn;
else
reqn = apb_dma_reqn_table[device].xc5_reqn;
} else { /* AHB */
if ((platform_id & PRODUCT_ID_MASK) == AG101P_EMERALD)
reqn = ahb_dma_reqn_table[device].xc7_reqn;
else
reqn = ahb_dma_reqn_table[device].xc5_reqn;
}
return reqn;
}
/*****************************************************************************
* FUNCTION
*
* _dmad_detach_node
*
* DESCRIPTION
*
* Detach a DRB node from a specified list. The list is acknowledged in the
* form of a head node and a tail one.
*
* INPUTS
*
* drb_pool : (in) The DRB pool of a DMA queue for a DMA channel
* head : (in/out) Pointer to the head node of the list
* tail : (in/out) Pointer to the tail node of the list
* node : (in) The node to detach from the list
*
* OUTPUTS
*
* none
*
****************************************************************************/
static void _dmad_detach_node(DMAD_DRB *drb_pool, uint32_t *head, uint32_t *tail, uint32_t node){
if (drb_pool[node].prev != 0){
/* prev->next = this->next (= 0, if this is a tail) */
drb_pool[drb_pool[node].prev].next = drb_pool[node].next;
}
else {
/* this node is head, move head to next node (= 0, if this is the only one node) */
*head = drb_pool[node].next;
}
if (drb_pool[node].next != 0){
/* next->prev = this->prev (= 0, if this is a head) */
drb_pool[drb_pool[node].next].prev = drb_pool[node].prev;
}
else {
/* this node is tail, move tail to previous node (= 0, if this is the only one node) */
*tail = drb_pool[node].prev;
}
drb_pool[node].prev = drb_pool[node].next = 0;
}
/*****************************************************************************
* FUNCTION
*
* _dmad_detach_head
*
* DESCRIPTION
*
* Detach a DRB node from the head of a specified list. The list is
* acknowledged in the form of a head node and a tail one.
*
* INPUTS
*
* drb_pool : (in) The DRB pool of a DMA queue for a DMA channel
* head : (in/out) Pointer to the head node of the list
* tail : (in/out) Pointer to the tail node of the list
* drb : (out) Reference to the detached node pointer
*
* OUTPUTS
*
* none
*
****************************************************************************/
static void _dmad_detach_head(DMAD_DRB *drb_pool, uint32_t *head, uint32_t *tail, DMAD_DRB **drb){
if (*head == 0){
*drb = HAL_NULL;
return;
}
*drb = &drb_pool[*head];
if ((*drb)->next != 0){
/* next->prev = this->prev (= 0, if this is a head) */
drb_pool[(*drb)->next].prev = 0;
/* prev->next = this->next (do nothing, if this is a head) */
/* head = this->next */
*head = (*drb)->next;
}
else {
/* head = tail = 0 */
*head = 0;
*tail = 0;
}
/* this->prev = this->next = 0 (do nothing, if save code size) */
(*drb)->prev = (*drb)->next = 0;
}
/*****************************************************************************
* FUNCTION
*
* _dmad_detach_tail
*
* DESCRIPTION
*
* Detach a DRB node from the tail of a specified list. The list is
* acknowledged in the form of a head node and a tail one.
*
* INPUTS
*
* drb_pool : (in) The DRB pool of a DMA queue for a DMA channel
* head : (in/out) Pointer to the head node of the list
* tail : (in/out) Pointer to the tail node of the list
* drb : (out) Reference to the detached node pointer
*
* OUTPUTS
*
* none
*
****************************************************************************/
static inline void _dmad_detach_tail(DMAD_DRB *drb_pool, uint32_t *head, uint32_t *tail, DMAD_DRB **drb){
if (*tail == 0){
*drb = HAL_NULL;
return;
}
*drb = &drb_pool[*tail];
if ((*drb)->prev != 0){
/* prev->next = this->next (= 0, if this is a tail) */
drb_pool[(*drb)->prev].next = 0;
/* next->prev = this->prev (do nothing, if this is a tail) */
/* tail = this->prev */
*tail = (*drb)->prev;
}
else {
/* head = tail = 0 */
*head = 0;
*tail = 0;
}
/* this->next = this->prev = 0 (do nothing, if save code size) */
(*drb)->prev = (*drb)->next = 0;
}
/*****************************************************************************
* FUNCTION
*
* _dmad_attach_head
*
* DESCRIPTION
*
* Attach a DRB node to the head of a specified list. The list is
* acknowledged in the form of a head node and a tail one.
*
* INPUTS
*
* drb_pool : (in) The DRB pool of a DMA queue for a DMA channel
* head : (in/out) Pointer to the head node of the list
* tail : (in/out) Pointer to the tail node of the list
* drb : (in) The node number of the node to attach
*
* OUTPUTS
*
* none
*
****************************************************************************/
static inline void _dmad_attach_head(DMAD_DRB *drb_pool, uint32_t *head, uint32_t *tail, uint32_t node){
if (*head != 0){
drb_pool[*head].prev = node; /* head->prev = this */
drb_pool[node].next = *head; /* this->next = head */
drb_pool[node].prev = 0; /* this->prev = 0 */
*head = node; /* head = node */
}
else {
*head = *tail = node; /* head = tail = node */
drb_pool[node].prev = drb_pool[node].next = 0;
}
}
/*****************************************************************************
* FUNCTION
*
* _dmad_attach_tail
*
* DESCRIPTION
*
* Attach a DRB node to the tail of a specified list. The list is
* acknowledged in the form of a head node and a tail one.
*
* INPUTS
*
* drb_pool : (in) The DRB pool of a DMA queue for a DMA channel
* head : (in/out) Pointer to the head node of the list
* tail : (in/out) Pointer to the tail node of the list
* drb : (in) The node number of the node to attach
*
* OUTPUTS
*
* none
*
****************************************************************************/
static void _dmad_attach_tail(DMAD_DRB *drb_pool, uint32_t *head, uint32_t *tail, uint32_t node){
if (*tail != 0){
drb_pool[*tail].next = node; /* tail->next = this */
drb_pool[node].prev = *tail; /* this->prev = tail */
drb_pool[node].next = 0; /* this->next = 0 */
*tail = node; /* tail = node */
}
else {
*head = *tail = node; /* head = tail = node */
drb_pool[node].prev = drb_pool[node].next = 0;
}
}
/*****************************************************************************
* FUNCTION
*
* _dmad_ahb_lisr
*
* DESCRIPTION
*
* This is the ISR that services all AHB DMA channels on the NDS32
* Integrator.
*
* NOTE
* Currently this ISR processes one channel at a time. This replies the
* assumption that the ISR will be invoked again as long as it's status
* bit remains not-cleared, if interrupts for multiple channels happens
* simultaneously.
*
* [todo] Above assumption may not be the real world case. Check it and
* implement processing of multiple channels at once in the ISR, if
* necessary.
*
* INPUTS
*
* vector : Interrupt vector number
*
* OUTPUTS
*
* none
*
****************************************************************************/
static void _dmad_ahb_lisr(int vector){
DMAD_DRQ *drq;
DMAD_DRB *drb;
uint32_t channel; /* interrupt channel number */
uint8_t tc_int = 0; /* interrupt reason is terminal count */
uint8_t err_int = 0; /* interrupt reason is DMA error */
//uint8_t abt_int = 0; /* interrupt reason is abort DMA transfer of this channel */
uint32_t prv_msk = 0;
if (vector != INTC_DMA_BIT)
hal_system_error(HAL_ERR_UNHANDLED_INTERRUPT);
prv_msk = hal_intc_irq_mask(IRQ_DMA_VECTOR);
/* Check DMA status register to get channel number */
for (channel = 0; channel < DMAD_AHB_MAX_CHANNELS; ++channel){
if (GETB32(DMAC_INT_TC, channel)){
tc_int = 1; /* Mark as TC int */
SETB32(DMAC_INT_TC_CLR, channel); /* DMAC INT TC status clear */
hal_intc_irq_clean(IRQ_DMA_VECTOR);
break;
}
}
/* Perform DMA error checking if no valid channel was found who assert the TC signal. */
if (channel == DMAD_AHB_MAX_CHANNELS){
for (channel = 0; channel < DMAD_AHB_MAX_CHANNELS; ++channel){
if (GETB32(DMAC_INT_ERRABT, channel + DMAC_INT_ERR_SHIFT)){
err_int = 1; /* Mark as ERR int */
SETB32(DMAC_INT_ERRABT_CLR, channel + DMAC_INT_ERR_CLR_SHIFT); /* DMAC INT ERR status clear */
hal_intc_irq_clean(IRQ_DMA_VECTOR);
break;
}
}
if (channel == DMAD_AHB_MAX_CHANNELS){
for (channel = 0; channel < DMAD_AHB_MAX_CHANNELS; ++channel){
if (GETB32(DMAC_INT_ERRABT, channel + DMAC_INT_ABT_SHIFT)){
//abt_int = 1; /* Mark as ABT int */
SETB32(DMAC_INT_ERRABT_CLR, channel + DMAC_INT_ABT_CLR_SHIFT); /* DMAC INT ABT status clear */
hal_intc_irq_clean(IRQ_DMA_VECTOR);
break;
}
}
if (channel == DMAD_AHB_MAX_CHANNELS){
/* Unknown reason ... (check why) */
hal_system_error(HAL_ERR_UNHANDLED_INTERRUPT); /*return; */
}
}
}
/* Lookup channel's DRQ (DMA Request Queue) */
drq = (DMAD_DRQ *)&ahb_drq_pool[channel];
/* DMAC */
/* Stop DMA channel temporarily */
CLRB32(drq->channel_base + DMAC_CSR_OFFSET, DMAC_CSR_CH_EN_BIT);
/*
* Lookup/detach latest submitted DRB (DMA Request Block) from
* the DRQ (DMA Request Queue), so ISR could kick off next DRB
*/
_dmad_detach_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb);
if (drb == HAL_NULL){
/* Check why DMA is triggered while submitted list is empty. */
hal_system_error(HAL_ERR_UNHANDLED_INTERRUPT); /*return; */
}
/* Enable nested interrupt */
GIE_ENABLE();
/* Notify that new node is going to be available in the free list */
if (drb->completion_sem != HAL_NULL){
dmad.hisr_as |= (1 << channel); /* [15:0] AHB channel indicator */
hal_raise_bh(&dmad.hisr); /* Call HISR to complete deferred tasks */
}
/* Process DRBs according to interrupt reason */
if (tc_int){
/* Mark DRB state as completed */
drb->state = DMAD_DRB_STATE_COMPLETED;
/* destination is memory */
if (drq->dc_invalidate != HAL_NULL && drb->dst_index == DMAC_REQN_NONE)
drq->dc_invalidate((unsigned long)(drb->dst_addr),
(unsigned long)(drb->dst_addr) + (unsigned long)(drb->transfer_size));
_dmad_attach_tail(drq->drb_pool, &drq->cpl_head, &drq->cpl_tail, drb->node);
/* Check whether there are pending requests in the DRQ */
if (drq->sbt_head != 0){
drb = &drq->drb_pool[drq->sbt_head]; /* Lookup next DRB (DMA Request Block) */
/* pre-submission-programming */
if (drb->psp)
drb->psp(drb->data);
/* Kick-off DMA for next DRB */
/* - Source and destination address */
OUT32(drq->channel_base + DMAC_SRC_ADDR_OFFSET, drb->src_addr);
OUT32(drq->channel_base + DMAC_DST_ADDR_OFFSET, drb->dst_addr);
/* - Transfer size (in units of source width) */
OUT32(drq->channel_base + DMAC_SIZE_OFFSET, drb->req_size);
/* - Re-enable DMA channel */
SETB32(drq->channel_base + DMAC_CSR_OFFSET, DMAC_CSR_CH_EN_BIT);
}
}
else if (err_int){
/* Mark DRB state as error */
drb->state = DMAD_DRB_STATE_ERROR;
_dmad_attach_tail(drq->drb_pool, &drq->cpl_head, &drq->cpl_tail, drb->node);
/* Check whether there are pending requests in the DRQ */
if (drq->sbt_head != 0){
/* Lookup next DRB (DMA Request Block) */
drb = &drq->drb_pool[drq->sbt_head];
/* pre-submission-programming */
if (drb->psp)
drb->psp(drb->data);
/*
* Kick-off DMA for next DRB
*/
/* Source and destination address */
OUT32(drq->channel_base + DMAC_SRC_ADDR_OFFSET, drb->src_addr);
OUT32(drq->channel_base + DMAC_DST_ADDR_OFFSET, drb->dst_addr);
/* Transfer size (in units of source width) */
OUT32(drq->channel_base + DMAC_SIZE_OFFSET, drb->req_size);
/* Re-enable DMA channel */
SETB32(drq->channel_base + DMAC_CSR_OFFSET, DMAC_CSR_CH_EN_BIT);
}
}
else { /* abt_int */
/* Remove all pending requests in the queue */
while (1){
/* Mark DRB state as abort */
drb->state = DMAD_DRB_STATE_ABORT;
_dmad_attach_tail(drq->drb_pool, &drq->cpl_head, &drq->cpl_tail, drb->node);
/* Detach next submitted DRB (DMA Request Block) from the DRQ (DMA Request Queue) */
_dmad_detach_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb);
if (drb == HAL_NULL)
break;
}
}
#ifdef DMAD_POLLING
if (dmad.hisr_as & 0x0000ffff){
while (drq->cpl_head != 0){
_dmad_detach_head(drq->drb_pool, &drq->cpl_head, &drq->cpl_tail, &drb);
_dmad_attach_tail(drq->drb_pool, &drq->fre_head, &drq->fre_tail, drb->node);
/* completion-of-submission-programming */
if (drb->rcp)
drb->rcp(drb->data);
}
}
#endif
GIE_DISABLE();
hal_intc_irq_unmask(prv_msk);
}
/*****************************************************************************
* FUNCTION
*
* _dmad_apb_lisr
*
* DESCRIPTION
*
* This is the ISR that services all APB DMA channels on the NDS32
* Integrator.
*
* NOTE
* Currently this ISR processes one channel at a time. This replies the
* assumption that the ISR will be invoked again as long as it's status
* bit remains not-cleared, if interrupts for multiple channels happens
* simultaneously.
*
* [todo] Above assumption may not be the real world case. Check it and
* implement processing of multiple channels at once in the ISR, if
* necessary.
*
* INPUTS
*
* vector : Interrupt vector number
*
* OUTPUTS
*
* none
*
****************************************************************************/
static void _dmad_apb_lisr(int vector){
DMAD_DRQ *drq;
DMAD_DRB *drb;
uint32_t channel; /* interrupt channel number */
uint8_t finish_int = 0; /* interrupt reason is transfer completed */
uint8_t err_int = 0; /* interrupt reason is DMA error */
uint32_t prv_msk = 0;
if (vector != INTC_APB_BIT)
hal_system_error(HAL_ERR_UNHANDLED_INTERRUPT);
/* Mask priority <= apb_bridge's interrupt */
prv_msk = hal_intc_irq_mask(IRQ_APBBRIDGE_VECTOR);
/* Check DMA status register to get channel number & clean pending */
for (channel = 0; channel < DMAD_APB_MAX_CHANNELS; ++channel){
uint32_t channel_base = APBBR_DMA_BASE_CH(channel);
if (GETB32(channel_base + APBBR_DMA_CMD_OFFSET, APBBR_DMA_FINTST_BIT)){
/* Mark as finish int */
finish_int = 1;
/* APB DMA finish int status clear */
CLRB32(channel_base + APBBR_DMA_CMD_OFFSET, APBBR_DMA_FINTST_BIT);
hal_intc_irq_clean(IRQ_APBBRIDGE_VECTOR);
break;
}
}
/* Perform DMA error checking if no valid channel was found who assert the finish signal
* & clean pending
*/
if (channel == DMAD_APB_MAX_CHANNELS){
for (channel = 0; channel < DMAD_APB_MAX_CHANNELS; ++channel){
uint32_t channel_base = APBBR_DMA_BASE_CH(channel);
if (GETB32(channel_base + APBBR_DMA_CMD_OFFSET, APBBR_DMA_ERRINTST_BIT)){
/* Mark as error int */
err_int = 1;
/* APB DMA error int status clear */
CLRB32(channel_base + APBBR_DMA_CMD_OFFSET, APBBR_DMA_ERRINTST_BIT);
hal_intc_irq_clean(IRQ_APBBRIDGE_VECTOR);
break;
}
}
if (channel == DMAD_AHB_MAX_CHANNELS)
hal_system_error(HAL_ERR_UNHANDLED_INTERRUPT);
}
/* Lookup channel's DRQ (DMA Request Queue) */
drq = (DMAD_DRQ *)&apb_drq_pool[channel];
/*
* APB
*/
/* Stop DMA channel temporarily */
CLRB32(drq->channel_base + APBBR_DMA_CMD_OFFSET, APBBR_DMA_CHEN_BIT);
/*
* Lookup/detach latest submitted DRB (DMA Request Block) from
* the DRQ (DMA Request Queue), so ISR could kick off next DRB
*/
_dmad_detach_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb);
if (drb == HAL_NULL){
/* Check why DMA is triggered while submitted list is empty. */
hal_system_error(HAL_ERR_UNHANDLED_INTERRUPT);
}
GIE_ENABLE();
/* Notify that new node is going to be available in the free list */
dmad.hisr_as |= (0x00010000 << channel); /* [31:16] APB channel indicator */
hal_raise_bh(&dmad.hisr); /* Call HISR to complete deferred tasks */
/* Process DRBs according to the cause of this interrupt */
if (finish_int){
/* Mark DRB state as completed */
drb->state = DMAD_DRB_STATE_COMPLETED;
_dmad_attach_tail(drq->drb_pool, &drq->cpl_head, &drq->cpl_tail, drb->node);
/* destination is memory */
if (drq->dc_invalidate != HAL_NULL && drb->dst_index == DMAC_REQN_NONE)
drq->dc_invalidate((unsigned long)(drb->dst_addr),
(unsigned long)(drb->dst_addr) + (unsigned long)(drb->transfer_size));
/* Check whether there are pending requests in the DRQ */
if (drq->sbt_head != 0){
/* Lookup next DRB (DMA Request Block) */
drb = &drq->drb_pool[drq->sbt_head];
/* pre-submission-programming */
if (drb->psp)
drb->psp(drb->data);
/*
* Kick-off DMA for next DRB
*/
/* Source and destination address */
OUT32(drq->channel_base + APBBR_DMA_SAD_OFFSET, drb->src_addr);
OUT32(drq->channel_base + APBBR_DMA_DAD_OFFSET, drb->dst_addr);
/* - Transfer size (in units of source width) */
OUT32(drq->channel_base + APBBR_DMA_CYC_OFFSET, drb->req_size & APBBR_DMA_CYC_MASK);
/* - Re-enable DMA channel */
SETB32(drq->channel_base + APBBR_DMA_CMD_OFFSET, APBBR_DMA_CHEN_BIT);
}
}
else if (err_int){
/* Remove all pending requests in the queue */
while (1){
/* Mark DRB state as abort */
drb->state = DMAD_DRB_STATE_ABORT;
_dmad_attach_tail(drq->drb_pool, &drq->cpl_head, &drq->cpl_tail, drb->node);
_dmad_detach_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb);
if (drb == HAL_NULL)
break;
}
}
#ifdef DMAD_POLLING
if (dmad.hisr_as & 0xffff0000){
while (drq->cpl_head != 0){
_dmad_detach_head(drq->drb_pool, &drq->cpl_head, &drq->cpl_tail, &drb);
_dmad_attach_tail(drq->drb_pool, &drq->fre_head, &drq->fre_tail, drb->node);
/* completion-of-submission-programming */
if (drb->rcp)
drb->rcp(drb->data);
}
dmad.hisr_as &= ~(0x00010000 << channel);
}
#endif
GIE_DISABLE();
hal_intc_irq_unmask(prv_msk);
}
/*****************************************************************************
* FUNCTION
*
* _dmad_hisr
*
* DESCRIPTION
*
* This HISR performs the defferred tasks from LISR.
*
* NOTE
*
* Current task list of this HISR
*
* - Signal DRQ available event for waiting DRQ allocations.
*
* INPUTS
*
* vector : Interrupt vector number
*
* OUTPUTS
*
* none
*
****************************************************************************/
static inline void _dmad_hisr(void *param){
DMAD_DRQ *drq;
DMAD_DRB *drb = NULL;
//uint32_t core_intl;
uint32_t channel;
while(1){
hal_bh_t *bh = (hal_bh_t *)param;
hal_pend_semaphore(&bh->sem, HAL_SUSPEND);
//core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
/* Signal free-list available event */
if ((dmad.hisr_as & 0xffff0000) != 0){
/* Disable apb_bridge interrupt to avoid race condition */
HAL_INTC_IRQ_ATOMIC_DISABLE(IRQ_APBBRIDGE_VECTOR);
/* APB LISR */
for (channel = 0; channel < DMAD_APB_MAX_CHANNELS; ++channel){
if (dmad.hisr_as & (0x00010000 << channel)){
drq = (DMAD_DRQ *)&apb_drq_pool[channel];
while (drq->cpl_head != 0){
_dmad_detach_head(drq->drb_pool, &drq->cpl_head, &drq->cpl_tail, &drb);
_dmad_attach_tail(drq->drb_pool, &drq->fre_head, &drq->fre_tail, drb->node);
hal_post_semaphore(&drq->drb_sem);
/* completion-of-submission-programming */
if (drb->rcp)
drb->rcp(drb->data);
if(drb->completion_sem != HAL_NULL)
{
// puts("APB DMA HISR Complete!!!\r\n");
hal_post_semaphore(drb->completion_sem);
}
}
dmad.hisr_as &= ~(0x00010000 << channel);
}
}
/* Re-enable apb_bridge interrupt */
HAL_INTC_IRQ_ATOMIC_ENABLE(IRQ_APBBRIDGE_VECTOR);
}
else if ((dmad.hisr_as & 0x0000ffff) != 0){
/* Disable AHB_DMA interrupt to avoid race condition */
HAL_INTC_IRQ_ATOMIC_DISABLE(IRQ_DMA_VECTOR);
/* AHB LISR */
for (channel = 0; channel < DMAD_AHB_MAX_CHANNELS; ++channel){
if (dmad.hisr_as & (1 << channel)){
drq = (DMAD_DRQ *)&ahb_drq_pool[channel];
while (drq->cpl_head != 0){
_dmad_detach_head(drq->drb_pool, &drq->cpl_head, &drq->cpl_tail, &drb);
_dmad_attach_tail(drq->drb_pool, &drq->fre_head, &drq->fre_tail, drb->node);
hal_post_semaphore(&drq->drb_sem);
/* completion-of-submission-programming */
if (drb->rcp)
drb->rcp(drb->data);
}
if (drb->completion_sem != HAL_NULL)
hal_post_semaphore(drb->completion_sem);
dmad.hisr_as &= ~(1 << channel);
}
}
HAL_INTC_IRQ_ATOMIC_ENABLE(IRQ_DMA_VECTOR);
}
// hal_global_int_ctl(core_intl);
}
}
/*****************************************************************************
* FUNCTION
*
* _dmad_channel_alloc
*
* DESCRIPTION
*
* This function allocates a DMA channel for client's request. If the
* channel is already used by other clients, then this function will
* fail the allocation.
*
* INPUTS
*
* ch_req : Pointer to the DMA request descriptor structure
* init : Specify whether to initialize the DMA channel HW if the
* allocation is successfull. Clients could also postpone
* the initialization task to the _dmad_channel_init()
* routine.
*
* OUTPUTS
*
* uint32_t : Returns HAL_SUCCESS if successful allocation,
* else positive value is DMAD-specific error code,
* else negative value is NU system error code.
*
****************************************************************************/
uint32_t _dmad_channel_alloc(DMAD_CHANNEL_REQUEST_DESC *ch_req, uint8_t init){
uint32_t status;
DMAD_DRQ *drq_iter;
DMAD_DRB *drb_iter;
uint32_t i = 0;
if (ch_req == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
if (ch_req->controller == DMAD_DMAC_AHB_CORE)
drq_iter = ahb_drq_pool;
else if (ch_req->controller == DMAD_DMAC_APB_CORE)
drq_iter = apb_drq_pool;
else
return HAL_ERR_NOT_PRESENT;
/* First-time initialization for DMA queue pool access control object */
if (dmad.drq_pool_mutex_init == 0){
status = hal_create_mutex(&dmad.drq_pool_mutex, "drqpool");
if (status != HAL_SUCCESS){
DMAD_TRACE(("[dmad] failed to create drq_pool mutex!\r\n"));
return status;
}
dmad.drq_pool_mutex_init = 1;
}
/* Obtain exclusive access to the pool of channel queues */
if (hal_current() != HAL_NULL){
/* Suspending is only valid to the current task -- no need to lock if invoked from HISR. */
status = hal_wait_for_mutex(&dmad.drq_pool_mutex, HAL_SUSPEND);
if (status != HAL_SUCCESS){
DMAD_TRACE(("[dmad] failed to lock drq_pool! status(0x%08lx)\r\n", status));
return status;
}
}
/* Locate an available DMA channel */
if (ch_req->controller == DMAD_DMAC_AHB_CORE){
#if 0
/* UART - TX/RX channel is limitted */
if ((ch_req->ahbch_req.src_index == DMAC_REQN_UART1TX) ||
(ch_req->ahbch_req.dst_index == DMAC_REQN_UART1TX) ||
(ch_req->ahbch_req.src_index == DMAC_REQN_UART2TX) ||
(ch_req->ahbch_req.dst_index == DMAC_REQN_UART2TX))
{
/* TX channel is limitied to C/D */
drq_iter = &ahb_drq_pool[2];
for (i = 2; i < 4; ++i, ++drq_iter){
if (drq_iter->allocated == 0)
break;
}
}
else if ((ch_req->ahbch_req.src_index == DMAC_REQN_UART1RX) ||
(ch_req->ahbch_req.dst_index == DMAC_REQN_UART1RX) ||
(ch_req->ahbch_req.src_index == DMAC_REQN_UART2RX) ||
(ch_req->ahbch_req.dst_index == DMAC_REQN_UART2RX)){
/* RX channel is limitied to A/B */
for (i = 0; i < 2; ++i, ++drq_iter){
if (drq_iter->allocated == 0)
break;
}
}
else
#endif
{
if ((ch_req->ahbch_req.src_index != DMAC_REQN_NONE) ||
(ch_req->ahbch_req.dst_index != DMAC_REQN_NONE)){
/*
* [2007-12-03] It looks current board have problem to do dma
* traffic for APB devices on DMAC channel 0/1. Redirect all
* APB devices to start from channel 2.
* [todo] include USB controller ?
*/
drq_iter = &ahb_drq_pool[2];
for (i = 2; i < DMAD_AHB_MAX_CHANNELS; ++i, ++drq_iter){
if (drq_iter->allocated == 0)
break;
}
}
else {
/* channel for other devices is free to allocate */
for (i = 0; i < DMAD_AHB_MAX_CHANNELS; ++i, ++drq_iter){
if (drq_iter->allocated == 0)
break;
}
}
}
if (i == DMAD_AHB_MAX_CHANNELS){
DMAD_TRACE(("out of available channels (AHB DMAC)!\r\n"));
return HAL_ERR_UNAVAILABLE;
}
DMAD_TRACE(("allocated channel: %d (AHB DMAC)\r\n"));
}
else if (ch_req->controller == DMAD_DMAC_APB_CORE){
for (i = 0; i < DMAD_APB_MAX_CHANNELS; ++i, ++drq_iter){
if (drq_iter->allocated == 0)
break;
}
if (i == DMAD_APB_MAX_CHANNELS){
DMAD_TRACE(("out of available channels (APB DMAC)!\r\n"));
return HAL_ERR_UNAVAILABLE;
}
DMAD_TRACE(("allocated channel: %d (APB DMAC)\r\n", i));
}
/* Allocate the DMA channel */
drq_iter->allocated = 1;
if (hal_current() != HAL_NULL){
/*
* Suspending is only valid to the current task -- no need to lock if invoked from HISR.
* Release exclusive access to the pool of channel queues
*/
status = hal_release_mutex(&dmad.drq_pool_mutex);
if (status != HAL_SUCCESS){
DMAD_TRACE(("[dmad] failed to unlock drq_pool!\r\n"));
return status;
}
}
/* Create mutex object for DMA queue access control */
status = hal_create_mutex(&drq_iter->drb_pool_mutex, "drq");
if (status != HAL_SUCCESS){
DEBUG(1, 1, "failed to create mutex for drb_pool!\n");
return status;
}
/* Create semaphores for free-list allocation operation */
status = hal_create_semaphore(&drq_iter->drb_sem, DMAD_DRB_POOL_SIZE - 1, (void*)0);
if (status != HAL_SUCCESS){
DEBUG(1, 1, "failed to create semaphores for drb_pool!\n");
return status;
}
/* Record the channel number in client's struct */
ch_req->channel = i;
/* Record the channel's queue handle in client's struct */
ch_req->drq = drq_iter;
if (ch_req->controller == DMAD_DMAC_AHB_CORE){
//drq_iter->controller_base = DMAC_BASE;
drq_iter->channel_base = DMAC_BASE_CH(i);
}
else {
//drq_iter->controller_base = APBBR_BASE;
drq_iter->channel_base = APBBR_DMA_BASE_CH(i);
}
/* Initialize DMA channel's DRB pool as list of free DRBs */
drb_iter = &drq_iter->drb_pool[0];
drb_iter->prev = 0;
drb_iter->next = 0;
drb_iter->node = 0;
//drb_iter->src_addr = 0;
//drb_iter->dst_addr = 0;
//drb_iter->req_size = 0;
++drb_iter;
for (i = 1; i < DMAD_DRB_POOL_SIZE; ++i, ++drb_iter){
drb_iter->prev = i - 1;
drb_iter->next = i + 1;
drb_iter->node = i;
//drb_iter->src_addr = 0;
//drb_iter->dst_addr = 0;
//drb_iter->req_size = 0;
}
drq_iter->drb_pool[DMAD_DRB_POOL_SIZE - 1].next = 0;
/* Initialize DMA channel's DRB free-list, ready-list, and submitted-list */
drq_iter->fre_head = 1;
drq_iter->fre_tail = DMAD_DRB_POOL_SIZE - 1;
drq_iter->rdy_head = drq_iter->rdy_tail = 0;
drq_iter->sbt_head = drq_iter->sbt_tail = 0;
drq_iter->cpl_head = drq_iter->cpl_tail = 0;
/* Initialize the channel */
if (init)
_dmad_channel_init(ch_req);
/* Initialize cache writeback function */
#ifndef CONFIG_CPU_DCACHE_ENABLE
drq_iter->dc_writeback = HAL_NULL;
drq_iter->dc_invalidate = HAL_NULL;
#else
drq_iter->dc_writeback = nds32_dma_flush_range;
drq_iter->dc_invalidate = nds32_dma_inv_range;
#endif
return HAL_SUCCESS;
}
/*****************************************************************************
* FUNCTION
*
* _dmad_channel_free
*
* DESCRIPTION
*
* This function frees a DMA channel for future clients' request.
*
* INPUTS
*
* ch_req : Pointer to the DMA request descriptor structure
*
* OUTPUTS
*
* uint32_t : Returns HAL_SUCCESS if successful channel free,
* else positive value is DMAD-specific error code,
* else negative value is NU system error code.
*
****************************************************************************/
uint32_t _dmad_channel_free(const DMAD_CHANNEL_REQUEST_DESC *ch_req){
uint32_t status;
DMAD_DRQ *drq;
if (ch_req == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
drq = (DMAD_DRQ *)ch_req->drq;
if (drq == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
if (drq->allocated == 0)
return HAL_ERR_INVALID_POINTER;
if (hal_current() != HAL_NULL){
/*
* Suspending is only valid to the current task -- no need to lock if invoked from HISR.
* Obtain exclusive access to the pool of channel queues
*/
status = hal_wait_for_mutex(&dmad.drq_pool_mutex, HAL_SUSPEND);
if (status != HAL_SUCCESS)
return status;
}
/* Todo: Stop/abort channel I/O if it's busy ? */
/* Delete mutex object of DMA queue access control */
status = hal_destroy_mutex(&drq->drb_pool_mutex);
if (status != HAL_SUCCESS)
return status;
/* Delete semaphores of free-list allocation operation */
status = hal_destroy_semaphore(&drq->drb_sem);
if (status != HAL_SUCCESS)
return status;
/* Reset HISR activation state */
if (ch_req->controller == DMAD_DMAC_AHB_CORE)
dmad.hisr_as &= ~(1 << ch_req->channel);
else
dmad.hisr_as &= ~(1 << (ch_req->channel + 16));
/* Set released flag. */
drq->allocated = 0;
if (hal_current() != HAL_NULL){
/*
* Suspending is only valid to the current task -- no need to lock if invoked from HISR.
* Release exclusive access to the pool of channel queues
*/
status = hal_release_mutex(&dmad.drq_pool_mutex);
if (status != HAL_SUCCESS)
return status;
}
return HAL_SUCCESS;
}
/*****************************************************************************
* FUNCTION
*
* _dmad_ahb_init
*
* DESCRIPTION
*
* This function performs the AHB DMAC channel initialization.
*
* INPUTS
*
* ch_req : Pointer to the DMA request descriptor structure
*
* OUTPUTS
*
* uint32_t : Returns HAL_SUCCESS if successful initialization,
* else positive value is DMAD-specific error code,
* else negative value is NU system error code.
*
****************************************************************************/
static uint32_t _dmad_ahb_init(const DMAD_CHANNEL_REQUEST_DESC *ch_req){
uint32_t status = HAL_SUCCESS;
DMAD_DRQ *drq = (DMAD_DRQ *)ch_req->drq;
DMAD_AHBCH_REQUEST *ahb_req = (DMAD_AHBCH_REQUEST *)(&ch_req->ahbch_req);
uint32_t channel = (uint32_t)ch_req->channel;
uint32_t channel_base = drq->channel_base;
uint32_t core_intl;
/* Register LISR */
if (dmad.ahb_lisr_registered == 0){
status = hal_register_isr(IRQ_DMA_VECTOR, _dmad_ahb_lisr, (void*)0);
// status = hal_register_isr(INTC_DMA_BIT, _dmad_ahb_lisr, (void*)0);
if (status != HAL_SUCCESS)
return status;
dmad.ahb_lisr_registered = 1;
}
core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
/* Following code require _safe_exit return path */
/* INTC */
/* Disable DMAC interrupt */
hal_intc_irq_disable(IRQ_DMA_VECTOR);
/* Clear DMAC interrupt status */
hal_intc_irq_clean(IRQ_DMA_VECTOR);
/* Setup DMAC interrupt trigger mode - level trigger */
/* Setup DMAC interrupt trigger level - assert high */
hal_intc_irq_config(IRQ_DMA_VECTOR, IRQ_LEVEL_TRIGGER, IRQ_ACTIVE_HIGH);
/* Enable DMAC interrupt */
hal_intc_irq_enable(IRQ_DMA_VECTOR);
#if 0
#if ( NO_EXTERNAL_INT_CTL == 1 )
/*
* IVIC without INTC
*/
/* FIXME add trigger mode */
/* Enable DMAC interupt */
SR_SETB32(NDS32_SR_INT_MASK2,IRQ_DMA_VECTOR);
#else
/*
* INTC
*/
/* Clear DMAC interrupt status */
SETB32(INTC_HW1_CLR, INTC_DMA_BIT);
/* Setup DMAC interrupt trigger mode - level trigger */
CLRB32(INTC_HW1_TMR, INTC_DMA_BIT);
/* Setup DMAC interrupt trigger level - assert high */
CLRB32(INTC_HW1_TLR, INTC_DMA_BIT);
/* Enable DMAC interrupt */
SETB32(INTC_HW1_ER, INTC_DMA_BIT);
#endif
#endif
/*
* PMU
*/
/*
* Route APB device DMA to an AHB DMAC channel and specify the channel
* number. (connection status could be read back from PMU_AHBDMA_REQACK
* register)
* Note: Only one device is routed per AHB DMA channel, the other target
* should be either (1) the same device (same reqn), or (2) the AHB
* device (reqn = 0).
*/
if (ahb_req->dst_index != PMU_REQN_NONE){
/* DMA transfer to device */
if ((ahb_req->dst_index > PMU_REQN_EXT1) ||
(dmad_ahb_ch_route_table[ahb_req->dst_index].route_cr == 0)){
status = HAL_ERR_NOT_PRESENT;
goto _safe_exit;
}
OUT32(dmad_ahb_ch_route_table[ahb_req->dst_index].route_cr,
PMU_DMACUSED_MASK | ((ahb_req->dst_reqn << PMU_CHANNEL_SHIFT) & PMU_CHANNEL_MASK));
}
else if (ahb_req->src_index != PMU_REQN_NONE){
/* DMA transfer from device */
if ((ahb_req->src_index > PMU_REQN_EXT1) ||
(dmad_ahb_ch_route_table[ahb_req->src_index].route_cr == 0)){
status = HAL_ERR_NOT_PRESENT;
goto _safe_exit;
}
OUT32(dmad_ahb_ch_route_table[ahb_req->src_index].route_cr,
PMU_DMACUSED_MASK | ((ahb_req->src_reqn << PMU_CHANNEL_SHIFT) & PMU_CHANNEL_MASK));
}
/*
* DMAC (Controller Setting)
* Note: Controller global setting actually should not placed in this channel
* specific setup routine. However, currently the only global setting
* is a fixed value, so it is ok to set it here. In this way, we save
* the effert to setup the global parameters elsewhere.
*/
/* INT TC/ERR/ABT status clear */
SETB32(DMAC_INT_TC_CLR, channel); /* TC clear */
SETB32(DMAC_INT_ERRABT_CLR, channel + DMAC_INT_ERR_CLR_SHIFT); /* ERR clear */
SETB32(DMAC_INT_ERRABT_CLR, channel + DMAC_INT_ABT_CLR_SHIFT); /* ABT clear */
/* CSR (enable DMAC, set M0 & M1 endian default to little endian transfer) */
OUT32(DMAC_CSR, DMAC_DMACEN_MASK |
((DMAC_ENDIAN_LITTLE << DMAC_M0ENDIAN_BIT) & DMAC_M0ENDIAN_MASK) |
((DMAC_ENDIAN_LITTLE << DMAC_M1ENDIAN_BIT) & DMAC_M1ENDIAN_MASK));
/* DMAC (Channel-Specific Setting) */
/* SYNC */
if (ahb_req->sync)
SETB32(DMAC_SYNC, channel);
else
CLRB32(DMAC_SYNC, channel);
/*
* Channel CSR
* CH_EN : 0 (disable)
* DST_SEL : 0 (Master 0)
* SRC_SEL : 0 (Master 0)
* DSTAD_CTL : ahb_req->dst_addr_ctrl
* SRCAD_CTL : ahb_req->src_addr_ctrl
* MODE : 0 (normal)
* DST_WIDTH : ahb_req->dst_width
* SRC_WIDTH : ahb_req->src_width
* ABT : 0 (not abort)
* SRC_SIZE : 0 (burst size = 1 byte)
* PROT1 : 0 (user mode)
* PROT2 : 0 (bot bufferable)
* PROT3 : 0 (not cacheable)
* CHPRI : ahb_req->priority
* DMA_FF_TH : 0 (FIA320 only, threshold = 1)
* TC_MSK : 0 (TC counter status enable)
*/
OUT32(channel_base + DMAC_CSR_OFFSET,
((ahb_req->src_width << DMAC_CSR_SRC_WIDTH_SHIFT) & DMAC_CSR_SRC_WIDTH_MASK) |
((ahb_req->src_addr_ctrl << DMAC_CSR_SRCAD_CTL_SHIFT) & DMAC_CSR_SRCAD_CTL_MASK) |
((ahb_req->dst_width << DMAC_CSR_DST_WIDTH_SHIFT) & DMAC_CSR_DST_WIDTH_MASK) |
((ahb_req->dst_addr_ctrl << DMAC_CSR_DSTAD_CTL_SHIFT) & DMAC_CSR_DSTAD_CTL_MASK) |
((ahb_req->priority << DMAC_CSR_CHPRI_SHIFT) & DMAC_CSR_CHPRI_MASK));
/* Channel CFG
* INT_TC_MSK : 0 (enable TC int)
* INT_ERR_MSK : 0 (enable ERR int)
* INT_ABT_MSK : 0 (enable ABT int)
* SRC_RS : 0
* SRC_HE : 0
* BUSY : r/o
* DST_RS : 0
* DST_HE : 0
* LLP_CNT : r/o
*/
OUT32(channel_base + DMAC_CFG_OFFSET, 0);
/*(DMAC_CFG_INT_TC_MSK | DMAC_CFG_INT_ERR_MSK | DMAC_CFG_INT_ABT_MSK)); */
#if 1 /* (Not found in AG101 spec -- has removed this setting?) */
/* - HW handshake mode: CSR & CFG */
if (ahb_req->hw_handshake != 0){
/* Channel CFG - Device REQN and HW-handshake mode */
uint32_t cfg = IN32(channel_base + DMAC_CFG_OFFSET);
if (ahb_req->src_index != DMAC_REQN_NONE){
OUT32(channel_base + DMAC_CFG_OFFSET, cfg |
((ahb_req->src_reqn << DMAC_CFG_INT_SRC_RS_SHIFT) & DMAC_CFG_INT_SRC_RS_MASK) |
((1 << DMAC_CFG_INT_SRC_HE_BIT) & DMAC_CFG_INT_SRC_HE_MASK) |
((0 << DMAC_CFG_INT_DST_RS_SHIFT) & DMAC_CFG_INT_DST_RS_MASK) |
((0 << DMAC_CFG_INT_DST_HE_BIT) & DMAC_CFG_INT_DST_HE_MASK));
}
else {
OUT32(channel_base + DMAC_CFG_OFFSET, cfg |
((0 << DMAC_CFG_INT_SRC_RS_SHIFT) & DMAC_CFG_INT_SRC_RS_MASK) |
((0 << DMAC_CFG_INT_SRC_HE_BIT) & DMAC_CFG_INT_SRC_HE_MASK) |
((ahb_req->dst_reqn << DMAC_CFG_INT_DST_RS_SHIFT) & DMAC_CFG_INT_DST_RS_MASK) |
((1 << DMAC_CFG_INT_DST_HE_BIT) & DMAC_CFG_INT_DST_HE_MASK));
}
/* Channel CSR - Enable HW-handshake mode */
SETB32(channel_base + DMAC_CSR_OFFSET, DMAC_CSR_MODE_BIT);
}
#endif
/* SRC_ADDR and DST_ADDR - not now */
/* LLP */
OUT32(channel_base + DMAC_LLP_OFFSET, 0);
/* TOT_SIZE - not now */
_safe_exit:
hal_global_int_ctl(core_intl);
return status;
}
/*****************************************************************************
* FUNCTION
*
* _dmad_apb_init
*
* DESCRIPTION
*
* This function performs the APB bridge DMA channel initialization.
*
* INPUTS
*
* ch_req : Pointer to the DMA request descriptor structure
*
* OUTPUTS
*
* uint32_t : Returns HAL_SUCCESS if successful initialization,
* else positive value is DMAD-specific error code,
* else negative value is NU system error code.
*
****************************************************************************/
static uint32_t _dmad_apb_init(const DMAD_CHANNEL_REQUEST_DESC *ch_req){
uint32_t status = HAL_SUCCESS;
DMAD_DRQ *drq = (DMAD_DRQ *)ch_req->drq;
DMAD_APBCH_REQUEST *apb_req = (DMAD_APBCH_REQUEST *)(&ch_req->apbch_req);
uint32_t channel = (uint32_t)ch_req->channel;
uint32_t channel_base = drq->channel_base;
uint32_t channel_cmd = 0;
uint32_t core_intl;
uint32_t dst_bus_sel;
uint32_t src_bus_sel;
/* Register LISR */
if (dmad.apb_lisr_registered == 0){
status = hal_register_isr(IRQ_APBBRIDGE_VECTOR , _dmad_apb_lisr, (void*)0);
if (status != HAL_SUCCESS)
return status;
dmad.apb_lisr_registered = 1;
}
core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
/* Following code require _safe_exit return path */
/* INTC */
/* Disable APB Bridge interrupt */
hal_intc_irq_disable(IRQ_APBBRIDGE_VECTOR);
/* Clear APB Bridge interrupt status */
hal_intc_irq_clean(IRQ_APBBRIDGE_VECTOR);
/* Setup APB Bridge interrupt trigger mode - level trigger */
/* Setup APB Bridge interrupt trigger level - assert high */
hal_intc_irq_config(IRQ_APBBRIDGE_VECTOR, IRQ_LEVEL_TRIGGER, IRQ_ACTIVE_HIGH);
/* Enable APB Bridge interrupt */
hal_intc_irq_enable(IRQ_APBBRIDGE_VECTOR);
#if 0
#if ( NO_EXTERNAL_INT_CTL == 1 )
/*
* IVIC without INTC
*/
/* FIXME add trigger mode */
/* Enable APB Bridge interrupt */
SR_SETB32(NDS32_SR_INT_MASK2,IRQ_APBBRIDGE_VECTOR);
#else
/*
* INTC
*/
/* Clear APB Bridge interrupt status */
SETB32(INTC_HW1_CLR, INTC_APB_BIT);
/* Setup APB Bridge interrupt trigger mode - level trigger */
CLRB32(INTC_HW1_TMR, INTC_APB_BIT);
/* Setup APB Bridge interrupt trigger level - assert high */
CLRB32(INTC_HW1_TLR, INTC_APB_BIT);
/* Enable APB Bridge interrupt */
SETB32(INTC_HW1_ER, INTC_APB_BIT);
#endif
#endif
/* PMU */
/* Check platform version */
uint32_t max_reqn = _dmad_get_reqn(DMAD_DMAC_APB_CORE, APB_MAX);
/*
* Undo APB device DMA to AHB DMAC channel routing. (connection status
* is obtained from reading back the PMU_AHBDMA_REQACK register)
*/
if ((apb_req->src_index > max_reqn) ||
(apb_req->dst_index > max_reqn)){
status = HAL_ERR_NOT_PRESENT;
goto _safe_exit;
}
if (apb_req->src_index != APBBR_REQN_NONE){ /* quick filter out non-APB reqn */
uint32_t ahb_ch;
/* Search for source device whether it is re-routed to AHB DMA channel */
for (ahb_ch = 0; ahb_ch < DMAD_AHB_MAX_CHANNELS; ++ahb_ch){
uint32_t ahb_reqn = (IN32(PMU_AHBDMA_REQACK) >> (ahb_ch << 2)) & 0x0000000f;
if ((ahb_reqn != APBBR_REQN_NONE) &&
(ahb_reqn == dmad_apb_reqn_route_table[apb_req->src_index].ahb_reqn)){
DMAD_TRACE(("src: re-route DMAC ch %2d to APB.\r\n", ahb_ch));
/* got it! un-route from AHB back to APB */
OUT32(dmad_ahb_ch_route_table[ahb_reqn].route_cr,
((channel << PMU_CHANNEL_SHIFT) & PMU_CHANNEL_MASK));
break;
}
}
}
if (apb_req->dst_index != APBBR_REQN_NONE){ /* quick filter out non-APB reqn */
uint32_t ahb_ch;
/* Search for source device whether it is re-routed to AHB DMA channel */
for (ahb_ch = 0; ahb_ch < DMAD_AHB_MAX_CHANNELS; ++ahb_ch){
uint32_t ahb_reqn = (IN32(PMU_AHBDMA_REQACK) >> (ahb_ch << 2)) & 0x0000000f;
if ((ahb_reqn != APBBR_REQN_NONE) &&
(ahb_reqn == dmad_apb_reqn_route_table[apb_req->dst_index].ahb_reqn)){
DMAD_TRACE(("dst: re-route DMAC ch %2d to APB.\r\n", ahb_ch));
/* got it! un-route from AHB back to APB */
OUT32(dmad_ahb_ch_route_table[ahb_reqn].route_cr,
((channel << PMU_CHANNEL_SHIFT) & PMU_CHANNEL_MASK));
break;
}
}
}
/* APB Bridge DMA (Channel Setting) */
/*
* - CMD
* ENBDIS : 0 (disable for now)
* FININTSTS : 0 (clear finishing interrupt status)
* FININTENB : 1 (enable finishing interrupt)
* BURMOD : apb_req->burst_mode
* ERRINTSTS : 0 (clear error interrupt status)
* ERRINTENB : 1 (enable error interrupt)
* SRCADRSEL : AHB/APB, driver auto-conf according to apb_req->src_index
* DESADRSEL : AHB/APB, driver auto-conf according to apb_req->dst_index
* SRCADR : apb_req->src_addr_ctrl
* DESADR : apb_req->dst_addr_ctrl
* REQSEL : apb_req->src_index (? a "req/gnt" device looks to be a src... check to use reqn of src or dst)
* DATAWIDTH : apb_req->data_width
*/
/*
* - CMD
* ENBDIS
* FININTSTS
* FININTENB
* BURMOD
* ERRINTSTS
* ERRINTENB
* SRCADR
* DESADR
* DATAWIDTH
*/
channel_cmd = ((uint32_t)APBBR_DMA_FINTEN_MASK | APBBR_DMA_ERRINTEN_MASK |
((apb_req->burst_mode << APBBR_DMA_BURST_BIT) & APBBR_DMA_BURST_MASK) |
((apb_req->src_addr_ctrl << APBBR_DMA_SRCADDRINC_SHIFT) & APBBR_DMA_SRCADDRINC_MASK) |
((apb_req->dst_addr_ctrl << APBBR_DMA_DSTADDRINC_SHIFT) & APBBR_DMA_DSTADDRINC_MASK) |
((apb_req->data_width << APBBR_DMA_DATAWIDTH_SHIFT) & APBBR_DMA_DATAWIDTH_MASK));
/*
* - CMD
* DSTADRSEL
* DREQSEL (todo: this is FIA320 bit-mask, check AG101 bit-mask location)
*/
if (apb_req->dst_index != APBBR_REQN_NONE)
dst_bus_sel = APBBR_ADDRSEL_APB;
else
dst_bus_sel = APBBR_ADDRSEL_AHB;
channel_cmd |= ((uint32_t)(APBBR_DMA_DSTADDRSEL_MASK &
(dst_bus_sel << APBBR_DMA_DSTADDRSEL_BIT)) |
(((uint32_t)apb_req->dst_index << APBBR_DMA_DREQSEL_SHIFT) & APBBR_DMA_DREQSEL_MASK));
/*
* - CMD
* SRCADRSEL
* SREQSEL (todo: this is FIA320 bit-mask, check AG101 bit-mask location)
*/
if (apb_req->src_index != APBBR_REQN_NONE)
src_bus_sel = APBBR_ADDRSEL_APB;
else
src_bus_sel = APBBR_ADDRSEL_AHB;
channel_cmd |= ((uint32_t)(APBBR_DMA_SRCADDRSEL_MASK &
(src_bus_sel << APBBR_DMA_SRCADDRSEL_BIT)) |
(((uint32_t)apb_req->src_index << APBBR_DMA_SREQSEL_SHIFT) & APBBR_DMA_SREQSEL_MASK));
/* - CMD outport */
OUT32(channel_base + APBBR_DMA_CMD_OFFSET, channel_cmd);
/* SRCADR and DESADR - not now */
/* CYC - not now */
_safe_exit:
hal_global_int_ctl(core_intl);
return status;
}
/*****************************************************************************
* FUNCTION
*
* _dmad_channel_init
*
* DESCRIPTION
*
* This function performs the DMA channel HW initialization abstraction.
* The real initialization task is dispatched according to the requested
* DMA controller type (AHB DMAC or APB bridge DMA controller).
*
* INPUTS
*
* ch_req : Pointer to the DMA request descriptor structure
*
* OUTPUTS
*
* uint32_t : Returns HAL_SUCCESS if successful initialization,
* else positive value is DMAD-specific error code,
* else negative value is NU system error code.
*
****************************************************************************/
uint32_t _dmad_channel_init(const DMAD_CHANNEL_REQUEST_DESC *ch_req){
uint32_t status;
DMAD_TRACE(("_dmad_channel_init\r\n"));
if (ch_req == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
if (ch_req->drq == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
/* Initialize DMA controller */
if (ch_req->controller == DMAD_DMAC_AHB_CORE)
status = _dmad_ahb_init(ch_req);
else
status = _dmad_apb_init(ch_req);
/* Register HISR to perform deffered DMA ISR tasks */
if (dmad.hisr_registered == 0){
printf("_dmad_channel_init Register HISR\n");
dmad.hisr.th.fn = _dmad_hisr;
dmad.hisr.th.arg = &dmad.hisr;
dmad.hisr.th.prio = CONFIG_DMAD_HISR_PRIORITY;
dmad.hisr.th.ptos = &dmad_hisr_stack[DMAD_HISR_STACK_SIZE];
dmad.hisr.th.stack_size = sizeof(dmad_hisr_stack);
dmad.hisr.th.name = "DMA BH";
status = hal_create_bh(&dmad.hisr);
if (status != HAL_SUCCESS)
return status;
dmad.hisr_registered = 1;
}
return status;
}
/*****************************************************************************
* FUNCTION
*
* _dmad_channel_enable
*
* DESCRIPTION
*
* This function is a abstraction routine to enable or disable a DMA
* channel.
*
* INPUTS
*
* ch_req : Pointer to the DMA request descriptor structure
*
* OUTPUTS
*
* uint32_t : Returns HAL_SUCCESS if successful enable/disable,
* else positive value is DMAD-specific error code,
* else negative value is NU system error code.
*
****************************************************************************/
uint32_t _dmad_channel_enable(const DMAD_CHANNEL_REQUEST_DESC *ch_req, uint8_t enable){
DMAD_DRQ *drq;
if (ch_req == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
drq = (DMAD_DRQ *)ch_req->drq;
if (drq == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
/* Enable/disable DMA channel */
if (ch_req->controller == DMAD_DMAC_AHB_CORE){
if (enable)
SETB32(drq->channel_base + DMAC_CSR_OFFSET, DMAC_CSR_CH_EN_BIT);
else
CLRB32(drq->channel_base + DMAC_CSR_OFFSET, DMAC_CSR_CH_EN_BIT);
}
else { /* APB */
if (enable)
SETB32(drq->channel_base + APBBR_DMA_CMD_OFFSET, APBBR_DMA_CHEN_BIT);
else
CLRB32(drq->channel_base + APBBR_DMA_CMD_OFFSET, APBBR_DMA_CHEN_BIT);
}
return HAL_SUCCESS;
}
/*****************************************************************************
* FUNCTION
*
* _dmad_alloc_drb
*
* DESCRIPTION
*
* This function is used to allocate a DRB (DMA request block) within a DMA
* channel. DRB is used to queue all DMA submission requests for the
* channel. Allocated DRB node is moved from the free-list to the ready-
* list.
*
* INPUTS
*
* ch_req : (in) Pointer to the DMA request descriptor structure
* drb : (out) Reference to the pointer of allocated DRB.
*
* OUTPUTS
*
* uint32_t : Returns HAL_SUCCESS if successful allocation,
* else positive value is DMAD-specific error code,
* else negative value is NU system error code.
*
****************************************************************************/
volatile int taskId=0;
uint32_t _dmad_alloc_drb(DMAD_CHANNEL_REQUEST_DESC *ch_req, DMAD_DRB **drb){
uint32_t status = HAL_SUCCESS;
DMAD_DRQ *drq;
uint32_t core_intl;
if (ch_req == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
drq = (DMAD_DRQ *)ch_req->drq;
if (drq == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
/* Obtain exclusive access to the drq from other tasks */
if (hal_current() != HAL_NULL){
/*
* Suspending is only valid to the current task -- no need to lock if invoked from HISR.
* Lock DMA queue to prevent been updated by other tasks
*/
status = hal_wait_for_mutex(&drq->drb_pool_mutex, HAL_SUSPEND);
if (status != HAL_SUCCESS)
return status;
}
/* Initialize drb ptr in case of fail allocation */
*drb = HAL_NULL;
#ifdef DMAD_POLLING
core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
while (drq->fre_head == 0){
/* Wait for free urbs. Sleep for 50 ms before polling again. */
hal_global_int_ctl(core_intl);
hal_sleep(50);
core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
}
#else
status = hal_pend_semaphore(&drq->drb_sem, HAL_SUSPEND);
if (status == HAL_ERR_TIMEOUT){
status = HAL_ERR_NO_MEMORY;
goto _safe_exit;
}
else if (status != HAL_SUCCESS){
goto _safe_exit;
}
core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
#endif
_dmad_detach_head(drq->drb_pool, &drq->fre_head, &drq->fre_tail, drb);
hal_global_int_ctl(core_intl);
_dmad_attach_tail(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail, (*drb)->node);
(*drb)->state = DMAD_DRB_STATE_READY;
(*drb)->completion_sem = HAL_NULL;
(*drb)->psp = HAL_NULL;
(*drb)->rcp = HAL_NULL;
if (ch_req->controller == DMAD_DMAC_AHB_CORE) {
(*drb)->src_index = ch_req->ahbch_req.src_index;
(*drb)->dst_index = ch_req->ahbch_req.dst_index;
} else if (ch_req->controller == DMAD_DMAC_APB_CORE) {
(*drb)->src_index = ch_req->apbch_req.src_index;
(*drb)->dst_index = ch_req->apbch_req.dst_index;
} else
status = HAL_ERR_NOT_PRESENT;
goto _safe_exit;
_safe_exit:
/* Release locking of this function from other tasks */
if (hal_current() != HAL_NULL){
/*
* Suspending is only valid to the current task -- no need to lock if invoked from HISR.
* Unlock DMA queue to allow its access from other tasks
*/
hal_release_mutex(&drq->drb_pool_mutex);
}
return status;
}
/*****************************************************************************
* FUNCTION
*
* _dmad_free_drb
*
* DESCRIPTION
*
* This function is used to free a DRB (DMA request block) within a DMA
* channel. DRB is used to queue all DMA submission requests for the
* channel. Freed DRB node is moved from the ready-list to the free-
* list.
*
* INPUTS
*
* ch_req : (in) Pointer to the DMA request descriptor structure
* drb : (in) Pointer of a DRB struct to be freed.
*
* OUTPUTS
*
* uint32_t : Returns HAL_SUCCESS if successful freeing,
* else positive value is DMAD-specific error code,
* else negative value is NU system error code.
*
****************************************************************************/
uint32_t _dmad_free_drb(DMAD_CHANNEL_REQUEST_DESC *ch_req, DMAD_DRB *drb){
uint32_t status = HAL_SUCCESS;
DMAD_DRQ *drq;
uint32_t core_intl;
if (ch_req == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
drq = (DMAD_DRQ *)ch_req->drq;
if (drq == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
/* Obtain exclusive access to the drq from other tasks */
if (hal_current() != HAL_NULL){
/* Suspending is only valid to the current task -- no need to lock if invoked from HISR. */
status = hal_wait_for_mutex(&drq->drb_pool_mutex, HAL_SUSPEND);
if (status != HAL_SUCCESS)
return status;
}
/* Following code require _safe_exit return path */
if ((drq->rdy_head == 0) || (drb->node == 0) ||
(drb->node >= DMAD_DRB_POOL_SIZE)){
DMAD_TRACE(("Ready-queue is empty or invalid node!\r\n"));
/* Unlock DMA queue to allow its access from other tasks */
status = HAL_ERR_INVALID_ENTRY;
goto _safe_exit;
}
core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
_dmad_detach_node(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail, drb->node);
_dmad_attach_tail(drq->drb_pool, &drq->fre_head, &drq->fre_tail, drb->node);
hal_global_int_ctl(core_intl);
drb->state = DMAD_DRB_STATE_FREE;
drb->completion_sem = HAL_NULL;
_safe_exit:
/* Release locking of this function from other tasks */
if (hal_current() != HAL_NULL){
/*
* Suspending is only valid to the current task -- no need to lock if invoked from HISR
* Unlock DMA queue to allow its access from other tasks
*/
hal_release_mutex(&drq->drb_pool_mutex);
}
return status;
}
int dmad_apb_config_dir(const DMAD_CHANNEL_REQUEST_DESC *ch_req, uint8_t dir)
{
uint32_t status = HAL_SUCCESS;
DMAD_DRQ *drq = (DMAD_DRQ *)ch_req->drq;
DMAD_APBCH_REQUEST *apb_req = (DMAD_APBCH_REQUEST *)(&ch_req->apbch_req);
uint32_t channel_base = drq->channel_base;
uint32_t channel_cmd = 0;
uint32_t dst_bus_sel;
uint32_t src_bus_sel;
channel_cmd = IN32(channel_base + APBBR_DMA_CMD_OFFSET);
channel_cmd &= ~(uint32_t)
(APBBR_DMA_SRCADDRINC_MASK | APBBR_DMA_DSTADDRINC_MASK |
APBBR_DMA_DSTADDRSEL_MASK | APBBR_DMA_DREQSEL_MASK |
APBBR_DMA_SRCADDRSEL_MASK | APBBR_DMA_SREQSEL_MASK);
if( dir == 0){
channel_cmd = ((uint32_t)APBBR_DMA_FINTEN_MASK | APBBR_DMA_ERRINTEN_MASK |
((apb_req->src_addr_ctrl << APBBR_DMA_SRCADDRINC_SHIFT) & APBBR_DMA_SRCADDRINC_MASK) |
((apb_req->dst_addr_ctrl << APBBR_DMA_DSTADDRINC_SHIFT) & APBBR_DMA_DSTADDRINC_MASK));
/*
* - CMD
* DSTADRSEL
* DREQSEL (todo: this is FIA320 bit-mask, check AG101 bit-mask location)
*/
if (apb_req->dst_index != APBBR_REQN_NONE)
dst_bus_sel = APBBR_ADDRSEL_APB;
else
dst_bus_sel = APBBR_ADDRSEL_AHB;
channel_cmd |= ((uint32_t)(APBBR_DMA_DSTADDRSEL_MASK &
(dst_bus_sel << APBBR_DMA_DSTADDRSEL_BIT)) |
(((uint32_t)apb_req->dst_index << APBBR_DMA_DREQSEL_SHIFT) & APBBR_DMA_DREQSEL_MASK));
/*
* - CMD
* SRCADRSEL
* SREQSEL (todo: this is FIA320 bit-mask, check AG101 bit-mask location)
*/
if (apb_req->src_index != APBBR_REQN_NONE)
src_bus_sel = APBBR_ADDRSEL_APB;
else
src_bus_sel = APBBR_ADDRSEL_AHB;
channel_cmd |= ((uint32_t)(APBBR_DMA_SRCADDRSEL_MASK &
(src_bus_sel << APBBR_DMA_SRCADDRSEL_BIT)) |
(((uint32_t)apb_req->src_index << APBBR_DMA_SREQSEL_SHIFT) & APBBR_DMA_SREQSEL_MASK));
/* - CMD outport */
OUT32(channel_base + APBBR_DMA_CMD_OFFSET, channel_cmd);
} else {
channel_cmd = ((uint32_t)APBBR_DMA_FINTEN_MASK | APBBR_DMA_ERRINTEN_MASK |
((apb_req->dst_addr_ctrl << APBBR_DMA_SRCADDRINC_SHIFT) & APBBR_DMA_SRCADDRINC_MASK) |
((apb_req->src_addr_ctrl << APBBR_DMA_DSTADDRINC_SHIFT) & APBBR_DMA_DSTADDRINC_MASK));
/*
* - CMD
* DSTADRSEL
* DREQSEL (todo: this is FIA320 bit-mask, check AG101 bit-mask location)
*/
if (apb_req->src_index != APBBR_REQN_NONE)
src_bus_sel = APBBR_ADDRSEL_APB;
else
src_bus_sel = APBBR_ADDRSEL_AHB;
channel_cmd |= ((uint32_t)(APBBR_DMA_DSTADDRSEL_MASK &
(src_bus_sel << APBBR_DMA_DSTADDRSEL_BIT)) |
(((uint32_t)apb_req->src_index << APBBR_DMA_DREQSEL_SHIFT) & APBBR_DMA_DREQSEL_MASK));
/*
* - CMD
* SRCADRSEL
* SREQSEL (todo: this is FIA320 bit-mask, check AG101 bit-mask location)
*/
if (apb_req->dst_index != APBBR_REQN_NONE)
dst_bus_sel = APBBR_ADDRSEL_APB;
else
dst_bus_sel = APBBR_ADDRSEL_AHB;
channel_cmd |= ((uint32_t)(APBBR_DMA_SRCADDRSEL_MASK &
(dst_bus_sel << APBBR_DMA_SRCADDRSEL_BIT)) |
(((uint32_t)apb_req->dst_index << APBBR_DMA_SREQSEL_SHIFT) & APBBR_DMA_SREQSEL_MASK));
/* - CMD outport */
OUT32(channel_base + APBBR_DMA_CMD_OFFSET, channel_cmd);
}
return status;
}
void set_drq_transfer_size(DMAD_CHANNEL_REQUEST_DESC *ch_req, DMAD_DRB *drb)
{
int data_width = -1;
if (ch_req->controller == DMAD_DMAC_AHB_CORE) {
/* AHB DMA */
DMAD_AHBCH_REQUEST *ahb_req = (DMAD_AHBCH_REQUEST *)(&ch_req->ahbch_req);
if (drb->src_index == DMAC_REQN_NONE && drb->src_index == DMAC_REQN_NONE)
data_width = 0;
else {
if (drb->src_index != DMAC_REQN_NONE)
data_width = 2 - ahb_req->src_width;
else if (drb->dst_index != DMAC_REQN_NONE)
data_width = 2 - ahb_req->dst_width;
}
} else {
/* APB DMA */
DMAD_APBCH_REQUEST *apb_req = (DMAD_APBCH_REQUEST *)(&ch_req->apbch_req);
data_width = 2 - apb_req->data_width;
}
if (data_width < 0)
KASSERT(1);
drb->transfer_size = drb->req_size << data_width;
}
/*****************************************************************************
* FUNCTION
*
* _dmad_submit_request
*
* DESCRIPTION
*
* This function is used to submit a DRB (DMA request block) to a DMA
* channel. DRB is used to queue all DMA submission requests for the
* channel. Submitted DRB node is moved from the ready-list to the
* submitted-list. DMA kick-off is performed automatically if the DMA
* transaction has not started. When the DRB is completed, it will be
* removed from the submittied-list to the free-list in the DMA ISR.
*
* INPUTS
*
* ch_req : (in) Pointer to the DMA request descriptor structure
* drb : (in) Pointer of a DRB struct to be submitted.
*
* OUTPUTS
*
* uint32_t : Returns HAL_SUCCESS if successful submission,
* else positive value is DMAD-specific error code,
* else negative value is NU system error code.
*
****************************************************************************/
uint32_t _dmad_submit_request(DMAD_CHANNEL_REQUEST_DESC *ch_req, DMAD_DRB *drb){
uint32_t status = HAL_SUCCESS;
DMAD_DRQ *drq;
uint32_t core_intl;
if (ch_req == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
drq = (DMAD_DRQ *)ch_req->drq;
if (drq == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
/* Obtain exclusive access to the drq from other tasks */
if (hal_current() != HAL_NULL){
/*
* Suspending is only valid to the current task -- no need to lock if invoked from HISR
* Lock DMA queue to prevent been updated by other tasks
*/
status = hal_wait_for_mutex(&drq->drb_pool_mutex, HAL_SUSPEND);
if (status != HAL_SUCCESS)
return status;
}
/* Following code require _safe_exit return path */
if ((drq->rdy_head == 0) || (drb->node == 0) || (drb->node >= DMAD_DRB_POOL_SIZE)){
status = HAL_ERR_INVALID_ENTRY;
goto _safe_exit;
}
_dmad_detach_node(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail, drb->node);
core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
/*
* writeback d-cache if necessary
*
* Note: Here we take the assumption that, after writeback, the memory
* contents is in physical ram and valid for for dma transfer.
* Hence, we only need to do writeback at the beginning of the drb
* submission, and ignore the writeback before kicking off every
* drb in isr.
*
* Place writeback code before interrupt-disable to shorten the
* disable time. This might generate a penalty of cache-miss
* if the writeback routine also invalidated the cache contents.
*/
set_drq_transfer_size(ch_req, drb);
#if ( defined(CONFIG_CPU_DCACHE_ENABLE) && !defined(CONFIG_CPU_DCACHE_WRITETHROUGH) )
/* source is memory */
//if (drq->dc_writeback != HAL_NULL && drb->src_index == DMAC_REQN_NONE)
if ( (unsigned long)drb->src_addr >= NTC0_BONDER_START && (unsigned long)drb->src_addr < NTC0_BONDER_END )//JUNIOR@2013/05/16
drq->dc_writeback((unsigned long)(drb->src_addr),(unsigned long)(drb->src_addr) + (unsigned long)(drb->transfer_size));
#endif
/* Check if submission is performed to an empty queue */
if (drq->sbt_head == 0){
_dmad_attach_tail(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, drb->node);
drb->state = DMAD_DRB_STATE_SUBMITTED;
hal_global_int_ctl(core_intl);
/* pre-submission-programming */
if (drb->psp)
drb->psp(drb->data);
/* DMA is not running, so kick off transmission */
if (ch_req->controller == DMAD_DMAC_AHB_CORE){ /* AHB */
/* Source and destination address */
OUT32(drq->channel_base + DMAC_SRC_ADDR_OFFSET, drb->src_addr);
OUT32(drq->channel_base + DMAC_DST_ADDR_OFFSET, drb->dst_addr);
/* Transfer size (in units of source width) */
OUT32(drq->channel_base + DMAC_SIZE_OFFSET, drb->req_size);
/* Enable DMA channel (Kick off transmission when client enable it's transfer state) */
SETB32(drq->channel_base + DMAC_CSR_OFFSET, DMAC_CSR_CH_EN_BIT);
}
else { /* APB */
/* Source and destination address */
OUT32(drq->channel_base + APBBR_DMA_SAD_OFFSET, drb->src_addr);
OUT32(drq->channel_base + APBBR_DMA_DAD_OFFSET, drb->dst_addr);
/* Transfer size (in units of source width) */
OUT32(drq->channel_base + APBBR_DMA_CYC_OFFSET, drb->req_size & APBBR_DMA_CYC_MASK);
/* Enable DMA channel (Kick off transmission when client enable it's transfer state) */
SETB32(drq->channel_base + APBBR_DMA_CMD_OFFSET, APBBR_DMA_CHEN_BIT);
}
}
else {
/* DMA is already running, so only queue DRB to the end of the list */
_dmad_attach_tail(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, drb->node);
drb->state = DMAD_DRB_STATE_SUBMITTED;
hal_global_int_ctl(core_intl);
}
_safe_exit:
/* Release locking of this function from other tasks */
if (hal_current() != HAL_NULL){
/*
* Suspending is only valid to the current task -- no need to lock if invoked from HISR
* Unlock DMA queue to allow its access from other tasks
*/
hal_release_mutex(&drq->drb_pool_mutex);
}
return status;
}
/*****************************************************************************
* FUNCTION
*
* _dmad_cancel_request
*
* DESCRIPTION
*
* This function is used to cancel a submitted DRB (DMA request block)
* of a DMA channel. DRB is used to queue all DMA submission requests for
* the channel. Submitted DRB node is moved from the ready-list to the
* submitted-list. Cancellation will fail if the DRB has already been
* kicked off and is waiting to be completed.
*
* INPUTS
*
* ch_req : (in) Pointer to the DMA request descriptor structure
* drb : (in) Pointer of a DRB struct to be cancelled.
*
* OUTPUTS
*
* uint32_t : Returns HAL_SUCCESS if successful cancellation,
* else positive value is DMAD-specific error code,
* else negative value is NU system error code.
*
****************************************************************************/
uint32_t _dmad_cancel_request(DMAD_CHANNEL_REQUEST_DESC *ch_req, DMAD_DRB *drb){
DMAD_DRQ *drq;;
if (ch_req == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
drq = (DMAD_DRQ *)ch_req->drq;
if (drq == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
if (drq->sbt_head == 0)
return HAL_ERR_INVALID_ENTRY;
if ((drb->node == 0) || (drb->node >= DMAD_DRB_POOL_SIZE))
return HAL_ERR_INVALID_ENTRY;
if (drb->completion_sem != HAL_NULL)
hal_destroy_semaphore(drb->completion_sem);
// NDS_DCache_Enable();
return HAL_ERR_UNAVAILABLE;
}
uint32_t _dmad_wait(DMAD_CHANNEL_REQUEST_DESC *ch_req){
uint32_t status = HAL_SUCCESS;
DMAD_DRQ *drq;
uint32_t core_intl;
if (ch_req == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
drq = (DMAD_DRQ *)ch_req->drq;
if (drq == HAL_NULL)
return HAL_ERR_INVALID_POINTER;
/* Obtain exclusive access to the drq from other tasks */
if (hal_current() != HAL_NULL){
/*
* Suspending is only valid to the current task -- no need to lock if invoked from HISR.
* Lock DMA queue to prevent been updated by other tasks
*/
status = hal_wait_for_mutex(&drq->drb_pool_mutex, HAL_SUSPEND);
if (status != HAL_SUCCESS)
return status;
}
#ifdef DMAD_POLLING
core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
while (drq->sbt_head != 0){
hal_global_int_ctl(core_intl);
hal_sleep(50);
core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
}
#else
status = hal_pend_semaphore(&drq->drb_sem, 300);
if (status == HAL_ERR_TIMEOUT){
status = HAL_ERR_NO_MEMORY;
goto _safe_exit;
}
else if (status != HAL_SUCCESS){
goto _safe_exit;
}
core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
#endif
hal_global_int_ctl(core_intl);
goto _safe_exit;
_safe_exit:
/* Release locking of this function from other tasks */
if (hal_current() != HAL_NULL){
/*
* Suspending is only valid to the current task -- no need to lock if invoked from HISR.
* Unlock DMA queue to allow its access from other tasks
*/
hal_release_mutex(&drq->drb_pool_mutex);
}
return status;
}