]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] pcnet_cs driver bug fix / update
authorAndrew Morton <akpm@osdl.org>
Tue, 30 Dec 2003 08:59:58 +0000 (00:59 -0800)
committerLinus Torvalds <torvalds@home.osdl.org>
Tue, 30 Dec 2003 08:59:58 +0000 (00:59 -0800)
From: David Hinds <dhinds@sonic.net>

This fixes half/full duplex selection for certain NE2000 compatible PCMCIA
cards.

drivers/net/pcmcia/pcnet_cs.c

index c52f1c16271031d89c21175a578965be8722e0d8..635cead35632cd6b313be394e62d012c2b9e33cd 100644 (file)
@@ -11,7 +11,7 @@
 
     Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
 
-    pcnet_cs.c 1.149 2002/06/29 06:27:37
+    pcnet_cs.c 1.153 2003/11/09 18:53:09
     
     The network driver code is based on Donald Becker's NE2000 code:
 
@@ -74,7 +74,7 @@ static int pc_debug = PCMCIA_DEBUG;
 MODULE_PARM(pc_debug, "i");
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"pcnet_cs.c 1.149 2002/06/29 06:27:37 (David Hinds)";
+"pcnet_cs.c 1.153 2003/11/09 18:53:09 (David Hinds)";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -871,13 +871,15 @@ static int pcnet_event(event_t event, int priority,
 
     MII interface support for DL10019 and DL10022 based cards
 
-    On the DL10019, the MII IO direction bit is 0x10; on  the DL10022
+    On the DL10019, the MII IO direction bit is 0x10; on the DL10022
     it is 0x20.  Setting both bits seems to work on both card types.
 
 ======================================================================*/
 
 #define DLINK_GPIO             0x1c
 #define DLINK_DIAG             0x1d
+#define DLINK_EEPROM           0x1e
+
 #define MDIO_SHIFT_CLK         0x80
 #define MDIO_DATA_OUT          0x40
 #define MDIO_DIR_WRITE         0x30
@@ -940,6 +942,98 @@ static void mdio_reset(ioaddr_t addr, int phy_id)
     outb_p(0x00, addr);
 }
 
+/*======================================================================
+
+    EEPROM access routines for DL10019 and DL10022 based cards
+
+======================================================================*/
+
+#define EE_EEP         0x40
+#define EE_ASIC                0x10
+#define EE_CS          0x08
+#define EE_CK          0x04
+#define EE_DO          0x02
+#define EE_DI          0x01
+#define EE_ADOT                0x01    /* DataOut for ASIC */
+#define EE_READ_CMD    0x06
+
+#define DL19FDUPLX     0x0400  /* DL10019 Full duplex mode */
+
+static int read_eeprom(ioaddr_t ioaddr, int location)
+{
+    int i, retval = 0;
+    ioaddr_t ee_addr = ioaddr + DLINK_EEPROM;
+    int read_cmd = location | (EE_READ_CMD << 8);
+
+    outb(0, ee_addr);
+    outb(EE_EEP|EE_CS, ee_addr);
+
+    /* Shift the read command bits out. */
+    for (i = 10; i >= 0; i--) {
+       short dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
+       outb_p(EE_EEP|EE_CS|dataval, ee_addr);
+       outb_p(EE_EEP|EE_CS|dataval|EE_CK, ee_addr);
+    }
+    outb(EE_EEP|EE_CS, ee_addr);
+
+    for (i = 16; i > 0; i--) {
+       outb_p(EE_EEP|EE_CS | EE_CK, ee_addr);
+       retval = (retval << 1) | ((inb(ee_addr) & EE_DI) ? 1 : 0);
+       outb_p(EE_EEP|EE_CS, ee_addr);
+    }
+
+    /* Terminate the EEPROM access. */
+    outb(0, ee_addr);
+    return retval;
+}
+
+/*
+    The internal ASIC registers can be changed by EEPROM READ access
+    with EE_ASIC bit set.
+    In ASIC mode, EE_ADOT is used to output the data to the ASIC.
+*/
+
+static void write_asic(ioaddr_t ioaddr, int location, short asic_data)
+{
+       int i;
+       ioaddr_t ee_addr = ioaddr + DLINK_EEPROM;
+       short dataval;
+       int read_cmd = location | (EE_READ_CMD << 8);
+
+       asic_data |= read_eeprom(ioaddr, location);
+
+       outb(0, ee_addr);
+       outb(EE_ASIC|EE_CS|EE_DI, ee_addr);
+
+       read_cmd = read_cmd >> 1;
+
+       /* Shift the read command bits out. */
+       for (i = 9; i >= 0; i--) {
+               dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
+               outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
+               outb_p(EE_ASIC|EE_CS|EE_DI|dataval|EE_CK, ee_addr);
+               outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
+       }
+       // sync
+       outb(EE_ASIC|EE_CS, ee_addr);
+       outb(EE_ASIC|EE_CS|EE_CK, ee_addr);
+       outb(EE_ASIC|EE_CS, ee_addr);
+
+       for (i = 15; i >= 0; i--) {
+               dataval = (asic_data & (1 << i)) ? EE_ADOT : 0;
+               outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
+               outb_p(EE_ASIC|EE_CS|dataval|EE_CK, ee_addr);
+               outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
+       }
+
+       /* Terminate the ASIC access. */
+       outb(EE_ASIC|EE_DI, ee_addr);
+       outb(EE_ASIC|EE_DI| EE_CK, ee_addr);
+       outb(EE_ASIC|EE_DI, ee_addr);
+
+       outb(0, ee_addr);
+}
+
 /*====================================================================*/
 
 static void set_misc_reg(struct net_device *dev)
@@ -1154,6 +1248,9 @@ static void ei_watchdog(u_long arg)
        if (link && (info->flags & IS_DL10022)) {
            /* Disable collision detection on full duplex links */
            outb((p & 0x0140) ? 4 : 0, nic_base + DLINK_DIAG);
+       } else if (link && (info->flags & IS_DL10019)) {
+           /* Disable collision detection on full duplex links */
+           write_asic(dev->base_addr, 4, (p & 0x140) ? DL19FDUPLX : 0);
        }
        if (link) {
            if (info->phy_id == info->eth_phy) {