forked from Imagelibrary/rtems
Add and use <machine/rtems-bsd-kernel-space.h> and <machine/rtems-bsd-user-space.h> similar to the libbsd to avoid command line defines and defines scattered throught the code base. Simplify cpukit/libnetworking/Makefile.am. Update #3375.
3554 lines
99 KiB
C
3554 lines
99 KiB
C
/*
|
|
* 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 <wpaul@ctr.columbia.edu>. 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 <machine/rtems-bsd-kernel-space.h>
|
|
|
|
#include <rtems.h>
|
|
|
|
/*
|
|
* 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 <bsp.h>
|
|
|
|
#if !defined(PCI_DRAM_OFFSET)
|
|
#undef ELNK_SUPPORTED
|
|
#endif
|
|
|
|
/* #undef ELNK_SUPPORTED */
|
|
|
|
|
|
#if defined(ELNK_SUPPORTED)
|
|
#include <rtems/pci.h>
|
|
|
|
#if defined(__PPC__)
|
|
#include <libcpu/byteorder.h>
|
|
#include <libcpu/io.h>
|
|
#endif
|
|
|
|
#if defined(__i386__)
|
|
#include <libcpu/byteorder.h>
|
|
#endif
|
|
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <rtems/error.h>
|
|
#include <rtems/bspIo.h>
|
|
#include <rtems/rtems_bsdnet.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/socket.h>
|
|
#include <sys/sockio.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_ether.h>
|
|
|
|
#include <net/if_media.h>
|
|
#include <dev/mii/mii.h>
|
|
#include <bsp/irq.h>
|
|
|
|
#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 ; i<sc->numRxbuffers; 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 ; i<sc->numTxbuffers; 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 ; i<sc->numRxbuffers; 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 ; i<sc->numTxbuffers; 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<numFound; i++)
|
|
{
|
|
if( sysboards[i-1].pbus > 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], "<no media bits>");
|
|
|
|
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 */
|