diff -r 000000000000 -r 5de814552237 navienginebsp/naviengine_assp/uart/uart16550_ne.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/navienginebsp/naviengine_assp/uart/uart16550_ne.cpp Tue Sep 28 18:00:05 2010 +0100 @@ -0,0 +1,776 @@ +/* +* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* naviengine_assp\uart\uart16550.cpp +* PDD for 16550 UART +* +*/ + + + +#include +#include +#include "../naviengine_priv.h" +#include "../naviengine.h" +#include "uart16550_ne.h" +#include + +_LIT(KPddName,"Comm.16550_ne"); + +#define __COMMS_MACHINE_CODED__ +#ifdef __COMMS_MACHINE_CODED__ +#define DBASE_VPTR_OFFSET 4 +#define RX_ISR_VT_OFFSET 0x24 +#define CHK_TXB_VT_OFFSET 0x28 +#define STATE_ISR_VT_OFFSET 0x2C +#endif + +// needs ldd version.. +const TInt KMinimumLddMajorVersion=1; +const TInt KMinimumLddMinorVersion=1; +const TInt KMinimumLddBuild=122; + +// The following defines when enabled can help debugging uart: + +// RX_WORKAROUND - The basic workaround. There is a small window where data loss could occur +// #define RX_WORKAROUND + +// RX_WORKAROUND_SAVE - A more advanced workaround that notifies the LDD that there +// has been a read error. May cause new reporting of errors +// #define RX_WORKAROUND_SAVE + +// TRACK_WORKAROUND - Records the number activations of the workaround +// and the state of the last one. printf's the values on on deconstruct +// #define TRACK_WORKAROUND + +// TRACK_DISTRIBUTION - Records the number of bytes read at a time to +// demonstrate operation of fifo mode. printf's the values on on deconstruct +// #define TRACK_DISTRIBUTION + +// DISABLE_RX_FLUSH - Disables RX Flush in uart configuration procedure. +// #define DISABLE_RX_FLUSH + +// TEMPORARY_SMP_FIX reduces the number of bytes pumped into the Tx FIFO from 16 to 8 +// which works around a hardware problem not fully understood. +#define TEMPORARY_SMP_FIX + +// configuration data +static const TUint16 BaudRateDivisor[] = + { + KBaudRateDiv_50, + KBaudRateDiv_75, + KBaudRateDiv_110, + KBaudRateDiv_134, + KBaudRateDiv_150, + KBaudRateDiv_300, + KBaudRateDiv_600, + KBaudRateDiv_1200, + KBaudRateDiv_1800, + KBaudRateDiv_2000, + KBaudRateDiv_2400, + KBaudRateDiv_3600, + KBaudRateDiv_4800, + KBaudRateDiv_7200, + KBaudRateDiv_9600, + KBaudRateDiv_19200, + KBaudRateDiv_38400, + KBaudRateDiv_57600, + KBaudRateDiv_115200, + KBaudRateDiv_230400, + }; + +class DDriverComm : public DPhysicalDevice + { +public: + DDriverComm(); + ~DDriverComm(); + virtual TInt Install(); + virtual void GetCaps(TDes8 &aDes) const; + virtual TInt Create(DBase*& aChannel, TInt aUnit, const TDesC8* anInfo, const TVersion &aVer); + virtual TInt Validate(TInt aUnit, const TDesC8* anInfo, const TVersion &aVer); +public: + TDynamicDfcQue* iDfcQ; + }; + +class DComm16550 : public DComm + { +public: + DComm16550(); + ~DComm16550(); + TInt DoCreate(TInt aUnit, const TDesC8* anInfo, TDynamicDfcQue* aDfcQ); +public: + virtual TInt Start(); + virtual void Stop(TStopMode aMode); + virtual void Break(TBool aState); + virtual void EnableTransmit(); + virtual TUint Signals() const; + virtual void SetSignals(TUint aSetMask,TUint aClearMask); + virtual TInt ValidateConfig(const TCommConfigV01 &aConfig) const; + virtual void Configure(TCommConfigV01 &aConfig); + virtual void Caps(TDes8 &aCaps) const; + virtual TInt DisableIrqs(); + virtual void RestoreIrqs(TInt aIrq); + virtual TDfcQue* DfcQ(TInt aUnit); + virtual void CheckConfig(TCommConfigV01& aConfig); +public: + static void Isr(TAny* aPtr); +public: + TInt iInterruptId; + TInt iUnit; + T16550Uart* iUart; + TDynamicDfcQue* iDfcQ; + +#ifdef TRACK_WORKAROUND + TInt iWorkaround; // how many times did the workaround fire + TInt iWorkaroundLSR; + TInt iWorkaroundISR; +#endif + +#ifdef TRACK_DISTRIBUTION + TUint iRead[33]; // array of how many bytes reported back to the ldd +#endif + }; + + +DDriverComm::DDriverComm() +// +// Constructor +// + { + iUnitsMask=~(0xffffffffu<iThread), KCpuAffinityAny); +#endif + r = SetName(&KPddName); + } + return r; + } + +/** + Destructor +*/ +DDriverComm::~DDriverComm() + { + if (iDfcQ) + iDfcQ->Destroy(); + } + +void Get16550CommsCaps(TDes8& aCaps, TInt aUnit) + { + TCommCaps3 capsBuf; + TCommCapsV03 &c=capsBuf(); + c.iRate = + KCapsBps150 + |KCapsBps300 + |KCapsBps600 + |KCapsBps1200 + |KCapsBps1800 + |KCapsBps2000 + |KCapsBps2400 + |KCapsBps3600 + |KCapsBps4800 + |KCapsBps7200 + |KCapsBps9600 + |KCapsBps19200 + |KCapsBps38400 + |KCapsBps57600 + |KCapsBps115200; + if (aUnit != 2) + { + c.iRate |= KCapsBps230400; + } + + c.iDataBits = + KCapsData5 + |KCapsData6 + |KCapsData7 + |KCapsData8; + c.iStopBits = + KCapsStop1 + |KCapsStop2; + c.iParity = + KCapsParityNone + |KCapsParityEven + |KCapsParityOdd + |KCapsParityMark + |KCapsParitySpace; + c.iSIR = 0; + switch (aUnit) + { + case 0: + c.iHandshake = + KCapsObeyXoffSupported + |KCapsSendXoffSupported + |KCapsObeyCTSSupported + |KCapsFailCTSSupported + |KCapsFreeRTSSupported; + c.iSignals = + KCapsSignalCTSSupported + |KCapsSignalRTSSupported; + break; + case 1: + c.iHandshake = + KCapsObeyXoffSupported + |KCapsSendXoffSupported + |KCapsObeyCTSSupported + |KCapsFailCTSSupported + |KCapsObeyDSRSupported + |KCapsFailDSRSupported + |KCapsObeyDCDSupported + |KCapsFailDCDSupported + |KCapsFreeRTSSupported + |KCapsFreeDTRSupported; + c.iSignals = + KCapsSignalCTSSupported + |KCapsSignalRTSSupported + |KCapsSignalDTRSupported + |KCapsSignalDSRSupported + |KCapsSignalDCDSupported; + break; + default: + case 2: + c.iHandshake = + KCapsObeyXoffSupported + |KCapsSendXoffSupported; + c.iSignals = 0; + break; + } + c.iFifo=KCapsHasFifo; + c.iNotificationCaps= + KNotifyDataAvailableSupported + |KNotifySignalsChangeSupported; + c.iRoleCaps = 0; + c.iFlowControlCaps = 0; + c.iBreakSupported = ETrue; + aCaps.FillZ(aCaps.MaxLength()); + aCaps=capsBuf.Left(Min(capsBuf.Length(),aCaps.MaxLength())); + } + +void DDriverComm::GetCaps(TDes8 &aDes) const +// +// Return the drivers capabilities +// + { + Get16550CommsCaps(aDes, 0); + } + +TInt DDriverComm::Create(DBase*& aChannel, TInt aUnit, const TDesC8* anInfo, const TVersion& aVer) +// +// Create a driver +// + { + DComm16550* pD=new DComm16550; + aChannel=pD; + TInt r=KErrNoMemory; + if (pD) + r=pD->DoCreate(aUnit,anInfo, iDfcQ); + return r; + } + +TInt DDriverComm::Validate(TInt aUnit, const TDesC8* /*anInfo*/, const TVersion& aVer) +// +// Validate the requested configuration +// + { + if ((!Kern::QueryVersionSupported(iVersion,aVer)) || (!Kern::QueryVersionSupported(aVer,TVersion(KMinimumLddMajorVersion,KMinimumLddMinorVersion,KMinimumLddBuild)))) + return KErrNotSupported; + if (TUint(aUnit)>=TUint(KNum16550Uarts)) + return KErrNotSupported; + return KErrNone; + } + +DComm16550::DComm16550() +// +// Constructor +// + { +// iTransmitting=EFalse; + iInterruptId=-1; // -1 means not bound + } + +DComm16550::~DComm16550() +// +// Destructor +// + { + if (iUart) + delete iUart; + if (iInterruptId>=0) + Interrupt::Unbind(iInterruptId); + +#ifdef TRACK_WORKAROUND + if (iWorkaround) + { + Kern::Printf("########################################"); + Kern::Printf("####%d iWorkaround %d LSR 0x%x ISR 0x%x", iUnit, iWorkaround, iWorkaroundLSR, iWorkaroundISR); + Kern::Printf("########################################"); + } + else + { + Kern::Printf("####%d iWorkaround %d LSR 0x%x ISR 0x%x", iUnit, iWorkaround, iWorkaroundLSR, iWorkaroundISR); + } +#endif +#ifdef TRACK_DISTRIBUTION + Kern::Printf("####%d iRead: %d, %d %d %d %d %d %d %d %d, %d %d %d %d %d %d %d %d, %d %d %d %d %d %d %d %d, %d %d %d %d %d %d %d", + iUnit, + iRead[0], + iRead[1], iRead[2], iRead[3], iRead[4], iRead[5], iRead[6], iRead[7], iRead[8], + iRead[9], iRead[10], iRead[11], iRead[12], iRead[13], iRead[14], iRead[15], iRead[16], + iRead[17], iRead[18], iRead[19], iRead[20], iRead[21], iRead[22], iRead[23], iRead[24], + iRead[25], iRead[26], iRead[27], iRead[28], iRead[29], iRead[30], iRead[31], iRead[32]); +#endif + } + +TInt DComm16550::DoCreate(TInt aUnit, const TDesC8* /*anInfo*/, TDynamicDfcQue* aDfcQ) +// +// Sets up the PDD +// + { + TUint base; + + switch (aUnit) + { + case 0: + base=KHwBaseUart0; + break; + case 1: + base=KHwBaseUart1; + break; + case 2: + base=KHwBaseUart2; + break; + default: + return KErrNotSupported; + } + + iUnit=aUnit; + iDfcQ = aDfcQ; + TInt irq=KIntIdUart0+aUnit; + + // bind to UART interrupt + TInt r=Interrupt::Bind(irq,Isr,this); + if (r>=0) + { + iInterruptId = r; + r = KErrNone; + + iUart = new T16550Uart; + if (iUart) + { + iUart->iBase = (TUint8*)base; + iUart->SetIER(0); + iUart->SetLCR(0); + iUart->SetFCR(0); + iUart->SetMCR(0); + } + else + { + Interrupt::Unbind(iInterruptId); + r = KErrNoMemory; + } + } + +#ifdef TRACK_WORKAROUND + iWorkaround = iWorkaroundLSR = iWorkaroundISR = 0; +#endif + return r; + } + +TDfcQue* DComm16550::DfcQ(TInt /*aUnit*/) +// +// Return the DFC queue to be used for this device +// For PC cards, use the PC card controller thread for the socket in question. +// + { + return iDfcQ; + } + +TInt DComm16550::Start() +// +// Start receiving characters +// + { + // if EnableTransmit() called before Start() + iTransmitting=EFalse; + iUart->SetIER(K16550IER_RDAI|K16550IER_RLSI|K16550IER_MSI); + iLdd->UpdateSignals(Signals()); + Interrupt::Enable(iInterruptId); + return KErrNone; + } + +TBool FinishedTransmitting(TAny* aPtr) + { + DComm16550& d=*(DComm16550*)aPtr; + return d.iUart->TestLSR(K16550LSR_TxIdle); + } + +void DComm16550::Stop(TStopMode aMode) +// +// Stop receiving characters +// + { + switch (aMode) + { + case EStopNormal: + case EStopPwrDown: + iUart->SetIER(0); + Interrupt::Disable(iInterruptId); + iTransmitting=EFalse; + + // wait for uart to stop tranmitting + Kern::PollingWait(FinishedTransmitting,this,3,100); + break; + case EStopEmergency: + iUart->SetIER(0); + Interrupt::Disable(iInterruptId); + iTransmitting=EFalse; + break; + } + } + +void DComm16550::Break(TBool aState) +// +// Start or stop the uart breaking +// + { + if (aState) + iUart->ModifyLCR(0,K16550LCR_TxBreak); + else + iUart->ModifyLCR(K16550LCR_TxBreak,0); + } + +void DComm16550::EnableTransmit() +// +// Start sending characters. +// + { + TBool tx = (TBool)__e32_atomic_swp_ord32(&iTransmitting, 1); + if (tx) + return; + iUart->ModifyIER(0,K16550IER_THREI); + } + +TUint DComm16550::Signals() const +// +// Read and translate the modem lines +// + { + TUint msr=iUart->MSR(); + msr=((msr>>4)&0x0f); // true input signals + TUint sig=msr & 3; // CTS,DSR OK + if (msr & 4) + sig|=KSignalRNG; // swap DCD,RNG + if (msr & 8) + sig|=KSignalDCD; + + return sig; + } + +void DComm16550::SetSignals(TUint aSetMask, TUint aClearMask) +// +// Set signals. +// + { + TUint set=0; + TUint clear=0; + if (aSetMask & KSignalRTS) + set|=K16550MCR_RTS; + if (aSetMask & KSignalDTR) + set|=K16550MCR_DTR; + if (aClearMask & KSignalRTS) + clear|=K16550MCR_RTS; + if (aClearMask & KSignalDTR) + clear|=K16550MCR_DTR; + iUart->ModifyMCR(clear,set); + } + +TInt DComm16550::ValidateConfig(const TCommConfigV01 &aConfig) const +// +// Check a config structure. +// + { + if (aConfig.iSIREnable==ESIREnable) + return KErrNotSupported; + switch (aConfig.iParity) + { + case EParityNone: + case EParityOdd: + case EParityEven: + case EParityMark: + case EParitySpace: + break; + default: + return KErrNotSupported; + } +// if (TUint(aConfig.iRate)>TUint(EBps115200)) +// return KErrNotSupported; + return KErrNone; + } + +void DComm16550::CheckConfig(TCommConfigV01& aConfig) + { + // do nothing + } + +TInt DComm16550::DisableIrqs() +// +// Disable normal interrupts +// + { + + return NKern::DisableInterrupts(1); + } + +void DComm16550::RestoreIrqs(TInt aLevel) +// +// Restore normal interrupts +// + { + + NKern::RestoreInterrupts(aLevel); + } + +void DComm16550::Configure(TCommConfigV01 &aConfig) +// +// Set up the Uart +// + { + // wait for uart to stop tranmitting + Kern::PollingWait(FinishedTransmitting,this,3,100); + + TUint lcr=0; + switch (aConfig.iDataBits) + { + case EData5: + lcr=K16550LCR_Data5; + break; + case EData6: + lcr=K16550LCR_Data6; + break; + case EData7: + lcr=K16550LCR_Data7; + break; + case EData8: + lcr=K16550LCR_Data8; + break; + } + switch (aConfig.iStopBits) + { + case EStop1: + break; + case EStop2: + lcr|=K16550LCR_Stop2; + break; + } + switch (aConfig.iParity) + { + case EParityNone: + break; + case EParityEven: + lcr|=K16550LCR_ParityEnable|K16550LCR_ParityEven; + break; + case EParityOdd: + lcr|=K16550LCR_ParityEnable; + break; + case EParityMark: + lcr|=K16550LCR_ParityEnable|K16550LCR_ParityMark; + break; + case EParitySpace: + lcr|=K16550LCR_ParityEnable|K16550LCR_ParitySpace; + break; + } + + /* + S18599EJ1V5UM.pdf page 746 uart configuration + Caution: Must not change the baud rate while any data transfers are executing. + If the baud rate is necessary to changed, please apply following sequence. + ... (inlined with code) ... + */ + + // 1) Put UART in Local loop-back mode (write 1 to MCR4 bit). + iUart->ModifyMCR(0,K16550MCR_LocalLoop); + + // 2) Clear transmit FIFO (write 1 to FCR2 bit). + iUart->SetFCR(K16550FCR_TxReset); + + // 3) Wait 1 character period (ex. 100µs at 115kbps) + NKern::Sleep(1); // sleep 1ms (for, 9600 bump up to 2ms?) + + // 4) Clear receive buffer (read RBR register until LSR0 bit becomes 0). + TInt maxdrain = 1000; // arbitrary count (fifo depth is 16 bytes) + while (iUart->TestLSR(K16550LSR_RxReady) && --maxdrain) + iUart->RxData(); // read character from RBR + + // 5) Clear receive FIFO (write 1 to FCR1 bit). + iUart->SetFCR(K16550FCR_RxReset); + + // 6) Change the baud rate (write 1 to LCR7, write DLL and DLH, and then write 0 to LCR7). + iUart->SetLCR(lcr|K16550LCR_DLAB); + iUart->SetBaudRateDivisor(BaudRateDivisor[(TInt)aConfig.iRate]); + iUart->SetLCR(lcr); + + // 7) Go back to normal mode (write 0 to MCR4 bit). + iUart->ModifyMCR(K16550MCR_LocalLoop, 0); + + // Enable fifo mode + //iUart->SetFCR(K16550FCR_Enable | K16550FCR_TxRxRdy | K16550FCR_RxTrig8); + iUart->SetFCR(K16550FCR_Enable | K16550FCR_RxTrig8); + +#ifndef DISABLE_RX_FLUSH + // Read characters from RBR; this isn't part of the recommended baud rate change code + // but this has fixed the problem with ISR running indefinitely. + TInt j; + for (j=0;j<16;j++) // Fifo buffer is 16 bytes deep + iUart->RxData(); +#endif + +#ifdef TRACK_DISTRIBUTION + // blank an array of 33 integers 0 to 32 + TInt i=0; + for (i=0;i<33;i++) + { + iRead[i]=0; + } +#endif + } + +void DComm16550::Caps(TDes8 &aCaps) const +// +// return our caps +// + { + Get16550CommsCaps(aCaps,iUnit); + } + +void DComm16550::Isr(TAny* aPtr) +// +// Service the UART interrupt +// + { + DComm16550& d=*(DComm16550*)aPtr; + T16550Uart& u=*d.iUart; + TUint rx[32]; + TUint xon=d.iLdd->iRxXonChar; + TUint xoff=d.iLdd->iRxXoffChar; + + TUint isr=u.ISR(); + if (isr & K16550ISR_NotPending) + return; + isr&=K16550ISR_IntIdMask; + + // if receive data available or line status interrupt + if (isr==K16550ISR_RDAI || isr==K16550ISR_RLSI) + { + TInt rxi=0; + TInt x=0; + +#if defined(RX_WORKAROUND) || defined(RX_WORKAROUND_SAVE) + // It has been observed that the hardware can assert RDAI without asserting RxReady + // This is nasty but it reads from the head of the fifo, ORs in a faked error + // and saves it in the buffer, later on in the loop it will be passed to the ldd + // as data with an error + if (!(u.LSR() & K16550LSR_RxReady)) + { +#ifdef TRACK_WORKAROUND + ++d.iWorkaround; + d.iWorkaroundLSR=u.LSR(); + d.iWorkaroundISR=u.ISR(); +#endif + +#ifdef RX_WORKAROUND_SAVE + // save the character and flag error, this is a bit risky as it could report errors + rx[rxi++]=(u.RxData() | KReceiveIsrFrameError); +#else // or just discard it (normal workaround) + u.RxData(); +#endif + } +#endif + while (u.TestLSR(K16550LSR_RxReady|K16550LSR_RxParityErr|K16550LSR_RxOverrun|K16550LSR_RxFrameErr|K16550LSR_RxBreak) && Kern::PowerGood()) + { + TUint lsr=0; + // checks for EIF flag + if (isr==K16550ISR_RLSI) + lsr=u.LSR()&(K16550LSR_RxParityErr|K16550LSR_RxOverrun|K16550LSR_RxFrameErr); + TUint ch=u.RxData(); + // if error in this character + if (lsr) + { + if (lsr & K16550LSR_RxParityErr) + ch|=KReceiveIsrParityError; + if (lsr & K16550LSR_RxBreak) + ch|=KReceiveIsrBreakError; + if (lsr & K16550LSR_RxFrameErr) + ch|=KReceiveIsrFrameError; + if (lsr & K16550LSR_RxOverrun) + ch|=KReceiveIsrOverrunError; + } + if (ch==xon) + x=1; + else if (ch==xoff) + x=-1; + else + rx[rxi++]=ch; + } +#ifdef TRACK_DISTRIBUTION + d.iRead[rxi]++; // record how many bytes we're reporting +#endif + d.ReceiveIsr(rx,rxi,x); + return; + } + // if TFS flag and TIE + if (isr==K16550ISR_THREI) + { + TInt n; +#ifdef TEMPORARY_SMP_FIX + for (n=0; n<8; ++n) +#else + for (n=0; n<16; ++n) +#endif + { + TInt r=d.TransmitIsr(); + if (r<0) + { + //no more to send + // Disable the TX interrupt + u.ModifyIER(K16550IER_THREI,0); + d.iTransmitting=EFalse; + break; + } + u.SetTxData(r); + } + d.CheckTxBuffer(); + return; + } + // must be signal change + d.StateIsr(d.Signals()); + } + +DECLARE_STANDARD_PDD() + { + return new DDriverComm; + } +