Commit 9a639403 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] pcnet_cs driver bug fix / update

From: David Hinds <dhinds@sonic.net>

This fixes half/full duplex selection for certain NE2000 compatible PCMCIA
cards.
parent 59326357
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net 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: The network driver code is based on Donald Becker's NE2000 code:
...@@ -74,7 +74,7 @@ static int pc_debug = PCMCIA_DEBUG; ...@@ -74,7 +74,7 @@ static int pc_debug = PCMCIA_DEBUG;
MODULE_PARM(pc_debug, "i"); MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version = 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 #else
#define DEBUG(n, args...) #define DEBUG(n, args...)
#endif #endif
...@@ -871,13 +871,15 @@ static int pcnet_event(event_t event, int priority, ...@@ -871,13 +871,15 @@ static int pcnet_event(event_t event, int priority,
MII interface support for DL10019 and DL10022 based cards 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. it is 0x20. Setting both bits seems to work on both card types.
======================================================================*/ ======================================================================*/
#define DLINK_GPIO 0x1c #define DLINK_GPIO 0x1c
#define DLINK_DIAG 0x1d #define DLINK_DIAG 0x1d
#define DLINK_EEPROM 0x1e
#define MDIO_SHIFT_CLK 0x80 #define MDIO_SHIFT_CLK 0x80
#define MDIO_DATA_OUT 0x40 #define MDIO_DATA_OUT 0x40
#define MDIO_DIR_WRITE 0x30 #define MDIO_DIR_WRITE 0x30
...@@ -940,6 +942,98 @@ static void mdio_reset(ioaddr_t addr, int phy_id) ...@@ -940,6 +942,98 @@ static void mdio_reset(ioaddr_t addr, int phy_id)
outb_p(0x00, addr); 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) static void set_misc_reg(struct net_device *dev)
...@@ -1154,6 +1248,9 @@ static void ei_watchdog(u_long arg) ...@@ -1154,6 +1248,9 @@ static void ei_watchdog(u_long arg)
if (link && (info->flags & IS_DL10022)) { if (link && (info->flags & IS_DL10022)) {
/* Disable collision detection on full duplex links */ /* Disable collision detection on full duplex links */
outb((p & 0x0140) ? 4 : 0, nic_base + DLINK_DIAG); 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 (link) {
if (info->phy_id == info->eth_phy) { if (info->phy_id == info->eth_phy) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment