/***************************************************************************** * * 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; }