summaryrefslogtreecommitdiffstats
path: root/debian/grub-extras/disabled/gpxe/src/drivers/net/mtd80x.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/grub-extras/disabled/gpxe/src/drivers/net/mtd80x.c')
-rw-r--r--debian/grub-extras/disabled/gpxe/src/drivers/net/mtd80x.c1022
1 files changed, 1022 insertions, 0 deletions
diff --git a/debian/grub-extras/disabled/gpxe/src/drivers/net/mtd80x.c b/debian/grub-extras/disabled/gpxe/src/drivers/net/mtd80x.c
new file mode 100644
index 0000000..7cf59b0
--- /dev/null
+++ b/debian/grub-extras/disabled/gpxe/src/drivers/net/mtd80x.c
@@ -0,0 +1,1022 @@
+/**************************************************************************
+*
+* mtd80x.c: Etherboot device driver for the mtd80x Ethernet chip.
+* Written 2004-2004 by Erdem Güven <zuencap@yahoo.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* fealnx.c: A Linux device driver for the mtd80x Ethernet chip
+* Written 1998-2000 by Donald Becker
+*
+***************************************************************************/
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include <gpxe/pci.h>
+#include <gpxe/ethernet.h>
+#include <mii.h>
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+#define get_unaligned(ptr) (*(ptr))
+
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency. */
+/* The compiler will convert <unsigned>'%'<2^N> into a bit mask. */
+/* Making the Tx ring too large decreases the effectiveness of channel */
+/* bonding and packet priority. */
+/* There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE 2
+#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */
+#define RX_RING_SIZE 4
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define HZ 100
+#define TX_TIME_OUT (6*HZ)
+
+/* Allocation size of Rx buffers with normal sized Ethernet frames.
+ Do not change this value without good reason. This is not a limit,
+ but a way to keep a consistent allocation size among drivers.
+ */
+#define PKT_BUF_SZ 1536
+
+/* for different PHY */
+enum phy_type_flags {
+ MysonPHY = 1,
+ AhdocPHY = 2,
+ SeeqPHY = 3,
+ MarvellPHY = 4,
+ Myson981 = 5,
+ LevelOnePHY = 6,
+ OtherPHY = 10,
+};
+
+/* A chip capabilities table*/
+enum chip_capability_flags {
+ HAS_MII_XCVR,
+ HAS_CHIP_XCVR,
+};
+
+#if 0 /* not used */
+static
+struct chip_info
+{
+ u16 dev_id;
+ int flag;
+}
+mtd80x_chips[] = {
+ {0x0800, HAS_MII_XCVR},
+ {0x0803, HAS_CHIP_XCVR},
+ {0x0891, HAS_MII_XCVR}
+ };
+static int chip_cnt = sizeof( mtd80x_chips ) / sizeof( struct chip_info );
+#endif
+
+/* Offsets to the Command and Status Registers. */
+enum mtd_offsets {
+ PAR0 = 0x0, /* physical address 0-3 */
+ PAR1 = 0x04, /* physical address 4-5 */
+ MAR0 = 0x08, /* multicast address 0-3 */
+ MAR1 = 0x0C, /* multicast address 4-7 */
+ FAR0 = 0x10, /* flow-control address 0-3 */
+ FAR1 = 0x14, /* flow-control address 4-5 */
+ TCRRCR = 0x18, /* receive & transmit configuration */
+ BCR = 0x1C, /* bus command */
+ TXPDR = 0x20, /* transmit polling demand */
+ RXPDR = 0x24, /* receive polling demand */
+ RXCWP = 0x28, /* receive current word pointer */
+ TXLBA = 0x2C, /* transmit list base address */
+ RXLBA = 0x30, /* receive list base address */
+ ISR = 0x34, /* interrupt status */
+ IMR = 0x38, /* interrupt mask */
+ FTH = 0x3C, /* flow control high/low threshold */
+ MANAGEMENT = 0x40, /* bootrom/eeprom and mii management */
+ TALLY = 0x44, /* tally counters for crc and mpa */
+ TSR = 0x48, /* tally counter for transmit status */
+ BMCRSR = 0x4c, /* basic mode control and status */
+ PHYIDENTIFIER = 0x50, /* phy identifier */
+ ANARANLPAR = 0x54, /* auto-negotiation advertisement and link
+ partner ability */
+ ANEROCR = 0x58, /* auto-negotiation expansion and pci conf. */
+ BPREMRPSR = 0x5c, /* bypass & receive error mask and phy status */
+};
+
+/* Bits in the interrupt status/enable registers. */
+/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */
+enum intr_status_bits {
+ RFCON = 0x00020000, /* receive flow control xon packet */
+ RFCOFF = 0x00010000, /* receive flow control xoff packet */
+ LSCStatus = 0x00008000, /* link status change */
+ ANCStatus = 0x00004000, /* autonegotiation completed */
+ FBE = 0x00002000, /* fatal bus error */
+ FBEMask = 0x00001800, /* mask bit12-11 */
+ ParityErr = 0x00000000, /* parity error */
+ TargetErr = 0x00001000, /* target abort */
+ MasterErr = 0x00000800, /* master error */
+ TUNF = 0x00000400, /* transmit underflow */
+ ROVF = 0x00000200, /* receive overflow */
+ ETI = 0x00000100, /* transmit early int */
+ ERI = 0x00000080, /* receive early int */
+ CNTOVF = 0x00000040, /* counter overflow */
+ RBU = 0x00000020, /* receive buffer unavailable */
+ TBU = 0x00000010, /* transmit buffer unavilable */
+ TI = 0x00000008, /* transmit interrupt */
+ RI = 0x00000004, /* receive interrupt */
+ RxErr = 0x00000002, /* receive error */
+};
+
+/* Bits in the NetworkConfig register. */
+enum rx_mode_bits {
+ RxModeMask = 0xe0,
+ AcceptAllPhys = 0x80, /* promiscuous mode */
+ AcceptBroadcast = 0x40, /* accept broadcast */
+ AcceptMulticast = 0x20, /* accept mutlicast */
+ AcceptRunt = 0x08, /* receive runt pkt */
+ ALP = 0x04, /* receive long pkt */
+ AcceptErr = 0x02, /* receive error pkt */
+
+ AcceptMyPhys = 0x00000000,
+ RxEnable = 0x00000001,
+ RxFlowCtrl = 0x00002000,
+ TxEnable = 0x00040000,
+ TxModeFDX = 0x00100000,
+ TxThreshold = 0x00e00000,
+
+ PS1000 = 0x00010000,
+ PS10 = 0x00080000,
+ FD = 0x00100000,
+};
+
+/* Bits in network_desc.status */
+enum rx_desc_status_bits {
+ RXOWN = 0x80000000, /* own bit */
+ FLNGMASK = 0x0fff0000, /* frame length */
+ FLNGShift = 16,
+ MARSTATUS = 0x00004000, /* multicast address received */
+ BARSTATUS = 0x00002000, /* broadcast address received */
+ PHYSTATUS = 0x00001000, /* physical address received */
+ RXFSD = 0x00000800, /* first descriptor */
+ RXLSD = 0x00000400, /* last descriptor */
+ ErrorSummary = 0x80, /* error summary */
+ RUNT = 0x40, /* runt packet received */
+ LONG = 0x20, /* long packet received */
+ FAE = 0x10, /* frame align error */
+ CRC = 0x08, /* crc error */
+ RXER = 0x04, /* receive error */
+};
+
+enum rx_desc_control_bits {
+ RXIC = 0x00800000, /* interrupt control */
+ RBSShift = 0,
+};
+
+enum tx_desc_status_bits {
+ TXOWN = 0x80000000, /* own bit */
+ JABTO = 0x00004000, /* jabber timeout */
+ CSL = 0x00002000, /* carrier sense lost */
+ LC = 0x00001000, /* late collision */
+ EC = 0x00000800, /* excessive collision */
+ UDF = 0x00000400, /* fifo underflow */
+ DFR = 0x00000200, /* deferred */
+ HF = 0x00000100, /* heartbeat fail */
+ NCRMask = 0x000000ff, /* collision retry count */
+ NCRShift = 0,
+};
+
+enum tx_desc_control_bits {
+ TXIC = 0x80000000, /* interrupt control */
+ ETIControl = 0x40000000, /* early transmit interrupt */
+ TXLD = 0x20000000, /* last descriptor */
+ TXFD = 0x10000000, /* first descriptor */
+ CRCEnable = 0x08000000, /* crc control */
+ PADEnable = 0x04000000, /* padding control */
+ RetryTxLC = 0x02000000, /* retry late collision */
+ PKTSMask = 0x3ff800, /* packet size bit21-11 */
+ PKTSShift = 11,
+ TBSMask = 0x000007ff, /* transmit buffer bit 10-0 */
+ TBSShift = 0,
+};
+
+/* BootROM/EEPROM/MII Management Register */
+#define MASK_MIIR_MII_READ 0x00000000
+#define MASK_MIIR_MII_WRITE 0x00000008
+#define MASK_MIIR_MII_MDO 0x00000004
+#define MASK_MIIR_MII_MDI 0x00000002
+#define MASK_MIIR_MII_MDC 0x00000001
+
+/* ST+OP+PHYAD+REGAD+TA */
+#define OP_READ 0x6000 /* ST:01+OP:10+PHYAD+REGAD+TA:Z0 */
+#define OP_WRITE 0x5002 /* ST:01+OP:01+PHYAD+REGAD+TA:10 */
+
+/* ------------------------------------------------------------------------- */
+/* Constants for Myson PHY */
+/* ------------------------------------------------------------------------- */
+#define MysonPHYID 0xd0000302
+/* 89-7-27 add, (begin) */
+#define MysonPHYID0 0x0302
+#define StatusRegister 18
+#define SPEED100 0x0400 // bit10
+#define FULLMODE 0x0800 // bit11
+/* 89-7-27 add, (end) */
+
+/* ------------------------------------------------------------------------- */
+/* Constants for Seeq 80225 PHY */
+/* ------------------------------------------------------------------------- */
+#define SeeqPHYID0 0x0016
+
+#define MIIRegister18 18
+#define SPD_DET_100 0x80
+#define DPLX_DET_FULL 0x40
+
+/* ------------------------------------------------------------------------- */
+/* Constants for Ahdoc 101 PHY */
+/* ------------------------------------------------------------------------- */
+#define AhdocPHYID0 0x0022
+
+#define DiagnosticReg 18
+#define DPLX_FULL 0x0800
+#define Speed_100 0x0400
+
+/* 89/6/13 add, */
+/* -------------------------------------------------------------------------- */
+/* Constants */
+/* -------------------------------------------------------------------------- */
+#define MarvellPHYID0 0x0141
+#define LevelOnePHYID0 0x0013
+
+#define MII1000BaseTControlReg 9
+#define MII1000BaseTStatusReg 10
+#define SpecificReg 17
+
+/* for 1000BaseT Control Register */
+#define PHYAbletoPerform1000FullDuplex 0x0200
+#define PHYAbletoPerform1000HalfDuplex 0x0100
+#define PHY1000AbilityMask 0x300
+
+// for phy specific status register, marvell phy.
+#define SpeedMask 0x0c000
+#define Speed_1000M 0x08000
+#define Speed_100M 0x4000
+#define Speed_10M 0
+#define Full_Duplex 0x2000
+
+// 89/12/29 add, for phy specific status register, levelone phy, (begin)
+#define LXT1000_100M 0x08000
+#define LXT1000_1000M 0x0c000
+#define LXT1000_Full 0x200
+// 89/12/29 add, for phy specific status register, levelone phy, (end)
+
+#if 0
+/* for 3-in-1 case */
+#define PS10 0x00080000
+#define FD 0x00100000
+#define PS1000 0x00010000
+#endif
+
+/* for PHY */
+#define LinkIsUp 0x0004
+#define LinkIsUp2 0x00040000
+
+/* Create a static buffer of size PKT_BUF_SZ for each
+RX and TX Descriptor. All descriptors point to a
+part of this buffer */
+struct {
+ u8 txb[PKT_BUF_SZ * TX_RING_SIZE] __attribute__ ((aligned(8)));
+ u8 rxb[PKT_BUF_SZ * RX_RING_SIZE] __attribute__ ((aligned(8)));
+} mtd80x_bufs __shared;
+#define txb mtd80x_bufs.txb
+#define rxb mtd80x_bufs.rxb
+
+/* The Tulip Rx and Tx buffer descriptors. */
+struct mtd_desc
+{
+ s32 status;
+ s32 control;
+ u32 buffer;
+ u32 next_desc;
+ struct mtd_desc *next_desc_logical;
+ u8* skbuff;
+ u32 reserved1;
+ u32 reserved2;
+};
+
+struct mtd_private
+{
+ struct mtd_desc rx_ring[RX_RING_SIZE];
+ struct mtd_desc tx_ring[TX_RING_SIZE];
+
+ /* Frequently used values: keep some adjacent for cache effect. */
+ int flags;
+ struct pci_dev *pci_dev;
+ unsigned long crvalue;
+ unsigned long bcrvalue;
+ /*unsigned long imrvalue;*/
+ struct mtd_desc *cur_rx;
+ struct mtd_desc *lack_rxbuf;
+ int really_rx_count;
+ struct mtd_desc *cur_tx;
+ struct mtd_desc *cur_tx_copy;
+ int really_tx_count;
+ int free_tx_count;
+ unsigned int rx_buf_sz; /* Based on MTU+slack. */
+
+ /* These values are keep track of the transceiver/media in use. */
+ unsigned int linkok;
+ unsigned int line_speed;
+ unsigned int duplexmode;
+ unsigned int default_port:
+ 4; /* Last dev->if_port value. */
+ unsigned int PHYType;
+
+ /* MII transceiver section. */
+ int mii_cnt; /* MII device addresses. */
+ unsigned char phys[1]; /* MII device addresses. */
+
+ /*other*/
+ const char *nic_name;
+ int ioaddr;
+ u16 dev_id;
+};
+
+static struct mtd_private mtdx;
+
+static int mdio_read(struct nic * , int phy_id, int location);
+static void getlinktype(struct nic * );
+static void getlinkstatus(struct nic * );
+static void set_rx_mode(struct nic *);
+
+/**************************************************************************
+ * init_ring - setup the tx and rx descriptors
+ *************************************************************************/
+static void init_ring(struct nic *nic __unused)
+{
+ int i;
+
+ mtdx.cur_rx = &mtdx.rx_ring[0];
+
+ mtdx.rx_buf_sz = PKT_BUF_SZ;
+ /*mtdx.rx_head_desc = &mtdx.rx_ring[0];*/
+
+ /* Initialize all Rx descriptors. */
+ /* Fill in the Rx buffers. Handle allocation failure gracefully. */
+ for (i = 0; i < RX_RING_SIZE; i++)
+ {
+ mtdx.rx_ring[i].status = RXOWN;
+ mtdx.rx_ring[i].control = mtdx.rx_buf_sz << RBSShift;
+ mtdx.rx_ring[i].next_desc = virt_to_le32desc(&mtdx.rx_ring[i+1]);
+ mtdx.rx_ring[i].next_desc_logical = &mtdx.rx_ring[i+1];
+ mtdx.rx_ring[i].buffer = virt_to_le32desc(&rxb[i * PKT_BUF_SZ]);
+ mtdx.rx_ring[i].skbuff = &rxb[i * PKT_BUF_SZ];
+ }
+ /* Mark the last entry as wrapping the ring. */
+ mtdx.rx_ring[i-1].next_desc = virt_to_le32desc(&mtdx.rx_ring[0]);
+ mtdx.rx_ring[i-1].next_desc_logical = &mtdx.rx_ring[0];
+
+ /* We only use one transmit buffer, but two
+ * descriptors so transmit engines have somewhere
+ * to point should they feel the need */
+ mtdx.tx_ring[0].status = 0x00000000;
+ mtdx.tx_ring[0].buffer = virt_to_bus(&txb[0]);
+ mtdx.tx_ring[0].next_desc = virt_to_le32desc(&mtdx.tx_ring[1]);
+
+ /* This descriptor is never used */
+ mtdx.tx_ring[1].status = 0x00000000;
+ mtdx.tx_ring[1].buffer = 0; /*virt_to_bus(&txb[1]); */
+ mtdx.tx_ring[1].next_desc = virt_to_le32desc(&mtdx.tx_ring[0]);
+
+ return;
+}
+
+/**************************************************************************
+RESET - Reset Adapter
+***************************************************************************/
+static void mtd_reset( struct nic *nic )
+{
+ /* Reset the chip to erase previous misconfiguration. */
+ outl(0x00000001, mtdx.ioaddr + BCR);
+
+ init_ring(nic);
+
+ outl(virt_to_bus(mtdx.rx_ring), mtdx.ioaddr + RXLBA);
+ outl(virt_to_bus(mtdx.tx_ring), mtdx.ioaddr + TXLBA);
+
+ /* Initialize other registers. */
+ /* Configure the PCI bus bursts and FIFO thresholds. */
+ mtdx.bcrvalue = 0x10; /* little-endian, 8 burst length */
+ mtdx.crvalue = 0xa00; /* rx 128 burst length */
+
+ if ( mtdx.dev_id == 0x891 ) {
+ mtdx.bcrvalue |= 0x200; /* set PROG bit */
+ mtdx.crvalue |= 0x02000000; /* set enhanced bit */
+ }
+
+ outl( mtdx.bcrvalue, mtdx.ioaddr + BCR);
+
+ /* Restart Rx engine if stopped. */
+ outl(0, mtdx.ioaddr + RXPDR);
+
+ getlinkstatus(nic);
+ if (mtdx.linkok)
+ {
+ static const char* texts[]={"half","full","10","100","1000"};
+ getlinktype(nic);
+ DBG ( "Link is OK : %s %s\n", texts[mtdx.duplexmode-1], texts[mtdx.line_speed+1] );
+ } else
+ {
+ DBG ( "No link!!!\n" );
+ }
+
+ mtdx.crvalue |= /*TxEnable |*/ RxEnable | TxThreshold;
+ set_rx_mode(nic);
+
+ /* Clear interrupts by setting the interrupt mask. */
+ outl(FBE | TUNF | CNTOVF | RBU | TI | RI, mtdx.ioaddr + ISR);
+ outl( 0, mtdx.ioaddr + IMR);
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int mtd_poll(struct nic *nic, __unused int retrieve)
+{
+ s32 rx_status = mtdx.cur_rx->status;
+ int retval = 0;
+
+ if( ( rx_status & RXOWN ) != 0 )
+ {
+ return 0;
+ }
+
+ if (rx_status & ErrorSummary)
+ { /* there was a fatal error */
+ printf( "%s: Receive error, Rx status %8.8x, Error(s) %s%s%s\n",
+ mtdx.nic_name, (unsigned int) rx_status,
+ (rx_status & (LONG | RUNT)) ? "length_error ":"",
+ (rx_status & RXER) ? "frame_error ":"",
+ (rx_status & CRC) ? "crc_error ":"" );
+ retval = 0;
+ } else if( !((rx_status & RXFSD) && (rx_status & RXLSD)) )
+ {
+ /* this pkt is too long, over one rx buffer */
+ printf("Pkt is too long, over one rx buffer.\n");
+ retval = 0;
+ } else
+ { /* this received pkt is ok */
+ /* Omit the four octet CRC from the length. */
+ short pkt_len = ((rx_status & FLNGMASK) >> FLNGShift) - 4;
+
+ DBG ( " netdev_rx() normal Rx pkt length %d"
+ " status %x.\n", pkt_len, (unsigned int) rx_status );
+
+ nic->packetlen = pkt_len;
+ memcpy(nic->packet, mtdx.cur_rx->skbuff, pkt_len);
+
+ retval = 1;
+ }
+
+ while( ( mtdx.cur_rx->status & RXOWN ) == 0 )
+ {
+ mtdx.cur_rx->status = RXOWN;
+ mtdx.cur_rx = mtdx.cur_rx->next_desc_logical;
+ }
+
+ /* Restart Rx engine if stopped. */
+ outl(0, mtdx.ioaddr + RXPDR);
+
+ return retval;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void mtd_transmit(
+ struct nic *nic,
+ const char *dest, /* Destination */
+ unsigned int type, /* Type */
+ unsigned int size, /* size */
+ const char *data) /* Packet */
+{
+ u32 to;
+ u32 tx_status;
+ unsigned int nstype = htons ( type );
+
+ memcpy( txb, dest, ETH_ALEN );
+ memcpy( txb + ETH_ALEN, nic->node_addr, ETH_ALEN );
+ memcpy( txb + 2 * ETH_ALEN, &nstype, 2 );
+ memcpy( txb + ETH_HLEN, data, size );
+
+ size += ETH_HLEN;
+ size &= 0x0FFF;
+ while( size < ETH_ZLEN )
+ {
+ txb[size++] = '\0';
+ }
+
+ mtdx.tx_ring[0].control = TXLD | TXFD | CRCEnable | PADEnable;
+ mtdx.tx_ring[0].control |= (size << PKTSShift); /* pkt size */
+ mtdx.tx_ring[0].control |= (size << TBSShift); /* buffer size */
+ mtdx.tx_ring[0].status = TXOWN;
+
+ /* Point to transmit descriptor */
+ outl(virt_to_bus(mtdx.tx_ring), mtdx.ioaddr + TXLBA);
+ /* Enable Tx */
+ outl( mtdx.crvalue | TxEnable, mtdx.ioaddr + TCRRCR);
+ /* Wake the potentially-idle transmit channel. */
+ outl(0, mtdx.ioaddr + TXPDR);
+
+ to = currticks() + TX_TIME_OUT;
+ while(( mtdx.tx_ring[0].status & TXOWN) && (currticks() < to));
+
+ /* Disable Tx */
+ outl( mtdx.crvalue & (~TxEnable), mtdx.ioaddr + TCRRCR);
+
+ tx_status = mtdx.tx_ring[0].status;
+ if (currticks() >= to){
+ DBG ( "TX Time Out" );
+ } else if( tx_status & (CSL | LC | EC | UDF | HF)){
+ printf( "Transmit error: %8.8x %s %s %s %s %s\n",
+ (unsigned int) tx_status,
+ tx_status & EC ? "abort" : "",
+ tx_status & CSL ? "carrier" : "",
+ tx_status & LC ? "late" : "",
+ tx_status & UDF ? "fifo" : "",
+ tx_status & HF ? "heartbeat" : "" );
+ }
+
+ /*hex_dump( txb, size );*/
+ /*pause();*/
+
+ DBG ( "TRANSMIT\n" );
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void mtd_disable ( struct nic *nic ) {
+
+ /* Disable Tx Rx*/
+ outl( mtdx.crvalue & (~TxEnable) & (~RxEnable), mtdx.ioaddr + TCRRCR );
+
+ /* Reset the chip to erase previous misconfiguration. */
+ mtd_reset(nic);
+
+ DBG ( "DISABLE\n" );
+}
+
+static struct nic_operations mtd_operations = {
+ .connect = dummy_connect,
+ .poll = mtd_poll,
+ .transmit = mtd_transmit,
+ .irq = dummy_irq,
+
+};
+
+static struct pci_device_id mtd80x_nics[] = {
+ PCI_ROM(0x1516, 0x0800, "MTD800", "Myson MTD800", 0),
+ PCI_ROM(0x1516, 0x0803, "MTD803", "Surecom EP-320X", 0),
+ PCI_ROM(0x1516, 0x0891, "MTD891", "Myson MTD891", 0),
+};
+
+PCI_DRIVER ( mtd80x_driver, mtd80x_nics, PCI_NO_CLASS );
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+static int mtd_probe ( struct nic *nic, struct pci_device *pci ) {
+
+ int i;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ adjust_pci_device(pci);
+
+ nic->ioaddr = pci->ioaddr;
+ nic->irqno = 0;
+
+ mtdx.nic_name = pci->driver_name;
+ mtdx.dev_id = pci->device;
+ mtdx.ioaddr = nic->ioaddr;
+
+ /* read ethernet id */
+ for (i = 0; i < 6; ++i)
+ {
+ nic->node_addr[i] = inb(mtdx.ioaddr + PAR0 + i);
+ }
+
+ if (memcmp(nic->node_addr, "\0\0\0\0\0\0", 6) == 0)
+ {
+ return 0;
+ }
+
+ DBG ( "%s: ioaddr %4.4x MAC %s\n", mtdx.nic_name, mtdx.ioaddr, eth_ntoa ( nic->node_addr ) );
+
+ /* Reset the chip to erase previous misconfiguration. */
+ outl(0x00000001, mtdx.ioaddr + BCR);
+
+ /* find the connected MII xcvrs */
+
+ if( mtdx.dev_id != 0x803 )
+ {
+ int phy, phy_idx = 0;
+
+ for (phy = 1; phy < 32 && phy_idx < 1; phy++) {
+ int mii_status = mdio_read(nic, phy, 1);
+
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ mtdx.phys[phy_idx] = phy;
+
+ DBG ( "%s: MII PHY found at address %d, status "
+ "0x%4.4x.\n", mtdx.nic_name, phy, mii_status );
+ /* get phy type */
+ {
+ unsigned int data;
+
+ data = mdio_read(nic, mtdx.phys[phy_idx], 2);
+ if (data == SeeqPHYID0)
+ mtdx.PHYType = SeeqPHY;
+ else if (data == AhdocPHYID0)
+ mtdx.PHYType = AhdocPHY;
+ else if (data == MarvellPHYID0)
+ mtdx.PHYType = MarvellPHY;
+ else if (data == MysonPHYID0)
+ mtdx.PHYType = Myson981;
+ else if (data == LevelOnePHYID0)
+ mtdx.PHYType = LevelOnePHY;
+ else
+ mtdx.PHYType = OtherPHY;
+ }
+ phy_idx++;
+ }
+ }
+
+ mtdx.mii_cnt = phy_idx;
+ if (phy_idx == 0) {
+ printf("%s: MII PHY not found -- this device may "
+ "not operate correctly.\n", mtdx.nic_name);
+ }
+ } else {
+ mtdx.phys[0] = 32;
+ /* get phy type */
+ if (inl(mtdx.ioaddr + PHYIDENTIFIER) == MysonPHYID ) {
+ mtdx.PHYType = MysonPHY;
+ DBG ( "MysonPHY\n" );
+ } else {
+ mtdx.PHYType = OtherPHY;
+ DBG ( "OtherPHY\n" );
+ }
+ }
+
+ getlinkstatus(nic);
+ if( !mtdx.linkok )
+ {
+ printf("No link!!!\n");
+ return 0;
+ }
+
+ mtd_reset( nic );
+
+ /* point to NIC specific routines */
+ nic->nic_op = &mtd_operations;
+ return 1;
+}
+
+
+/**************************************************************************/
+static void set_rx_mode(struct nic *nic __unused)
+{
+ u32 mc_filter[2]; /* Multicast hash filter */
+ u32 rx_mode;
+
+ /* Too many to match, or accept all multicasts. */
+ mc_filter[1] = mc_filter[0] = ~0;
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+
+ outl(mc_filter[0], mtdx.ioaddr + MAR0);
+ outl(mc_filter[1], mtdx.ioaddr + MAR1);
+
+ mtdx.crvalue = ( mtdx.crvalue & ~RxModeMask ) | rx_mode;
+ outb( mtdx.crvalue, mtdx.ioaddr + TCRRCR);
+}
+/**************************************************************************/
+static unsigned int m80x_read_tick(void)
+/* function: Reads the Timer tick count register which decrements by 2 from */
+/* 65536 to 0 every 1/36.414 of a second. Each 2 decrements of the */
+/* count represents 838 nsec's. */
+/* input : none. */
+/* output : none. */
+{
+ unsigned char tmp;
+ int value;
+
+ outb((char) 0x06, 0x43); // Command 8254 to latch T0's count
+
+ // now read the count.
+ tmp = (unsigned char) inb(0x40);
+ value = ((int) tmp) << 8;
+ tmp = (unsigned char) inb(0x40);
+ value |= (((int) tmp) & 0xff);
+ return (value);
+}
+
+static void m80x_delay(unsigned int interval)
+/* function: to wait for a specified time. */
+/* input : interval ... the specified time. */
+/* output : none. */
+{
+ unsigned int interval1, interval2, i = 0;
+
+ interval1 = m80x_read_tick(); // get initial value
+ do
+ {
+ interval2 = m80x_read_tick();
+ if (interval1 < interval2)
+ interval1 += 65536;
+ ++i;
+ } while (((interval1 - interval2) < (u16) interval) && (i < 65535));
+}
+
+
+static u32 m80x_send_cmd_to_phy(long miiport, int opcode, int phyad, int regad)
+{
+ u32 miir;
+ int i;
+ unsigned int mask, data;
+
+ /* enable MII output */
+ miir = (u32) inl(miiport);
+ miir &= 0xfffffff0;
+
+ miir |= MASK_MIIR_MII_WRITE + MASK_MIIR_MII_MDO;
+
+ /* send 32 1's preamble */
+ for (i = 0; i < 32; i++) {
+ /* low MDC; MDO is already high (miir) */
+ miir &= ~MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ /* high MDC */
+ miir |= MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+ }
+
+ /* calculate ST+OP+PHYAD+REGAD+TA */
+ data = opcode | (phyad << 7) | (regad << 2);
+
+ /* sent out */
+ mask = 0x8000;
+ while (mask) {
+ /* low MDC, prepare MDO */
+ miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO);
+ if (mask & data)
+ miir |= MASK_MIIR_MII_MDO;
+
+ outl(miir, miiport);
+ /* high MDC */
+ miir |= MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+ m80x_delay(30);
+
+ /* next */
+ mask >>= 1;
+ if (mask == 0x2 && opcode == OP_READ)
+ miir &= ~MASK_MIIR_MII_WRITE;
+ }
+ return miir;
+}
+
+static int mdio_read(struct nic *nic __unused, int phyad, int regad)
+{
+ long miiport = mtdx.ioaddr + MANAGEMENT;
+ u32 miir;
+ unsigned int mask, data;
+
+ miir = m80x_send_cmd_to_phy(miiport, OP_READ, phyad, regad);
+
+ /* read data */
+ mask = 0x8000;
+ data = 0;
+ while (mask)
+ {
+ /* low MDC */
+ miir &= ~MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ /* read MDI */
+ miir = inl(miiport);
+ if (miir & MASK_MIIR_MII_MDI)
+ data |= mask;
+
+ /* high MDC, and wait */
+ miir |= MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+ m80x_delay((int) 30);
+
+ /* next */
+ mask >>= 1;
+ }
+
+ /* low MDC */
+ miir &= ~MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ return data & 0xffff;
+}
+
+#if 0 /* not used */
+static void mdio_write(struct nic *nic __unused, int phyad, int regad,
+ int data)
+{
+ long miiport = mtdx.ioaddr + MANAGEMENT;
+ u32 miir;
+ unsigned int mask;
+
+ miir = m80x_send_cmd_to_phy(miiport, OP_WRITE, phyad, regad);
+
+ /* write data */
+ mask = 0x8000;
+ while (mask)
+ {
+ /* low MDC, prepare MDO */
+ miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO);
+ if (mask & data)
+ miir |= MASK_MIIR_MII_MDO;
+ outl(miir, miiport);
+
+ /* high MDC */
+ miir |= MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ /* next */
+ mask >>= 1;
+ }
+
+ /* low MDC */
+ miir &= ~MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ return;
+}
+#endif
+
+static void getlinkstatus(struct nic *nic)
+/* function: Routine will read MII Status Register to get link status. */
+/* input : dev... pointer to the adapter block. */
+/* output : none. */
+{
+ unsigned int i, DelayTime = 0x1000;
+
+ mtdx.linkok = 0;
+
+ if (mtdx.PHYType == MysonPHY)
+ {
+ for (i = 0; i < DelayTime; ++i) {
+ if (inl(mtdx.ioaddr + BMCRSR) & LinkIsUp2) {
+ mtdx.linkok = 1;
+ return;
+ }
+ // delay
+ m80x_delay(100);
+ }
+ } else
+ {
+ for (i = 0; i < DelayTime; ++i) {
+ if (mdio_read(nic, mtdx.phys[0], MII_BMSR) & BMSR_LSTATUS) {
+ mtdx.linkok = 1;
+ return;
+ }
+ // delay
+ m80x_delay(100);
+ }
+ }
+}
+
+
+static void getlinktype(struct nic *dev)
+{
+ if (mtdx.PHYType == MysonPHY)
+ { /* 3-in-1 case */
+ if (inl(mtdx.ioaddr + TCRRCR) & FD)
+ mtdx.duplexmode = 2; /* full duplex */
+ else
+ mtdx.duplexmode = 1; /* half duplex */
+ if (inl(mtdx.ioaddr + TCRRCR) & PS10)
+ mtdx.line_speed = 1; /* 10M */
+ else
+ mtdx.line_speed = 2; /* 100M */
+ } else
+ {
+ if (mtdx.PHYType == SeeqPHY) { /* this PHY is SEEQ 80225 */
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], MIIRegister18);
+ if (data & SPD_DET_100)
+ mtdx.line_speed = 2; /* 100M */
+ else
+ mtdx.line_speed = 1; /* 10M */
+ if (data & DPLX_DET_FULL)
+ mtdx.duplexmode = 2; /* full duplex mode */
+ else
+ mtdx.duplexmode = 1; /* half duplex mode */
+ } else if (mtdx.PHYType == AhdocPHY) {
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], DiagnosticReg);
+ if (data & Speed_100)
+ mtdx.line_speed = 2; /* 100M */
+ else
+ mtdx.line_speed = 1; /* 10M */
+ if (data & DPLX_FULL)
+ mtdx.duplexmode = 2; /* full duplex mode */
+ else
+ mtdx.duplexmode = 1; /* half duplex mode */
+ }
+ /* 89/6/13 add, (begin) */
+ else if (mtdx.PHYType == MarvellPHY) {
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], SpecificReg);
+ if (data & Full_Duplex)
+ mtdx.duplexmode = 2; /* full duplex mode */
+ else
+ mtdx.duplexmode = 1; /* half duplex mode */
+ data &= SpeedMask;
+ if (data == Speed_1000M)
+ mtdx.line_speed = 3; /* 1000M */
+ else if (data == Speed_100M)
+ mtdx.line_speed = 2; /* 100M */
+ else
+ mtdx.line_speed = 1; /* 10M */
+ }
+ /* 89/6/13 add, (end) */
+ /* 89/7/27 add, (begin) */
+ else if (mtdx.PHYType == Myson981) {
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], StatusRegister);
+
+ if (data & SPEED100)
+ mtdx.line_speed = 2;
+ else
+ mtdx.line_speed = 1;
+
+ if (data & FULLMODE)
+ mtdx.duplexmode = 2;
+ else
+ mtdx.duplexmode = 1;
+ }
+ /* 89/7/27 add, (end) */
+ /* 89/12/29 add */
+ else if (mtdx.PHYType == LevelOnePHY) {
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], SpecificReg);
+ if (data & LXT1000_Full)
+ mtdx.duplexmode = 2; /* full duplex mode */
+ else
+ mtdx.duplexmode = 1; /* half duplex mode */
+ data &= SpeedMask;
+ if (data == LXT1000_1000M)
+ mtdx.line_speed = 3; /* 1000M */
+ else if (data == LXT1000_100M)
+ mtdx.line_speed = 2; /* 100M */
+ else
+ mtdx.line_speed = 1; /* 10M */
+ }
+ // chage crvalue
+ // mtdx.crvalue&=(~PS10)&(~FD);
+ mtdx.crvalue &= (~PS10) & (~FD) & (~PS1000);
+ if (mtdx.line_speed == 1)
+ mtdx.crvalue |= PS10;
+ else if (mtdx.line_speed == 3)
+ mtdx.crvalue |= PS1000;
+ if (mtdx.duplexmode == 2)
+ mtdx.crvalue |= FD;
+ }
+}
+
+DRIVER ( "MTD80X", nic_driver, pci_driver, mtd80x_driver,
+ mtd_probe, mtd_disable );