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 @@
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) {
......
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