/* * Copyright (C) 2003-2005 by egnite Software GmbH. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ * */ /* * $Log$ * Revision 1.5 2008/08/28 11:12:15 haraldkipp * Added interface flags, which will be required to implement Ethernet ioctl * functions. * * Revision 1.4 2008/08/11 06:59:07 haraldkipp * BSD types replaced by stdint types (feature request #1282721). * * Revision 1.3 2007/05/02 11:22:51 haraldkipp * Added multicast table entry. * * Revision 1.2 2006/05/25 09:09:57 haraldkipp * API documentation updated and corrected. * * Revision 1.1 2006/02/23 15:33:59 haraldkipp * Support for Philips LPC2xxx Family and LPC-E2294 Board from Olimex added. * Many thanks to Michael Fischer for this port. * * Revision 1.2 2005/08/02 17:46:45 haraldkipp * Major API documentation update. * * Revision 1.1 2005/07/26 18:02:40 haraldkipp * Moved from dev. * * Revision 1.13 2005/04/30 16:42:41 chaac * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG * is defined in NutConf, it will make effect where it is used. * * Revision 1.12 2005/02/02 19:55:34 haraldkipp * If no Ethernet link was available on the LAN91C111, each outgoing packet * took 15 seconds and, even worse, the ouput routine doesn't return an error. * Now the first attempt to send a packet without Ethernet link will wait for * 5 seconds and subsequent attempts take 0.5 seconds only, always returning * an error. * * Revision 1.11 2005/01/24 21:11:49 freckle * renamed NutEventPostFromIRQ into NutEventPostFromIrq * * Revision 1.10 2005/01/22 19:24:11 haraldkipp * Changed AVR port configuration names from PORTx to AVRPORTx. * * Revision 1.9 2005/01/21 16:49:45 freckle * Seperated calls to NutEventPostAsync between Threads and IRQs * * Revision 1.8 2004/09/22 08:14:48 haraldkipp * Made configurable * * Revision 1.7 2004/03/08 11:14:17 haraldkipp * Added quick hack for fixed mode. * * Revision 1.6 2004/02/25 16:22:33 haraldkipp * Do not initialize MAC with all zeros * * Revision 1.5 2004/01/14 19:31:43 drsung * Speed improvement to NicWrite applied. Thanks to Kolja Waschk * * Revision 1.4 2003/11/06 09:26:50 haraldkipp * Removed silly line with hardcoded MAC, left over from testing * * Revision 1.3 2003/11/04 17:54:47 haraldkipp * PHY configuration timing changed again for reliable linking * * Revision 1.2 2003/11/03 17:12:53 haraldkipp * Allow linking with RTL8019 driver. * Links more reliable to 10 MBit networks now. * Reset MMU on allocation failures. * Some optimizations. * * Revision 1.1 2003/10/13 10:13:49 haraldkipp * First release * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NUTDEBUG #include #endif /* * This file is for the Olimex LPC-E2294 board @@MF */ #include #include <__armlib.h> #define cli() __ARMLIB_disableIRQ() #define sei() __ARMLIB_enableIRQ() /*=========================================================================*/ /* DEFINE: All Structures and Common Constants */ /*=========================================================================*/ /* * Cirrus Logic CS8900a I/O Registers */ #define CS_DATA_P0 (cs_base + 0x0000UL) #define CS_DATA_P1 (cs_base + 0x0002UL) #define CS_TX_CMD_I (cs_base + 0x0004UL) #define CS_TX_LEN_I (cs_base + 0x0006UL) #define CS_INT_STAT (cs_base + 0x0008UL) #define CS_PP_PTR (cs_base + 0x000AUL) #define CS_PP_DATA0 (cs_base + 0x000CUL) #define CS_PP_DATA1 (cs_base + 0x000EUL) // Cirrus Logic CS8900a Packet Page registers #define CS_PROD_ID 0x0000 #define CS_IO_BASE 0x0020 #define CS_INT_NUM 0x0022 #define CS_DMA_CHAN 0x0024 #define CS_DMA_SOF 0x0026 #define CS_DMA_FCNT 0x0028 #define CS_DMA_RXCNT 0x002A #define CS_MEM_BASE 0x002C #define CS_BOOT_BASE 0x0030 #define CS_BOOT_MASK 0x0034 #define CS_EE_CMD 0x0040 #define CS_EE_DATA 0x0042 #define CS_RX_FRM_CNT 0x0050 #define CS_ISQ 0x0120 #define CS_RX_CFG 0x0102 #define CS_RX_EVENT 0x0124 #define CS_RX_CTL 0x0104 #define CS_TX_CFG 0x0106 #define CS_TX_EVENT 0x0128 #define CS_TX_CMD_P 0x0108 #define CS_BUF_CFG 0x010A #define CS_BUF_EVENT 0x012C #define CS_RX_MISS 0x0130 #define CS_TX_COLL 0x0132 #define CS_LINE_CTRL 0x0112 #define CS_LINE_STAT 0x0134 #define CS_SELF_CTRL 0x0114 #define CS_SELF_STAT 0x0136 #define CS_BUS_CTRL 0x0116 #define CS_BUS_STAT 0x0138 #define CS_TEST_CTRL 0x0118 #define CS_AUI_TDR 0x013C #define CS_PP_TX_CMD 0x0144 #define CS_PP_TX_LEN 0x0146 #define CS_IEEE_ADDR 0x0158 /*! * \addtogroup xgNicCs8900 */ /*@{*/ /*! * \struct _NICINFO * \brief Network interface controller information structure. */ struct _NICINFO { uint32_t ni_rx_packets; /*!< Number of packets received. */ uint32_t ni_tx_packets; /*!< Number of packets sent. */ }; /*! * \brief Network interface controller information type. */ typedef struct _NICINFO NICINFO; /*=========================================================================*/ /* DEFINE: Definition of all local Data */ /*=========================================================================*/ // Ethernet flags byte // Bit 0 = transmit byte flag static uint8_t cs_flags = 0; volatile uint32_t cs_base = 0x82000000UL; /*=========================================================================*/ /* DEFINE: Definition of all local Procedures */ /*=========================================================================*/ void CSWrite16(uint32_t addr, uint16_t data) { uint8_t *p; p = (uint8_t *) addr; cli(); *p = data; p++; *p = data >> 8; sei(); } void CSWritePP16(uint16_t addr, uint16_t data) { uint8_t *p; cli(); p = (uint8_t *) CS_PP_PTR; *p = addr; p++; *p = addr >> 8; CSWrite16(CS_PP_DATA0, data); return; } uint16_t CSRead16(uint32_t addr) { uint8_t *p; uint16_t d; cli(); p = (uint8_t *) addr; d = *p; p++; d |= (*p << 8); sei(); return d; } uint16_t CSReadPP16(uint16_t addr) { uint8_t *p; cli(); p = (uint8_t *) CS_PP_PTR; *p = addr; p++; *p = addr >> 8; return CSRead16(CS_PP_DATA0); } void CSBeginFrame(void) { cs_flags &= ~1; } void CSEndFrame(void) { uint8_t *p; cli(); p = (uint8_t *) CS_DATA_P0 + 1; sei(); // If odd number of bytes in packet pad it out if (cs_flags & 1) p = 0; } void CSWriteFrameByte(uint8_t data) { uint8_t *p; if (cs_flags & 1) p = (uint8_t *) CS_DATA_P0 + 1; else p = (uint8_t *) CS_DATA_P0; *p = data; cs_flags ^= 1; } static int CSEthPutPacket(NUTDEVICE * dev, NETBUF * nb) { uint16_t i; uint16_t sz; uint8_t *p; NICINFO *ni; ni = (NICINFO *) dev->dev_dcb; // // Calculate the number of bytes to be send. Do not // send packets larger than 1536 bytes. // sz = nb->nb_dl.sz + nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz; if (sz >= 0x600) { NutNetBufFree(nb); return -1; } #if 0 if (tcp_trace) { NutPrintFormat_P(dev_debug, PSTR("[ETHTX-%u]\r\n"), sz); NutPrintFlush(dev_debug); } #endif // Start transmission after entire frame is loaded into CS8900 CSWrite16(CS_TX_CMD_I, 0xC0); // Set frame size CSWrite16(CS_TX_LEN_I, sz); // Wait for buffer space, but only for a while (200ms) // If the cable is disconnected this will never become true // If we don't get the go ahead within 200ms return 0 (Sucess) // And let the upper layers deal with re-transmission // If we return failure TCP sockets will close straight away which probably // isn't the correct behaviour i = 0; while ((CSReadPP16(CS_BUS_STAT) & 0x0100) == 0) { i++; if (i > 20) return 0; NutSleep(10); } // // Transfer ethernet physical header. // CSBeginFrame(); p = nb->nb_dl.vp; for (i = 0; i < nb->nb_dl.sz; i++) { CSWriteFrameByte(*p++); } p = nb->nb_nw.vp; for (i = 0; i < nb->nb_nw.sz; i++) { CSWriteFrameByte(*p++); } p = nb->nb_tp.vp; for (i = 0; i < nb->nb_tp.sz; i++) { CSWriteFrameByte(*p++); } p = nb->nb_ap.vp; for (i = 0; i < nb->nb_ap.sz; i++) { CSWriteFrameByte(*p++); } CSEndFrame(); return 0; } void CSSoftwareWakeup(void) { volatile uint8_t *p; uint16_t data = CS_SELF_CTRL; p = (uint8_t *) CS_PP_PTR; *p = data; p++; *p = data >> 8; NutDelay(10); } void CSSoftwareReset(void) { volatile uint8_t *p; uint16_t data; data = CS_SELF_CTRL; p = (uint8_t *) CS_PP_PTR; *p = data; p++; *p = data >> 8; data = 0x0040; p = (uint8_t *) CS_DATA_P0; *p = data; p++; *p = data >> 8; } THREAD(CSNICrx, arg) { NUTDEVICE *dev; IFNET *ifn; NICINFO *ni; NETBUF *nb; uint8_t *p; uint8_t *q; uint16_t i, m; volatile uint16_t l; dev = arg; ifn = (IFNET *) dev->dev_icb; ni = (NICINFO *) dev->dev_dcb; #if 0 if (tcp_trace) { NutPrintFormat_P(dev_debug, PSTR("Enter ETHReceive\r\n")); NutPrintFlush(dev_debug); } #endif l = 0; NutThreadSetPriority(8); for (;;) { while ((CSReadPP16(CS_RX_EVENT) & 0x0100) == 0) { NutSleep(10); } l = *(uint8_t *) (CS_DATA_P0 + 1) << 8 | *(uint8_t *) (CS_DATA_P0); l = *(uint8_t *) (CS_DATA_P0 + 1) << 8 | *(uint8_t *) (CS_DATA_P0); //NutPrintFormat_P(dev_debug,PSTR("RxLength = %x \r\n"), l); //NutPrintFlush(dev_debug); // Account for odd length packets if (l & 1) m = l - 1; else m = l; nb = NutNetBufAlloc(0, NBAF_DATALINK, l); if (nb) { q = nb->nb_dl.vp; for (i = 0; i < m; i += 2) { p = (uint8_t *) CS_DATA_P0; *q++ = *p; p = (uint8_t *) CS_DATA_P0 + 1; *q++ = *p; } // Odd length packets if (m != l) { p = (uint8_t *) CS_DATA_P0; *q++ = *p; p = (uint8_t *) CS_DATA_P0 + 1; m = *p; } ni->ni_rx_packets++; (*ifn->if_recv) (dev, nb); } } } /*=========================================================================*/ /* DEFINE: All code exported */ /*=========================================================================*/ /*! * \brief Send Ethernet packet. * * \param dev Identifies the device to use. * \param nb Network buffer structure containing the packet to be sent. * The structure must have been allocated by a previous * call NutNetBufAlloc(). * * \return 0 on success, -1 in case of any errors. */ int cs8900Output(NUTDEVICE * dev, NETBUF * nb) { int rc = -1; NICINFO *ni; ni = (NICINFO *) dev->dev_dcb; #if 0 if (tcp_trace) { NutPrintFormat_P(dev_debug, PSTR("Enter EthOutput\r\n")); NutPrintFlush(dev_debug); } #endif if ((rc = CSEthPutPacket(dev, nb)) == 0) ni->ni_tx_packets++; return rc; } /*! * \brief Initialize Ethernet hardware. * * Resets the CS8900 Ethernet controller, initializes all required * hardware registers and starts a background thread for incoming * Ethernet traffic. * * Applications should do not directly call this function. It is * automatically executed during during device registration by * NutRegisterDevice(). * * If the network configuration hasn't been set by the application * before registering the specified device, this function will * call NutNetLoadConfig() to get the MAC address. * * \param dev Identifies the device to initialize. */ int cs8900Init(NUTDEVICE * dev) { uint16_t i; uint16_t j; IFNET *ifn; NICINFO *ni; #if 0 if (tcp_trace) { NutPrintFormat_P(dev_debug, PSTR("Enter NicInit \r\n")); NutPrintFlush(dev_debug); } #endif cs_base = dev->dev_base; #if defined(OLIMEX_LPCE2294) if (cs_base == 0) { cs_base = 0x82000000UL; } #endif if (confnet.cd_size == 0) NutNetLoadConfig(dev->dev_name); ifn = dev->dev_icb; #if 0 /* @@MF */ memcpy(ifn->if_mac, confnet.cdn_mac, 6); #else ifn->if_mac[0] = 0x00; ifn->if_mac[1] = 0x06; ifn->if_mac[2] = 0x98; ifn->if_mac[3] = 0x00; ifn->if_mac[4] = 0x00; ifn->if_mac[5] = 0x00; #endif memset(dev->dev_dcb, 0, sizeof(NICINFO)); ni = (NICINFO *) dev->dev_dcb; // Take CS8900 out of reset and wait for internal reset to complete CSSoftwareWakeup(); CSSoftwareReset(); NutDelay(100); // Check for presence if (CSReadPP16(CS_PROD_ID) != 0x630E) return -1; // // Copy our MAC address to the NIC // for (i = 0; i < 6; i += 2) { j = ifn->if_mac[i] << 8; j |= ifn->if_mac[i + 1]; CSWritePP16(CS_IEEE_ADDR + i, j); j = CSReadPP16(CS_IEEE_ADDR + i); #if 0 if (tcp_trace) { NutPrintFormat_P(dev_debug, PSTR("ADDR = %x\r\n"), j); NutPrintFlush(dev_debug); } #endif } // // Enable the Transmitter and Receiver // CSWritePP16(CS_LINE_CTRL, 0x00C0); //i = CSReadPP16(CS_LINE_CTRL); //NutPrintFormat_P(dev_debug,PSTR("CS_LINE_CTRL = %x\r\n"), i); CSWritePP16(CS_RX_CTL, 0x0F40); //i = CSReadPP16(CS_RX_CTL); //NutPrintFormat_P(dev_debug,PSTR("CS_RX_CTL = %x\r\n"), i); // // Start receiver thread // NutThreadCreate("csnicrx", CSNICrx, dev, 768); return 0; } static NICINFO dcb_eth0; /*! * \brief Network interface information structure. * * Used to call. */ static IFNET ifn_eth0 = { IFT_ETHER, /*!< \brief Interface type. */ 0, /*!< \brief Interface flags, if_flags. */ {0, 0, 0, 0, 0, 0}, /*!< \brief Hardware net address. */ 0, /*!< \brief IP address. */ 0, /*!< \brief Remote IP address for point to point. */ 0, /*!< \brief IP network mask. */ ETHERMTU, /*!< \brief Maximum size of a transmission unit. */ 0, /*!< \brief Packet identifier. */ 0, /*!< \brief Linked list of arp entries. */ 0, /*!< \brief Linked list of multicast address entries, if_mcast. */ NutEtherInput, /*!< \brief Routine to pass received data to, if_recv(). */ cs8900Output, /*!< \brief Driver output routine, if_send(). */ NutEtherOutput, /*!< \brief Media output routine, if_output(). */ NULL /*!< \brief Interface specific control function, if_ioctl(). */ #ifdef NUT_PERFMON , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 #endif }; /*! * \brief Device information structure. * * A pointer to this structure must be passed to NutRegisterDevice() * to bind this Ethernet device driver to the Nut/OS kernel. * An application may then call NutNetIfConfig() with the name \em eth0 * of this driver to initialize the network interface. * */ NUTDEVICE devCS8900A = { 0, /* Pointer to next device. */ {'e', 't', 'h', '0', 0, 0, 0, 0, 0}, /* Unique device name. */ IFTYP_NET, /* Type of device. */ 0, /* Base address. */ 0, /* First interrupt number. */ &ifn_eth0, /* Interface control block. */ &dcb_eth0, /* Driver control block. */ cs8900Init, /* Driver initialization routine. */ 0, /* Driver specific control function. */ 0, /* Read from device. */ 0, /* Write to device. */ 0, /* Open a device or file. */ 0, /* Close a device or file. */ 0, /* Request file size. */ 0, /* Select function, optional, not yet implemented */ }; /*@}*/