--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/navienginebsp/ne1_tb/ethernet/smcs9118_ethernet.cpp Tue Sep 28 18:00:05 2010 +0100
@@ -0,0 +1,1141 @@
+/*
+* 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:
+* ne1_tb\ethernet\shared_ethernet.h
+* SMCS 9118 Ethernet driver implementation.
+*
+*/
+
+
+
+#include "variant.h"
+#include "smcs9118_ethernet.h"
+
+const TUint32 ONE_MSEC = 1000; // in nanoseconds
+const TUint32 TWO_SECONDS = 2000; // in milliseconds
+const TUint32 SMCS9118_MAC_TIMEOUT = 50;
+const TUint32 SMCS9118_MAX_RETRIES = 100;
+
+
+DEthernetPddFactory::DEthernetPddFactory()
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetPddFactory::DEthernetPddFactory()");
+ #endif
+ iVersion=TVersion(KEthernetMajorVersionNumber,
+ KEthernetMinorVersionNumber,
+ KEthernetBuildVersionNumber);
+ }
+
+TInt DEthernetPddFactory::Install()
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetPddFactory::Install()");
+ #endif
+ return SetName(&KEthernetPddName);
+ }
+
+void DEthernetPddFactory::GetCaps(TDes8& /*aDes*/) const
+/*
+ * Return the drivers capabilities.
+ */
+ {
+ }
+
+
+TInt DEthernetPddFactory::Create(DBase*& aChannel, TInt aUnit, const TDesC8* aInfo, const TVersion& aVer)
+/*
+ * Create a Driver for the device.
+ */
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetPddFactory::Create()");
+ #endif
+ TInt r = Validate(aUnit, aInfo, aVer);
+ if (r != KErrNone)
+ {
+ return r;
+ }
+ DEthernetPdd* pP = new DEthernetSMCS9118Pdd;
+ aChannel = pP;
+ if (!pP)
+ {
+ return KErrNoMemory;
+ }
+ r = pP->DoCreate();
+ return r;
+ }
+
+TInt DEthernetPddFactory::Validate(TInt /*aUnit*/, const TDesC8* /*aInfo*/, const TVersion& aVer)
+/*
+ * Validate the requested configuration
+ */
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetPddFactory::Validate()");
+ #endif
+
+ if (!Kern::QueryVersionSupported(iVersion,aVer))
+ {
+ return KErrNotSupported;
+ }
+
+ return KErrNone;
+ }
+
+#ifdef __SMP__
+TSpinLock DEthernetSMCS9118PddLock(SMCS9118_LOCK_ORDER);
+#endif
+DEthernetSMCS9118Pdd::DEthernetSMCS9118Pdd()
+//Constructor
+ :iRxDfc(ServiceRxDfc, this, 1)
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetSMCS9118Pdd::DEthernetSMCS9118Pdd()");
+ #endif
+
+#ifdef __SMP__
+ iDriverLock = &DEthernetSMCS9118PddLock;
+#endif
+ iReady = EFalse;
+ }
+
+DEthernetSMCS9118Pdd::~DEthernetSMCS9118Pdd()
+//Destructor
+ {
+
+ //cancel any pending DFC requests
+ iRxDfc.Cancel();
+
+ // UnRegister the power handler with Symbian Power Framework
+ iPowerHandler.RelinquishPower();
+ iPowerHandler.Remove();
+
+ // UnRegister interrupts
+ DisableInterrupt(iInterruptId);
+ UnbindInterrupt(iInterruptId);
+
+ if (iDfcQ)
+ iDfcQ->Destroy();
+ }
+
+void DEthernetSMCS9118Pdd::Stop(TStopMode aMode)
+/**
+ * Stop receiving frames
+ * @param aMode The stop mode
+ */
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetSMCS9118Pdd::Stop()");
+ #endif
+
+ switch (aMode)
+ {
+ case EStopNormal:
+ case EStopEmergency:
+ iReady = EFalse;
+ iRxDfc.Cancel();
+ // disable Rx, Tx
+ TUint32 status;
+ TInt32 err = ReadMac(SMCS9118_MAC_CR, status);
+ status &= ~(SMCS9118_MAC_RXEN | SMCS9118_MAC_TXEN | SMCS9118_MAC_RXALL);
+ err = WriteMac(SMCS9118_MAC_CR, status);
+ // clear any pending interrupts
+ Write32(SMCS9118_INT_EN, 0);
+ ByteTestDelay(1);
+ ClearInterrupt(iInterruptId);
+ // turn off the LED
+ status = Read32(SMCS9118_GPIO_CFG) & ~SMCS9118_GPIO_LED_EN;
+ Write32(SMCS9118_GPIO_CFG, status);
+ ByteTestDelay(1);
+
+ break;
+ }
+ }
+
+TInt DEthernetSMCS9118Pdd::Configure(TEthernetConfigV01& /*aConfig*/)
+/**
+ * Configure the device
+ * Reconfigure the device using the new configuration supplied.
+ * This should not change the MAC address.
+ * @param aConfig The new configuration
+ * @see ValidateConfig()
+ * @see MacConfigure()
+ * assume iDriverLock not held
+ */
+ {
+ TUint32 retry;
+ TUint32 status;
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetSMCS9118Pdd::Configure()");
+ #endif
+
+ if ((status = CardSoftReset()) != (TUint32)KErrNone)
+ {
+ return status;
+ }
+
+ TInt irq = DriverLock();
+ // disable chip interrupts
+ Write32(SMCS9118_INT_EN, 0);
+ ByteTestDelay(1);
+ Write32(SMCS9118_INT_STS, 0xffffffff);
+ ByteTestDelay(2);
+ Write32(SMCS9118_FIFO_INT, 0);
+ ByteTestDelay(1);
+ Write32(SMCS9118_IRQ_CFG, SMCS9118_IRQ_CFG_DEAS|SMCS9118_IRQ_CFG_TYPE);
+ ByteTestDelay(3);
+
+ // AutoFlowControl setup
+ Write32(SMCS9118_AFC_CFG, SMCS9118_AFC_CFG_VAL);
+ ByteTestDelay(1);
+
+ // TX FIFO setup
+ status = Read32(SMCS9118_HW_CFG);
+ status |= SMCS9118_TX_FIFO_SZ;
+ Write32(SMCS9118_HW_CFG, status);
+ ByteTestDelay(1);
+
+ // wait for EEPROM load
+ retry = SMCS9118_MAX_RETRIES;
+ do
+ {
+ status = Read32(SMCS9118_E2P_CMD);
+ if (!(status & SMCS9118_E2P_CMD_BUSY))
+ {
+ break;
+ }
+ Kern::NanoWait(ONE_MSEC);
+ } while (--retry);
+ if (retry == 0)
+ {
+ DriverUnlock(irq);
+ return KErrGeneral;
+ }
+
+ // GPIO setup - turn on the LED !
+ Write32(SMCS9118_GPIO_CFG, SMCS9118_GPIO_GPIOBUF|SMCS9118_GPIO_LED_EN);
+ ByteTestDelay(1);
+
+ // PHY reset
+ status = Read32(SMCS9118_PMT_CTRL);
+ status |= SMCS9118_PMT_PHY_RST;
+ Write32(SMCS9118_PMT_CTRL, status);
+ ByteTestDelay(7);
+ retry = SMCS9118_MAX_RETRIES;
+ do
+ {
+ status = Read32(SMCS9118_PMT_CTRL);
+ if (!(status & SMCS9118_PMT_PHY_RST))
+ {
+ break;
+ }
+ Kern::NanoWait(ONE_MSEC);
+ } while (--retry);
+ if (retry == 0)
+ {
+ DriverUnlock(irq);
+ return KErrGeneral;
+ }
+ ByteTestDelay(1);
+
+ // Auto negotiate setup
+ TInt32 err = ReadPhy(SMCS9118_PHY_AUTONEG_AD, status);
+ if (err != KErrNone)
+ {
+ DriverUnlock(irq);
+ return err;
+ }
+ status |= SMCS9118_PHY_DEF_ANEG;
+ err = WritePhy(SMCS9118_PHY_AUTONEG_AD, status);
+ if (err != KErrNone)
+ {
+ DriverUnlock(irq);
+ return err;
+ }
+
+ err = ReadPhy(SMCS9118_PHY_AUTONEG_AD, status);
+ if (err != KErrNone)
+ {
+ DriverUnlock(irq);
+ return err;
+ }
+
+ err = ReadPhy(SMCS9118_PHY_BCR, status);
+ if (err != KErrNone)
+ {
+ DriverUnlock(irq);
+ return err;
+ }
+ status |= (SMCS9118_PHY_ANEG_RESTART|SMCS9118_PHY_ANEG_EN);
+ err = WritePhy(SMCS9118_PHY_BCR, status);
+ if (err != KErrNone)
+ {
+ DriverUnlock(irq);
+ return err;
+ }
+
+ // wait for auto negotiation
+ DriverUnlock(irq);
+ NKern::Sleep(TWO_SECONDS);
+ irq = DriverLock();
+
+ err = ReadPhy(SMCS9118_PHY_BSR, status);
+ if (err != KErrNone)
+ {
+ DriverUnlock(irq);
+ return err;
+ }
+ if (!(status & SMCS9118_PHY_ANEG_CMPL))
+ {
+ DriverUnlock(irq);
+ return KErrGeneral;
+ }
+
+ // update the config based on what we negotiated
+ if (status & (SMCS9118_PHY_100BTX|SMCS9118_PHY_100BTXFD))
+ {
+ iDefaultConfig.iEthSpeed = KEthSpeed100BaseTX;
+ }
+ if (status & (SMCS9118_PHY_10BTFD|SMCS9118_PHY_100BTXFD))
+ {
+ iDefaultConfig.iEthDuplex = KEthDuplexFull;
+ }
+
+ // setup store + forward
+ status = Read32(SMCS9118_HW_CFG);
+ status |= SMCS9118_HW_CFG_SF;
+ Write32(SMCS9118_HW_CFG, status);
+ ByteTestDelay(1);
+
+ // setup Tx and Rx
+ Write32(SMCS9118_TX_CFG, SMCS9118_TX_CFG_TXSAO|SMCS9118_TX_CFG_TX_ON);
+ ByteTestDelay(1);
+
+ TInt r;
+ r = WriteMac(SMCS9118_MAC_CR, SMCS9118_MAC_RXEN|SMCS9118_MAC_TXEN|SMCS9118_MAC_RXALL);
+ if (r != KErrNone)
+ {
+ DriverUnlock(irq);
+ return r;
+ }
+
+ // Enable interrupts to CPU
+ r = EnableInterrupt(iInterruptId);
+ if(r != KErrNone)
+ {
+ TInt err;
+ __KTRACE_OPT(KHARDWARE,Kern::Printf("DEthernetSMSC9118Pdd::Start --- Interrupt::Enable()=%d", r));
+
+ // Disable TX, RX and exit
+ status &= ~(SMCS9118_MAC_RXEN | SMCS9118_MAC_TXEN | SMCS9118_MAC_RXALL);
+ err = WriteMac(SMCS9118_MAC_CR, status);
+ if (err != KErrNone)
+ {
+ DriverUnlock(irq);
+ return err;
+ }
+
+ DriverUnlock(irq);
+ return r;
+ }
+
+ // Rx Interrupt
+ Write32(SMCS9118_INT_EN, SMCS9118_INT_EN_RSFL);
+ ByteTestDelay(1);
+
+ DriverUnlock(irq);
+ return KErrNone;
+ }
+
+
+void DEthernetSMCS9118Pdd::MacConfigure(TEthernetConfigV01& aConfig)
+/**
+ * Change the MAC address
+ * Attempt to change the MAC address of the device
+ * @param aConfig A Configuration containing the new MAC
+ * @see Configure()
+ * assume iDriverLock not held
+ */
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetSMCS9118Pdd::MacConfigure()");
+ #endif
+
+
+ TUint32 mac, checkMac;
+ TInt err;
+
+ TInt irq = DriverLock();
+ mac = aConfig.iEthAddress[0];
+ mac |= aConfig.iEthAddress[1]<<8;
+ mac |= aConfig.iEthAddress[2]<<16;
+ mac |= aConfig.iEthAddress[3]<<24;
+
+ err = WriteMac(SMCS9118_MAC_ADDRL, mac);
+ if (err)
+ {
+ __KTRACE_OPT(KHARDWARE, Kern::Printf("DEthernetSMCS9118Pdd::MacConfigure() -- Failed to set MAC Address"));
+ DriverUnlock(irq);
+ return;
+ }
+ err = ReadMac(SMCS9118_MAC_ADDRL, checkMac);
+ if (err || checkMac != mac)
+ {
+ __KTRACE_OPT(KHARDWARE, Kern::Printf("DEthernetSMCS9118Pdd::MacConfigure() -- Failed to set MAC Address"));
+ DriverUnlock(irq);
+ return;
+ }
+
+ mac = aConfig.iEthAddress[4];
+ mac |= aConfig.iEthAddress[5]<<8;
+ err = WriteMac(SMCS9118_MAC_ADDRH, mac);
+ if (err)
+ {
+ __KTRACE_OPT(KHARDWARE, Kern::Printf("DEthernetSMCS9118Pdd::MacConfigure() -- Failed to set MAC Address"));
+ DriverUnlock(irq);
+ return;
+ }
+ err = ReadMac(SMCS9118_MAC_ADDRH, checkMac);
+ if (err || checkMac != mac)
+ {
+ __KTRACE_OPT(KHARDWARE, Kern::Printf("DEthernetSMCS9118Pdd::MacConfigure() -- Failed to set MAC Address"));
+ DriverUnlock(irq);
+ return;
+ }
+
+ for (TInt i=0; i<=5; i++)
+ {
+ iDefaultConfig.iEthAddress[i] = aConfig.iEthAddress[i];
+ }
+
+ __KTRACE_OPT(KHARDWARE, Kern::Printf("-- MAC address %2x.%2x.%2x.%2x.%2x.%2x",
+ iDefaultConfig.iEthAddress[0], iDefaultConfig.iEthAddress[1],
+ iDefaultConfig.iEthAddress[2], iDefaultConfig.iEthAddress[3],
+ iDefaultConfig.iEthAddress[4], iDefaultConfig.iEthAddress[5]));
+
+ DriverUnlock(irq);
+ return;
+ }
+
+
+TInt DEthernetSMCS9118Pdd::Send(TBuf8<KMaxEthernetPacket+32>& aBuffer)
+/**
+ * Transmit data
+ * @param aBuffer reference to the data to be sent
+ * @return KErrNone if the data has been sent
+ * assume iDriverLock not held
+ */
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetSMCS9118Pdd::Send()");
+ #endif
+
+ // Always request for power
+ iPowerHandler.RequestPower();
+ TUint32* dataP = (TUint32 *)aBuffer.Ptr();
+ TUint32 length = aBuffer.Length();
+
+ TInt irq = DriverLock();
+ // can it fit
+ TUint32 status = Read32(SMCS9118_TX_FIFO_INF);
+ if ((status & SMCS9118_TX_SPACE_MASK) < length)
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("eth -- Error... Send KErrSMCS9118TxOutOfMenory");
+ #endif
+ DriverUnlock(irq);
+ return KErrTxOutOfMemory;
+ }
+
+ status = (length & 0x7ff) | SMCS9118_TX_FIRSTSEG | SMCS9118_TX_LASTSEG;
+ Write32(SMCS9118_TX_DATA_FIFO, status);
+ status = (length & 0x7ff) | SMCS9118_TX_PKT_TAG;
+ Write32(SMCS9118_TX_DATA_FIFO, status);
+
+ // calculate number of full words + remaining bytes
+
+ TUint32 words = length >> 2;
+ TUint32 bytes = length & 3;
+
+ // write words
+ while (words--)
+ {
+ Write32(SMCS9118_TX_DATA_FIFO, *dataP++);
+ }
+
+ // write bytes
+ if (bytes)
+ {
+ TUint8 *dataBytes = (TUint8*) dataP;
+ status = 0;
+ switch (bytes)
+ {
+ case 3:
+ status |= dataBytes[2] << 16;
+ // fallthrough
+ case 2:
+ status |= dataBytes[1] << 8;
+ // fallthrough
+ case 1:
+ status |= dataBytes[0];
+ }
+ Write32(SMCS9118_TX_DATA_FIFO, status);
+ }
+
+ // Clear interrupt
+ TUint32 retries = SMCS9118_MAX_RETRIES;
+ while(retries-- && !((status = Read32(SMCS9118_INT_STS)) & SMCS9118_INT_STS_TX))
+ {
+ ByteTestDelay(1); // delay
+ }
+ if (retries == 0 || (status & SMCS9118_INT_STS_TXE))
+ {
+ DriverUnlock(irq);
+ return KErrGeneral;
+ }
+ Write32(SMCS9118_INT_STS, SMCS9118_INT_STS_TX);
+ ByteTestDelay(2);
+
+ DriverUnlock(irq);
+ return KErrNone;
+ }
+
+TInt DEthernetSMCS9118Pdd::DiscardFrame()
+/**
+ * Discard the frame by fast forwarding over it
+ *
+ * Optional: if this doesn't
+ * clear the frame, stop the receiver, dump the whole RX FIFO
+ * and restart the receiver
+ * assume iDriverLock held
+ */
+ {
+ TUint32 retries = SMCS9118_MAX_RETRIES;
+
+ // if it is 4 words or less then just read it
+ TInt32 status = Read32(SMCS9118_RX_STATUS);
+ TInt32 length = (status >> SMCS9118_RX_LEN_SHIFT) & SMCS9118_RX_LEN_MASK;
+ TInt32 words = length >> 2;
+ if (length & 3)
+ {
+ words++;
+ }
+ if (words <= 4)
+ {
+ while (words--)
+ {
+ status = Read32(SMCS9118_RX_DATA_FIFO);
+ }
+ status = Read32(SMCS9118_RX_DATA_FIFO);
+ return KErrNone;
+ }
+
+ // FFWD over the frame
+ Write32(SMCS9118_RX_DP_CTL, SMCS9118_RX_DP_FFWD);
+ ByteTestDelay(1);
+ while((Read32(SMCS9118_RX_DP_CTL) & SMCS9118_RX_DP_FFWD) && --retries)
+ {
+ ByteTestDelay(1); // delay
+ }
+ if (retries != 0)
+ {
+ return KErrNone;
+ }
+
+#ifdef SMCS9118_DUMP_FIFO
+ // stop the receiver
+ TUint32 status;
+ TInt32 err;
+ err = ReadMac(SMCS9118_MAC_CR, status);
+ if (err != KErrNone)
+ {
+ return err;
+ }
+ status &= ~SMCS9118_MAC_RXEN;
+ err = WriteMac(SMCS9118_MAC_CR, status);
+ if (err != KErrNone)
+ {
+ return err;
+ }
+
+ // wait for reciever to stop
+ retries = SMCS9118_MAX_RETRIES;
+ Write32(SMCS9118_RX_DP_CTL, SMCS9118_RX_DP_FFWD);
+ ByteTestDelay(2);
+ while((Read32(SMCS9118_INT_STS) & SMCS9118_RXSTOP_INT) && --retries)
+ {
+ ByteTestDelay(1); // delay
+ }
+ if (retries == 0)
+ {
+ return KErrGeneral;
+ }
+
+ // dump the whole FIFO
+ retries = SMCS9118_MAX_RETRIES;
+ Write32(SMCS9118_RX_CFG, SMCS9118_RX_DUMP);
+ ByteTestDelay(1);
+ while((Read32(SMCS9118_RX_CFG) & SMCS9118_RX_DUMP) && --retries)
+ {
+ ByteTestDelay(1); // delay
+ }
+ if (retries != 0)
+ {
+ // re-enable RX
+ err = ReadMac(SMCS9118_MAC_CR, status);
+ if (err != KErrNone)
+ {
+ return err;
+ }
+ status |= SMCS9118_MAC_RXEN;
+ err = WriteMac(SMCS9118_MAC_CR, status);
+ return err;
+ }
+#endif
+ return KErrGeneral;
+ }
+
+
+
+TInt DEthernetSMCS9118Pdd::ReceiveFrame(TBuf8<KMaxEthernetPacket+32>& aBuffer, TBool aOkToUse)
+/**
+ * Retrieve data from the device - called by the RxDFC queued by the (variant's) ISR.
+ * Pull the received data out of the device and into the supplied buffer.
+ * Need to be told if the buffer is OK to use as if it not we could dump
+ * the waiting frame in order to clear the interrupt if necessory.
+ * @param aBuffer Reference to the buffer to be used to store the data in
+ * @param aOkToUse Bool to indicate if the buffer is usable
+ * @return KErrNone if the buffer has been filled.
+ */
+ {
+ TInt32 status;
+ TUint32 length;
+
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetSMCS9118Pdd::ReceiveFrame()");
+ #endif
+ // Always request for power (Needs to be done incase of external wakeup event)
+ iPowerHandler.RequestPower();
+
+ TInt irq = DriverLock();
+ // If no buffer available dump frame
+ if (!aOkToUse)
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("SMCS9118: No Rx buffer available");
+ #endif
+
+ if ((status = DiscardFrame()) == KErrNone)
+ {
+ status = KErrGeneral;
+ }
+ DriverUnlock(irq);
+ return status;
+ }
+
+
+ status = Read32(SMCS9118_RX_FIFO_INF);
+ if(!(status & 0xffff))
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("SMCS9118: Empty Rx FIFO");
+ #endif
+ DriverUnlock(irq);
+ return KErrGeneral;
+ }
+
+ // discard bad packets
+ status = Read32(SMCS9118_RX_STATUS);
+ length = (status >> SMCS9118_RX_LEN_SHIFT) & SMCS9118_RX_LEN_MASK;
+ if (status & SMCS9118_RX_ES)
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("SMCS9118: Bad Rx Packet");
+ #endif
+
+ if ((status = DiscardFrame()) == (TUint32)KErrNone)
+ {
+ status = KErrGeneral;
+ }
+ DriverUnlock(irq);
+ return status;
+ }
+
+ TUint32 words = length >> 2;
+ TUint32 *dataP = (TUint32*) aBuffer.Ptr();
+
+ if (length & 3)
+ {
+ words++;
+ }
+
+ while (words--)
+ {
+ *dataP++ = Read32(SMCS9118_RX_DATA_FIFO);
+ }
+ aBuffer.SetLength(length-4);
+
+ DriverUnlock(irq);
+ return KErrNone;
+ }
+
+
+const TInt KEthernetDfcThreadPriority = 24;
+_LIT(KEthernetDfcThread,"EthernetDfcThread");
+
+
+TInt DEthernetSMCS9118Pdd::DoCreate()
+/**
+ * Does the hard and soft reset of the lan card.
+ * Puts the default configuration in iDefaultConfig member
+ */
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetSMCS9118Pdd::DoCreate()");
+ #endif
+
+
+ // Register the power handler with Symbian Power Framework
+ iPowerHandler.Add();
+ TInt r = iPowerHandler.SetEthernetPdd(this);
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ __KTRACE_OPT(KPOWER, Kern::Printf("iPowerHandler.SetEthernetPdd() returned [%d]",r));
+ #endif
+
+ // Allocate a kernel thread to run the DFC
+ r = Kern::DynamicDfcQCreate(iDfcQ, KEthernetDfcThreadPriority, KEthernetDfcThread);
+
+ if (r != KErrNone)
+ {
+ return r;
+ }
+
+#ifdef CPU_AFFINITY_ANY
+ NKern::ThreadSetCpuAffinity((NThread*) iDfcQ->iThread, KCpuAffinityAny);
+#endif
+
+ iRxDfc.SetDfcQ(iDfcQ);
+
+ TInt irq = DriverLock();
+ iDefaultConfig.iEthSpeed = KEthSpeed10BaseT;
+ iDefaultConfig.iEthDuplex = KEthDuplexHalf;
+
+ // detect if SMSC9118 card is available, ignore revision
+ TUint32 id = Read32(SMCS9118_ID_REV) & SMCS9118_ID_MASK;
+ if(id != SMCS9118_ID_VAL)
+ {
+ DriverUnlock(irq);
+ return KErrHardwareNotAvailable;
+ }
+
+ TUint32 mac;
+ r = ReadMac(SMCS9118_MAC_ADDRL, mac);
+ if (r != KErrNone)
+ {
+ DriverUnlock(irq);
+ return r;
+ }
+
+ iDefaultConfig.iEthAddress[0] = (TUint8)(mac);
+ iDefaultConfig.iEthAddress[1] = (TUint8)(mac>>8);
+ iDefaultConfig.iEthAddress[2] = (TUint8)(mac>>16);
+ iDefaultConfig.iEthAddress[3] = (TUint8)(mac>>24);
+ r = ReadMac(SMCS9118_MAC_ADDRH, mac);
+ if (r != KErrNone)
+ {
+ DriverUnlock(irq);
+ return r;
+ }
+ iDefaultConfig.iEthAddress[4] = (TUint8)(mac);
+ iDefaultConfig.iEthAddress[5] = (TUint8)(mac>>8);
+
+ // Serial number is the bottom 4 bytes of the MAC address
+ TInt serialNum = (iDefaultConfig.iEthAddress[2] << 24)
+ | (iDefaultConfig.iEthAddress[3] << 16)
+ | (iDefaultConfig.iEthAddress[4] << 8)
+ | (iDefaultConfig.iEthAddress[5] );
+
+ // Push the serial numberinto the variant config so it can be retrieved via HAL
+ NE1_TBVariant::SetSerialNumber(serialNum);
+
+ iInterruptId = KEthernetInterruptId;
+
+ DriverUnlock(irq);
+ __KTRACE_OPT(KHARDWARE, Kern::Printf("-- MAC address %2x.%2x.%2x.%2x.%2x.%2x",
+ iDefaultConfig.iEthAddress[0], iDefaultConfig.iEthAddress[1],
+ iDefaultConfig.iEthAddress[2], iDefaultConfig.iEthAddress[3],
+ iDefaultConfig.iEthAddress[4], iDefaultConfig.iEthAddress[5]));
+
+ // Register ISR
+ r = BindInterrupt(KEthernetInterruptId, Isr, this);
+ if(r != KErrNone)
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("-- Error!!! Ethernet failed to bind interrupt.");
+ #endif
+ return r;
+ }
+ return r;
+ }
+
+void DEthernetSMCS9118Pdd::Sleep()
+/**
+ * Put the card into D1 sleep
+ * assume iDriverLock not held
+ */
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetSMCS9118Pdd::Sleep()");
+ #endif
+
+ TUint32 status;
+ TInt irq = DriverLock();
+ status = Read32(SMCS9118_PMT_CTRL) | SMCS9118_PM_MODE_D1;
+ Write32(SMCS9118_PMT_CTRL, status);
+ DriverUnlock(irq);
+ }
+
+TInt DEthernetSMCS9118Pdd::Wakeup()
+/**
+ * Wake the card up
+ * assume iDriverLock not held
+ */
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetSMCS9118Pdd::Wakeup()");
+ #endif
+
+ TUint32 status;
+
+ // has card woken up yet ?
+
+ TInt irq = DriverLock();
+ TUint32 retry = SMCS9118_MAX_RETRIES;
+ do
+ {
+ Write32(SMCS9118_BYTE_TEST, 0x12345678);
+ status = Read32(SMCS9118_PMT_CTRL);
+ if (status & SMCS9118_PMT_READY)
+ {
+ break;
+ }
+ Kern::NanoWait(ONE_MSEC);
+ } while (--retry);
+
+ if (retry == 0)
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("-- Error!!! Problem in Wakeup of SMCS9118 card.");
+ #endif
+ DriverUnlock(irq);
+ return KErrGeneral;
+ }
+ DriverUnlock(irq);
+ return KErrNone;
+ }
+
+TInt DEthernetSMCS9118Pdd::CardSoftReset()
+/**
+ * Does the soft reset of the lan card
+ * assume iDriverLock not held
+ */
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetSMCS9118Pdd::CardSoftReset()");
+ #endif
+
+ TInt32 status;
+
+ // wake the card up
+ status = Wakeup();
+ if (status != KErrNone)
+ {
+ return status;
+ }
+
+ // do soft reset
+ TInt irq = DriverLock();
+ status = Read32(SMCS9118_HW_CFG);
+ status |= SMCS9118_HW_CFG_SRST;
+ Write32(SMCS9118_HW_CFG, status);
+
+ ByteTestDelay(1);
+
+ TUint32 retry = SMCS9118_MAX_RETRIES;
+ do
+ {
+ status = Read32(SMCS9118_HW_CFG);
+ if (!(status & SMCS9118_HW_CFG_SRST))
+ {
+ break;
+ }
+ Kern::NanoWait(ONE_MSEC);
+ } while (--retry);
+
+ if (retry == 0)
+ {
+ DriverUnlock(irq);
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("-- Error!!! Problem in soft reset of SMCS9118 card.");
+ #endif
+ return KErrGeneral;
+ }
+
+ ByteTestDelay(1);
+ DriverUnlock(irq);
+
+ return KErrNone;
+ }
+
+
+/**
+ * service the Isr
+ */
+void DEthernetSMCS9118Pdd::Isr(TAny* aPtr)
+ {
+
+ DEthernetSMCS9118Pdd &d=*(DEthernetSMCS9118Pdd*)aPtr;
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetSMCS9118Pdd::Isr");
+ #endif
+
+ // get interrupt status
+ TUint32 status = d.Read32(SMCS9118_INT_STS);
+
+ if (d.IsReady() && (status & SMCS9118_INT_STS_RSFL))
+ {
+ // We received an Rx Interrupt, so clear it
+ // and queue on dfc
+ d.Write32(SMCS9118_INT_EN, 0);
+ d.iRxDfc.Add();
+ }
+ else
+ {
+ // spurious interrupt ?
+ d.Write32(SMCS9118_INT_STS, status);
+ }
+ d.ClearInterrupt(d.iInterruptId);
+ }
+
+//
+// Queued by the ISR on a receive interrupt. Calls into LDD which calls back into
+// PDD.ReceiveFrame() to get the data and write it into the LDD managed FIFO.
+// Assume iDriverLock not held
+//
+
+void DEthernetSMCS9118Pdd::ServiceRxDfc(TAny* aPtr)
+ {
+ #if defined(INSTR) && defined(KTRACE_SYNCH)
+ KPROFILE_PRINT("DEthernetSMCS9118Pdd::ServiceRxDfc");
+ #endif
+
+ DEthernetSMCS9118Pdd &d=*(DEthernetSMCS9118Pdd*)aPtr;
+
+ d.ReceiveIsr();
+
+ TInt irq = d.DriverLock();
+ // reset status
+ d.Write32(SMCS9118_INT_STS, SMCS9118_INT_STS_RSFL);
+ d.ByteTestDelay(2);
+
+ // reenable interrupt
+ d.Write32(SMCS9118_INT_EN, SMCS9118_INT_EN_RSFL);
+ d.ByteTestDelay(1);
+ d.DriverUnlock(irq);
+
+ return;
+ }
+
+/**
+ * Read a MAC register
+ * assume iDriverLock held
+ */
+TInt32 DEthernetSMCS9118Pdd::ReadMac(TUint32 aReg, TUint32 &aVal)
+ {
+ TUint32 timeout;
+
+ for(timeout = 0; timeout < SMCS9118_MAC_TIMEOUT; timeout ++)
+ {
+ if(Read32(SMCS9118_MAC_CSR_CMD) & SMCS9118_MAC_CSR_BUSY)
+ {
+ Kern::NanoWait(ONE_MSEC);
+ }
+ }
+
+ TUint32 cmd = 0;
+
+ Write32(SMCS9118_MAC_CSR_CMD, cmd);
+ ByteTestDelay(1);
+
+ cmd = (aReg & 0xff) | SMCS9118_MAC_CSR_BUSY | SMCS9118_MAC_CSR_READ;
+
+ Write32(SMCS9118_MAC_CSR_CMD, cmd);
+ ByteTestDelay(1);
+
+ for(timeout = 0; timeout < SMCS9118_MAC_TIMEOUT; timeout ++)
+ {
+ if(Read32(SMCS9118_MAC_CSR_CMD) & SMCS9118_MAC_CSR_BUSY)
+ {
+ Kern::NanoWait(ONE_MSEC);
+ }
+ else
+ {
+ aVal = Read32(SMCS9118_MAC_CSR_DATA);
+ return KErrNone;
+ }
+ }
+ return KErrTimedOut;
+ }
+
+/**
+ * Write a MAC register
+ * assume iDriverLock held
+ */
+TInt32 DEthernetSMCS9118Pdd::WriteMac(TUint32 aReg, TUint32 aVal)
+ {
+ if (Read32(SMCS9118_MAC_CSR_CMD) & SMCS9118_MAC_CSR_BUSY)
+ {
+ return KErrTimedOut;
+ }
+
+ TUint32 cmd = 0;
+ TUint32 timeout;
+
+ Write32(SMCS9118_MAC_CSR_CMD, cmd);
+ ByteTestDelay(1);
+
+ cmd = (aReg & 0xff) | SMCS9118_MAC_CSR_BUSY;
+
+ Write32(SMCS9118_MAC_CSR_DATA, aVal);
+ ByteTestDelay(1);
+ Write32(SMCS9118_MAC_CSR_CMD, cmd);
+ ByteTestDelay(1);
+
+ for (timeout = 0; timeout < SMCS9118_MAC_TIMEOUT; timeout ++)
+ {
+ if(Read32(SMCS9118_MAC_CSR_CMD) & SMCS9118_MAC_CSR_BUSY)
+ {
+ Kern::NanoWait(ONE_MSEC);
+ }
+ else
+ {
+ return KErrNone;
+ }
+ }
+ return KErrTimedOut;
+ }
+
+/**
+ * Read a PHY register
+ * assume iDriverLock held
+ */
+TInt32 DEthernetSMCS9118Pdd::ReadPhy(TUint32 aReg, TUint32 &aValue)
+ {
+ TUint32 cmd;
+ TUint32 timeout;
+ TInt32 err;
+
+ // bail out if busy
+ err = ReadMac(SMCS9118_MAC_MII_ACC, aValue);
+ if (err != KErrNone)
+ {
+ return err;
+ }
+
+ if (aValue & SMCS9118_MII_BUSY)
+ {
+ return KErrTimedOut;
+ }
+
+ cmd = SMCS9118_PHY_ADDR | (aReg << 6) | SMCS9118_MII_BUSY;
+
+ err = WriteMac(SMCS9118_MAC_MII_ACC, cmd);
+ if (err != KErrNone)
+ {
+ return err;
+ }
+
+ for(timeout = 0; timeout < SMCS9118_MAC_TIMEOUT; timeout++)
+ {
+ err = ReadMac(SMCS9118_MAC_MII_ACC, aValue);
+ if (err != KErrNone)
+ {
+ return err;
+ }
+
+ if (!(aValue & SMCS9118_MII_BUSY))
+ {
+ err = ReadMac(SMCS9118_MAC_MII_DATA, aValue);
+ return err;
+ }
+ }
+ return KErrTimedOut;
+ }
+
+/**
+ * Write a PHY register
+ * assume iDriverLock held
+ */
+TInt32 DEthernetSMCS9118Pdd::WritePhy(TUint32 aReg, TUint32 aVal)
+ {
+ TUint32 cmd;
+ TUint32 timeout;
+ TUint32 status;
+ TInt32 err;
+
+ // bail out if busy
+ err = ReadMac(SMCS9118_MAC_MII_ACC, status);
+ if (err != KErrNone)
+ {
+ return err;
+ }
+
+ if (status & SMCS9118_MII_BUSY)
+ {
+ return KErrTimedOut;
+ }
+
+ cmd = SMCS9118_PHY_ADDR | (aReg << 6) | SMCS9118_MII_WRITE | SMCS9118_MII_BUSY;
+
+ err = WriteMac(SMCS9118_MAC_MII_DATA, aVal & 0xffff);
+ if (err != KErrNone)
+ {
+ return err;
+ }
+ err = WriteMac(SMCS9118_MAC_MII_ACC, cmd);
+ if (err != KErrNone)
+ {
+ return err;
+ }
+
+ for(timeout = 0; timeout < SMCS9118_MAC_TIMEOUT; timeout++)
+ {
+ err = ReadMac(SMCS9118_MAC_MII_ACC, status);
+ if (err != KErrNone)
+ {
+ return err;
+ }
+ if(!(status & SMCS9118_MII_BUSY))
+ {
+ return KErrNone;
+ }
+ }
+ return KErrTimedOut;
+ }
+// PDD entry point
+DECLARE_STANDARD_PDD()
+ {
+ return new DEthernetPddFactory;
+ }