Commit 4546ef0b authored by David Hinds's avatar David Hinds Committed by Linus Torvalds

[PATCH] PATCH: PCMCIA network driver update

This brings several PCMCIA network drivers into sync with 2.4 and the
pcmcia-cs package.  The axnet_cs driver gets a major cleanup.
parent a2ad211e
/* Generic NS8390 register definitions. */
/* This file is part of Donald Becker's 8390 drivers, and is distributed
under the same license. Auto-loading of 8390.o only in v2.2 - Paul G.
Some of these names and comments originated from the Crynwr
packet drivers, which are distributed under the GPL. */
#ifndef _8390_h
#define _8390_h
#include <linux/config.h>
#include <linux/if_ether.h>
#include <linux/ioport.h>
#include <linux/skbuff.h>
#define TX_2X_PAGES 12
#define TX_1X_PAGES 6
/* Should always use two Tx slots to get back-to-back transmits. */
#define EI_PINGPONG
#ifdef EI_PINGPONG
#define TX_PAGES TX_2X_PAGES
#else
#define TX_PAGES TX_1X_PAGES
#endif
#define ETHER_ADDR_LEN 6
/* The 8390 specific per-packet-header format. */
struct e8390_pkt_hdr {
unsigned char status; /* status */
unsigned char next; /* pointer to next packet. */
unsigned short count; /* header + packet length in bytes */
};
#ifdef notdef
extern int ei_debug;
#else
#define ei_debug 1
#endif
static int ethdev_init(struct net_device *dev);
static void NS8390_init(struct net_device *dev, int startp);
static int ei_open(struct net_device *dev);
static int ei_close(struct net_device *dev);
static void ei_interrupt(int irq, void *dev_id, struct pt_regs *regs);
/* Most of these entries should be in 'struct net_device' (or most of the
things in there should be here!) */
/* You have one of these per-board */
struct ei_device {
const char *name;
void (*reset_8390)(struct net_device *);
void (*get_8390_hdr)(struct net_device *, struct e8390_pkt_hdr *, int);
void (*block_output)(struct net_device *, int, const unsigned char *, int);
void (*block_input)(struct net_device *, int, struct sk_buff *, int);
unsigned char mcfilter[8];
unsigned open:1;
unsigned word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
unsigned txing:1; /* Transmit Active */
unsigned irqlock:1; /* 8390's intrs disabled when '1'. */
unsigned dmaing:1; /* Remote DMA Active */
unsigned char tx_start_page, rx_start_page, stop_page;
unsigned char current_page; /* Read pointer in buffer */
unsigned char interface_num; /* Net port (AUI, 10bT.) to use. */
unsigned char txqueue; /* Tx Packet buffer queue length. */
short tx1, tx2; /* Packet lengths for ping-pong tx. */
short lasttx; /* Alpha version consistency check. */
unsigned char reg0; /* Register '0' in a WD8013 */
unsigned char reg5; /* Register '5' in a WD8013 */
unsigned char saved_irq; /* Original dev->irq value. */
struct net_device_stats stat; /* The new statistics table. */
u32 *reg_offset; /* Register mapping table */
spinlock_t page_lock; /* Page register locks */
unsigned long priv; /* Private field to store bus IDs etc. */
};
/* The maximum number of 8390 interrupt service routines called per IRQ. */
#define MAX_SERVICE 12
/* The maximum time waited (in jiffies) before assuming a Tx failed. (20ms) */
#define TX_TIMEOUT (20*HZ/100)
#define ei_status (*(struct ei_device *)(dev->priv))
/* Some generic ethernet register configurations. */
#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
#define E8390_RX_IRQ_MASK 0x5
#define E8390_RXCONFIG 0x44 /* EN0_RXCR: broadcasts, no multicast,errors */
#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
/* Register accessed at EN_CMD, the 8390 base addr. */
#define E8390_STOP 0x01 /* Stop and reset the chip */
#define E8390_START 0x02 /* Start the chip, clear reset */
#define E8390_TRANS 0x04 /* Transmit a frame */
#define E8390_RREAD 0x08 /* Remote read */
#define E8390_RWRITE 0x10 /* Remote write */
#define E8390_NODMA 0x20 /* Remote DMA */
#define E8390_PAGE0 0x00 /* Select page chip registers */
#define E8390_PAGE1 0x40 /* using the two high-order bits */
#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
/*
* Only generate indirect loads given a machine that needs them.
*/
#if defined(CONFIG_MAC) || defined(CONFIG_AMIGA_PCMCIA) || \
defined(CONFIG_ARIADNE2) || defined(CONFIG_ARIADNE2_MODULE) || \
defined(CONFIG_HYDRA) || defined(CONFIG_HYDRA_MODULE)
#define EI_SHIFT(x) (ei_local->reg_offset[x])
#else
#define EI_SHIFT(x) (x)
#endif
#define E8390_CMD EI_SHIFT(0x00) /* The command register (for all pages) */
/* Page 0 register offsets. */
#define EN0_CLDALO EI_SHIFT(0x01) /* Low byte of current local dma addr RD */
#define EN0_STARTPG EI_SHIFT(0x01) /* Starting page of ring bfr WR */
#define EN0_CLDAHI EI_SHIFT(0x02) /* High byte of current local dma addr RD */
#define EN0_STOPPG EI_SHIFT(0x02) /* Ending page +1 of ring bfr WR */
#define EN0_BOUNDARY EI_SHIFT(0x03) /* Boundary page of ring bfr RD WR */
#define EN0_TSR EI_SHIFT(0x04) /* Transmit status reg RD */
#define EN0_TPSR EI_SHIFT(0x04) /* Transmit starting page WR */
#define EN0_NCR EI_SHIFT(0x05) /* Number of collision reg RD */
#define EN0_TCNTLO EI_SHIFT(0x05) /* Low byte of tx byte count WR */
#define EN0_FIFO EI_SHIFT(0x06) /* FIFO RD */
#define EN0_TCNTHI EI_SHIFT(0x06) /* High byte of tx byte count WR */
#define EN0_ISR EI_SHIFT(0x07) /* Interrupt status reg RD WR */
#define EN0_CRDALO EI_SHIFT(0x08) /* low byte of current remote dma address RD */
#define EN0_RSARLO EI_SHIFT(0x08) /* Remote start address reg 0 */
#define EN0_CRDAHI EI_SHIFT(0x09) /* high byte, current remote dma address RD */
#define EN0_RSARHI EI_SHIFT(0x09) /* Remote start address reg 1 */
#define EN0_RCNTLO EI_SHIFT(0x0a) /* Remote byte count reg WR */
#define EN0_RCNTHI EI_SHIFT(0x0b) /* Remote byte count reg WR */
#define EN0_RSR EI_SHIFT(0x0c) /* rx status reg RD */
#define EN0_RXCR EI_SHIFT(0x0c) /* RX configuration reg WR */
#define EN0_TXCR EI_SHIFT(0x0d) /* TX configuration reg WR */
#define EN0_COUNTER0 EI_SHIFT(0x0d) /* Rcv alignment error counter RD */
#define EN0_DCFG EI_SHIFT(0x0e) /* Data configuration reg WR */
#define EN0_COUNTER1 EI_SHIFT(0x0e) /* Rcv CRC error counter RD */
#define EN0_IMR EI_SHIFT(0x0f) /* Interrupt mask reg WR */
#define EN0_COUNTER2 EI_SHIFT(0x0f) /* Rcv missed frame error counter RD */
/* Bits in EN0_ISR - Interrupt status register */
#define ENISR_RX 0x01 /* Receiver, no error */
#define ENISR_TX 0x02 /* Transmitter, no error */
#define ENISR_RX_ERR 0x04 /* Receiver, with error */
#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
#define ENISR_COUNTERS 0x20 /* Counters need emptying */
#define ENISR_RDC 0x40 /* remote dma complete */
#define ENISR_RESET 0x80 /* Reset completed */
#define ENISR_ALL 0x3f /* Interrupts we will enable */
/* Bits in EN0_DCFG - Data config register */
#define ENDCFG_WTS 0x01 /* word transfer mode selection */
/* Page 1 register offsets. */
#define EN1_PHYS EI_SHIFT(0x01) /* This board's physical enet addr RD WR */
#define EN1_PHYS_SHIFT(i) EI_SHIFT(i+1) /* Get and set mac address */
#define EN1_CURPAG EI_SHIFT(0x07) /* Current memory page RD WR */
#define EN1_MULT EI_SHIFT(0x08) /* Multicast filter mask array (8 bytes) RD WR */
#define EN1_MULT_SHIFT(i) EI_SHIFT(8+i) /* Get and set multicast filter */
/* Bits in received packet status byte and EN0_RSR*/
#define ENRSR_RXOK 0x01 /* Received a good packet */
#define ENRSR_CRC 0x02 /* CRC error */
#define ENRSR_FAE 0x04 /* frame alignment error */
#define ENRSR_FO 0x08 /* FIFO overrun */
#define ENRSR_MPA 0x10 /* missed pkt */
#define ENRSR_PHY 0x20 /* physical/multicast address */
#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
#define ENRSR_DEF 0x80 /* deferring */
/* Transmitted packet status, EN0_TSR. */
#define ENTSR_PTX 0x01 /* Packet transmitted without error */
#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */
#define ENTSR_COL 0x04 /* The transmit collided at least once. */
#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */
#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */
#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */
#endif /* _8390_h */
......@@ -11,8 +11,8 @@
Copyright (C) 2001 David A. Hinds -- dahinds@users.sourceforge.net
axnet_cs.c 1.11 2001/06/12 12:42:40
axnet_cs.c 1.28 2002/06/29 06:27:37
The network driver code is based on Donald Becker's NE2000 code:
Written 1992,1993 by Donald Becker.
......@@ -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@cesdis1.gsfc.nasa.gov
Donald Becker may be reached at becker@scyld.com
======================================================================*/
......@@ -33,14 +33,15 @@
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/ethtool.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
#include <linux/netdevice.h>
#include "ax8390.h"
#include "../8390.h"
#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
......@@ -53,34 +54,25 @@
#define AXNET_CMD 0x00
#define AXNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */
#define AXNET_RESET 0x1f /* Issue a read to reset, a write to clear. */
#define AXNET_MISC 0x18 /* For IBM CCAE and Socket EA cards */
#define AXNET_MII_EEP 0x14 /* Offset of MII access port */
#define AXNET_TEST 0x15 /* Offset of TEST Register port */
#define AXNET_GPIO 0x17 /* Offset of General Purpose Register Port */
#define AXNET_START_PG 0x40 /* First page of TX buffer */
#define AXNET_STOP_PG 0x80 /* Last page +1 of RX ring */
#define AXNET_RDC_TIMEOUT 0x02 /* Max wait in jiffies for Tx RDC */
#ifdef PCMCIA_DEBUG
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 =
"axnet_cs.c 1.11 2001/06/12 12:42:40 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
#define DEV_KFREE_SKB(skb) dev_kfree_skb(skb);
#define skb_tx_check(dev, skb)
#define add_rx_bytes(stats, n) (stats)->rx_bytes += n;
#define add_tx_bytes(stats, n) (stats)->tx_bytes += n;
#define netif_mark_up(dev) do { } while (0)
#define netif_mark_down(dev) do { } while (0)
#define IS_AX88190 0x0001
#define IS_AX88790 0x0002
/*====================================================================*/
/* Parameters that can be set with 'insmod' */
/* Module parameters */
MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
MODULE_DESCRIPTION("Asix AX88190 PCMCIA ethernet driver");
MODULE_LICENSE("GPL");
#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
......@@ -89,9 +81,14 @@ INT_MODULE_PARM(irq_mask, 0xdeb8);
static int irq_list[4] = { -1 };
MODULE_PARM(irq_list, "1-4i");
/* Ugh! Let the user hardwire the hardware address for queer cards */
static int hw_addr[6] = { 0, /* ... */ };
MODULE_PARM(hw_addr, "6i");
#ifdef PCMCIA_DEBUG
INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
static char *version =
"axnet_cs.c 1.28 2002/06/29 06:27:37 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
/*====================================================================*/
......@@ -122,6 +119,12 @@ static void axnet_detach(dev_link_t *);
static dev_info_t dev_info = "axnet_cs";
static dev_link_t *dev_list;
static int axdev_init(struct net_device *dev);
static void AX88190_init(struct net_device *dev, int startp);
static int ax_open(struct net_device *dev);
static int ax_close(struct net_device *dev);
static void ax_interrupt(int irq, void *dev_id, struct pt_regs *regs);
/*====================================================================*/
typedef struct axnet_dev_t {
......@@ -134,6 +137,7 @@ typedef struct axnet_dev_t {
u_short link_status;
u_char duplex_flag;
int phy_id;
int flags;
} axnet_dev_t;
/*======================================================================
......@@ -212,7 +216,7 @@ static dev_link_t *axnet_attach(void)
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
ethdev_init(dev);
axdev_init(dev);
dev->init = &axnet_init;
dev->open = &axnet_open;
dev->stop = &axnet_close;
......@@ -304,7 +308,7 @@ static int get_prom(dev_link_t *link)
{0x00, EN0_RCNTHI},
{0x00, EN0_IMR}, /* Mask completion irq. */
{0xFF, EN0_ISR},
{E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
{E8390_RXOFF|0x40, EN0_RXCR}, /* 0x60 Set to monitor */
{E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
{0x10, EN0_RCNTLO},
{0x00, EN0_RCNTHI},
......@@ -331,30 +335,6 @@ static int get_prom(dev_link_t *link)
return 1;
} /* get_prom */
/*======================================================================
This should be totally unnecessary... but when we can't figure
out the hardware address any other way, we'll let the user hard
wire it when the module is initialized.
======================================================================*/
static int get_hwired(dev_link_t *link)
{
struct net_device *dev = link->priv;
int i;
for (i = 0; i < 6; i++)
if (hw_addr[i] != 0) break;
if (i == 6)
return 0;
for (i = 0; i < 6; i++)
dev->dev_addr[i] = hw_addr[i];
return 1;
} /* get_hwired */
/*======================================================================
axnet_config() is scheduled to run after a CARD_INSERTION event
......@@ -421,7 +401,8 @@ static void axnet_config(dev_link_t *link)
CS_CHECK(GetTupleData, handle, &tuple);
CS_CHECK(ParseTuple, handle, &tuple, &parse);
link->conf.ConfigBase = parse.config.base;
link->conf.Present = parse.config.rmask[0];
/* don't trust the CIS on this; Linksys got it wrong */
link->conf.Present = 0x63;
/* Configure card */
link->state |= DEV_CONFIG;
......@@ -442,7 +423,7 @@ static void axnet_config(dev_link_t *link)
if ((cfg->index == 0) || (cfg->io.nwin == 0))
goto next_entry;
link->conf.ConfigIndex = cfg->index;
link->conf.ConfigIndex = 0x05;
/* For multifunction cards, by convention, we configure the
network function with window 0, and serial with window 1 */
if (io->nwin > 1) {
......@@ -482,9 +463,9 @@ static void axnet_config(dev_link_t *link)
goto failed;
}
if (!get_prom(link) && !get_hwired(link)) {
printk(KERN_NOTICE "axnet_cs: unable to read hardware net"
" address for io base %#3lx\n", dev->base_addr);
if (!get_prom(link)) {
printk(KERN_NOTICE "axnet_cs: this is not an AX88190 card!\n");
printk(KERN_NOTICE "axnet_cs: use pcnet_cs instead.\n");
unregister_netdev(dev);
goto failed;
}
......@@ -501,17 +482,37 @@ static void axnet_config(dev_link_t *link)
strcpy(info->node.dev_name, dev->name);
link->dev = &info->node;
link->state &= ~DEV_CONFIG_PENDING;
printk(KERN_INFO "%s: Asix AX88190: io %#3lx, irq %d, hw_addr ",
dev->name, dev->base_addr, dev->irq);
if (inb(dev->base_addr + AXNET_TEST) != 0)
info->flags |= IS_AX88790;
else
info->flags |= IS_AX88190;
printk(KERN_INFO "%s: Asix AX88%d90: io %#3lx, irq %d, hw_addr ",
dev->name, ((info->flags & IS_AX88790) ? 7 : 1),
dev->base_addr, dev->irq);
for (i = 0; i < 6; i++)
printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
if (info->flags & IS_AX88790)
outb(0x10, dev->base_addr + AXNET_GPIO); /* select Internal PHY */
for (i = 0; i < 32; i++) {
j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1);
if ((j != 0) && (j != 0xffff)) break;
}
/* Maybe PHY is in power down mode. (PPD_SET = 1)
Bit 2 of CCSR is active low. */
if (i == 32) {
conf_reg_t reg = { 0, CS_WRITE, CISREG_CCSR, 0x04 };
CardServices(AccessConfigurationRegister, link->handle, &reg);
for (i = 0; i < 32; i++) {
j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1);
if ((j != 0) && (j != 0xffff)) break;
}
}
info->phy_id = (i < 32) ? i : -1;
if (i < 32) {
DEBUG(0, " MII transceiver at index %d, status %x.\n", i, j);
......@@ -519,12 +520,14 @@ static void axnet_config(dev_link_t *link)
printk(KERN_NOTICE " No MII transceivers found!\n");
}
link->state &= ~DEV_CONFIG_PENDING;
return;
cs_failed:
cs_error(link->handle, last_fn, last_ret);
failed:
axnet_release((u_long)link);
link->state &= ~DEV_CONFIG_PENDING;
return;
} /* axnet_config */
......@@ -544,7 +547,7 @@ static void axnet_release(u_long arg)
if (link->open) {
DEBUG(1, "axnet_cs: release postponed, '%s' still open\n",
info->node.dev_name);
((axnet_dev_t *)(link->priv))->node.dev_name);
link->state |= DEV_STALE_CONFIG;
return;
}
......@@ -583,7 +586,7 @@ static int axnet_event(event_t event, int priority,
}
break;
case CS_EVENT_CARD_INSERTION:
link->state |= DEV_PRESENT;
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
axnet_config(link);
break;
case CS_EVENT_PM_SUSPEND:
......@@ -604,7 +607,7 @@ static int axnet_event(event_t event, int priority,
CardServices(RequestConfiguration, link->handle, &link->conf);
if (link->open) {
axnet_reset_8390(&info->dev);
NS8390_init(&info->dev, 1);
AX88190_init(&info->dev, 1);
netif_device_attach(&info->dev);
}
}
......@@ -694,7 +697,7 @@ static int axnet_open(struct net_device *dev)
info->watchdog.expires = jiffies + HZ;
add_timer(&info->watchdog);
return ei_open(dev);
return ax_open(dev);
} /* axnet_open */
/*====================================================================*/
......@@ -706,11 +709,11 @@ static int axnet_close(struct net_device *dev)
DEBUG(2, "axnet_close('%s')\n", dev->name);
ax_close(dev);
free_irq(dev->irq, dev);
link->open--;
netif_stop_queue(dev);
netif_mark_down(dev);
del_timer(&info->watchdog);
if (link->state & DEV_STALE_CONFIG)
mod_timer(&link->release, jiffies + HZ/20);
......@@ -757,7 +760,7 @@ static void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs)
{
axnet_dev_t *info = dev_id;
info->stale = 0;
ei_interrupt(irq, dev_id, regs);
ax_interrupt(irq, dev_id, regs);
}
static void ei_watchdog(u_long arg)
......@@ -809,7 +812,7 @@ static void ei_watchdog(u_long arg)
else
printk(KERN_INFO "%s: link partner did not autonegotiate\n",
dev->name);
NS8390_init(dev, 1);
AX88190_init(dev, 1);
}
info->link_status = link;
}
......@@ -821,23 +824,22 @@ static void ei_watchdog(u_long arg)
static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr)
{
u32 ethcmd;
if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
return -EFAULT;
switch (ethcmd) {
case ETHTOOL_GDRVINFO: {
struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
strncpy(info.driver, "axnet_cs", sizeof(info.driver)-1);
if (copy_to_user(useraddr, &info, sizeof(info)))
return -EFAULT;
return 0;
}
}
return -EOPNOTSUPP;
u32 ethcmd;
if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
return -EFAULT;
switch (ethcmd) {
case ETHTOOL_GDRVINFO: {
struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
strncpy(info.driver, "axnet_cs", sizeof(info.driver)-1);
if (copy_to_user(useraddr, &info, sizeof(info)))
return -EFAULT;
return 0;
}
}
return -EOPNOTSUPP;
}
/*====================================================================*/
......@@ -849,7 +851,7 @@ static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
ioaddr_t mii_addr = dev->base_addr + AXNET_MII_EEP;
switch (cmd) {
case SIOCETHTOOL:
return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
case SIOCDEVPRIVATE:
data[0] = info->phy_id;
case SIOCDEVPRIVATE+1:
......@@ -940,7 +942,7 @@ static int __init init_axnet_cs(void)
if (serv.Revision != CS_RELEASE_CODE) {
printk(KERN_NOTICE "axnet_cs: Card Services release "
"does not match!\n");
return -1;
return -EINVAL;
}
register_pccard_driver(&dev_info, &axnet_attach, &axnet_detach);
return 0;
......@@ -969,10 +971,11 @@ module_exit(exit_axnet_cs);
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
Center of Excellence in Space Data and Information Sciences
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
The author may be reached as becker@scyld.com, or C/O
Scyld Computing Corporation
410 Severn Ave., Suite 210
Annapolis MD 21403
This is the chip-specific code for many 8390-based ethernet adaptors.
This is not a complete driver, it must be combined with board-specific
code such as ne.c, wd.c, 3c503.c, etc.
......@@ -982,7 +985,6 @@ module_exit(exit_axnet_cs);
a simple innocent change. Please contact me or Donald if you think
you have found something that needs changing. -- PG
Changelog:
Paul Gortmaker : remove set_bit lock, other cleanups.
......@@ -1006,8 +1008,8 @@ module_exit(exit_axnet_cs);
*/
static const char *version =
"8390.c:v1.10cvs 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
static const char *version_8390 =
"8390.c:v1.10cvs 9/23/94 Donald Becker (becker@scyld.com)\n";
#include <asm/uaccess.h>
#include <asm/bitops.h>
......@@ -1087,25 +1089,23 @@ static void do_set_multicast_list(struct net_device *dev);
* them.
*/
/**
* ei_open - Open/initialize the board.
* ax_open - Open/initialize the board.
* @dev: network device to initialize
*
* This routine goes all-out, setting everything
* up anew at each open, even though many of these registers should only
* need to be set once at boot.
*/
static int ei_open(struct net_device *dev)
static int ax_open(struct net_device *dev)
{
unsigned long flags;
struct ei_device *ei_local = (struct ei_device *) dev->priv;
/* This can't happen unless somebody forgot to call ethdev_init(). */
/* This can't happen unless somebody forgot to call axdev_init(). */
if (ei_local == NULL)
{
printk(KERN_EMERG "%s: ei_open passed a non-existent device!\n", dev->name);
printk(KERN_EMERG "%s: ax_open passed a non-existent device!\n", dev->name);
return -ENXIO;
}
......@@ -1124,33 +1124,34 @@ static int ei_open(struct net_device *dev)
*/
spin_lock_irqsave(&ei_local->page_lock, flags);
NS8390_init(dev, 1);
AX88190_init(dev, 1);
/* Set the flag before we drop the lock, That way the IRQ arrives
after its set and we get no silly warnings */
netif_mark_up(dev);
netif_start_queue(dev);
spin_unlock_irqrestore(&ei_local->page_lock, flags);
ei_local->irqlock = 0;
return 0;
}
#define dev_lock(dev) (((struct ei_device *)(dev)->priv)->page_lock)
/**
* ei_close - shut down network device
* ax_close - shut down network device
* @dev: network device to close
*
* Opposite of ei_open(). Only used when "ifconfig <devname> down" is done.
* Opposite of ax_open(). Only used when "ifconfig <devname> down" is done.
*/
static int ei_close(struct net_device *dev)
int ax_close(struct net_device *dev)
{
unsigned long flags;
/*
* Hold the page lock during close
* Hold the page lock during close
*/
spin_lock_irqsave(&((struct ei_device *)dev->priv)->page_lock, flags);
NS8390_init(dev, 0);
spin_unlock_irqrestore(&((struct ei_device *)dev->priv)->page_lock, flags);
spin_lock_irqsave(&dev_lock(dev), flags);
AX88190_init(dev, 0);
spin_unlock_irqrestore(&dev_lock(dev), flags);
netif_stop_queue(dev);
return 0;
}
......@@ -1194,7 +1195,7 @@ void ei_tx_timeout(struct net_device *dev)
/* Try to restart the card. Perhaps the user has fixed something. */
ei_reset_8390(dev);
NS8390_init(dev, 1);
AX88190_init(dev, 1);
spin_unlock(&ei_local->page_lock);
enable_irq(dev->irq);
......@@ -1217,7 +1218,6 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
unsigned long flags;
netif_stop_queue(dev);
skb_tx_check(dev, skb);
length = skb->len;
......@@ -1230,7 +1230,6 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
outb_p(0x00, e8390_base + EN0_IMR);
spin_unlock_irqrestore(&ei_local->page_lock, flags);
/*
* Slow phase with lock held.
*/
......@@ -1243,8 +1242,6 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
#ifdef EI_PINGPONG
/*
* We have two Tx slots available for use. Find the first free
* slot, and then perform some sanity checks. With two Tx bufs,
......@@ -1313,22 +1310,6 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
else
netif_start_queue(dev);
#else /* EI_PINGPONG */
/*
* Only one Tx buffer in use. You need two Tx bufs to come close to
* back-to-back transmits. Expect a 20 -> 25% performance hit on
* reasonable hardware if you only use one Tx buffer.
*/
ei_block_output(dev, length, skb->data, ei_local->tx_start_page);
ei_local->txing = 1;
NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
dev->trans_start = jiffies;
netif_stop_queue(dev);
#endif /* EI_PINGPONG */
/* Turn 8390 interrupts back on. */
ei_local->irqlock = 0;
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
......@@ -1336,14 +1317,14 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
spin_unlock(&ei_local->page_lock);
enable_irq(dev->irq);
DEV_KFREE_SKB (skb);
add_tx_bytes(&ei_local->stat, send_length);
dev_kfree_skb (skb);
ei_local->stat.tx_bytes += send_length;
return 0;
}
/**
* ei_interrupt - handle the interrupts from an 8390
* ax_interrupt - handle the interrupts from an 8390
* @irq: interrupt number
* @dev_id: a pointer to the net_device
* @regs: unused
......@@ -1355,7 +1336,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
* needed.
*/
static void ei_interrupt(int irq, void *dev_id, struct pt_regs * regs)
static void ax_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
struct net_device *dev = dev_id;
long e8390_base;
......@@ -1522,8 +1503,6 @@ static void ei_tx_intr(struct net_device *dev)
struct ei_device *ei_local = (struct ei_device *) dev->priv;
int status = inb(e8390_base + EN0_TSR);
#ifdef EI_PINGPONG
/*
* There are two Tx buffers, see which one finished, and trigger
* the send of another one if it exists.
......@@ -1566,13 +1545,6 @@ static void ei_tx_intr(struct net_device *dev)
// else printk(KERN_WARNING "%s: unexpected TX-done interrupt, lasttx=%d.\n",
// dev->name, ei_local->lasttx);
#else /* EI_PINGPONG */
/*
* Single Tx buffer: mark it free so another packet can be loaded.
*/
ei_local->txing = 0;
#endif
/* Minimize Tx latency: update the statistics after we restart TXing. */
if (status & ENTSR_COL)
ei_local->stat.collisions++;
......@@ -1680,7 +1652,7 @@ static void ei_receive(struct net_device *dev)
netif_rx(skb);
dev->last_rx = jiffies;
ei_local->stat.rx_packets++;
add_rx_bytes(&ei_local->stat, pkt_len);
ei_local->stat.rx_bytes += pkt_len;
if (pkt_stat & ENRSR_PHY)
ei_local->stat.multicast++;
}
......@@ -1826,11 +1798,11 @@ static void do_set_multicast_list(struct net_device *dev)
long e8390_base = dev->base_addr;
if(dev->flags&IFF_PROMISC)
outb_p(E8390_RXCONFIG | 0x18, e8390_base + EN0_RXCR);
outb_p(E8390_RXCONFIG | 0x58, e8390_base + EN0_RXCR);
else if(dev->flags&IFF_ALLMULTI || dev->mc_list)
outb_p(E8390_RXCONFIG | 0x08, e8390_base + EN0_RXCR);
outb_p(E8390_RXCONFIG | 0x48, e8390_base + EN0_RXCR);
else
outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR);
}
/*
......@@ -1838,28 +1810,28 @@ static void do_set_multicast_list(struct net_device *dev)
* be parallel to just about everything else. Its also fairly quick and
* not called too often. Must protect against both bh and irq users
*/
static void set_multicast_list(struct net_device *dev)
{
unsigned long flags;
spin_lock_irqsave(&((struct ei_device *)dev->priv)->page_lock, flags);
spin_lock_irqsave(&dev_lock(dev), flags);
do_set_multicast_list(dev);
spin_unlock_irqrestore(&((struct ei_device *)dev->priv)->page_lock, flags);
spin_unlock_irqrestore(&dev_lock(dev), flags);
}
/**
* ethdev_init - init rest of 8390 device struct
* axdev_init - init rest of 8390 device struct
* @dev: network device structure to init
*
* Initialize the rest of the 8390 device structure. Do NOT __init
* this, as it is used by 8390 based modular drivers too.
*/
static int ethdev_init(struct net_device *dev)
static int axdev_init(struct net_device *dev)
{
if (ei_debug > 1)
printk(version);
printk(version_8390);
if (dev->priv == NULL)
{
......@@ -1882,20 +1854,18 @@ static int ethdev_init(struct net_device *dev)
return 0;
}
/* This page of functions should be 8390 generic */
/* Follow National Semi's recommendations for initializing the "NIC". */
/**
* NS8390_init - initialize 8390 hardware
* AX88190_init - initialize 8390 hardware
* @dev: network device to initialize
* @startp: boolean. non-zero value to initiate chip processing
*
* Must be called with lock held.
*/
static void NS8390_init(struct net_device *dev, int startp)
static void AX88190_init(struct net_device *dev, int startp)
{
axnet_dev_t *info = (axnet_dev_t *)dev;
long e8390_base = dev->base_addr;
......@@ -1912,7 +1882,7 @@ static void NS8390_init(struct net_device *dev, int startp)
outb_p(0x00, e8390_base + EN0_RCNTLO);
outb_p(0x00, e8390_base + EN0_RCNTHI);
/* Set to monitor and loopback mode -- this is vital!. */
outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
outb_p(E8390_RXOFF|0x40, e8390_base + EN0_RXCR); /* 0x60 */
outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
/* Set the transmit page and receive ring. */
outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
......@@ -1956,7 +1926,7 @@ static void NS8390_init(struct net_device *dev, int startp)
outb_p(E8390_TXCONFIG | info->duplex_flag,
e8390_base + EN0_TXCR); /* xmit on. */
/* 3c503 TechMan says rxconfig only after the NIC is started. */
outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */
outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR); /* rx on, */
do_set_multicast_list(dev); /* (re)load the mcast table */
}
}
......
......@@ -106,6 +106,10 @@ Log: nmclan_cs.c,v
---------------------------------------------------------------------------- */
#define DRV_NAME "nmclan_cs"
#define DRV_VERSION "0.16"
/* ----------------------------------------------------------------------------
Conditional Compilation Options
---------------------------------------------------------------------------- */
......@@ -130,6 +134,9 @@ Include Files
#include <linux/interrupt.h>
#include <linux/in.h>
#include <linux/delay.h>
#include <linux/ethtool.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/bitops.h>
......@@ -375,7 +382,7 @@ Private Global Variables
static char rcsid[] =
"nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao";
static char *version =
"nmclan_cs 0.16 (Roger C. Pao)";
DRV_NAME " " DRV_VERSION " (Roger C. Pao)";
#endif
static dev_info_t dev_info="nmclan_cs";
......@@ -430,8 +437,8 @@ static void mace_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static struct net_device_stats *mace_get_stats(struct net_device *dev);
static int mace_rx(struct net_device *dev, unsigned char RxCnt);
static void restore_multicast_list(struct net_device *dev);
static void set_multicast_list(struct net_device *dev);
static int mace_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static dev_link_t *nmclan_attach(void);
static void nmclan_detach(dev_link_t *);
......@@ -513,6 +520,7 @@ static dev_link_t *nmclan_attach(void)
dev->set_config = &mace_config;
dev->get_stats = &mace_get_stats;
dev->set_multicast_list = &set_multicast_list;
dev->do_ioctl = &mace_ioctl;
ether_setup(dev);
dev->open = &mace_open;
dev->stop = &mace_close;
......@@ -1002,6 +1010,66 @@ static int mace_close(struct net_device *dev)
return 0;
} /* mace_close */
static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
{
u32 ethcmd;
/* dev_ioctl() in ../../net/core/dev.c has already checked
capable(CAP_NET_ADMIN), so don't bother with that here. */
if (get_user(ethcmd, (u32 *)useraddr))
return -EFAULT;
switch (ethcmd) {
case ETHTOOL_GDRVINFO: {
struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
strcpy (info.driver, DRV_NAME);
strcpy (info.version, DRV_VERSION);
sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr);
if (copy_to_user (useraddr, &info, sizeof (info)))
return -EFAULT;
return 0;
}
#ifdef PCMCIA_DEBUG
/* get message-level */
case ETHTOOL_GMSGLVL: {
struct ethtool_value edata = {ETHTOOL_GMSGLVL};
edata.data = pc_debug;
if (copy_to_user(useraddr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
}
/* set message-level */
case ETHTOOL_SMSGLVL: {
struct ethtool_value edata;
if (copy_from_user(&edata, useraddr, sizeof(edata)))
return -EFAULT;
pc_debug = edata.data;
return 0;
}
#endif
default:
break;
}
return -EOPNOTSUPP;
}
static int mace_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
switch (cmd) {
case SIOCETHTOOL:
return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
default:
return -EOPNOTSUPP;
}
return 0;
}
/* ----------------------------------------------------------------------------
mace_start_xmit
This routine begins the packet transmit function. When completed,
......
......@@ -11,7 +11,7 @@
Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
pcnet_cs.c 1.144 2001/11/07 04:06:56
pcnet_cs.c 1.149 2002/06/29 06:27:37
The network driver code is based on Donald Becker's NE2000 code:
......@@ -75,7 +75,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.144 2001/11/07 04:06:56 (David Hinds)";
"pcnet_cs.c 1.149 2002/06/29 06:27:37 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
......@@ -765,12 +765,13 @@ static void pcnet_config(dev_link_t *link)
strcpy(info->node.dev_name, dev->name);
link->dev = &info->node;
link->state &= ~DEV_CONFIG_PENDING;
if (info->flags & (IS_DL10019|IS_DL10022)) {
u_char id = inb(dev->base_addr + 0x1a);
dev->do_ioctl = &ei_ioctl;
mii_phy_probe(dev);
if ((id == 0x30) && !info->pna_phy && (info->eth_phy == 4))
info->eth_phy = 0;
printk(KERN_INFO "%s: NE2000 (DL100%d rev %02x): ",
dev->name, ((info->flags & IS_DL10022) ? 22 : 19), id);
if (info->pna_phy)
......@@ -787,12 +788,14 @@ static void pcnet_config(dev_link_t *link)
printk(" hw_addr ");
for (i = 0; i < 6; i++)
printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
link->state &= ~DEV_CONFIG_PENDING;
return;
cs_failed:
cs_error(link->handle, last_fn, last_ret);
failed:
pcnet_release((u_long)link);
link->state &= ~DEV_CONFIG_PENDING;
return;
} /* pcnet_config */
......@@ -856,7 +859,7 @@ static int pcnet_event(event_t event, int priority,
}
break;
case CS_EVENT_CARD_INSERTION:
link->state |= DEV_PRESENT;
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
pcnet_config(link);
break;
case CS_EVENT_PM_SUSPEND:
......@@ -1051,6 +1054,7 @@ static int pcnet_close(struct net_device *dev)
DEBUG(2, "pcnet_close('%s')\n", dev->name);
ei_close(dev);
free_irq(dev->irq, dev);
link->open--;
......@@ -1620,7 +1624,7 @@ static int __init init_pcnet_cs(void)
if (serv.Revision != CS_RELEASE_CODE) {
printk(KERN_NOTICE "pcnet_cs: Card Services release "
"does not match!\n");
return -1;
return -EINVAL;
}
register_pccard_driver(&dev_info, &pcnet_attach, &pcnet_detach);
return 0;
......
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