Project

General

Profile

Sample uPP Device Driver » DspUpp.cpp

DSP based uPP Driver .cpp source - Thomas Catalino, 01/26/2012 01:40 AM

 
/**
* \file tcDspUpp.cpp
*
* \brief DSP uPP driver source
*
* o 0
* | / Copyright (c) 2005-2011
* (CL)---o Critical Link, LLC
* \
* O
*/

#include "DspUpp.h"
#include "memorymap.h"
#include "core/DspSyscfg.h"
#include "core/DspLpsc.h"

#include <tsk.h>
#include <assert.h>
#include <hwi.h>
#include <c62.h>

using namespace MityDSP;

SEM_Handle tcDspUpp::mhGetInstSem = SEM_create(1, NULL);

tcDspUpp* tcDspUpp::mpDspUpp = NULL;

/**
* Get instance of tcDspUpp.
*/
tcDspUpp*
tcDspUpp::getInstance()
{
// Pend on the static mutex so that we do not accidentally
// init too many objects
SEM_pend(mhGetInstSem, SYS_FOREVER);

// Check if the singleton need initialization
if (NULL == mpDspUpp)
{
mpDspUpp = new tcDspUpp();
}

// Safe to return the mutex now
SEM_post(mhGetInstSem);

// Return pointer to singleton
return mpDspUpp;
}

/**
* Intiailize the uPP device.
*/
int
tcDspUpp::initialize(tsDspUppConfig const* apDspUppConfig)
{
// ISR attributes
HWI_Attrs hwi_attrs = {0, 0, (Arg)this};

TSK_Attrs tsk_attrs = TSK_ATTRS;

tuUppcrReg luUppcrReg = {0};
tuUpctlReg luUpctlReg = {0};
tuUpicrReg luUpicrReg = {0};
tuUpivrReg luUpivrReg = {0};
tuUptcrReg luUptcrReg = {0};
tuUpiesReg luUpiesReg = {0};

// Pin Mux functions to make sure uPP is enabled properly for Channel A
tePinFunc laPinFuncCHA[] =
{
UPP_CH1_WAIT,
UPP_CH1_ENABLE,
UPP_CH1_START,
UPP_CH1_CLK,
PINFUNC_LIST_TERMINATE
};

// Pin Mux functions to make sure uPP is enabled properly for Channel B
tePinFunc laPinFuncCHB[] =
{
UPP_CH0_WAIT,
UPP_CH0_ENABLE,
UPP_CH0_START,
UPP_CH0_CLK,
PINFUNC_LIST_TERMINATE
};

// Pin Mux functions for DATA[15:8]
tePinFunc laPinFuncData15_8[] =
{
UPP_D8,
UPP_D9,
UPP_D10,
UPP_D11,
UPP_D12,
UPP_D13,
UPP_D14,
UPP_D15,
PINFUNC_LIST_TERMINATE
};

// Pin Mux functions for DATA[7:0]
tePinFunc laPinFuncData7_0[] =
{
UPP_D0,
UPP_D1,
UPP_D2,
UPP_D3,
UPP_D4,
UPP_D5,
UPP_D6,
UPP_D7,
PINFUNC_LIST_TERMINATE
};
// Pin Mux functions for XDATA[15:8]
tePinFunc laPinFuncXData15_8[] =
{
UPP_XD8,
UPP_XD9,
UPP_XD10,
UPP_XD11,
UPP_XD12,
UPP_XD13,
UPP_XD14,
UPP_XD15,
PINFUNC_LIST_TERMINATE
};
// Pin Mux functions for XDATA[7:0]
tePinFunc laPinFuncXData7_0[] =
{
UPP_XD0,
UPP_XD1,
UPP_XD2,
UPP_XD3,
UPP_XD4,
UPP_XD5,
UPP_XD6,
UPP_XD7,
PINFUNC_LIST_TERMINATE
};

// MbxA attributes //TODO: non-default attributes?
MBX_Attrs lsMbxAttrsA = MBX_ATTRS;
// MbxB attributes //TODO: non-default attributes?
MBX_Attrs lsMbxAttrsB = MBX_ATTRS;

// Length of Chan A MBXs
uint32_t lnMbxLenA = apDspUppConfig->nMbxLenA;
// Length of Chan B MBXs
uint32_t lnMbxLenB = apDspUppConfig->nMbxLenB;


// Configuration sanity checks
if (eeDisabled == apDspUppConfig->eChanADir &&
eeDisabled == apDspUppConfig->eChanBDir)
return -1;

if (apDspUppConfig->nHWInterruptLevel > 15 ||
apDspUppConfig->nHWInterruptLevel < 4)
{
return -1;
}

// TODO: Additional checks?

// Check if we've already been initialized
if (false == mbFirstInit)
{
// TODO: any necessary shutdown before the re-init

// Reset all pin config to default? (need to? Make sense?)
}

mbFirstInit = false;

// Reset channels to disabled in case there is a failure
meChanADir = eeDisabled;
meChanADir = eeDisabled;

// Set uPP DMA Master Priority
tcDspSyscfg::SetMasterPriority(tcDspSyscfg::eeUPP,
apDspUppConfig->nDmaMasterPriority);

// Apply the appropriate pin mux settings to enable the uPP
// (based on configuration)
if (eeTransmit == apDspUppConfig->eChanADir ||
eeTransmit == apDspUppConfig->eChanBDir)
{
// Select the appropriate Transmit Clock
if (eeUPP_2xTXCLK == apDspUppConfig->eTxClockSel)
{
tcDspSyscfg::SetChipConfig(UPP_TX_CLKSRC_2xTXCLK);

// Enable 2xTXCLK pin
if (tcDspSyscfg::SetPinMuxConfig(UPP_2xTXCLK) < 0)
return -1;
}
else if (eePLL0_SYSCLK2 == apDspUppConfig->eTxClockSel)
{
tcDspSyscfg::SetChipConfig(ASYNC3_CLKSRC_PLL0_SYSCLK2);
tcDspSyscfg::SetChipConfig(UPP_TX_CLKSRC_ASYNC3);
}
else if (eePLL1_SYSCLK2 == apDspUppConfig->eTxClockSel)
{
tcDspSyscfg::SetChipConfig(ASYNC3_CLKSRC_PLL1_SYSCLK2);
tcDspSyscfg::SetChipConfig(UPP_TX_CLKSRC_ASYNC3);
}
}

if (eeDisabled != apDspUppConfig->eChanADir)
{
// Enable Channel A pins if it is not disabled
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncCHA) < 0)
return -1;
}

