www.pudn.com > scaling.rar > c6x1x_edma_mcasp.c
/* * Copyright 2003 by Texas Instruments Incorporated. * All rights reserved. Property of Texas Instruments Incorporated. * Restricted rights to use, duplicate or disclose this code are * granted through contract. * */ /* "@(#) DDK 1.10.00.23 07-02-03 (ddk-b12)" */ /* * ======== c6x1x_edma_mcasp.c ======== * * Generic McASP driver for the TMS320C6x1x series. Uses the EDMA. */ #include#include #include #include #include #include #include #include #include #include #include #include /* * Macro to start serializer & state machine * if turned on, driver will start both transmit and receive serializer * and state machine synchronously */ #define STARTSERIALIZERSYNC 0 /* * Macro to enable edma loop job interrupt * if turned on, each time edma loop job gets running, an ineterrupt will * be generated. This is only used for debug purpose. */ #define ENABLELOOPINTR 0 #if ENABLELOOPINTR #include extern far LOG_Obj trace; #endif #define IRQEVTINPUT 5 #define IRQEVTOUTPUT 6 #define IRQEDMA 8 /* Maximum number of EDMA jobs linked at a time (Must be 2). */ #define MAXLINKCNT 2 /* Used as index since IOM mode is a bit mask and not an index */ #define INPUT 0 #define OUTPUT 1 /* States for chanCleanUp() */ #define SETFALSE 1 #define FREETCC 2 #define FREETABLE 3 #define FREETABLEEX 4 #define DELCHAN 5 /* Macro to increment and return the indice that ranges over MAXLINKCNT */ #define nextIndex(index) ((index) ^ 1) /* Number of ports available */ #define NUMPORTS _MCASP_PORT_CNT /* Number of channels per port (one input and one output channel) */ #define NUMCHANS 2 /* Structure containing channel specific variables */ typedef struct ChanObj { Uns inUse; /* True if the channel is in use */ Int mode; /* Input or output channel */ struct PortObj *port; /* Pointer to the port which owns this chan */ EDMA_Handle xferPram; /* Handle to transfer PaRAM */ EDMA_Handle pramTbl[MAXLINKCNT]; /* Handles to link PaRAMs */ EDMA_Handle prevPramPtr; /* Points to the PaRAM last used */ EDMA_Handle loophEdma; /* Handle to the Loop job PaRAM */ IOM_Packet *flushPacket; /* Holds the flushpacket (if any) */ IOM_Packet *abortPacket; /* Holds the abortpacket (if any) */ IOM_Packet *packetList[MAXLINKCNT]; /* Holds linked packets */ QUE_Obj packetQueue; /* Holds submitted but not linked packets */ Int submitCount; /* Number of submit calls pending */ Int writeIndex; /* Index of next PaRAM to write to */ Int readIndex; /* Index of next PaRAM to read from */ Int tcc; /* Channel transfer complete code */ IOM_TiomCallback cbFxn; /* Called when I/O complete */ Ptr cbArg; /* Argument to callback function */ Bool pause; Fxn avSyncCbFxn; } ChanObj, *ChanHandle; /* Structure containing port specific variables */ typedef struct PortObj { Uns inUse; /* True if the port is in use */ Int devId; /* The device id passed to mdBindDev() */ Bool cacheCalls; /* Submitted buffers are in cacheable memory */ Uint32 enableHclkg; /* Holds enable Hclk variable */ Uint32 enableClkg; /* Holds enable Clk variable */ Uint32 enableFsyncg; /* Holds enable Fsync variable */ MCASP_Handle hMcasp; /* CSL Device handle */ ChanObj chans[NUMCHANS]; /* The channels associated with the port */ Uns chanCreated; /* One channel in this port has been Created */ C6X1X_EDMA_MCASP_TevtCallback evtCallback; /* event callback */ Uns evtMask; /* registered events */ } PortObj, *PortHandle; /* Declare the port and channel structures */ static PortObj ports[NUMPORTS]; /* Define EDMA Event Id's Array */ static Uns eventIds[NUMPORTS][2] = { { EDMA_CHA_AREVT0, EDMA_CHA_AXEVT0 }, #if NUMPORTS >= 2 { EDMA_CHA_AREVT1, EDMA_CHA_AXEVT1 }, #endif #if NUMPORTS == 3 { EDMA_CHA_AREVT2, EDMA_CHA_AXEVT2 } #endif }; /* * Forward declaration of the IOM interface functions. They are only * exposed via the IOM function table to avoid namespace pollution. */ static Int mdBindDev(Ptr *devp, Int devid, Ptr devParams); static Int mdCreateChan(Ptr *chanp, Ptr devp, String name, Int mode, Ptr chanParams, IOM_TiomCallback cbFxn, Ptr cbArg); static Int mdControlChan(Ptr *chanp, Int cmd, Arg arg); static Int mdDeleteChan(Ptr chanp); static Int mdSubmitChan(Ptr chanp, IOM_Packet *packet); static Int mdUnBindDev(Ptr devp); #if ENABLELOOPINTR static Void isrLoop(Int tcc); #endif /* Public IOM interface table */ IOM_Fxns C6X1X_EDMA_MCASP_FXNS = { &mdBindDev, &mdUnBindDev, &mdControlChan, &mdCreateChan, &mdDeleteChan, &mdSubmitChan }; /* Local function prototypes */ static Void chanCleanUp(ChanHandle chan, Uns state); static Void isrCommon(ChanHandle chan); static Void isrInput(Int tcc); static Void isrOutput(Int tcc); static Void isrEvent(Int mode); static Void linkPacket(ChanHandle chan, IOM_Packet *packet); /* Local driver variables. */ static Uint32 loopDstBuf; static Uint32 loopSrcBuf; #if ENABLELOOPINTR static Void isrLoop(Int tcc) { LOG_printf(&trace, "LOOP=%d", tcc); } #endif /* * ======== chanCleanUp ======== * Cleans up the channel resources. */ static Void chanCleanUp(ChanHandle chan, Uns state) { switch(state) { case DELCHAN: /* Close the EDMA channel */ EDMA_close(chan->xferPram); /* Disable transfer interrupts from the EDMA */ EDMA_intDisable(chan->tcc); /* will fall through the next case */ case FREETABLEEX: /* Free the EDMA link PaRAM tables */ EDMA_freeTableEx(MAXLINKCNT, chan->pramTbl); /* will fall through the next case */ case FREETABLE: /* Free the loop EDMA PaRAM table */ EDMA_freeTable(chan->loophEdma); /* will fall through the next case */ case FREETCC: /* Free the transfer complete code */ EDMA_intFree(chan->tcc); /* will fall through the next case */ case SETFALSE: /* Mark the channel as closed */ chan->inUse = FALSE; break; } } /* * ======== isrCommon ======== * Shared ISR code between input and output. Processes a normal EDMA job. */ static Void isrCommon(ChanHandle chan) { IOM_Packet *packet; Int cnt; /* Check to see if this is a completed async abort call */ if (chan->abortPacket) { /* Discard all packets in transmission or queued up */ if (chan->submitCount > MAXLINKCNT) { cnt = MAXLINKCNT; } else { cnt = chan->submitCount; } while (cnt > 0) { packet = chan->packetList[chan->readIndex]; packet->status = IOM_ABORTED; (*chan->cbFxn)(chan->cbArg, packet); chan->readIndex = nextIndex(chan->readIndex); cnt--; } while (!QUE_empty(&chan->packetQueue)) { packet = QUE_dequeue(&chan->packetQueue); packet->status = IOM_ABORTED; (*chan->cbFxn)(chan->cbArg, packet); } /* Reset the driver channel state */ chan->writeIndex = 0; chan->readIndex = 0; chan->submitCount = 0; chan->abortPacket->status = IOM_COMPLETED; (*chan->cbFxn)(chan->cbArg, chan->abortPacket); chan->abortPacket = NULL; return; } /* Fetch the completed packet */ packet = chan->packetList[chan->readIndex]; chan->readIndex = nextIndex(chan->readIndex); /* Mark the packet as completed */ packet->status = IOM_COMPLETED; /* Call the callback function */ (*chan->cbFxn)(chan->cbArg, packet); chan->submitCount--; /* * See if there are any unlinked packets in the packetQueue * and if so link them. */ if (chan->submitCount >= MAXLINKCNT) { packet = QUE_dequeue(&chan->packetQueue); linkPacket(chan, packet); } } /* * ======== isrInput ======== * The input isr called from the EDMA dispatcher every time an input * EDMA job completes. */ static Void isrInput(Int tcc) { ChanHandle chan; Int portNbr; /* Check which port was responsible for the interrupt */ for (portNbr = 0; portNbr < NUMPORTS; portNbr++) { chan = &ports[portNbr].chans[INPUT]; if (chan->tcc == tcc && chan->inUse) { if (EDMA_RGETH(chan->xferPram, DST) == (Uint32) &loopDstBuf && chan->submitCount > 1 && !chan->abortPacket) { /* * An emulation halt has occured with more than 1 job * submitted. Link the currently executing job (the Loop job) * to the first of the linked jobs which hadn't been called * back. This way we still have the same number of submitted * jobs after the execution continues as we had before the * emulation halt (breakpoint) occured (this preserves double * buffering if used). */ //EDMA_disableChannel(chan->xferPram); EDMA_link(chan->xferPram, chan->pramTbl[chan->readIndex]); //EDMA_enableChannel(chan->xferPram); } else { /* Call the common ISR code for a finished normal EDMA job */ isrCommon(chan); } } } } /* * ======== isrOutput ======== * The output isr called from the EDMA dispatcher every time an output * EDMA job completes. */ static Void isrOutput(Int tcc) { ChanHandle chan; Int portNbr; /* Check which port was responsible for the interrupt */ for (portNbr = 0; portNbr < NUMPORTS; portNbr++) { chan = &ports[portNbr].chans[OUTPUT]; if (chan->tcc == tcc && chan->inUse ) { if(chan->avSyncCbFxn != NULL) { chan->avSyncCbFxn(); /* add hook for the a/v sync call-back function */ } if(!chan->pause) { if (EDMA_RGETH(chan->xferPram, SRC) == (Uint32)&loopSrcBuf && chan->submitCount > 1 && !chan->abortPacket) { /* * An emulation halt has occured with more than 1 job * submitted. Link the currently executing job (the Loop job) * to the first of the linked jobs which hadn't been called * back. This way we still have the same number of submitted * jobs after the execution continues as we had before the * emulation halt (breakpoint) occured (this preserves double * buffering if used). */ //EDMA_disableChannel(chan->xferPram); EDMA_link(chan->xferPram, chan->pramTbl[chan->readIndex]); //EDMA_enableChannel(chan->xferPram); } else { /* Call the common ISR code for a finished normal EDMA job */ isrCommon(chan); /* Check to see if an async flush has completed */ if (chan->submitCount == 0 && chan->flushPacket) { chan->flushPacket->status = IOM_COMPLETED; (*chan->cbFxn)(chan->cbArg,chan->flushPacket); chan->flushPacket = NULL; } } } } } } /* * ======== isrEvent ======== * The event isr called when an McASP input/output event happens * This is used for exception event handling asserted by McASP. Normal data * hanlding is done by EDMA. */ static Void isrEvent(Int mode) { MCASP_Handle hMcasp; Uns portNbr; Uns events; Uns eventReturn; /* Check which port was responsible for the interrupt */ for (portNbr = 0; portNbr < NUMPORTS; portNbr++) { if (ports[portNbr].inUse) { hMcasp = ports[portNbr].hMcasp; events = 0; if (mode == INPUT) { if (MCASP_FGETH(hMcasp, RSTAT, ROVRN)) { MCASP_FSETSH(hMcasp, RSTAT, ROVRN, YES); events |= C6X1X_EDMA_MCASP_EVT_ROVRN; } if (MCASP_FGETH(hMcasp, RSTAT, RCKFAIL)) { MCASP_FSETSH(hMcasp, RSTAT, RCKFAIL, YES); events |= C6X1X_EDMA_MCASP_EVT_RCKFAIL; } if (MCASP_FGETH(hMcasp, RSTAT, RSYNCERR)) { MCASP_FSETSH(hMcasp, RSTAT, RSYNCERR, YES); events |= C6X1X_EDMA_MCASP_EVT_RSYNCERR; } if (MCASP_FGETH(hMcasp, RSTAT, RDMAERR)) { MCASP_FSETH(hMcasp, RSTAT, RDMAERR, 1); events |= C6X1X_EDMA_MCASP_EVT_RDMAERR; } /* Clear not registered events here */ MCASP_RSETH(hMcasp, RSTAT, 0xFFFFFFFF); } else { if (MCASP_FGETH(hMcasp, XSTAT, XUNDRN)) { MCASP_FSETSH(hMcasp, XSTAT, XUNDRN, YES); events |= C6X1X_EDMA_MCASP_EVT_XUNDRN; } if (MCASP_FGETH(hMcasp, XSTAT, XCKFAIL)) { MCASP_FSETSH(hMcasp, XSTAT, XCKFAIL, YES); events |= C6X1X_EDMA_MCASP_EVT_XCKFAIL; } if (MCASP_FGETH(hMcasp, XSTAT, XSYNCERR)) { MCASP_FSETSH(hMcasp, XSTAT, XSYNCERR, YES); events |= C6X1X_EDMA_MCASP_EVT_XSYNCERR; } if (MCASP_FGETH(hMcasp, XSTAT, XDMAERR)) { MCASP_FSETH(hMcasp, XSTAT, XDMAERR, 1); events |= C6X1X_EDMA_MCASP_EVT_XDMAERR; } /* Clear not registered events here */ MCASP_RSETH(hMcasp, XSTAT, 0xFFFFFFFF); } eventReturn = ports[portNbr].evtMask & events; if (eventReturn) { (*ports[portNbr].evtCallback)(eventReturn, portNbr); } } } } /* * ======== linkPacket ======== * Links a packet with the EDMA. When called by mdSubmitChan() it is called * with all interrupts disabled, but when called by an ISR only the EDMA IRQ * is disabled. */ static Void linkPacket(ChanHandle chan, IOM_Packet *packet) { EDMA_Handle pramPtr; Uns edmaCnt; /* Store the packet in the packetList */ chan->packetList[chan->writeIndex] = packet; /* Set up pointer to link PaRAM to write submit job info to */ pramPtr = chan->pramTbl[chan->writeIndex]; chan->writeIndex = nextIndex(chan->writeIndex); /* Load the buffer pointer into the EDMA */ if (chan->mode == INPUT) { EDMA_RSETH(pramPtr, DST, (Uint32) packet->addr); } else { EDMA_RSETH(pramPtr, SRC, (Uint32) packet->addr); } /* * Load the transfer count (in samples) into the EDMA. Use the ESIZE * field of the EDMA job to calculate number of samples. */ edmaCnt = (Uint32)packet->size >> (2 - EDMA_FGETH(pramPtr, OPT, ESIZE)); EDMA_FSETH(pramPtr, CNT, FRMCNT, (edmaCnt / EDMA_FGETH(pramPtr, CNT, ELECNT) - 1)); /* * Link to loop EDMA job upon termination. This way we won't * loose the frame sync if the channel is starved. */ EDMA_link(pramPtr, chan->loophEdma); /* Disable the EDMA channel to make sure current job doesn't complete */ //EDMA_disableChannel(chan->xferPram); /* * Link the currently executing job to the new job. This can be * either the loop EDMA job or a real data EDMA job. */ EDMA_link(chan->xferPram, pramPtr); if (chan->submitCount > 0) { /* * We need to link the parameter space corresponding to the running * job so that if a breakpoint occurs, we know how to recover. */ EDMA_link(chan->prevPramPtr, pramPtr); } /* Reenable the EDMA channel */ //EDMA_enableChannel(chan->xferPram); /* Save the new job for the loop above for next time */ chan->prevPramPtr = pramPtr; } /* * ======== mdBindDev ======== * This function allocates and configures the McASP port specified by devId. */ static Int mdBindDev(Ptr *devp, Int devid, Ptr devParams) { Uns old; Int inIrqId; int outIrqId; int irqId; PortHandle port; HWI_Attrs hwiAttrs; C6X1X_EDMA_MCASP_DevParams *params = (C6X1X_EDMA_MCASP_DevParams *) devParams; /* This driver must receive a valid devparams */ if (params == NULL) { return (IOM_EBADARGS); } /* Check the version number */ if (params->versionId != C6X1X_EDMA_MCASP_VERSION_1){ /* Unsupported version */ return(IOM_EBADARGS); } /* Get the device parameters of the specified port */ port = &ports[devid]; /* Mark the port as in use */ old = ATM_setu(&(port->inUse), TRUE); /* Check if the port was already bound */ if (old) { return (IOM_EALLOC); } /* Map the supplied IRQ to the EDMA event */ if (params->irqId > 0 ) { irqId = params->irqId; } else { irqId = IRQEDMA; } IRQ_map(IRQ_EVT_EDMAINT, irqId); hwiAttrs.intrMask = params->edmaIntrMask; hwiAttrs.ccMask = IRQ_CCMASK_NONE; /* the default value */ hwiAttrs.arg = NULL; /* Plug the EDMA dispatcher into the HWI dispatcher */ HWI_dispatchPlug(irqId, (Fxn)EDMA_intDispatcher, -1, &hwiAttrs); /* Set the McASP high frequency sample rate generator */ /* Set the McASP sample rate generator */ /* Set the McASP frame sync generator */ port->enableHclkg = params->enableHclkg; port->enableClkg = params->enableClkg; port->enableFsyncg = params->enableFsyncg; /* True if buffers are in external memory */ port->cacheCalls = params->cacheCalls; /* Store the devid */ port->devId = devid; /* No channel create yet */ port->chanCreated = 0; /* Open and reset the McASP */ port->hMcasp = MCASP_open(devid, MCASP_OPEN_RESET); if (port->hMcasp == INV) { return (IOM_EALLOC); } /* Configure the McASP with the supplied configuration */ MCASP_config(port->hMcasp, params->mcaspCfgPtr); if (params->evtCallback != NULL) { /* register the events */ port->evtCallback = params->evtCallback->evtFxn; port->evtMask = params->evtCallback->evtMask; if (params->inEvtIrqId > 0) { inIrqId = params->inEvtIrqId; } else { inIrqId = IRQEVTINPUT; } if (params->outEvtIrqId > 0) { outIrqId = params->outEvtIrqId; } else { outIrqId = IRQEVTOUTPUT; } IRQ_map(MCASP_getXmtEventId(port->hMcasp), outIrqId); IRQ_map(MCASP_getRcvEventId(port->hMcasp), inIrqId); hwiAttrs.intrMask = params->inEvtIntrMask; hwiAttrs.ccMask = IRQ_CCMASK_NONE; /* the default value */ hwiAttrs.arg = INPUT; HWI_dispatchPlug(inIrqId, (Fxn)isrEvent, -1, &hwiAttrs); hwiAttrs.intrMask = params->outEvtIntrMask; hwiAttrs.ccMask = IRQ_CCMASK_NONE; /* the default value */ hwiAttrs.arg = OUTPUT; HWI_dispatchPlug(outIrqId, (Fxn)isrEvent, -1, &hwiAttrs); } else { port->evtCallback = NULL; } /* set 0xe for debug, real value should be 0 */ loopSrcBuf = 0; loopDstBuf = 0; /* Return the device handle and a status code for success */ *devp = port; return (IOM_COMPLETED); } static Int mdControlChan(Ptr *chanp, Int cmd, Arg arg) { ChanHandle chan = (ChanHandle) chanp; Int imask; if(cmd == C6X1X_EDMA_MCASP_CMD_PAUSE) { chan->pause = TRUE; /* Disable interrupts to protect submitCount */ imask = HWI_disable(); if (chan->submitCount > 0) { /* * Disable the EDMA channel while linking the currently * executing job while linking it with the Loop job to make * sure the currently executing job doesn't complete before * the link is complete. */ //EDMA_disableChannel(chan->xferPram); EDMA_link(chan->xferPram, chan->loophEdma); //EDMA_enableChannel(chan->xferPram); } /* Reenable interrupts */ HWI_restore(imask); } else if(cmd == C6X1X_EDMA_MCASP_CMD_RESUME && chan->pause == TRUE) { chan->pause = FALSE; if(chan->submitCount > 0 && !chan->abortPacket) { /* * An emulation halt has occured with more than 1 job * submitted. Link the currently executing job (the Loop job) * to the first of the linked jobs which hadn't been called * back. This way we still have the same number of submitted * jobs after the execution continues as we had before the * emulation halt (breakpoint) occured (this preserves double * buffering if used). */ //EDMA_disableChannel(chan->xferPram); EDMA_link(chan->xferPram, chan->pramTbl[nextIndex(chan->writeIndex)]); //EDMA_enableChannel(chan->xferPram); } } else if(cmd == C6X1X_EDMA_MCASP_CMD_SET_AVSYNC_CALLBACK) { chan->avSyncCbFxn = (Fxn)arg; } return IOM_COMPLETED; } /* * ======== mdCreateChan ======== * This function creates and configures a device channel. */ static Int mdCreateChan(Ptr *chanp, Ptr devp, String name, Int mode, Ptr chanParams, IOM_TiomCallback cbFxn, Ptr cbArg) { PortHandle port = (PortHandle) devp; C6X1X_EDMA_MCASP_ChanParams *params = (C6X1X_EDMA_MCASP_ChanParams *) chanParams; ChanHandle chan; Uns old; Uns esize; Uns cnt; Uns fs; Uns elerld; Uns mcaspAddr; Int edmaChanEvent; Int i; #if ENABLELOOPINTR Int loopTcc; #endif /* * Configuration structure for the loop EDMA job. If the McASP * is left to free running, this loop job is running when there is * no data to transfer. This is useful if the McASP is externally * clocked. If the driver is starved or if an emulation halt * (breakpoint) occurs, the frame sync will still be correct when * the driver continues transmitting data. */ EDMA_Config loopEdmaCfg = { EDMA_FMKS(OPT, PRI, HIGH) | EDMA_FMKS(OPT, ESIZE, 32BIT) | EDMA_FMKS(OPT, 2DS, NO) | EDMA_FMKS(OPT, SUM, NONE) | EDMA_FMKS(OPT, 2DD, NO) | EDMA_FMKS(OPT, DUM, NONE) | EDMA_FMKS(OPT, TCINT, NO) | EDMA_FMKS(OPT, TCC, DEFAULT) | EDMA_FMKS(OPT, LINK, YES) | EDMA_FMKS(OPT, FS, NO), EDMA_FMK (SRC, SRC, NULL), EDMA_FMK (CNT, FRMCNT, 0) | EDMA_FMK (CNT, ELECNT, 0), EDMA_FMKS(IDX, FRMIDX, OF(0)) | EDMA_FMKS(IDX, ELEIDX, OF(0)), EDMA_FMK (DST, DST, NULL), EDMA_FMK (RLD, ELERLD, 0) | EDMA_FMK (RLD, LINK, NULL) }; /* This driver needs a valid channel parameter structure passed */ if (params == NULL) { return (IOM_EBADARGS); } /* Use own indexes since IOM mode is a bit mask and not an index */ if (mode == IOM_INPUT) { chan = &port->chans[INPUT]; chan->mode = INPUT; } else { chan = &port->chans[OUTPUT]; chan->mode = OUTPUT; } /* Mark the channel as used */ old = ATM_setu(&(chan->inUse), TRUE); /* Make sure the channel wasn't already created */ if (old) { return (IOM_EALLOC); } /* Initialise the channel structure */ chan->cbFxn = cbFxn; chan->cbArg = cbArg; chan->port = port; chan->writeIndex = 0; chan->readIndex = 0; chan->submitCount = 0; chan->flushPacket = NULL; chan->abortPacket = NULL; chan->pause = FALSE; chan->avSyncCbFxn = NULL; /* Initialize the packet queue */ QUE_new(&chan->packetQueue); /* * Set the number of elements (corresponding to number of McASP TDM * channels) used in the Loop job to preserve the frame sync. * FRMCNT should be (num of TDM slots - 1); */ cnt = params->tdmChans - 1; EDMA_FSETA(&loopEdmaCfg.cnt, CNT, FRMCNT, cnt); EDMA_FSETA(&loopEdmaCfg.cnt, CNT, ELECNT, params->edmaCfgPtr->cnt); /* Use the same sample size in the Loop job as in normal jobs */ esize = EDMA_FGETA(¶ms->edmaCfgPtr->opt, OPT, ESIZE); EDMA_FSETA(&loopEdmaCfg.opt, OPT, ESIZE, esize); /* Use the same FS info as in the normal job */ fs = EDMA_FGETA(¶ms->edmaCfgPtr->opt, OPT, FS); EDMA_FSETA(&loopEdmaCfg.opt, OPT, FS, fs); /* Use the same ELERLD info as in the normal job */ elerld = EDMA_FGETA(¶ms->edmaCfgPtr->rld, RLD, ELERLD); EDMA_FSETA(&loopEdmaCfg.rld, RLD, ELERLD, elerld); /* Allocate a TCC for the EDMA */ chan->tcc = EDMA_intAlloc(-1); /* If tcc > 15 we abort. */ if (chan->tcc == -1 || chan->tcc > 15) { chanCleanUp(chan, SETFALSE); return (IOM_EALLOC); } #if ENABLELOOPINTR loopTcc = EDMA_intAlloc(-1); /* If tcc > 15 we abort. */ if (loopTcc == -1 || loopTcc > 15) { EDMA_intFree(loopTcc); chanCleanUp(chan, FREETCC); return (IOM_EALLOC); } #endif /* Allocate an EDMA PaRAM for the Loop EDMA job. */ chan->loophEdma = EDMA_allocTable(-1); if (chan->loophEdma == EDMA_HINV) { chanCleanUp(chan, FREETCC); return (IOM_EALLOC); } /* * Allocate EDMA PaRAM link area based on max number of * submits possible. */ if (EDMA_allocTableEx(MAXLINKCNT, chan->pramTbl) != MAXLINKCNT) { chanCleanUp(chan, FREETABLE); return (IOM_EALLOC); } /* workaround for big endian problem in McASP */ if (chan->mode == INPUT) { mcaspAddr = MCASP_getRbufAddr(port->hMcasp); } else { mcaspAddr = MCASP_getXbufAddr(port->hMcasp); } #ifdef _BIG_ENDIAN if (esize == EDMA_OPT_ESIZE_8BIT) { mcaspAddr += 3; } else if (esize == EDMA_OPT_ESIZE_16BIT) { mcaspAddr += 2; } #endif if (chan->mode == INPUT) { /* Put input specific parameters in the Loop EDMA config */ EDMA_RSETA(&loopEdmaCfg.src, SRC, mcaspAddr); EDMA_RSETA(&loopEdmaCfg.dst, DST, (Uint32) &loopDstBuf); EDMA_RSETA(¶ms->edmaCfgPtr->src, SRC, mcaspAddr); /* Register our isrInput with the EDMA dispatcher */ EDMA_intHook(chan->tcc, &isrInput); } else { /* Put output specific parameters in the Loop EDMA config */ EDMA_RSETA(&loopEdmaCfg.src, SRC, (Uint32) &loopSrcBuf); EDMA_RSETA(&loopEdmaCfg.dst, DST, mcaspAddr); EDMA_RSETA(¶ms->edmaCfgPtr->dst, DST, mcaspAddr); /* Register our isrOutput with the EDMA dispatcher */ EDMA_intHook(chan->tcc, &isrOutput); } /* Open theEDMA Channel */ #if (CHIP_DM642) edmaChanEvent = eventIds[port->devId][chan->mode]; #else edmaChanEvent = EDMA_map(eventIds[port->devId][chan->mode], params->edmaChan); #endif chan->xferPram = EDMA_open(edmaChanEvent, EDMA_OPEN_RESET); if (chan->xferPram == EDMA_HINV) { chanCleanUp(chan, FREETABLEEX); return (IOM_EALLOC); } /* Program the data EDMA job with the TCC */ EDMA_FSETA(¶ms->edmaCfgPtr->opt, OPT, TCC, chan->tcc); #if ENABLELOOPINTR EDMA_intHook(loopTcc, &isrLoop); EDMA_FSETA(&loopEdmaCfg.opt, OPT, TCINT, EDMA_OPT_TCINT_YES); EDMA_FSETA(&loopEdmaCfg.opt, OPT, TCC, loopTcc); #endif /* Program the link PaRAMs with the config struct */ for (i=0; i < MAXLINKCNT; i++) { EDMA_config(chan->pramTbl[i], params->edmaCfgPtr); } /* Program the Loop EDMA job with its config struct */ EDMA_config(chan->loophEdma, &loopEdmaCfg); /* * Link the Loop EDMA job to itself to make it run * continuously when there is no data to transmit. */ EDMA_link(chan->loophEdma, chan->loophEdma); /* Configure the EDMA channel to start with the Loop EDMA job */ EDMA_config(chan->xferPram, &loopEdmaCfg); EDMA_link(chan->xferPram, chan->loophEdma); /* Transfer complete is edge triggered, so clear before enabling */ EDMA_intClear(chan->tcc); EDMA_intEnable(chan->tcc); #if ENABLELOOPINTR EDMA_intClear(loopTcc); EDMA_intEnable(loopTcc); #endif /* Enable the EDMA interrupt */ IRQ_enable(IRQ_EVT_EDMAINT); EDMA_enableChannel(chan->xferPram); /* Start the McASP */ if (chan->mode == INPUT) { old = ATM_setu(&(port->chanCreated), TRUE); /* * clock generator is divided from high frequency clock, so start high * freq clock generator first */ if (port->enableHclkg == C6X1X_EDMA_MCASP_RCV) { MCASP_enableHclk(port->hMcasp, MCASP_RCV); while(!MCASP_FGETH(port->hMcasp, GBLCTL, RHCLKRST)); } else if ((port->enableHclkg == C6X1X_EDMA_MCASP_RCVXMT) && (old)) { MCASP_enableHclk(port->hMcasp, MCASP_RCVXMT); while(!(MCASP_FGETH(port->hMcasp, GBLCTL, RHCLKRST) & MCASP_FGETH(port->hMcasp, GBLCTL, XHCLKRST))); } if (port->enableClkg == C6X1X_EDMA_MCASP_RCV) { MCASP_enableClk(port->hMcasp, MCASP_RCV); while(!MCASP_FGETH(port->hMcasp, GBLCTL, RCLKRST)); } else if ((port->enableClkg == C6X1X_EDMA_MCASP_RCVXMT) && (old)) { MCASP_enableClk(port->hMcasp, MCASP_RCVXMT); while(!( MCASP_FGETH(port->hMcasp, GBLCTL, RCLKRST) & MCASP_FGETH(port->hMcasp, GBLCTL, XCLKRST))); } #if STARTSERIALIZERSYNC if (old) { MCASP_enableSers(port->hMcasp, MCASP_RCVXMT); while (!(MCASP_FGETH(port->hMcasp, GBLCTL, RSRCLR) & MCASP_FGETH(port->hMcasp, GBLCTL, XSRCLR))); while (MCASP_xdata(port->hMcasp)); MCASP_enableSm(port->hMcasp, MCASP_RCVXMT); while (!(MCASP_FGETH(port->hMcasp, GBLCTL, RSMRST) & MCASP_FGETH(port->hMcasp, GBLCTL, XSMRST))); } #else MCASP_enableSers(port->hMcasp, MCASP_RCV); while (!MCASP_FGETH(port->hMcasp, GBLCTL, RSRCLR)); MCASP_enableSm(port->hMcasp, MCASP_RCV); while (!MCASP_FGETH(port->hMcasp, GBLCTL, RSMRST)); #endif if (port->enableFsyncg == C6X1X_EDMA_MCASP_RCV) { MCASP_enableFsync(port->hMcasp, MCASP_RCV); while(!MCASP_FGETH(port->hMcasp, GBLCTL, RFRST)); } else if ((port->enableFsyncg == C6X1X_EDMA_MCASP_RCVXMT) && (old)) { MCASP_enableFsync(port->hMcasp, MCASP_RCVXMT); while(!(MCASP_FGETH(port->hMcasp, GBLCTL, RFRST) & MCASP_FGETH(port->hMcasp, GBLCTL, XFRST)) ); } } else { old = ATM_setu(&(port->chanCreated), TRUE); /* * clock generator is divided from high frequency clock, so start high * freq clock generator first */ if (port->enableHclkg == C6X1X_EDMA_MCASP_XMT) { MCASP_enableHclk(port->hMcasp, MCASP_XMT); while(!MCASP_FGETH(port->hMcasp, GBLCTL, XHCLKRST)); } else if ((port->enableHclkg == C6X1X_EDMA_MCASP_RCVXMT) && (old)) { MCASP_enableHclk(port->hMcasp, MCASP_RCVXMT); while(!( MCASP_FGETH(port->hMcasp, GBLCTL, RHCLKRST) & MCASP_FGETH(port->hMcasp, GBLCTL, XHCLKRST))); } if (port->enableClkg == C6X1X_EDMA_MCASP_XMT) { MCASP_enableClk(port->hMcasp, MCASP_XMT); while(!MCASP_FGETH(port->hMcasp, GBLCTL, XCLKRST)); } else if ((port->enableClkg == C6X1X_EDMA_MCASP_RCVXMT) && (old)) { MCASP_enableClk(port->hMcasp, MCASP_RCVXMT); while(!( MCASP_FGETH(port->hMcasp, GBLCTL, RCLKRST) & MCASP_FGETH(port->hMcasp, GBLCTL, XCLKRST))); } #if STARTSERIALIZERSYNC if (old) { MCASP_enableSers(port->hMcasp, MCASP_RCVXMT); while (!(MCASP_FGETH(port->hMcasp, GBLCTL, XSRCLR) & MCASP_FGETH(port->hMcasp, GBLCTL, RSRCLR))); while (MCASP_xdata(port->hMcasp)); MCASP_enableSm(port->hMcasp, MCASP_RCVXMT); while (!(MCASP_FGETH(port->hMcasp, GBLCTL, XSMRST) & MCASP_FGETH(port->hMcasp, GBLCTL, RSMRST))); } #else MCASP_enableSers(port->hMcasp, MCASP_XMT); while (!(MCASP_FGETH(port->hMcasp, GBLCTL, XSRCLR))); while (MCASP_xdata(port->hMcasp)); MCASP_enableSm(port->hMcasp, MCASP_XMT); while (!(MCASP_FGETH(port->hMcasp, GBLCTL, XSMRST))); #endif if (port->enableFsyncg == C6X1X_EDMA_MCASP_XMT) { MCASP_enableFsync(port->hMcasp, MCASP_XMT); while(!MCASP_FGETH(port->hMcasp,GBLCTL, XFRST)); } else if ((port->enableFsyncg == C6X1X_EDMA_MCASP_RCVXMT) && (old)) { MCASP_enableFsync(port->hMcasp, MCASP_RCVXMT); while(!(MCASP_FGETH(port->hMcasp,GBLCTL, RFRST) & MCASP_FGETH(port->hMcasp,GBLCTL, XFRST)) ); } } /* enable McASP event interrupt if registered */ /* Clear CKFAIL error before enable interrupt */ if (chan->mode == INPUT) { if (port->evtCallback != NULL) { if (port->evtMask & C6X1X_EDMA_MCASP_EVT_RCKFAIL) { MCASP_FSETSH(port->hMcasp, RINTCTL, RCKFAIL, DISABLE); while (MCASP_FGETH(port->hMcasp, RSTAT, RCKFAIL)) { MCASP_FSETSH(port->hMcasp, RSTAT, RCKFAIL, YES); } MCASP_FSETSH(port->hMcasp, RINTCTL, RCKFAIL, ENABLE); } IRQ_enable(MCASP_getRcvEventId(port->hMcasp)); } } else { if (port->evtCallback != NULL) { if (port->evtMask & C6X1X_EDMA_MCASP_EVT_XCKFAIL) { MCASP_FSETSH(port->hMcasp, XINTCTL, XCKFAIL, DISABLE); while (MCASP_FGETH(port->hMcasp, XSTAT, XCKFAIL)) { MCASP_FSETSH(port->hMcasp, XSTAT, XCKFAIL, YES); } MCASP_FSETSH(port->hMcasp, XINTCTL, XCKFAIL, ENABLE); } IRQ_enable(MCASP_getXmtEventId(port->hMcasp)); } } *chanp = (Ptr) chan; return (IOM_COMPLETED); } /* * ======== mdDeleteChan ======== * This function frees a channel and all it's associated resources. */ static Int mdDeleteChan(Ptr chanp) { ChanHandle chan = (ChanHandle) chanp; PortHandle port = chan->port; /* Clean up the channel resources */ chanCleanUp(chan, DELCHAN); /* * Reset the McASP transmitter or receiver. If the channel is * recreated, mdCreateChan() will reenable the transmitter/receiver * and by pulling it out of reset it will also restart the EDMA channel. */ if (chan->mode == INPUT) { MCASP_resetRcv(port->hMcasp); } else { MCASP_resetXmt(port->hMcasp); } return (IOM_COMPLETED); } /* * ======== mdSubmitChan ======== * This function transmits a buffer to or from the McASP using the EDMA. */ static Int mdSubmitChan(Ptr chanp, IOM_Packet *packet) { ChanHandle chan = (ChanHandle) chanp; PortHandle port = chan->port; Uns imask; /* No packets can be submitted while abort or flush is active */ if (chan->flushPacket || chan->abortPacket) { return (IOM_EBADIO); } /* * Check to see if an abort command has been issued. Note that * flushing the input channel is handled the same as abort. */ if ((packet->cmd == IOM_FLUSH && chan->mode == INPUT) || packet->cmd == IOM_ABORT) { /* Disable interrupts to protect submitCount */ imask = HWI_disable(); /* Store the abort packet for the ISR to check */ if (chan->submitCount > 0) { chan->abortPacket = packet; /* * Disable the EDMA channel while linking the currently * executing job while linking it with the Loop job to make * sure the currently executing job doesn't complete before * the link is complete. */ //EDMA_disableChannel(chan->xferPram); EDMA_link(chan->xferPram, chan->loophEdma); //EDMA_enableChannel(chan->xferPram); } /* Reenable interrupts */ HWI_restore(imask); if (chan->abortPacket) { return(IOM_PENDING); } /* If there were no buffers in the channel, return synchronously */ packet->status = IOM_COMPLETED; return (IOM_COMPLETED); } /* Check to see if the submitted packet is an output flush packet */ if (packet->cmd == IOM_FLUSH && chan->mode == OUTPUT) { /* Disable interrupts to protect submitCount */ imask = HWI_disable(); /* Store the flush packet for the ISR to check */ if (chan->submitCount > 0) { chan->flushPacket = packet; } /* Reenable interrupts */ HWI_restore(imask); if (chan->flushPacket) { return(IOM_PENDING); } /* If there were no buffers in the channel, return synchronously */ packet->status = IOM_COMPLETED; return (IOM_COMPLETED); } if (packet->cmd != IOM_READ && packet->cmd != IOM_WRITE) { /* Unsupported command passed */ return (IOM_ENOTIMPL); } /* maintain cache coherency */ if (chan->mode == INPUT) { /* CACHE uses words, and packet->size is in nmaus (bytes on c6x) */ if (port->cacheCalls) { CACHE_clean(CACHE_L2, packet->addr, packet->size >> 2); } } else { /* CACHE uses words, and packet->size is in nmaus (bytes on c6x) */ if (port->cacheCalls) { CACHE_flush(CACHE_L2, packet->addr, packet->size >> 2); } } /* Disable interrupts to protect submitCount */ imask = HWI_disable(); /* * If there is no space available for the new packet, put it on a * queue to be linked in when space is available. Otherwise link it in. */ if (chan->submitCount >= MAXLINKCNT) { QUE_enqueue(&chan->packetQueue, packet); } else { linkPacket(chan, packet); } chan->submitCount++; HWI_restore(imask); return (IOM_PENDING); } /* * ======== mdUnBindDev ======== * This function frees a port and all it's associated resources. */ static Int mdUnBindDev(Ptr devp) { PortHandle port = (PortHandle) devp; port->inUse = FALSE; /* Close the McASP */ MCASP_close(port->hMcasp); return (IOM_COMPLETED); } /* * ======== C6X1X_EDMA_MCASP_init ======== * This function initializes the driver's data structures. */ #pragma CODE_SECTION(C6X1X_EDMA_MCASP_init, ".text:init"); Void C6X1X_EDMA_MCASP_init() { PortHandle port; ChanHandle chan; Int i, j; /* Make sure the initialization only happens once for thid driver */ static Bool curInit = FALSE; if (curInit) { return; } curInit = TRUE; for (i=0; i inUse = FALSE; for (j=0; j chans[j]; chan->inUse = FALSE; } } }