/* * RTEMS driver for Etherlink based Ethernet Controllers * * Copyright (C) 2003, Gregory Menke, NASA/GSFC * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. * * elnk.c * * */ /* * Portions of this driver are taken from the Freebsd if_xl.c driver, * version "1.133 2003/03/19 01:48:14" and are covered by the license * text included below that was taken verbatim from the original file. * More particularly, all structures, variables, and #defines prefixed * with XL_ or xl_, along with their associated comments were taken * directly from the Freebsd driver and modified as required to suit the * purposes of this one. Additionally, much of the device setup & * manipulation logic was also copied and modified to suit. All types * and functions beginning with elnk are either my own creations or were * adapted from other RTEMS components, and regardless, are subject to * the standard OAR licensing terms given in the comments at the top of * this file. * * Greg Menke, 6/11/2003 */ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul . 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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. */ #include #include /* * This driver only supports architectures with the new style * exception processing. The following checks try to keep this * from being compiled on systems which can't support this driver. */ #if defined(__i386__) #define ELNK_SUPPORTED #define PCI_DRAM_OFFSET 0 #endif #if defined(__PPC__) #define ELNK_SUPPORTED #endif #include #if !defined(PCI_DRAM_OFFSET) #undef ELNK_SUPPORTED #endif /* #undef ELNK_SUPPORTED */ #if defined(ELNK_SUPPORTED) #include #if defined(__PPC__) #include #include #endif #if defined(__i386__) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__i386__) #define IO_MASK 0x3 #define MEM_MASK 0xF #endif #ifdef malloc #undef malloc #endif #ifdef free #undef free #endif #define ELNK_DEBUG #define DRIVER_PREFIX "elnk" /* * These buffers allocated for each unit, so ensure * * rtems_bsdnet_config.mbuf_bytecount * rtems_bsdnet_config.mbuf_cluster_bytecount * * are adequately sized to provide enough clusters and mbufs for all the * units. The default bsdnet configuration is sufficient for one unit, * but will be nearing exhaustion with 2 or more. Although a little * expensive in memory, the following configuration should eliminate all * mbuf/cluster issues; * * rtems_bsdnet_config.mbuf_bytecount = 128*1024; * rtems_bsdnet_config.mbuf_cluster_bytecount = 256*1024; * * The default size in buffers of the rx & tx rings are given below. * This driver honors the rtems_bsdnet_ifconfig fields 'rbuf_count' and * 'xbuf_count', allowing the user to specify something else. */ #define RX_RING_SIZE 16 /* default number of receive buffers */ #define TX_RING_SIZE 16 /* default number of transmit buffers */ /* * Number of boards supported by this driver */ #define NUM_UNITS 8 /* * Receive buffer size -- Allow for a full ethernet packet including CRC */ #define XL_PACKET_SIZE 1540 /* ** Events, one per unit. The event is sent to the rx task from the isr ** or from the stack to the tx task whenever a unit needs service. The ** rx/tx tasks identify the requesting unit(s) by their particular ** events so only requesting units are serviced. */ static rtems_event_set unit_signals[NUM_UNITS]= { RTEMS_EVENT_1, RTEMS_EVENT_2, RTEMS_EVENT_3, RTEMS_EVENT_4, RTEMS_EVENT_5, RTEMS_EVENT_6, RTEMS_EVENT_7, RTEMS_EVENT_8 }; #if defined(__PPC__) #define phys_to_bus(address) ((unsigned int)((address)) + PCI_DRAM_OFFSET) #define bus_to_phys(address) ((unsigned int)((address)) - PCI_DRAM_OFFSET) #define CPU_CACHE_ALIGNMENT_FOR_BUFFER PPC_CACHE_ALIGNMENT #else extern void Wait_X_ms( unsigned int timeToWait ); #define phys_to_bus(address) ((unsigned int) ((address))) #define bus_to_phys(address) ((unsigned int) ((address))) #define rtems_bsp_delay_in_bus_cycles(cycle) Wait_X_ms( cycle/100 ) #define CPU_CACHE_ALIGNMENT_FOR_BUFFER PG_SIZE #endif /* the actual duration waited in DELAY is not especially predictable, * though it will be consistent on a given host. It should not be * relied upon for specific timing given the vague per-bsp, * per-architecture implementation of the actual delay function. It * would probably be helpful to make this more accurate at some point... */ #define DELAY(n) rtems_bsp_delay_in_bus_cycles( n*20 ) /* * Register layouts. */ #define XL_COMMAND 0x0E #define XL_STATUS 0x0E #define XL_TX_STATUS 0x1B #define XL_TX_FREE 0x1C #define XL_DMACTL 0x20 #define XL_DOWNLIST_PTR 0x24 #define XL_DOWN_POLL 0x2D /* 3c90xB only */ #define XL_TX_FREETHRESH 0x2F #define XL_UPLIST_PTR 0x38 #define XL_UPLIST_STATUS 0x30 #define XL_UP_POLL 0x3D /* 3c90xB only */ #define XL_PKTSTAT_UP_STALLED 0x00002000 #define XL_PKTSTAT_UP_ERROR 0x00004000 #define XL_PKTSTAT_UP_CMPLT 0x00008000 #define XL_DMACTL_DN_CMPLT_REQ 0x00000002 #define XL_DMACTL_DOWN_STALLED 0x00000004 #define XL_DMACTL_UP_CMPLT 0x00000008 #define XL_DMACTL_DOWN_CMPLT 0x00000010 #define XL_DMACTL_UP_RX_EARLY 0x00000020 #define XL_DMACTL_ARM_COUNTDOWN 0x00000040 #define XL_DMACTL_DOWN_INPROG 0x00000080 #define XL_DMACTL_COUNTER_SPEED 0x00000100 #define XL_DMACTL_DOWNDOWN_MODE 0x00000200 #define XL_DMACTL_TARGET_ABORT 0x40000000 #define XL_DMACTL_MASTER_ABORT 0x80000000 /* * Command codes. Some command codes require that we wait for * the CMD_BUSY flag to clear. Those codes are marked as 'mustwait.' */ #define XL_CMD_RESET 0x0000 /* mustwait */ #define XL_CMD_WINSEL 0x0800 #define XL_CMD_COAX_START 0x1000 #define XL_CMD_RX_DISABLE 0x1800 #define XL_CMD_RX_ENABLE 0x2000 #define XL_CMD_RX_RESET 0x2800 /* mustwait */ #define XL_CMD_UP_STALL 0x3000 /* mustwait */ #define XL_CMD_UP_UNSTALL 0x3001 #define XL_CMD_DOWN_STALL 0x3002 /* mustwait */ #define XL_CMD_DOWN_UNSTALL 0x3003 #define XL_CMD_RX_DISCARD 0x4000 #define XL_CMD_TX_ENABLE 0x4800 #define XL_CMD_TX_DISABLE 0x5000 #define XL_CMD_TX_RESET 0x5800 /* mustwait */ #define XL_CMD_INTR_FAKE 0x6000 #define XL_CMD_INTR_ACK 0x6800 #define XL_CMD_INTR_ENB 0x7000 #define XL_CMD_STAT_ENB 0x7800 #define XL_CMD_RX_SET_FILT 0x8000 #define XL_CMD_RX_SET_THRESH 0x8800 #define XL_CMD_TX_SET_THRESH 0x9000 #define XL_CMD_TX_SET_START 0x9800 #define XL_CMD_DMA_UP 0xA000 #define XL_CMD_DMA_STOP 0xA001 #define XL_CMD_STATS_ENABLE 0xA800 #define XL_CMD_STATS_DISABLE 0xB000 #define XL_CMD_COAX_STOP 0xB800 #define XL_CMD_SET_TX_RECLAIM 0xC000 /* 3c905B only */ #define XL_CMD_RX_SET_HASH 0xC800 /* 3c905B only */ #define XL_HASH_SET 0x0400 #define XL_HASHFILT_SIZE 256 /* * status codes * Note that bits 15 to 13 indicate the currently visible register window * which may be anything from 0 to 7. */ #define XL_STAT_INTLATCH 0x0001 /* 0 */ #define XL_STAT_ADFAIL 0x0002 /* 1 */ #define XL_STAT_TX_COMPLETE 0x0004 /* 2 */ #define XL_STAT_TX_AVAIL 0x0008 /* 3 first generation */ #define XL_STAT_RX_COMPLETE 0x0010 /* 4 */ #define XL_STAT_RX_EARLY 0x0020 /* 5 */ #define XL_STAT_INTREQ 0x0040 /* 6 */ #define XL_STAT_STATSOFLOW 0x0080 /* 7 */ #define XL_STAT_DMADONE 0x0100 /* 8 first generation */ #define XL_STAT_LINKSTAT 0x0100 /* 8 3c509B */ #define XL_STAT_DOWN_COMPLETE 0x0200 /* 9 */ #define XL_STAT_UP_COMPLETE 0x0400 /* 10 */ #define XL_STAT_DMABUSY 0x0800 /* 11 first generation */ #define XL_STAT_CMDBUSY 0x1000 /* 12 */ /* * Interrupts we normally want enabled. */ #define XL_INTRS \ (XL_STAT_UP_COMPLETE | XL_STAT_STATSOFLOW | XL_STAT_ADFAIL| \ XL_STAT_DOWN_COMPLETE | XL_STAT_TX_COMPLETE | XL_STAT_INTLATCH) /* * General constants that are fun to know. * * 3Com PCI vendor ID */ #define TC_VENDORID 0x10B7 /* * 3Com chip device IDs. */ #define TC_DEVICEID_BOOMERANG_10BT 0x9000 #define TC_DEVICEID_BOOMERANG_10BT_COMBO 0x9001 #define TC_DEVICEID_BOOMERANG_10_100BT 0x9050 #define TC_DEVICEID_BOOMERANG_100BT4 0x9051 #define TC_DEVICEID_KRAKATOA_10BT 0x9004 #define TC_DEVICEID_KRAKATOA_10BT_COMBO 0x9005 #define TC_DEVICEID_KRAKATOA_10BT_TPC 0x9006 #define TC_DEVICEID_CYCLONE_10FL 0x900A #define TC_DEVICEID_HURRICANE_10_100BT 0x9055 #define TC_DEVICEID_CYCLONE_10_100BT4 0x9056 #define TC_DEVICEID_CYCLONE_10_100_COMBO 0x9058 #define TC_DEVICEID_CYCLONE_10_100FX 0x905A #define TC_DEVICEID_TORNADO_10_100BT 0x9200 #define TC_DEVICEID_TORNADO_10_100BT_920B 0x9201 #define TC_DEVICEID_HURRICANE_10_100BT_SERV 0x9800 #define TC_DEVICEID_TORNADO_10_100BT_SERV 0x9805 #define TC_DEVICEID_HURRICANE_SOHO100TX 0x7646 #define TC_DEVICEID_TORNADO_HOMECONNECT 0x4500 #define TC_DEVICEID_HURRICANE_555 0x5055 #define TC_DEVICEID_HURRICANE_556 0x6055 #define TC_DEVICEID_HURRICANE_556B 0x6056 #define TC_DEVICEID_HURRICANE_575A 0x5057 #define TC_DEVICEID_HURRICANE_575B 0x5157 #define TC_DEVICEID_HURRICANE_575C 0x5257 #define TC_DEVICEID_HURRICANE_656 0x6560 #define TC_DEVICEID_HURRICANE_656B 0x6562 #define TC_DEVICEID_TORNADO_656C 0x6564 #define XL_RXSTAT_LENMASK 0x00001FFF #define XL_RXSTAT_UP_ERROR 0x00004000 #define XL_RXSTAT_UP_CMPLT 0x00008000 #define XL_RXSTAT_UP_OVERRUN 0x00010000 #define XL_RXSTAT_RUNT 0x00020000 #define XL_RXSTAT_ALIGN 0x00040000 #define XL_RXSTAT_CRC 0x00080000 #define XL_RXSTAT_OVERSIZE 0x00100000 #define XL_RXSTAT_DRIBBLE 0x00800000 #define XL_RXSTAT_UP_OFLOW 0x01000000 #define XL_RXSTAT_IPCKERR 0x02000000 /* 3c905B only */ #define XL_RXSTAT_TCPCKERR 0x04000000 /* 3c905B only */ #define XL_RXSTAT_UDPCKERR 0x08000000 /* 3c905B only */ #define XL_RXSTAT_BUFEN 0x10000000 /* 3c905B only */ #define XL_RXSTAT_IPCKOK 0x20000000 /* 3c905B only */ #define XL_RXSTAT_TCPCOK 0x40000000 /* 3c905B only */ #define XL_RXSTAT_UDPCKOK 0x80000000 /* 3c905B only */ #define XL_TXSTAT_LENMASK 0x00001FFF #define XL_TXSTAT_CRCDIS 0x00002000 #define XL_TXSTAT_TX_INTR 0x00008000 #define XL_TXSTAT_DL_COMPLETE 0x00010000 #define XL_TXSTAT_IPCKSUM 0x02000000 /* 3c905B only */ #define XL_TXSTAT_TCPCKSUM 0x04000000 /* 3c905B only */ #define XL_TXSTAT_UDPCKSUM 0x08000000 /* 3c905B only */ #define XL_TXSTAT_RND_DEFEAT 0x10000000 /* 3c905B only */ #define XL_TXSTAT_EMPTY 0x20000000 /* 3c905B only */ #define XL_TXSTAT_DL_INTR 0x80000000 #define XL_FLAG_FUNCREG 0x0001 #define XL_FLAG_PHYOK 0x0002 #define XL_FLAG_EEPROM_OFFSET_30 0x0004 #define XL_FLAG_WEIRDRESET 0x0008 #define XL_FLAG_8BITROM 0x0010 #define XL_FLAG_INVERT_LED_PWR 0x0020 #define XL_FLAG_INVERT_MII_PWR 0x0040 #define XL_FLAG_NO_XCVR_PWR 0x0080 #define XL_FLAG_USE_MMIO 0x0100 #define XL_EE_READ 0x0080 /* read, 5 bit address */ #define XL_EE_WRITE 0x0040 /* write, 5 bit address */ #define XL_EE_ERASE 0x00c0 /* erase, 5 bit address */ #define XL_EE_EWEN 0x0030 /* erase, no data needed */ #define XL_EE_8BIT_READ 0x0200 /* read, 8 bit address */ #define XL_EE_BUSY 0x8000 #define XL_EE_EADDR0 0x00 /* station address, first word */ #define XL_EE_EADDR1 0x01 /* station address, next word, */ #define XL_EE_EADDR2 0x02 /* station address, last word */ #define XL_EE_PRODID 0x03 /* product ID code */ #define XL_EE_MDATA_DATE 0x04 /* manufacturing data, date */ #define XL_EE_MDATA_DIV 0x05 /* manufacturing data, division */ #define XL_EE_MDATA_PCODE 0x06 /* manufacturing data, product code */ #define XL_EE_MFG_ID 0x07 #define XL_EE_PCI_PARM 0x08 #define XL_EE_ROM_ONFO 0x09 #define XL_EE_OEM_ADR0 0x0A #define XL_EE_OEM_ADR1 0x0B #define XL_EE_OEM_ADR2 0x0C #define XL_EE_SOFTINFO1 0x0D #define XL_EE_COMPAT 0x0E #define XL_EE_SOFTINFO2 0x0F #define XL_EE_CAPS 0x10 /* capabilities word */ #define XL_EE_RSVD0 0x11 #define XL_EE_ICFG_0 0x12 #define XL_EE_ICFG_1 0x13 #define XL_EE_RSVD1 0x14 #define XL_EE_SOFTINFO3 0x15 #define XL_EE_RSVD_2 0x16 /* * Bits in the capabilities word */ #define XL_CAPS_PNP 0x0001 #define XL_CAPS_FULL_DUPLEX 0x0002 #define XL_CAPS_LARGE_PKTS 0x0004 #define XL_CAPS_SLAVE_DMA 0x0008 #define XL_CAPS_SECOND_DMA 0x0010 #define XL_CAPS_FULL_BM 0x0020 #define XL_CAPS_FRAG_BM 0x0040 #define XL_CAPS_CRC_PASSTHRU 0x0080 #define XL_CAPS_TXDONE 0x0100 #define XL_CAPS_NO_TXLENGTH 0x0200 #define XL_CAPS_RX_REPEAT 0x0400 #define XL_CAPS_SNOOPING 0x0800 #define XL_CAPS_100MBPS 0x1000 #define XL_CAPS_PWRMGMT 0x2000 /* * Window 0 registers */ #define XL_W0_EE_DATA 0x0C #define XL_W0_EE_CMD 0x0A #define XL_W0_RSRC_CFG 0x08 #define XL_W0_ADDR_CFG 0x06 #define XL_W0_CFG_CTRL 0x04 #define XL_W0_PROD_ID 0x02 #define XL_W0_MFG_ID 0x00 /* * Window 1 */ #define XL_W1_TX_FIFO 0x10 #define XL_W1_FREE_TX 0x0C #define XL_W1_TX_STATUS 0x0B #define XL_W1_TX_TIMER 0x0A #define XL_W1_RX_STATUS 0x08 #define XL_W1_RX_FIFO 0x00 /* * RX status codes */ #define XL_RXSTATUS_OVERRUN 0x01 #define XL_RXSTATUS_RUNT 0x02 #define XL_RXSTATUS_ALIGN 0x04 #define XL_RXSTATUS_CRC 0x08 #define XL_RXSTATUS_OVERSIZE 0x10 #define XL_RXSTATUS_DRIBBLE 0x20 /* * TX status codes */ #define XL_TXSTATUS_RECLAIM 0x02 /* 3c905B only */ #define XL_TXSTATUS_OVERFLOW 0x04 #define XL_TXSTATUS_MAXCOLS 0x08 #define XL_TXSTATUS_UNDERRUN 0x10 #define XL_TXSTATUS_JABBER 0x20 #define XL_TXSTATUS_INTREQ 0x40 #define XL_TXSTATUS_COMPLETE 0x80 /* * Window 2 */ #define XL_W2_RESET_OPTIONS 0x0C /* 3c905B only */ #define XL_W2_STATION_MASK_HI 0x0A #define XL_W2_STATION_MASK_MID 0x08 #define XL_W2_STATION_MASK_LO 0x06 #define XL_W2_STATION_ADDR_HI 0x04 #define XL_W2_STATION_ADDR_MID 0x02 #define XL_W2_STATION_ADDR_LO 0x00 #define XL_RESETOPT_FEATUREMASK 0x0001|0x0002|0x004 #define XL_RESETOPT_D3RESETDIS 0x0008 #define XL_RESETOPT_DISADVFD 0x0010 #define XL_RESETOPT_DISADV100 0x0020 #define XL_RESETOPT_DISAUTONEG 0x0040 #define XL_RESETOPT_DEBUGMODE 0x0080 #define XL_RESETOPT_FASTAUTO 0x0100 #define XL_RESETOPT_FASTEE 0x0200 #define XL_RESETOPT_FORCEDCONF 0x0400 #define XL_RESETOPT_TESTPDTPDR 0x0800 #define XL_RESETOPT_TEST100TX 0x1000 #define XL_RESETOPT_TEST100RX 0x2000 #define XL_RESETOPT_INVERT_LED 0x0010 #define XL_RESETOPT_INVERT_MII 0x4000 /* * Window 3 (fifo management) */ #define XL_W3_INTERNAL_CFG 0x00 #define XL_W3_MAXPKTSIZE 0x04 /* 3c905B only */ #define XL_W3_RESET_OPT 0x08 #define XL_W3_FREE_TX 0x0C #define XL_W3_FREE_RX 0x0A #define XL_W3_MAC_CTRL 0x06 #define XL_ICFG_CONNECTOR_MASK 0x00F00000 #define XL_ICFG_CONNECTOR_BITS 20 #define XL_ICFG_RAMSIZE_MASK 0x00000007 #define XL_ICFG_RAMWIDTH 0x00000008 #define XL_ICFG_ROMSIZE_MASK (0x00000040|0x00000080) #define XL_ICFG_DISABLE_BASSD 0x00000100 #define XL_ICFG_RAMLOC 0x00000200 #define XL_ICFG_RAMPART (0x00010000|0x00020000) #define XL_ICFG_XCVRSEL (0x00100000|0x00200000|0x00400000) #define XL_ICFG_AUTOSEL 0x01000000 #define XL_XCVR_10BT 0x00 #define XL_XCVR_AUI 0x01 #define XL_XCVR_RSVD_0 0x02 #define XL_XCVR_COAX 0x03 #define XL_XCVR_100BTX 0x04 #define XL_XCVR_100BFX 0x05 #define XL_XCVR_MII 0x06 #define XL_XCVR_RSVD_1 0x07 #define XL_XCVR_AUTO 0x08 /* 3c905B only */ #define XL_MACCTRL_DEFER_EXT_END 0x0001 #define XL_MACCTRL_DEFER_0 0x0002 #define XL_MACCTRL_DEFER_1 0x0004 #define XL_MACCTRL_DEFER_2 0x0008 #define XL_MACCTRL_DEFER_3 0x0010 #define XL_MACCTRL_DUPLEX 0x0020 #define XL_MACCTRL_ALLOW_LARGE_PACK 0x0040 #define XL_MACCTRL_EXTEND_AFTER_COL 0x0080 (3c905B only) #define XL_MACCTRL_FLOW_CONTROL_ENB 0x0100 (3c905B only) #define XL_MACCTRL_VLT_END 0x0200 (3c905B only) /* * The 'reset options' register contains power-on reset values * loaded from the EEPROM. This includes the supported media * types on the card. It is also known as the media options register. */ #define XL_W3_MEDIA_OPT 0x08 #define XL_MEDIAOPT_BT4 0x0001 /* MII */ #define XL_MEDIAOPT_BTX 0x0002 /* on-chip */ #define XL_MEDIAOPT_BFX 0x0004 /* on-chip */ #define XL_MEDIAOPT_BT 0x0008 /* on-chip */ #define XL_MEDIAOPT_BNC 0x0010 /* on-chip */ #define XL_MEDIAOPT_AUI 0x0020 /* on-chip */ #define XL_MEDIAOPT_MII 0x0040 /* MII */ #define XL_MEDIAOPT_VCO 0x0100 /* 1st gen chip only */ #define XL_MEDIAOPT_10FL 0x0100 /* 3x905B only, on-chip */ #define XL_MEDIAOPT_MASK 0x01FF /* * Window 4 (diagnostics) */ #define XL_W4_UPPERBYTESOK 0x0D #define XL_W4_BADSSD 0x0C #define XL_W4_MEDIA_STATUS 0x0A #define XL_W4_PHY_MGMT 0x08 #define XL_W4_NET_DIAG 0x06 #define XL_W4_FIFO_DIAG 0x04 #define XL_W4_VCO_DIAG 0x02 #define XL_W4_CTRLR_STAT 0x08 #define XL_W4_TX_DIAG 0x00 #define XL_MII_CLK 0x01 #define XL_MII_DATA 0x02 #define XL_MII_DIR 0x04 #define XL_MEDIA_SQE 0x0008 #define XL_MEDIA_10TP 0x00C0 #define XL_MEDIA_LNK 0x0080 #define XL_MEDIA_LNKBEAT 0x0800 #define XL_MEDIASTAT_CRCSTRIP 0x0004 #define XL_MEDIASTAT_SQEENB 0x0008 #define XL_MEDIASTAT_COLDET 0x0010 #define XL_MEDIASTAT_CARRIER 0x0020 #define XL_MEDIASTAT_JABGUARD 0x0040 #define XL_MEDIASTAT_LINKBEAT 0x0080 #define XL_MEDIASTAT_JABDETECT 0x0200 #define XL_MEDIASTAT_POLREVERS 0x0400 #define XL_MEDIASTAT_LINKDETECT 0x0800 #define XL_MEDIASTAT_TXINPROG 0x1000 #define XL_MEDIASTAT_DCENB 0x4000 #define XL_MEDIASTAT_AUIDIS 0x8000 #define XL_NETDIAG_TEST_LOWVOLT 0x0001 #define XL_NETDIAG_ASIC_REVMASK (0x0002|0x0004|0x0008|0x0010|0x0020) #define XL_NETDIAG_UPPER_BYTES_ENABLE 0x0040 #define XL_NETDIAG_STATS_ENABLED 0x0080 #define XL_NETDIAG_TX_FATALERR 0x0100 #define XL_NETDIAG_TRANSMITTING 0x0200 #define XL_NETDIAG_RX_ENABLED 0x0400 #define XL_NETDIAG_TX_ENABLED 0x0800 #define XL_NETDIAG_FIFO_LOOPBACK 0x1000 #define XL_NETDIAG_MAC_LOOPBACK 0x2000 #define XL_NETDIAG_ENDEC_LOOPBACK 0x4000 #define XL_NETDIAG_EXTERNAL_LOOP 0x8000 /* * Window 5 */ #define XL_W5_STAT_ENB 0x0C #define XL_W5_INTR_ENB 0x0A #define XL_W5_RECLAIM_THRESH 0x09 /* 3c905B only */ #define XL_W5_RX_FILTER 0x08 #define XL_W5_RX_EARLYTHRESH 0x06 #define XL_W5_TX_AVAILTHRESH 0x02 #define XL_W5_TX_STARTTHRESH 0x00 /* * RX filter bits */ #define XL_RXFILTER_INDIVIDUAL 0x01 #define XL_RXFILTER_ALLMULTI 0x02 #define XL_RXFILTER_BROADCAST 0x04 #define XL_RXFILTER_ALLFRAMES 0x08 #define XL_RXFILTER_MULTIHASH 0x10 /* 3c905B only */ /* * Window 6 (stats) */ #define XL_W6_TX_BYTES_OK 0x0C #define XL_W6_RX_BYTES_OK 0x0A #define XL_W6_UPPER_FRAMES_OK 0x09 #define XL_W6_DEFERRED 0x08 #define XL_W6_RX_OK 0x07 #define XL_W6_TX_OK 0x06 #define XL_W6_RX_OVERRUN 0x05 #define XL_W6_COL_LATE 0x04 #define XL_W6_COL_SINGLE 0x03 #define XL_W6_COL_MULTIPLE 0x02 #define XL_W6_SQE_ERRORS 0x01 #define XL_W6_CARRIER_LOST 0x00 /* * Window 7 (bus master control) */ #define XL_W7_BM_ADDR 0x00 #define XL_W7_BM_LEN 0x06 #define XL_W7_BM_STATUS 0x0B #define XL_W7_BM_TIMEr 0x0A /* * bus master control registers */ #define XL_BM_PKTSTAT 0x20 #define XL_BM_DOWNLISTPTR 0x24 #define XL_BM_FRAGADDR 0x28 #define XL_BM_FRAGLEN 0x2C #define XL_BM_TXFREETHRESH 0x2F #define XL_BM_UPPKTSTAT 0x30 #define XL_BM_UPLISTPTR 0x38 struct xl_mii_frame { u_int8_t mii_stdelim; u_int8_t mii_opcode; u_int8_t mii_phyaddr; u_int8_t mii_regaddr; u_int8_t mii_turnaround; u_int16_t mii_data; }; /* * MII constants */ #define XL_MII_STARTDELIM 0x01 #define XL_MII_READOP 0x02 #define XL_MII_WRITEOP 0x01 #define XL_MII_TURNAROUND 0x02 /* * The 3C905B adapters implement a few features that we want to * take advantage of, namely the multicast hash filter. With older * chips, you only have the option of turning on reception of all * multicast frames, which is kind of lame. * * We also use this to decide on a transmit strategy. For the 3c90xB * cards, we can use polled descriptor mode, which reduces CPU overhead. */ #define XL_TYPE_905B 1 #define XL_TYPE_90X 2 #define XL_FLAG_FUNCREG 0x0001 #define XL_FLAG_PHYOK 0x0002 #define XL_FLAG_EEPROM_OFFSET_30 0x0004 #define XL_FLAG_WEIRDRESET 0x0008 #define XL_FLAG_8BITROM 0x0010 #define XL_FLAG_INVERT_LED_PWR 0x0020 #define XL_FLAG_INVERT_MII_PWR 0x0040 #define XL_FLAG_NO_XCVR_PWR 0x0080 #define XL_FLAG_USE_MMIO 0x0100 #define XL_NO_XCVR_PWR_MAGICBITS 0x0900 #define XL_MIN_FRAMELEN 60 #define XL_LAST_FRAG 0x80000000 struct xl_stats { /* accumulated stats */ u_int16_t xl_carrier_lost; u_int16_t xl_sqe_errs; u_int16_t xl_tx_multi_collision; u_int16_t xl_tx_single_collision; u_int16_t xl_tx_late_collision; u_int16_t xl_rx_overrun; u_int16_t xl_tx_deferred; u_int32_t xl_rx_bytes_ok; u_int32_t xl_tx_bytes_ok; u_int32_t xl_tx_frames_ok; u_int32_t xl_rx_frames_ok; u_int16_t xl_badssd; /* non-accumulated stats */ u_int16_t intstatus; u_int16_t rxstatus; u_int8_t txstatus; u_int16_t mediastatus; u_int32_t txcomplete_ints; u_int16_t miianr, miipar, miistatus, miicmd; u_int32_t device_interrupts; u_int32_t internalconfig; u_int16_t mac_control; u_int16_t smbstatus; u_int32_t dmactl; u_int16_t txfree; }; struct xl_type { u_int16_t xl_vid; u_int16_t xl_did; char *xl_name; }; /* * Various supported device vendors/types and their names. */ static struct xl_type xl_devs[] = { { TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT, "3Com 3c900-TPO Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT_COMBO, "3Com 3c900-COMBO Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_BOOMERANG_10_100BT, "3Com 3c905-TX Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_BOOMERANG_100BT4, "3Com 3c905-T4 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT, "3Com 3c900B-TPO Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_COMBO, "3Com 3c900B-COMBO Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_TPC, "3Com 3c900B-TPC Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10FL, "3Com 3c900B-FL Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT, "3Com 3c905B-TX Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100BT4, "3Com 3c905B-T4 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100FX, "3Com 3c905B-FX/SC Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100_COMBO, "3Com 3c905B-COMBO Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT, "3Com 3c905C-TX Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_920B, "3Com 3c920B-EMB Integrated Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT_SERV, "3Com 3c980 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_SERV, "3Com 3c980C Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_SOHO100TX, "3Com 3cSOHO100-TX OfficeConnect" }, { TC_VENDORID, TC_DEVICEID_TORNADO_HOMECONNECT, "3Com 3c450-TX HomeConnect" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_555, "3Com 3c555 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_556, "3Com 3c556 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_556B, "3Com 3c556B Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_575A, "3Com 3c575TX Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_575B, "3Com 3c575B Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_575C, "3Com 3c575C Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_656, "3Com 3c656 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_656B, "3Com 3c656B Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_TORNADO_656C, "3Com 3c656C Fast Etherlink XL" }, { 0, 0, NULL } }; #define XL_TIMEOUT 1000 /* rx message descriptor entry, ensure the struct is aligned to 8 bytes */ struct RXMD { /* used by hardware */ volatile uint32_t next; volatile uint32_t status; volatile uint32_t addr; volatile uint32_t length; /* used by software */ struct mbuf *mbuf; /* scratch variable used in the tx ring */ struct RXMD *next_md; } __attribute__ ((aligned (8), packed)); #define NUM_FRAGS 6 /* * tx message descriptor entry, ensure the struct is aligned to 8 bytes */ struct tfrag { volatile uint32_t addr; volatile uint32_t length; } __attribute__ ((packed)); struct TXMD { /* used by hardware */ volatile uint32_t next; volatile uint32_t status; struct tfrag txfrags[NUM_FRAGS]; /* used by software */ struct mbuf *mbuf; /* scratch variable used in the tx ring */ struct TXMD *next_md, *chainptr; } __attribute__ ((aligned (8), packed)); #define NUM_CHAIN_LENGTHS 50 /* * Per-device data */ struct elnk_softc { struct arpcom arpcom; rtems_irq_connect_data irqInfo; rtems_event_set ioevent; unsigned int ioaddr; unsigned char *bufferBase, *ringBase; struct RXMD *rx_ring, *curr_rx_md; struct TXMD *tx_ring, *last_tx_md, *last_txchain_head; rtems_id stat_timer_id; uint32_t stats_update_ticks; struct xl_stats xl_stats; u_int8_t xl_unit; /* interface number */ u_int8_t xl_type; int xl_flags; u_int16_t xl_media; u_int16_t xl_caps; u_int32_t xl_xcvr; u_int8_t xl_stats_no_timeout; u_int16_t xl_tx_thresh; int tx_idle; short chain_lengths[NUM_CHAIN_LENGTHS]; int chlenIndex; unsigned short vendorID, deviceID; int acceptBroadcast; int numTxbuffers, numRxbuffers; }; static struct elnk_softc elnk_softc[NUM_UNITS]; static rtems_id rxDaemonTid; static rtems_id txDaemonTid; static rtems_id chainRecoveryQueue; #if defined(__i386__) #define CSR_WRITE_4(sc, reg, val) i386_outport_long( sc->ioaddr + reg, val ) #define CSR_WRITE_2(sc, reg, val) i386_outport_word( sc->ioaddr + reg, val ) #define CSR_WRITE_1(sc, reg, val) i386_outport_byte( sc->ioaddr + reg, val ) inline unsigned int CSR_READ_4( struct elnk_softc *sc, int reg) { unsigned int myval; i386_inport_long( sc->ioaddr + reg, myval ); return myval; } inline unsigned short CSR_READ_2( struct elnk_softc *sc, int reg) { unsigned short myval; i386_inport_word( sc->ioaddr + reg, myval ); return myval; } inline unsigned char CSR_READ_1( struct elnk_softc *sc, int reg) { unsigned char myval; i386_inport_byte( sc->ioaddr + reg, myval ); return myval; } #endif #if defined(__PPC__) #define CSR_WRITE_4(sc, reg, val) outl( val, sc->ioaddr + reg) #define CSR_WRITE_2(sc, reg, val) outw( val, sc->ioaddr + reg) #define CSR_WRITE_1(sc, reg, val) outb( val, sc->ioaddr + reg) #define CSR_READ_4(sc, reg) inl(sc->ioaddr + reg) #define CSR_READ_2(sc, reg) inw(sc->ioaddr + reg) #define CSR_READ_1(sc, reg) inb(sc->ioaddr + reg) #endif #define XL_SEL_WIN(x) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_WINSEL | x) /* * Murphy's law says that it's possible the chip can wedge and * the 'command in progress' bit may never clear. Hence, we wait * only a finite amount of time to avoid getting caught in an * infinite loop. Normally this delay routine would be a macro, * but it isn't called during normal operation so we can afford * to make it a function. */ static void xl_wait(struct elnk_softc *sc) { register int i; for(i = 0; i < XL_TIMEOUT; i++) { if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY)) break; } if (i == XL_TIMEOUT) printk("etherlink : unit elnk%d command never completed\n", sc->xl_unit ); return; } /* * MII access routines are provided for adapters with external * PHYs (3c905-TX, 3c905-T4, 3c905B-T4) and those with built-in * autoneg logic that's faked up to look like a PHY (3c905B-TX). * Note: if you don't perform the MDIO operations just right, * it's possible to end up with code that works correctly with * some chips/CPUs/processor speeds/bus speeds/etc but not * with others. */ #define MII_SET(x) \ CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \ CSR_READ_2(sc, XL_W4_PHY_MGMT) | (x)) #define MII_CLR(x) \ CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \ CSR_READ_2(sc, XL_W4_PHY_MGMT) & ~(x)) /* * Sync the PHYs by setting data bit and strobing the clock 32 times. */ static void xl_mii_sync( struct elnk_softc *sc) { register int i; XL_SEL_WIN(4); MII_SET(XL_MII_DIR|XL_MII_DATA); for (i = 0; i < 32; i++) { MII_SET(XL_MII_CLK); MII_SET(XL_MII_DATA); MII_CLR(XL_MII_CLK); MII_SET(XL_MII_DATA); } return; } /* * Clock a series of bits through the MII. */ static void xl_mii_send( struct elnk_softc *sc, u_int32_t bits, int cnt ) { int i; XL_SEL_WIN(4); MII_CLR(XL_MII_CLK); for (i = (0x1 << (cnt - 1)); i; i >>= 1) { if (bits & i) { MII_SET(XL_MII_DATA); } else { MII_CLR(XL_MII_DATA); } MII_CLR(XL_MII_CLK); MII_SET(XL_MII_CLK); } } /* * Read an PHY register through the MII. */ static int xl_mii_readreg( struct elnk_softc *sc, struct xl_mii_frame *frame ) { int i, ack; /* * Set up frame for RX. */ frame->mii_stdelim = XL_MII_STARTDELIM; frame->mii_opcode = XL_MII_READOP; frame->mii_turnaround = 0; frame->mii_data = 0; /* * Select register window 4. */ XL_SEL_WIN(4); CSR_WRITE_2(sc, XL_W4_PHY_MGMT, 0); /* * Turn on data xmit. */ MII_SET(XL_MII_DIR); xl_mii_sync(sc); /* * Send command/address info. */ xl_mii_send(sc, frame->mii_stdelim, 2); xl_mii_send(sc, frame->mii_opcode, 2); xl_mii_send(sc, frame->mii_phyaddr, 5); xl_mii_send(sc, frame->mii_regaddr, 5); /* Idle bit */ MII_CLR((XL_MII_CLK|XL_MII_DATA)); MII_SET(XL_MII_CLK); /* Turn off xmit. */ MII_CLR(XL_MII_DIR); /* Check for ack */ MII_CLR(XL_MII_CLK); ack = CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA; MII_SET(XL_MII_CLK); /* * Now try reading data bits. If the ack failed, we still * need to clock through 16 cycles to keep the PHY(s) in sync. */ if (ack) { for(i = 0; i < 16; i++) { MII_CLR(XL_MII_CLK); MII_SET(XL_MII_CLK); } goto fail; } for (i = 0x8000; i; i >>= 1) { MII_CLR(XL_MII_CLK); if (!ack) { if (CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA) frame->mii_data |= i; } MII_SET(XL_MII_CLK); } fail: MII_CLR(XL_MII_CLK); MII_SET(XL_MII_CLK); if (ack) return(1); return(0); } /* * Write to a PHY register through the MII. */ static int xl_mii_writereg( struct elnk_softc *sc, struct xl_mii_frame *frame ) { /* * Set up frame for TX. */ frame->mii_stdelim = XL_MII_STARTDELIM; frame->mii_opcode = XL_MII_WRITEOP; frame->mii_turnaround = XL_MII_TURNAROUND; /* * Select the window 4. */ XL_SEL_WIN(4); /* * Turn on data output. */ MII_SET(XL_MII_DIR); xl_mii_sync(sc); xl_mii_send(sc, frame->mii_stdelim, 2); xl_mii_send(sc, frame->mii_opcode, 2); xl_mii_send(sc, frame->mii_phyaddr, 5); xl_mii_send(sc, frame->mii_regaddr, 5); xl_mii_send(sc, frame->mii_turnaround, 2); xl_mii_send(sc, frame->mii_data, 16); /* Idle bit. */ MII_SET(XL_MII_CLK); MII_CLR(XL_MII_CLK); /* * Turn off xmit. */ MII_CLR(XL_MII_DIR); return(0); } static int xl_miibus_readreg( struct elnk_softc *sc, int phy, int reg ) { struct xl_mii_frame frame; /* * Pretend that PHYs are only available at MII address 24. * This is to guard against problems with certain 3Com ASIC * revisions that incorrectly map the internal transceiver * control registers at all MII addresses. This can cause * the miibus code to attach the same PHY several times over. */ if ((!(sc->xl_flags & XL_FLAG_PHYOK)) && phy != 24) { printk("etherlink : unit elnk%d xl_miibus_readreg returned\n", sc->xl_unit); return(0); } memset((char *)&frame, 0, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; xl_mii_readreg(sc, &frame); return(frame.mii_data); } static int xl_miibus_writereg( struct elnk_softc *sc, int phy, int reg, int data ) { struct xl_mii_frame frame; if ((!(sc->xl_flags & XL_FLAG_PHYOK)) && phy != 24) { printk("etherlink : unit elnk%d xl_miibus_writereg returned\n", sc->xl_unit); return(0); } memset((char *)&frame, 0, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; frame.mii_data = data; xl_mii_writereg(sc, &frame); return(0); } /* * The EEPROM is slow: give it time to come ready after issuing * it a command. */ static int xl_eeprom_wait(struct elnk_softc *sc) { int i; for (i = 0; i < 100; i++) { if (CSR_READ_2(sc, XL_W0_EE_CMD) & XL_EE_BUSY) DELAY(162); else break; } if (i == 100) { printk("etherlink : unit elnk%d eeprom failed to come ready\n", sc->xl_unit); return(1); } return(0); } /* * Read a sequence of words from the EEPROM. Note that ethernet address * data is stored in the EEPROM in network byte order. */ static int xl_read_eeprom( struct elnk_softc *sc, caddr_t dest, int off, int cnt, int swap) { int err = 0, i; u_int16_t word = 0, *ptr; #define EEPROM_5BIT_OFFSET(A) ((((A) << 2) & 0x7F00) | ((A) & 0x003F)) #define EEPROM_8BIT_OFFSET(A) ((A) & 0x003F) /* WARNING! DANGER! * It's easy to accidentally overwrite the rom content! * Note: the 3c575 uses 8bit EEPROM offsets. */ XL_SEL_WIN(0); if (xl_eeprom_wait(sc)) return(1); if (sc->xl_flags & XL_FLAG_EEPROM_OFFSET_30) off += 0x30; for (i = 0; i < cnt; i++) { if (sc->xl_flags & XL_FLAG_8BITROM) CSR_WRITE_2(sc, XL_W0_EE_CMD, XL_EE_8BIT_READ | EEPROM_8BIT_OFFSET(off + i)); else CSR_WRITE_2(sc, XL_W0_EE_CMD, XL_EE_READ | EEPROM_5BIT_OFFSET(off + i)); err = xl_eeprom_wait(sc); if (err) break; word = CSR_READ_2(sc, XL_W0_EE_DATA); ptr = (u_int16_t*)(dest + (i * 2)); if (swap) *ptr = ntohs(word); else *ptr = word; } return(err ? 1 : 0); } static void xl_stats_update( rtems_id timerid, void *xsc) { struct elnk_softc *sc = (struct elnk_softc *)xsc; struct ifnet *ifp = &sc->arpcom.ac_if; u_int32_t t1; sc->xl_stats.intstatus = CSR_READ_2(sc, XL_STATUS); sc->xl_stats.miianr = xl_miibus_readreg(sc, 0x18, MII_ANAR ); sc->xl_stats.miipar = xl_miibus_readreg(sc, 0x18, MII_ANLPAR ); sc->xl_stats.miistatus = xl_miibus_readreg(sc, 0x18, MII_BMSR ); sc->xl_stats.miicmd = xl_miibus_readreg(sc, 0x18, MII_BMCR ); XL_SEL_WIN(1); sc->xl_stats.rxstatus = CSR_READ_2(sc, XL_W1_RX_STATUS ); sc->xl_stats.txstatus = CSR_READ_1(sc, XL_W1_TX_STATUS ); sc->xl_stats.smbstatus = CSR_READ_2(sc, 2 ); XL_SEL_WIN(3); sc->xl_stats.internalconfig = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); sc->xl_stats.mac_control = CSR_READ_2(sc, XL_W3_MAC_CTRL); sc->xl_stats.txfree = CSR_READ_2(sc, XL_W3_FREE_TX ); /* Read all the stats registers. */ XL_SEL_WIN(6); sc->xl_stats.xl_carrier_lost += CSR_READ_1(sc, XL_W6_CARRIER_LOST); sc->xl_stats.xl_sqe_errs += CSR_READ_1(sc, XL_W6_SQE_ERRORS); sc->xl_stats.xl_tx_multi_collision += CSR_READ_1(sc, XL_W6_COL_MULTIPLE); sc->xl_stats.xl_tx_single_collision += CSR_READ_1(sc, XL_W6_COL_SINGLE); sc->xl_stats.xl_tx_late_collision += CSR_READ_1(sc, XL_W6_COL_LATE); sc->xl_stats.xl_rx_overrun += CSR_READ_1(sc, XL_W6_RX_OVERRUN); sc->xl_stats.xl_tx_deferred += CSR_READ_1(sc, XL_W6_DEFERRED); sc->xl_stats.xl_tx_frames_ok += CSR_READ_1(sc, XL_W6_TX_OK); sc->xl_stats.xl_rx_frames_ok += CSR_READ_1(sc, XL_W6_RX_OK); sc->xl_stats.xl_rx_bytes_ok += CSR_READ_2(sc, XL_W6_TX_BYTES_OK ); sc->xl_stats.xl_tx_bytes_ok += CSR_READ_2(sc, XL_W6_RX_BYTES_OK ); t1 = CSR_READ_1(sc, XL_W6_UPPER_FRAMES_OK); sc->xl_stats.xl_rx_frames_ok += ((t1 & 0x3) << 8); sc->xl_stats.xl_tx_frames_ok += (((t1 >> 4) & 0x3) << 8); ifp->if_ierrors += sc->xl_stats.xl_rx_overrun; ifp->if_collisions += sc->xl_stats.xl_tx_multi_collision + sc->xl_stats.xl_tx_single_collision + sc->xl_stats.xl_tx_late_collision; /* * Boomerang and cyclone chips have an extra stats counter * in window 4 (BadSSD). We have to read this too in order * to clear out all the stats registers and avoid a statsoflow * interrupt. */ XL_SEL_WIN(4); t1 = CSR_READ_1(sc, XL_W4_UPPERBYTESOK); sc->xl_stats.xl_rx_bytes_ok += ((t1 & 0xf) << 16); sc->xl_stats.xl_tx_bytes_ok += (((t1 >> 4) & 0xf) << 16); sc->xl_stats.xl_badssd += CSR_READ_1(sc, XL_W4_BADSSD); sc->xl_stats.mediastatus = CSR_READ_2(sc, XL_W4_MEDIA_STATUS ); sc->xl_stats.dmactl = CSR_READ_4(sc, XL_DMACTL ); XL_SEL_WIN(7); if (!sc->xl_stats_no_timeout) rtems_timer_fire_after( sc->stat_timer_id, sc->stats_update_ticks, xl_stats_update, (void *)sc ); return; } static void xl_reset(struct elnk_softc *sc) { register int i; XL_SEL_WIN(0); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET | ((sc->xl_flags & XL_FLAG_WEIRDRESET) ? XL_RESETOPT_DISADVFD:0)); for (i = 0; i < XL_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY)) break; } if (i == XL_TIMEOUT) printk("etherlink : unit elnk%d reset didn't complete\n", sc->xl_unit); /* Reset TX and RX. */ /* Note: the RX reset takes an absurd amount of time * on newer versions of the Tornado chips such as those * on the 3c905CX and newer 3c908C cards. We wait an * extra amount of time so that xl_wait() doesn't complain * and annoy the users. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); DELAY(100000); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); xl_wait(sc); if (sc->xl_flags & XL_FLAG_INVERT_LED_PWR || sc->xl_flags & XL_FLAG_INVERT_MII_PWR) { XL_SEL_WIN(2); CSR_WRITE_2(sc, XL_W2_RESET_OPTIONS, CSR_READ_2(sc, XL_W2_RESET_OPTIONS) | ((sc->xl_flags & XL_FLAG_INVERT_LED_PWR)?XL_RESETOPT_INVERT_LED:0) | ((sc->xl_flags & XL_FLAG_INVERT_MII_PWR)?XL_RESETOPT_INVERT_MII:0) ); } /* Wait a little while for the chip to get its brains in order. */ DELAY(100000); return; } static void xl_stop(struct elnk_softc *sc) { struct ifnet *ifp; ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; rtems_timer_cancel( sc->stat_timer_id ); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISCARD); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); DELAY(800); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_INTLATCH); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|0); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0); return; } static void xl_setcfg(struct elnk_softc *sc) { u_int32_t icfg; XL_SEL_WIN(3); icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); icfg &= ~XL_ICFG_CONNECTOR_MASK; if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BT4) icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS); if (sc->xl_media & XL_MEDIAOPT_BTX) icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS); CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); XL_SEL_WIN(7); return; } static void xl_setmode( struct elnk_softc *sc, int media) { u_int32_t icfg; u_int16_t mediastat; printk("etherlink : unit elnk%d selecting ", sc->xl_unit); XL_SEL_WIN(4); mediastat = CSR_READ_2(sc, XL_W4_MEDIA_STATUS); XL_SEL_WIN(3); icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); if (sc->xl_media & XL_MEDIAOPT_BT) { if (IFM_SUBTYPE(media) == IFM_10_T) { printk("10baseT transceiver, "); sc->xl_xcvr = XL_XCVR_10BT; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_10BT << XL_ICFG_CONNECTOR_BITS); mediastat |= XL_MEDIASTAT_LINKBEAT| XL_MEDIASTAT_JABGUARD; mediastat &= ~XL_MEDIASTAT_SQEENB; } } if (sc->xl_media & XL_MEDIAOPT_BFX) { if (IFM_SUBTYPE(media) == IFM_100_FX) { printk("100baseFX port, "); sc->xl_xcvr = XL_XCVR_100BFX; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS); mediastat |= XL_MEDIASTAT_LINKBEAT; mediastat &= ~XL_MEDIASTAT_SQEENB; } } if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) { if (IFM_SUBTYPE(media) == IFM_10_5) { printk("AUI port, "); sc->xl_xcvr = XL_XCVR_AUI; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS); mediastat &= ~(XL_MEDIASTAT_LINKBEAT| XL_MEDIASTAT_JABGUARD); mediastat |= ~XL_MEDIASTAT_SQEENB; } if (IFM_SUBTYPE(media) == IFM_10_FL) { printk("10baseFL transceiver, "); sc->xl_xcvr = XL_XCVR_AUI; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS); mediastat &= ~(XL_MEDIASTAT_LINKBEAT| XL_MEDIASTAT_JABGUARD); mediastat |= ~XL_MEDIASTAT_SQEENB; } } if (sc->xl_media & XL_MEDIAOPT_BNC) { if (IFM_SUBTYPE(media) == IFM_10_2) { printk("BNC port, "); sc->xl_xcvr = XL_XCVR_COAX; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_COAX << XL_ICFG_CONNECTOR_BITS); mediastat &= ~(XL_MEDIASTAT_LINKBEAT| XL_MEDIASTAT_JABGUARD| XL_MEDIASTAT_SQEENB); } } if ((media & IFM_GMASK) == IFM_FDX || IFM_SUBTYPE(media) == IFM_100_FX) { printk("full duplex\n"); XL_SEL_WIN(3); CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX); } else { printk("half duplex\n"); XL_SEL_WIN(3); CSR_WRITE_1(sc, XL_W3_MAC_CTRL, (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); } if (IFM_SUBTYPE(media) == IFM_10_2) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START); else CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); XL_SEL_WIN(4); CSR_WRITE_2(sc, XL_W4_MEDIA_STATUS, mediastat); DELAY(800); XL_SEL_WIN(7); return; } static void xl_choose_xcvr( struct elnk_softc *sc, int verbose) { u_int16_t devid; /* * Read the device ID from the EEPROM. * This is what's loaded into the PCI device ID register, so it has * to be correct otherwise we wouldn't have gotten this far. */ xl_read_eeprom(sc, (caddr_t)&devid, XL_EE_PRODID, 1, 0); switch(devid) { case TC_DEVICEID_BOOMERANG_10BT: /* 3c900-TPO */ case TC_DEVICEID_KRAKATOA_10BT: /* 3c900B-TPO */ sc->xl_media = XL_MEDIAOPT_BT; sc->xl_xcvr = XL_XCVR_10BT; if (verbose) printk("etherlink : unit elnk%d guessing 10BaseT " "transceiver\n", sc->xl_unit); break; case TC_DEVICEID_BOOMERANG_10BT_COMBO: /* 3c900-COMBO */ case TC_DEVICEID_KRAKATOA_10BT_COMBO: /* 3c900B-COMBO */ sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI; sc->xl_xcvr = XL_XCVR_10BT; if (verbose) printk("etherlink : unit elnk%d guessing COMBO " "(AUI/BNC/TP)\n", sc->xl_unit); break; case TC_DEVICEID_KRAKATOA_10BT_TPC: /* 3c900B-TPC */ sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC; sc->xl_xcvr = XL_XCVR_10BT; if (verbose) printk("etherlink : unit elnk%d guessing TPC (BNC/TP)\n", sc->xl_unit); break; case TC_DEVICEID_CYCLONE_10FL: /* 3c900B-FL */ sc->xl_media = XL_MEDIAOPT_10FL; sc->xl_xcvr = XL_XCVR_AUI; if (verbose) printk("etherlink : unit elnk%d guessing 10baseFL\n", sc->xl_unit); break; case TC_DEVICEID_BOOMERANG_10_100BT: /* 3c905-TX */ case TC_DEVICEID_HURRICANE_555: /* 3c555 */ case TC_DEVICEID_HURRICANE_556: /* 3c556 */ case TC_DEVICEID_HURRICANE_556B: /* 3c556B */ case TC_DEVICEID_HURRICANE_575A: /* 3c575TX */ case TC_DEVICEID_HURRICANE_575B: /* 3c575B */ case TC_DEVICEID_HURRICANE_575C: /* 3c575C */ case TC_DEVICEID_HURRICANE_656: /* 3c656 */ case TC_DEVICEID_HURRICANE_656B: /* 3c656B */ case TC_DEVICEID_TORNADO_656C: /* 3c656C */ case TC_DEVICEID_TORNADO_10_100BT_920B: /* 3c920B-EMB */ sc->xl_media = XL_MEDIAOPT_MII; sc->xl_xcvr = XL_XCVR_MII; if (verbose) printk("etherlink : unit elnk%d guessing MII\n", sc->xl_unit); break; case TC_DEVICEID_BOOMERANG_100BT4: /* 3c905-T4 */ case TC_DEVICEID_CYCLONE_10_100BT4: /* 3c905B-T4 */ sc->xl_media = XL_MEDIAOPT_BT4; sc->xl_xcvr = XL_XCVR_MII; if (verbose) printk("etherlink : unit elnk%d guessing 100BaseT4/MII\n", sc->xl_unit); break; case TC_DEVICEID_HURRICANE_10_100BT: /* 3c905B-TX */ case TC_DEVICEID_HURRICANE_10_100BT_SERV:/*3c980-TX */ case TC_DEVICEID_TORNADO_10_100BT_SERV: /* 3c980C-TX */ case TC_DEVICEID_HURRICANE_SOHO100TX: /* 3cSOHO100-TX */ case TC_DEVICEID_TORNADO_10_100BT: /* 3c905C-TX */ case TC_DEVICEID_TORNADO_HOMECONNECT: /* 3c450-TX */ sc->xl_media = XL_MEDIAOPT_BTX; sc->xl_xcvr = XL_XCVR_AUTO; if (verbose) printk("etherlink : unit elnk%d guessing 10/100 internal\n", sc->xl_unit); break; case TC_DEVICEID_CYCLONE_10_100_COMBO: /* 3c905B-COMBO */ sc->xl_media = XL_MEDIAOPT_BTX|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI; sc->xl_xcvr = XL_XCVR_AUTO; if (verbose) printk("etherlink : unit elnk%d guessing 10/100 " "plus BNC/AUI\n", sc->xl_unit); break; default: printk("etherlink : unit elnk%d unknown device ID: %x -- " "defaulting to 10baseT\n", sc->xl_unit, devid); sc->xl_media = XL_MEDIAOPT_BT; break; } return; } /* * This routine is a kludge to work around possible hardware faults * or manufacturing defects that can cause the media options register * (or reset options register, as it's called for the first generation * 3c90x adapters) to return an incorrect result. I have encountered * one Dell Latitude laptop docking station with an integrated 3c905-TX * which doesn't have any of the 'mediaopt' bits set. This screws up * the attach routine pretty badly because it doesn't know what media * to look for. If we find ourselves in this predicament, this routine * will try to guess the media options values and warn the user of a * possible manufacturing defect with his adapter/system/whatever. */ static void xl_mediacheck(struct elnk_softc *sc) { xl_choose_xcvr(sc, 1); /* * If some of the media options bits are set, assume they are * correct. If not, try to figure it out down below. * XXX I should check for 10baseFL, but I don't have an adapter * to test with. */ if (sc->xl_media & (XL_MEDIAOPT_MASK & ~XL_MEDIAOPT_VCO)) { /* * Check the XCVR value. If it's not in the normal range * of values, we need to fake it up here. */ if (sc->xl_xcvr <= XL_XCVR_AUTO) return; else { printk("etherlink : unit elnk%d bogus xcvr value " "in EEPROM (%" PRIx32 ")\n", sc->xl_unit, sc->xl_xcvr); printk("etherlink : unit elnk%d choosing new default based " "on card type\n", sc->xl_unit); } } else { if (sc->xl_type == XL_TYPE_905B && sc->xl_media & XL_MEDIAOPT_10FL) return; printk("etherlink : unit elnk%d WARNING: no media options bits set in " "the media options register!!\n", sc->xl_unit); printk("etherlink : unit elnk%d this could be a manufacturing defect in " "your adapter or system\n", sc->xl_unit); printk("etherlink : unit elnk%d attempting to guess media type; you " "should probably consult your vendor\n", sc->xl_unit); } return; } static void no_op(const rtems_irq_connect_data* irq) { return; } static void elnk_start_txchain( struct elnk_softc *sc, struct TXMD *chainhead ) { xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL); /* save the address of the TX list */ sc->last_txchain_head = chainhead; sc->tx_idle = 0; xl_wait(sc); CSR_WRITE_4(sc, XL_DOWNLIST_PTR, phys_to_bus( sc->last_txchain_head )); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); } /* * ELNK interrupt handler */ static rtems_isr elnk_interrupt_handler ( struct elnk_softc *sc ) { struct ifnet *ifp = &sc->arpcom.ac_if; u_int16_t status; while( ((status = CSR_READ_2(sc, XL_STATUS)) & XL_INTRS) && status != 0xFFFF) { sc->xl_stats.device_interrupts++; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK | (status & XL_INTRS)); #if 0 printk("etherlink : unit elnk%d intstatus %04x\n", sc->xl_unit, status ); #endif if (status & XL_STAT_UP_COMPLETE) { #if 0 printk("etherlink : unit elnk%d rx\n", sc->xl_unit ); #endif /* received packets */ rtems_bsdnet_event_send(rxDaemonTid, sc->ioevent); } if( (status & XL_STAT_DOWN_COMPLETE) || (status & XL_STAT_TX_COMPLETE) ) { /* all packets uploaded to the device */ struct TXMD *chaintailmd = NULL; if( status & XL_STAT_TX_COMPLETE ) { /* if we got a tx complete error, count it, then reset the transmitter. Consider the entire chain lost.. */ ifp->if_oerrors++; sc->xl_stats.txcomplete_ints++; printk("etherlink : unit elnk%d transmit error\n", sc->xl_unit ); /* reset, re-enable fifo */ xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET | 1 ); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); xl_wait(sc); } /* send the chain head to the tx task which will recover the whole chain */ rtems_message_queue_send( chainRecoveryQueue, &sc->last_txchain_head, sizeof(struct TXMD *)); /* set up the next chain */ if( sc->last_txchain_head->chainptr ) { /* check the head of the chain of packets we just finished, * if != 0, either this is a chain of 2 or more packets or * its a single packet chain and another chain is ready to * send. */ if( (int)sc->last_txchain_head->chainptr == -1 ) { /* ** single packet was sent so no indirection to the last ** entry in the chain. since chainptr is != 0, then ** another chain is ready starting from the packet AFTER ** the chain we just finished. - in this case the last ** chain's head == its tail */ chaintailmd = sc->last_txchain_head; } else { /* ** otherwise, this is a pointer to the last packet in the ** chain of 2 or more packets. If the chain's last ** packet's chainptr is != 0, then another chain is ready ** to send. */ chaintailmd = sc->last_txchain_head->chainptr; if( !chaintailmd->chainptr ) chaintailmd = NULL; } } if( chaintailmd ) { /* the next MD is the start of another chain */ elnk_start_txchain(sc, chaintailmd->next_md ); } else { /* otherwise nothing to send, so go idle */ sc->tx_idle = -1; /* wake up the tx daemon once so we're sure this last chain will be freed */ rtems_bsdnet_event_send( txDaemonTid, sc->ioevent ); #if 0 printk("unit elnk%d tx done\n", sc->xl_unit ); #endif } } if (status & XL_STAT_ADFAIL) { printk("etherlink : unit elnk%d Catastrophic bus failure\n", sc->xl_unit ); } if (status & XL_STAT_STATSOFLOW) { sc->xl_stats_no_timeout = 1; xl_stats_update(sc->stat_timer_id,sc); sc->xl_stats_no_timeout = 0; } } #if 0 { uint16_t intstatus, intenable, indenable; intstatus = CSR_READ_2(sc, XL_STATUS ); XL_SEL_WIN(5); intenable = CSR_READ_2(sc, XL_W5_INTR_ENB ); indenable = CSR_READ_2(sc, XL_W5_STAT_ENB ); XL_SEL_WIN(7); printk("etherlink : unit elnk%d istat %04x, ien %04x, ind %04x\n", sc->xl_unit, intstatus, intenable, indenable ); } #endif } static rtems_isr elnk_interrupt_handler_entry(void) { int i; /* ** Check all the initialized units for interrupt service */ for(i=0; i< NUM_UNITS; i++ ) { if( elnk_softc[i].ioaddr ) elnk_interrupt_handler( &elnk_softc[i] ); } } /* * Initialize the ethernet hardware */ static void elnk_initialize_hardware (struct elnk_softc *sc) { unsigned char *cp; int i, j, rxsize, txsize, ringsize; /* * Init RX ring */ cp = (unsigned char *)malloc( (ringsize = ((rxsize = (sc->numRxbuffers * sizeof(struct RXMD))) + (txsize = (sc->numTxbuffers * sizeof(struct TXMD)))) ) + + CPU_CACHE_ALIGNMENT_FOR_BUFFER); sc->bufferBase = cp; cp += (CPU_CACHE_ALIGNMENT_FOR_BUFFER - (int)cp) & (CPU_CACHE_ALIGNMENT_FOR_BUFFER - 1); #if defined(__i386__) #ifdef PCI_BRIDGE_DOES_NOT_ENSURE_CACHE_COHERENCY_FOR_DMA if (_CPU_is_paging_enabled()) _CPU_change_memory_mapping_attribute (NULL, cp, ringsize, PTE_CACHE_DISABLE | PTE_WRITABLE); #endif #endif sc->ringBase = cp; /* build tx and rx rings */ sc->rx_ring = (struct RXMD *)sc->ringBase; sc->tx_ring = (struct TXMD *)&sc->ringBase[ rxsize ]; { struct mbuf *m; struct RXMD *nxtmd; /* * The rx ring is easy as its just an array of RXMD structs. New * mbuf entries are allocated from the stack whenever the rx * daemon forwards an incoming packet into it. Here, we * pre-allocate the rx mbufs for the rx ring entries. */ for(i=0 ; inumRxbuffers; i++) { if( ((uint32_t)&sc->rx_ring[i] & 0x7) ) { rtems_panic ("etherlink : unit elnk%d rx ring entry %d not aligned to 8 bytes\n", sc->xl_unit, i ); } /* allocate an mbuf for each receive descriptor */ MGETHDR (m, M_WAIT, MT_DATA); MCLGET (m, M_WAIT); m->m_pkthdr.rcvif = &sc->arpcom.ac_if; if( i == sc->numRxbuffers-1 ) nxtmd = &sc->rx_ring[0]; else nxtmd = &sc->rx_ring[i+1]; sc->rx_ring[i].next_md = nxtmd; sc->rx_ring[i].mbuf = m; st_le32( &sc->rx_ring[i].status, 0); st_le32( &sc->rx_ring[i].next, (uint32_t)phys_to_bus( nxtmd )); st_le32( &sc->rx_ring[i].addr, (uint32_t)phys_to_bus( mtod(m, void *) )); st_le32( &sc->rx_ring[i].length, XL_LAST_FRAG | XL_PACKET_SIZE ); } sc->curr_rx_md = &sc->rx_ring[0]; } { struct TXMD *thismd, *nxtmd; /* * The tx ring is more complex. Each MD has an array of fragment * descriptors that are loaded from each packet as they arrive * from the stack. Each packet gets one ring entry, this allows * the lanboard to efficiently assemble the piecemeal packets into * a contiguous unit at transmit time, rather than spending * cputime concatenating them first. Although the next_md fields * form a ring, the DPD next is filled only when packets are added * to the tx chain, thus last entry of a series of packets has the * requisite dpd->next value == 0 to terminate the dma. mbuf * holds the packet info so it can be freed once the packet has * been sent. chainptr is used to link the head & tail of a chain * of 2 or more packets. A chain is formed when the tx daemon * gets 2 or more packets from the stack's queue in a service * period, so higher outgoing loads are handled as efficiently as * possible. */ for(i=0 ; inumTxbuffers; i++) { if( ((uint32_t)&sc->tx_ring[i] & 0x7) ) { rtems_panic ("etherlink : unit elnk%d tx ring entry %d not aligned to 8 bytes\n", sc->xl_unit, i ); } if( i == sc->numTxbuffers-1 ) nxtmd = &sc->tx_ring[0]; else nxtmd = &sc->tx_ring[i+1]; thismd = &sc->tx_ring[i]; thismd->next_md = nxtmd; thismd->chainptr = NULL; thismd->mbuf = NULL; st_le32( &thismd->status, XL_TXSTAT_DL_COMPLETE ); st_le32( &thismd->next, 0); for(j=0; j< NUM_FRAGS; j++) { st_le32( &thismd->txfrags[j].addr, 0 ); st_le32( &thismd->txfrags[j].length, 0 ); } } sc->last_tx_md = &sc->tx_ring[0]; } #ifdef ELNK_DEBUG printk("etherlink : %02x:%02x:%02x:%02x:%02x:%02x name 'elnk%d', io %x, int %d\n", sc->arpcom.ac_enaddr[0], sc->arpcom.ac_enaddr[1], sc->arpcom.ac_enaddr[2], sc->arpcom.ac_enaddr[3], sc->arpcom.ac_enaddr[4], sc->arpcom.ac_enaddr[5], sc->xl_unit, (unsigned)sc->ioaddr, sc->irqInfo.name ); #endif sc->irqInfo.hdl = (rtems_irq_hdl)elnk_interrupt_handler_entry; sc->irqInfo.on = no_op; sc->irqInfo.off = no_op; sc->irqInfo.isOn = NULL; if( sc->irqInfo.name != 255 ) { int st; #ifdef BSP_SHARED_HANDLER_SUPPORT st = BSP_install_rtems_shared_irq_handler( &sc->irqInfo ); #else st = BSP_install_rtems_irq_handler( &sc->irqInfo ); #endif if (!st) rtems_panic ("etherlink : unit elnk%d Interrupt name %d already in use\n", sc->xl_unit, sc->irqInfo.name ); } else { printk("etherlink : unit elnk%d Interrupt not specified by device\n", sc->xl_unit ); } } static void elnk_rxDaemon (void *arg) { struct elnk_softc *sc; struct ether_header *eh; struct mbuf *m; struct RXMD *rmd; unsigned int i,len, rxstat; rtems_event_set events; for (;;) { rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS, RTEMS_WAIT|RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &events); for(;;) { for(i=0; i< NUM_UNITS; i++ ) { sc = &elnk_softc[i]; if( sc->ioaddr ) { if( events & sc->ioevent ) { struct ifnet *ifp = &sc->arpcom.ac_if; rmd = sc->curr_rx_md; /* ** Read off all the packets we've received on this unit */ while( (rxstat = ld_le32(&rmd->status)) ) { if (rxstat & XL_RXSTAT_UP_ERROR) { printk("unit %i up error\n", sc->xl_unit ); ifp->if_ierrors++; } if( (rxstat & XL_RXSTAT_UP_CMPLT) ) { #if 0 { char *pkt, *delim; int i; pkt = mtod(rmd->mbuf, char *); printk("unit %i rx pkt (%08x) ", sc->xl_unit, pkt ); for(delim="", i=0; i < sizeof(struct ether_header)+8; i++, delim=":") printk("%s%02x", delim, (char) pkt[i] ); printk("\n"); } #endif /* pass on the packet in the mbuf */ len = ( ld_le32(&rmd->status) & XL_RXSTAT_LENMASK); m = rmd->mbuf; m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header); eh = mtod(m, struct ether_header *); m->m_data += sizeof(struct ether_header); ether_input(ifp, eh, m); /* get a new mbuf */ MGETHDR (m, M_WAIT, MT_DATA); MCLGET (m, M_WAIT); m->m_pkthdr.rcvif = ifp; rmd->mbuf = m; st_le32( &rmd->status, 0 ); st_le32( &rmd->addr, (uint32_t)phys_to_bus(mtod(m, void *)) ); } else { /* some kind of packet failure */ printk("etherlink : unit elnk%d bad receive status -- packet dropped\n", sc->xl_unit); ifp->if_ierrors++; } /* clear descriptor status */ rmd->status = 0; rmd = rmd->next_md; } sc->curr_rx_md = rmd; } } } /* ** If more events are pending, service them before we go back to sleep */ if( rtems_event_system_receive( RTEMS_ALL_EVENTS, RTEMS_NO_WAIT | RTEMS_EVENT_ANY, 0, &events ) == RTEMS_UNSATISFIED ) break; } } } /* * Driver transmit daemon */ static void elnk_txDaemon (void *arg) { struct elnk_softc *sc; struct ifnet *ifp; struct mbuf *m; struct TXMD *lastmd, *nextmd, *firstmd; int chainCount,i; rtems_event_set events; for (;;) { /* * Wait for any unit's signal to wake us up */ rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events); for(i=0; i< NUM_UNITS; i++ ) { sc = &elnk_softc[i]; if( sc->ioaddr ) { if( events & sc->ioevent ) { ifp = &sc->arpcom.ac_if; /* * Send packets till queue is empty or tx ring is full */ chainCount = 0; firstmd = NULL; lastmd = sc->last_tx_md; for(;;) { /* ** Check the chain recovery queue whenever the tx ** daemon services the stack. Note this routine does ** not assume the context of one of the lanboard units ** because used tx mbufs are no longer associated with ** any unit. */ { struct TXMD *chainhead, *chaintail; size_t esize; if( rtems_message_queue_receive( chainRecoveryQueue, &chainhead, &esize, RTEMS_NO_WAIT, 0) == RTEMS_SUCCESSFUL ) { /* get a pointer to the tail */ chaintail = chainhead->chainptr; /* if the tail points somewhere, free the entire chain */ if( chaintail && (int)chaintail != -1 ) { for(;;) { m_freem( chainhead->mbuf ); st_le32( &chainhead->status, XL_TXSTAT_DL_COMPLETE ); chainhead->mbuf = NULL; if( chainhead == chaintail ) break; chainhead = chainhead->next_md; } } else { /* a single packet chain */ m_freem( chainhead->mbuf ); st_le32( &chainhead->status, XL_TXSTAT_DL_COMPLETE ); chainhead->mbuf = NULL; } } } nextmd = lastmd->next_md; /* stop when ring is full */ if( ! (ld_le32(&nextmd->status) & XL_TXSTAT_DL_COMPLETE) ) { printk("etherlink : unit elnk%d tx ring full!\n", sc->xl_unit); break; } /* sanity check the next packet descriptor */ if( nextmd->mbuf ) { printk("etherlink : unit elnk%d tx ring corrupt!\n", sc->xl_unit); break; } IF_DEQUEUE(&ifp->if_snd, m); if( !m ) break; { int i; nextmd->mbuf = m; for(i=0; i< NUM_FRAGS; i++) { st_le32( &nextmd->txfrags[i].length, ((m->m_next)?0:XL_LAST_FRAG) | ( m->m_len & XL_TXSTAT_LENMASK) ); st_le32( &nextmd->txfrags[i].addr, (uint32_t)phys_to_bus( m->m_data ) ); if ((m = m->m_next) == NULL) break; } if( m ) { printk("etherlink : unit elnk%d tx fragments exhausted, truncating packet!\n", sc->xl_unit); st_le32( &nextmd->txfrags[NUM_FRAGS-1].length, XL_LAST_FRAG | ld_le32( &nextmd->txfrags[NUM_FRAGS-1].length) ); } } #if 0 { char *pkt = bus_to_phys( ld_le32( &nextmd->txfrags[i].addr )), *delim; int i; printk("unit %d queued pkt (%08x) ", sc->xl_unit, (uint32_t)pkt ); for(delim="", i=0; i < sizeof(struct ether_header); i++, delim=":") printk("%s%02x", delim, (char) pkt[i] ); printk("\n"); } #endif /* this packet will be the new end of the list */ st_le32( &nextmd->next, 0); st_le32( &nextmd->status, 0); if( !firstmd ) { /* keep track of the first packet we add to the chain */ firstmd = nextmd; /* ** use the chainbuf pointer of the last packet of ** the previous chain as a flag so when a ** dnComplete interrupt indicates the card is ** finished downloading the chain, the isr can ** immediately start the next which always begins ** with the next packet in the ring. Note several ** chains of packets may be assembled this way. */ lastmd->chainptr = (struct TXMD *)-1; } else { /* hook this packet to the previous one */ st_le32( &lastmd->next, (uint32_t)phys_to_bus( nextmd )); } ++chainCount; lastmd = nextmd; } if( firstmd ) { /* only enter if we've queued one or more packets */ /* save the last descriptor we set up in the chain */ sc->last_tx_md = lastmd; /* * We've added one or more packets to a chain, flag * the last packet so we get an dnComplete interrupt * when the card finishes accepting the chain */ st_le32( &lastmd->status, XL_TXSTAT_DL_INTR ); /* * point the chain head's chainptr to the tail so we * can jump to the next chain to send inside the isr. * If we're only sending one packet, then don't bother * with the link, as the chainptr value will either be * 0 if theres no next chain or -1 if there is. */ if( chainCount > 1 ) { firstmd->chainptr = lastmd; sc->chain_lengths[sc->chlenIndex]= (short)chainCount; if( ++sc->chlenIndex == NUM_CHAIN_LENGTHS ) sc->chlenIndex = 0; } /* ** clear the last packet's chainptr flag. If another ** chain is added later but before this chain is ** finished being sent, this flag on this packet will ** be re-set to -1 */ lastmd->chainptr = NULL; #if 0 printk("unit %d queued %d pkts, lastpkt status %08X\n", sc->xl_unit, chainCount, (uint32_t)ld_le32( &lastmd->status) ); #endif if( sc->tx_idle == 0 && CSR_READ_4(sc, XL_DOWNLIST_PTR) == 0 ) { printk("etherlink : unit elnk%d tx forced!\n", sc->xl_unit); sc->tx_idle = -1; } /* ** start sending this chain of packets if tx isn't ** busy, else the dnComplete interrupt will see there ** is another chain waiting and begin it immediately. */ if( sc->tx_idle ) { #if 0 printk("etherlink : unit elnk%d tx started %d packets\n", sc->xl_unit, chainCount ); #endif elnk_start_txchain(sc, firstmd); } } ifp->if_flags &= ~IFF_OACTIVE; } } } } } static void elnk_start (struct ifnet *ifp) { struct elnk_softc *sc = ifp->if_softc; #if 0 printk("unit %i tx signaled\n", sc->xl_unit ); #endif ifp->if_flags |= IFF_OACTIVE; rtems_bsdnet_event_send( txDaemonTid, sc->ioevent ); } /* * Initialize and start the device */ static void elnk_init (void *arg) { int i; struct elnk_softc *sc = arg; struct ifnet *ifp = &sc->arpcom.ac_if; if( !(ifp->if_flags & IFF_RUNNING) ) { xl_stop(sc); xl_reset(sc); sc->tx_idle = -1; { uint32_t cr,sr; xl_miibus_writereg(sc, 0x18, MII_BMCR, BMCR_RESET ); while( (cr = xl_miibus_readreg(sc, 0x18, MII_BMCR )) & BMCR_RESET ) { DELAY(100000); } xl_miibus_writereg(sc, 0x18, MII_ANAR, ANAR_10 | ANAR_TX | ANAR_10_FD | ANAR_TX_FD ); /* ANAR_T4 */ xl_miibus_writereg(sc, 0x18, MII_BMCR, BMCR_STARTNEG | BMCR_AUTOEN ); for (i=0; ((sr = xl_miibus_readreg(sc, 0x18, MII_BMSR)) & BMSR_ACOMP) == 0 && i < 20; i++) DELAY(10000); } /* * Set up hardware if its not already been done */ if( !sc->irqInfo.hdl ) { elnk_initialize_hardware(sc); } /* * Enable the card */ { u_int8_t rxfilt; /* Init our MAC address */ XL_SEL_WIN(2); for (i = 0; i < ETHER_ADDR_LEN; i++) { CSR_WRITE_1(sc, XL_W2_STATION_ADDR_LO + i, sc->arpcom.ac_enaddr[i]); } { int media = IFM_ETHER|IFM_100_TX|IFM_FDX; xl_mediacheck(sc); /* Choose a default media. */ switch(sc->xl_xcvr) { case XL_XCVR_10BT: media = IFM_ETHER|IFM_10_T; xl_setmode(sc, media); break; case XL_XCVR_AUI: if (sc->xl_type == XL_TYPE_905B && sc->xl_media == XL_MEDIAOPT_10FL) { media = IFM_ETHER|IFM_10_FL; xl_setmode(sc, media); } else { media = IFM_ETHER|IFM_10_5; xl_setmode(sc, media); } break; case XL_XCVR_COAX: media = IFM_ETHER|IFM_10_2; xl_setmode(sc, media); break; case XL_XCVR_AUTO: case XL_XCVR_100BTX: xl_setcfg(sc); break; case XL_XCVR_MII: printk( "etherlink : unit elnk%d MII media not supported!\n", sc->xl_unit); break; case XL_XCVR_100BFX: media = IFM_ETHER|IFM_100_FX; break; default: printk( "etherlink : unit elnk%d unknown XCVR type: %" PRId32 "\n", sc->xl_unit, sc->xl_xcvr); /* * This will probably be wrong, but it prevents * the ifmedia code from panicking. */ media = IFM_ETHER|IFM_10_T; break; } if (sc->xl_flags & XL_FLAG_NO_XCVR_PWR) { XL_SEL_WIN(0); CSR_WRITE_2(sc, XL_W0_MFG_ID, XL_NO_XCVR_PWR_MAGICBITS); } } XL_SEL_WIN(2); /* Clear the station mask. */ for (i = 0; i < 3; i++) CSR_WRITE_2(sc, XL_W2_STATION_MASK_LO + (i * 2), 0); /* * Set the TX freethresh value. * Note that this has no effect on 3c905B "cyclone" * cards but is required for 3c900/3c905 "boomerang" * cards in order to enable the download engine. */ CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8); /* Set the TX start threshold for best performance. */ sc->xl_tx_thresh = XL_MIN_FRAMELEN; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_SET_START|sc->xl_tx_thresh); /* * If this is a 3c905B, also set the tx reclaim threshold. * This helps cut down on the number of tx reclaim errors * that could happen on a busy network. The chip multiplies * the register value by 16 to obtain the actual threshold * in bytes, so we divide by 16 when setting the value here. * The existing threshold value can be examined by reading * the register at offset 9 in window 5. */ if (sc->xl_type == XL_TYPE_905B) { CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4)); } /* Set RX filter bits. */ XL_SEL_WIN(5); rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); /* Set the individual bit to receive frames for this host only. */ rxfilt |= XL_RXFILTER_INDIVIDUAL; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) { rxfilt |= XL_RXFILTER_ALLFRAMES; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); } else { rxfilt &= ~XL_RXFILTER_ALLFRAMES; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); } /* * Set capture broadcast bit to capture broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) { rxfilt |= XL_RXFILTER_BROADCAST; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); } else { rxfilt &= ~XL_RXFILTER_BROADCAST; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); } #if 0 /* * Program the multicast filter, if necessary. */ if (sc->xl_type == XL_TYPE_905B) xl_setmulti_hash(sc); else xl_setmulti(sc); #endif /* * Load the address of the RX list. We have to * stall the upload engine before we can manipulate * the uplist pointer register, then unstall it when * we're finished. We also have to wait for the * stall command to complete before proceeding. * Note that we have to do this after any RX resets * have completed since the uplist register is cleared * by a reset. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL); xl_wait(sc); CSR_WRITE_4(sc, XL_UPLIST_PTR, phys_to_bus( sc->curr_rx_md )); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL); xl_wait(sc); #if 0 if (sc->xl_type == XL_TYPE_905B) { /* Set polling interval */ CSR_WRITE_1(sc, XL_DOWN_POLL, 64); xl_wait(sc); printk("etherlink : unit elnk%d tx polling enabled\n", sc->xl_unit ); } #endif /* * If the coax transceiver is on, make sure to enable * the DC-DC converter. */ XL_SEL_WIN(3); if (sc->xl_xcvr == XL_XCVR_COAX) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START); else CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); /* increase packet size to allow reception of 802.1q or ISL packets */ if (sc->xl_type == XL_TYPE_905B) CSR_WRITE_2(sc, XL_W3_MAXPKTSIZE, XL_PACKET_SIZE); /* Clear out the stats counters. */ memset( &sc->xl_stats, 0, sizeof(struct xl_stats)); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE); sc->xl_stats_no_timeout = 1; xl_stats_update(sc->stat_timer_id,sc); sc->xl_stats_no_timeout = 0; XL_SEL_WIN(4); CSR_WRITE_2(sc, XL_W4_NET_DIAG, XL_NETDIAG_UPPER_BYTES_ENABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_ENABLE); /* * Enable interrupts. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|XL_INTRS); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS); /* Set the RX early threshold */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_THRESH|(XL_PACKET_SIZE >>2)); CSR_WRITE_4(sc, XL_DMACTL, XL_DMACTL_UP_RX_EARLY ); /* Enable receiver and transmitter. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE); xl_wait(sc); /* Select window 7 for normal operations. */ XL_SEL_WIN(7); /* schedule the stats update timer */ rtems_timer_fire_after( sc->stat_timer_id, sc->stats_update_ticks, xl_stats_update, (void *)sc ); } /* * Tell the world that we're running. */ ifp->if_flags |= IFF_RUNNING; } } /* * Stop the device */ static void elnk_stop (struct elnk_softc *sc) { struct ifnet *ifp = &sc->arpcom.ac_if; int i; /* * Stop the transmitter */ xl_stop(sc); xl_reset(sc); sc->tx_idle = -1; ifp->if_flags &= ~IFF_RUNNING; /* ** Clear out the rx & tx rings */ { struct TXMD *chainhead; size_t esize; while( rtems_message_queue_receive( chainRecoveryQueue, &chainhead, &esize, RTEMS_NO_WAIT, 0) == RTEMS_SUCCESSFUL ); } for(i=0 ; inumRxbuffers; i++) { st_le32( &sc->rx_ring[i].status, 0); st_le32( &sc->rx_ring[i].length, XL_LAST_FRAG | XL_PACKET_SIZE ); } for(i=0 ; inumTxbuffers; i++) { st_le32( &sc->tx_ring[i].status, XL_TXSTAT_DL_COMPLETE ); st_le32( &sc->tx_ring[i].next, 0); if( sc->tx_ring[i].mbuf ) { m_free( sc->tx_ring[i].mbuf ); sc->tx_ring[i].mbuf = NULL; } } } /* * Show interface statistics */ static void elnk_stats (struct elnk_softc *sc) { printf(" MII PHY data { anr:%04x lpar:%04x stat:%04x ctl:%04x }\n", sc->xl_stats.miianr, sc->xl_stats.miipar, sc->xl_stats.miistatus, sc->xl_stats.miicmd); printf(" internalcfg:%08" PRIx32 " macctl:%04x dmactl:%08" PRIx32 "\n", sc->xl_stats.internalconfig, sc->xl_stats.mac_control, sc->xl_stats.dmactl); printf(" rxstatus:%04x txstatus:%02x smbstat:%04x\n", sc->xl_stats.rxstatus, sc->xl_stats.txstatus, sc->xl_stats.smbstatus); printf(" txfree:%04X intstatus:%04x mediastat:%04x\n", sc->xl_stats.txfree, sc->xl_stats.intstatus, sc->xl_stats.mediastatus); { int i, totalLengths= 0, numLengths= 0; for(i=0; i< NUM_CHAIN_LENGTHS; i++) { if( sc->chain_lengths[i] > -1 ) { totalLengths += sc->chain_lengths[i]; ++numLengths; } } printf(" interrupts:%-9" PRIu32 " txcmp_ints:%-5" PRIu32 " avg_chain_len:%-4d\n", sc->xl_stats.device_interrupts, sc->xl_stats.txcomplete_ints, numLengths ? (totalLengths / numLengths) : -1 ); } printf(" carrier_lost:%-5d sqe_errs:%-5d\n", sc->xl_stats.xl_carrier_lost, sc->xl_stats.xl_sqe_errs); printf(" tx_multi_collision:%-5d tx_single_collision:%-5d\n", sc->xl_stats.xl_tx_multi_collision, sc->xl_stats.xl_tx_single_collision); printf(" tx_late_collision:%-5d rx_overrun:%-5d\n", sc->xl_stats.xl_tx_late_collision, sc->xl_stats.xl_rx_overrun); printf(" tx_deferred:%-5d badssd:%-5d\n", sc->xl_stats.xl_tx_deferred, sc->xl_stats.xl_badssd); printf(" rx_frames_ok:%-9" PRIu32 " tx_frames_ok:%-9" PRIu32 "\n", sc->xl_stats.xl_rx_frames_ok, sc->xl_stats.xl_tx_frames_ok); printf(" rx_bytes_ok:%-9" PRIu32 " tx_bytes_ok:%-9" PRIu32 "\n", sc->xl_stats.xl_rx_bytes_ok, sc->xl_stats.xl_tx_bytes_ok ); } /* * Driver ioctl handler */ static int elnk_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data) { struct elnk_softc *sc = ifp->if_softc; int error = 0; switch (command) { case SIOCGIFADDR: case SIOCSIFADDR: ether_ioctl (ifp, command, data); break; case SIOCSIFFLAGS: switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { case IFF_RUNNING: elnk_stop (sc); break; case IFF_UP: elnk_init (sc); break; case IFF_UP | IFF_RUNNING: elnk_stop (sc); elnk_init (sc); break; default: break; } break; case SIO_RTEMS_SHOW_STATS: elnk_stats (sc); break; /* * FIXME: All sorts of multicast commands need to be added here! */ default: error = EINVAL; break; } return error; } #if 0 static int iftap(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m ) { int i; char *delim, *pkt; printk("unit %i, src ", ifp->if_unit ); for(delim= "", i=0; i< ETHER_ADDR_LEN; i++, delim=":") printk("%s%02x", delim, (char) eh->ether_shost[i] ); printk(" dest "); for(delim= "", i=0; i< ETHER_ADDR_LEN; i++, delim=":") printk("%s%02x", delim, (char) eh->ether_dhost[i] ); printk(" pkt "); pkt = (char *)eh; for(delim="", i=0; i < sizeof(struct ether_header); i++, delim=":") printk("%s%02x", delim, (char) pkt[i] ); printk("\n"); return 0; } #endif struct el_boards { int pbus,pdev,pfun, vid, did, tindex; }; /* Prototype to avoid warning. This must be a global symbol. */ int rtems_elnk_driver_attach(struct rtems_bsdnet_ifconfig *config, int attach); /* * Attach an ELNK driver to the system */ int rtems_elnk_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach) { struct elnk_softc *sc; struct ifnet *ifp; char *unitName; int unitNumber; int mtu, i; unsigned char cvalue; struct el_boards sysboards[NUM_UNITS]; int numFound = 0; int pbus, pdev, pfun; #if defined(__i386__) uint32_t value; uint8_t interrupt; #endif #if defined(__PPC__) uint32_t lvalue; #endif /* * Get the instance number for the board we're going to configure * from the user. */ if( (unitNumber = rtems_bsdnet_parse_driver_name( config, &unitName)) == -1 ) { return 0; } if( strcmp(unitName, DRIVER_PREFIX) ) { printk("etherlink : invalid unit name '%s'\n", unitName ); return 0; } if ((unitNumber < 1) || (unitNumber > NUM_UNITS)) { printk("etherlink : unit %i is invalid, must be (1 <= n <= %d)\n", unitNumber, NUM_UNITS); return 0; } { int done= 0, unum; /* * Run thru the list of boards, finding all that are present in * the system. Sort by slot,dev - and then use the unitNumber-1 * to index the list and select the device. Yucky. */ for( i=0; !done && xl_devs[i].xl_vid; i++) { for(unum= 1; !done && pci_find_device( xl_devs[i].xl_vid, xl_devs[i].xl_did, unum-1, &sysboards[numFound].pbus, &sysboards[numFound].pdev, &sysboards[numFound].pfun)==0; unum++) { if( numFound == NUM_UNITS ) { printk("etherlink : Maximum of %d units found, extra devices ignored.\n", NUM_UNITS ); done=-1; } else { sysboards[numFound].vid = xl_devs[i].xl_vid; sysboards[numFound].did = xl_devs[i].xl_did; sysboards[numFound].tindex = i; ++numFound; } } } if( ! numFound ) { printk("etherlink : No Etherlink devices found\n"); return 0; } if( unitNumber-1 >= numFound ) { printk("etherlink : device '%s' not found\n", config->name ); return 0; } /* * Got the list of etherlink boards in the system, now sort by * slot,device. bubble sorts aren't all that wonderful, but this * is a short & infrequently sorted list. */ if( numFound > 1 ) { struct el_boards tboard; int didsort; do { didsort = 0; for(i=1; i sysboards[i].pbus || (sysboards[i-1].pbus == sysboards[i].pbus && sysboards[i-1].pdev > sysboards[i].pdev) ) { memcpy(&tboard, &sysboards[i-1], sizeof(struct el_boards)); memcpy(&sysboards[i-1], &sysboards[i], sizeof(struct el_boards)); memcpy(&sysboards[i], &tboard, sizeof(struct el_boards)); didsort++; } } } while( didsort ); } /* ** board list is sorted, now select the unit */ pbus = sysboards[unitNumber-1].pbus; pdev = sysboards[unitNumber-1].pdev; pfun = sysboards[unitNumber-1].pfun; } sc = &elnk_softc[unitNumber - 1]; ifp = &sc->arpcom.ac_if; if (ifp->if_softc != NULL) { printk("etherlink : unit %i already in use.\n", unitNumber ); return 0; } /* ** Save various things */ sc->xl_unit = unitNumber; sc->xl_type = sysboards[ unitNumber-1 ].tindex; sc->vendorID = sysboards[numFound].vid; sc->deviceID = sysboards[numFound].did; sc->numRxbuffers = (config->rbuf_count) ? config->rbuf_count : RX_RING_SIZE; sc->numTxbuffers = (config->xbuf_count) ? config->xbuf_count : TX_RING_SIZE; for(i=0; i< NUM_CHAIN_LENGTHS; i++) sc->chain_lengths[i]= -1; sc->chlenIndex = 0; if (config->mtu) mtu = config->mtu; else mtu = ETHERMTU; sc->acceptBroadcast = !config->ignore_broadcast; #ifdef ELNK_DEBUG printk("etherlink : device '%s', name 'elnk%d', pci %02x:%02x.%02x, %d rx/%d tx buffers\n", xl_devs[sc->xl_type].xl_name, sc->xl_unit, pbus, pdev, pfun, sc->numRxbuffers, sc->numTxbuffers); #endif /* ** Create this unit's stats timer */ if( rtems_timer_create( rtems_build_name( 'X', 'L', 't', (char)(sc->xl_unit & 255)), &sc->stat_timer_id ) != RTEMS_SUCCESSFUL ) { printk("etherlink : unit elnk%d unable to create stats timer\n", sc->xl_unit ); return 0; } /* update stats 1 times/second if things aren't incrementing fast * enough to trigger stats interrupts */ sc->stats_update_ticks = rtems_clock_get_ticks_per_second(); /* ** Get this unit's rx/tx event */ sc->ioevent = unit_signals[unitNumber-1]; #if defined(__i386__) pci_read_config_dword(pbus, pdev, pfun, 16, &value); sc->ioaddr = value & ~IO_MASK; pci_read_config_byte(pbus, pdev, pfun, 60, &interrupt); cvalue = interrupt; #endif #if defined(__PPC__) /* ** Prep the board */ pci_write_config_word(pbus, pdev, pfun, PCI_COMMAND, (uint16_t)( PCI_COMMAND_IO | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_WAIT ) ); /* * Get the device's base address */ pci_read_config_dword(pbus, pdev, pfun, PCI_BASE_ADDRESS_0, &lvalue); sc->ioaddr = (uint32_t)lvalue & PCI_BASE_ADDRESS_IO_MASK; /* ** Store the interrupt name, we'll use it later when we initialize ** the board. */ pci_read_config_byte(pbus, pdev, pfun, PCI_INTERRUPT_LINE, &cvalue); #endif memset(&sc->irqInfo,0,sizeof(rtems_irq_connect_data)); sc->irqInfo.name = cvalue; /* ** Establish basic board config, set node address from config or ** board eeprom, do stuff with additional device properties */ { uint8_t pci_latency; uint8_t new_latency = 248; /* Check the PCI latency value. On the 3c590 series the latency timer must be set to the maximum value to avoid data corruption that occurs when the timer expires during a transfer. This bug exists the Vortex chip only. */ #if defined(__i386__) pci_read_config_byte(pbus, pdev, pfun, 0x0d, &pci_latency); #endif #if defined(__PPC__) pci_read_config_byte(pbus,pdev,pfun, PCI_LATENCY_TIMER, &pci_latency); #endif if (pci_latency < new_latency) { printk("etherlink : unit elnk%d Overriding PCI latency, timer (CFLT) setting of %d, new value is %d.\n", sc->xl_unit, pci_latency, new_latency ); #if defined(__i386__) pci_write_config_byte(pbus, pdev, pfun, 0x0d, new_latency); #endif #if defined(__PPC__) pci_write_config_byte(pbus,pdev,pfun, PCI_LATENCY_TIMER, new_latency); #endif } } /* Reset the adapter. */ xl_reset(sc); { u_int16_t xcvr[2]; u_char eaddr[ETHER_ADDR_LEN]; sc->xl_flags = 0; if (sc->deviceID == TC_DEVICEID_HURRICANE_555) sc->xl_flags |= XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_PHYOK; if (sc->deviceID == TC_DEVICEID_HURRICANE_556 || sc->deviceID == TC_DEVICEID_HURRICANE_556B) sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_WEIRDRESET | XL_FLAG_INVERT_LED_PWR | XL_FLAG_INVERT_MII_PWR; if (sc->deviceID == TC_DEVICEID_HURRICANE_555 || sc->deviceID == TC_DEVICEID_HURRICANE_556) sc->xl_flags |= XL_FLAG_8BITROM; if (sc->deviceID == TC_DEVICEID_HURRICANE_556B) sc->xl_flags |= XL_FLAG_NO_XCVR_PWR; if (sc->deviceID == TC_DEVICEID_HURRICANE_575A || sc->deviceID == TC_DEVICEID_HURRICANE_575B || sc->deviceID == TC_DEVICEID_HURRICANE_575C || sc->deviceID == TC_DEVICEID_HURRICANE_656B || sc->deviceID == TC_DEVICEID_TORNADO_656C) sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_8BITROM; if (sc->deviceID == TC_DEVICEID_HURRICANE_656) sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK; if (sc->deviceID == TC_DEVICEID_HURRICANE_575B) sc->xl_flags |= XL_FLAG_INVERT_LED_PWR; if (sc->deviceID == TC_DEVICEID_HURRICANE_575C) sc->xl_flags |= XL_FLAG_INVERT_MII_PWR; if (sc->deviceID == TC_DEVICEID_TORNADO_656C) sc->xl_flags |= XL_FLAG_INVERT_MII_PWR; if (sc->deviceID == TC_DEVICEID_HURRICANE_656 || sc->deviceID == TC_DEVICEID_HURRICANE_656B) sc->xl_flags |= XL_FLAG_INVERT_MII_PWR | XL_FLAG_INVERT_LED_PWR; if (sc->deviceID == TC_DEVICEID_TORNADO_10_100BT_920B) sc->xl_flags |= XL_FLAG_PHYOK; if (config->hardware_address) { memcpy(sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); } else { if (xl_read_eeprom(sc, (caddr_t)&eaddr, XL_EE_OEM_ADR0, 3, 1)) { printk("etherlink : unit elnk%d Failed to read station address\n", sc->xl_unit ); return 0; } memcpy((char *)&sc->arpcom.ac_enaddr, eaddr, ETHER_ADDR_LEN); } /* * Figure out the card type. 3c905B adapters have the * 'supportsNoTxLength' bit set in the capabilities * word in the EEPROM. */ xl_read_eeprom(sc, (caddr_t)&sc->xl_caps, XL_EE_CAPS, 1, 0); if (sc->xl_caps & XL_CAPS_NO_TXLENGTH) sc->xl_type = XL_TYPE_905B; else sc->xl_type = XL_TYPE_90X; /* * Now we have to see what sort of media we have. * This includes probing for an MII interace and a * possible PHY. */ XL_SEL_WIN(3); sc->xl_media = CSR_READ_2(sc, XL_W3_MEDIA_OPT); xl_read_eeprom(sc, (char *)&xcvr, XL_EE_ICFG_0, 2, 0); sc->xl_xcvr = xcvr[0] | xcvr[1] << 16; sc->xl_xcvr &= XL_ICFG_CONNECTOR_MASK; sc->xl_xcvr >>= XL_ICFG_CONNECTOR_BITS; #if 0 printk("etherlink : unit elnk%d EEPROM set xcvr to 0x%x\n", sc->xl_unit, sc->xl_xcvr); #endif { char msg[255]; int i; struct _availmedia { int bit; char *name; } _am[]= {{ XL_MEDIAOPT_BT4, "100BaseT4" }, { XL_MEDIAOPT_BTX, "100BaseTX" }, { XL_MEDIAOPT_BFX, "100BaseFX" }, { XL_MEDIAOPT_BT, "10BaseT" }, { XL_MEDIAOPT_BNC, "10Base2" }, { XL_MEDIAOPT_AUI, "10mbps AUI"}, { XL_MEDIAOPT_MII, "MII"}, { 0, NULL }}; msg[0]= 0; for( i=0; _am[i].bit; i++) { if( sc->xl_media & _am[i].bit ) sprintf( &msg[strlen(msg)], ",%s", _am[i].name ); } if( !strlen(msg) ) strcpy( &msg[1], ""); printk("etherlink : unit elnk%d available media : %s\n", sc->xl_unit, &msg[1]); } XL_SEL_WIN(7); } /* * Set up network interface */ ifp->if_softc = sc; ifp->if_name = unitName; ifp->if_unit = sc->xl_unit; ifp->if_mtu = mtu; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; if (ifp->if_snd.ifq_maxlen == 0) ifp->if_snd.ifq_maxlen = ifqmaxlen; ifp->if_init = elnk_init; ifp->if_start = elnk_start; ifp->if_ioctl = elnk_ioctl; ifp->if_output = ether_output; #if 0 ifp->if_tap = iftap; #endif /* * Attach the interface */ if_attach (ifp); ether_ifattach (ifp); #ifdef ELNK_DEBUG printk( "etherlink : unit elnk%d driver attached\n", sc->xl_unit ); #endif /* * Start driver tasks if this is the first unit initialized */ if (txDaemonTid == 0) { if( rtems_message_queue_create( rtems_build_name('X','L','c','r'), sc->numTxbuffers+1, sizeof(struct TXMD *), RTEMS_FIFO | RTEMS_LOCAL, &chainRecoveryQueue ) != RTEMS_SUCCESSFUL ) { rtems_panic( "etherlink : Unable to create TX buffer recovery queue\n" ); } rxDaemonTid = rtems_bsdnet_newproc( "XLrx", 4096, elnk_rxDaemon, NULL); txDaemonTid = rtems_bsdnet_newproc( "XLtx", 4096, elnk_txDaemon, NULL); #ifdef ELNK_DEBUG printk( "etherlink : driver tasks created\n" ); #endif } return 1; }; #endif /* ELNK_SUPPORTED */ /* eof */