if (eeDisabled != apDspUppConfig->eChanBDir)
{
// Enable Channel B pins if it is not disabled
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncCHB) < 0)
return -1;
}

if (eeDisabled == apDspUppConfig->eChanBDir &&
eeDisabled != apDspUppConfig->eChanADir)
{
// Enable lower 8 bits for Channel A
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncData7_0) < 0)
return -1;

if (ee8Bit != apDspUppConfig->eChanBitWidthA)
{
// Enable upper 8 bits for Channel A
if (true == apDspUppConfig->bChanAUseXData)
{
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncXData7_0) < 0)
return -1;
}
else
{
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncData15_8) < 0)
return -1;
}
}
}
else
{
if (eeDisabled != apDspUppConfig->eChanADir)
{
// Enable lower 8 bits for Channel A
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncData7_0) < 0)
return -1;
if (ee8Bit != apDspUppConfig->eChanBitWidthA)
{
// Enable upper 8 bits for Channel A
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncXData7_0) < 0)
return -1;
}
}

if (eeDisabled != apDspUppConfig->eChanBDir)
{
// Enable lower 8 bits for Channel B
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncData15_8) < 0)
return -1;

if (ee8Bit != apDspUppConfig->eChanBitWidthB)
{
// Enable upper 8 bits for Channel B
if (tcDspSyscfg::SetPinMuxConfig(laPinFuncXData15_8) < 0)
return -1;
}
}

}
// make sure to enable the power and clocks to the uPP device
tcDspLpsc::ConfigPeripheral(tcDspLpsc::eeUPP, tcDspLpsc::eeENABLE);

