patch-2.3.26 linux/drivers/net/sis900.c
Next file: linux/drivers/net/sis900.h
Previous file: linux/drivers/net/ppp_synctty.c
Back to the patch index
Back to the overall index
- Lines: 3015
- Date:
Fri Nov 5 09:52:05 1999
- Orig file:
v2.3.25/linux/drivers/net/sis900.c
- Orig date:
Thu Aug 26 13:05:38 1999
diff -u --recursive --new-file v2.3.25/linux/drivers/net/sis900.c linux/drivers/net/sis900.c
@@ -1,58 +1,29 @@
-/*****************************************************************************/
-/* sis900.c: A SiS 900 PCI Fast Ethernet driver for Linux. */
-/* */
-/* Silicon Integrated System Corporation */
-/* Revision: 1.05 Aug 7 1999 */
-/* */
-/*****************************************************************************/
-
-/*
- Modified from the driver which is originally written by Donald Becker.
-
- This software may be used and distributed according to the terms
- of the GNU Public License (GPL), incorporated herein by reference.
- Drivers based on this skeleton fall under the GPL and must retain
- the authorship (implicit copyright) notice.
-
- The author may be reached as becker@tidalwave.net, or
- Donald Becker
- 312 Severn Ave. #W302
- Annapolis MD 21403
-
- Support and updates [to the original skeleton] available at
- http://www.tidalwave.net/~becker/pci-skeleton.html
+/* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
+ Silicon Integrated System Corporation
+ Revision: 1.05 Aug 7 1999
+
+ Modified from the driver which is originally written by Donald Becker.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+ Drivers based on this skeleton fall under the GPL and must retain
+ the authorship (implicit copyright) notice.
+
+ References:
+ SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support,
+ preliminary Rev. 1.0 Jan. 14, 1998
+ SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support,
+ preliminary Rev. 1.0 Nov. 10, 1998
+ SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution,
+ preliminary Rev. 1.0 Jan. 18, 1998
+ http://www.sis.com.tw/support/databook.htm
+
+ Rev 1.06 Nov. 4 1999 Ollie Lho (ollie@sis.com.tw) Second release
+ Rev 1.05.05 Oct. 29 1999 Ollie Lho (ollie@sis.com.tw) Single buffer Tx/Rx
+ Chin-Shan Li (lcs@sis.com.tw) Added AMD Am79c901 HomePNA PHY support
+ Rev 1.05 Aug. 7 1999 Jim Huang (cmhuang@sis.com.tw) Initial release
*/
-static const char *version =
-"sis900.c:v1.05 8/07/99\n";
-
-static int max_interrupt_work = 20;
-#define sis900_debug debug
-static int sis900_debug = 0;
-
-static int multicast_filter_limit = 128;
-
-#define MAX_UNITS 8 /* More are supported, limit only on options */
-static int speeds[MAX_UNITS] = {100, 100, 100, 100, 100, 100, 100, 100};
-static int full_duplex[MAX_UNITS] = {1, 1, 1, 1, 1, 1, 1, 1};
-
-#define TX_BUF_SIZE 1536
-#define RX_BUF_SIZE 1536
-
-#define TX_DMA_BURST 0
-#define RX_DMA_BURST 0
-#define TX_FIFO_THRESH 16
-#define TxDRNT_100 (1536>>5)
-#define TxDRNT_10 16
-#define RxDRNT_100 8
-#define RxDRNT_10 8
-#define TRUE 1
-#define FALSE 0
-
-/* Operational parameters that usually are not changed. */
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT (4*HZ)
-
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
@@ -65,1845 +36,1244 @@
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
+
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/processor.h> /* Processor type for cache alignment. */
#include <asm/bitops.h>
#include <asm/io.h>
+#include <linux/delay.h>
-#define RUN_AT(x) (jiffies + (x))
+#include "sis900.h"
-#include <linux/delay.h>
+static const char *version =
+"sis900.c: v1.06 11/04/99\n";
-#if LINUX_VERSION_CODE < 0x20123
-#define test_and_set_bit(val, addr) set_bit(val, addr)
-#endif
-#if LINUX_VERSION_CODE <= 0x20139
-#define net_device_stats enet_statistics
-#else
-#define NETSTATS_VER2
-#endif
-#if LINUX_VERSION_CODE < 0x20155 || defined(CARDBUS)
-/* Grrrr, the PCI code changed, but did not consider CardBus... */
-#include <linux/bios32.h>
-#define PCI_SUPPORT_VER1
-#else
-#define PCI_SUPPORT_VER2
-#endif
-#if LINUX_VERSION_CODE < 0x20159
-#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE);
-#else
-#define dev_free_skb(skb) dev_kfree_skb(skb);
-#endif
+static int max_interrupt_work = 20;
+#define sis900_debug debug
+static int sis900_debug = 0;
-/* The I/O extent. */
-#define SIS900_TOTAL_SIZE 0x100
+static int multicast_filter_limit = 128;
-/* This table drives the PCI probe routines. It's mostly boilerplate in all
- of the drivers, and will likely be provided by some future kernel.
- Note the matching code -- the first table entry matchs all 56** cards but
- second only the 1234 card.
-*/
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (4*HZ)
-enum pci_flags_bit {
- PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+struct mac_chip_info {
+ const char *name;
+ u16 vendor_id, device_id, flags;
+ int io_size;
+ struct net_device *(*probe) (struct mac_chip_info *mac, struct pci_dev * pci_dev,
+ struct net_device * net_dev);
};
+static struct net_device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev,
+ struct net_device * net_dev);
-struct pci_id_info {
- const char *name;
- u16 vendor_id, device_id, device_id_mask, flags;
- int io_size;
- struct net_device *(*probe1)(int pci_bus, int pci_devfn, struct net_device *dev,
- long ioaddr, int irq, int chip_idx, int fnd_cnt);
+static struct mac_chip_info mac_chip_table[] = {
+ { "SiS 900 PCI Fast Ethernet", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_900,
+ PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe},
+ { "SiS 7016 PCI Fast Ethernet",PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7016,
+ PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe},
+ {0,}, /* 0 terminated list. */
};
-static struct net_device * sis900_probe1(int pci_bus, int pci_devfn,
- struct net_device *dev, long ioaddr,
- int irq, int chp_idx, int fnd_cnt);
-
-static struct pci_id_info pci_tbl[] =
-{{ "SiS 900 PCI Fast Ethernet",
- 0x1039, 0x0900, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x100, sis900_probe1},
- { "SiS 7016 PCI Fast Ethernet",
- 0x1039, 0x7016, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x100, sis900_probe1},
- {0,}, /* 0 terminated list. */
-};
+static void sis900_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
+static void amd79c901_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
-/* The capability table matches the chip table above. */
-enum {HAS_MII_XCVR=0x01, HAS_CHIP_XCVR=0x02, HAS_LNK_CHNG=0x04};
-static int sis_cap_tbl[] = {
- HAS_MII_XCVR|HAS_CHIP_XCVR|HAS_LNK_CHNG,
- HAS_MII_XCVR|HAS_CHIP_XCVR|HAS_LNK_CHNG,
+static struct mii_chip_info {
+ const char * name;
+ u16 phy_id0;
+ u16 phy_id1;
+ void (*read_mode) (struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
+} mii_chip_table[] = {
+ {"SiS 900 Internal MII PHY", 0x001d, 0x8000, sis900_read_mode},
+ {"SiS 7014 Physical Layer Solution", 0x0016, 0xf830,sis900_read_mode},
+ {"AMD 79C901 10BASE-T PHY", 0x0000, 0x35b9, amd79c901_read_mode},
+ {"AMD 79C901 HomePNA PHY", 0x0000, 0x35c8, amd79c901_read_mode},
+ {0,},
};
-/* The rest of these values should never change. */
-#define NUM_TX_DESC 16 /* Number of Tx descriptor registers. */
-#define NUM_RX_DESC 8 /* Number of Rx descriptor registers. */
-
-/* Symbolic offsets to registers. */
-enum SIS900_registers {
- cr=0x0, //Command Register
- cfg=0x4, //Configuration Register
- mear=0x8, //EEPROM Access Register
- ptscr=0xc, //PCI Test Control Register
- isr=0x10, //Interrupt Status Register
- imr=0x14, //Interrupt Mask Register
- ier=0x18, //Interrupt Enable Register
- epar=0x18, //Enhanced PHY Access Register
- txdp=0x20, //Transmit Descriptor Pointer Register
- txcfg=0x24, //Transmit Configuration Register
- rxdp=0x30, //Receive Descriptor Pointer Register
- rxcfg=0x34, //Receive Configuration Register
- flctrl=0x38, //Flow Control Register
- rxlen=0x3c, //Receive Packet Length Register
- rfcr=0x48, //Receive Filter Control Register
- rfdr=0x4C, //Receive Filter Data Register
- pmctrl=0xB0, //Power Management Control Register
- pmer=0xB4 //Power Management Wake-up Event Register
+struct mii_phy {
+ struct mii_phy * next;
+ struct mii_chip_info * chip_info;
+ int phy_addr;
+ u16 status;
};
-#define RESET 0x00000100
-#define SWI 0x00000080
-#define RxRESET 0x00000020
-#define TxRESET 0x00000010
-#define RxDIS 0x00000008
-#define RxENA 0x00000004
-#define TxDIS 0x00000002
-#define TxENA 0x00000001
-
-#define BISE 0x80000000
-#define EUPHCOM 0x00000100
-#define REQALG 0x00000080
-#define SB 0x00000040
-#define POW 0x00000020
-#define EXD 0x00000010
-#define PESEL 0x00000008
-#define LPM 0x00000004
-#define BEM 0x00000001
-
-/* Interrupt register bits, using my own meaningful names. */
-#define WKEVT 0x10000000
-#define TxPAUSEEND 0x08000000
-#define TxPAUSE 0x04000000
-#define TxRCMP 0x02000000
-#define RxRCMP 0x01000000
-#define DPERR 0x00800000
-#define SSERR 0x00400000
-#define RMABT 0x00200000
-#define RTABT 0x00100000
-#define RxSOVR 0x00010000
-#define HIBERR 0x00008000
-#define SWINT 0x00001000
-#define MIBINT 0x00000800
-#define TxURN 0x00000400
-#define TxIDLE 0x00000200
-#define TxERR 0x00000100
-#define TxDESC 0x00000080
-#define TxOK 0x00000040
-#define RxORN 0x00000020
-#define RxIDLE 0x00000010
-#define RxEARLY 0x00000008
-#define RxERR 0x00000004
-#define RxDESC 0x00000002
-#define RxOK 0x00000001
-
-#define IE 0x00000001
-
-#define TxCSI 0x80000000
-#define TxHBI 0x40000000
-#define TxMLB 0x20000000
-#define TxATP 0x10000000
-#define TxIFG 0x0C000000
-#define TxMXF 0x03800000
-#define TxMXF_shift 0x23
-#define TxMXDMA 0x00700000
-#define TxMXDMA_shift 20
-#define TxRTCNT 0x000F0000
-#define TxRTCNT_shift 16
-#define TxFILLT 0x00007F00
-#define TxFILLT_shift 8
-#define TxDRNT 0x0000007F
-
-#define RxAEP 0x80000000
-#define RxARP 0x40000000
-#define RxATP 0x10000000
-#define RxAJAB 0x08000000
-#define RxMXF 0x03800000
-#define RxMXF_shift 23
-#define RxMXDMA 0x00700000
-#define RxMXDMA_shift 20
-#define RxDRNT 0x0000007F
-
-#define RFEN 0x80000000
-#define RFAAB 0x40000000
-#define RFAAM 0x20000000
-#define RFAAP 0x10000000
-#define RFPromiscuous (RFAAB|RFAAM|RFAAP)
-#define RFAA_shift 28
-#define RFEP 0x00070000
-#define RFEP_shift 16
-
-#define RFDAT 0x0000FFFF
-
-#define OWN 0x80000000
-#define MORE 0x40000000
-#define INTR 0x20000000
-#define OK 0x08000000
-#define DSIZE 0x00000FFF
-
-#define SUPCRC 0x10000000
-#define ABORT 0x04000000
-#define UNDERRUN 0x02000000
-#define NOCARRIER 0x01000000
-#define DEFERD 0x00800000
-#define EXCDEFER 0x00400000
-#define OWCOLL 0x00200000
-#define EXCCOLL 0x00100000
-#define COLCNT 0x000F0000
-
-#define INCCRC 0x10000000
-// ABORT 0x04000000
-#define OVERRUN 0x02000000
-#define DEST 0x01800000
-#define BCAST 0x01800000
-#define MCAST 0x01000000
-#define UNIMATCH 0x00800000
-#define TOOLONG 0x00400000
-#define RUNT 0x00200000
-#define RXISERR 0x00100000
-#define CRCERR 0x00080000
-#define FAERR 0x00040000
-#define LOOPBK 0x00020000
-#define RXCOL 0x00010000
-
-#define EuphLiteEEMACAddr 0x08
-#define EuphLiteEEVendorID 0x02
-#define EuphLiteEEDeviceID 0x03
-#define EuphLiteEECardTypeRev 0x0b
-#define EuphLiteEEPlexusRev 0x0c
-#define EuphLiteEEChecksum 0x0f
-
-#define RXSTS_shift 18
-#define OWN 0x80000000
-#define MORE 0x40000000
-#define INTR 0x20000000
-#define OK 0x08000000
-#define DSIZE 0x00000FFF
-/* MII register offsets */
-#define MII_CONTROL 0x0000
-#define MII_STATUS 0x0001
-#define MII_PHY_ID0 0x0002
-#define MII_PHY_ID1 0x0003
-#define MII_ANAR 0x0004
-#define MII_ANLPAR 0x0005
-#define MII_ANER 0x0006
-/* MII Control register bit definitions. */
-#define MIICNTL_FDX 0x0100
-#define MIICNTL_RST_AUTO 0x0200
-#define MIICNTL_ISOLATE 0x0400
-#define MIICNTL_PWRDWN 0x0800
-#define MIICNTL_AUTO 0x1000
-#define MIICNTL_SPEED 0x2000
-#define MIICNTL_LPBK 0x4000
-#define MIICNTL_RESET 0x8000
-/* MII Status register bit significance. */
-#define MIISTAT_EXT 0x0001
-#define MIISTAT_JAB 0x0002
-#define MIISTAT_LINK 0x0004
-#define MIISTAT_CAN_AUTO 0x0008
-#define MIISTAT_FAULT 0x0010
-#define MIISTAT_AUTO_DONE 0x0020
-#define MIISTAT_CAN_T 0x0800
-#define MIISTAT_CAN_T_FDX 0x1000
-#define MIISTAT_CAN_TX 0x2000
-#define MIISTAT_CAN_TX_FDX 0x4000
-#define MIISTAT_CAN_T4 0x8000
-/* MII NWAY Register Bits ...
-** valid for the ANAR (Auto-Negotiation Advertisement) and
-** ANLPAR (Auto-Negotiation Link Partner) registers */
-#define MII_NWAY_NODE_SEL 0x001f
-#define MII_NWAY_CSMA_CD 0x0001
-#define MII_NWAY_T 0x0020
-#define MII_NWAY_T_FDX 0x0040
-#define MII_NWAY_TX 0x0080
-#define MII_NWAY_TX_FDX 0x0100
-#define MII_NWAY_T4 0x0200
-#define MII_NWAY_RF 0x2000
-#define MII_NWAY_ACK 0x4000
-#define MII_NWAY_NP 0x8000
-
-/* MII Auto-Negotiation Expansion Register Bits */
-#define MII_ANER_PDF 0x0010
-#define MII_ANER_LP_NP_ABLE 0x0008
-#define MII_ANER_NP_ABLE 0x0004
-#define MII_ANER_RX_PAGE 0x0002
-#define MII_ANER_LP_AN_ABLE 0x0001
-#define HALF_DUPLEX 1
-#define FDX_CAPABLE_DUPLEX_UNKNOWN 2
-#define FDX_CAPABLE_HALF_SELECTED 3
-#define FDX_CAPABLE_FULL_SELECTED 4
-#define HW_SPEED_UNCONFIG 0
-#define HW_SPEED_10_MBPS 10
-#define HW_SPEED_100_MBPS 100
-#define HW_SPEED_DEFAULT (HW_SPEED_10_MBPS)
-
-#define ACCEPT_ALL_PHYS 0x01
-#define ACCEPT_ALL_MCASTS 0x02
-#define ACCEPT_ALL_BCASTS 0x04
-#define ACCEPT_ALL_ERRORS 0x08
-#define ACCEPT_CAM_QUALIFIED 0x10
-#define MAC_LOOPBACK 0x20
-//#define FDX_CAPABLE_FULL_SELECTED 4
-#define CRC_SIZE 4
-#define MAC_HEADER_SIZE 14
-
-typedef struct _EuphLiteDesc {
- u32 llink;
- unsigned char* buf;
- u32 physAddr;
- /* Hardware sees the physical address of descriptor */
- u32 plink;
- u32 cmdsts;
- u32 bufPhys;
-} EuphLiteDesc;
+typedef struct _BufferDesc {
+ u32 link;
+ u32 cmdsts;
+ u32 bufptr;
+} BufferDesc;
struct sis900_private {
- char devname[8]; /* Used only for kernel debugging. */
- const char *product_name;
- struct net_device *next_module;
- int chip_id;
- int chip_revision;
- unsigned char pci_bus, pci_devfn;
-#if LINUX_VERSION_CODE > 0x20139
- struct net_device_stats stats;
-#else
- struct enet_statistics stats;
-#endif
- struct timer_list timer; /* Media selection timer. */
- unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */
- unsigned int cur_tx, dirty_tx, tx_flag;
-
- /* The saved address of a sent-in-place packet/buffer, for skfree(). */
- struct sk_buff* tx_skbuff[NUM_TX_DESC];
- EuphLiteDesc tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */
- EuphLiteDesc rx_buf[NUM_RX_DESC];
- unsigned char *rx_bufs;
- unsigned char *tx_bufs; /* Tx bounce buffer region. */
- char phys[4]; /* MII device addresses. */
- int phy_idx; /* Support Max 4 PHY */
- u16 pmd_status;
- unsigned int tx_full; /* The Tx queue is full. */
- int MediaSpeed; /* user force speed */
- int MediaDuplex; /* user force duplex */
- int full_duplex; /* Full/Half-duplex. */
- int speeds; /* 100/10 Mbps. */
- u16 LinkOn;
- u16 LinkChange;
+ struct net_device *next_module;
+ struct net_device_stats stats;
+ struct pci_dev * pci_dev;
+
+ struct mac_chip_info * mac;
+ struct mii_phy * mii;
+ unsigned int cur_phy;
+
+ struct timer_list timer; /* Link status detection timer. */
+ unsigned int cur_rx, dirty_rx;
+ unsigned int cur_tx, dirty_tx;
+
+ /* The saved address of a sent/receive-in-place packet buffer */
+ struct sk_buff *tx_skbuff[NUM_TX_DESC];
+ struct sk_buff *rx_skbuff[NUM_RX_DESC];
+ BufferDesc tx_ring[NUM_TX_DESC];
+ BufferDesc rx_ring[NUM_RX_DESC];
+ unsigned int tx_full; /* The Tx queue is full. */
+
+ int LinkOn;
};
#ifdef MODULE
#if LINUX_VERSION_CODE > 0x20115
MODULE_AUTHOR("Jim Huang <cmhuang@sis.com.tw>");
MODULE_DESCRIPTION("SiS 900 PCI Fast Ethernet driver");
-MODULE_PARM(speeds, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(multicast_filter_limit, "i");
MODULE_PARM(max_interrupt_work, "i");
MODULE_PARM(debug, "i");
#endif
#endif
-static int sis900_open(struct net_device *dev);
+static int sis900_open(struct net_device *net_dev);
+static int sis900_mii_probe (struct net_device * net_dev);
+static void sis900_init_rxfilter (struct net_device * net_dev);
static u16 read_eeprom(long ioaddr, int location);
-static int mdio_read(struct net_device *dev, int phy_id, int location);
-static void mdio_write(struct net_device *dev, int phy_id, int location, int val);
+static u16 mdio_read(struct net_device *net_dev, int phy_id, int location);
+static void mdio_write(struct net_device *net_dev, int phy_id, int location, int val);
static void sis900_timer(unsigned long data);
-static void sis900_tx_timeout(struct net_device *dev);
-static void sis900_init_ring(struct net_device *dev);
-static int sis900_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static int sis900_rx(struct net_device *dev);
+static void sis900_check_mode (struct net_device *net_dev, struct mii_phy *mii_phy);
+static void sis900_tx_timeout(struct net_device *net_dev);
+static void sis900_init_tx_ring(struct net_device *net_dev);
+static void sis900_init_rx_ring(struct net_device *net_dev);
+static int sis900_start_xmit(struct sk_buff *skb, struct net_device *net_dev);
+static int sis900_rx(struct net_device *net_dev);
+static void sis900_finish_xmit (struct net_device *net_dev);
static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-static int sis900_close(struct net_device *dev);
-static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static struct enet_statistics *sis900_get_stats(struct net_device *dev);
-static void set_rx_mode(struct net_device *dev);
-static void sis900_reset(struct net_device *dev);
-static u16 elAutoNegotiate(struct net_device *dev, int phy_id, int *duplex, int *speed);
-static void elSetCapability(struct net_device *dev, int phy_id, int duplex, int speed);
-static u16 elPMDreadMode(struct net_device *dev, int phy_id, int *speed, int *duplex);
-static u16 elMIIpollBit(struct net_device *dev, int phy_id, int location, u16 mask, u16 polarity, u16 *value);
-static void elSetMediaType(struct net_device *dev, int speed, int duplex);
+static int sis900_close(struct net_device *net_dev);
+static int mii_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd);
+static struct enet_statistics *sis900_get_stats(struct net_device *net_dev);
+static u16 sis900_compute_hashtable_index(u8 *addr);
+static void set_rx_mode(struct net_device *net_dev);
+static void sis900_reset(struct net_device *net_dev);
/* A list of all installed SiS900 devices, for removing the driver module. */
static struct net_device *root_sis900_dev = NULL;
-/* Ideally we would detect all network cards in slot order. That would
- be best done a central PCI probe dispatch, which wouldn't work
- well when dynamically adding drivers. So instead we detect just the
- SiS 900 cards in slot order. */
-
-int sis900_probe(struct net_device *dev)
-{
- int cards_found = 0;
- int pci_index = 0;
- unsigned char pci_bus, pci_device_fn;
-
- if ( ! pcibios_present())
- return -ENODEV;
-
- for (;pci_index < 0xff; pci_index++) {
- u16 vendor, device, pci_command, new_command;
- int chip_idx, irq;
- long ioaddr;
-
- if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
- pci_index,
- &pci_bus, &pci_device_fn)
- != PCIBIOS_SUCCESSFUL) {
- break;
- }
- pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID,
- &vendor);
- pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID,
- &device);
-
- for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
- if (vendor == pci_tbl[chip_idx].vendor_id &&
- (device & pci_tbl[chip_idx].device_id_mask) ==
- pci_tbl[chip_idx].device_id)
- break;
- if (pci_tbl[chip_idx].vendor_id == 0) /* Compiled out! */
- continue;
-
- {
- struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
- ioaddr = pdev->resource[0].start;
- irq = pdev->irq;
- }
-
- if ((pci_tbl[chip_idx].flags & PCI_USES_IO) &&
- check_region(ioaddr, pci_tbl[chip_idx].io_size))
- continue;
-
- /* Activate the card: fix for brain-damaged Win98 BIOSes. */
- pcibios_read_config_word(pci_bus, pci_device_fn,
- PCI_COMMAND, &pci_command);
- new_command = pci_command | (pci_tbl[chip_idx].flags & 7);
- if (pci_command != new_command) {
- printk(KERN_INFO " The PCI BIOS has not enabled the"
- " device at %d/%d!"
- "Updating PCI command %4.4x->%4.4x.\n",
- pci_bus, pci_device_fn,
- pci_command, new_command);
-
- pcibios_write_config_word(pci_bus, pci_device_fn,
- PCI_COMMAND, new_command);
- }
-
- dev = pci_tbl[chip_idx].probe1(pci_bus,
- pci_device_fn,
- dev,
- ioaddr,
- irq,
- chip_idx,
- cards_found);
-
- if (dev && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) {
- u8 pci_latency;
-
- pcibios_read_config_byte(pci_bus, pci_device_fn,
- PCI_LATENCY_TIMER, &pci_latency);
-
- if (pci_latency < 32) {
- printk(KERN_NOTICE " PCI latency timer (CFLT) is "
- "unreasonably low at %d. Setting to 64 clocks.\n",
- pci_latency);
- pcibios_write_config_byte(pci_bus, pci_device_fn,
- PCI_LATENCY_TIMER, 64);
- }
- }
- dev = 0;
- cards_found++;
- }
- return cards_found ? 0 : -ENODEV;
-}
-
-static struct net_device * sis900_probe1( int pci_bus,
- int pci_devfn,
- struct net_device *dev,
- long ioaddr,
- int irq,
- int chip_idx,
- int found_cnt)
-{
- static int did_version = 0; /* Already printed version info. */
- struct sis900_private *tp;
- u16 status;
- int duplex = found_cnt < MAX_UNITS ? full_duplex[found_cnt] : 0 ;
- int speed = found_cnt < MAX_UNITS ? speeds[found_cnt] : 0 ;
- int phy=0, phy_idx=0, i;
-
- if (did_version++ == 0)
- printk(KERN_INFO "%s", version);
-
- dev = init_etherdev(dev, 0);
-
- if(dev==NULL)
- return NULL;
-
- printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ",
- dev->name, pci_tbl[chip_idx].name, ioaddr, irq);
-
- if ((u16)read_eeprom(ioaddr, EuphLiteEEVendorID) != 0xffff) {
- for (i = 0; i < 3; i++)
- ((u16 *)(dev->dev_addr))[i] =
- read_eeprom(ioaddr,i+EuphLiteEEMACAddr);
- for (i = 0; i < 5; i++)
- printk("%2.2x:", (u8)dev->dev_addr[i]);
- printk("%2.2x.\n", dev->dev_addr[i]);
- } else
- printk(KERN_INFO "Error EEPROM read\n");
-
- /* We do a request_region() to register /proc/ioports info. */
- request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name);
-
- dev->base_addr = ioaddr;
- dev->irq = irq;
-
- /* Some data structures must be quadword aligned. */
- tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA);
- if(tp==NULL)
- {
- release_region(ioaddr, pci_tbl[chip_idx].io_size);
- return NULL;
- }
- memset(tp, 0, sizeof(*tp));
- dev->priv = tp;
-
- tp->next_module = root_sis900_dev;
- root_sis900_dev = dev;
-
- tp->chip_id = chip_idx;
- tp->pci_bus = pci_bus;
- tp->pci_devfn = pci_devfn;
-
- /* Find the connected MII xcvrs.
- Doing this in open() would allow detecting external xcvrs later, but
- takes too much time. */
- if (sis_cap_tbl[chip_idx] & HAS_MII_XCVR) {
- for (phy = 0, phy_idx = 0;
- phy < 32 && phy_idx < sizeof(tp->phys); phy++)
- {
- int mii_status ;
- mii_status = mdio_read(dev, phy, MII_STATUS);
-
- if (mii_status != 0xffff && mii_status != 0x0000) {
- tp->phy_idx = phy_idx;
- tp->phys[phy_idx++] = phy;
- tp->pmd_status=mdio_read(dev, phy, MII_STATUS);
- printk(KERN_INFO "%s: MII transceiver found "
- "at address %d.\n",
- dev->name, phy);
- break;
- }
- }
-
- if (phy_idx == 0) {
- printk(KERN_INFO "%s: No MII transceivers found!\n",
- dev->name);
- tp->phys[0] = -1;
- tp->pmd_status = 0;
- }
- } else {
- tp->phys[0] = -1;
- tp->pmd_status = 0;
- }
-
- if ((tp->pmd_status > 0) && (phy_idx > 0)) {
- if (sis900_debug > 1) {
- printk(KERN_INFO "duplex=%d, speed=%d\n",
- duplex, speed);
+/* walk through every ethernet PCI devices to see if some of them are matched with our card list*/
+int sis900_probe (struct net_device * net_dev)
+{
+ int found = 0;
+ struct pci_dev * pci_dev = NULL;
+
+ if (!pci_present())
+ return -ENODEV;
+
+ while ((pci_dev = pci_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_dev)) != NULL) {
+ /* pci_dev contains all ethernet devices */
+ u32 pci_io_base;
+ struct mac_chip_info * mac;
+
+ for (mac = mac_chip_table; mac->vendor_id; mac++) {
+ /* try to match our card list */
+ if (pci_dev->vendor == mac->vendor_id &&
+ pci_dev->device == mac->device_id)
+ break;
}
- if (!duplex && !speed) {
- // auto-config media type
- // Set full capability
- if (sis900_debug > 1) {
- printk(KERN_INFO "Auto Config ...\n");
- }
- elSetCapability(dev, tp->phys[tp->phy_idx], 1, 100);
- tp->pmd_status=elAutoNegotiate(dev,
- tp->phys[tp->phy_idx],
- &tp->full_duplex,
- &tp->speeds);
- } else {
- tp->MediaSpeed = speed;
- tp->MediaDuplex = duplex;
- elSetCapability(dev, tp->phys[tp->phy_idx],
- duplex, speed);
- elAutoNegotiate(dev, tp->phys[tp->phy_idx],
- &tp->full_duplex,
- &tp->speeds);
- status = mdio_read(dev, phy, MII_ANLPAR);
- if ( !(status & (MII_NWAY_T | MII_NWAY_T_FDX |
- MII_NWAY_TX | MII_NWAY_TX_FDX )))
- {
- u16 cmd=0;
- cmd |= ( speed == 100 ?
- MIICNTL_SPEED : 0 );
- cmd |= ( duplex ? MIICNTL_FDX : 0 );
- mdio_write(dev, phy, MII_CONTROL, cmd);
- elSetMediaType(dev, speed==100 ?
- HW_SPEED_100_MBPS :
- HW_SPEED_10_MBPS,
- duplex ?
- FDX_CAPABLE_FULL_SELECTED:
- FDX_CAPABLE_HALF_SELECTED);
- elMIIpollBit(dev, phy, MII_STATUS,
- MIISTAT_LINK, TRUE, &status);
- } else {
- status = mdio_read(dev, phy, MII_STATUS);
- }
+
+ if (mac->vendor_id == 0)
+ /* pci_dev does not match any of our cards */
+ continue;
+
+ /* now, pci_dev should be either 900 or 7016 */
+ pci_io_base = pci_dev->resource[0].start;
+ if ((mac->flags & PCI_COMMAND_IO ) &&
+ check_region(pci_io_base, mac->io_size))
+ continue;
+
+ /* setup various bits in PCI command register */
+ pci_set_master(pci_dev);
+
+ /* do the real low level jobs */
+ net_dev = mac->probe(mac, pci_dev, net_dev);
+
+ if (net_dev != NULL) {
+ found++;
}
+ net_dev = NULL;
+ }
+ return found ? 0 : -ENODEV;
+}
+
+static struct net_device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev,
+ struct net_device * net_dev)
+{
+ struct sis900_private *sis_priv;
+ long ioaddr = pci_dev->resource[0].start;
+ int irq = pci_dev->irq;
+ static int did_version = 0;
+ u16 signature;
+ int i;
+
+ if (did_version++ == 0)
+ printk(KERN_INFO "%s", version);
+
+ /* check to see if we have sane EEPROM */
+ signature = (u16) read_eeprom(ioaddr, EEPROMSignature);
+ if (signature == 0xffff || signature == 0x0000) {
+ printk (KERN_INFO "%s: Error EERPOM read %x\n",
+ net_dev->name, signature);
+ return NULL;
+ }
+
+ if ((net_dev = init_etherdev(net_dev, 0)) == NULL)
+ return NULL;
+
+ printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name, mac->name,
+ ioaddr, irq);
+
+ /* get MAC address from EEPROM */
+ for (i = 0; i < 3; i++)
+ ((u16 *)(net_dev->dev_addr))[i] = read_eeprom(ioaddr, i+EEPROMMACAddr);
+ for (i = 0; i < 5; i++)
+ printk("%2.2x:", (u8)net_dev->dev_addr[i]);
+ printk("%2.2x.\n", net_dev->dev_addr[i]);
+
+ if ((net_dev->priv = kmalloc(sizeof(struct sis900_private), GFP_KERNEL)) == NULL) {
+ unregister_netdevice(net_dev);
+ return NULL;
+ }
+
+ sis_priv = net_dev->priv;
+ memset(sis_priv, 0, sizeof(struct sis900_private));
+
+ /* We do a request_region() to register /proc/ioports info. */
+ request_region(ioaddr, mac->io_size, net_dev->name);
+ net_dev->base_addr = ioaddr;
+ net_dev->irq = irq;
+ sis_priv->pci_dev = pci_dev;
+ sis_priv->mac = mac;
+
+ /* probe for mii transciver */
+ if (sis900_mii_probe(net_dev) == 0) {
+ unregister_netdev(net_dev);
+ kfree(sis_priv);
+ release_region(ioaddr, mac->io_size);
+ return NULL;
+ }
+
+ sis_priv->next_module = root_sis900_dev;
+ root_sis900_dev = net_dev;
+
+ /* The SiS900-specific entries in the device structure. */
+ net_dev->open = &sis900_open;
+ net_dev->hard_start_xmit = &sis900_start_xmit;
+ net_dev->stop = &sis900_close;
+ net_dev->get_stats = &sis900_get_stats;
+ net_dev->set_multicast_list = &set_rx_mode;
+ net_dev->do_ioctl = &mii_ioctl;
- if (tp->pmd_status & MIISTAT_LINK)
- tp->LinkOn = TRUE;
- else
- tp->LinkOn = FALSE;
+ return net_dev;
+}
- tp->LinkChange = FALSE;
+static int sis900_mii_probe (struct net_device * net_dev)
+{
+ struct sis900_private * sis_priv = (struct sis900_private *)net_dev->priv;
+ int phy_addr;
- }
+ sis_priv->mii = NULL;
+
+ /* search for total of 32 possible mii phy addresses */
+ for (phy_addr = 0; phy_addr < 32; phy_addr++) {
+ u16 mii_status;
+ u16 phy_id0, phy_id1;
+ int i;
+
+ mii_status = mdio_read(net_dev, phy_addr, MII_STATUS);
+ if (mii_status == 0xffff || mii_status == 0x0000)
+ /* the mii is not accessable, try next one */
+ continue;
+
+ phy_id0 = mdio_read(net_dev, phy_addr, MII_PHY_ID0);
+ phy_id1 = mdio_read(net_dev, phy_addr, MII_PHY_ID1);
+
+ /* search our mii table for the current mii */
+ for (i = 0; mii_chip_table[i].phy_id1; i++)
+ if (phy_id0 == mii_chip_table[i].phy_id0) {
+ struct mii_phy * mii_phy;
+
+ printk(KERN_INFO
+ "%s: %s transceiver found at address %d.\n",
+ net_dev->name, mii_chip_table[i].name,
+ phy_addr);;
+ if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) != NULL) {
+ mii_phy->chip_info = mii_chip_table+i;
+ mii_phy->phy_addr = phy_addr;
+ mii_phy->status = mdio_read(net_dev, phy_addr,
+ MII_STATUS);
+ mii_phy->next = sis_priv->mii;
+ sis_priv->mii = mii_phy;
+ }
+ /* the current mii is on our mii_info_table,
+ try next address */
+ break;
+ }
+ }
+
+ if (sis_priv->mii == NULL) {
+ printk(KERN_INFO "%s: No MII transceivers found!\n",
+ net_dev->name);
+ return 0;
+ }
- if (sis900_debug > 1) {
- if (tp->full_duplex == FDX_CAPABLE_FULL_SELECTED) {
- printk(KERN_INFO "%s: Media type is Full Duplex.\n",
- dev->name);
- } else {
- printk(KERN_INFO "%s: Media type is Half Duplex.\n",
- dev->name);
- }
- if (tp->speeds == HW_SPEED_100_MBPS) {
- printk(KERN_INFO "%s: Speed is 100mbps.\n", dev->name);
- } else {
- printk(KERN_INFO "%s: Speed is 10mbps.\n", dev->name);
- }
- }
-
- /* The SiS900-specific entries in the device structure. */
- dev->open = &sis900_open;
- dev->hard_start_xmit = &sis900_start_xmit;
- dev->stop = &sis900_close;
- dev->get_stats = &sis900_get_stats;
- dev->set_multicast_list = &set_rx_mode;
- dev->do_ioctl = &mii_ioctl;
-
- return dev;
-}
-
-/* Serial EEPROM section. */
-
-/* EEPROM_Ctrl bits. */
-#define EECLK 0x00000004 /* EEPROM shift clock. */
-#define EECS 0x00000008 /* EEPROM chip select. */
-#define EEDO 0x00000002 /* EEPROM chip data out. */
-#define EEDI 0x00000001 /* EEPROM chip data in. */
+ /* arbitrary choose that last PHY and current PHY */
+ sis_priv->cur_phy = sis_priv->mii->phy_addr;
+ printk(KERN_INFO "%s: Using %s as default\n", net_dev->name,
+ sis_priv->mii->chip_info->name);
-/* Delay between EEPROM clock transitions.
- No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
- */
+ if (sis_priv->mii->status & MII_STAT_LINK)
+ sis_priv->LinkOn = TRUE;
+ else
+ sis_priv->LinkOn = FALSE;
-#define eeprom_delay() inl(ee_addr)
+ return 1;
+}
-/* The EEPROM commands include the alway-set leading bit. */
-#define EEread 0x0180
-#define EEwrite 0x0140
-#define EEerase 0x01C0
-#define EEwriteEnable 0x0130
-#define EEwriteDisable 0x0100
-#define EEeraseAll 0x0120
-#define EEwriteAll 0x0110
-#define EEaddrMask 0x013F
-#define EEcmdShift 16
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay() inl(ee_addr)
+/* Read Serial EEPROM through EEPROM Access Register, Note that location is
+ in word (16 bits) unit */
static u16 read_eeprom(long ioaddr, int location)
{
- int i;
- u16 retval = 0;
- long ee_addr = ioaddr + mear;
- u32 read_cmd = location | EEread;
-
- outl(0, ee_addr);
- eeprom_delay();
- outl(EECLK, ee_addr);
- eeprom_delay();
-
- /* Shift the read command bits out. */
- for (i = 8; i >= 0; i--) {
- u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS;
- outl(dataval, ee_addr);
- eeprom_delay();
- outl(dataval | EECLK, ee_addr);
- eeprom_delay();
- }
- outb(EECS, ee_addr);
- eeprom_delay();
-
- for (i = 16; i > 0; i--) {
- outl(EECS, ee_addr);
- eeprom_delay();
- outl(EECS | EECLK, ee_addr);
- eeprom_delay();
- retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0);
- eeprom_delay();
- }
-
- /* Terminate the EEPROM access. */
- outl(0, ee_addr);
- eeprom_delay();
- outl(EECLK, ee_addr);
- return (retval);
+ int i;
+ u16 retval = 0;
+ long ee_addr = ioaddr + mear;
+ u32 read_cmd = location | EEread;
+
+ outl(0, ee_addr);
+ eeprom_delay();
+ outl(EECLK, ee_addr);
+ eeprom_delay();
+
+ /* Shift the read command (9) bits out. */
+ for (i = 8; i >= 0; i--) {
+ u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS;
+ outl(dataval, ee_addr);
+ eeprom_delay();
+ outl(dataval | EECLK, ee_addr);
+ eeprom_delay();
+ }
+ outb(EECS, ee_addr);
+ eeprom_delay();
+
+ /* read the 16-bits data in */
+ for (i = 16; i > 0; i--) {
+ outl(EECS, ee_addr);
+ eeprom_delay();
+ outl(EECS | EECLK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0);
+ eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ outl(0, ee_addr);
+ eeprom_delay();
+ outl(EECLK, ee_addr);
+
+ return (retval);
}
-/* MII serial management: mostly bogus for now. */
/* Read and write the MII management registers using software-generated
- serial MDIO protocol.
- The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
- met by back-to-back PCI I/O cycles, but we insert a delay to avoid
- "overclocking" issues. */
-
+ serial MDIO protocol. Note that the command bits and data bits are
+ send out seperately */
#define mdio_delay() inl(mdio_addr)
-#define MIIread 0x6000
-#define MIIwrite 0x6002
-#define MIIpmdMask 0x0F80
-#define MIIpmdShift 7
-#define MIIregMask 0x007C
-#define MIIregShift 2
-#define MIIturnaroundBits 2
-#define MIIcmdLen 16
-#define MIIcmdShift 16
-#define MIIreset 0xFFFFFFFF
-#define MIIwrLen 32
-
-#define MDC 0x00000040
-#define MDDIR 0x00000020
-#define MDIO 0x00000010
-
static void mdio_idle(long mdio_addr)
{
- outl(MDIO | MDDIR, mdio_addr);
- mdio_delay();
- outl(MDIO | MDDIR | MDC, mdio_addr);
+ outl(MDIO | MDDIR, mdio_addr);
+ mdio_delay();
+ outl(MDIO | MDDIR | MDC, mdio_addr);
}
/* Syncronize the MII management interface by shifting 32 one bits out. */
static void mdio_reset(long mdio_addr)
{
- int i;
+ int i;
- for (i = 31; i >= 0; i--) {
- outl(MDDIR | MDIO, mdio_addr);
- mdio_delay();
- outl(MDDIR | MDIO | MDC, mdio_addr);
- mdio_delay();
- }
- return;
-}
-
-static int mdio_read(struct net_device *dev, int phy_id, int location)
-{
- long mdio_addr = dev->base_addr + mear;
- int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
- int retval = 0;
- int i;
-
- mdio_reset(mdio_addr);
- mdio_idle(mdio_addr);
-
- for (i = 15; i >= 0; i--) {
- int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
- outl(dataval, mdio_addr);
- outl(dataval | MDC, mdio_addr);
- }
-
- /* Read the two transition, 16 data, and wire-idle bits. */
- for (i = 16; i > 0; i--) {
- outl(0, mdio_addr);
- //mdio_delay();
- retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0);
- outl(MDC, mdio_addr);
- mdio_delay();
- }
- return retval;
-}
-
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
-{
- long mdio_addr = dev->base_addr + mear;
- int mii_cmd = MIIwrite|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
- int i;
-
- mdio_reset(mdio_addr);
- mdio_idle(mdio_addr);
-
- /* Shift the command bits out. */
- for (i = 31; i >= 0; i--) {
- int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
- outb(dataval, mdio_addr);
- mdio_delay();
- outb(dataval | MDC, mdio_addr);
- mdio_delay();
- }
- mdio_delay();
- /* Clear out extra bits. */
- for (i = 2; i > 0; i--) {
- outb(0, mdio_addr);
- mdio_delay();
- outb(MDC, mdio_addr);
- mdio_delay();
- }
- return;
+ for (i = 31; i >= 0; i--) {
+ outl(MDDIR | MDIO, mdio_addr);
+ mdio_delay();
+ outl(MDDIR | MDIO | MDC, mdio_addr);
+ mdio_delay();
+ }
+ return;
+}
+
+static u16 mdio_read(struct net_device *net_dev, int phy_id, int location)
+{
+ long mdio_addr = net_dev->base_addr + mear;
+ int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
+ u16 retval = 0;
+ int i;
+
+ mdio_reset(mdio_addr);
+ mdio_idle(mdio_addr);
+
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
+ outl(dataval, mdio_addr);
+ mdio_delay();
+ outl(dataval | MDC, mdio_addr);
+ mdio_delay();
+ }
+
+ /* Read the 16 data bits. */
+ for (i = 16; i > 0; i--) {
+ outl(0, mdio_addr);
+ mdio_delay();
+ retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0);
+ outl(MDC, mdio_addr);
+ mdio_delay();
+ }
+ return retval;
+}
+
+static void mdio_write(struct net_device *net_dev, int phy_id, int location, int value)
+{
+ long mdio_addr = net_dev->base_addr + mear;
+ int mii_cmd = MIIwrite|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
+ int i;
+
+ mdio_reset(mdio_addr);
+ mdio_idle(mdio_addr);
+
+ /* Shift the command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
+ outb(dataval, mdio_addr);
+ mdio_delay();
+ outb(dataval | MDC, mdio_addr);
+ mdio_delay();
+ }
+ mdio_delay();
+
+ /* Shift the value bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (value & (1 << i)) ? MDDIR | MDIO : MDDIR;
+ outl(dataval, mdio_addr);
+ mdio_delay();
+ outl(dataval | MDC, mdio_addr);
+ mdio_delay();
+ }
+ mdio_delay();
+
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ outb(0, mdio_addr);
+ mdio_delay();
+ outb(MDC, mdio_addr);
+ mdio_delay();
+ }
+ return;
}
static int
-sis900_open(struct net_device *dev)
+sis900_open(struct net_device *net_dev)
{
- struct sis900_private *tp = (struct sis900_private *)dev->priv;
- long ioaddr = dev->base_addr;
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+ long ioaddr = net_dev->base_addr;
- if (sis900_debug > 0)
- printk(KERN_INFO "%s sis900_open, IO Addr=%x, Irq=%x\n",
- dev->name, (unsigned int)ioaddr, dev->irq);
-
- /* Soft reset the chip. */
- outl(0, ioaddr + imr);
- outl(0, ioaddr + ier);
- outl(0, ioaddr + rfcr);
- outl(RESET | RxRESET | TxRESET, ioaddr + cr);
-
- if (request_irq(dev->irq, &sis900_interrupt, SA_SHIRQ, dev->name, dev))
- {
- return -EAGAIN;
- }
-
- MOD_INC_USE_COUNT;
-
- tp->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL);
- tp->rx_bufs = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL);
- if (tp->tx_bufs == NULL || tp->rx_bufs == NULL) {
- if (tp->tx_bufs)
- kfree(tp->tx_bufs);
- if (tp->rx_bufs)
- kfree(tp->rx_bufs);
- if (!tp->tx_bufs) {
- printk(KERN_ERR "%s: Can't allocate a %d byte TX Bufs.\n",
- dev->name, TX_BUF_SIZE * NUM_TX_DESC);
- }
- if (!tp->rx_bufs) {
- printk(KERN_ERR "%s: Can't allocate a %d byte RX Bufs.\n",
- dev->name, RX_BUF_SIZE * NUM_RX_DESC);
- }
- return -ENOMEM;
- }
+ /* Soft reset the chip. */
+ sis900_reset(net_dev);
- {
- u32 rfcrSave;
- u32 w;
- u32 i;
-
- rfcrSave = inl(rfcr);
- outl(rfcrSave & ~RFEN, rfcr);
- for (i=0 ; i<3 ; i++) {
- w = (u16)*((u16*)(dev->dev_addr)+i);
- outl((((u32) i) << RFEP_shift), ioaddr + rfcr);
- outl((u32)w, ioaddr + rfdr);
- if (sis900_debug > 4) {
- printk(KERN_INFO "Filter Addr[%d]=%x\n",
- i, inl(ioaddr + rfdr));
- }
- }
- outl(rfcrSave, rfcr);
- }
-
- sis900_init_ring(dev);
- outl((u32)tp->tx_buf[0].physAddr, ioaddr + txdp);
- outl((u32)tp->rx_buf[0].physAddr, ioaddr + rxdp);
-
- if (sis900_debug > 4)
- printk(KERN_INFO "txdp:%8.8x\n", inl(ioaddr + txdp));
-
- /* Check that the chip has finished the reset. */
- {
- u32 status;
- int j=0;
- status = TxRCMP | RxRCMP;
- while (status && (j++ < 30000)) {
- status ^= (inl(isr) & status);
- }
- }
-
- outl(PESEL, ioaddr + cfg);
-
- /* Must enable Tx/Rx before setting transfer thresholds! */
- /*
- * #define TX_DMA_BURST 0
- * #define RX_DMA_BURST 0
- * #define TX_FIFO_THRESH 16
- * #define TxDRNT_100 (1536>>5)
- * #define TxDRNT_10 (1536>>5)
- * #define RxDRNT_100 (1536>>5)
- * #define RxDRNT_10 (1536>>5)
- */
- outl((RX_DMA_BURST<<20) | (RxDRNT_10 << 1), ioaddr+rxcfg);
- outl(TxATP | (TX_DMA_BURST << 20) | (TX_FIFO_THRESH<<8) | TxDRNT_10,
- ioaddr + txcfg);
- if (sis900_debug > 1)
- {
- if (tp->LinkOn) {
- printk(KERN_INFO"%s: Media Type %s%s-duplex.\n",
- dev->name,
- tp->speeds==HW_SPEED_100_MBPS ?
- "100mbps " : "10mbps ",
- tp->full_duplex== FDX_CAPABLE_FULL_SELECTED ?
- "full" : "half");
- }
- else printk(KERN_INFO"%s: Media Link Off\n", dev->name);
+ if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) {
+ return -EAGAIN;
}
- set_rx_mode(dev);
- dev->tbusy = 0;
- dev->interrupt = 0;
- dev->start = 1;
-
- /* Enable all known interrupts by setting the interrupt mask. */
- outl((RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr);
- outl(RxENA, ioaddr + cr);
- outl(IE, ioaddr + ier);
-
- if (sis900_debug > 3)
- printk(KERN_INFO "%s: sis900_open() ioaddr %#lx IRQ %d \n",
- dev->name, ioaddr, dev->irq);
-
- /* Set the timer to switch to check for link beat and perhaps switch
- to an alternate media type. */
- init_timer(&tp->timer);
- tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */
- tp->timer.data = (unsigned long)dev;
- tp->timer.function = &sis900_timer; /* timer handler */
- add_timer(&tp->timer);
+ MOD_INC_USE_COUNT;
+
+ sis900_init_rxfilter(net_dev);
- return 0;
+ sis900_init_tx_ring(net_dev);
+ sis900_init_rx_ring(net_dev);
+
+ set_rx_mode(net_dev);
+
+ net_dev->tbusy = 0;
+ net_dev->interrupt = 0;
+ net_dev->start = 1;
+
+ /* Enable all known interrupts by setting the interrupt mask. */
+ outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxOK), ioaddr + imr);
+ outl(RxENA, ioaddr + cr);
+ outl(IE, ioaddr + ier);
+
+ sis900_check_mode(net_dev, sis_priv->mii);
+
+ /* Set the timer to switch to check for link beat and perhaps switch
+ to an alternate media type. */
+ init_timer(&sis_priv->timer);
+ sis_priv->timer.expires = jiffies + HZ;
+ sis_priv->timer.data = (unsigned long)net_dev;
+ sis_priv->timer.function = &sis900_timer;
+ add_timer(&sis_priv->timer);
+
+ return 0;
}
+/* set receive filter address to our MAC address */
+static void
+sis900_init_rxfilter (struct net_device * net_dev)
+{
+ long ioaddr = net_dev->base_addr;
+ u32 rfcrSave;
+ u32 i;
+
+ rfcrSave = inl(rfcr + ioaddr);
+
+ /* disable packet filtering before setting filter */
+ outl(rfcrSave & ~RFEN, rfcr);
+
+ /* load MAC addr to filter data register */
+ for (i = 0 ; i < 3 ; i++) {
+ u32 w;
+
+ w = (u32) *((u16 *)(net_dev->dev_addr)+i);
+ outl((i << RFADDR_shift), ioaddr + rfcr);
+ outl(w, ioaddr + rfdr);
+
+ if (sis900_debug > 2) {
+ printk(KERN_INFO "%s: Receive Filter Addrss[%d]=%x\n",
+ net_dev->name, i, inl(ioaddr + rfdr));
+ }
+ }
+
+ /* enable packet filitering */
+ outl(rfcrSave | RFEN, rfcr + ioaddr);
+}
+
+/* Initialize the Tx ring. */
+static void
+sis900_init_tx_ring(struct net_device *net_dev)
+{
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+ long ioaddr = net_dev->base_addr;
+ int i;
+
+ sis_priv->tx_full = 0;
+ sis_priv->dirty_tx = sis_priv->cur_tx = 0;
+
+ for (i = 0; i < NUM_TX_DESC; i++) {
+ sis_priv->tx_skbuff[i] = NULL;
+
+ sis_priv->tx_ring[i].link = (u32) virt_to_bus(&sis_priv->tx_ring[i+1]);
+ sis_priv->tx_ring[i].cmdsts = 0;
+ sis_priv->tx_ring[i].bufptr = 0;
+ }
+ sis_priv->tx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->tx_ring[0]);
+
+ /* load Transmit Descriptor Register */
+ outl(virt_to_bus(&sis_priv->tx_ring[0]), ioaddr + txdp);
+ if (sis900_debug > 2)
+ printk(KERN_INFO "%s: TX descriptor register loaded with: %8.8x\n",
+ net_dev->name, inl(ioaddr + txdp));
+}
+
+/* Initialize the Rx descriptor ring, pre-allocate recevie buffers */
+static void
+sis900_init_rx_ring(struct net_device *net_dev)
+{
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+ long ioaddr = net_dev->base_addr;
+ int i;
+
+ sis_priv->cur_rx = 0;
+ sis_priv->dirty_rx = 0;
+
+ /* init RX descriptor */
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ sis_priv->rx_skbuff[i] = NULL;
+
+ sis_priv->rx_ring[i].link = (u32) virt_to_bus(&sis_priv->rx_ring[i+1]);
+ sis_priv->rx_ring[i].cmdsts = 0;
+ sis_priv->rx_ring[i].bufptr = 0;
+ }
+ sis_priv->rx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->rx_ring[0]);
+
+ /* allocate sock buffers */
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ struct sk_buff *skb;
+
+ if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
+ /* not enough memory for skbuff, this makes a "hole"
+ on the buffer ring, it is not clear how the
+ hardware will react to this kind of degenerated
+ buffer */
+ break;
+ }
+ skb->dev = net_dev;
+ sis_priv->rx_skbuff[i] = skb;
+ sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE;
+ sis_priv->rx_ring[i].bufptr = virt_to_bus(skb->tail);
+ }
+ sis_priv->dirty_rx = (unsigned int) (i - NUM_RX_DESC);
+
+ /* load Receive Descriptor Register */
+ outl(virt_to_bus(&sis_priv->rx_ring[0]), ioaddr + rxdp);
+ if (sis900_debug > 2)
+ printk(KERN_INFO "%s: RX descriptor register loaded with: %8.8x\n",
+ net_dev->name, inl(ioaddr + rxdp));
+}
+/* on each timer ticks we check two things, Link Status (ON/OFF) and
+ Link Mode (10/100/Full/Half)
+ */
static void sis900_timer(unsigned long data)
{
- struct net_device *dev = (struct net_device *)data;
- struct sis900_private *tp = (struct sis900_private *)dev->priv;
- int next_tick = 0;
- u16 status;
-
- if (!tp->LinkOn) {
- status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS);
- if (status & MIISTAT_LINK) {
- elPMDreadMode(dev, tp->phys[tp->phy_idx],
- &tp->speeds, &tp->full_duplex);
- tp->LinkOn = TRUE;
- printk(KERN_INFO "%s: Media Link On %s%s-duplex ",
- dev->name,
- tp->speeds == HW_SPEED_100_MBPS ?
- "100mbps " : "10mbps ",
- tp->full_duplex==FDX_CAPABLE_FULL_SELECTED ?
- "full" : "half");
+ struct net_device *net_dev = (struct net_device *)data;
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+ struct mii_phy *mii_phy = sis_priv->mii;
+ static int next_tick = 5*HZ;
+ u16 status;
+
+ status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
+
+ /* current mii phy is failed to link, try another one */
+ while (!(status & MII_STAT_LINK)) {
+ if (mii_phy->next == NULL) {
+ if (sis_priv->LinkOn) {
+ /* link stat change from ON to OFF */
+ next_tick = HZ;
+ sis_priv->LinkOn = FALSE;
+ printk(KERN_INFO "%s: Media Link Off\n",
+ net_dev->name);
+ }
+ sis_priv->timer.expires = jiffies + next_tick;
+ add_timer(&sis_priv->timer);
+ return;
}
- } else { // previous link on
- status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS);
- if (!(status & MIISTAT_LINK)) {
- tp->LinkOn = FALSE;
- printk(KERN_INFO "%s: Media Link Off\n", dev->name);
+ mii_phy = mii_phy->next;
+ status = mdio_read(net_dev, mii_phy->phy_addr, MII_STATUS);
+ }
+
+ if (!sis_priv->LinkOn) {
+ /* link stat change forn OFF to ON, read and report link mode */
+ sis_priv->LinkOn = TRUE;
+ next_tick = 5*HZ;
+ /* change what cur_phy means */
+ if (mii_phy->phy_addr != sis_priv->cur_phy) {
+ printk(KERN_INFO "%s: Changing transceiver to %s\n", net_dev->name,
+ mii_phy->chip_info->name);
+ status = mdio_read(net_dev, sis_priv->cur_phy, MII_CONTROL);
+ mdio_write(net_dev, sis_priv->cur_phy,
+ MII_CONTROL, status | MII_CNTL_ISOLATE);
+ status = mdio_read(net_dev, mii_phy->phy_addr, MII_CONTROL);
+ mdio_write(net_dev, mii_phy->phy_addr,
+ MII_CONTROL, status & ~MII_CNTL_ISOLATE);
+ sis_priv->cur_phy = mii_phy->phy_addr;
}
- }
- next_tick = 2*HZ;
+ sis900_check_mode(net_dev, mii_phy);
+ }
- if (next_tick) {
- tp->timer.expires = RUN_AT(next_tick);
- add_timer(&tp->timer);
- }
-}
-
-static void sis900_tx_timeout(struct net_device *dev)
-{
- struct sis900_private *tp = (struct sis900_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int i;
-
- if (sis900_debug > 0)
- printk(KERN_INFO "%s: Transmit timeout, status %2.2x %4.4x \n",
- dev->name, inl(ioaddr + cr), inl(ioaddr + isr));
-
- /* Disable interrupts by clearing the interrupt mask. */
- outl(0x0000, ioaddr + imr);
-
- /* Emit info to figure out what went wrong. */
- if (sis900_debug > 1) {
- printk(KERN_INFO "%s:Tx queue start entry %d dirty entry %d.\n",
- dev->name, tp->cur_tx, tp->dirty_tx);
- for (i = 0; i < NUM_TX_DESC; i++)
- printk(KERN_INFO "%s: Tx descriptor %d is %8.8x.%s\n",
- dev->name, i, (unsigned int)&tp->tx_buf[i],
- i == tp->dirty_tx % NUM_TX_DESC ?
- " (queue head)" : "");
- }
-
- /* Soft reset the chip. */
- //outb(RESET, ioaddr + cr);
- /* Check that the chip has finished the reset. */
- /*
- for (i = 1000; i > 0; i--)
- if ((inb(ioaddr + cr) & RESET) == 0)
- break;
- */
-
- tp->cur_rx = 0;
- /* Must enable Tx/Rx before setting transfer thresholds! */
- /*
- set_rx_mode(dev);
- */
- { /* Save the unsent Tx packets. */
- struct sk_buff *saved_skb[NUM_TX_DESC], *skb;
- int j;
- for (j = 0; tp->cur_tx - tp->dirty_tx > 0 ; j++, tp->dirty_tx++)
- saved_skb[j]=tp->tx_skbuff[tp->dirty_tx % NUM_TX_DESC];
- tp->dirty_tx = tp->cur_tx = 0;
-
- for (i = 0; i < j; i++) {
- skb = tp->tx_skbuff[i] = saved_skb[i];
- /* Always alignment */
- memcpy((unsigned char*)(tp->tx_buf[i].buf),
- skb->data, skb->len);
- tp->tx_buf[i].cmdsts = OWN | skb->len;
- /* Note: the chip doesn't have auto-pad! */
- /*
- outl(tp->tx_flag|(skb->len>=ETH_ZLEN?skb->len:ETH_ZLEN),
- ioaddr + TxStatus0 + i*4);
- */
- }
- outl(TxENA, ioaddr + cr);
- tp->cur_tx = i;
- while (i < NUM_TX_DESC)
- tp->tx_skbuff[i++] = 0;
- if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
- dev->tbusy = 0;
- tp->tx_full = 0;
- } else {
- tp->tx_full = 1;
- }
- }
-
- dev->trans_start = jiffies;
- tp->stats.tx_errors++;
- /* Enable all known interrupts by setting the interrupt mask. */
- outl((RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr);
- return;
+ sis_priv->timer.expires = jiffies + next_tick;
+ add_timer(&sis_priv->timer);
}
+static void sis900_check_mode (struct net_device *net_dev, struct mii_phy *mii_phy)
+{
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+ long ioaddr = net_dev->base_addr;
+ int speed, duplex;
+ u32 tx_flags = 0, rx_flags = 0;
+ mii_phy->chip_info->read_mode(net_dev, sis_priv->cur_phy, &speed, &duplex);
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void
-sis900_init_ring(struct net_device *dev)
+ tx_flags = TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
+ rx_flags = RX_DMA_BURST << RxMXDMA_shift;
+
+ if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS ) {
+ rx_flags |= (RxDRNT_10 << RxDRNT_shift);
+ tx_flags |= (TxDRNT_10 << TxDRNT_shift);
+ }
+ else {
+ rx_flags |= (RxDRNT_100 << RxDRNT_shift);
+ tx_flags |= (TxDRNT_100 << TxDRNT_shift);
+ }
+
+ if (duplex == FDX_CAPABLE_FULL_SELECTED) {
+ tx_flags |= (TxCSI | TxHBI);
+ rx_flags |= RxATX;
+ }
+
+ outl (tx_flags, ioaddr + txcfg);
+ outl (rx_flags, ioaddr + rxcfg);
+}
+static void sis900_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex)
{
- struct sis900_private *tp = (struct sis900_private *)dev->priv;
- int i;
+ int i = 0;
+ u32 status;
+
+ /* STSOUT register is Latched on Transition, read operation updates it */
+ while (i++ < 2)
+ status = mdio_read(net_dev, phy_addr, MII_STSOUT);
+
+ if (status & MII_STSOUT_SPD)
+ *speed = HW_SPEED_100_MBPS;
+ else
+ *speed = HW_SPEED_10_MBPS;
+
+ if (status & MII_STSOUT_DPLX)
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ else
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+
+ if (status & MII_STSOUT_LINK_FAIL)
+ printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
+ else
+ printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
+ net_dev->name,
+ *speed == HW_SPEED_100_MBPS ?
+ "100mbps" : "10mbps",
+ *duplex == FDX_CAPABLE_FULL_SELECTED ?
+ "full" : "half");
+}
+static void amd79c901_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex)
+{
+ int i;
+ u16 status;
+
+ for (i = 0; i < 2; i++)
+ status = mdio_read(net_dev, phy_addr, MII_STATUS);
+
+ if (status & MII_STAT_CAN_AUTO) {
+ /* 10BASE-T PHY */
+ for (i = 0; i < 2; i++)
+ status = mdio_read(net_dev, phy_addr, MII_STATUS_SUMMARY);
+ if (status & MII_STSSUM_SPD)
+ *speed = HW_SPEED_100_MBPS;
+ else
+ *speed = HW_SPEED_10_MBPS;
+ if (status & MII_STSSUM_DPLX)
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ else
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+
+ if (status & MII_STSSUM_LINK)
+ printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",
+ net_dev->name,
+ *speed == HW_SPEED_100_MBPS ?
+ "100mbps" : "10mbps",
+ *duplex == FDX_CAPABLE_FULL_SELECTED ?
+ "full" : "half");
+ else
+ printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
+
+ }
+ else {
+ /* HomePNA */
+ *speed = HW_SPEED_HOME;
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+ if (status & MII_STAT_LINK)
+ printk(KERN_INFO "%s: Media Link On 1mbps half-duplex \n",
+ net_dev->name);
+ else
+ printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
+ }
+}
+static void sis900_tx_timeout(struct net_device *net_dev)
+{
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+ long ioaddr = net_dev->base_addr;
+
+ printk(KERN_INFO "%s: Transmit timeout, status %8.8x %8.8x \n",
+ net_dev->name, inl(ioaddr + cr), inl(ioaddr + isr));
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ outl(0x0000, ioaddr + imr);
- tp->tx_full = 0;
- tp->cur_rx = 0;
- tp->dirty_tx = tp->cur_tx = 0;
-
- /* Tx Buffer */
- for (i = 0; i < NUM_TX_DESC; i++) {
- tp->tx_skbuff[i] = 0;
- tp->tx_buf[i].buf = &tp->tx_bufs[i*TX_BUF_SIZE];
- tp->tx_buf[i].bufPhys =
- virt_to_bus(&tp->tx_bufs[i*TX_BUF_SIZE]);
- }
-
- /* Tx Descriptor */
- for (i = 0; i< NUM_TX_DESC; i++) {
- tp->tx_buf[i].llink = (u32)
- &(tp->tx_buf[((i+1) < NUM_TX_DESC) ? (i+1) : 0]);
- tp->tx_buf[i].plink = (u32)
- virt_to_bus(&(tp->tx_buf[((i+1) < NUM_TX_DESC) ?
- (i+1) : 0].plink));
- tp->tx_buf[i].physAddr=
- virt_to_bus(&(tp->tx_buf[i].plink));
- tp->tx_buf[i].cmdsts=0;
- }
-
- /* Rx Buffer */
- for (i = 0; i < NUM_RX_DESC; i++) {
- tp->rx_buf[i].buf = &tp->rx_bufs[i*RX_BUF_SIZE];
- tp->rx_buf[i].bufPhys =
- virt_to_bus(&tp->rx_bufs[i*RX_BUF_SIZE]);
- }
-
- /* Rx Descriptor */
- for (i = 0; i< NUM_RX_DESC; i++) {
- tp->rx_buf[i].llink = (u32)
- &(tp->rx_buf[((i+1) < NUM_RX_DESC) ? (i+1) : 0]);
- tp->rx_buf[i].plink = (u32)
- virt_to_bus(&(tp->rx_buf[((i+1) < NUM_RX_DESC) ?
- (i+1) : 0].plink));
- tp->rx_buf[i].physAddr=
- virt_to_bus(&(tp->rx_buf[i].plink));
- tp->rx_buf[i].cmdsts=RX_BUF_SIZE;
- }
+ sis_priv->cur_rx = 0;
+ net_dev->trans_start = jiffies;
+ sis_priv->stats.tx_errors++;
+
+ /* FIXME: Should we restart the transmission thread here ?? */
+
+ /* Enable all known interrupts by setting the interrupt mask. */
+ outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxOK), ioaddr + imr);
+ return;
}
static int
-sis900_start_xmit(struct sk_buff *skb, struct net_device *dev)
+sis900_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
{
- struct sis900_private *tp = (struct sis900_private *)dev->priv;
- long ioaddr = dev->base_addr;
- int entry;
-
- /* Block a timer-based transmit from overlapping. This could better be
- done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
- if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
- if (jiffies - dev->trans_start < TX_TIMEOUT)
- return 1;
- sis900_tx_timeout(dev);
- return 1;
- }
-
- /* Calculate the next Tx descriptor entry. ????? */
- entry = tp->cur_tx % NUM_TX_DESC;
-
- tp->tx_skbuff[entry] = skb;
-
- if (sis900_debug > 5) {
- int i;
- printk(KERN_INFO "%s: SKB Tx Frame contents:(len=%d)",
- dev->name,skb->len);
-
- for (i = 0; i < skb->len; i++) {
- printk("%2.2x ",
- (u8)skb->data[i]);
- }
- printk(".\n");
- }
-
- memcpy(tp->tx_buf[entry].buf,
- skb->data, skb->len);
-
- tp->tx_buf[entry].cmdsts=(OWN | skb->len);
-
- //tp->tx_buf[entry].plink = 0;
- outl(TxENA, ioaddr + cr);
- if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
- clear_bit(0, (void*)&dev->tbusy);
- } else {
- tp->tx_full = 1;
- }
-
- /* Note: the chip doesn't have auto-pad! */
-
- dev->trans_start = jiffies;
- if (sis900_debug > 4)
- printk(KERN_INFO "%s: Queued Tx packet at "
- "%p size %d to slot %d.\n",
- dev->name, skb->data, (int)skb->len, entry);
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+ long ioaddr = net_dev->base_addr;
+ unsigned int entry;
+
+ /* test tbusy to see if we have timeout situation then set it */
+ if (test_and_set_bit(0, (void*)&net_dev->tbusy) != 0) {
+ if (jiffies - net_dev->trans_start > TX_TIMEOUT)
+ sis900_tx_timeout(net_dev);
+ return 1;
+ }
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = sis_priv->cur_tx % NUM_TX_DESC;
+ sis_priv->tx_skbuff[entry] = skb;
+
+ /* set the transmit buffer descriptor and enable Transmit State Machine */
+ sis_priv->tx_ring[entry].bufptr = virt_to_bus(skb->data);
+ sis_priv->tx_ring[entry].cmdsts = (OWN | skb->len);
+ outl(TxENA, ioaddr + cr);
+
+ if (++sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC) {
+ /* Typical path, clear tbusy to indicate more
+ transmission is possible */
+ clear_bit(0, (void*)&net_dev->tbusy);
+ } else {
+ /* no more transmit descriptor avaiable, tbusy remain set */
+ sis_priv->tx_full = 1;
+ }
- return 0;
+ net_dev->trans_start = jiffies;
+
+ if (sis900_debug > 3)
+ printk(KERN_INFO "%s: Queued Tx packet at %p size %d "
+ "to slot %d.\n",
+ net_dev->name, skb->data, (int)skb->len, entry);
+
+ return 0;
}
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
{
- struct net_device *dev = (struct net_device *)dev_instance;
- struct sis900_private *tp = (struct sis900_private *)dev->priv;
- int boguscnt = max_interrupt_work;
- int status;
- long ioaddr = dev->base_addr;
+ struct net_device *net_dev = (struct net_device *)dev_instance;
+ int boguscnt = max_interrupt_work;
+ long ioaddr = net_dev->base_addr;
+ u32 status;
#if defined(__i386__)
- /* A lock to prevent simultaneous entry bug on Intel SMP machines. */
- if (test_and_set_bit(0, (void*)&dev->interrupt)) {
- printk(KERN_INFO "%s: SMP simultaneous entry of "
- "an interrupt handler.\n", dev->name);
- dev->interrupt = 0; /* Avoid halting machine. */
- return;
- }
+ /* A lock to prevent simultaneous entry bug on Intel SMP machines. */
+ if (test_and_set_bit(0, (void*)&net_dev->interrupt)) {
+ printk(KERN_INFO "%s: SMP simultaneous entry of "
+ "an interrupt handler.\n", net_dev->name);
+ net_dev->interrupt = 0; /* Avoid halting machine. */
+ return;
+ }
#else
- if (dev->interrupt) {
- printk(KERN_INFO "%s: Re-entering the "
- "interrupt handler.\n", dev->name);
- return;
- }
- dev->interrupt = 1;
-#endif
-
- do {
- status = inl(ioaddr + isr);
- /* Acknowledge all of the current interrupt sources ASAP. */
- outl(status, ioaddr + isr); // ?????
-
- if (sis900_debug > 4)
- printk(KERN_INFO "%s: interrupt status=%#4.4x "
- "new intstat=%#4.4x.\n",
- dev->name, status, inl(ioaddr + isr));
-
- if ((status & (TxURN|TxERR|TxOK | RxORN|RxERR|RxOK)) == 0) {
- break;
- }
-
- if (status & (RxOK|RxORN|RxERR)) /* Rx interrupt */
- sis900_rx(dev);
-
- if (status & (TxOK | TxERR)) {
- unsigned int dirty_tx;
-
- if (sis900_debug > 5) {
- printk(KERN_INFO "TxOK:tp->cur_tx:%d,"
- "tp->dirty_tx:%x\n",
- tp->cur_tx, tp->dirty_tx);
- }
- for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx;
- dirty_tx++)
- {
- int i;
- int entry = dirty_tx % NUM_TX_DESC;
- int txstatus = tp->tx_buf[entry].cmdsts;
-
- if (sis900_debug > 4) {
- printk(KERN_INFO "%s: Tx Frame contents:"
- "(len=%d)",
- dev->name, (txstatus & DSIZE));
-
- for (i = 0; i < (txstatus & DSIZE) ;
- i++) {
- printk("%2.2x ",
- (u8)(tp->tx_buf[entry].buf[i]));
- }
- printk(".\n");
- }
- if ( ! (txstatus & (OK | UNDERRUN)))
- {
- if (sis900_debug > 1)
- printk(KERN_INFO "Tx NOT (OK,"
- "UnderRun)\n");
- break; /* It still hasn't been Txed */
- }
-
- /* Note: TxCarrierLost is always asserted
- at 100mbps. */
- if (txstatus & (OWCOLL | ABORT)) {
- /* There was an major error, log it. */
- if (sis900_debug > 1)
- printk(KERN_INFO "Tx Out of "
- " Window,Abort\n");
-#ifndef final_version
- if (sis900_debug > 1)
- printk(KERN_INFO "%s: Transmit "
- "error, Tx status %8.8x.\n",
- dev->name, txstatus);
-#endif
- tp->stats.tx_errors++;
- if (txstatus & ABORT) {
- tp->stats.tx_aborted_errors++;
- }
- if (txstatus & NOCARRIER)
- tp->stats.tx_carrier_errors++;
- if (txstatus & OWCOLL)
- tp->stats.tx_window_errors++;
-#ifdef ETHER_STATS
- if ((txstatus & COLCNT)==COLCNT)
- tp->stats.collisions16++;
-#endif
- } else {
-#ifdef ETHER_STATS
- /* No count for tp->stats.tx_deferred */
-#endif
- if (txstatus & UNDERRUN) {
- if (sis900_debug > 2)
- printk(KERN_INFO "Tx UnderRun\n");
- }
- tp->stats.collisions +=
- (txstatus >> 16) & 0xF;
-#if LINUX_VERSION_CODE > 0x20119
- tp->stats.tx_bytes += txstatus & DSIZE;
-#endif
- if (sis900_debug > 2)
- printk(KERN_INFO "Tx Transmit OK\n");
- tp->stats.tx_packets++;
- }
-
- /* Free the original skb. */
- if (sis900_debug > 2)
- printk(KERN_INFO "Free original skb\n");
- dev_free_skb(tp->tx_skbuff[entry]);
- tp->tx_skbuff[entry] = 0;
- } // for dirty
-
-#ifndef final_version
- if (tp->cur_tx - dirty_tx > NUM_TX_DESC) {
- printk(KERN_INFO"%s: Out-of-sync dirty pointer,"
- " %d vs. %d, full=%d.\n",
- dev->name, dirty_tx,
- tp->cur_tx, tp->tx_full);
- dirty_tx += NUM_TX_DESC;
- }
+ if (net_dev->interrupt) {
+ printk(KERN_INFO "%s: Re-entering the interrupt handler.\n",
+ net_dev->name);
+ return;
+ }
+ net_dev->interrupt = 1;
#endif
- if (tp->tx_full && dirty_tx > tp->cur_tx-NUM_TX_DESC) {
- /* The ring is no longer full, clear tbusy. */
- if (sis900_debug > 3)
- printk(KERN_INFO "Tx Ring NO LONGER Full\n");
- tp->tx_full = 0;
- dev->tbusy = 0;
- mark_bh(NET_BH);
- }
-
- tp->dirty_tx = dirty_tx;
- if (sis900_debug > 2)
- printk(KERN_INFO "TxOK,tp->cur_tx:%d,tp->dirty:%d\n",
- tp->cur_tx, tp->dirty_tx);
- } // if (TxOK | TxERR)
-
- /* Check uncommon events with one test. */
- if (status & (RxORN | TxERR | RxERR)) {
- if (sis900_debug > 2)
- printk(KERN_INFO "%s: Abnormal interrupt,"
- "status %8.8x.\n", dev->name, status);
-
- if (status == 0xffffffff)
- break;
- if (status & (RxORN | RxERR))
- tp->stats.rx_errors++;
-
-
- if (status & RxORN) {
- tp->stats.rx_over_errors++;
- }
- }
- if (--boguscnt < 0) {
- printk(KERN_INFO "%s: Too much work at interrupt, "
- "IntrStatus=0x%4.4x.\n",
- dev->name, status);
- break;
- }
- } while (1);
-
- if (sis900_debug > 3)
- printk(KERN_INFO "%s: exiting interrupt, intr_status=%#4.4x.\n",
- dev->name, inl(ioaddr + isr));
-
+ do {
+ status = inl(ioaddr + isr);
+
+ if (sis900_debug > 3)
+ printk(KERN_INFO "%s: entering interrupt, "
+ "original status = %#8.8x, "
+ "new status = %#8.8x.\n",
+ net_dev->name, status, inl(ioaddr + isr));
+
+ if ((status & (HIBERR|TxURN|TxERR|TxOK|RxORN|RxERR|RxOK)) == 0)
+ /* nothing intresting happened */
+ break;
+
+ /* why dow't we break after Tx/Rx case ?? keyword: full-duplex */
+ if (status & (RxORN | RxERR | RxOK))
+ /* Rx interrupt */
+ sis900_rx(net_dev);
+
+ if (status & (TxURN | TxERR | TxOK))
+ /* Tx interrupt */
+ sis900_finish_xmit(net_dev);
+
+ /* something strange happened !!! */
+ if (status & HIBERR) {
+ printk(KERN_INFO "%s: Abnormal interrupt,"
+ "status %#8.8x.\n", net_dev->name, status);
+ break;
+ }
+ if (--boguscnt < 0) {
+ printk(KERN_INFO "%s: Too much work at interrupt, "
+ "interrupt status = %#8.8x.\n",
+ net_dev->name, status);
+ break;
+ }
+ } while (1);
+
+ if (sis900_debug > 3)
+ printk(KERN_INFO "%s: exiting interrupt, "
+ "interrupt status = 0x%#8.8x.\n",
+ net_dev->name, inl(ioaddr + isr));
+
#if defined(__i386__)
- clear_bit(0, (void*)&dev->interrupt);
+ clear_bit(0, (void*)&net_dev->interrupt);
#else
- dev->interrupt = 0;
+ net_dev->interrupt = 0;
#endif
- return;
+ return;
}
-/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the
- field alignments and semantics. */
-static int sis900_rx(struct net_device *dev)
-{
- struct sis900_private *tp = (struct sis900_private *)dev->priv;
- long ioaddr = dev->base_addr;
- u16 cur_rx = tp->cur_rx % NUM_RX_DESC;
- int rx_status=tp->rx_buf[cur_rx].cmdsts;
-
- if (sis900_debug > 4)
- printk(KERN_INFO "%s: sis900_rx, current %4.4x,"
- " rx status=%8.8x\n",
- dev->name, cur_rx,
- rx_status);
-
- while (rx_status & OWN) {
- int rx_size = rx_status & DSIZE;
- rx_size -= CRC_SIZE;
-
- if (sis900_debug > 4) {
- int i;
- printk(KERN_INFO "%s: sis900_rx, rx status %8.8x,"
- " size %4.4x, cur %4.4x.\n",
- dev->name, rx_status, rx_size, cur_rx);
- printk(KERN_INFO "%s: Rx Frame contents:", dev->name);
-
- for (i = 0; i < rx_size; i++) {
- printk("%2.2x ",
- (u8)(tp->rx_buf[cur_rx].buf[i]));
- }
-
- printk(".\n");
- }
- if (rx_status & TOOLONG) {
- if (sis900_debug > 1)
- printk(KERN_INFO "%s: Oversized Ethernet frame,"
- " status %4.4x!\n",
- dev->name, rx_status);
- tp->stats.rx_length_errors++;
- } else if (rx_status & (RXISERR | RUNT | CRCERR | FAERR)) {
- if (sis900_debug > 1)
- printk(KERN_INFO"%s: Ethernet frame had errors,"
- " status %4.4x.\n",
- dev->name, rx_status);
- tp->stats.rx_errors++;
- if (rx_status & (RXISERR | FAERR))
- tp->stats.rx_frame_errors++;
- if (rx_status & (RUNT | TOOLONG))
- tp->stats.rx_length_errors++;
- if (rx_status & CRCERR) tp->stats.rx_crc_errors++;
- } else {
- /* Malloc up new buffer, compatible with net-2e. */
- /* Omit the four octet CRC from the length. */
- struct sk_buff *skb;
-
- skb = dev_alloc_skb(rx_size + 2);
- if (skb == NULL) {
- printk(KERN_INFO "%s: Memory squeeze,"
- "deferring packet.\n",
- dev->name);
- /* We should check that some rx space is free.
- If not,
- free one and mark stats->rx_dropped++. */
- tp->stats.rx_dropped++;
- tp->rx_buf[cur_rx].cmdsts = RX_BUF_SIZE;
- break;
- }
- skb->dev = dev;
- skb_reserve(skb, 2); /* 16 byte align the IP fields. */
- if (rx_size+CRC_SIZE > RX_BUF_SIZE) {
- /*
- int semi_count = RX_BUF_LEN - ring_offset - 4;
- memcpy(skb_put(skb, semi_count),
- &rx_bufs[ring_offset + 4], semi_count);
- memcpy(skb_put(skb, rx_size-semi_count),
- rx_bufs, rx_size - semi_count);
- if (sis900_debug > 4) {
- int i;
- printk(KERN_DEBUG"%s: Frame wrap @%d",
- dev->name, semi_count);
- for (i = 0; i < 16; i++)
- printk(" %2.2x", rx_bufs[i]);
- printk(".\n");
- memset(rx_bufs, 0xcc, 16);
- }
- */
- } else {
-#if 0 /* USE_IP_COPYSUM */
- eth_copy_and_sum(skb,
- tp->rx_buf[cur_rx].buf, rx_size, 0);
- skb_put(skb, rx_size);
-#else
- memcpy(skb_put(skb, rx_size),
- tp->rx_buf[cur_rx].buf, rx_size);
-#endif
- }
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
-#if LINUX_VERSION_CODE > 0x20119
- tp->stats.rx_bytes += rx_size;
-#endif
- tp->stats.rx_packets++;
- }
- tp->rx_buf[cur_rx].cmdsts = RX_BUF_SIZE;
-
- cur_rx = ((cur_rx+1) % NUM_RX_DESC);
- rx_status = tp->rx_buf[cur_rx].cmdsts;
- } // while
- if (sis900_debug > 4)
- printk(KERN_INFO "%s: Done sis900_rx(), current %4.4x "
- "Cmd %2.2x.\n",
- dev->name, cur_rx,
- inb(ioaddr + cr));
- tp->cur_rx = cur_rx;
- return 0;
+/* Process receive interrupt events, put buffer to higher layer and refill buffer pool
+ Note: This fucntion is called by interrupt handler, don't do "too much" work here */
+static int sis900_rx(struct net_device *net_dev)
+{
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+ long ioaddr = net_dev->base_addr;
+ unsigned int entry = sis_priv->cur_rx % NUM_RX_DESC;
+ u32 rx_status = sis_priv->rx_ring[entry].cmdsts;
+
+ if (sis900_debug > 3)
+ printk(KERN_INFO "sis900_rx, cur_rx:%4.4d, dirty_rx:%4.4d "
+ "status:0x%8.8x\n",
+ sis_priv->cur_rx, sis_priv->dirty_rx,rx_status);
+
+ while (rx_status & OWN) {
+ unsigned int rx_size;
+
+ rx_size = (rx_status & DSIZE) - CRC_SIZE;
+
+ if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) {
+ /* corrupted packet received */
+ if (sis900_debug > 3)
+ printk(KERN_INFO "%s: Corrupted packet "
+ "received, buffer status = 0x%8.8x.\n",
+ net_dev->name, rx_status);
+ sis_priv->stats.rx_errors++;
+ if (rx_status & OVERRUN)
+ sis_priv->stats.rx_over_errors++;
+ if (rx_status & (TOOLONG|RUNT))
+ sis_priv->stats.rx_length_errors++;
+ if (rx_status & (RXISERR | FAERR))
+ sis_priv->stats.rx_frame_errors++;
+ if (rx_status & CRCERR)
+ sis_priv->stats.rx_crc_errors++;
+ /* reset buffer descriptor state */
+ sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
+ } else {
+ struct sk_buff * skb;
+
+ if (sis_priv->rx_skbuff[entry] == NULL) {
+ printk(KERN_INFO "%s: NULL pointer "
+ "encountered in Rx ring, skipping\n",
+ net_dev->name);
+ break;
+ }
+ skb = sis_priv->rx_skbuff[entry];
+ sis_priv->rx_skbuff[entry] = NULL;
+ /* reset buffer descriptor state */
+ sis_priv->rx_ring[entry].cmdsts = 0;
+ sis_priv->rx_ring[entry].bufptr = 0;
+
+ skb_put(skb, rx_size);
+ skb->protocol = eth_type_trans(skb, net_dev);
+ netif_rx(skb);
+
+ if ((rx_status & BCAST) == MCAST)
+ sis_priv->stats.multicast++;
+ net_dev->last_rx = jiffies;
+ sis_priv->stats.rx_bytes += rx_size;
+ sis_priv->stats.rx_packets++;
+ }
+ sis_priv->cur_rx++;
+ entry = sis_priv->cur_rx % NUM_RX_DESC;
+ rx_status = sis_priv->rx_ring[entry].cmdsts;
+ } // while
+
+ /* refill the Rx buffer, what if the rate of refilling is slower than
+ consuming ?? */
+ for (;sis_priv->cur_rx - sis_priv->dirty_rx > 0; sis_priv->dirty_rx++) {
+ struct sk_buff *skb;
+
+ entry = sis_priv->dirty_rx % NUM_RX_DESC;
+
+ if (sis_priv->rx_skbuff[entry] == NULL) {
+ if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
+ /* not enough memory for skbuff, this makes a "hole"
+ on the buffer ring, it is not clear how the
+ hardware will react to this kind of degenerated
+ buffer */
+ printk(KERN_INFO "%s: Memory squeeze,"
+ "deferring packet.\n",
+ net_dev->name);
+ sis_priv->stats.rx_dropped++;
+ break;
+ }
+ skb->dev = net_dev;
+ sis_priv->rx_skbuff[entry] = skb;
+ sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
+ sis_priv->rx_ring[entry].bufptr = virt_to_bus(skb->tail);
+ }
+ }
+ /* re-enable the potentially idle receive state matchine */
+ outl(RxENA , ioaddr + cr );
+
+ return 0;
}
-static int
-sis900_close(struct net_device *dev)
+/* finish up transmission of packets, check for error condition and free skbuff etc.
+ Note: This fucntion is called by interrupt handler, don't do "too much" work here */
+static void sis900_finish_xmit (struct net_device *net_dev)
{
- long ioaddr = dev->base_addr;
- struct sis900_private *tp = (struct sis900_private *)dev->priv;
- int i;
-
- dev->start = 0;
- dev->tbusy = 1;
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+
+ for (; sis_priv->dirty_tx < sis_priv->cur_tx; sis_priv->dirty_tx++) {
+ unsigned int entry;
+ u32 tx_status;
+
+ entry = sis_priv->dirty_tx % NUM_TX_DESC;
+ tx_status = sis_priv->tx_ring[entry].cmdsts;
+
+ if (tx_status & OWN) {
+ /* The packet is not transmited yet (owned by hardware) ! */
+ break;
+ }
+
+ if (tx_status & (ABORT | UNDERRUN | OWCOLL)) {
+ /* packet unsuccessfully transmited */
+ if (sis900_debug > 3)
+ printk(KERN_INFO "%s: Transmit "
+ "error, Tx status %8.8x.\n",
+ net_dev->name, tx_status);
+ sis_priv->stats.tx_errors++;
+ if (tx_status & UNDERRUN)
+ sis_priv->stats.tx_fifo_errors++;
+ if (tx_status & ABORT)
+ sis_priv->stats.tx_aborted_errors++;
+ if (tx_status & NOCARRIER)
+ sis_priv->stats.tx_carrier_errors++;
+ if (tx_status & OWCOLL)
+ sis_priv->stats.tx_window_errors++;
+ } else {
+ /* packet successfully transmited */
+ if (sis900_debug > 3)
+ printk(KERN_INFO "Tx Transmit OK\n");
+ sis_priv->stats.collisions += (tx_status & COLCNT) >> 16;
+ sis_priv->stats.tx_bytes += tx_status & DSIZE;
+ sis_priv->stats.tx_packets++;
+ }
+ /* Free the original skb. */
+ dev_kfree_skb(sis_priv->tx_skbuff[entry]);
+ sis_priv->tx_skbuff[entry] = NULL;
+ sis_priv->tx_ring[entry].bufptr = 0;
+ sis_priv->tx_ring[entry].cmdsts = 0;
+ }
+
+ if (sis_priv->tx_full && net_dev->tbusy &&
+ sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC - 4) {
+ /* The ring is no longer full, clear tbusy, tx_full and schedule
+ more transmission by marking NET_BH */
+ sis_priv->tx_full = 0;
+ clear_bit(0, (void *)&net_dev->tbusy);
+ mark_bh(NET_BH);
+ }
+}
- if (sis900_debug > 1)
- printk(KERN_DEBUG"%s: Shutting down ethercard, status was 0x%4.4x.\n",
- dev->name, inl(ioaddr + isr));
+static int
+sis900_close(struct net_device *net_dev)
+{
+ long ioaddr = net_dev->base_addr;
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+ int i;
+
+ net_dev->start = 0;
+ net_dev->tbusy = 1;
- /* Disable interrupts by clearing the interrupt mask. */
- outl(0x0000, ioaddr + imr);
+ /* Disable interrupts by clearing the interrupt mask. */
+ outl(0x0000, ioaddr + imr);
+ outl(0x0000, ioaddr + ier);
- /* Stop the chip's Tx and Rx DMA processes. */
- outl(0x00, ioaddr + cr);
+ /* Stop the chip's Tx and Rx Status Machine */
+ outl(RxDIS | TxDIS, ioaddr + cr);
- del_timer(&tp->timer);
+ del_timer(&sis_priv->timer);
- free_irq(dev->irq, dev);
+ free_irq(net_dev->irq, net_dev);
- for (i = 0; i < NUM_TX_DESC; i++) {
- if (tp->tx_skbuff[i])
- dev_free_skb(tp->tx_skbuff[i]);
- tp->tx_skbuff[i] = 0;
- }
- kfree(tp->rx_bufs);
- kfree(tp->tx_bufs);
+ /* Free Tx and RX skbuff */
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ if (sis_priv->rx_skbuff[i] != NULL)
+ dev_kfree_skb(sis_priv->rx_skbuff[i]);
+ sis_priv->rx_skbuff[i] = 0;
+ }
+ for (i = 0; i < NUM_TX_DESC; i++) {
+ if (sis_priv->tx_skbuff[i] != NULL)
+ dev_kfree_skb(sis_priv->tx_skbuff[i]);
+ sis_priv->tx_skbuff[i] = 0;
+ }
- /* Green! Put the chip in low-power mode. */
+ /* Green! Put the chip in low-power mode. */
- MOD_DEC_USE_COUNT;
+ MOD_DEC_USE_COUNT;
- return 0;
+ return 0;
}
-static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+static int mii_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd)
{
- struct sis900_private *tp = (struct sis900_private *)dev->priv;
- u16 *data = (u16 *)&rq->ifr_data;
-
- switch(cmd) {
- case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
- data[0] = tp->phys[tp->phy_idx];
- /* Fall Through */
- case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
- data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
- return 0;
- case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
- return -EPERM;
- mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
- return 0;
- default:
- return -EOPNOTSUPP;
- }
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+ u16 *data = (u16 *)&rq->ifr_data;
+
+ switch(cmd) {
+ case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
+ data[0] = sis_priv->mii->phy_addr;
+ /* Fall Through */
+ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
+ data[3] = mdio_read(net_dev, data[0] & 0x1f, data[1] & 0x1f);
+ return 0;
+ case SIOCDEVPRIVATE+2: /* Write the specified MII register */
+ if (!suser())
+ return -EPERM;
+ mdio_write(net_dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
static struct enet_statistics *
-sis900_get_stats(struct net_device *dev)
+sis900_get_stats(struct net_device *net_dev)
{
- struct sis900_private *tp = (struct sis900_private *)dev->priv;
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
- return &tp->stats;
+ return &sis_priv->stats;
}
-/* Set or clear the multicast filter for this adaptor.
- This routine is not state sensitive and need not be SMP locked. */
-
-static u16 elComputeHashTableIndex(u8 *addr)
+/* SiS 900 uses the most sigificant 7 bits to index a 128 bits multicast hash table, which makes
+ this function a little bit different from other drivers */
+static u16 sis900_compute_hashtable_index(u8 *addr)
{
+
+/* what is the correct value of the POLYNOMIAL ??
+ Donald Becker use 0x04C11DB7U */
#define POLYNOMIAL 0x04C11DB6L
- u32 crc = 0xffffffff, msb;
- int i, j;
- u8 byte;
-
- for( i=0; i<6; i++ ) {
- byte = *addr++;
- for( j=0; j<8; j++ ) {
- msb = crc >> 31;
- crc <<= 1;
- if( msb ^ ( byte & 1 )) {
- crc ^= POLYNOMIAL;
- crc |= 1;
- }
- byte >>= 1;
- }
- }
- // 7 bit crc for 128 bit hash table
- return( (int)(crc >> 25) );
-}
-
-static u16 elMIIpollBit(struct net_device *dev,
- int phy_id,
- int location,
- u16 mask,
- u16 polarity,
- u16 *value)
-{
- u32 i;
- i=0;
- while (1) {
- *value = mdio_read(dev, phy_id, location);
- if (polarity) {
- if (mask & *value) return(TRUE);
- } else {
- if (mask & ~(*value)) return(TRUE);
- }
- if (++i == 1200) break;
- }
- return(FALSE);
-}
-
-static u16 elPMDreadMode(struct net_device *dev,
- int phy_id,
- int *speed,
- int *duplex)
-{
- u16 status, OurCap;
-
- *speed = HW_SPEED_10_MBPS;
- *duplex = FDX_CAPABLE_HALF_SELECTED;
-
- status = mdio_read(dev, phy_id, MII_ANLPAR);
- OurCap = mdio_read(dev, phy_id, MII_ANAR);
- if (sis900_debug > 1) {
- printk(KERN_INFO "Link Part Status %4X\n", status);
- printk(KERN_INFO "Our Status %4X\n", OurCap);
- printk(KERN_INFO "Status Reg %4X\n",
- mdio_read(dev, phy_id, MII_STATUS));
- }
- status &= OurCap;
-
- if ( !( status &
- (MII_NWAY_T|MII_NWAY_T_FDX | MII_NWAY_TX | MII_NWAY_TX_FDX ))) {
- if (sis900_debug > 1) {
- printk(KERN_INFO "The other end NOT support NWAY...\n");
- }
- while (( status = mdio_read(dev, phy_id, 18)) & 0x4000) ;
- while (( status = mdio_read(dev, phy_id, 18)) & 0x0020) ;
- if (status & 0x80)
- *speed = HW_SPEED_100_MBPS;
- if (status & 0x40)
- *duplex = FDX_CAPABLE_FULL_SELECTED;
- if (sis900_debug > 3) {
- printk(KERN_INFO"%s: Setting %s%s-duplex.\n",
- dev->name,
- *speed == HW_SPEED_100_MBPS ?
- "100mbps " : "10mbps ",
- *duplex == FDX_CAPABLE_FULL_SELECTED ?
- "full" : "half");
- }
- } else {
- if (sis900_debug > 1) {
- printk(KERN_INFO "The other end support NWAY...\n");
+ u32 crc = 0xffffffff, msb;
+ int i, j;
+ u8 byte;
+
+ for (i = 0; i < 6; i++) {
+ byte = *addr++;
+ for (j = 0; j < 8; j++) {
+ msb = crc >> 31;
+ crc <<= 1;
+ if (msb ^ (byte & 1)) {
+ crc ^= POLYNOMIAL;
+ crc |= 1;
+ }
+ byte >>= 1;
}
+ }
+ /* leave 7 most siginifant bits */
+ return ((int)(crc >> 25));
+}
- if (status & (MII_NWAY_TX_FDX | MII_NWAY_T_FDX)) {
- *duplex = FDX_CAPABLE_FULL_SELECTED;
- }
- if (status & (MII_NWAY_TX_FDX | MII_NWAY_TX)) {
- *speed = HW_SPEED_100_MBPS;
- }
- if (sis900_debug > 3) {
- printk(KERN_INFO"%s: Setting %s%s-duplex based on"
- " auto-negotiated partner ability.\n",
- dev->name,
- *speed == HW_SPEED_100_MBPS ?
- "100mbps " : "10mbps ",
- *duplex == FDX_CAPABLE_FULL_SELECTED ?
- "full" : "half");
- }
- }
- return (status);
-}
-
-static u16 elAutoNegotiate(struct net_device *dev, int phy_id, int *duplex, int *speed)
-{
- u16 status, retnVal;
-
- if (sis900_debug > 1) {
- printk(KERN_INFO "AutoNegotiate...\n");
- }
- mdio_write(dev, phy_id, MII_CONTROL, 0);
- mdio_write(dev, phy_id, MII_CONTROL, MIICNTL_AUTO | MIICNTL_RST_AUTO);
- retnVal = elMIIpollBit(dev, phy_id, MII_CONTROL, MIICNTL_RST_AUTO,
- FALSE,&status);
- if (!retnVal) {
- printk(KERN_INFO "Not wait for Reset Complete\n");
- }
- retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_AUTO_DONE,
- TRUE, &status);
- if (!retnVal) {
- printk(KERN_INFO "Not wait for AutoNego Complete\n");
- }
- retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_LINK,
- TRUE, &status);
- if (!retnVal) {
- printk(KERN_INFO "Not wait for Link Complete\n");
- }
- if (status & MIISTAT_LINK) {
- elPMDreadMode(dev, phy_id, speed, duplex);
- elSetMediaType(dev, *speed, *duplex);
- }
- return(status);
-}
-
-static void elSetCapability(struct net_device *dev, int phy_id,
- int duplex, int speed)
-{
- u16 cap = ( MII_NWAY_T | MII_NWAY_T_FDX |
- MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_CSMA_CD );
-
- if (speed != 100) {
- cap &= ~( MII_NWAY_TX | MII_NWAY_TX_FDX );
- if (sis900_debug > 1) {
- printk(KERN_INFO "UNSET 100Mbps\n");
- }
+static void set_rx_mode(struct net_device *net_dev)
+{
+ long ioaddr = net_dev->base_addr;
+ u16 mc_filter[8]; /* 128 bits multicast hash table */
+ int i;
+ u32 rx_mode;
+
+ if (net_dev->flags & IFF_PROMISC) {
+ /* Accept any kinds of packets */
+ rx_mode = RFPromiscuous;
+ for (i = 0; i < 8; i++)
+ mc_filter[i] = 0xffff;
+ } else if ((net_dev->mc_count > multicast_filter_limit) ||
+ (net_dev->flags & IFF_ALLMULTI)) {
+ /* too many multicast addresses or accept all multicast packet */
+ rx_mode = RFAAB | RFAAM;
+ for (i = 0; i < 8; i++)
+ mc_filter[i] = 0xffff;
+ } else {
+ /* Accept Broadcast packet, destination address matchs our MAC address,
+ use Receive Filter to reject unwanted MCAST packet */
+ struct dev_mc_list *mclist;
+ rx_mode = RFAAB;
+ for (i = 0; i < 8; i++)
+ mc_filter[i]=0;
+ for (i = 0, mclist = net_dev->mc_list; mclist && i < net_dev->mc_count;
+ i++, mclist = mclist->next)
+ set_bit(sis900_compute_hashtable_index(mclist->dmi_addr),
+ mc_filter);
}
- if (!duplex) {
- cap &= ~( MII_NWAY_T_FDX | MII_NWAY_TX_FDX );
- if (sis900_debug > 1) {
- printk(KERN_INFO "UNSET full-duplex\n");
- }
+ /* update Multicast Hash Table in Receive Filter */
+ for (i = 0; i < 8; i++) {
+ /* why plus 0x04 ??, I don't know, UNDOCUMENT FEATURE ?? */
+ outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
+ outl(mc_filter[i], ioaddr + rfdr);
}
- mdio_write(dev, phy_id, MII_ANAR, cap);
-}
+ outl(RFEN | rx_mode, ioaddr + rfcr);
-static void elSetMediaType(struct net_device *dev, int speed, int duplex)
-{
- long ioaddr = dev->base_addr;
- u32 txCfgOn = 0, txCfgOff = TxDRNT;
- u32 rxCfgOn = 0, rxCfgOff = 0;
-
- if (speed == HW_SPEED_100_MBPS) {
- txCfgOn |= (TxDRNT_100 | TxHBI);
- } else {
- txCfgOn |= TxDRNT_10;
- }
-
- if (duplex == FDX_CAPABLE_FULL_SELECTED) {
- txCfgOn |= (TxCSI | TxHBI);
- rxCfgOn |= RxATP;
- } else {
- txCfgOff |= (TxCSI | TxHBI);
- rxCfgOff |= RxATP;
- }
- outl( (inl(ioaddr + txcfg) & ~txCfgOff) | txCfgOn, ioaddr + txcfg);
- outl( (inl(ioaddr + rxcfg) & ~rxCfgOff) | rxCfgOn, ioaddr + rxcfg);
-}
-
-static void set_rx_mode(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- u16 mc_filter[8];
- int i;
- int rx_mode;
- u32 rxCfgOn = 0, rxCfgOff = 0;
- u32 txCfgOn = 0, txCfgOff = 0;
-
- if (sis900_debug > 3)
- printk(KERN_INFO "%s: set_rx_mode (%4.4x) done--"
- "RxCfg %8.8x.\n",
- dev->name, dev->flags, inl(ioaddr + rxcfg));
-
- /* Note: do not reorder, GCC is clever about common statements. */
- if (dev->flags & IFF_PROMISC) {
- printk(KERN_NOTICE"%s: Promiscuous mode enabled.\n", dev->name);
- rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS |
- ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_PHYS;
- for (i=0 ; i<8 ; i++)
- mc_filter[i]=0xffff;
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
- rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS |
- ACCEPT_CAM_QUALIFIED;
- for (i=0 ; i<8 ; i++)
- mc_filter[i]=0xffff;
- } else {
- struct dev_mc_list *mclist;
- rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS |
- ACCEPT_CAM_QUALIFIED;
- for (i=0 ; i<8 ; i++)
- mc_filter[i]=0;
- for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
- i++, mclist = mclist->next)
- set_bit(elComputeHashTableIndex(mclist->dmi_addr),
- mc_filter);
- }
-
- for (i=0 ; i<8 ; i++) {
- outl((u32)(0x00000004+i) << 16, ioaddr + rfcr);
- outl(mc_filter[i], ioaddr + rfdr);
- }
- /* We can safely update without stopping the chip. */
- //rx_mode = ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_BCASTS | ACCEPT_ALL_PHYS;
- //rx_mode = ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_BCASTS;
- outl(RFEN | ((rx_mode & (ACCEPT_ALL_MCASTS | ACCEPT_ALL_BCASTS |
- ACCEPT_ALL_PHYS)) << RFAA_shift), ioaddr + rfcr);
-
- if (rx_mode & ACCEPT_ALL_ERRORS) {
- rxCfgOn = RxAEP | RxARP | RxAJAB;
- } else {
- rxCfgOff = RxAEP | RxARP | RxAJAB;
- }
- if (rx_mode & MAC_LOOPBACK) {
- rxCfgOn |= RxATP;
- txCfgOn |= TxMLB;
- } else {
- if (!(( (struct sis900_private *)(dev->priv) )->full_duplex))
- rxCfgOff |= RxATP;
- txCfgOff |= TxMLB;
- }
-
- if (sis900_debug > 2) {
- printk(KERN_INFO "Before Set TxCfg=%8.8x\n",inl(ioaddr+txcfg));
- printk(KERN_INFO "Before Set RxCfg=%8.8x\n",inl(ioaddr+rxcfg));
- }
-
- outl((inl(ioaddr + rxcfg) | rxCfgOn) & ~rxCfgOff, ioaddr + rxcfg);
- outl((inl(ioaddr + txcfg) | txCfgOn) & ~txCfgOff, ioaddr + txcfg);
-
- if (sis900_debug > 2) {
- printk(KERN_INFO "After Set TxCfg=%8.8x\n",inl(ioaddr+txcfg));
- printk(KERN_INFO "After Set RxCfg=%8.8x\n",inl(ioaddr+rxcfg));
- printk(KERN_INFO "Receive Filter Register:%8.8x\n",
- inl(ioaddr + rfcr));
- }
- return;
-}
-
-static void sis900_reset(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
-
- outl(0, ioaddr + ier);
- outl(0, ioaddr + imr);
- outl(0, ioaddr + rfcr);
+ /* sis900 is capatable of looping back packet at MAC level for debugging purpose */
+ if (net_dev->flags & IFF_LOOPBACK) {
+ u32 cr_saved;
+ /* We must disable Tx/Rx before setting loopback mode */
+ cr_saved = inl(ioaddr + cr);
+ outl(cr_saved | TxDIS | RxDIS, ioaddr + cr);
+ /* enable loopback */
+ outl(inl(ioaddr + txcfg) | TxMLB, ioaddr + txcfg);
+ outl(inl(ioaddr + rxcfg) | RxATX, ioaddr + rxcfg);
+ /* restore cr */
+ outl(cr_saved, ioaddr + cr);
+ }
+
+ return;
+}
+
+static void sis900_reset(struct net_device *net_dev)
+{
+ long ioaddr = net_dev->base_addr;
+ int i = 0;
+ u32 status = TxRCMP | RxRCMP;
+
+ outl(0, ioaddr + ier);
+ outl(0, ioaddr + imr);
+ outl(0, ioaddr + rfcr);
- outl(RxRESET | TxRESET | RESET, ioaddr + cr);
- outl(PESEL, ioaddr + cfg);
+ outl(RxRESET | TxRESET | RESET, ioaddr + cr);
+
+ /* Check that the chip has finished the reset. */
+ while (status && (i++ < 1000)) {
+ status ^= (inl(isr + ioaddr) & status);
+ }
- set_rx_mode(dev);
+ outl(PESEL, ioaddr + cfg);
}
#ifdef MODULE
int init_module(void)
{
- return sis900_probe(0);
+ return sis900_probe(NULL);
}
void
cleanup_module(void)
{
- struct net_device *next_dev;
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ while (root_sis900_dev) {
+ struct sis900_private *sis_priv =
+ (struct sis900_private *)root_sis900_dev->priv;
+ struct net_device *next_dev = sis_priv->next_module;
+
+ unregister_netdev(root_sis900_dev);
+ release_region(root_sis900_dev->base_addr,
+ sis_priv->mac->io_size);
+ kfree(sis_priv);
+ kfree(root_sis900_dev);
- /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
- while (root_sis900_dev) {
- struct sis900_private *tp =
- (struct sis900_private *)root_sis900_dev->priv;
- next_dev = tp->next_module;
- unregister_netdev(root_sis900_dev);
- release_region(root_sis900_dev->base_addr,
- pci_tbl[tp->chip_id].io_size);
- kfree(tp);
- kfree(root_sis900_dev);
- root_sis900_dev = next_dev;
- }
+ root_sis900_dev = next_dev;
+ }
}
#endif /* MODULE */
-/*
- * Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c sis900.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c sis900.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- * c-indent-level: 4
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)