patch-2.4.15 linux/drivers/net/pcmcia/pcnet_cs.c
Next file: linux/drivers/net/pcmcia/smc91c92_cs.c
Previous file: linux/drivers/net/pcmcia/nmclan_cs.c
Back to the patch index
Back to the overall index
- Lines: 291
- Date:
Tue Nov 13 09:02:30 2001
- Orig file:
v2.4.14/linux/drivers/net/pcmcia/pcnet_cs.c
- Orig date:
Tue Oct 9 17:06:52 2001
diff -u --recursive --new-file v2.4.14/linux/drivers/net/pcmcia/pcnet_cs.c linux/drivers/net/pcmcia/pcnet_cs.c
@@ -11,7 +11,7 @@
Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
- pcnet_cs.c 1.132 2001/02/09 03:13:29
+ pcnet_cs.c 1.144 2001/11/07 04:06:56
The network driver code is based on Donald Becker's NE2000 code:
@@ -20,7 +20,7 @@
Director, National Security Agency. This software may be used and
distributed according to the terms of the GNU General Public License,
incorporated herein by reference.
- Donald Becker may be reached at becker@scyld.com.
+ Donald Becker may be reached at becker@scyld.com
Based also on Keith Moore's changes to Don Becker's code, for IBM
CCAE support. Drivers merged back together, and shared-memory
@@ -39,6 +39,7 @@
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/system.h>
+#include <asm/byteorder.h>
#include <linux/netdevice.h>
#include <../drivers/net/8390.h>
@@ -72,14 +73,18 @@
MODULE_PARM(pc_debug, "i");
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version =
-"pcnet_cs.c 1.132 2001/02/09 03:13:29 (David Hinds)";
+"pcnet_cs.c 1.144 2001/11/07 04:06:56 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
/*====================================================================*/
-/* Parameters that can be set with 'insmod' */
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("NE2000 compatible PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
@@ -94,6 +99,7 @@
INT_MODULE_PARM(delay_output, 0); /* pause after xmit? */
INT_MODULE_PARM(delay_time, 4); /* in usec */
INT_MODULE_PARM(use_shmem, -1); /* use shared memory? */
+INT_MODULE_PARM(full_duplex, 0); /* full duplex? */
/* Ugh! Let the user hardwire the hardware address for queer cards */
static int hw_addr[6] = { 0, /* ... */ };
@@ -101,13 +107,14 @@
/*====================================================================*/
+static void mii_phy_probe(struct net_device *dev);
static void pcnet_config(dev_link_t *link);
static void pcnet_release(u_long arg);
static int pcnet_event(event_t event, int priority,
event_callback_args_t *args);
static int pcnet_open(struct net_device *dev);
static int pcnet_close(struct net_device *dev);
-static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs);
static void ei_watchdog(u_long arg);
static void pcnet_reset_8390(struct net_device *dev);
@@ -140,6 +147,12 @@
#define HAS_MII 0x40
#define USE_SHMEM 0x80 /* autodetected */
+#define AM79C9XX_HOME_PHY 0x00006B90 /* HomePNA PHY */
+#define AM79C9XX_ETH_PHY 0x00006B70 /* 10baseT PHY */
+#define MII_PHYID_REV_MASK 0xfffffff0
+#define MII_PHYID_REG1 0x02
+#define MII_PHYID_REG2 0x03
+
static hw_info_t hw_info[] = {
{ /* Accton EN2212 */ 0x0ff0, 0x00, 0x00, 0xe8, DELAY_OUTPUT },
{ /* Allied Telesis LA-PCM */ 0x0ff0, 0x00, 0x00, 0xf4, 0 },
@@ -216,7 +229,10 @@
caddr_t base;
struct timer_list watchdog;
int stale, fast_poll;
+ u_char phy_id;
+ u_char eth_phy, pna_phy;
u_short link_status;
+ u_long mii_reset;
} pcnet_dev_t;
/*======================================================================
@@ -518,8 +534,8 @@
dev->dev_addr[i] = j & 0xff;
dev->dev_addr[i+1] = j >> 8;
}
- printk(KERN_INFO "pcnet_cs: sorry, the AX88190 chipset is not "
- "supported.\n");
+ printk(KERN_NOTICE "pcnet_cs: this is an AX88190 card!\n");
+ printk(KERN_NOTICE "pcnet_cs: use axnet_cs instead.\n");
return NULL;
}
@@ -749,10 +765,13 @@
link->state &= ~DEV_CONFIG_PENDING;
if (info->flags & (IS_DL10019|IS_DL10022)) {
- dev->do_ioctl = &do_ioctl;
+ u_char id = inb(dev->base_addr + 0x1a);
+ dev->do_ioctl = &ei_ioctl;
+ mii_phy_probe(dev);
printk(KERN_INFO "%s: NE2000 (DL100%d rev %02x): ",
- dev->name, ((info->flags & IS_DL10022) ? 22 : 19),
- inb(dev->base_addr + 0x1a));
+ dev->name, ((info->flags & IS_DL10022) ? 22 : 19), id);
+ if (info->pna_phy)
+ printk("PNA, ");
} else
printk(KERN_INFO "%s: NE2000 Compatible: ", dev->name);
printk("io %#3lx, irq %d,", dev->base_addr, dev->irq);
@@ -954,15 +973,44 @@
outb_p(tmp, nic_base + PCNET_MISC);
}
if (info->flags & IS_DL10022) {
- mdio_reset(nic_base + DLINK_GPIO, 0);
- /* Restart MII autonegotiation */
- mdio_write(nic_base + DLINK_GPIO, 0, 0, 0x0000);
- mdio_write(nic_base + DLINK_GPIO, 0, 0, 0x1200);
+ if (info->flags & HAS_MII) {
+ mdio_reset(nic_base + DLINK_GPIO, info->eth_phy);
+ /* Restart MII autonegotiation */
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000);
+ mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200);
+ info->mii_reset = jiffies;
+ } else {
+ outb(full_duplex ? 4 : 0, nic_base + DLINK_DIAG);
+ }
}
}
/*====================================================================*/
+static void mii_phy_probe(struct net_device *dev)
+{
+ pcnet_dev_t *info = (pcnet_dev_t *)dev;
+ ioaddr_t mii_addr = dev->base_addr + DLINK_GPIO;
+ int i;
+ u_int tmp, phyid;
+
+ for (i = 31; i >= 0; i--) {
+ tmp = mdio_read(mii_addr, i, 1);
+ if ((tmp == 0) || (tmp == 0xffff))
+ continue;
+ tmp = mdio_read(mii_addr, i, MII_PHYID_REG1);
+ phyid = tmp << 16;
+ phyid |= mdio_read(mii_addr, i, MII_PHYID_REG2);
+ phyid &= MII_PHYID_REV_MASK;
+ DEBUG(0, "%s: MII at %d is 0x%08x\n", dev->name, i, phyid);
+ if (phyid == AM79C9XX_HOME_PHY) {
+ info->pna_phy = i;
+ } else if (phyid != AM79C9XX_ETH_PHY) {
+ info->eth_phy = i;
+ }
+ }
+}
+
static int pcnet_open(struct net_device *dev)
{
pcnet_dev_t *info = (pcnet_dev_t *)dev;
@@ -979,6 +1027,7 @@
set_misc_reg(dev);
request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info, dev);
+ info->phy_id = info->eth_phy;
info->link_status = 0x00;
info->watchdog.function = &ei_watchdog;
info->watchdog.data = (u_long)info;
@@ -1074,6 +1123,7 @@
pcnet_dev_t *info = (pcnet_dev_t *)(arg);
struct net_device *dev = &info->dev;
ioaddr_t nic_base = dev->base_addr;
+ ioaddr_t mii_addr = nic_base + DLINK_GPIO;
u_short link;
if (!netif_device_present(dev)) goto reschedule;
@@ -1097,35 +1147,57 @@
if (!(info->flags & HAS_MII))
goto reschedule;
- link = mdio_read(dev->base_addr + DLINK_GPIO, 0, 1);
+ mdio_read(mii_addr, info->phy_id, 1);
+ link = mdio_read(mii_addr, info->phy_id, 1);
if (!link || (link == 0xffff)) {
- printk(KERN_INFO "%s: MII is missing!\n", dev->name);
- info->flags &= ~HAS_MII;
+ if (info->eth_phy) {
+ info->phy_id = info->eth_phy = 0;
+ } else {
+ printk(KERN_INFO "%s: MII is missing!\n", dev->name);
+ info->flags &= ~HAS_MII;
+ }
goto reschedule;
}
link &= 0x0004;
if (link != info->link_status) {
- u_short p = mdio_read(dev->base_addr + DLINK_GPIO, 0, 5);
+ u_short p = mdio_read(mii_addr, info->phy_id, 5);
printk(KERN_INFO "%s: %s link beat\n", dev->name,
(link) ? "found" : "lost");
if (link && (info->flags & IS_DL10022)) {
/* Disable collision detection on full duplex links */
- outb((p & 0x0140) ? 4 : 0, dev->base_addr + DLINK_DIAG);
+ outb((p & 0x0140) ? 4 : 0, nic_base + DLINK_DIAG);
}
if (link) {
- if (p)
- printk(KERN_INFO "%s: autonegotiation complete: "
- "%sbaseT-%cD selected\n", dev->name,
- ((p & 0x0180) ? "100" : "10"),
- ((p & 0x0140) ? 'F' : 'H'));
- else
- printk(KERN_INFO "%s: link partner did not autonegotiate\n",
- dev->name);
+ if (info->phy_id == info->eth_phy) {
+ if (p)
+ printk(KERN_INFO "%s: autonegotiation complete: "
+ "%sbaseT-%cD selected\n", dev->name,
+ ((p & 0x0180) ? "100" : "10"),
+ ((p & 0x0140) ? 'F' : 'H'));
+ else
+ printk(KERN_INFO "%s: link partner did not "
+ "autonegotiate\n", dev->name);
+ }
NS8390_init(dev, 1);
}
info->link_status = link;
}
+ if (info->pna_phy && (jiffies - info->mii_reset > 6*HZ)) {
+ link = mdio_read(mii_addr, info->eth_phy, 1) & 0x0004;
+ if (((info->phy_id == info->pna_phy) && link) ||
+ ((info->phy_id != info->pna_phy) && !link)) {
+ /* isolate this MII and try flipping to the other one */
+ mdio_write(mii_addr, info->phy_id, 0, 0x0400);
+ info->phy_id ^= info->pna_phy ^ info->eth_phy;
+ printk(KERN_INFO "%s: switched to %s transceiver\n", dev->name,
+ (info->phy_id == info->eth_phy) ? "ethernet" : "PNA");
+ mdio_write(mii_addr, info->phy_id, 0,
+ (info->phy_id == info->eth_phy) ? 0x1000 : 0);
+ info->link_status = 0;
+ info->mii_reset = jiffies;
+ }
+ }
reschedule:
info->watchdog.expires = jiffies + HZ;
@@ -1134,20 +1206,21 @@
/*====================================================================*/
-static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
+ pcnet_dev_t *info = (pcnet_dev_t *)dev;
u16 *data = (u16 *)&rq->ifr_data;
- ioaddr_t addr = dev->base_addr + DLINK_GPIO;
+ ioaddr_t mii_addr = dev->base_addr + DLINK_GPIO;
switch (cmd) {
case SIOCDEVPRIVATE:
- data[0] = 0;
+ data[0] = info->phy_id;
case SIOCDEVPRIVATE+1:
- data[3] = mdio_read(addr, data[0], data[1] & 0x1f);
+ data[3] = mdio_read(mii_addr, data[0], data[1] & 0x1f);
return 0;
case SIOCDEVPRIVATE+2:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- mdio_write(addr, data[0], data[1] & 0x1f, data[2]);
+ mdio_write(mii_addr, data[0], data[1] & 0x1f, data[2]);
return 0;
}
return -EOPNOTSUPP;
@@ -1522,4 +1595,3 @@
module_init(init_pcnet_cs);
module_exit(exit_pcnet_cs);
-MODULE_LICENSE("GPL");
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)