// Delete Chan A MBXs if they exists
if (NULL != mhMbxDoneA)
MBX_delete(mhMbxDoneA);

if (NULL != mhMbxIntA)
MBX_delete(mhMbxIntA);

if (NULL != mhMbxQueueA)
MBX_delete(mhMbxQueueA);

// Delete Chan B MBXs if they exists
if (NULL != mhMbxDoneB)
MBX_delete(mhMbxDoneB);
if (NULL != mhMbxIntB)
MBX_delete(mhMbxIntB);

if (NULL != mhMbxQueueB)
MBX_delete(mhMbxQueueB);


// Initialize Chan A MBXs if Chan A is enabled
if (eeDisabled != apDspUppConfig->eChanADir)
{
lsMbxAttrsA.name = "mhMbxDoneA";
mhMbxDoneA = MBX_create(sizeof(tsMbxMsg), lnMbxLenA,
&lsMbxAttrsA);
if (NULL == mhMbxDoneA)
return -1;

lsMbxAttrsA.name = "mhMbxIntA";
mhMbxIntA = MBX_create(sizeof(tsMbxMsg), 2, &lsMbxAttrsA);
if (NULL == mhMbxIntA)
return -1;

lsMbxAttrsA.name = "mhMbxQueueA";
mhMbxQueueA = MBX_create(sizeof(tsMbxMsg), lnMbxLenA,
&lsMbxAttrsA);
if (NULL == mhMbxQueueA)
return -1;
}

// Initialize Chan B MBXs if Chan B is enabled
if (eeDisabled != apDspUppConfig->eChanBDir)
{
lsMbxAttrsB.name = "mhMbxDoneB";
mhMbxDoneB = MBX_create(sizeof(tsMbxMsg), lnMbxLenB,
&lsMbxAttrsB);
if (NULL == mhMbxDoneB)
return -1;

lsMbxAttrsB.name = "mhMbxIntB";
mhMbxIntB = MBX_create(sizeof(tsMbxMsg), 2, &lsMbxAttrsB);
if (NULL == mhMbxIntB)
return -1;

lsMbxAttrsB.name = "mhMbxQueueB";
mhMbxQueueB = MBX_create(sizeof(tsMbxMsg), lnMbxLenB,
&lsMbxAttrsB);
if (NULL == mhMbxQueueB)
return -1;
}

// Reset the uPP
reset();

// Program UPCTL reg (mode, data width/format, etc.)
if (eeTransmit == apDspUppConfig->eChanADir &&
eeReceive == apDspUppConfig->eChanBDir)
{
luUpctlReg.sRegBits.MODE = eeAXmitBRcv; // Xmit/Rcv Mode
}
else if (eeReceive == apDspUppConfig->eChanADir &&
eeTransmit == apDspUppConfig->eChanBDir)
{
luUpctlReg.sRegBits.MODE = eeARcvBXmit; // Xmit/Rcv Mode
}
else if (eeTransmit == apDspUppConfig->eChanADir ||
eeTransmit == apDspUppConfig->eChanBDir)
{
luUpctlReg.sRegBits.MODE = eeAllXmit; // Xmit/Rcv Mode
}
else if (eeReceive == apDspUppConfig->eChanADir ||
eeReceive == apDspUppConfig->eChanBDir)
{
luUpctlReg.sRegBits.MODE = eeAllRcv; // Xmit/Rcv Mode
}
if (eeDisabled != apDspUppConfig->eChanBDir ||
true == apDspUppConfig->bChanAUseXData)
{
// Must "enable" both channels if B is active
// Though if it's just B active, we do not enable A's pinmuxing
// Or in case where only Channel A is active, but we want CHN=1
// data bit assignments
luUpctlReg.sRegBits.CHN = 1; // Only Chan A active, or Chan A/B active
}

luUpctlReg.sRegBits.SDRTXIL = 0; // Not supported... yet
luUpctlReg.sRegBits.DDRDEMUX = 0; // Not supported... yet
luUpctlReg.sRegBits.DRA = 0; // Chan A single/double data rate
if (ee8Bit != apDspUppConfig->eChanBitWidthA)
{
luUpctlReg.sRegBits.IWA = 1; // Chan A 8/16-bit interface
}

