| author | William Roberts <williamr@symbian.org> |
| Fri, 01 Oct 2010 12:45:26 +0100 | |
| changeset 3 | b41049883d87 |
| parent 0 | 5de814552237 |
| permissions | -rw-r--r-- |
/* * 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; }