/* * GRPCI2 DMA Driver * * COPYRIGHT (c) 2017 * Cobham Gaisler AB * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #include #include #include #include #include #include /* for printk */ #include #include #include /* This driver has been prepared for SMP operation */ /*#define STATIC*/ #define STATIC static /*#define INLINE*/ #define INLINE inline /*#define UNUSED*/ #define UNUSED __attribute__((unused)) /*#define DEBUG 1*/ #ifdef DEBUG #define DBG(x...) printf(x) #else #define DBG(x...) #endif #define BD_CHAN_EN (1<regs->dma_ctrl, 0|DMACTRL_SAFE|DMACTRL_CHIRQ|DMACTRL_ERR); /* Clear DMA BASE */ REG_WRITE(&priv->regs->dma_bdbase, 0); /* Clear DMA Chan */ REG_WRITE(&priv->regs->dma_chact, 0); return 0; } /* Stop the DMA */ STATIC INLINE int grpci2dma_ctrl_stop( void ) { struct grpci2dma_priv *priv = grpci2dmapriv; /* Stop DMA */ unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl); REG_WRITE(&priv->regs->dma_ctrl, (ctrl & ~(DMACTRL_WCLEAR | DMACTRL_EN)) | DMACTRL_DIS); return 0; } /* Start the DMA */ STATIC INLINE int grpci2dma_ctrl_start( struct grpci2_bd_chan * chan) { struct grpci2dma_priv *priv = grpci2dmapriv; /* Set BDBASE to linked list of chans */ REG_WRITE(&priv->regs->dma_bdbase, (unsigned int) chan); /* Start DMA */ unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl); REG_WRITE(&priv->regs->dma_ctrl, (ctrl & ~(DMACTRL_WCLEAR | DMACTRL_DIS)) | DMACTRL_EN); return 0; } /* Resume the DMA */ STATIC INLINE int grpci2dma_ctrl_resume( void ) { struct grpci2dma_priv *priv = grpci2dmapriv; /* Resume DMA */ unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl); REG_WRITE(&priv->regs->dma_ctrl, (ctrl & ~(DMACTRL_WCLEAR | DMACTRL_DIS)) | DMACTRL_EN); return 0; } /* Interrupt status*/ STATIC INLINE int grpci2dma_ctrl_interrupt_status(void) { struct grpci2dma_priv *priv = grpci2dmapriv; unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl); return (ctrl & DMACTRL_IE); } /* Enable interrupts */ STATIC INLINE int grpci2dma_ctrl_interrupt_enable(void) { struct grpci2dma_priv *priv = grpci2dmapriv; unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl); if (ctrl & DMACTRL_IE){ /* Nothing to do. Already enabled */ return 0; } /* Clear pending CHIRQ and errors */ ctrl = ctrl | (DMACTRL_CHIRQ | DMACTRL_ERR); /* Enable interrupts */ ctrl = ctrl | DMACTRL_IE; REG_WRITE(&priv->regs->dma_ctrl, ctrl ); return 0; } /* Disable interrupts */ STATIC INLINE int grpci2dma_ctrl_interrupt_disable(void) { struct grpci2dma_priv *priv = grpci2dmapriv; unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl); if ((ctrl & DMACTRL_IE) == 0){ /* Nothing to do. Already disabled */ return 0; } /* Clear pending CHIRQ and errors */ ctrl = ctrl | (DMACTRL_CHIRQ | DMACTRL_ERR); /* Disable interrupts */ ctrl = ctrl & ~(DMACTRL_IE); REG_WRITE(&priv->regs->dma_ctrl, ctrl ); return 0; } /* Clear interrupts */ STATIC INLINE int grpci2dma_ctrl_interrupt_clear(void) { struct grpci2dma_priv *priv = grpci2dmapriv; unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl); REG_WRITE(&priv->regs->dma_ctrl, (ctrl | DMACTRL_ERR | DMACTRL_CHIRQ)); return 0; } STATIC INLINE unsigned int grpci2dma_ctrl_status() { struct grpci2dma_priv *priv = grpci2dmapriv; /* Read DMA */ return (REG_READ(&priv->regs->dma_ctrl)); } STATIC INLINE unsigned int grpci2dma_ctrl_base() { struct grpci2dma_priv *priv = grpci2dmapriv; /* Read DMA */ return (REG_READ(&priv->regs->dma_bdbase)); } UNUSED STATIC INLINE unsigned int grpci2dma_ctrl_active() { struct grpci2dma_priv *priv = grpci2dmapriv; /* Read DMA */ return (REG_READ(&priv->regs->dma_chact)); } /* Set the DMA CTRL register NUMCH field */ STATIC INLINE int grpci2dma_ctrl_numch_set(int numch) { struct grpci2dma_priv *priv = grpci2dmapriv; unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl); /* Clear old value */ ctrl = (ctrl & ~(DMACTRL_NUMCH)); /* Put new value */ ctrl = (ctrl | ( (numch << DMACTRL_NUMCH_BIT) & DMACTRL_NUMCH)); REG_WRITE(&priv->regs->dma_ctrl, ctrl & ~(DMACTRL_WCLEAR)); return 0; } /*** END OF DMACTRL ACCESS FUNCTIONS ***/ /*** START OF DESCRIPTOR ACCESS FUNCTIONS ***/ STATIC int grpci2dma_data_bd_init(struct grpci2_bd_data * data, uint32_t pci_adr, uint32_t ahb_adr, int dir, int endianness, int size, struct grpci2_bd_data * next) { BD_WRITE(&data->ctrl, 0 | (BD_DATA_EN) | (BD_DATA_TYPE_DATA) | (dir == GRPCI2DMA_AHBTOPCI? BD_DATA_DR:0) | (endianness == GRPCI2DMA_LITTLEENDIAN? BD_DATA_BE:0) | ( (size << BD_DATA_LEN_BIT) & BD_DATA_LEN ) ); BD_WRITE(&data->pci_adr, pci_adr); BD_WRITE(&data->ahb_adr, ahb_adr); BD_WRITE(&data->next, (unsigned int) next); return 0; } STATIC int grpci2dma_channel_bd_init(struct grpci2_bd_chan * chan) { BD_WRITE(&chan->ctrl, 0 | BD_CHAN_TYPE_DMA | BD_CHAN_EN); BD_WRITE(&chan->nchan, (unsigned int) chan); BD_WRITE(&chan->nbd, (unsigned int) DISABLED_DESCRIPTOR); return 0; } /* Enable a channel with options. * options include: * - options & 0xFFFF: Maximum data descriptor count before * moving to next DMA channel. */ STATIC int grpci2dma_channel_bd_enable(struct grpci2_bd_chan * chan, unsigned int options) { unsigned int ctrl = BD_READ(&chan->ctrl); ctrl = (ctrl & ~(BD_CHAN_BDCNT)); BD_WRITE(&chan->ctrl, (ctrl | BD_CHAN_EN | ( (options << BD_CHAN_BDCNT_BIT) & BD_CHAN_BDCNT))); return 0; } /* Disable channel. */ STATIC int grpci2dma_channel_bd_disable(struct grpci2_bd_chan * chan) { unsigned int ctrl = BD_READ(&chan->ctrl); BD_WRITE(&chan->ctrl, (ctrl & ~(BD_CHAN_EN))); return 0; } /* Get the CID of a channel. */ UNUSED STATIC int grpci2dma_channel_bd_get_cid(struct grpci2_bd_chan * chan) { /* Get cid from chan */ unsigned ctrl = BD_READ(&chan->ctrl); unsigned cid = (ctrl & (BD_CHAN_ID)) >> BD_CHAN_ID_BIT; return cid; } /* Set the CID of a channel. */ STATIC void grpci2dma_channel_bd_set_cid(struct grpci2_bd_chan * chan, int cid) { /* Set cid from chan */ unsigned ctrl = BD_READ(&chan->ctrl); ctrl = (ctrl & ~(BD_CHAN_ID)) | ((cid << BD_CHAN_ID_BIT) & BD_CHAN_ID); BD_WRITE(&chan->ctrl,ctrl); return; } /* Disable data descriptor*/ UNUSED STATIC int grpci2dma_data_bd_disable(struct grpci2_bd_data *desc) { BD_WRITE(&desc->ctrl,0); return 0; } /* Return status of data descriptor*/ STATIC int grpci2dma_data_bd_status(struct grpci2_bd_data *desc) { int status = BD_READ(&desc->ctrl); if (status & BD_DATA_ER) { return GRPCI2DMA_BD_STATUS_ERR; }else if (status & BD_DATA_EN) { return GRPCI2DMA_BD_STATUS_ENABLED; }else { return GRPCI2DMA_BD_STATUS_DISABLED; } return GRPCI2DMA_BD_STATUS_ERR; } /* Enable interrupts in data descriptor*/ STATIC int grpci2dma_data_bd_interrupt_enable(struct grpci2_bd_data * data) { unsigned int ctrl = BD_READ(&data->ctrl); BD_WRITE(&data->ctrl, ctrl | BD_DATA_IE); return 0; } /* Get data descriptor */ STATIC struct grpci2_bd_data * grpci2dma_channel_bd_get_data( struct grpci2_bd_chan * chan) { return (struct grpci2_bd_data *) BD_READ(&chan->nbd); } /* Set data descriptorl */ STATIC void grpci2dma_channel_bd_set_data(struct grpci2_bd_chan * chan, struct grpci2_bd_data * data) { BD_WRITE(&chan->nbd, (unsigned int) data); } /* Get next channel */ STATIC struct grpci2_bd_chan * grpci2dma_channel_bd_get_next( struct grpci2_bd_chan * chan) { return (struct grpci2_bd_chan *) BD_READ(&chan->nchan); } /* Get next data */ STATIC struct grpci2_bd_data * grpci2dma_data_bd_get_next( struct grpci2_bd_data * data) { return (struct grpci2_bd_data *) BD_READ(&data->next); } /* Set next channel */ STATIC void grpci2dma_channel_bd_set_next(struct grpci2_bd_chan * chan, struct grpci2_bd_chan * next) { BD_WRITE(&chan->nchan,(unsigned int) next); } /* Set next data */ STATIC void grpci2dma_data_bd_set_next(struct grpci2_bd_data * data, struct grpci2_bd_data * next) { BD_WRITE(&data->next,(unsigned int) next); } /*** END OF DESCRIPTOR ACCESS FUNCTIONS ***/ /*** START OF CHANNEL FUNCTIONS ***/ STATIC int grpci2dma_channel_open(struct grpci2_bd_chan * chan, int cid) { struct grpci2dma_priv *priv = grpci2dmapriv; int allocated = 0; /* Get pointer */ if (chan == NULL) { /* User does not provide channel, let's create it */ chan = grpci2dma_channel_new(1); allocated = 1; }else{ /* Make sure the pointer is not already on the linked list */ int i; for (i=0; ichannel[i].ptr == chan){ return GRPCI2DMA_ERR_WRONGPTR; } } } DBG("Opening channel %d (0x%08x)\n", cid, (unsigned int) chan); /* Init channel descriptor */ grpci2dma_channel_bd_init(chan); /* Assign cid to chan */ priv->channel[cid].ptr = chan; grpci2dma_channel_bd_set_cid(chan, cid); /* Increase number of channels */ priv->nchans++; DBG("number of channels: %d\n", priv->nchans); /* Initialize channel data */ priv->channel[cid].allocated = allocated; priv->channel[cid].active = 0; /* Initialize record of last added data */ priv->channel[cid].lastdata = DISABLED_DESCRIPTOR; return cid; } /* Get first free CID. */ STATIC int grpci2dma_channel_free_id() { struct grpci2dma_priv *priv = grpci2dmapriv; /* Find the first free CID */ int i; for (i=0; ichannel[i].ptr == NULL){ return i; } } return GRPCI2DMA_ERR_TOOMANY; } /* Get the active channel circular linked list */ STATIC struct grpci2_bd_chan * grpci2dma_channel_get_active_list() { struct grpci2dma_priv *priv = grpci2dmapriv; int i; /* Just get the first non NULL associated cid */ for (i=0; i< MAX_DMA_CHANS; i++){ if ((priv->channel[i].ptr != NULL) && (priv->channel[i].active)){ return priv->channel[i].ptr; } } return NULL; } /* Start a channel */ STATIC int grpci2dma_channel_start(int chan_no, int options) { struct grpci2dma_priv *priv = grpci2dmapriv; struct grpci2_bd_chan *chan; SPIN_IRQFLAGS(irqflags); /* Get chan pointer */ chan = priv->channel[chan_no].ptr; /* Check if channel is active */ if (priv->channel[chan_no].active){ /* nothing to do */ return GRPCI2DMA_ERR_OK; } /* Get the max descriptor count */ unsigned int desccnt; if (options == 0){ /* Default */ desccnt = 0xffff; }else{ desccnt = options & 0xffff; } /* Start the channel by enabling it. * HWNOTE: In GRPCI2 this bit does not work as it is supposed. * So we better add/remove the channel from the active linked * list. */ grpci2dma_channel_bd_enable(chan, desccnt); priv->channel[chan_no].active = 1; priv->nactive++; /* Get active linked list */ struct grpci2_bd_chan * list = grpci2dma_channel_get_active_list(); if (list == NULL){ /* No previous channels. New list */ list = chan; } /* Add channel from the linked list */ if (grpci2dma_channel_list_add(list, chan) < 0){ return GRPCI2DMA_ERR_ERROR; } /* Increase NUMCH in DMA ctrl */ SPIN_LOCK_IRQ(&priv->devlock, irqflags); grpci2dma_ctrl_numch_set( (priv->nactive? priv->nactive -1:0)); /* Check if DMA is active */ if (!grpci2dma_active()){ /* Start DMA */ grpci2dma_ctrl_start(chan); } SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); DBG("Channel %d started (0x%08x)\n", chan_no, (unsigned int) chan); return GRPCI2DMA_ERR_OK; } /* Stop a channel */ STATIC int grpci2dma_channel_stop(int chan_no) { struct grpci2dma_priv *priv = grpci2dmapriv; struct grpci2_bd_chan *chan; SPIN_IRQFLAGS(irqflags); int resume; /* Get chan pointer */ chan = priv->channel[chan_no].ptr; /* Check if channel is active */ if (!priv->channel[chan_no].active){ /* nothing to do */ return GRPCI2DMA_ERR_OK; } /* First remove channel from the linked list */ if (grpci2dma_channel_list_remove(chan) < 0){ return GRPCI2DMA_ERR_ERROR; } /* Update driver struct */ priv->channel[chan_no].active = 0; priv->nactive--; /* Check if DMA is active and it the removed * channel is the active */ resume = 0; SPIN_LOCK_IRQ(&priv->devlock, irqflags); if (grpci2dma_active() && (grpci2dma_ctrl_active() == (unsigned int)chan)){ /* We need to stop the DMA */ grpci2dma_ctrl_stop(); SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); /* Wait until DMA stops */ while (grpci2dma_active()){} /* We need to check later to resume the DMA */ resume = 1; }else{ SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); } /* Now either the DMA is stopped, or it is processing * a different channel and the removed channel is no * longer in the linked list */ /* Now is safe to update the removed channel */ grpci2dma_channel_bd_set_next(chan, chan); /* Stop the channel by disabling it. * HWNOTE: In GRPCI2 this bit does not work as it is supposed. * So we better remove the channel from the active linked * list. */ grpci2dma_channel_bd_disable(chan); /* Point channel to disabled descriptor */ grpci2dma_channel_bd_set_data(chan, DISABLED_DESCRIPTOR); DBG("Channel %d stoped (0x%08x)\n", chan_no, (unsigned int) chan); /* Decrease NUMCH in DMA ctrl */ SPIN_LOCK_IRQ(&priv->devlock, irqflags); grpci2dma_ctrl_numch_set( (priv->nactive? priv->nactive -1:0)); /* Reactivate DMA only if we stopped */ if (resume){ /* We have two options, either we stopped when the active * channel was still the active one, or we stopped when * the active channel was a different one */ if (grpci2dma_ctrl_active() == (unsigned int) chan){ /* In this case, we need to start the DMA with * any active channel on the list */ int i; for (i=0; ichannel[i].active){ grpci2dma_ctrl_start(priv->channel[i].ptr); break; } } }else{ /* In this case, we need to resume the DMA operation */ /* HWNOTE: The GRPCI2 core does not update the channel next * data descriptor if we stopped a channel. This means that * we need to resume the DMA from the descriptor is was, * by only setting the enable bit, and not changing the * base register */ grpci2dma_ctrl_resume(); } } SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); return GRPCI2DMA_ERR_OK; } STATIC int grpci2dma_channel_push(int chan_no, void *dataptr, int index, int ndata) { struct grpci2dma_priv *priv = grpci2dmapriv; struct grpci2_bd_chan * chan; struct grpci2_bd_data * data = dataptr; /* Get channel */ chan = priv->channel[chan_no].ptr; DBG("Pushing %d data (starting at 0x%08x) to channel %d (0x%08x)\n", ndata, (unsigned int) &data[index], chan_no, (unsigned int) chan); /* Get last added data */ struct grpci2_bd_data * last_added = priv->channel[chan_no].lastdata; /* Add data to channel */ grpci2dma_data_list_add(chan, &data[index], last_added); /* Update last added */ priv->channel[chan_no].lastdata = &data[index + ndata-1]; return GRPCI2DMA_ERR_OK; } STATIC int grpci2dma_channel_close(int chan_no) { struct grpci2dma_priv *priv = grpci2dmapriv; struct grpci2_bd_chan * chan; /* Get channel */ chan = priv->channel[chan_no].ptr; DBG("Closing channel %d (0x%08x)\n", chan_no, (unsigned int) chan); /* Stop channel */ if (grpci2dma_channel_stop(chan_no) != GRPCI2DMA_ERR_OK ){ DBG("Cannot stop channel!.\n"); return GRPCI2DMA_ERR_STOPDMA; } /* Unregister channel ISR */ grpci2dma_channel_isr_unregister(chan_no); /* Free the cid */ priv->channel[chan_no].ptr = NULL; /* Remove the ISR */ priv->channel[chan_no].isr = NULL; priv->channel[chan_no].isr_arg = NULL; /* Deallocate channel if needed */ if (priv->channel[chan_no].allocated){ grpci2dma_channel_delete((void *)chan); } /* Decrease number of channels */ priv->nchans--; DBG("number of channels: %d\n", priv->nchans); /* Everything OK */ return GRPCI2DMA_ERR_OK; } /* Register channel ISR */ STATIC int grpci2dma_channel_isr_unregister(int chan_no) { struct grpci2dma_priv *priv = grpci2dmapriv; SPIN_IRQFLAGS(irqflags); /* Unregister channel ISR */ priv->channel[chan_no].isr = NULL; priv->channel[chan_no].isr_arg = NULL; /* Unregister DMA ISR in GRPCI2 if needed */ priv->isr_registered--; if(priv->isr_registered == 0){ /* Disable DMA Interrupts */ SPIN_LOCK_IRQ(&priv->devlock, irqflags); grpci2dma_ctrl_interrupt_disable(); SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); (priv->isr_register)( NULL, NULL); } /* Everything OK */ return GRPCI2DMA_ERR_OK; } /*** END OF CHANNEL FUNCTIONS ***/ /*** START OF ISR FUNCTIONS ***/ /* PCI DMA Interrupt handler, called when there is a PCI DMA interrupt */ STATIC void grpci2dma_isr(void *arg) { struct grpci2dma_priv *priv = arg; SPIN_ISR_IRQFLAGS(irqflags); unsigned int ctrl = grpci2dma_ctrl_status(); /* Clear Interrupts */ SPIN_LOCK(&priv->devlock, irqflags); grpci2dma_ctrl_interrupt_clear(); SPIN_UNLOCK(&priv->devlock, irqflags); unsigned int sts = (ctrl & DMACTRL_CHIRQ) >> DMACTRL_CHIRQ_BIT; unsigned int errsts = (ctrl & DMACTRL_ERR); /* Error interrupt */ if(errsts){ /* Find which channels had the error. * The GRPCI2DMA core does not indicate which channel * had the error, so we need to get 1st the base descriptor register * and see if it a channel. If is not a channel, then the active * channel register tells us which channel is. * After having the channel we need to find out which channel was. */ struct grpci2_bd_chan * chan = (struct grpci2_bd_chan *) grpci2dma_ctrl_base(); /* Check if the base is a channel descriptor */ if ((BD_READ(&chan->ctrl) & BD_CHAN_TYPE) != BD_CHAN_TYPE_DMA){ /* Is not a channel, so the channel is in the channel active * register */ chan = (struct grpci2_bd_chan *) grpci2dma_ctrl_active(); } int i; for (i=0; ichannel[i].ptr){ /* Found */ if (priv->channel[i].isr != NULL){ (priv->channel[i].isr)(priv->channel[i].isr_arg,i,errsts); }else{ printk("Unhandled GRPCI2 DMA error interrupt, sts:0x%02x\n", errsts); } break; } } if (i == MAX_DMA_CHANS){ printk("Unhandled GRPCI2 DMA error interrupt , sts:0x%02x\n", errsts); } } /* Normal packet interrupt */ int cid=0; /* Find which channels have interrupts */ while(sts){ /* Find if current channel has an interrupt*/ if(sts & 0x1){ /* Find if current channel has an ISR */ if (priv->channel[cid].isr != NULL){ (priv->channel[cid].isr)( priv->channel[cid].isr_arg, cid, errsts); }else{ printk("Unhandled GRPCI2 DMA interrupt in channel %d, sts:0x%02x\n", cid, 0); } } /* Next channel */ sts = sts >> 1; cid++; } } /*** END OF ISR FUNCTIONS ***/ /*** START OF DEBUG HELPERS ***/ #ifdef DEBUG STATIC int grpci2dma_channel_print(struct grpci2_bd_chan * chan) { printf(" GRPCI2 DMA channel descriptor\n"); printf(" 0x%08x DMA channel control 0x%08x\n", (unsigned int) chan, chan->ctrl); printf(" 31 en 0x%01x Channel descriptor enable.\n", (chan->ctrl >> 31) & (0x1)); printf(" 24:22 cid 0x%01x Channel ID.\n", (chan->ctrl >> 22) & (0x7)); printf(" 21:20 type 0x%01x Descriptor type. 01=DMA channel descriptor.\n", (chan->ctrl >> 20) & (0x3)); printf(" 15:0 dlen 0x%04x Data descriptor count.\n", (chan->ctrl >> 0) & (0xffff)); printf("\n"); printf(" 0x%08x Next DMA channel 0x%08x\n", (unsigned int) &(chan->nchan), chan->nchan); printf(" 31:0 nc 0x%08x Next DMA channel.\n", chan->nchan); printf("\n"); printf(" 0x%08x Next data descriptor 0x%08x\n" , (unsigned int) &(chan->nbd), chan->nbd); printf(" 31:0 nd 0x%08x Next data descriptor.\n", chan->nbd); printf("\n"); return 0; } STATIC int grpci2dma_data_print(struct grpci2_bd_data * data) { printf(" GRPCI2 DMA data descriptor\n"); printf(" 0x%08x DMA data control 0x%08x\n", (unsigned int) data, data->ctrl); printf(" 31 en 0x%01x Data descriptor enable.\n" , (data->ctrl >> 31) & (0x1)); printf(" 30 ie 0x%01x Interrupt generation enable.\n" , (data->ctrl >> 30) & (0x1)); printf(" 29 dr 0x%01x Tranfer direction.\n" , (data->ctrl >> 29) & (0x1)); printf(" 28 be 0x%01x Bus endianess.\n" , (data->ctrl >> 28) & (0x1)); printf(" 21:20 type 0x%01x Descriptor type. 00=DMA data descriptor.\n" , (data->ctrl >> 20) & (0x3)); printf(" 19 er 0x%01x Error status.\n" , (data->ctrl >> 19) & (0x1)); printf(" 15:0 len 0x%04x Transfer lenght (in words) - 1.\n" , (data->ctrl >> 0) & (0xffff)); printf("\n"); printf(" 0x%08x 32-bit PCI start address 0x%08x\n" , (unsigned int) &(data->pci_adr), data->pci_adr); printf(" 31:0 pa 0x%08x PCI address.\n" , data->pci_adr); printf("\n"); printf(" 0x%08x 32-bit AHB start address 0x%08x\n" , (unsigned int) &(data->ahb_adr), data->ahb_adr); printf(" 31:0 aa 0x%08x AHB address.\n" , data->ahb_adr); printf("\n"); printf(" 0x%08x Next data descriptor 0x%08x\n" , (unsigned int) &(data->next), data->next); printf(" 31:0 nd 0x%08x Next data descriptor.\n" , data->next); printf("\n"); return 0; } #endif /*** END OF DEBUG HELPERS ***/ /*** START OF MEMORY ALLOCATION FUNCTIONS ***/ void * grpci2dma_channel_new(int number) { /* Allocate memory */ unsigned int * orig_ptr = (unsigned int *) grlib_malloc( (GRPCI2DMA_BD_CHAN_SIZE)*number + GRPCI2DMA_BD_CHAN_ALIGN); if (orig_ptr == NULL) return NULL; /* Get the aligned pointer */ unsigned int aligned_ptr = ( ((unsigned int) orig_ptr + GRPCI2DMA_BD_CHAN_ALIGN) & ~(GRPCI2DMA_BD_CHAN_ALIGN - 1)); /* Save the original pointer just before the aligned pointer */ unsigned int ** tmp_ptr = (unsigned int **) (aligned_ptr - sizeof(orig_ptr)); *tmp_ptr= orig_ptr; /* Return aligned pointer */ return (void *) aligned_ptr; } void grpci2dma_channel_delete(void * chan) { /* Recover orignal pointer placed just before the aligned pointer */ unsigned int * orig_ptr; unsigned int ** tmp_ptr = (unsigned int **) (chan - sizeof(orig_ptr)); orig_ptr = *tmp_ptr; /* Deallocate memory */ free(orig_ptr); } void * grpci2dma_data_new(int number) { /* Allocate memory */ unsigned int * orig_ptr = (unsigned int *) grlib_malloc( (GRPCI2DMA_BD_DATA_SIZE)*number + GRPCI2DMA_BD_DATA_ALIGN); if (orig_ptr == NULL) return NULL; /* Get the aligned pointer */ unsigned int aligned_ptr = ( ((unsigned int) orig_ptr + GRPCI2DMA_BD_DATA_ALIGN) & ~(GRPCI2DMA_BD_DATA_ALIGN - 1)); /* Save the original pointer before the aligned pointer */ unsigned int ** tmp_ptr = (unsigned int **) (aligned_ptr - sizeof(orig_ptr)); *tmp_ptr= orig_ptr; /* Return aligned pointer */ return (void *) aligned_ptr; } void grpci2dma_data_delete(void * data) { /* Recover orignal pointer placed just before the aligned pointer */ unsigned int * orig_ptr; unsigned int ** tmp_ptr = (unsigned int **) (data - sizeof(orig_ptr)); orig_ptr = *tmp_ptr; /* Deallocate memory */ free(orig_ptr); } /*** END OF MEMORY ALLOCATION FUNCTIONS ***/ /*** START OF USER API ***/ /* Initialize GRPCI2 DMA: GRPCI2 DRIVER calls this * using a weak function definition */ int grpci2dma_init( void * regs, void isr_register( void (*isr)(void*), void * arg)) { struct grpci2dma_priv *priv; int i; DBG("Registering GRPCI2 DMA driver with arg: 0x%08x\n", (unsigned int) regs); /* We only allow one GRPCI2 DMA */ if (grpci2dmapriv) { DBG("Driver only supports one PCI DMA core\n"); return DRVMGR_FAIL; } /* Device Semaphore created with count = 1 */ if (rtems_semaphore_create(rtems_build_name('G', 'P', '2', 'D'), 1, RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | \ RTEMS_NO_INHERIT_PRIORITY | RTEMS_LOCAL | \ RTEMS_NO_PRIORITY_CEILING, 0, &grpci2dma_sem) != RTEMS_SUCCESSFUL) return -1; /* Allocate and init Memory for DMA */ priv = grlib_calloc(1, sizeof(*priv)); if (priv == NULL) return DRVMGR_NOMEM; priv->regs = regs; strncpy(&priv->devname[0], "grpci2dma0", DEVNAME_LEN); /* Initialize Spin-lock for GRPCI2dma Device. */ SPIN_INIT(&priv->devlock, priv->devname); /* Channel Sempahores */ for (i=0; ichannel[i].sem = RTEMS_ID_NONE; } /* Register device */ grpci2dmapriv = priv; /* Initialize Ctrl regs */ grpci2dma_ctrl_init(); /* Install DMA ISR */ priv->isr_register = isr_register; /* Startup actions: * - stop DMA */ grpci2dma_ctrl_stop(); return DRVMGR_OK; } /* Assign ISR Function to DMA IRQ */ int grpci2dma_isr_register(int chan_no, grpci2dma_isr_t dmaisr, void *data) { struct grpci2dma_priv *priv = grpci2dmapriv; SPIN_IRQFLAGS(irqflags); if (!priv){ /* DMA not initialized */ return GRPCI2DMA_ERR_NOINIT; } /* Check isr */ if (dmaisr == NULL){ /* No ISR */ return GRPCI2DMA_ERR_WRONGPTR; } /* Get chan pointer */ if ((chan_no < 0 ) || (chan_no >= MAX_DMA_CHANS)) { /* Wrong channel id */ return GRPCI2DMA_ERR_WRONGPTR; } /* Check chan is open */ if (priv->channel[chan_no].ptr == NULL){ /* No channel */ return GRPCI2DMA_ERR_NOTFOUND; } /* Take driver lock - Wait until we get semaphore */ if (rtems_semaphore_obtain(grpci2dma_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL){ return GRPCI2DMA_ERR_ERROR; } /* Take channel lock - Wait until we get semaphore */ if (rtems_semaphore_obtain(priv->channel[chan_no].sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL){ rtems_semaphore_release(grpci2dma_sem); return GRPCI2DMA_ERR_ERROR; } /* Register channel ISR */ priv->channel[chan_no].isr_arg = data; priv->channel[chan_no].isr = dmaisr; /* Register DMA ISR in GRPCI2 if not done yet */ if(priv->isr_registered == 0){ (priv->isr_register)( grpci2dma_isr, (void *) priv); /* Enable DMA Interrupts */ SPIN_LOCK_IRQ(&priv->devlock, irqflags); grpci2dma_ctrl_interrupt_enable(); SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); } priv->isr_registered++; /* Release channel sempahore */ rtems_semaphore_release(priv->channel[chan_no].sem); /* Release driver sempahore */ rtems_semaphore_release(grpci2dma_sem); return GRPCI2DMA_ERR_OK; } /* Assign ISR Function to DMA IRQ */ int grpci2dma_isr_unregister(int chan_no) { struct grpci2dma_priv *priv = grpci2dmapriv; int ret; if (!priv){ /* DMA not initialized */ return GRPCI2DMA_ERR_NOINIT; } /* Get chan pointer */ if ((chan_no < 0 ) || (chan_no >= MAX_DMA_CHANS)) { /* Wrong channel id */ return GRPCI2DMA_ERR_WRONGPTR; } /* Check chan is open */ if (priv->channel[chan_no].ptr == NULL){ /* No channel */ return GRPCI2DMA_ERR_NOTFOUND; } /* Get chan ISR */ if (priv->channel[chan_no].isr == NULL){ /* Nothing to do */ return GRPCI2DMA_ERR_OK; } /* Take driver lock - Wait until we get semaphore */ if (rtems_semaphore_obtain(grpci2dma_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL){ return GRPCI2DMA_ERR_ERROR; } /* Take channel lock - Wait until we get semaphore */ if (rtems_semaphore_obtain(priv->channel[chan_no].sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL){ rtems_semaphore_release(grpci2dma_sem); return GRPCI2DMA_ERR_ERROR; } /* Unregister channel ISR */ ret = grpci2dma_channel_isr_unregister(chan_no); /* Release channel sempahore */ rtems_semaphore_release(priv->channel[chan_no].sem); /* Release driver sempahore */ rtems_semaphore_release(grpci2dma_sem); return ret; } int grpci2dma_open(void * chanptr) { struct grpci2dma_priv *priv = grpci2dmapriv; int cid; int ret; if (!priv){ /* DMA not initialized */ return GRPCI2DMA_ERR_NOINIT; } /* Check alignment */ if (((unsigned int ) chanptr) & (GRPCI2DMA_BD_CHAN_ALIGN-1)) { /* Channel is not properly aligned */ return GRPCI2DMA_ERR_WRONGPTR; } /* Take driver lock - Wait until we get semaphore */ if (rtems_semaphore_obtain(grpci2dma_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL){ return GRPCI2DMA_ERR_ERROR; } /* Get free channel id */ cid = grpci2dma_channel_free_id(); if (cid < 0 ){ rtems_semaphore_release(grpci2dma_sem); return GRPCI2DMA_ERR_TOOMANY; } /* Open channel */ ret = grpci2dma_channel_open((struct grpci2_bd_chan *) chanptr, cid); /* Create channel semaphore with count = 1 */ if (ret >= 0){ if (rtems_semaphore_create( rtems_build_name('P', 'D', '0', '0' + cid), 1, RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | \ RTEMS_NO_INHERIT_PRIORITY | RTEMS_LOCAL | \ RTEMS_NO_PRIORITY_CEILING, 0, &priv->channel[cid].sem ) != RTEMS_SUCCESSFUL) { priv->channel[cid].sem = RTEMS_ID_NONE; rtems_semaphore_release(grpci2dma_sem); return GRPCI2DMA_ERR_ERROR; } } /* Release driver semaphore */ rtems_semaphore_release(grpci2dma_sem); /* Return channel id */ return ret; } int grpci2dma_close(int chan_no) { struct grpci2dma_priv *priv = grpci2dmapriv; int ret; if (!priv){ /* DMA not initialized */ return GRPCI2DMA_ERR_NOINIT; } /* Get chan pointer */ if ((chan_no < 0) || (chan_no >= MAX_DMA_CHANS)){ /* Wrong channel id */ return GRPCI2DMA_ERR_WRONGPTR; } /* Check chan is open */ if (priv->channel[chan_no].ptr == NULL){ /* No channel */ return GRPCI2DMA_ERR_NOTFOUND; } /* Take driver lock - Wait until we get semaphore */ if (rtems_semaphore_obtain(grpci2dma_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL){ return GRPCI2DMA_ERR_ERROR; } /* Take channel lock - Wait until we get semaphore */ if (rtems_semaphore_obtain(priv->channel[chan_no].sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL){ rtems_semaphore_release(grpci2dma_sem); return GRPCI2DMA_ERR_ERROR; } /* Close channel */ ret = grpci2dma_channel_close(chan_no); /* Release channel sempahore */ rtems_semaphore_release(priv->channel[chan_no].sem); /* Delete channel semaphore */ if (ret == GRPCI2DMA_ERR_OK){ if (rtems_semaphore_delete(priv->channel[chan_no].sem) != RTEMS_SUCCESSFUL){ /* Release driver semaphore */ rtems_semaphore_release(grpci2dma_sem); return GRPCI2DMA_ERR_ERROR; } } /* Release driver semaphore */ rtems_semaphore_release(grpci2dma_sem); return ret; } /* Transfer_size =0 means maximum */ int grpci2dma_prepare( uint32_t pci_start, uint32_t ahb_start, int dir, int endianness, int size, void * dataptr, int index, int ndata, int transfer_size) { struct grpci2_bd_data * data = dataptr; /* Check data pointer */ if ((data == NULL) || (((unsigned int ) data) & (GRPCI2DMA_BD_DATA_ALIGN-1))){ return GRPCI2DMA_ERR_WRONGPTR; } /* Check indexes */ int maxdata = ndata - index; if ((maxdata < 1) || (index < 0)){ /* No data descriptors to use */ return GRPCI2DMA_ERR_WRONGPTR; } /* Check PCI transfer size */ if ( (transfer_size < 0) || (transfer_size > MAX_DMA_TRANSFER_SIZE) || (transfer_size%4 != 0) ) { return GRPCI2DMA_ERR_WRONGPTR; } if (transfer_size == 0){ transfer_size = MAX_DMA_TRANSFER_SIZE; } /* Check total size */ if ( (size <=0) || (size % 4 != 0)){ return GRPCI2DMA_ERR_WRONGPTR; } /* Calculate number of data descriptors needed */ int words = size/4; int blocksize = transfer_size/4; int datacnt = words/blocksize + (words%blocksize != 0? 1: 0); /* Check that we can transfer the data */ if (datacnt > maxdata) { return GRPCI2DMA_ERR_TOOMANY; } /* Prepare data descriptors */ int i; uint32_t pci_adr; uint32_t ahb_adr; int remaining=words; int datasize; struct grpci2_bd_data * next; for (i=0; i= blocksize){ datasize = blocksize - 1; remaining -= blocksize; } else { datasize = remaining -1; remaining = 0; } /* Get linked list pointers */ if (i == datacnt - 1){ /* Last transfer */ next = DISABLED_DESCRIPTOR; }else{ next = &data[i+index+1]; } /* Set Data descriptor */ grpci2dma_data_bd_init(&data[i+index], pci_adr, ahb_adr, dir, endianness, datasize, next); } /* Return number of transfers used */ return datacnt; } int grpci2dma_status(void *dataptr, int index, int ndata) { struct grpci2_bd_data * data = dataptr; int i; /* Check data pointer */ if ((data == NULL) || (((unsigned int ) data) & (GRPCI2DMA_BD_DATA_ALIGN-1))){ return GRPCI2DMA_ERR_WRONGPTR; } /* Check maxdata */ int maxdata = ndata - index; if ((maxdata < 1) || (index < 0)){ /* No data descriptors to use */ return GRPCI2DMA_ERR_WRONGPTR; } /* Check status of all packets in transfer */ int status; for (i=0; i< maxdata; i++){ status = grpci2dma_data_bd_status(&data[i+index]); if (status == GRPCI2DMA_BD_STATUS_ERR){ /* Error in one packet, means error in transfer */ return status; } else if (status == GRPCI2DMA_BD_STATUS_ENABLED){ /* If one packet is enabled, means transfer is not done */ return status; } } /* If we reach here it means they are all disabled */ return status; } int grpci2dma_print(int chan_no) { struct grpci2dma_priv *priv = grpci2dmapriv; struct grpci2_bd_chan * chan; if (!priv){ /* DMA not initialized */ DBG("DMA not initialized.\n"); return GRPCI2DMA_ERR_NOINIT; } if ( (chan_no < 0) || (chan_no >= MAX_DMA_CHANS )){ /* Wrong chan no*/ return GRPCI2DMA_ERR_WRONGPTR; } chan = priv->channel[chan_no].ptr; if (chan == NULL) { return GRPCI2DMA_ERR_WRONGPTR; } #ifdef DEBUG /* Print channel state */ grpci2dma_channel_print(chan); /* Get current DATA desc */ struct grpci2_bd_data * first_data = (struct grpci2_bd_data *) BD_READ(&chan->nbd); /* Print data state */ grpci2dma_data_list_foreach(first_data, grpci2dma_data_print, MAX_DMA_DATA); #endif return GRPCI2DMA_ERR_OK; } int grpci2dma_print_bd(void * dataptr) { struct grpci2dma_priv *priv = grpci2dmapriv; struct grpci2_bd_data * data = (struct grpci2_bd_data *) dataptr; if (!priv){ /* DMA not initialized */ DBG("DMA not initialized.\n"); return GRPCI2DMA_ERR_NOINIT; } if ( data == NULL ){ /* Wrong chan no*/ return GRPCI2DMA_ERR_WRONGPTR; } #ifdef DEBUG /* Print data state */ grpci2dma_data_list_foreach(data, grpci2dma_data_print, MAX_DMA_DATA); #endif return GRPCI2DMA_ERR_OK; } int grpci2dma_interrupt_enable( void *dataptr, int index, int maxindex, int options) { struct grpci2_bd_data * data = dataptr; struct grpci2dma_priv *priv = grpci2dmapriv; SPIN_IRQFLAGS(irqflags); if (!priv){ /* DMA not initialized */ return GRPCI2DMA_ERR_NOINIT; } /* Check data pointer */ if ((data == NULL) || (((unsigned int ) data) & (GRPCI2DMA_BD_DATA_ALIGN-1))){ return GRPCI2DMA_ERR_WRONGPTR; } /* Check index */ if ((index < 0) || (maxindex < 1) || (index >= maxindex)){ /* No data descriptors to use */ return GRPCI2DMA_ERR_WRONGPTR; } if (options & GRPCI2DMA_OPTIONS_ALL){ /* Enable all interrupts */ if (grpci2dma_data_list_foreach( &data[index], grpci2dma_data_bd_interrupt_enable, maxindex -index)){ return GRPCI2DMA_ERR_ERROR; } }else{ /* Enable one packet interrupts */ grpci2dma_data_bd_interrupt_enable(&data[index]); } /* Finally enable DMA interrupts if they are not already enabled */ if (grpci2dma_ctrl_interrupt_status()==0){ SPIN_LOCK_IRQ(&priv->devlock, irqflags); grpci2dma_ctrl_interrupt_enable(); SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); } DBG("Interrupts enabled for data (0x%08x), index:%d, maxindex:%d, %s.\n", (unsigned int) data, index, maxindex, (options & GRPCI2DMA_OPTIONS_ALL)? "ALL":"ONE" ); return GRPCI2DMA_ERR_OK; } int grpci2dma_push(int chan_no, void *dataptr, int index, int ndata) { struct grpci2dma_priv *priv = grpci2dmapriv; SPIN_IRQFLAGS(irqflags); int ret; if (!priv){ /* DMA not initialized */ return GRPCI2DMA_ERR_NOINIT; } /* Check data pointer */ if ((dataptr == NULL) || (((unsigned int ) dataptr) & (GRPCI2DMA_BD_DATA_ALIGN-1))){ return GRPCI2DMA_ERR_WRONGPTR; } /* Check index */ if ((ndata < 1) || (index < 0)){ /* No data descriptors to use */ return GRPCI2DMA_ERR_WRONGPTR; } /* Check chan_no */ if ( (chan_no < 0) || (chan_no >= MAX_DMA_CHANS )){ /* Wrong chan no*/ return GRPCI2DMA_ERR_WRONGPTR; } /* Check chan is open */ if (priv->channel[chan_no].ptr == NULL){ /* No channel */ return GRPCI2DMA_ERR_NOTFOUND; } /* Take channel lock - Wait until we get semaphore */ if (rtems_semaphore_obtain(priv->channel[chan_no].sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL){ return GRPCI2DMA_ERR_ERROR; } /* push data to channel */ ret = grpci2dma_channel_push(chan_no, dataptr, index, ndata); if (ret != GRPCI2DMA_ERR_OK){ /* Release channel lock */ rtems_semaphore_release(priv->channel[chan_no].sem); return ret; } /* Start DMA if it is not active and channel is active*/ SPIN_LOCK_IRQ(&priv->devlock, irqflags); if ((!grpci2dma_active()) && (priv->channel[chan_no].active)){ grpci2dma_ctrl_start(priv->channel[chan_no].ptr); } SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); /* Release channel lock */ rtems_semaphore_release(priv->channel[chan_no].sem); return ret; } /* Start the channel */ int grpci2dma_start(int chan_no, int options) { struct grpci2dma_priv *priv = grpci2dmapriv; int ret; if (!priv){ /* DMA not initialized */ return GRPCI2DMA_ERR_NOINIT; } if ((chan_no < 0 ) || (chan_no >= MAX_DMA_CHANS )) { /* Wrong channel id */ return GRPCI2DMA_ERR_WRONGPTR; } if ( options < 0 ) { /* Wrong options */ return GRPCI2DMA_ERR_WRONGPTR; } /* Check chan is open */ if (priv->channel[chan_no].ptr == NULL){ /* No channel */ return GRPCI2DMA_ERR_NOTFOUND; } /* Take driver lock - Wait until we get semaphore */ if (rtems_semaphore_obtain(grpci2dma_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL){ return GRPCI2DMA_ERR_ERROR; } /* Take channel lock - Wait until we get semaphore */ if (rtems_semaphore_obtain(priv->channel[chan_no].sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL){ rtems_semaphore_release(grpci2dma_sem); return GRPCI2DMA_ERR_ERROR; } /* Start the channel */ ret = grpci2dma_channel_start(chan_no, options); /* Release channel lock */ rtems_semaphore_release(priv->channel[chan_no].sem); /* Release driver lock */ rtems_semaphore_release(grpci2dma_sem); return ret; } /* Stop the channel, but don't stop ongoing transfers! */ int grpci2dma_stop(int chan_no) { struct grpci2dma_priv *priv = grpci2dmapriv; int ret; if (!priv){ /* DMA not initialized */ return GRPCI2DMA_ERR_NOINIT; } if ((chan_no < 0 ) || (chan_no >= MAX_DMA_CHANS)) { /* Wrong channel id */ return GRPCI2DMA_ERR_WRONGPTR; } /* Check chan is open */ if (priv->channel[chan_no].ptr == NULL){ /* No channel */ return GRPCI2DMA_ERR_NOTFOUND; } /* Take driver lock - Wait until we get semaphore */ if (rtems_semaphore_obtain(grpci2dma_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL){ return GRPCI2DMA_ERR_ERROR; } /* Take channel lock - Wait until we get semaphore */ if (rtems_semaphore_obtain(priv->channel[chan_no].sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL){ rtems_semaphore_release(grpci2dma_sem); return GRPCI2DMA_ERR_ERROR; } /* Stop the channel */ ret = grpci2dma_channel_stop(chan_no); /* Release channel lock */ rtems_semaphore_release(priv->channel[chan_no].sem); /* Release driver lock */ rtems_semaphore_release(grpci2dma_sem); return ret; } int grpci2dma_active() { return ((grpci2dma_ctrl_status()) & DMACTRL_ACT) >> DMACTRL_ACT_BIT; }