// Mod 8 because 8 and 16 bit = 0
luUpctlReg.sRegBits.DPWA = (apDspUppConfig->eChanBitWidthA)%8;
luUpctlReg.sRegBits.DPFA = eeRJSE; // Chan A data packing format

luUpctlReg.sRegBits.DRB = 0; // Chan B single/double data rate
if (ee8Bit != apDspUppConfig->eChanBitWidthB)
{
luUpctlReg.sRegBits.IWB = 1; // Chan B 8/16-bit interface
}

// Mod 8 because 8 and 16 bit = 0
luUpctlReg.sRegBits.DPWB = (apDspUppConfig->eChanBitWidthB)%8;
luUpctlReg.sRegBits.DPFB = eeRJSE; // Chan B data packing format
mpUppRegs->UPCTL = luUpctlReg.nRegWord;

// Program UPICR reg (signal enable, clock rate)
luUpicrReg.sRegBits.STARTPOLA = 0;
luUpicrReg.sRegBits.ENAPOLA = 0;
luUpicrReg.sRegBits.WAITPOLA = 0;
luUpicrReg.sRegBits.STARTA = apDspUppConfig->bChanAUseStart;
luUpicrReg.sRegBits.ENAA = 1;
luUpicrReg.sRegBits.WAITA = 1;
luUpicrReg.sRegBits.CLKDIVA = apDspUppConfig->nChanAClkDiv;
luUpicrReg.sRegBits.CLKINVA = 0;
luUpicrReg.sRegBits.TRISA = 0; // Chan A high-impedence state

luUpicrReg.sRegBits.STARTPOLB = 0;
luUpicrReg.sRegBits.ENAPOLB = 0;
luUpicrReg.sRegBits.WAITPOLB = 0;
luUpicrReg.sRegBits.STARTB = apDspUppConfig->bChanBUseStart;
luUpicrReg.sRegBits.ENAB = 1;
luUpicrReg.sRegBits.WAITB = 1;
luUpicrReg.sRegBits.CLKDIVB = apDspUppConfig->nChanBClkDiv;
luUpicrReg.sRegBits.CLKINVB = 0;
luUpicrReg.sRegBits.TRISB = 0; // Chan B high-impedence state

mpUppRegs->UPICR = luUpicrReg.nRegWord;

// Program UPIVR reg (idle xmit value)
luUpivrReg.sRegBits.VALA = 0xFFFF; // Chan A idle value, if TRISA==0
luUpivrReg.sRegBits.VALB = 0xFFFF; // Chan B idle value, if TRISB==0

mpUppRegs->UPIVR = luUpivrReg.nRegWord;

// Program UPTCR reg (i/o threshold)
luUptcrReg.sRegBits.RDSIZEI = apDspUppConfig->eThresholdRxA;
luUptcrReg.sRegBits.RDSIZEQ = apDspUppConfig->eThresholdRxB;
luUptcrReg.sRegBits.TXSIZEA = apDspUppConfig->eThresholdTxA;
luUptcrReg.sRegBits.TXSIZEB = apDspUppConfig->eThresholdTxB;

mpUppRegs->UPTCR = luUptcrReg.nRegWord;

// Program UPDLB reg (digital loopback)
mpUppRegs->UPDLB = 0; // TODO: support internal loopback?

// Clear all interrupts using UPIEC
mpUppRegs->UPIEC = 0x1F1F;

// Program uPP interrupt enable reg (UPIES)
luUpiesReg.nRegWord = 0;
// DMA I interrupts
if (eeDisabled != apDspUppConfig->eChanADir)
{
luUpiesReg.sRegBits.EOWI = 1;
luUpiesReg.sRegBits.DPEI = 1;
luUpiesReg.sRegBits.UORI = 1;
luUpiesReg.sRegBits.ERRI = 1;
// No need to service end of line interrupt
luUpiesReg.sRegBits.EOLI = 0;
}
// DMA Q interrupts
if (eeDisabled != apDspUppConfig->eChanBDir)
{
luUpiesReg.sRegBits.EOWQ = 1;
luUpiesReg.sRegBits.DPEQ = 1;
luUpiesReg.sRegBits.UORQ = 1;
luUpiesReg.sRegBits.ERRQ = 1;
// No need to service end of line interrupt
luUpiesReg.sRegBits.EOLQ = 0;
}
mpUppRegs->UPIES = luUpiesReg.nRegWord;

