patch-2.0.31 linux/drivers/net/de4x5.c
Next file: linux/drivers/net/de4x5.h
Previous file: linux/drivers/net/auto_irq.c
Back to the patch index
Back to the overall index
- Lines: 2902
- Date:
Tue Aug 12 13:21:12 1997
- Orig file:
v2.0.30/linux/drivers/net/de4x5.c
- Orig date:
Sat Nov 9 09:31:38 1996
diff -u --recursive --new-file v2.0.30/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
@@ -1,12 +1,35 @@
-/* de4x5.c: A DIGITAL DE425/DE434/DE435/DE450/DE500 ethernet driver for Linux.
+/* de4x5.c: A DIGITAL DC21x4x DECchip and DE425/DE434/DE435/DE450/DE500
+ ethernet driver for Linux.
Copyright 1994, 1995 Digital Equipment Corporation.
- This software may be used and distributed according to the terms of
- the GNU Public License, incorporated herein by reference.
+ Testing resources for this driver have been made available
+ in part by NASA Ames Research Center (mjacob@nas.nasa.gov).
- This driver is written for the Digital Equipment Corporation series
- of EtherWORKS ethernet cards:
+ The author may be reached at davies@maniac.ultranet.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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ 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.
+
+ Originally, this driver was written for the Digital Equipment
+ Corporation series of EtherWORKS ethernet cards:
DE425 TP/COAX EISA
DE434 TP PCI
@@ -14,6 +37,25 @@
DE450 TP/COAX/AUI PCI
DE500 10/100 PCI Fasternet
+ but it will now attempt to support all cards which conform to the
+ Digital Semiconductor SROM Specification. The driver currently
+ recognises the following chips:
+
+ DC21040 (no SROM)
+ DC21041[A]
+ DC21140[A]
+
+ I plan to add DC2114[23] support ASAP, time permitting. So far the
+ driver is known to work with the following cards:
+
+ KINGSTON
+ Linksys
+ ZNYX342
+ SMC8432
+ SMC9332 (w/new SROM)
+ ZNYX31[45]
+ ZNYX346 10/100 4 port (can act as a 10/100 bridge!)
+
The driver has been tested on a relatively busy network using the DE425,
DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
16M of data to a DECstation 5000/200 as follows:
@@ -29,12 +71,12 @@
measurement. Their error is +/-20k on a quiet (private) network and also
depend on what load the CPU has.
- The author may be reached at davies@maniac.ultranet.com.
-
=========================================================================
- This driver has been written substantially from scratch, although its
+ This driver has been written substantially from scratch, although its
inheritance of style and stack interface from 'ewrk3.c' and in turn from
- Donald Becker's 'lance.c' should be obvious.
+ Donald Becker's 'lance.c' should be obvious. With the module autoload of
+ every usable DECchip board, I pinched Donald's 'next_module' field to
+ link my modules together.
Upto 15 EISA cards can be supported under this driver, limited primarily
by the available IRQ lines. I have checked different configurations of
@@ -46,43 +88,37 @@
to the differences in the EISA and PCI CSR address offsets from the base
address.
- The ability to load this driver as a loadable module has been included
- and used extensively during the driver development (to save those long
+ The ability to load this driver as a loadable module has been included
+ and used extensively during the driver development (to save those long
reboot sequences). Loadable module support under PCI and EISA has been
achieved by letting the driver autoprobe as if it were compiled into the
- kernel, except that there is no autoprobing of the IRQ lines. This is of
- no great consequence except do make sure you're not sharing interrupts
- with anything that cannot accommodate interrupt sharing! The existing
- register_netdevice() code will only allow one device to be registered at
- a time.
-
- ************************************************************************
- For now, please only use the 'io=??' assignment (see 2. below, ?? != 0)
- when loading a module.
- ************************************************************************
-
- Essentially, the I/O address and IRQ information are ignored and filled
- in later by the PCI BIOS during the PCI probe. Note that the board
- should be in the system at boot time so that its I/O address and IRQ are
- allocated by the PCI BIOS automatically.
+ kernel. Do make sure you're not sharing interrupts with anything that
+ cannot accommodate interrupt sharing!
To utilise this ability, you have to do 8 things:
0) have a copy of the loadable modules code installed on your system.
1) copy de4x5.c from the /linux/drivers/net directory to your favourite
temporary directory.
- 2) edit the source code near line 3779 to reflect the I/O address you're
- using (only if you want to manually load the module), or assign these
- when loading by:
+ 2) for fixed autoprobes (not recommended), edit the source code near
+ line 5005 to reflect the I/O address you're using, or assign these when
+ loading by:
- insmod de4x5.o io=0xghh where g = bus number
+ insmod de4x5 io=0xghh where g = bus number
hh = device number
+ NB: autoprobing for modules is now supported by default. You may just
+ use:
+
+ insmod de4x5
+
+ to load all available boards. For a specific board, still use
+ the 'io=?' above.
3) compile de4x5.c, but include -DMODULE in the command line to ensure
that the correct bits are compiled (see end of source code).
4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
kernel with the de4x5 configuration turned off and reboot.
- 5) insmod de4x5.o [io=0xghh]
+ 5) insmod de4x5 [io=0xghh]
6) run the net startup bits for your new eth?? interface(s) manually
(usually /etc/rc.inet[12] at boot time).
7) enjoy!
@@ -98,13 +134,7 @@
By default, the driver will now autodetect any DECchip based card.
Should you have a need to restrict the driver to DIGITAL only cards, you
can compile with a DEC_ONLY define, or if loading as a module, use the
- 'dec_only=1' parameter. However, this "feature" is in no way supported
- nor tested in this driver and the user may use it at his/her sole
- discretion. I have had 2 conflicting reports that my driver will or
- won't work with Znyx. Try Donald Becker's 'tulip.c' if this driver
- doesn't work for you. I will not be supporting Znyx and SMC cards since
- I have no information on them and can't test them in a system (this
- applies most particularly to the DC21140 based cards).
+ 'dec_only=1' parameter.
I've changed the timing routines to use the kernel timer and scheduling
functions so that the hangs and other assorted problems that occurred
@@ -125,6 +155,30 @@
aligned DMA transfers and the Alphas get alignment traps with non
longword aligned data copies (which makes them really slow). No comment.
+ I have added SROM decoding routines to make this driver work with any
+ card that supports the Digital Semiconductor SROM spec. This will help
+ all cards running the dc2114x series chips in particular. Cards using
+ the dc2104x chips should run correctly with the basic driver. I'm in
+ debt to <mjacob@feral.com> for the testing and feedback that helped get
+ this feature working. So far we have tested KINGSTON, SMC8432, SMC9332
+ (with the latest SROM complying with the SROM spec V3: their first was
+ broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315
+ (quad 21041 MAC) cards also appear to work despite their incorrectly
+ wired IRQs.
+
+ I have added a temporary fix for interrupt problems when some SCSI cards
+ share the same interrupt as the DECchip based cards. The problem occurs
+ because the SCSI card wants to grab the interrupt as a fast interrupt
+ (runs the service routine with interrupts turned off) vs. this card
+ which really needs to run the service routine with interrupts turned on.
+ This driver will now add the interrupt service routine as a fast
+ interrupt if it is bounced from the slow interrupt. THIS IS NOT A
+ RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time
+ until people sort out their compatibility issues and the kernel
+ interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST
+ INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not
+ run on the same interrupt. PCMCIA/CardBus is another can of worms...
+
TO DO:
------
@@ -208,16 +262,49 @@
with <csd@microplex.com>
0.44 13-Aug-96 Fix RX overflow bug in 2114[023] chips.
Fix EISA probe bugs reported by <os2@kpi.kharkov.ua>
- and <michael@compurex.com>
+ and <michael@compurex.com>.
0.441 9-Sep-96 Change dc21041_autoconf() to probe quiet BNC media
with a loopback packet.
0.442 9-Sep-96 Include AUI in dc21041 media printout. Bug reported
by <bhat@mundook.cs.mu.OZ.AU>
+ 0.45 8-Dec-96 Include endian functions for PPC use, from work
+ by <cort@cs.nmt.edu> and <g.thomas@opengroup.org>.
+ 0.451 28-Dec-96 Added fix to allow autoprobe for modules after
+ suggestion from <mjacob@feral.com>.
+ 0.5 30-Jan-97 Added SROM decoding functions.
+ Updated debug flags.
+ Fix sleep/wakeup calls for PCI cards, bug reported
+ by <cross@gweep.lkg.dec.com>.
+ Added multi-MAC, one SROM feature from discussion
+ with <mjacob@feral.com>.
+ Added full module autoprobe capability.
+ Added attempt to use an SMC9332 with broken SROM.
+ Added fix for ZYNX multi-mac cards that didn't
+ get their IRQs wired correctly.
+ 0.51 13-Feb-97 Added endian fixes for the SROM accesses from
+ <paubert@iram.es>
+ Fix init_connection() to remove extra device reset.
+ Fix MAC/PHY reset ordering in dc21140m_autoconf().
+ Fix initialisation problem with lp->timeout in
+ typeX_infoblock() from <paubert@iram.es>.
+ Fix MII PHY reset problem from work done by
+ <paubert@iram.es>.
+ 0.52 26-Apr-97 Some changes may not credit the right people -
+ a disk crash meant I lost some mail.
+ Change RX interrupt routine to drop rather than
+ defer packets to avoid hang reported by
+ <g.thomas@opengroup.org>.
+ Fix srom_exec() to return for COMPACT and type 1
+ infoblocks.
+ Added DC21142 and DC21143 functions.
+ Added byte counters from <phil@tazenda.demon.co.uk>
+ Added SA_INTERRUPT temporary fix from
+ <mjacob@feral.com>.
=========================================================================
*/
-static const char *version = "de4x5.c:v0.442 96/11/7 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com\n";
#include <linux/module.h>
@@ -235,7 +322,8 @@
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
-#include <asm/segment.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -250,14 +338,37 @@
#define c_char const char
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < ((2 << 16) | (1 << 8))
+#define net_device_stats enet_statistics
+#define copy_to_user(a,b,c) memcpy_tofs(a,b,c)
+#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
+#define le16_to_cpu(a) cpu_to_le16(a)
+#define le32_to_cpu(a) cpu_to_le32(a)
+#ifdef __powerpc__
+#define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8))
+#define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\
+ (((a) & 0x0000ff00U) << 8) |\
+ (((a) & 0x00ff0000U) >> 8) |\
+ (((a) & 0xff000000U) >> 24))
+#else
+#define cpu_to_le16(a) (a)
+#define cpu_to_le32(a) (a)
+#endif /* __powerpc__ */
+#include <asm/segment.h>
+#else
+#include <asm/uaccess.h>
+#endif /* LINUX_VERSION_CODE */
+#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a)))
+
/*
** MII Information
*/
struct phy_table {
- int reset; /* Hard reset required? */
- int id; /* IEEE OUI */
+ int reset; /* Hard reset required? */
+ int id; /* IEEE OUI */
int ta; /* One cycle TA time - 802.3u is confusing here */
- struct { /* Non autonegotiation (parallel) speed det. */
+ struct { /* Non autonegotiation (parallel) speed det. */
int reg;
int mask;
int value;
@@ -265,19 +376,35 @@
};
struct mii_phy {
- int reset; /* Hard reset required? */
- int id; /* IEEE OUI */
- int ta; /* One cycle TA time */
+ int reset; /* Hard reset required? */
+ int id; /* IEEE OUI */
+ int ta; /* One cycle TA time */
struct { /* Non autonegotiation (parallel) speed det. */
int reg;
int mask;
int value;
} spd;
- int addr; /* MII address for the PHY */
+ int addr; /* MII address for the PHY */
+ u_char *gep; /* Start of GEP sequence block in SROM */
+ u_char *rst; /* Start of reset sequence in SROM */
+ u_int mc; /* Media Capabilities */
+ u_int ana; /* NWay Advertisement */
+ u_int fdx; /* Full DupleX capabilites for each media */
+ u_int ttm; /* Transmit Threshold Mode for each media */
};
#define DE4X5_MAX_PHY 8 /* Allow upto 8 attached PHY devices per board */
+struct sia_phy {
+ u_char mc; /* Media Code */
+ u_char ext; /* csr13-15 valid when set */
+ int csr13; /* SIA Connectivity Register */
+ int csr14; /* SIA TX/RX Register */
+ int csr15; /* SIA General Register */
+ int gepc; /* SIA GEP Control Information */
+ int gep; /* SIA GEP Data */
+};
+
/*
** Define the know universe of PHY devices that can be
** recognised by this driver
@@ -285,8 +412,8 @@
static struct phy_table phy_info[] = {
{0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}}, /* National TX */
{1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}}, /* Broadcom T4 */
- {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */
- {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}} /* Cypress T4 */
+ {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */
+ {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}} /* Cypress T4 */
};
/*
@@ -300,11 +427,23 @@
#define SMC 1
#define ACCTON 2
+/*
+** SROM Repair definitions. If a broken SROM is detected a card may
+** use this information to help figure out what to do. This is a
+** "stab in the dark" and so far for SMC9332's only.
+*/
+static c_char srom_repair_info[][100] = {
+ {0x00,0x1e,0x00,0x00,0x00,0x08, /* SMC9332 */
+ 0x1f,0x01,0x8f,0x01,0x00,0x01,0x00,0x02,
+ 0x01,0x00,0x00,0x78,0xe0,0x01,0x00,0x50,
+ 0x00,0x18,}
+};
+
#ifdef DE4X5_DEBUG
static int de4x5_debug = DE4X5_DEBUG;
#else
-static int de4x5_debug = 1;
+static int de4x5_debug = (0);
#endif
#ifdef DE4X5_AUTOSENSE /* Should be done on a per adapter basis */
@@ -449,7 +588,7 @@
char id_block_crc;
char reserved2;
char version;
- char num_adapters;
+ char num_controllers;
char ieee_addr[6];
char info[100];
short chksum;
@@ -494,7 +633,7 @@
int tx_new, tx_old; /* TX descriptor ring pointers */
char setup_frame[SETUP_FRAME_LEN]; /* Holds MCA and PA info. */
char frame[64]; /* Min sized packet for loopback*/
- struct enet_statistics stats; /* Public stats */
+ struct net_device_stats stats; /* Public stats */
struct {
u_int bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */
u_int unicast;
@@ -512,19 +651,21 @@
char txRingSize;
int bus; /* EISA or PCI */
int bus_num; /* PCI Bus number */
+ int device; /* Device number on PCI bus */
int state; /* Adapter OPENED or CLOSED */
int chipset; /* DC21040, DC21041 or DC21140 */
s32 irq_mask; /* Interrupt Mask (Enable) bits */
s32 irq_en; /* Summary interrupt bits */
int media; /* Media (eg TP), mode (eg 100B)*/
int c_media; /* Remember the last media conn */
+ int fdx; /* media full duplex flag */
int linkOK; /* Link is OK */
int autosense; /* Allow/disallow autosensing */
int tx_enable; /* Enable descriptor polling */
- int lostMedia; /* Possibly lost media */
int setup_f; /* Setup frame filtering type */
int local_state; /* State within a 'media' state */
struct mii_phy phy[DE4X5_MAX_PHY]; /* List of attached PHY devices */
+ struct sia_phy sia; /* SIA PHY Information */
int active; /* Index to active PHY device */
int mii_cnt; /* Number of attached PHY's */
int timeout; /* Scheduling counter */
@@ -537,13 +678,31 @@
s32 csr0; /* Saved Bus Mode Register */
s32 csr6; /* Saved Operating Mode Reg. */
s32 csr7; /* Saved IRQ Mask Register */
+ s32 gep; /* Saved General Purpose Reg. */
+ s32 gepc; /* Control info for GEP */
s32 csr13; /* Saved SIA Connectivity Reg. */
s32 csr14; /* Saved SIA TX/RX Register */
s32 csr15; /* Saved SIA General Register */
int save_cnt; /* Flag if state already saved */
struct sk_buff *skb; /* Save the (re-ordered) skb's */
} cache;
+ struct de4x5_srom srom; /* A copy of the SROM */
+ struct device *next_module; /* Link to the next module */
int rx_ovf; /* Check for 'RX overflow' tag */
+ int useSROM; /* For non-DEC card use SROM */
+ int useMII; /* Infoblock using the MII */
+ int asBitValid; /* Autosense bits in GEP? */
+ int asPolarity; /* 0 => asserted high */
+ int asBit; /* Autosense bit number in GEP */
+ int defMedium; /* SROM default medium */
+ int tcount; /* Last infoblock number */
+ int infoblock_init; /* Initialised this infoblock? */
+ int infoleaf_offset; /* SROM infoleaf for controller */
+ s32 infoblock_csr6; /* csr6 value in SROM infoblock */
+ int infoblock_media; /* infoblock media */
+ int (*infoleaf_fn)(struct device *); /* Pointer to infoleaf function */
+ u_char *rst; /* Pointer to Type 5 reset info */
+ u_char ibn; /* Infoblock number */
};
/*
@@ -558,9 +717,29 @@
int chipset;
struct de4x5_srom srom;
int autosense;
+ int useSROM;
} bus;
/*
+** To get around certain poxy cards that don't provide an SROM
+** for the second and more DECchip, I have to key off the first
+** chip's address. I'll assume there's not a bad SROM iff:
+**
+** o the chipset is the same
+** o the bus number is the same and > 0
+** o the sum of all the returned hw address bytes is 0 or 0x5fa
+**
+** Also have to save the irq for those cards whose hardware designers
+** can't follow the PCI to PCI Bridge Architecture spec.
+*/
+static struct {
+ int chipset;
+ int bus;
+ int irq;
+ u_char addr[ETH_ALEN];
+} last = {0,};
+
+/*
** The transmit ring full condition is described by the tx_old and tx_new
** pointers by:
** tx_old = tx_new Empty ring
@@ -580,7 +759,7 @@
static int de4x5_queue_pkt(struct sk_buff *skb, struct device *dev);
static void de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static int de4x5_close(struct device *dev);
-static struct enet_statistics *de4x5_get_stats(struct device *dev);
+static struct net_device_stats *de4x5_get_stats(struct device *dev);
static void de4x5_local_stats(struct device *dev, char *buf, int pkt_len);
static void set_multicast_list(struct device *dev);
static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd);
@@ -605,6 +784,7 @@
static int dc21040_autoconf(struct device *dev);
static int dc21041_autoconf(struct device *dev);
static int dc21140m_autoconf(struct device *dev);
+static int srom_autoconf(struct device *dev);
static int de4x5_suspect_state(struct device *dev, int timeout, int prev_state, int (*fn)(struct device *, int), int (*asfn)(struct device *));
static int dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct device *, int));
static int test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec);
@@ -642,6 +822,10 @@
/*static void srom_busy(u_int command, u_long address);*/
static void sendto_srom(u_int command, u_long addr);
static int getfrom_srom(u_long addr);
+static void srom_map_media(struct device *dev);
+static int srom_infoleaf_info(struct device *dev);
+static void srom_init(struct device *dev);
+static void srom_exec(struct device *dev, u_char *p);
static int mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr);
static void mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr);
static int mii_rdata(u_long ioaddr);
@@ -655,6 +839,8 @@
static int mii_get_phy(struct device *dev);
static void SetMulticastFilter(struct device *dev);
static int get_hw_addr(struct device *dev);
+static void srom_repair(struct device *dev, int card);
+static int test_bad_enet(struct device *dev, int status);
static void eisa_probe(struct device *dev, u_long iobase);
static void pci_probe(struct device *dev, u_long iobase);
@@ -664,20 +850,33 @@
static char *build_setup_frame(struct device *dev, int mode);
static void disable_ast(struct device *dev);
static void enable_ast(struct device *dev, u32 time_out);
-static long de4x5_switch_to_srl(struct device *dev);
-static long de4x5_switch_to_mii(struct device *dev);
+static long de4x5_switch_mac_port(struct device *dev);
static void timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec);
+static void yawn(struct device *dev, int state);
static int de4x5_dev_index(char *s);
+static void link_modules(struct device *dev, struct device *tmp);
static void de4x5_dbg_open(struct device *dev);
static void de4x5_dbg_mii(struct device *dev, int k);
static void de4x5_dbg_media(struct device *dev);
static void de4x5_dbg_srom(struct de4x5_srom *p);
static void de4x5_dbg_rx(struct sk_buff *skb, int len);
static int de4x5_strncmp(char *a, char *b, int n);
+static int dc21041_infoleaf(struct device *dev);
+static int dc21140_infoleaf(struct device *dev);
+static int dc21142_infoleaf(struct device *dev);
+static int dc21143_infoleaf(struct device *dev);
+static int type0_infoblock(struct device *dev, u_char count, u_char *p);
+static int type1_infoblock(struct device *dev, u_char count, u_char *p);
+static int type2_infoblock(struct device *dev, u_char count, u_char *p);
+static int type3_infoblock(struct device *dev, u_char count, u_char *p);
+static int type4_infoblock(struct device *dev, u_char count, u_char *p);
+static int type5_infoblock(struct device *dev, u_char count, u_char *p);
+static int compact_infoblock(struct device *dev, u_char count, u_char *p);
#ifdef MODULE
int init_module(void);
void cleanup_module(void);
+static struct device *unlink_modules(struct device *p);
static int autoprobed = 0, loading_module = 1;
# else
static int autoprobed = 0, loading_module = 0;
@@ -686,7 +885,37 @@
static char name[DE4X5_NAME_LENGTH + 1];
static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST;
static int num_de4x5s = 0, num_eth = 0;
-static int cfrv = 0;
+static int cfrv = 0, useSROM = 0;
+
+/*
+** List the SROM infoleaf functions and chipsets
+*/
+struct InfoLeaf {
+ int chipset;
+ int (*fn)(struct device *);
+};
+static struct InfoLeaf infoleaf_array[] = {
+ {DC21041, dc21041_infoleaf},
+ {DC21140, dc21140_infoleaf},
+ {DC21142, dc21142_infoleaf},
+ {DC21143, dc21143_infoleaf}
+};
+#define INFOLEAF_SIZE (sizeof(infoleaf_array)/(sizeof(int)+sizeof(int *)))
+
+/*
+** List the SROM info block functions
+*/
+static int (*dc_infoblock[])(struct device *dev, u_char, u_char *) = {
+ type0_infoblock,
+ type1_infoblock,
+ type2_infoblock,
+ type3_infoblock,
+ type4_infoblock,
+ type5_infoblock,
+ compact_infoblock
+};
+
+#define COMPACT (sizeof(dc_infoblock)/sizeof(int *) - 1)
/*
** Miscellaneous defines...
@@ -703,6 +932,13 @@
de4x5_ms_delay(1);\
}
+#define PHY_HARD_RESET {\
+ outl(GEP_HRST, DE4X5_GEP); /* Hard RESET the PHY dev. */\
+ udelay(1000); /* Assert for 1ms */\
+ outl(0x00, DE4X5_GEP);\
+ udelay(2000); /* Wait for 2ms */\
+}
+
/*
** Autoprobing in modules is allowed here. See the top of the file for
@@ -712,17 +948,12 @@
int
de4x5_probe(struct device *dev)
{
- int tmp = num_de4x5s, status = -ENODEV;
+ int status = -ENODEV;
u_long iobase = dev->base_addr;
eisa_probe(dev, iobase);
pci_probe(dev, iobase);
- if ((tmp == num_de4x5s) && (iobase != 0) && loading_module) {
- printk("%s: de4x5_probe() cannot find device at 0x%04lx.\n", dev->name,
- iobase);
- }
-
/*
** Walk the device list to check that at least one device
** initialised OK
@@ -731,7 +962,7 @@
if (dev->priv) status = 0;
if (iobase == 0) autoprobed = 1;
-
+
return status;
}
@@ -739,16 +970,18 @@
de4x5_hw_init(struct device *dev, u_long iobase)
{
struct bus_type *lp = &bus;
- int tmpbus, tmpchs, status=0;
- int i, media = *((char *)&(lp->srom) + *((char *)&(lp->srom) + 19) * 3);
+ int i, status=0;
char *tmp;
/* Ensure we're not sleeping */
- if (lp->chipset == DC21041) {
- outl(0, PCI_CFDA);
- de4x5_ms_delay(10);
+ if (lp->bus == EISA) {
+ outb(WAKEUP, PCI_CFPM);
+ } else {
+ pcibios_write_config_byte(lp->bus_num, lp->device << 3,
+ PCI_CFDA_PSM, WAKEUP);
}
-
+ de4x5_ms_delay(10);
+
RESET_DE4X5;
if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) {
@@ -758,6 +991,7 @@
/*
** Now find out what kind of DC21040/DC21041/DC21140 board we have.
*/
+ useSROM = FALSE;
if (lp->bus == PCI) {
PCI_signature(name, lp);
} else {
@@ -784,9 +1018,6 @@
}
printk("%2.2x,\n", dev->dev_addr[i]);
- tmpbus = lp->bus;
- tmpchs = lp->chipset;
-
if (status != 0) {
printk(" which has an Ethernet PROM CRC error.\n");
return -ENXIO;
@@ -810,26 +1041,23 @@
dev->priv = (void *)(((u_long)dev->priv + ALIGN) & ~ALIGN);
lp = (struct de4x5_private *)dev->priv;
memset(dev->priv, 0, sizeof(struct de4x5_private));
- lp->bus = tmpbus;
- lp->chipset = tmpchs;
+ lp->bus = bus.bus;
+ lp->bus_num = bus.bus_num;
+ lp->device = bus.device;
+ lp->chipset = bus.chipset;
lp->cache.priv = tmp;
+ lp->cache.gepc = GEP_INIT;
+ lp->asBit = GEP_SLNK;
+ lp->asPolarity = GEP_SLNK;
+ lp->asBitValid = TRUE;
+ lp->timeout = -1;
+ lp->useSROM = useSROM;
+ memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom));
/*
- ** Check for an MII interface
- */
- if (media & MEDIA_MII) { /* MII interface? */
- if (!mii_get_phy(dev)) {
- printk("%s: MII search failed, no device found when one was expected\n", dev->name);
- return -ENXIO;
- }
- } else {
- mii_get_phy(dev); /* Search the MII anyway! */
- }
-
- /*
** Choose correct autosensing in case someone messed up
*/
- if (de4x5_autosense & AUTO) {
+ if ((de4x5_autosense & AUTO) || lp->useSROM) {
lp->autosense = AUTO;
} else {
if (lp->chipset != DC21140) {
@@ -844,14 +1072,14 @@
lp->autosense = de4x5_autosense & 0x00c0;
}
}
-
+ lp->fdx = de4x5_full_duplex;
sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
/*
** Set up the RX descriptor ring (Intels)
** Allocate contiguous receive buffers, long word aligned (Alphas)
*/
-#if !defined(__alpha__) && !defined(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY)
for (i=0; i<NUM_RX_DESC; i++) {
lp->rx_ring[i].status = 0;
lp->rx_ring[i].des1 = RX_BUFF_SZ;
@@ -871,8 +1099,8 @@
tmp = (char *)(((u_long) tmp + ALIGN) & ~ALIGN);
for (i=0; i<NUM_RX_DESC; i++) {
lp->rx_ring[i].status = 0;
- lp->rx_ring[i].des1 = RX_BUFF_SZ;
- lp->rx_ring[i].buf = virt_to_bus(tmp + i * RX_BUFF_SZ);
+ lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ);
+ lp->rx_ring[i].buf = cpu_to_le32(virt_to_bus(tmp+i*RX_BUFF_SZ));
lp->rx_ring[i].next = 0;
lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */
}
@@ -888,8 +1116,8 @@
lp->txRingSize = NUM_TX_DESC;
/* Write the end of list marker to the descriptor lists */
- lp->rx_ring[lp->rxRingSize - 1].des1 |= RD_RER;
- lp->tx_ring[lp->txRingSize - 1].des1 |= TD_TER;
+ lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER);
+ lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER);
/* Tell the adapter where the TX/RX rings are located. */
outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
@@ -903,20 +1131,35 @@
create_packet(dev, lp->frame, sizeof(lp->frame));
/* Check if the RX overflow bug needs testing for */
- tmpchs = cfrv & 0x000000fe;
- if ((lp->chipset == DC21140) && (tmpchs == 0x20)) {
+ i = cfrv & 0x000000fe;
+ if ((lp->chipset == DC21140) && (i == 0x20)) {
lp->rx_ovf = 1;
}
- /* Initialise the adapter state */
+ /* Initialise the SROM pointers if possible */
+ if (lp->useSROM) {
+ lp->state = INITIALISED;
+ de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
+ if (srom_infoleaf_info(dev)) {
+ return -ENXIO;
+ }
+ srom_init(dev);
+ }
+
lp->state = CLOSED;
+ /*
+ ** Check for an MII interface
+ */
+ if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) {
+ mii_get_phy(dev);
+ }
+
printk(" and requires IRQ%d (provided by %s).\n", dev->irq,
((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG"));
-
}
- if (de4x5_debug > 1) {
+ if (de4x5_debug & DEBUG_VERSION) {
printk(version);
}
@@ -930,14 +1173,11 @@
dev->mem_start = 0;
- /* Fill in the generic field of the device structure. */
+ /* Fill in the generic fields of the device structure. */
ether_setup(dev);
/* Let the adapter sleep to save power */
- if (lp->chipset == DC21041) {
- outl(0, DE4X5_SICR);
- outl(CFDA_PSM, PCI_CFDA);
- }
+ yawn(dev, SLEEP);
return status;
}
@@ -962,10 +1202,7 @@
/*
** Wake up the adapter
*/
- if (lp->chipset == DC21041) {
- outl(0, PCI_CFDA);
- de4x5_ms_delay(10);
- }
+ yawn(dev, WAKEUP);
/*
** Re-initialize the DE4X5...
@@ -977,20 +1214,31 @@
if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ,
lp->adapter_name, dev)) {
- printk("de4x5_open(): Requested IRQ%d is busy\n",dev->irq);
- status = -EAGAIN;
- } else {
- dev->tbusy = 0;
- dev->start = 1;
- dev->interrupt = UNMASK_INTERRUPTS;
- dev->trans_start = jiffies;
-
- START_DE4X5;
-
- de4x5_setup_intr(dev);
+ printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq);
+ if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ,
+ lp->adapter_name, dev)) {
+ printk("\n Cannot get IRQ- reconfigure your hardware.\n");
+ disable_ast(dev);
+ de4x5_free_rx_buffs(dev);
+ de4x5_free_tx_buffs(dev);
+ yawn(dev, SLEEP);
+ lp->state = CLOSED;
+ return -EAGAIN;
+ } else {
+ printk("\n Succeeded, but you should reconfigure your hardware to avoid this.\n");
+ printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n");
+ }
}
+ dev->tbusy = 0;
+ dev->start = 1;
+ dev->interrupt = UNMASK_INTERRUPTS;
+ dev->trans_start = jiffies;
+
+ START_DE4X5;
+
+ de4x5_setup_intr(dev);
- if (de4x5_debug > 1) {
+ if (de4x5_debug & DEBUG_OPEN) {
printk("\tsts: 0x%08x\n", inl(DE4X5_STS));
printk("\tbmr: 0x%08x\n", inl(DE4X5_BMR));
printk("\timr: 0x%08x\n", inl(DE4X5_IMR));
@@ -1037,12 +1285,15 @@
s32 bmr, omr;
/* Select the MII or SRL port now and RESET the MAC */
- if (lp->phy[lp->active].id == 0) {
- de4x5_switch_to_srl(dev);
- } else {
- de4x5_switch_to_mii(dev);
+ if (!lp->useSROM) {
+ if (lp->phy[lp->active].id != 0) {
+ lp->infoblock_csr6 = OMR_PS | OMR_HBD;
+ } else {
+ lp->infoblock_csr6 = OMR_TTM;
+ }
+ de4x5_switch_mac_port(dev);
}
-
+
/*
** Set the programmable burst length to 8 longwords for all the DC21140
** Fasternet chips and 4 longwords for all others: DMA errors result
@@ -1063,11 +1314,11 @@
lp->tx_new = lp->tx_old = 0;
for (i = 0; i < lp->rxRingSize; i++) {
- lp->rx_ring[i].status = R_OWN;
+ lp->rx_ring[i].status = cpu_to_le32(R_OWN);
}
for (i = 0; i < lp->txRingSize; i++) {
- lp->tx_ring[i].status = 0;
+ lp->tx_ring[i].status = cpu_to_le32(0);
}
barrier();
@@ -1082,7 +1333,7 @@
sti(); /* Ensure timer interrupts */
for (j=0, i=0;(i<500) && (j==0);i++) { /* Upto 500ms delay */
udelay(1000);
- if (lp->tx_ring[lp->tx_new].status >= 0) j=1;
+ if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1;
}
outl(omr, DE4X5_OMR); /* Stop everything! */
@@ -1137,8 +1388,8 @@
} else {
de4x5_put_cache(dev, skb);
}
- if (de4x5_debug > 1) {
- printk("%s: transmit busy, lost media or stale skb found:\n STS:%08x\n tbusy:%ld\n lostMedia:%d\n IMR:%08x\n OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, lp->lostMedia, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO"));
+ if (de4x5_debug & DEBUG_TX) {
+ printk("%s: transmit busy, lost media or stale skb found:\n STS:%08x\n tbusy:%ld\n IMR:%08x\n OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO"));
}
} else if (skb->len > 0) {
/* If we already have stuff queued locally, use that first */
@@ -1151,6 +1402,9 @@
cli();
set_bit(0, (void*)&dev->tbusy);
load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
+#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
+ lp->stats.tx_bytes += skb->len;
+#endif
outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */
lp->tx_new = (++lp->tx_new) % lp->txRingSize;
@@ -1200,6 +1454,9 @@
printk("%s: Re-entering the interrupt handler.\n", dev->name);
DISABLE_IRQs; /* Ensure non re-entrancy */
+#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
+ synchronize_irq();
+#endif
dev->interrupt = MASK_INTERRUPTS;
for (limit=0; limit<8; limit++) {
@@ -1215,7 +1472,6 @@
de4x5_tx(dev);
if (sts & STS_LNF) { /* TP Link has failed */
- lp->lostMedia = LOST_MEDIA_THRESHOLD + 1;
lp->irq_mask &= ~IMR_LFM;
}
@@ -1253,8 +1509,9 @@
int entry;
s32 status;
- for (entry=lp->rx_new; lp->rx_ring[entry].status>=0;entry=lp->rx_new) {
- status = lp->rx_ring[entry].status;
+ for (entry=lp->rx_new; (s32)le32_to_cpu(lp->rx_ring[entry].status)>=0;
+ entry=lp->rx_new) {
+ status = (s32)le32_to_cpu(lp->rx_ring[entry].status);
if (lp->rx_ovf) {
if (inl(DE4X5_MFC) & MFC_FOCM) {
@@ -1268,7 +1525,7 @@
}
if (status & RD_LS) { /* Valid frame status */
- lp->linkOK++;
+ if (lp->tx_enable) lp->linkOK++;
if (status & RD_ES) { /* There was an error. */
lp->stats.rx_errors++; /* Update the error stats. */
if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++;
@@ -1281,31 +1538,35 @@
if (status & RD_OF) lp->pktStats.rx_overflow++;
} else { /* A valid frame received */
struct sk_buff *skb;
- short pkt_len = (short)(lp->rx_ring[entry].status >> 16) - 4;
+ short pkt_len = (short)(le32_to_cpu(lp->rx_ring[entry].status)
+ >> 16) - 4;
if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) {
printk("%s: Insufficient memory; nuking packet.\n",
dev->name);
- lp->stats.rx_dropped++; /* Really, deferred. */
- break;
- }
- de4x5_dbg_rx(skb, pkt_len);
+ lp->stats.rx_dropped++;
+ } else {
+ de4x5_dbg_rx(skb, pkt_len);
- /* Push up the protocol stack */
- skb->protocol=eth_type_trans(skb,dev);
- netif_rx(skb);
+ /* Push up the protocol stack */
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
- /* Update stats */
- lp->stats.rx_packets++;
- de4x5_local_stats(dev, skb->data, pkt_len);
+ /* Update stats */
+ lp->stats.rx_packets++;
+#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
+ lp->stats.rx_bytes += pkt_len;
+#endif
+ de4x5_local_stats(dev, skb->data, pkt_len);
+ }
}
/* Change buffer ownership for this frame, back to the adapter */
for (;lp->rx_old!=entry;lp->rx_old=(++lp->rx_old)%lp->rxRingSize) {
- lp->rx_ring[lp->rx_old].status = R_OWN;
+ lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN);
barrier();
}
- lp->rx_ring[entry].status = R_OWN;
+ lp->rx_ring[entry].status = cpu_to_le32(R_OWN);
barrier();
}
@@ -1330,7 +1591,7 @@
s32 status;
for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
- status = lp->tx_ring[entry].status;
+ status = (s32)le32_to_cpu(lp->tx_ring[entry].status);
if (status < 0) { /* Buffer not sent yet */
break;
} else if (status != 0x7fffffff) { /* Not setup frame */
@@ -1342,16 +1603,12 @@
if (status & TD_EC) lp->pktStats.excessive_collisions++;
if (status & TD_DE) lp->stats.tx_aborted_errors++;
- if (status & (TD_LO | TD_NC | TD_EC | TD_LF)) {
- lp->lostMedia++;
- }
if (TX_PKT_PENDING) {
outl(POLL_DEMAND, DE4X5_TPD);/* Restart a stalled TX */
}
} else { /* Packet sent */
lp->stats.tx_packets++;
- lp->lostMedia = 0; /* Remove transient problem */
- lp->linkOK++;
+ if (lp->tx_enable) lp->linkOK++;
}
/* Update the collision counter */
lp->stats.collisions += ((status & TD_EC) ? 16 :
@@ -1384,7 +1641,9 @@
disable_ast(dev);
- if (lp->chipset == DC21140) {
+ if (lp->useSROM) {
+ next_tick = srom_autoconf(dev);
+ } else if (lp->chipset == DC21140) {
next_tick = dc21140m_autoconf(dev);
} else if (lp->chipset == DC21041) {
next_tick = dc21041_autoconf(dev);
@@ -1431,8 +1690,8 @@
outl(omr & ~OMR_SR, DE4X5_OMR);
while (inl(DE4X5_STS) & STS_RS);
- for (; lp->rx_ring[lp->rx_new].status>=0;) {
- lp->rx_ring[lp->rx_new].status = R_OWN;
+ for (; (s32)le32_to_cpu(lp->rx_ring[lp->rx_new].status)>=0;) {
+ lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN);
lp->rx_new = (++lp->rx_new % lp->rxRingSize);
}
@@ -1452,7 +1711,7 @@
dev->start = 0;
dev->tbusy = 1;
- if (de4x5_debug > 1) {
+ if (de4x5_debug & DEBUG_CLOSE) {
printk("%s: Shutting down ethercard, status was %8.8x.\n",
dev->name, inl(DE4X5_STS));
}
@@ -1474,15 +1733,12 @@
MOD_DEC_USE_COUNT;
/* Put the adapter to sleep to save power */
- if (lp->chipset == DC21041) {
- outl(0, DE4X5_SICR);
- outl(CFDA_PSM, PCI_CFDA);
- }
+ yawn(dev, SLEEP);
return 0;
}
-static struct enet_statistics *
+static struct net_device_stats *
de4x5_get_stats(struct device *dev)
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
@@ -1529,12 +1785,12 @@
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- lp->tx_ring[lp->tx_new].buf = virt_to_bus(buf);
- lp->tx_ring[lp->tx_new].des1 &= TD_TER;
- lp->tx_ring[lp->tx_new].des1 |= flags;
+ lp->tx_ring[lp->tx_new].buf = cpu_to_le32(virt_to_bus(buf));
+ lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER);
+ lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags);
lp->tx_skb[lp->tx_new] = skb;
barrier();
- lp->tx_ring[lp->tx_new].status = T_OWN;
+ lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN);
barrier();
return;
@@ -1548,7 +1804,7 @@
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
u_long iobase = dev->base_addr;
-
+
/* First, double check that the adapter is open */
if (lp->state == OPEN) {
if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
@@ -1586,7 +1842,7 @@
u32 omr, crc, poly = CRC_POLYNOMIAL_LE;
char *pa;
unsigned char *addrs;
-
+
omr = inl(DE4X5_OMR);
omr &= ~(OMR_PR | OMR_PM);
pa = build_setup_frame(dev, ALL); /* Build the basic frame */
@@ -1639,15 +1895,15 @@
static void
eisa_probe(struct device *dev, u_long ioaddr)
{
- int i, maxSlots, status;
- u_short vendor, device;
- s32 cfid;
+ int i, maxSlots, status, device;
+ u_short vendor;
+ u32 cfid;
u_long iobase;
struct bus_type *lp = &bus;
char name[DE4X5_STRLEN];
struct device *tmp;
- if (!ioaddr && autoprobed) return; /* Been here before ! */
+ if (autoprobed) return; /* Been here before ! */
lp->bus = EISA;
@@ -1661,17 +1917,18 @@
maxSlots = i + 1;
}
- for (status = -ENODEV; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
+ for (status= -ENODEV;(i<maxSlots)&&(dev!=NULL);i++,iobase+=EISA_SLOT_INC) {
if (EISA_signature(name, EISA_ID)) {
- cfid = inl(PCI_CFID);
- cfrv = inl(PCI_CFRV);
- device = (u_short)(cfid >> 16);
+ cfid = (u32) inl(PCI_CFID);
+ cfrv = (u_short) inl(PCI_CFRV);
+ device = (cfid >> 8) & 0x00ffff00;
vendor = (u_short) cfid;
/* Read the EISA Configuration Registers */
dev->irq = inb(EISA_REG0);
dev->irq = de4x5_irq[(dev->irq >> 1) & 0x03];
+ if (is_DC2114x) device |= (cfrv & 0x00f0);
lp->chipset = device;
DevicePresent(DE4X5_APROM);
/* Write the PCI Configuration Registers */
@@ -1683,6 +1940,9 @@
if ((tmp = alloc_device(dev, iobase)) != NULL) {
if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
num_de4x5s++;
+ if (loading_module) link_modules(dev, tmp);
+ } else if (loading_module && (tmp != dev)) {
+ kfree(tmp);
}
}
} else if (autoprobed) {
@@ -1714,13 +1974,13 @@
{
u_char irq;
u_char pb, pbus, dev_num, dnum, dev_fn;
- u_short vendor, device, index, status;
+ u_short dev_id, vendor, index, status;
u_int class = DE4X5_CLASS_CODE;
- u_int iobase;
+ u_int device, iobase;
struct bus_type *lp = &bus;
struct device *tmp;
- if ((!ioaddr || !loading_module) && autoprobed) return;
+ if (autoprobed) return;
if (!pcibios_present()) return; /* No PCI bus in this machine! */
@@ -1738,21 +1998,32 @@
(pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
index++) {
dev_num = PCI_SLOT(dev_fn);
-
+
if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) {
+ device = 0;
pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
- pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &device);
- if (!(is_DC21040 || is_DC21041 || is_DC21140)) continue;
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
+ device = dev_id;
+ device <<= 8;
+ if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) {
+ continue;
+ }
+
+ /* Get the chip configuration revision register */
+ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
/* Set the device number information */
lp->device = dev_num;
lp->bus_num = pb;
/* Set the chipset information */
+ if (is_DC2114x) device |= (cfrv & 0x00f0);
lp->chipset = device;
-
- /* Get the chip configuration revision register */
- pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
+
+ if (is_DC21142 || is_DC21143) {
+ printk("de4x5: Detected a %s chip. Currently this is unsupported in this driver.\nPlease email the author to request its inclusion!\n", (is_DC21142?"DC21142":"DC21143"));
+ continue;
+ }
/* Get the board I/O address */
pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
@@ -1778,6 +2049,9 @@
tmp->irq = irq;
if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
num_de4x5s++;
+ if (loading_module) link_modules(dev, tmp);
+ } else if (loading_module && (tmp != dev)) {
+ kfree(tmp);
}
}
} else if (autoprobed) {
@@ -1802,9 +2076,17 @@
struct device *adev = NULL;
int fixed = 0, new_dev = 0;
+ if (!dev) return dev;
num_eth = de4x5_dev_index(dev->name);
- if (loading_module) return dev;
-
+
+ if (loading_module) {
+ if (dev->priv) {
+ dev = insert_device(dev, iobase, de4x5_probe);
+ }
+ num_eth++;
+ return dev;
+ }
+
while (1) {
if (((dev->base_addr == DE4X5_NDA) || (dev->base_addr==0)) && !adev) {
adev=dev;
@@ -1851,20 +2133,22 @@
printk("eth%d: Device not initialised, insufficient memory\n",num_eth);
return NULL;
} else {
- new->next = dev->next;
- dev->next = new;
- dev = dev->next; /* point to the new device */
- dev->name = (char *)(dev + 1);
- if (num_eth > 9999) {
- sprintf(dev->name,"eth????");/* New device name */
- } else {
- sprintf(dev->name,"eth%d", num_eth);/* New device name */
+ memset((char *)new, 0, sizeof(struct device)+8);
+ new->name = (char *)(new + 1);
+ new->base_addr = iobase; /* assign the io address */
+ new->init = init; /* initialisation routine */
+ if (!loading_module) {
+ new->next = dev->next;
+ dev->next = new;
+ if (num_eth > 9999) {
+ sprintf(new->name,"eth????");/* New device name */
+ } else {
+ sprintf(new->name,"eth%d", num_eth);/* New device name */
+ }
}
- dev->base_addr = iobase; /* assign the io address */
- dev->init = init; /* initialisation routine */
}
- return dev;
+ return new;
}
static int
@@ -1882,6 +2166,26 @@
return i;
}
+static void
+link_modules(struct device *dev, struct device *tmp)
+{
+ struct device *p=dev;
+
+ if (p) {
+ while (((struct de4x5_private *)(p->priv))->next_module) {
+ p = ((struct de4x5_private *)(p->priv))->next_module;
+ }
+
+ if (dev != tmp) {
+ ((struct de4x5_private *)(p->priv))->next_module = tmp;
+ } else {
+ ((struct de4x5_private *)(p->priv))->next_module = NULL;
+ }
+ }
+
+ return;
+}
+
/*
** Auto configure the media here rather than setting the port at compile
** time. This routine is called by de4x5_init() and when a loss of media is
@@ -1901,13 +2205,16 @@
disable_ast(dev);
inl(DE4X5_MFC); /* Zero the lost frames counter */
lp->media = INIT;
- if (lp->chipset == DC21040) {
+ if (lp->useSROM) {
+ next_tick = srom_autoconf(dev);
+ } else if (lp->chipset == DC21040) {
next_tick = dc21040_autoconf(dev);
} else if (lp->chipset == DC21041) {
next_tick = dc21041_autoconf(dev);
} else if (lp->chipset == DC21140) {
next_tick = dc21140m_autoconf(dev);
}
+
enable_ast(dev, next_tick);
return (lp->media);
@@ -2046,7 +2353,7 @@
switch (lp->local_state) {
case 1:
- if (lp->linkOK && !LOST_MEDIA) {
+ if (lp->linkOK) {
lp->media = prev_state;
} else {
lp->local_state++;
@@ -2063,6 +2370,7 @@
lp->media = prev_state;
} else {
lp->media = INIT;
+ lp->tcount++;
}
}
@@ -2110,7 +2418,7 @@
case TP_NW:
if (lp->timeout < 0) {
omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */
- outl(omr | OMR_FD, DE4X5_OMR);
+ outl(omr | OMR_FDX, DE4X5_OMR);
}
irqs = STS_LNF | STS_LNP;
irq_mask = IMR_LFM | IMR_LPM;
@@ -2157,7 +2465,7 @@
if (!lp->tx_enable) {
if (lp->timeout < 0) {
omr = inl(DE4X5_OMR); /* Set up half duplex for TP */
- outl(omr & ~OMR_FD, DE4X5_OMR);
+ outl(omr & ~OMR_FDX, DE4X5_OMR);
}
irqs = STS_LNF | STS_LNP;
irq_mask = IMR_LFM | IMR_LPM;
@@ -2191,7 +2499,7 @@
if (!lp->tx_enable) {
if (lp->timeout < 0) {
omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */
- outl(omr & ~OMR_FD, DE4X5_OMR);
+ outl(omr & ~OMR_FDX, DE4X5_OMR);
}
irqs = 0;
irq_mask = 0;
@@ -2222,7 +2530,7 @@
case 0:
if (lp->timeout < 0) {
omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */
- outl(omr & ~OMR_FD, DE4X5_OMR);
+ outl(omr & ~OMR_FDX, DE4X5_OMR);
}
irqs = 0;
irq_mask = 0;
@@ -2261,7 +2569,7 @@
case NC:
omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */
- outl(omr | OMR_FD, DE4X5_OMR);
+ outl(omr | OMR_FDX, DE4X5_OMR);
reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */
if (lp->media != lp->c_media) {
de4x5_dbg_media(dev);
@@ -2287,34 +2595,45 @@
int ana, anlpa, cap, cr, slnk, sr, iobase = dev->base_addr;
int next_tick = DE4X5_AUTOSENSE_MS;
u_long imr, omr;
-
+
switch(lp->media) {
case INIT:
- DISABLE_IRQs;
- lp->tx_enable = FALSE;
- lp->timeout = -1;
+ if (lp->timeout < 0) {
+ DISABLE_IRQs;
+ lp->tx_enable = FALSE;
+ lp->linkOK = 0;
+ de4x5_save_skbs(dev); /* Save non transmitted skb's */
+ }
if ((next_tick = de4x5_reset_phy(dev)) < 0) {
next_tick &= ~TIMER_CB;
} else {
- de4x5_save_skbs(dev); /* Save non transmitted skb's */
- lp->tmp = MII_SR_ASSC; /* Fake out the MII speed set */
- SET_10Mb;
- if (lp->autosense == _100Mb) {
- lp->media = _100Mb;
- } else if (lp->autosense == _10Mb) {
- lp->media = _10Mb;
- } else if ((lp->autosense == AUTO) &&
- ((sr=is_anc_capable(dev)) & MII_SR_ANC)) {
- ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA);
- ana &= (de4x5_full_duplex ? ~0 : ~MII_ANA_FDAM);
- mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
- lp->media = ANS;
- } else if (lp->autosense == AUTO) {
- lp->media = SPD_DET;
- } else if (is_spd_100(dev) && is_100_up(dev)) {
- lp->media = _100Mb;
+ if (lp->useSROM) {
+ srom_map_media(dev);
+ srom_exec(dev, lp->phy[lp->active].gep);
+ if (lp->infoblock_media == ANS) {
+ ana = lp->phy[lp->active].ana | MII_ANA_CSMA;
+ mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+ }
} else {
- lp->media = NC;
+ lp->tmp = MII_SR_ASSC; /* Fake out the MII speed set */
+ SET_10Mb;
+ if (lp->autosense == _100Mb) {
+ lp->media = _100Mb;
+ } else if (lp->autosense == _10Mb) {
+ lp->media = _10Mb;
+ } else if ((lp->autosense == AUTO) &&
+ ((sr=is_anc_capable(dev)) & MII_SR_ANC)) {
+ ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA);
+ ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM);
+ mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+ lp->media = ANS;
+ } else if (lp->autosense == AUTO) {
+ lp->media = SPD_DET;
+ } else if (is_spd_100(dev) && is_100_up(dev)) {
+ lp->media = _100Mb;
+ } else {
+ lp->media = NC;
+ }
}
lp->local_state = 0;
next_tick = dc21140m_autoconf(dev);
@@ -2354,10 +2673,10 @@
if (!(anlpa & MII_ANLPA_RF) &&
(cap = anlpa & MII_ANLPA_TAF & ana)) {
if (cap & MII_ANA_100M) {
- de4x5_full_duplex = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE);
+ lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE);
lp->media = _100Mb;
} else if (cap & MII_ANA_10M) {
- de4x5_full_duplex = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE);
+ lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE);
lp->media = _10Mb;
}
@@ -2390,7 +2709,7 @@
break;
case _100Mb: /* Set 100Mb/s */
- next_tick = 3000;
+ next_tick = 3000;
if (!lp->tx_enable) {
SET_100Mb;
de4x5_init_connection(dev);
@@ -2398,6 +2717,7 @@
if (!lp->linkOK && (lp->autosense == AUTO)) {
if (!(is_spd_100(dev) && is_100_up(dev))) {
lp->media = INIT;
+ lp->tcount++;
next_tick = DE4X5_AUTOSENSE_MS;
}
}
@@ -2405,7 +2725,7 @@
break;
case _10Mb: /* Set 10Mb/s */
- next_tick = 3000;
+ next_tick = 3000;
if (!lp->tx_enable) {
SET_10Mb;
de4x5_init_connection(dev);
@@ -2413,6 +2733,7 @@
if (!lp->linkOK && (lp->autosense == AUTO)) {
if (!(!is_spd_100(dev) && is_10_up(dev))) {
lp->media = INIT;
+ lp->tcount++;
next_tick = DE4X5_AUTOSENSE_MS;
}
}
@@ -2432,6 +2753,72 @@
return next_tick;
}
+static int
+srom_autoconf(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+ return lp->infoleaf_fn(dev);
+}
+
+/*
+** This mapping keeps the original media codes and FDX flag unchanged.
+** While it isn't strictly necessary, it helps me for the moment...
+*/
+static void
+srom_map_media(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+ lp->fdx = 0;
+ switch(lp->infoblock_media) {
+ case SROM_10BASETF:
+ lp->fdx = TRUE;
+ case SROM_10BASET:
+ if (lp->chipset == DC21140) {
+ lp->media = _10Mb;
+ } else {
+ lp->media = TP;
+ }
+ break;
+
+ case SROM_10BASE2:
+ lp->media = BNC;
+ break;
+
+ case SROM_10BASE5:
+ lp->media = AUI;
+ break;
+
+ case SROM_100BASETF:
+ lp->fdx = TRUE;
+ case SROM_100BASET:
+ lp->media = _100Mb;
+ break;
+
+ case SROM_100BASET4:
+ lp->media = _100Mb;
+ break;
+
+ case SROM_100BASEFF:
+ lp->fdx = TRUE;
+ case SROM_100BASEF:
+ lp->media = _100Mb;
+ break;
+
+ case ANS:
+ lp->media = ANS;
+ break;
+
+ default:
+ printk("%s: Bad media code [%d] detected in SROM!\n", dev->name,
+ lp->infoblock_media);
+ break;
+ }
+
+ return;
+}
+
static void
de4x5_init_connection(struct device *dev)
{
@@ -2442,11 +2829,10 @@
de4x5_dbg_media(dev);
lp->c_media = lp->media; /* Stop scrolling media messages */
}
- de4x5_restore_skbs(dev);
+
cli();
- de4x5_rx(dev);
+ de4x5_restore_skbs(dev);
de4x5_setup_intr(dev);
- lp->lostMedia = 0;
lp->tx_enable = YES;
dev->tbusy = 0;
sti();
@@ -2456,6 +2842,11 @@
return;
}
+/*
+** General PHY reset function. Some MII devices don't reset correctly
+** since their MII address pins can float at voltages that are dependent
+** on the signal pin use. Do a double reset to ensure a reset.
+*/
static int
de4x5_reset_phy(struct device *dev)
{
@@ -2463,17 +2854,30 @@
u_long iobase = dev->base_addr;
int next_tick = 0;
- if (lp->phy[lp->active].id) {
+ if ((lp->useSROM) || (lp->phy[lp->active].id)) {
if (lp->timeout < 0) {
- outl(GEP_HRST, DE4X5_GEP); /* Hard RESET the PHY dev. */
- udelay(1000); /* Assert for 1ms */
- outl(0x00, DE4X5_GEP);
- udelay(2000); /* Wait for 2ms */
- mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
+ if (lp->useSROM) {
+ if (lp->phy[lp->active].rst) { /* MII device specific reset */
+ srom_exec(dev, lp->phy[lp->active].rst);
+ srom_exec(dev, lp->phy[lp->active].rst);
+ } else if (lp->rst) { /* Type 5 infoblock reset */
+ srom_exec(dev, lp->rst);
+ srom_exec(dev, lp->rst);
+ }
+ } else {
+ PHY_HARD_RESET;
+ }
+ if (lp->useMII) {
+ mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
+ }
+ }
+ if (lp->useMII) {
+ next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500);
}
- next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500);
+ } else if (lp->chipset == DC21140) {
+ PHY_HARD_RESET;
}
-
+
return next_tick;
}
@@ -2546,7 +2950,7 @@
lp->timeout = msec/100;
}
- if (lp->phy[lp->active].id) {
+ if (lp->phy[lp->active].id || lp->useSROM) {
gep = ((is_100_up(dev) && is_spd_100(dev)) ? GEP_SLNK : 0);
} else {
gep = (~inl(DE4X5_GEP) & (GEP_SLNK | GEP_LNP));
@@ -2594,7 +2998,11 @@
u_long iobase = dev->base_addr;
int spd;
- if (lp->phy[lp->active].id) {
+ if (lp->useSROM && !lp->useMII) {
+ spd = (lp->asBitValid &
+ (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
+ (lp->linkOK & ~lp->asBitValid);
+ } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII);
spd = ~(spd ^ lp->phy[lp->active].spd.value);
spd &= lp->phy[lp->active].spd.mask;
@@ -2611,7 +3019,11 @@
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
u_long iobase = dev->base_addr;
- if (lp->phy[lp->active].id) {
+ if (lp->useSROM && !lp->useMII) {
+ return ((lp->asBitValid &
+ (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
+ (lp->linkOK & ~lp->asBitValid));
+ } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
/* Double read for sticky bits & temporary drops */
mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
@@ -2626,7 +3038,11 @@
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
u_long iobase = dev->base_addr;
- if (lp->phy[lp->active].id) {
+ if (lp->useSROM && !lp->useMII) {
+ return ((lp->asBitValid &
+ (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
+ (lp->linkOK & ~lp->asBitValid));
+ } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
/* Double read for sticky bits & temporary drops */
mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
@@ -2641,7 +3057,7 @@
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
u_long iobase = dev->base_addr;
- if (lp->phy[lp->active].id) {
+ if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII));
} else {
return 0;
@@ -2670,11 +3086,14 @@
sisr = inl(DE4X5_SISR);
- if ((!(sisr & SISR_NCR)) && (lp->tx_ring[lp->tmp].status < 0) && (--lp->timeout)) {
+ if ((!(sisr & SISR_NCR)) &&
+ ((s32)le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) &&
+ (--lp->timeout)) {
sisr = 100 | TIMER_CB;
} else {
if ((!(sisr & SISR_NCR)) &&
- !(lp->tx_ring[lp->tmp].status & (T_OWN | TD_ES)) && lp->timeout) {
+ !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) &&
+ lp->timeout) {
sisr = 0;
} else {
sisr = 1;
@@ -2696,7 +3115,7 @@
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
struct sk_buff *p;
-#if !defined(__alpha__) && !defined(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY)
struct sk_buff *ret;
u_long i=0, tmp;
@@ -2728,10 +3147,13 @@
skb_reserve(p, 2); /* Align */
if (index < lp->rx_old) { /* Wrapped buffer */
short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ;
- memcpy(skb_put(p,tlen), bus_to_virt(lp->rx_ring[lp->rx_old].buf),tlen);
- memcpy(skb_put(p,len-tlen), bus_to_virt(lp->rx_ring[0].buf), len-tlen);
+ memcpy(skb_put(p,tlen),
+ bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),tlen);
+ memcpy(skb_put(p,len-tlen),
+ bus_to_virt(le32_to_cpu(lp->rx_ring[0].buf)), len-tlen);
} else { /* Linear buffer */
- memcpy(skb_put(p,len), bus_to_virt(lp->rx_ring[lp->rx_old].buf),len);
+ memcpy(skb_put(p,len),
+ bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),len);
}
return p;
@@ -2810,13 +3232,26 @@
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
u_long iobase = dev->base_addr;
+ int i;
s32 omr;
if (lp->cache.save_cnt) {
STOP_DE4X5;
- de4x5_cache_state(dev, DE4X5_SAVE_STATE);
- de4x5_sw_reset(dev);
- de4x5_cache_state(dev, DE4X5_RESTORE_STATE);
+ outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
+ outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
+
+ lp->rx_new = lp->rx_old = 0;
+ lp->tx_new = lp->tx_old = 0;
+
+ for (i = 0; i < lp->rxRingSize; i++) {
+ lp->rx_ring[i].status = cpu_to_le32(R_OWN);
+ }
+
+ for (i = 0; i < lp->txRingSize; i++) {
+ lp->tx_ring[i].status = cpu_to_le32(0);
+ }
+
+ barrier();
lp->cache.save_cnt--;
START_DE4X5;
}
@@ -2829,7 +3264,6 @@
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
u_long iobase = dev->base_addr;
- s32 gep;
switch(flag) {
case DE4X5_SAVE_STATE:
@@ -2848,12 +3282,8 @@
outl(lp->cache.csr6, DE4X5_OMR);
outl(lp->cache.csr7, DE4X5_IMR);
if (lp->chipset == DC21140) {
- outl(GEP_INIT, DE4X5_GEP);
- gep = (lp->media == _100Mb ? GEP_MODE : 0);
- if (!lp->phy[lp->active].id && !de4x5_full_duplex) {
- gep |= GEP_FDXD;
- }
- outl(gep, DE4X5_GEP);
+ outl(lp->cache.gepc, DE4X5_GEP);
+ outl(lp->cache.gep, DE4X5_GEP);
} else {
reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14,
lp->cache.csr15);
@@ -2975,7 +3405,7 @@
}
/*
-** Create a loopback ethernet packet with an invalid CRC
+** Create a loopback ethernet packet
*/
static void
create_packet(struct device *dev, char *frame, int len)
@@ -3069,13 +3499,10 @@
if (lp->chipset == DC21040) {
strcpy(name, "DE434/5");
- } else {
+ return status;
+ } else { /* Search for a DEC name in the SROM */
int i = *((char *)&lp->srom + 19) * 3;
- if (lp->chipset == DC21041) {
- strncpy(name, (char *)&lp->srom + 26 + i, 8);
- } else if (lp->chipset == DC21140) {
- strncpy(name, (char *)&lp->srom + 26 + i, 8);
- }
+ strncpy(name, (char *)&lp->srom + 26 + i, 8);
}
name[8] = '\0';
for (i=0; i<siglen; i++) {
@@ -3087,8 +3514,13 @@
} else { /* Use chip name to avoid confusion */
strcpy(name, (((lp->chipset == DC21040) ? "DC21040" :
((lp->chipset == DC21041) ? "DC21041" :
- ((lp->chipset == DC21140) ? "DC21140" : "UNKNOWN"
- )))));
+ ((lp->chipset == DC21140) ? "DC21140" :
+ ((lp->chipset == DC21142) ? "DC21142" :
+ ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN"
+ )))))));
+ }
+ if (lp->chipset != DC21041) {
+ useSROM = TRUE; /* card is not recognisably DEC */
}
}
@@ -3108,9 +3540,10 @@
if (lp->chipset == DC21040) {
outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */
} else { /* Read new srom */
- short *p = (short *)&lp->srom;
+ u_short tmp, *p = (short *)&lp->srom;
for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) {
- *p++ = srom_rd(aprom_addr, i);
+ tmp = srom_rd(aprom_addr, i);
+ *p++ = le16_to_cpu(tmp);
}
de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
}
@@ -3118,6 +3551,12 @@
return;
}
+/*
+** For the bad status case and no SROM, then add one to the previous
+** address. However, need to add one backwards in case we have 0xff
+** as one or more of the bytes. Only the last 3 bytes should be checked
+** as the first three are invariant - assigned to an organisation.
+*/
static int
get_hw_addr(struct device *dev)
{
@@ -3171,6 +3610,12 @@
if ((k != chksum) && (dec_only)) status = -1;
}
+ /* If possible, try to fix a broken card - SMC only so far */
+ srom_repair(dev, broken);
+
+ /* Test for a bad enet address */
+ status = test_bad_enet(dev, status);
+
return status;
}
@@ -3210,6 +3655,55 @@
return ret;
}
+static void
+srom_repair(struct device *dev, int card)
+{
+ struct bus_type *lp = &bus;
+
+ switch(card) {
+ case SMC:
+ memset((char *)&bus.srom, 0, sizeof(struct de4x5_srom));
+ memcpy(lp->srom.ieee_addr, (char *)dev->dev_addr, ETH_ALEN);
+ memcpy(lp->srom.info, (char *)&srom_repair_info[SMC-1], 100);
+ useSROM = TRUE;
+ break;
+ }
+
+ return;
+}
+
+static int
+test_bad_enet(struct device *dev, int status)
+{
+ struct bus_type *lp = &bus;
+ int i, tmp;
+
+ for (tmp=0,i=0; i<ETH_ALEN; i++) tmp += (u_char)dev->dev_addr[i];
+ if ((tmp == 0) || (tmp == 0x5fa)) {
+ if ((lp->chipset == last.chipset) &&
+ (lp->bus_num == last.bus) && (lp->bus_num > 0)) {
+ for (i=0; i<ETH_ALEN; i++) dev->dev_addr[i] = last.addr[i];
+ for (i=ETH_ALEN-1; i>2; --i) {
+ dev->dev_addr[i] += 1;
+ if (dev->dev_addr[i] != 0) break;
+ }
+ for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i];
+ if (((*((int *)dev->dev_addr) & 0x00ffffff) == 0x95c000) &&
+ (lp->chipset == DC21040)) {
+ dev->irq = last.irq;
+ }
+ status = 0;
+ }
+ } else if (!status) {
+ last.chipset = lp->chipset;
+ last.bus = lp->bus_num;
+ last.irq = dev->irq;
+ for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i];
+ }
+
+ return status;
+}
+
/*
** SROM Read
*/
@@ -3321,16 +3815,401 @@
return tmp;
}
-/*
-** MII Read/Write
-*/
-
static int
-mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr)
+srom_infoleaf_info(struct device *dev)
{
- mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */
- mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */
- mii_wdata(MII_STRD, 4, ioaddr); /* SFD and Read operation */
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int i, count;
+ u_char *p;
+
+ /* Find the infoleaf decoder function that matches this chipset */
+ for (i=0; i<INFOLEAF_SIZE; i++) {
+ if (lp->chipset == infoleaf_array[i].chipset) break;
+ }
+ if (i == INFOLEAF_SIZE) {
+ lp->useSROM = FALSE;
+ printk("%s: Cannot find correct chipset for SROM decoding!\n",
+ dev->name);
+ return -ENXIO;
+ }
+
+ lp->infoleaf_fn = infoleaf_array[i].fn;
+
+ /* Find the information offset that this function should use */
+ count = *((u_char *)&lp->srom + 19);
+ p = (u_char *)&lp->srom + 26;
+
+ if (count > 1) {
+ for (i=count; i; --i, p+=3) {
+ if (lp->device == *p) break;
+ }
+ if (i == 0) {
+ lp->useSROM = FALSE;
+ printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n",
+ dev->name, lp->device);
+ return -ENXIO;
+ }
+ }
+
+ lp->infoleaf_offset = TWIDDLE(p+1);
+
+ return 0;
+}
+
+/*
+** This routine loads any type 1 or 3 MII info into the mii device
+** struct and executes any type 5 code to reset PHY devices for this
+** controller.
+** The info for the MII devices will be valid since the index used
+** will follow the discovery process from MII address 1-31 then 0.
+*/
+static void
+srom_init(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
+ u_char count;
+
+ p+=2;
+ if (lp->chipset == DC21140) {
+ lp->cache.gepc = (*p++ | GEP_CTRL);
+ outl(lp->cache.gepc, DE4X5_GEP);
+ }
+
+ /* Block count */
+ count = *p++;
+
+ /* Jump the infoblocks to find types */
+ for (;count; --count) {
+ if (*p < 128) {
+ p += COMPACT_LEN;
+ } else if (*(p+1) == 5) {
+ type5_infoblock(dev, 1, p);
+ p += ((*p & BLOCK_LEN) + 1);
+ } else if (*(p+1) == 3) {
+ type3_infoblock(dev, 1, p);
+ p += ((*p & BLOCK_LEN) + 1);
+ } else if (*(p+1) == 1) {
+ type1_infoblock(dev, 1, p);
+ p += ((*p & BLOCK_LEN) + 1);
+ } else {
+ p += ((*p & BLOCK_LEN) + 1);
+ }
+ }
+
+ return;
+}
+
+static void
+srom_exec(struct device *dev, u_char *p)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char count = (p ? *p++ : 0);
+
+ while (count--) {
+ if (lp->chipset == DC21140) {
+ outl(*p++, DE4X5_GEP);
+ }
+ udelay(2000); /* 2ms per action */
+ }
+
+ return;
+}
+
+/*
+** Basically this function is a NOP since it will never be called,
+** unless I implement the DC21041 SROM functions. There's no need
+** since the existing code will be satisfactory for all boards.
+*/
+static int
+dc21041_infoleaf(struct device *dev)
+{
+ return DE4X5_AUTOSENSE_MS;
+}
+
+static int
+dc21140_infoleaf(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_char count = 0;
+ u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
+ int next_tick = DE4X5_AUTOSENSE_MS;
+
+ /* Read the connection type */
+ p+=2;
+
+ /* GEP control */
+ lp->cache.gepc = (*p++ | GEP_CTRL);
+
+ /* Block count */
+ count = *p++;
+
+ /* Recursively figure out the info blocks */
+ if (*p < 128) {
+ next_tick = dc_infoblock[COMPACT](dev, count, p);
+ } else {
+ next_tick = dc_infoblock[*(p+1)](dev, count, p);
+ }
+
+ if (lp->tcount == count) {
+ lp->media = NC;
+ if (lp->media != lp->c_media) {
+ de4x5_dbg_media(dev);
+ lp->c_media = lp->media;
+ }
+ lp->media = INIT;
+ lp->tcount = 0;
+ lp->tx_enable = FALSE;
+ }
+
+ return next_tick & ~TIMER_CB;
+}
+
+static int
+dc21142_infoleaf(struct device *dev)
+{
+printk("dc21142_infoleaf()\n");
+ return DE4X5_AUTOSENSE_MS;
+}
+
+static int
+dc21143_infoleaf(struct device *dev)
+{
+printk("dc21143_infoleaf()\n");
+ return DE4X5_AUTOSENSE_MS;
+}
+
+/*
+** The compact infoblock is only designed for DC21140[A] chips, so
+** we'll reuse the dc21140m_autoconf function. Non MII media only.
+*/
+static int
+compact_infoblock(struct device *dev, u_char count, u_char *p)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char flags, csr6;
+
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p+COMPACT_LEN) < 128) {
+ return dc_infoblock[COMPACT](dev, count, p+COMPACT_LEN);
+ } else {
+ return dc_infoblock[*(p+COMPACT_LEN+1)](dev, count, p+COMPACT_LEN);
+ }
+ }
+
+ if ((lp->media == INIT) && (lp->timeout < 0)) {
+ outl(lp->cache.gepc, DE4X5_GEP);
+ lp->infoblock_media = (*p++) & COMPACT_MC;
+ lp->cache.gep = *p++;
+ csr6 = *p++;
+ flags = *p++;
+
+ lp->asBitValid = (flags & 0x80) ? 0 : -1;
+ lp->defMedium = (flags & 0x40) ? -1 : 0;
+ lp->asBit = 1 << ((csr6 >> 1) & 0x07);
+ lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
+ lp->infoblock_csr6 = (csr6 & 0x71) << 18;
+ lp->useMII = FALSE;
+
+ de4x5_switch_mac_port(dev);
+ }
+
+ return dc21140m_autoconf(dev);
+}
+
+/*
+** This block describes non MII media for the DC21140[A] only.
+ */
+static int
+type0_infoblock(struct device *dev, u_char count, u_char *p)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
+
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p+len) < 128) {
+ return dc_infoblock[COMPACT](dev, count, p+len);
+ } else {
+ return dc_infoblock[*(p+len+1)](dev, count, p+len);
+ }
+ }
+
+ if ((lp->media == INIT) && (lp->timeout < 0)) {
+ outl(lp->cache.gepc, DE4X5_GEP);
+ p+=2;
+ lp->infoblock_media = (*p++) & BLOCK0_MC;
+ lp->cache.gep = *p++;
+ csr6 = *p++;
+ flags = *p++;
+
+ lp->asBitValid = (flags & 0x80) ? 0 : -1;
+ lp->defMedium = (flags & 0x40) ? -1 : 0;
+ lp->asBit = 1 << ((csr6 >> 1) & 0x07);
+ lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
+ lp->infoblock_csr6 = (csr6 & 0x71) << 18;
+ lp->useMII = FALSE;
+
+ de4x5_switch_mac_port(dev);
+ }
+
+ return dc21140m_autoconf(dev);
+}
+
+/* These functions are under construction! */
+
+static int
+type1_infoblock(struct device *dev, u_char count, u_char *p)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_char len = (*p & BLOCK_LEN)+1;
+
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p+len) < 128) {
+ return dc_infoblock[COMPACT](dev, count, p+len);
+ } else {
+ return dc_infoblock[*(p+len+1)](dev, count, p+len);
+ }
+ }
+
+ p += 2;
+ if (lp->state == INITIALISED) {
+ lp->ibn = 1;
+ lp->active = *p++;
+ lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
+ lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
+ lp->phy[lp->active].mc = TWIDDLE(p); p += 2;
+ lp->phy[lp->active].ana = TWIDDLE(p); p += 2;
+ lp->phy[lp->active].fdx = TWIDDLE(p); p += 2;
+ lp->phy[lp->active].ttm = TWIDDLE(p);
+ return 0;
+ } else if ((lp->media == INIT) && (lp->timeout < 0)) {
+ lp->ibn = 1;
+ lp->active = *p;
+ lp->infoblock_csr6 = OMR_PS | OMR_HBD;
+ lp->useMII = TRUE;
+ lp->infoblock_media = ANS;
+ de4x5_switch_mac_port(dev);
+ }
+
+ return dc21140m_autoconf(dev);
+}
+
+static int
+type2_infoblock(struct device *dev, u_char count, u_char *p)
+{
+ return DE4X5_AUTOSENSE_MS;
+}
+
+static int
+type3_infoblock(struct device *dev, u_char count, u_char *p)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
+
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p+len) < 128) {
+ return dc_infoblock[COMPACT](dev, count, p+len);
+ } else {
+ return dc_infoblock[*(p+len+1)](dev, count, p+len);
+ }
+ }
+
+ if (lp->state == INITIALISED) {
+ lp->ibn = 3; p += 2;
+ lp->active = *p++;
+ lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
+ lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
+ lp->phy[lp->active].mc = TWIDDLE(p); p += 2;
+ lp->phy[lp->active].ana = TWIDDLE(p); p += 2;
+ lp->phy[lp->active].fdx = TWIDDLE(p); p += 2;
+ lp->phy[lp->active].ttm = TWIDDLE(p);
+ return 0;
+ } else if (lp->media == INIT) {
+ p+=2;
+ lp->infoblock_media = (*p++) & BLOCK0_MC;
+ lp->cache.gep = *p++;
+ csr6 = *p++;
+ flags = *p++;
+
+ lp->asBitValid = (flags & 0x80) ? 0 : -1;
+ lp->defMedium = (flags & 0x40) ? -1 : 0;
+ lp->asBit = 1 << ((csr6 >> 1) & 0x07);
+ lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
+ lp->infoblock_csr6 = (csr6 & 0x71) << 18;
+ lp->useMII = TRUE;
+
+ de4x5_switch_mac_port(dev);
+ }
+
+ return dc21140m_autoconf(dev);
+}
+
+static int
+type4_infoblock(struct device *dev, u_char count, u_char *p)
+{
+ return DE4X5_AUTOSENSE_MS;
+}
+
+/*
+** This block type provides information for resetting external devices
+** (chips) through the General Purpose Register.
+*/
+static int
+type5_infoblock(struct device *dev, u_char count, u_char *p)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char i, j, len = (*p & BLOCK_LEN)+1;
+
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p+len) < 128) {
+ return dc_infoblock[COMPACT](dev, count, p+len);
+ } else {
+ return dc_infoblock[*(p+len+1)](dev, count, p+len);
+ }
+ }
+
+ /* Must be initializing to run this code */
+ if ((lp->state == INITIALISED) || (lp->media == INIT)) {
+ p+=2;
+ lp->rst = p;
+ i = *p++;
+ for (j=0;i;--i) {
+ if (lp->chipset == DC21140) {
+ if (!j) {
+ outl(*p++ | GEP_CTRL, DE4X5_GEP);
+ j++;
+ }
+ outl(*p++, DE4X5_GEP);
+ } else if (lp->chipset == DC21142) {
+ } else if (lp->chipset == DC21143) {
+ }
+ }
+
+ }
+
+ return DE4X5_AUTOSENSE_MS;
+}
+
+/*
+** MII Read/Write
+*/
+
+static int
+mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr)
+{
+ mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */
+ mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */
+ mii_wdata(MII_STRD, 4, ioaddr); /* SFD and Read operation */
mii_address(phyaddr, ioaddr); /* PHY address to be accessed */
mii_address(phyreg, ioaddr); /* PHY Register to read */
mii_ta(MII_STRD, ioaddr); /* Turn around time - 2 MDC */
@@ -3494,23 +4373,25 @@
return r2; /* (I did it) My way */
}
+/*
+** The SROM spec forces us to search addresses [1-31 0]. Bummer.
+*/
static int
mii_get_phy(struct device *dev)
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
int iobase = dev->base_addr;
- int i, j, k, limit=sizeof(phy_info)/sizeof(struct phy_table);
+ int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table);
int id;
- /* Issue a hard PHY reset - Broadcom is screwed up otherwise */
- outl(GEP_HRST, DE4X5_GEP);
- udelay(1000); /* Assert for 1ms */
- outl(0x00, DE4X5_GEP);
- udelay(2000); /* Wait for 2ms */
-
- /* Search the MII address space for possible PHY devices */
lp->active = 0;
- for (lp->mii_cnt=0, i=1; i<DE4X5_MAX_MII; i++) {
+ lp->useMII = TRUE;
+
+ /* Search the MII address space for possible PHY devices */
+ for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(++i)%DE4X5_MAX_MII) {
+ lp->phy[lp->active].addr = i;
+ if (i==0) n++; /* Count cycles */
+ while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */
id = mii_get_oui(i, DE4X5_MII);
if ((id == 0) || (id == -1)) continue; /* Valid ID? */
for (j=0; j<limit; j++) { /* Search PHY table */
@@ -3521,13 +4402,15 @@
(char *)&phy_info[j], sizeof(struct phy_table));
lp->phy[k].addr = i;
lp->mii_cnt++;
+ lp->active++;
} else {
i = DE4X5_MAX_MII; /* Stop the search */
j = limit;
}
}
}
- if (lp->phy[lp->active].id) { /* Reset the PHY devices */
+ lp->active = 0;
+ if (lp->phy[0].id) { /* Reset the PHY devices */
for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/
mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII);
while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST);
@@ -3590,15 +4473,17 @@
}
static long
-de4x5_switch_to_mii(struct device *dev)
+de4x5_switch_mac_port(struct device *dev)
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
int iobase = dev->base_addr;
- long omr;
-
+ s32 omr;
+
/* Assert the OMR_PS bit in CSR6 */
- omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
- omr |= (OMR_PS | OMR_HBD);
+ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR |
+ OMR_FDX));
+ omr |= lp->infoblock_csr6;
+ if (omr & OMR_PS) omr |= OMR_HBD;
outl(omr, DE4X5_OMR);
/* Soft Reset */
@@ -3606,42 +4491,13 @@
/* Restore the GEP */
if (lp->chipset == DC21140) {
- outl(GEP_INIT, DE4X5_GEP);
- outl(0, DE4X5_GEP);
+ outl(lp->cache.gepc, DE4X5_GEP);
+ outl(lp->cache.gep, DE4X5_GEP);
}
-
- /* Restore CSR6 */
- outl(omr, DE4X5_OMR);
- /* Reset CSR8 */
- inl(DE4X5_MFC);
-
- return omr;
-}
-
-static long
-de4x5_switch_to_srl(struct device *dev)
-{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int iobase = dev->base_addr;
- long omr;
-
- /* Deassert the OMR_PS bit in CSR6 */
- omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
- outl(omr, DE4X5_OMR);
-
- /* Soft Reset */
- RESET_DE4X5;
-
- /* Restore the GEP */
- if (lp->chipset == DC21140) {
- outl(GEP_INIT, DE4X5_GEP);
- outl(0, DE4X5_GEP);
- }
-
/* Restore CSR6 */
outl(omr, DE4X5_OMR);
-
+
/* Reset CSR8 */
inl(DE4X5_MFC);
@@ -3671,12 +4527,60 @@
}
static void
+yawn(struct device *dev, int state)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+
+ if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return;
+
+ if(lp->bus == EISA) {
+ switch(state) {
+ case WAKEUP:
+ outb(WAKEUP, PCI_CFPM);
+ de4x5_ms_delay(10);
+ break;
+
+ case SNOOZE:
+ outb(SNOOZE, PCI_CFPM);
+ break;
+
+ case SLEEP:
+ outl(0, DE4X5_SICR);
+ outb(SLEEP, PCI_CFPM);
+ break;
+ }
+ } else {
+ switch(state) {
+ case WAKEUP:
+ pcibios_write_config_byte(lp->bus_num, lp->device << 3,
+ PCI_CFDA_PSM, WAKEUP);
+ de4x5_ms_delay(10);
+ break;
+
+ case SNOOZE:
+ pcibios_write_config_byte(lp->bus_num, lp->device << 3,
+ PCI_CFDA_PSM, SNOOZE);
+ break;
+
+ case SLEEP:
+ outl(0, DE4X5_SICR);
+ pcibios_write_config_byte(lp->bus_num, lp->device << 3,
+ PCI_CFDA_PSM, SLEEP);
+ break;
+ }
+ }
+
+ return;
+}
+
+static void
de4x5_dbg_open(struct device *dev)
{
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
int i;
- if (de4x5_debug > 1) {
+ if (de4x5_debug & DEBUG_OPEN) {
printk("%s: de4x5 opening with irq %d\n",dev->name,dev->irq);
printk("\tphysical address: ");
for (i=0;i<6;i++) {
@@ -3702,17 +4606,17 @@
printk("Descriptor buffers:\nRX: ");
for (i=0;i<lp->rxRingSize-1;i++){
if (i < 3) {
- printk("0x%8.8x ",lp->rx_ring[i].buf);
+ printk("0x%8.8x ",le32_to_cpu(lp->rx_ring[i].buf));
}
}
- printk("...0x%8.8x\n",lp->rx_ring[i].buf);
+ printk("...0x%8.8x\n",le32_to_cpu(lp->rx_ring[i].buf));
printk("TX: ");
for (i=0;i<lp->txRingSize-1;i++){
if (i < 3) {
- printk("0x%8.8x ", lp->tx_ring[i].buf);
+ printk("0x%8.8x ", le32_to_cpu(lp->tx_ring[i].buf));
}
}
- printk("...0x%8.8x\n", lp->tx_ring[i].buf);
+ printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf));
printk("Ring size: \nRX: %d\nTX: %d\n",
(short)lp->rxRingSize,
(short)lp->txRingSize);
@@ -3727,7 +4631,7 @@
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
int iobase = dev->base_addr;
- if (de4x5_debug > 2) {
+ if (de4x5_debug & DEBUG_MII) {
printk("\nMII CR: %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII));
printk("MII SR: %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII));
printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII));
@@ -3754,7 +4658,7 @@
struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
if (lp->media != lp->c_media) {
- if (de4x5_debug > 0) {
+ if (de4x5_debug & DEBUG_MEDIA) {
if (lp->chipset != DC21140) {
printk("%s: media is %s\n", dev->name,
(lp->media == NC ? "unconnected!" :
@@ -3786,10 +4690,12 @@
{
int i;
- if (de4x5_debug > 1) {
- printk("Sub-system Vendor ID: %04x\n", (u_short)*(p->sub_vendor_id));
- printk("Sub-system ID: %04x\n", (u_short)*(p->sub_system_id));
+ if (de4x5_debug & DEBUG_SROM) {
+ printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id));
+ printk("Sub-system ID: %04x\n", *((u_short *)p->sub_system_id));
printk("ID Block CRC: %02x\n", (u_char)(p->id_block_crc));
+ printk("SROM version: %02x\n", (u_char)(p->version));
+ printk("# controllers: %02x\n", (u_char)(p->num_controllers));
printk("Hardware Address: ");
for (i=0;i<ETH_ALEN-1;i++) {
@@ -3810,7 +4716,7 @@
{
int i, j;
- if (de4x5_debug > 2) {
+ if (de4x5_debug & DEBUG_RX) {
printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n",
(u_char)skb->data[0],
(u_char)skb->data[1],
@@ -3827,7 +4733,7 @@
(u_char)skb->data[12],
(u_char)skb->data[13],
len);
- if (de4x5_debug > 3) {
+ if (de4x5_debug & DEBUG_RX) {
for (j=0; len>0;j+=16, len-=16) {
printk(" %03x: ",j);
for (i=0; i<16 && i<len; i++) {
@@ -3854,9 +4760,9 @@
int i, j, status = 0;
s32 omr;
union {
- u8 addr[(HASH_TABLE_LEN * ETH_ALEN)];
- u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
- u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2];
+ u8 addr[144];
+ u16 sval[72];
+ u32 lval[36];
} tmp;
switch(ioc->cmd) {
@@ -3868,7 +4774,7 @@
for (i=0; i<ETH_ALEN; i++) {
tmp.addr[i] = dev->dev_addr[i];
}
- memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ copy_to_user(ioc->data, tmp.addr, ioc->len);
break;
case DE4X5_SET_HWADDR: /* Set the hardware address */
@@ -3879,7 +4785,7 @@
if (!suser())
break;
status = 0;
- memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN);
+ copy_from_user(tmp.addr, ioc->data, ETH_ALEN);
for (i=0; i<ETH_ALEN; i++) {
dev->dev_addr[i] = tmp.addr[i];
}
@@ -3918,39 +4824,12 @@
break;
case DE4X5_GET_MCA: /* Get the multicast address table */
- ioc->len = (HASH_TABLE_LEN >> 3);
- status = verify_area(VERIFY_WRITE, ioc->data, ioc->len);
- if (!status) {
- memcpy_tofs(ioc->data, lp->setup_frame, ioc->len);
- }
-
break;
case DE4X5_SET_MCA: /* Set a multicast address */
- if (suser()) {
- /******* FIX ME! ********/
- if (ioc->len != HASH_TABLE_LEN) { /* MCA changes */
- if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN * ioc->len))) {
- memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
- set_multicast_list(dev);
- }
- } else {
- set_multicast_list(dev);
- }
- } else {
- status = -EPERM;
- }
-
break;
case DE4X5_CLR_MCA: /* Clear all multicast addresses */
- if (suser()) {
- /******* FIX ME! ********/
- set_multicast_list(dev);
- } else {
- status = -EPERM;
- }
-
break;
- case DE4X5_MCA_EN: /* Enable pass all multicast addressing */
+ case DE4X5_MCA_EN: /* Enable pass all multicast addresses*/
if (suser()) {
omr = inl(DE4X5_OMR);
omr |= OMR_PM;
@@ -3967,7 +4846,7 @@
break;
cli();
- memcpy_tofs(ioc->data, &lp->pktStats, ioc->len);
+ copy_to_user(ioc->data, &lp->pktStats, ioc->len);
sti();
break;
@@ -3984,14 +4863,14 @@
case DE4X5_GET_OMR: /* Get the OMR Register contents */
tmp.addr[0] = inl(DE4X5_OMR);
if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, 1))) {
- memcpy_tofs(ioc->data, tmp.addr, 1);
+ copy_to_user(ioc->data, tmp.addr, 1);
}
break;
case DE4X5_SET_OMR: /* Set the OMR Register contents */
if (suser()) {
if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) {
- memcpy_fromfs(tmp.addr, ioc->data, 1);
+ copy_from_user(tmp.addr, ioc->data, 1);
outl(tmp.addr[0], DE4X5_OMR);
}
} else {
@@ -4011,11 +4890,12 @@
tmp.lval[7] = inl(DE4X5_SIGR); j+=4;
ioc->len = j;
if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
- memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ copy_to_user(ioc->data, tmp.addr, ioc->len);
}
break;
-#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */
+/*
+#define DE4X5_DUMP 0x0f / * Dump the DE4X5 Status * /
case DE4X5_DUMP:
j = 0;
@@ -4042,22 +4922,22 @@
for (i=0;i<lp->rxRingSize-1;i++){
if (i < 3) {
- tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4;
+ tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4;
}
}
- tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4;
+ tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4;
for (i=0;i<lp->txRingSize-1;i++){
if (i < 3) {
- tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4;
+ tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4;
}
}
- tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4;
+ tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4;
for (i=0;i<lp->rxRingSize;i++){
- tmp.lval[j>>2] = lp->rx_ring[i].status; j+=4;
+ tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4;
}
for (i=0;i<lp->txRingSize;i++){
- tmp.lval[j>>2] = lp->tx_ring[i].status; j+=4;
+ tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4;
}
tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4;
@@ -4078,7 +4958,7 @@
tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4;
}
tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4;
- if (lp->phy[lp->active].id) {
+ if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
tmp.lval[j>>2] = lp->active; j+=4;
tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
@@ -4102,10 +4982,11 @@
ioc->len = j;
if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
- memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ copy_to_user(ioc->data, tmp.addr, ioc->len);
}
break;
+*/
default:
status = -EOPNOTSUPP;
}
@@ -4118,85 +4999,60 @@
** Note now that module autoprobing is allowed under EISA and PCI. The
** IRQ lines will not be auto-detected; instead I'll rely on the BIOSes
** to "do the right thing".
-**
-** NB: Current register_netdevice() code does not permit assigning io=0 as
-** this driver will autoprobe all instances of acceptable DECchips. The
-** cleanup_module() code needs work still....(just to unload modules owned
-** by this driver).
-*/
-static char devicename[9] = { 0, };
-static struct device thisDE4X5 = {
- devicename, /* device name inserted by /linux/drivers/net/net_init.c */
- 0, 0, 0, 0,
- 0, 0, /* I/O address, IRQ */
- 0, 0, 0, NULL, de4x5_probe };
-
-static int io=0x0b; /* EDIT THESE LINES FOR YOUR CONFIGURATION */
+*/
+#define LP(a) ((struct de4x5_private *)(a))
+static struct device *mdev = NULL;
+static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */
int
init_module(void)
{
- struct device *p = (struct device *)&thisDE4X5;
-
- thisDE4X5.base_addr = io; /* Now autoprobe the module */
- thisDE4X5.irq = 0;
-
- for (; p!=NULL; p=p->next) {
+ struct device *p;
+
+ if ((mdev = insert_device(NULL, io, de4x5_probe)) == NULL)
+ return -ENOMEM;
+
+ for (p = mdev; p != NULL; p = LP(p->priv)->next_module) {
if (register_netdev(p) != 0)
return -EIO;
}
- io=0;
+
return 0;
}
void
cleanup_module(void)
{
- struct de4x5_private *lp = (struct de4x5_private *) thisDE4X5.priv;
- struct device *p = (struct device *)&thisDE4X5;
- int keep_loaded = 0;
-
- for (; p!=NULL; p=p->next) {
- keep_loaded += (p->flags & IFF_UP); /* Is an interface up? */
- }
-
- if (keep_loaded) {
- printk("de4x5: Cannot unload modules - %d interface%s%s still active.\n",
- keep_loaded, (keep_loaded>1 ? "s ": " "),
- (keep_loaded>1 ? "are": "is"));
- return;
+ while (mdev != NULL) {
+ mdev = unlink_modules(mdev);
}
-
- for (p=thisDE4X5.next; p!=NULL; p=p->next) {
- if (p->priv) { /* Private area allocated? */
- struct de4x5_private *lp = (struct de4x5_private *)p->priv;
- if (lp->cache.buf) { /* MAC buffers allocated? */
- kfree(lp->cache.buf); /* Free the MAC buffers */
- }
- release_region(p->base_addr, (lp->bus == PCI ?
- DE4X5_PCI_TOTAL_SIZE :
- DE4X5_EISA_TOTAL_SIZE));
- kfree(lp->cache.priv); /* Free the private area */
- }
- unregister_netdev(p);
- kfree(p); /* Free the device structure */
- }
-
- if (thisDE4X5.priv) {
- if (lp->cache.buf) { /* Are MAC buffers allocated */
- kfree(lp->cache.buf);
- }
- release_region(thisDE4X5.base_addr,
- (lp->bus == PCI ?
- DE4X5_PCI_TOTAL_SIZE :
- DE4X5_EISA_TOTAL_SIZE));
- kfree(lp->cache.priv);
- thisDE4X5.priv = NULL;
- }
- unregister_netdev(&thisDE4X5);
return;
}
+
+static struct device *
+unlink_modules(struct device *p)
+{
+ struct device *next = NULL;
+
+ if (p->priv) { /* Private areas allocated? */
+ struct de4x5_private *lp = (struct de4x5_private *)p->priv;
+
+ next = lp->next_module;
+ if (lp->cache.buf) { /* MAC buffers allocated? */
+ kfree(lp->cache.buf); /* Free the MAC buffers */
+ }
+ kfree(lp->cache.priv); /* Free the private area */
+ release_region(p->base_addr, (lp->bus == PCI ?
+ DE4X5_PCI_TOTAL_SIZE :
+ DE4X5_EISA_TOTAL_SIZE));
+ }
+ unregister_netdev(p);
+ kfree(p); /* Free the device structure */
+
+ return next;
+}
+
#endif /* MODULE */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov