patch-2.4.3 linux/drivers/net/natsemi.c
Next file: linux/drivers/net/ne.c
Previous file: linux/drivers/net/macsonic.c
Back to the patch index
Back to the overall index
- Lines: 595
- Date:
Sun Mar 25 18:24:31 2001
- Orig file:
v2.4.2/linux/drivers/net/natsemi.c
- Orig date:
Wed Feb 21 18:20:27 2001
diff -u --recursive --new-file v2.4.2/linux/drivers/net/natsemi.c linux/drivers/net/natsemi.c
@@ -1,6 +1,6 @@
-/* natsemi.c: A Linux PCI Ethernet driver for the NatSemi DP83810 series. */
+/* natsemi.c: A Linux PCI Ethernet driver for the NatSemi DP8381x series. */
/*
- Written/copyright 1999-2000 by Donald Becker.
+ Written/copyright 1999-2001 by Donald Becker.
This software may be used and distributed according to the terms of
the GNU General Public License (GPL), incorporated herein by reference.
@@ -31,25 +31,28 @@
- Call netif_start_queue from dev->tx_timeout
- wmb() in start_tx() to flush data
- Update Tx locking
+ - Clean up PCI enable (davej)
+ Version 1.0.4:
+ - Merge Donald Becker's natsemi.c version 1.07
*/
/* These identify the driver base version and may not be removed. */
static const char version1[] =
-"natsemi.c:v1.05 8/7/2000 Written by Donald Becker <becker@scyld.com>\n";
+"natsemi.c:v1.07 1/9/2001 Written by Donald Becker <becker@scyld.com>\n";
static const char version2[] =
" http://www.scyld.com/network/natsemi.html\n";
static const char version3[] =
-" (unofficial 2.4.x kernel port, version 1.0.3, January 21, 2001 Jeff Garzik, Tjeerd Mulder)\n";
+" (unofficial 2.4.x kernel port, version 1.0.4, February 26, 2001 Jeff Garzik, Tjeerd Mulder)\n";
/* Updated to recommendations in pci-skeleton v2.03. */
/* Automatically extracted configuration info:
probe-func: natsemi_probe
-config-in: tristate 'National Semiconductor DP83810 series PCI Ethernet support' CONFIG_NATSEMI
+config-in: tristate 'National Semiconductor DP8381x series PCI Ethernet support' CONFIG_NATSEMI
-c-help-name: National Semiconductor DP83810 series PCI Ethernet support
+c-help-name: National Semiconductor DP8381x series PCI Ethernet support
c-help-symbol: CONFIG_NATSEMI
-c-help: This driver is for the National Semiconductor DP83810 series,
+c-help: This driver is for the National Semiconductor DP8381x series,
c-help: including the 83815 chip.
c-help: More specific information and updates are available from
c-help: http://www.scyld.com/network/natsemi.html
@@ -87,7 +90,7 @@
bonding and packet priority.
There are no ill effects from too-large receive rings. */
#define TX_RING_SIZE 16
-#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */
+#define TX_QUEUE_LEN 10 /* Limit ring entries actually used, min 4. */
#define RX_RING_SIZE 32
/* Operational parameters that usually are not changed. */
@@ -127,7 +130,7 @@
#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
-MODULE_DESCRIPTION("National Semiconductor DP83810 series PCI Ethernet driver");
+MODULE_DESCRIPTION("National Semiconductor DP8381x series PCI Ethernet driver");
MODULE_PARM(max_interrupt_work, "i");
MODULE_PARM(mtu, "i");
MODULE_PARM(debug, "i");
@@ -192,14 +195,13 @@
The send packet thread has partial control over the Tx ring and 'dev->tbusy'
flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished. Under 2.4, the
-"tbusy flag" is now controlled by netif_{start,stop,wake}_queue() and tested
-by netif_queue_stopped().
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'lp->tx_full' flag.
The interrupt handler has exclusive control over the Rx ring and records stats
from the Tx ring. After reaping the stats, it marks the Tx queue entry as
-empty by incrementing the dirty_tx mark. Iff Tx queueing is stopped and Tx
-entries were reaped, the Tx queue is started and scheduled.
+empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it
+clears both the tx_full and tbusy flags.
IV. Notes
@@ -258,6 +260,7 @@
WOLCmd=0x40, PauseCmd=0x44, RxFilterAddr=0x48, RxFilterData=0x4C,
BootRomAddr=0x50, BootRomData=0x54, StatsCtrl=0x5C, StatsData=0x60,
RxPktErrs=0x60, RxMissed=0x68, RxCRCErrs=0x64,
+ PCIPM = 0x44,
};
/* Bit in ChipCmd. */
@@ -271,24 +274,20 @@
IntrRxDone=0x0001, IntrRxIntr=0x0002, IntrRxErr=0x0004, IntrRxEarly=0x0008,
IntrRxIdle=0x0010, IntrRxOverrun=0x0020,
IntrTxDone=0x0040, IntrTxIntr=0x0080, IntrTxErr=0x0100,
- IntrTxIdle=0x0200, IntrTxOverrun=0x0400,
+ IntrTxIdle=0x0200, IntrTxUnderrun=0x0400,
StatsMax=0x0800, LinkChange=0x4000,
WOLPkt=0x2000,
RxResetDone=0x1000000, TxResetDone=0x2000000,
IntrPCIErr=0x00f00000,
- IntrAbnormalSummary=0xCD20,
+ IntrNormalSummary=0x0251, IntrAbnormalSummary=0xED20,
};
/* Bits in the RxMode register. */
enum rx_mode_bits {
- EnableFilter = 0x80000000,
- AcceptBroadcast = 0x40000000,
- AcceptAllMulticast = 0x20000000,
- AcceptAllPhys = 0x10000000,
- AcceptMyPhys = 0x08000000,
- AcceptMulticast = 0x00200000,
- AcceptErr=0x20, /* these 2 are in another register */
- AcceptRunt=0x10,/* and are not used in this driver */
+ AcceptErr=0x20, AcceptRunt=0x10,
+ AcceptBroadcast=0xC0000000,
+ AcceptMulticast=0x00200000, AcceptAllMulticast=0x20000000,
+ AcceptAllPhys=0x10000000, AcceptMyPhys=0x08000000,
};
/* The Rx and Tx buffer descriptors. */
@@ -334,9 +333,9 @@
u32 cur_rx_mode;
u32 rx_filter[16];
/* FIFO and PCI burst thresholds. */
- int tx_config, rx_config;
+ u32 tx_config, rx_config;
/* original contents of ClkRun register */
- int SavedClkRun;
+ u32 SavedClkRun;
/* MII transceiver section. */
u16 advertising; /* NWay media advertisement */
@@ -346,7 +345,6 @@
static int eeprom_read(long ioaddr, int location);
static int mdio_read(struct net_device *dev, int phy_id, int location);
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
static int netdev_open(struct net_device *dev);
static void check_duplex(struct net_device *dev);
static void netdev_timer(unsigned long data);
@@ -368,68 +366,70 @@
{
struct net_device *dev;
struct netdev_private *np;
- int i, option, irq = pdev->irq, chip_idx = ent->driver_data;
+ int i, option, irq, chip_idx = ent->driver_data;
static int find_cnt = -1;
static int printed_version;
unsigned long ioaddr, iosize;
const int pcibar = 1; /* PCI base address register */
+ int prev_eedata;
+ u32 tmp;
if ((debug <= 1) && !printed_version++)
printk(KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s",
version1, version2, version3);
+ i = pci_enable_device(pdev);
+ if (i) return i;
+
+ /* natsemi has a non-standard PM control register
+ * in PCI config space. Some boards apparently need
+ * to be brought to D0 in this manner.
+ */
+ pci_read_config_dword(pdev, PCIPM, &tmp);
+ if (tmp & (0x03|0x100)) {
+ /* D0 state, disable PME assertion */
+ u32 newtmp = tmp & ~(0x03|0x100);
+ pci_write_config_dword(pdev, PCIPM, newtmp);
+ }
+
find_cnt++;
option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
ioaddr = pci_resource_start(pdev, pcibar);
iosize = pci_resource_len(pdev, pcibar);
-
- if (pci_enable_device(pdev))
- return -EIO;
+ irq = pdev->irq;
+
if (natsemi_pci_info[chip_idx].flags & PCI_USES_MASTER)
pci_set_master(pdev);
- dev = init_etherdev(NULL, sizeof (struct netdev_private));
+ dev = alloc_etherdev(sizeof (struct netdev_private));
if (!dev)
return -ENOMEM;
SET_MODULE_OWNER(dev);
+ i = pci_request_regions(pdev, dev->name);
+ if (i) {
+ kfree(dev);
+ return i;
+ }
+
{
- void *mmio;
- if (request_mem_region(ioaddr, iosize, dev->name) == NULL) {
- unregister_netdev(dev);
- kfree(dev);
- return -EBUSY;
- }
- mmio = ioremap (ioaddr, iosize);
+ void *mmio = ioremap (ioaddr, iosize);
if (!mmio) {
- release_mem_region(ioaddr, iosize);
- unregister_netdev(dev);
+ pci_release_regions(pdev);
kfree(dev);
return -ENOMEM;
}
ioaddr = (unsigned long) mmio;
}
- printk(KERN_INFO "%s: %s at 0x%lx, ",
- dev->name, natsemi_pci_info[chip_idx].name, ioaddr);
-
- for (i = 0; i < ETH_ALEN/2; i++) {
- /* weird organization */
- unsigned short a;
- a = (le16_to_cpu(eeprom_read(ioaddr, i + 6)) >> 15) +
- (le16_to_cpu(eeprom_read(ioaddr, i + 7)) << 1);
- ((u16 *)dev->dev_addr)[i] = a;
+ /* Work around the dropped serial bit. */
+ prev_eedata = eeprom_read(ioaddr, 6);
+ for (i = 0; i < 3; i++) {
+ int eedata = eeprom_read(ioaddr, i + 7);
+ dev->dev_addr[i*2] = (eedata << 1) + (prev_eedata >> 15);
+ dev->dev_addr[i*2+1] = eedata >> 7;
+ prev_eedata = eedata;
}
- for (i = 0; i < ETH_ALEN-1; i++)
- printk("%2.2x:", dev->dev_addr[i]);
- printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
-
-#if ! defined(final_version) /* Dump the EEPROM contents during development. */
- if (debug > 4)
- for (i = 0; i < 64; i++)
- printk("%4.4x%s",
- eeprom_read(ioaddr, i), i % 16 != 15 ? " " : "\n");
-#endif
/* Reset the chip to erase previous misconfiguration. */
writel(ChipReset, ioaddr + ChipCmd);
@@ -440,7 +440,7 @@
np = dev->priv;
np->pci_dev = pdev;
- pdev->driver_data = dev;
+ pci_set_drvdata(pdev, dev);
np->iosize = iosize;
spin_lock_init(&np->lock);
@@ -474,7 +474,31 @@
if (mtu)
dev->mtu = mtu;
- np->advertising = readl(ioaddr + 0x90);
+ i = register_netdev(dev);
+ if (i) {
+ pci_release_regions(pdev);
+ unregister_netdev(dev);
+ kfree(dev);
+ pci_set_drvdata(pdev, NULL);
+ return i;
+ }
+
+ printk(KERN_INFO "%s: %s at 0x%lx, ",
+ dev->name, natsemi_pci_info[chip_idx].name, ioaddr);
+ for (i = 0; i < ETH_ALEN-1; i++)
+ printk("%2.2x:", dev->dev_addr[i]);
+ printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
+
+ np->advertising = mdio_read(dev, 1, 4);
+ if ((readl(ioaddr + ChipConfig) & 0xe000) != 0xe000) {
+ u32 chip_config = readl(ioaddr + ChipConfig);
+ printk(KERN_INFO "%s: Transceiver default autonegotiation %s "
+ "10%s %s duplex.\n",
+ dev->name,
+ chip_config & 0x2000 ? "enabled, advertise" : "disabled, force",
+ chip_config & 0x4000 ? "0" : "",
+ chip_config & 0x8000 ? "full" : "half");
+ }
printk(KERN_INFO "%s: Transceiver status 0x%4.4x advertising %4.4x.\n",
dev->name, (int)readl(ioaddr + 0x84), np->advertising);
@@ -522,12 +546,12 @@
eeprom_delay(ee_addr);
}
writel(EE_ChipSelect, ee_addr);
+ eeprom_delay(ee_addr);
- for (i = 16; i > 0; i--) {
+ for (i = 0; i < 16; i++) {
writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
eeprom_delay(ee_addr);
- /* data bits are LSB first */
- retval = (retval >> 1) | ((readl(ee_addr) & EE_DataOut) ? 0x8000 : 0);
+ retval |= (readl(ee_addr) & EE_DataOut) ? 1 << i : 0;
writel(EE_ChipSelect, ee_addr);
eeprom_delay(ee_addr);
}
@@ -550,16 +574,10 @@
return 0xffff;
}
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
-{
- if (phy_id == 1 && location < 32)
- writew(value, dev->base_addr + 0x80 + (location<<2));
-}
-
static int netdev_open(struct net_device *dev)
{
- struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct netdev_private *np = dev->priv;
long ioaddr = dev->base_addr;
int i;
@@ -586,20 +604,26 @@
/* Initialize other registers. */
/* Configure the PCI bus bursts and FIFO thresholds. */
/* Configure for standard, in-spec Ethernet. */
- np->tx_config = (1<<28) + /* Automatic transmit padding */
- (1<<23) + /* Excessive collision retry */
- (0x0<<20) + /* Max DMA burst = 512 byte */
- (8<<8) + /* fill threshold = 256 byte */
- 2; /* drain threshold = 64 byte */
+
+ if (readl(ioaddr + ChipConfig) & 0x20000000) { /* Full duplex */
+ np->tx_config = 0xD0801002;
+ np->rx_config = 0x10000020;
+ } else {
+ np->tx_config = 0x10801002;
+ np->rx_config = 0x0020;
+ }
writel(np->tx_config, ioaddr + TxConfig);
- np->rx_config = (0x0<<20) /* Max DMA burst = 512 byte */ +
- (0x8<<1); /* Drain Threshold = 64 byte */
writel(np->rx_config, ioaddr + RxConfig);
if (dev->if_port == 0)
dev->if_port = np->default_port;
- /* Disable PME */
+ /* Disable PME:
+ * The PME bit is initialized from the EEPROM contents.
+ * PCI cards probably have PME disabled, but motherboard
+ * implementations may have PME set to enable WakeOnLan.
+ * With PME set the chip will scan incoming packets but
+ * nothing will be written to memory. */
np->SavedClkRun = readl(ioaddr + ClkRun);
writel(np->SavedClkRun & ~0x100, ioaddr + ClkRun);
@@ -608,10 +632,8 @@
check_duplex(dev);
set_rx_mode(dev);
- /* Enable interrupts by setting the interrupt mask.
- * We don't listen for TxDone interrupts and rely on TxIdle. */
- writel(IntrAbnormalSummary | IntrTxIdle | IntrRxIdle | IntrRxDone,
- ioaddr + IntrMask);
+ /* Enable interrupts by setting the interrupt mask. */
+ writel(IntrNormalSummary | IntrAbnormalSummary | 0x1f, ioaddr + IntrMask);
writel(1, ioaddr + IntrEnable);
writel(RxOn | TxOn, ioaddr + ChipCmd);
@@ -633,7 +655,7 @@
static void check_duplex(struct net_device *dev)
{
- struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct netdev_private *np = dev->priv;
long ioaddr = dev->base_addr;
int duplex;
@@ -661,7 +683,7 @@
static void netdev_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
- struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct netdev_private *np = dev->priv;
long ioaddr = dev->base_addr;
int next_tick = 60*HZ;
@@ -675,7 +697,7 @@
static void tx_timeout(struct net_device *dev)
{
- struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct netdev_private *np = dev->priv;
long ioaddr = dev->base_addr;
printk(KERN_WARNING "%s: Transmit timed out, status %8.8x,"
@@ -702,14 +724,14 @@
dev->trans_start = jiffies;
np->stats.tx_errors++;
- netif_start_queue(dev);
+ netif_wake_queue(dev);
}
/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
static void init_ring(struct net_device *dev)
{
- struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct netdev_private *np = dev->priv;
int i;
np->cur_rx = np->cur_tx = 0;
@@ -736,7 +758,7 @@
skb->dev = dev; /* Mark as being used by this device. */
np->rx_ring[i].addr = virt_to_le32desc(skb->tail);
np->rx_ring[i].cmd_status =
- cpu_to_le32(np->rx_buf_sz);
+ cpu_to_le32(DescIntr | np->rx_buf_sz);
}
np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
@@ -751,7 +773,7 @@
static int start_tx(struct sk_buff *skb, struct net_device *dev)
{
- struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct netdev_private *np = dev->priv;
unsigned entry;
/* Note: Ordering is important here, set the field with the
@@ -763,7 +785,7 @@
np->tx_skbuff[entry] = skb;
np->tx_ring[entry].addr = virt_to_le32desc(skb->data);
- np->tx_ring[entry].cmd_status = cpu_to_le32(DescOwn | skb->len);
+ np->tx_ring[entry].cmd_status = cpu_to_le32(DescOwn|DescIntr | skb->len);
np->cur_tx++;
/* StrongARM: Explicitly cache flush np->tx_ring and skb->data,skb->len. */
@@ -819,7 +841,7 @@
if (intr_status == 0)
break;
- if (intr_status & (IntrRxDone | IntrRxErr | IntrRxIdle | IntrRxOverrun))
+ if (intr_status & (IntrRxDone | IntrRxIntr))
netdev_rx(dev);
spin_lock(&np->lock);
@@ -886,7 +908,7 @@
for clarity and better register allocation. */
static int netdev_rx(struct net_device *dev)
{
- struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct netdev_private *np = dev->priv;
int entry = np->cur_rx % RX_RING_SIZE;
int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
s32 desc_status = le32_to_cpu(np->rx_head_desc->cmd_status);
@@ -898,7 +920,6 @@
entry, desc_status);
if (--boguscnt < 0)
break;
-
if ((desc_status & (DescMore|DescPktOK|RxTooLong)) != DescPktOK) {
if (desc_status & DescMore) {
printk(KERN_WARNING "%s: Oversized(?) Ethernet frame spanned "
@@ -984,7 +1005,7 @@
np->rx_ring[entry].addr = virt_to_le32desc(skb->tail);
}
np->rx_ring[entry].cmd_status =
- cpu_to_le32(np->rx_buf_sz);
+ cpu_to_le32(DescIntr | np->rx_buf_sz);
}
/* Restart Rx engine if stopped. */
@@ -994,7 +1015,7 @@
static void netdev_error(struct net_device *dev, int intr_status)
{
- struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct netdev_private *np = dev->priv;
long ioaddr = dev->base_addr;
if (intr_status & LinkChange) {
@@ -1006,7 +1027,17 @@
if (intr_status & StatsMax) {
get_stats(dev);
}
- if ((intr_status & ~(LinkChange|StatsMax|RxResetDone|TxResetDone|0x83ff))
+ if (intr_status & IntrTxUnderrun) {
+ if ((np->tx_config & 0x3f) < 62)
+ np->tx_config += 2;
+ writel(np->tx_config, ioaddr + TxConfig);
+ }
+ if (intr_status & WOLPkt) {
+ int wol_status = readl(ioaddr + WOLCmd);
+ printk(KERN_NOTICE "%s: Link wake-up event %8.8x",
+ dev->name, wol_status);
+ }
+ if ((intr_status & ~(LinkChange|StatsMax|RxResetDone|TxResetDone|0xA7ff))
&& debug)
printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
dev->name, intr_status);
@@ -1020,7 +1051,7 @@
static struct net_device_stats *get_stats(struct net_device *dev)
{
long ioaddr = dev->base_addr;
- struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct netdev_private *np = dev->priv;
/* We should lock this segment of code for SMP eventually, although
the vulnerability window is very small and statistics are
@@ -1061,8 +1092,8 @@
static void set_rx_mode(struct net_device *dev)
{
long ioaddr = dev->base_addr;
- struct netdev_private *np = (struct netdev_private *)dev->priv;
- u16 mc_filter[32]; /* Multicast hash filter */
+ struct netdev_private *np = dev->priv;
+ u8 mc_filter[64]; /* Multicast hash filter */
u32 rx_mode;
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
@@ -1082,18 +1113,19 @@
set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
mc_filter);
}
- for (i = 0; i < 32; i++) {
- writew(0x200 + (i<<1), ioaddr + RxFilterAddr);
- writew(cpu_to_be16(mc_filter[i]), ioaddr + RxFilterData);
- }
rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+ for (i = 0; i < 64; i += 2) {
+ writew(0x200 + i, ioaddr + RxFilterAddr);
+ writew((mc_filter[i+1]<<8) + mc_filter[i], ioaddr + RxFilterData);
+ }
}
- writel(rx_mode | EnableFilter, ioaddr + RxFilterAddr);
+ writel(rx_mode, ioaddr + RxFilterAddr);
np->cur_rx_mode = rx_mode;
}
static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
+ struct netdev_private *np = dev->priv;
u16 *data = (u16 *)&rq->ifr_data;
switch(cmd) {
@@ -1106,7 +1138,20 @@
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+ if (data[0] == 1) {
+ u16 miireg = data[1] & 0x1f;
+ u16 value = data[2];
+ writew(value, dev->base_addr + 0x80 + (miireg << 2));
+ switch (miireg) {
+ case 0:
+ /* Check for autonegotiation on or reset. */
+ np->duplex_lock = (value & 0x9000) ? 0 : 1;
+ if (np->duplex_lock)
+ np->full_duplex = (value & 0x0100) ? 1 : 0;
+ break;
+ case 4: np->advertising = value; break;
+ }
+ }
return 0;
default:
return -EOPNOTSUPP;
@@ -1116,7 +1161,7 @@
static int netdev_close(struct net_device *dev)
{
long ioaddr = dev->base_addr;
- struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct netdev_private *np = dev->priv;
int i;
netif_stop_queue(dev);
@@ -1187,14 +1232,13 @@
static void __devexit natsemi_remove1 (struct pci_dev *pdev)
{
- struct net_device *dev = pdev->driver_data;
- struct netdev_private *np = (struct netdev_private *)dev->priv;
- const int pcibar = 1; /* PCI base address register */
+ struct net_device *dev = pci_get_drvdata(pdev);
unregister_netdev (dev);
- release_mem_region(pci_resource_start(pdev, pcibar), np->iosize);
+ pci_release_regions (pdev);
iounmap ((char *) dev->base_addr);
kfree (dev);
+ pci_set_drvdata(pdev, NULL);
}
static struct pci_driver natsemi_driver = {
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)