// Register ISR (if enabled)
// Setup interrupt handling function
// TODO: Failure codes for these functions?
HWI_dispatchPlug(apDspUppConfig->nHWInterruptLevel,
(Fxn)isr,
-1,
&hwi_attrs);
HWI_eventMap(apDspUppConfig->nHWInterruptLevel,
94);
C62_enableIER(1 << apDspUppConfig->nHWInterruptLevel);

// Store directionality of channels
meChanADir = apDspUppConfig->eChanADir;
meChanBDir = apDspUppConfig->eChanBDir;

// Turn on the uPP and other final UPPCR config
luUppcrReg.sRegBits.FREE = 1; // Emulation will not halt uPP
luUppcrReg.sRegBits.EN = 1; // Enable uPP device
mpUppRegs->UPPCR = luUppcrReg.nRegWord;

// Start the Chan A thread for handling the DMA
if (eeDisabled != apDspUppConfig->eChanADir)
{
if (NULL != mhDmaTskA)
TSK_delete(mhDmaTskA);

tsk_attrs = TSK_ATTRS;
tsk_attrs.name = "DmaTskA";
tsk_attrs.stacksize = 1024;
tsk_attrs.priority = apDspUppConfig->nTskPriorityChanA;
mhDmaTskA = TSK_create((Fxn)programDMA, &tsk_attrs, this,
eeChanA);

// Check that task creation was successful
if (NULL == mhDmaTskA)
return -1;
}

// Start the Chan B thread for handling the DMA
if (eeDisabled != apDspUppConfig->eChanBDir)
{
if (NULL != mhDmaTskB)
TSK_delete(mhDmaTskB);

tsk_attrs = TSK_ATTRS;
tsk_attrs.name = "DmaTskB";
tsk_attrs.stacksize = 1024;
tsk_attrs.priority = apDspUppConfig->nTskPriorityChanB;
mhDmaTskB = TSK_create((Fxn)programDMA, &tsk_attrs, this,
eeChanB);

// Check that task creation was successful
if (NULL == mhDmaTskB)
return -1;
}

return 0;
}

/**
* Perform software reset of the uPP.
*
* @return None.
*/
void
tcDspUpp::reset()
{
tuUppcrReg luUppcrReg = {0};

// Read current contents of the register
luUppcrReg.nRegWord = mpUppRegs->UPPCR;

// Place the uPP in SW reset
luUppcrReg.sRegBits.SWRST = 1; // SW reset enabled
mpUppRegs->UPPCR = luUppcrReg.nRegWord;

// Wait at least 200 cycles
TSK_sleep(200);

// Clear the SW reset bit
luUppcrReg.sRegBits.SWRST = 0; // SW reset disabled
mpUppRegs->UPPCR = luUppcrReg.nRegWord;
}

/**
* Get handle to mailbox for associated channel where info on
*/
MBX_Handle
tcDspUpp::getMBX(teUppChan aeChan)
{
return (eeChanB == aeChan)?mhMbxDoneB:mhMbxDoneA;
}

