Initial contribution supporting NaviEngine 1
This package_definition.xml will build support for three memory models
- Single (sne1_tb)
- Multiple (ne1_tb)
- Flexible (fne1_tb)
/*
* 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 <drivers/comm.h>
#include <assp.h>
#include "../naviengine_priv.h"
#include "../naviengine.h"
#include "uart16550_ne.h"
#include <e32hal.h>
_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<<KNum16550Uarts);
iVersion=TVersion(KCommsMajorVersionNumber,KCommsMinorVersionNumber,KCommsBuildVersionNumber);
}
TInt DDriverComm::Install()
//
// Install the driver
//
{
// Allocate a kernel thread to run the DFC
TInt r = Kern::DynamicDfcQCreate(iDfcQ, KUart16550ThreadPriority, KUar16550tDriverThread);
if (r == KErrNone)
{
#ifdef CPU_AFFINITY_ANY
NKern::ThreadSetCpuAffinity((NThread*)(iDfcQ->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;
}