/**
* Queue transmit of given data buffer. Use getMBX() to get corresponding
* mailbox where pointer info will be posted once data has been tramsmitted.
*/
int
tcDspUpp::transmit(teUppChan aeChan,
const uint8_t* apXmitData,
uint16_t anByteCnt,
uint16_t anLineCnt,
uint16_t anLineOffset)
{
// MBX Queue msg
tsMbxMsg lsMbxMsg;
// Queue MBX
MBX_Handle lhMbxQueue = (eeChanB == aeChan)?mhMbxQueueB:mhMbxQueueA;

// Check aeChan directionality...
if (eeChanA == aeChan)
{
if (eeTransmit != meChanADir)
{
return -1;
}
}
else if (eeChanB == aeChan)
{
if (eeTransmit != meChanBDir)
{
return -1;
}
}
else
{
return -1;
}

// Check if apXmitData is on 64-bit aligned
if (0 != (((uint32_t)apXmitData)&0x7))
return -1;
// Check that anByteCnt is even
if (0 != (anByteCnt&0x1))
return -1;
// Check that anLineOffset is 64-bit aligned
if (0 != (anLineOffset&0x7))
return -1;
//TODO: Check restrictions on other inputs

// Setup the request
lsMbxMsg.pBufPtr = (uint8_t*)apXmitData;
lsMbxMsg.nByteCnt = anByteCnt;
lsMbxMsg.nLineCnt = anLineCnt;
lsMbxMsg.nLineOffset = anLineOffset;
lsMbxMsg.pOptArg = NULL;

// Add the request to the queue mailbox
if (false == MBX_post(lhMbxQueue, &lsMbxMsg, SYS_FOREVER))
{
return -1;
}

return 0;
// TODO: failure conditions!
}

/**
* Add buffer to receive queue. Use getMBX() to get corresponding
* mailbox where pointer info will be posted once data has been received.
*/
int
tcDspUpp::receive(teUppChan aeChan,
uint8_t* apRcvData,
uint16_t anByteCnt,
uint16_t anLineCnt,
uint16_t anLineOffset)
{
// MBX Queue msg
tsMbxMsg lsMbxMsg;
// Queue MBX
MBX_Handle lhMbxQueue = (eeChanB == aeChan)?mhMbxQueueB:mhMbxQueueA;

// Check aeChan directionality...
if (eeChanA == aeChan)
{
if (eeReceive != meChanADir)
{
return -1;
}
}
else if (eeChanB == aeChan)
{
if (eeReceive != meChanBDir)
{
return -1;
}
}
else
{
return -1;
}

// Check if apXmitData is on 64-bit aligned
if (0 != (((uint32_t)apRcvData)&0x7))
return -1;
// Check that anByteCnt is even
if (0 != (anByteCnt&0x1))
return -1;
// Check that anLineOffset is 64-bit aligned
if (0 != (anLineOffset&0x7))
return -1;


// Setup the request
lsMbxMsg.pBufPtr = apRcvData;
lsMbxMsg.nByteCnt = anByteCnt;
lsMbxMsg.nLineCnt = anLineCnt;
lsMbxMsg.nLineOffset = anLineOffset;
lsMbxMsg.pOptArg = NULL;

// Add the request to the queue mailbox
if (false == MBX_post(lhMbxQueue, &lsMbxMsg, SYS_FOREVER))
{
return -1;
}

return 0;
//TODO: failure conditions!
}

/**
* Thread for programming the DMA for the specified channel.
*/
void
tcDspUpp::programDMA(tcDspUpp* apDspUpp, teUppChan aeChan)
{
// Pointer to the tcDspUpp object
tcDspUpp* lpDspUpp = apDspUpp;
// Queue MBX
MBX_Handle lhMbxQueue = (eeChanB == aeChan)?
lpDspUpp->mhMbxQueueB:lpDspUpp->mhMbxQueueA;
// Intermediate MBX (for buffers being DMAed)
MBX_Handle lhMbxInt = (eeChanB == aeChan)?
lpDspUpp->mhMbxIntB:lpDspUpp->mhMbxIntA;
// Local MBX message for copying and setting DMA
tsMbxMsg lsMbxMsg;
// Used for checking DMA status
tuUpiqs2Reg luUpiqs2Reg = {0};
// Used for setting the DMA reg 0
tuUpiqd1Reg luUpiqd1Reg = {0};

// This thread runs continuously
while(1)
{

// Pend on mhMbxQueue waiting for a new message that can be
// used to program the DMA
MBX_pend(lhMbxQueue, &lsMbxMsg, SYS_FOREVER);

// Post to mhMbxInt waiting for space to open up in DMA
MBX_post(lhMbxInt, &lsMbxMsg, SYS_FOREVER);

// Now we should be able to safely program the DMA

// Read the appropriate DMA status register
if (eeChanB == aeChan)
{
luUpiqs2Reg.nRegWord = lpDspUpp->mpUppRegs->UPQS2;
}
else
{
luUpiqs2Reg.nRegWord = lpDspUpp->mpUppRegs->UPIS2;
}

// Check if the DMA can be programmed
while (1 == luUpiqs2Reg.sRegBits.PEND)
{
// The DMA is busy, this should not happen
if (NULL != lpDspUpp->mpErrorCallback)
lpDspUpp->mpErrorCallback(0xDEADBEEF);

// Maybe it will fix itself?
TSK_sleep(100);
}

// Program the DMA
luUpiqd1Reg.sRegBits.BCNT = lsMbxMsg.nByteCnt;
luUpiqd1Reg.sRegBits.LNCNT = lsMbxMsg.nLineCnt;

if (eeChanB == aeChan)
{
lpDspUpp->mpUppRegs->UPQD0 = (uint32_t)lsMbxMsg.pBufPtr;
lpDspUpp->mpUppRegs->UPQD1 = luUpiqd1Reg.nRegWord;
lpDspUpp->mpUppRegs->UPQD2 = lsMbxMsg.nLineOffset;
}
else
{
lpDspUpp->mpUppRegs->UPID0 = (uint32_t)lsMbxMsg.pBufPtr;
lpDspUpp->mpUppRegs->UPID1 = luUpiqd1Reg.nRegWord;
lpDspUpp->mpUppRegs->UPID2 = lsMbxMsg.nLineOffset;
}

}
}

/**
* Handle any uPP related interrupts that might occur.
*
* \return 0 on success, negative on failure.
*/
int
tcDspUpp::isr(tcDspUpp* apDspUpp)
{
// Return value
int retval = 0;
// Pointer to the tcDspUpp object
tcDspUpp* lpDspUpp = apDspUpp;
// Local copy of uPP registers so that we don't have to
// dereference lpDspUpp so many times
volatile tsUppRegs* const lpUppRegs = lpDspUpp->mpUppRegs;
// Value of the UPIER register, which tells us which interrupts occurred
tuUpierReg luUpierReg = {0};
// Used to clear the UPIER register interrupts once processed
tuUpierReg luUpierRegClr = {0};
// Used to update the Done MBX
tsMbxMsg lsMbxMsg;

// Check for interrupts
luUpierReg.nRegWord = lpUppRegs->UPIER;

// Process all pending interrupts.
while (0 != luUpierReg.nRegWord)
{
// Check for Channel I programming error interrupt
if (luUpierReg.sRegBits.DPEI == 1)
{
// Clear the interrupt
luUpierRegClr.nRegWord = 0;
luUpierRegClr.sRegBits.DPEI = 1;
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
// Handle the interrupt
if (NULL != lpDspUpp->mpErrorCallback)
lpDspUpp->mpErrorCallback(luUpierRegClr.nRegWord);
}

// Check for Channel I underrun/overflow interrupt
if (luUpierReg.sRegBits.UORI == 1)
{
// Clear the interrupt
luUpierRegClr.nRegWord = 0;
luUpierRegClr.sRegBits.UORI = 1;
lpUppRegs->UPIER = luUpierRegClr.nRegWord;

// Handle the interrupt
if (NULL != lpDspUpp->mpErrorCallback)
lpDspUpp->mpErrorCallback(luUpierRegClr.nRegWord);
}

// Check for Channel I error interrupt
if (luUpierReg.sRegBits.ERRI == 1)
{
// Clear the interrupt
luUpierRegClr.nRegWord = 0;
luUpierRegClr.sRegBits.ERRI = 1;
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
// Handle the interrupt
if (NULL != lpDspUpp->mpErrorCallback)
lpDspUpp->mpErrorCallback(luUpierRegClr.nRegWord);
}

// Check for Channel I End-of-Window interrupt
if (luUpierReg.sRegBits.EOWI == 1)
{
// Clear the interrupt
luUpierRegClr.nRegWord = 0;
luUpierRegClr.sRegBits.EOWI = 1;
lpUppRegs->UPIER = luUpierRegClr.nRegWord;

// Handle the interrupt
// Get the DMAed data info
if (true == MBX_pend(lpDspUpp->mhMbxIntA,
&lsMbxMsg,
0))
{
// Update the done MBX
if (false == MBX_post(lpDspUpp->mhMbxDoneA,
&lsMbxMsg,
0))
{
// Return queue overflow!
retval = -1;
}
}
else
{
// No data in the intermediate mailbox, but we received
// an interrupt, this is a problem
retval = -1;
}
}

// Check for Channel I End-of-Line interrupt
if (luUpierReg.sRegBits.EOLI == 1)
{
// Clear the interrupt
luUpierRegClr.sRegBits.EOLI = 1;
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
luUpierRegClr.nRegWord = 0;

// Handle the interrupt

}

// Check for Channel Q programming error interrupt
if (luUpierReg.sRegBits.DPEQ == 1)
{
// Clear the interrupt
luUpierRegClr.nRegWord = 0;
luUpierRegClr.sRegBits.DPEQ = 1;
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
// Handle the interrupt
if (NULL != lpDspUpp->mpErrorCallback)
lpDspUpp->mpErrorCallback(luUpierRegClr.nRegWord);
}

// Check for Channel Q underrun/overflow interrupt
if (luUpierReg.sRegBits.UORQ == 1)
{
// Clear the interrupt
luUpierRegClr.nRegWord = 0;
luUpierRegClr.sRegBits.UORQ = 1;
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
// Handle the interrupt
if (NULL != lpDspUpp->mpErrorCallback)
lpDspUpp->mpErrorCallback(luUpierRegClr.nRegWord);
}

// Check for Channel Q error interrupt
if (luUpierReg.sRegBits.ERRQ == 1)
{
// Clear the interrupt
luUpierRegClr.nRegWord = 0;
luUpierRegClr.sRegBits.ERRQ = 1;
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
// Handle the interrupt
if (NULL != lpDspUpp->mpErrorCallback)
lpDspUpp->mpErrorCallback(luUpierRegClr.nRegWord);
}

// Check for Channel Q End-of-Window interrupt
if (luUpierReg.sRegBits.EOWQ == 1)
{
// Clear the interrupt
luUpierRegClr.nRegWord = 0;
luUpierRegClr.sRegBits.EOWQ = 1;
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
// Handle the interrupt
// Get the DMAed data info
if (true == MBX_pend(lpDspUpp->mhMbxIntB,
&lsMbxMsg,
0))
{
// Update the done MBX
if (false == MBX_post(lpDspUpp->mhMbxDoneB,
&lsMbxMsg,
0))
{
// Return queue overflow!
retval = -1;
}
}
else
{
// No data in the intermediate mailbox, but we received
// an interrupt, this is a problem
retval = -1;
}
}

// Check for Channel Q End-of-Line interrupt
if (luUpierReg.sRegBits.EOLQ == 1)
{
// Clear the interrupt
luUpierRegClr.nRegWord = 0;
luUpierRegClr.sRegBits.EOLQ = 1;
lpUppRegs->UPIER = luUpierRegClr.nRegWord;
// Handle the interrupt

}

// Check for more interrupts
luUpierReg.nRegWord = lpUppRegs->UPIER;
}

// Write end of interrupt vector to allow future calls
lpUppRegs->UPEOI = 0;

return retval;
}

/**
* Set the error callback function.
*/
void
tcDspUpp::registerErrorCallback(tfErrorCallback afErrorCallback)
{
mpErrorCallback = afErrorCallback;
}

/**
* Private constructor.
*/
tcDspUpp::tcDspUpp()
: mpUppRegs((tsUppRegs*)UPP_REG_BASE)
, mhDmaTskA(NULL)
, mhDmaTskB(NULL)
, mhMbxDoneA(NULL)
, mhMbxDoneB(NULL)
, mhMbxIntA(NULL)
, mhMbxIntB(NULL)
, mhMbxQueueA(NULL)
, mhMbxQueueB(NULL)
, mpErrorCallback(NULL)
, mbFirstInit(true)
, meChanADir(eeDisabled)
, meChanBDir(eeDisabled)
{

}

/**
* Private destructor.
*/
tcDspUpp::~tcDspUpp()
{

}

(1-1/3)