Ejemplo n.º 1
0
/* Reset and restore all of the 3c589 registers. */
static void tc589_reset(struct net_device *dev)
{
    kio_addr_t ioaddr = dev->base_addr;
    int i;
    
    EL3WINDOW(0);
    outw(0x0001, ioaddr + 4);			/* Activate board. */ 
    outw(0x3f00, ioaddr + 8);			/* Set the IRQ line. */
    
    /* Set the station address in window 2. */
    EL3WINDOW(2);
    for (i = 0; i < 6; i++)
	outb(dev->dev_addr[i], ioaddr + i);

    tc589_set_xcvr(dev, dev->if_port);
    
    /* Switch to the stats window, and clear all stats by reading. */
    outw(StatsDisable, ioaddr + EL3_CMD);
    EL3WINDOW(6);
    for (i = 0; i < 9; i++)
	inb(ioaddr+i);
    inw(ioaddr + 10);
    inw(ioaddr + 12);
    
    /* Switch to register set 1 for normal use. */
    EL3WINDOW(1);

    /* Accept b-cast and phys addr only. */
    outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
    outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
    outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
    outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
    /* Allow status bits to be seen. */
    outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
    /* Ack all pending events, and set active indicator mask. */
    outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
	 ioaddr + EL3_CMD);
    outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
	 | AdapterFailure, ioaddr + EL3_CMD);
}
Ejemplo n.º 2
0
/*
  Set transceiver type, perhaps to something other than what the user
  specified in dev->if_port.
*/
static void tc589_set_xcvr(struct net_device *dev, int if_port)
{
    struct el3_private *lp = netdev_priv(dev);
    unsigned int ioaddr = dev->base_addr;

    EL3WINDOW(0);
    switch (if_port) {
    case 0: case 1: outw(0, ioaddr + 6); break;
    case 2: outw(3<<14, ioaddr + 6); break;
    case 3: outw(1<<14, ioaddr + 6); break;
    }
    /* On PCMCIA, this just turns on the LED */
    outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD);
    /* 10baseT interface, enable link beat and jabber check. */
    EL3WINDOW(4);
    outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA);
    EL3WINDOW(1);
    if (if_port == 2)
	lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000);
    else
	lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800);
}
Ejemplo n.º 3
0
static void media_check(unsigned long arg)
{
    struct net_device *dev = (struct net_device *)(arg);
    struct el3_private *lp = netdev_priv(dev);
    unsigned int ioaddr = dev->base_addr;
    u16 media, errs;
    unsigned long flags;

    if (!netif_device_present(dev)) goto reschedule;

    /* Check for pending interrupt with expired latency timer: with
       this, we can limp along even if the interrupt is blocked */
    if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
	(inb(ioaddr + EL3_TIMER) == 0xff)) {
	if (!lp->fast_poll)
		netdev_warn(dev, "interrupt(s) dropped!\n");

	local_irq_save(flags);
	el3_interrupt(dev->irq, dev);
	local_irq_restore(flags);

	lp->fast_poll = HZ;
    }
    if (lp->fast_poll) {
	lp->fast_poll--;
	lp->media.expires = jiffies + HZ/100;
	add_timer(&lp->media);
	return;
    }

    /* lp->lock guards the EL3 window. Window should always be 1 except
       when the lock is held */
    spin_lock_irqsave(&lp->lock, flags);
    EL3WINDOW(4);
    media = inw(ioaddr+WN4_MEDIA) & 0xc810;

    /* Ignore collisions unless we've had no irq's recently */
    if (time_before(jiffies, lp->last_irq + HZ)) {
	media &= ~0x0010;
    } else {
	/* Try harder to detect carrier errors */
	EL3WINDOW(6);
	outw(StatsDisable, ioaddr + EL3_CMD);
	errs = inb(ioaddr + 0);
	outw(StatsEnable, ioaddr + EL3_CMD);
	dev->stats.tx_carrier_errors += errs;
	if (errs || (lp->media_status & 0x0010)) media |= 0x0010;
    }

    if (media != lp->media_status) {
	if ((media & lp->media_status & 0x8000) &&
	    ((lp->media_status ^ media) & 0x0800))
		netdev_info(dev, "%s link beat\n",
			    (lp->media_status & 0x0800 ? "lost" : "found"));
	else if ((media & lp->media_status & 0x4000) &&
		 ((lp->media_status ^ media) & 0x0010))
		netdev_info(dev, "coax cable %s\n",
			    (lp->media_status & 0x0010 ? "ok" : "problem"));
	if (dev->if_port == 0) {
	    if (media & 0x8000) {
		if (media & 0x0800)
			netdev_info(dev, "flipped to 10baseT\n");
		else
			tc589_set_xcvr(dev, 2);
	    } else if (media & 0x4000) {
		if (media & 0x0010)
		    tc589_set_xcvr(dev, 1);
		else
		    netdev_info(dev, "flipped to 10base2\n");
	    }
	}
	lp->media_status = media;
    }

    EL3WINDOW(1);
    spin_unlock_irqrestore(&lp->lock, flags);

reschedule:
    lp->media.expires = jiffies + HZ;
    add_timer(&lp->media);
}
Ejemplo n.º 4
0
/* The EL3 interrupt handler. */
static irqreturn_t el3_interrupt(int irq, void *dev_id)
{
    struct net_device *dev = (struct net_device *) dev_id;
    struct el3_private *lp = netdev_priv(dev);
    unsigned int ioaddr;
    __u16 status;
    int i = 0, handled = 1;

    if (!netif_device_present(dev))
	return IRQ_NONE;

    ioaddr = dev->base_addr;

    netdev_dbg(dev, "interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS));

    spin_lock(&lp->lock);
    while ((status = inw(ioaddr + EL3_STATUS)) &
	(IntLatch | RxComplete | StatsFull)) {
	if ((status & 0xe000) != 0x2000) {
		netdev_dbg(dev, "interrupt from dead card\n");
		handled = 0;
		break;
	}
	if (status & RxComplete)
		el3_rx(dev);
	if (status & TxAvailable) {
		netdev_dbg(dev, "    TX room bit was handled.\n");
		/* There's room in the FIFO for a full-sized packet. */
		outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
		netif_wake_queue(dev);
	}
	if (status & TxComplete)
		pop_tx_status(dev);
	if (status & (AdapterFailure | RxEarly | StatsFull)) {
	    /* Handle all uncommon interrupts. */
	    if (status & StatsFull)		/* Empty statistics. */
		update_stats(dev);
	    if (status & RxEarly) {		/* Rx early is unused. */
		el3_rx(dev);
		outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
	    }
	    if (status & AdapterFailure) {
		u16 fifo_diag;
		EL3WINDOW(4);
		fifo_diag = inw(ioaddr + 4);
		EL3WINDOW(1);
		netdev_warn(dev, "adapter failure, FIFO diagnostic register %04x.\n",
			    fifo_diag);
		if (fifo_diag & 0x0400) {
		    /* Tx overrun */
		    tc589_wait_for_completion(dev, TxReset);
		    outw(TxEnable, ioaddr + EL3_CMD);
		}
		if (fifo_diag & 0x2000) {
		    /* Rx underrun */
		    tc589_wait_for_completion(dev, RxReset);
		    set_rx_mode(dev);
		    outw(RxEnable, ioaddr + EL3_CMD);
		}
		outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
	    }
	}
	if (++i > 10) {
		netdev_err(dev, "infinite loop in interrupt, status %4.4x.\n",
			   status);
		/* Clear all interrupts */
		outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
		break;
	}
	/* Acknowledge the IRQ. */
	outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
    }
    lp->last_irq = jiffies;
    spin_unlock(&lp->lock);
    netdev_dbg(dev, "exiting interrupt, status %4.4x.\n",
	       inw(ioaddr + EL3_STATUS));
    return IRQ_RETVAL(handled);
}
Ejemplo n.º 5
0
static int tc589_config(struct pcmcia_device *link)
{
    struct net_device *dev = link->priv;
    __be16 *phys_addr;
    int ret, i, j, multi = 0, fifo;
    unsigned int ioaddr;
    char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
    u8 *buf;
    size_t len;

    dev_dbg(&link->dev, "3c589_config\n");

    phys_addr = (__be16 *)dev->dev_addr;
    /* Is this a 3c562? */
    if (link->manf_id != MANFID_3COM)
	    printk(KERN_INFO "3c589_cs: hmmm, is this really a "
		   "3Com card??\n");
    multi = (link->card_id == PRODID_3COM_3C562);

    link->io_lines = 16;

    /* For the 3c562, the base address must be xx00-xx7f */
    for (i = j = 0; j < 0x400; j += 0x10) {
	if (multi && (j & 0x80)) continue;
	link->resource[0]->start = j ^ 0x300;
	i = pcmcia_request_io(link);
	if (i == 0)
		break;
    }
    if (i != 0)
	goto failed;

    ret = pcmcia_request_irq(link, el3_interrupt);
    if (ret)
	    goto failed;

    ret = pcmcia_request_configuration(link, &link->conf);
    if (ret)
	    goto failed;

    dev->irq = link->irq;
    dev->base_addr = link->resource[0]->start;
    ioaddr = dev->base_addr;
    EL3WINDOW(0);

    /* The 3c589 has an extra EEPROM for configuration info, including
       the hardware address.  The 3c562 puts the address in the CIS. */
    len = pcmcia_get_tuple(link, 0x88, &buf);
    if (buf && len >= 6) {
	    for (i = 0; i < 3; i++)
		    phys_addr[i] = htons(le16_to_cpu(buf[i*2]));
	    kfree(buf);
    } else {
	kfree(buf); /* 0 < len < 6 */
	for (i = 0; i < 3; i++)
	    phys_addr[i] = htons(read_eeprom(ioaddr, i));
	if (phys_addr[0] == htons(0x6060)) {
	    printk(KERN_ERR "3c589_cs: IO port conflict at 0x%03lx"
		   "-0x%03lx\n", dev->base_addr, dev->base_addr+15);
	    goto failed;
	}
    }

    /* The address and resource configuration register aren't loaded from
       the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version. */
    outw(0x3f00, ioaddr + 8);
    fifo = inl(ioaddr);

    /* The if_port symbol can be set when the module is loaded */
    if ((if_port >= 0) && (if_port <= 3))
	dev->if_port = if_port;
    else
	printk(KERN_ERR "3c589_cs: invalid if_port requested\n");

    SET_NETDEV_DEV(dev, &link->dev);

    if (register_netdev(dev) != 0) {
	printk(KERN_ERR "3c589_cs: register_netdev() failed\n");
	goto failed;
    }

    netdev_info(dev, "3Com 3c%s, io %#3lx, irq %d, hw_addr %pM\n",
		(multi ? "562" : "589"), dev->base_addr, dev->irq,
		dev->dev_addr);
    netdev_info(dev, "  %dK FIFO split %s Rx:Tx, %s xcvr\n",
		(fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3],
		if_names[dev->if_port]);
    return 0;

failed:
    tc589_release(link);
    return -ENODEV;
} /* tc589_config */
Ejemplo n.º 6
0
static int tc589_config(struct pcmcia_device *link)
{
    struct net_device *dev = link->priv;
    struct el3_private *lp = netdev_priv(dev);
    tuple_t tuple;
    __le16 buf[32];
    __be16 *phys_addr;
    int last_fn, last_ret, i, j, multi = 0, fifo;
    unsigned int ioaddr;
    char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
    DECLARE_MAC_BUF(mac);
    
    DEBUG(0, "3c589_config(0x%p)\n", link);

    phys_addr = (__be16 *)dev->dev_addr;
    tuple.Attributes = 0;
    tuple.TupleData = (cisdata_t *)buf;
    tuple.TupleDataMax = sizeof(buf);
    tuple.TupleOffset = 0;
    tuple.Attributes = TUPLE_RETURN_COMMON;

    /* Is this a 3c562? */
    if (link->manf_id != MANFID_3COM)
	    printk(KERN_INFO "3c589_cs: hmmm, is this really a "
		   "3Com card??\n");
    multi = (link->card_id == PRODID_3COM_3C562);

    /* For the 3c562, the base address must be xx00-xx7f */
    link->io.IOAddrLines = 16;
    for (i = j = 0; j < 0x400; j += 0x10) {
	if (multi && (j & 0x80)) continue;
	link->io.BasePort1 = j ^ 0x300;
	i = pcmcia_request_io(link, &link->io);
	if (i == CS_SUCCESS) break;
    }
    if (i != CS_SUCCESS) {
	cs_error(link, RequestIO, i);
	goto failed;
    }
    CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
	
    dev->irq = link->irq.AssignedIRQ;
    dev->base_addr = link->io.BasePort1;
    ioaddr = dev->base_addr;
    EL3WINDOW(0);

    /* The 3c589 has an extra EEPROM for configuration info, including
       the hardware address.  The 3c562 puts the address in the CIS. */
    tuple.DesiredTuple = 0x88;
    if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) {
	pcmcia_get_tuple_data(link, &tuple);
	for (i = 0; i < 3; i++)
	    phys_addr[i] = htons(le16_to_cpu(buf[i]));
    } else {
	for (i = 0; i < 3; i++)
	    phys_addr[i] = htons(read_eeprom(ioaddr, i));
	if (phys_addr[0] == htons(0x6060)) {
	    printk(KERN_ERR "3c589_cs: IO port conflict at 0x%03lx"
		   "-0x%03lx\n", dev->base_addr, dev->base_addr+15);
	    goto failed;
	}
    }

    /* The address and resource configuration register aren't loaded from
       the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version. */
    outw(0x3f00, ioaddr + 8);
    fifo = inl(ioaddr);

    /* The if_port symbol can be set when the module is loaded */
    if ((if_port >= 0) && (if_port <= 3))
	dev->if_port = if_port;
    else
	printk(KERN_ERR "3c589_cs: invalid if_port requested\n");
    
    link->dev_node = &lp->node;
    SET_NETDEV_DEV(dev, &handle_to_dev(link));

    if (register_netdev(dev) != 0) {
	printk(KERN_ERR "3c589_cs: register_netdev() failed\n");
	link->dev_node = NULL;
	goto failed;
    }

    strcpy(lp->node.dev_name, dev->name);

    printk(KERN_INFO "%s: 3Com 3c%s, io %#3lx, irq %d, "
	   "hw_addr %s\n",
	   dev->name, (multi ? "562" : "589"), dev->base_addr, dev->irq,
	   print_mac(mac, dev->dev_addr));
    printk(KERN_INFO "  %dK FIFO split %s Rx:Tx, %s xcvr\n",
	   (fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3],
	   if_names[dev->if_port]);
    return 0;

cs_failed:
    cs_error(link, last_fn, last_ret);
failed:
    tc589_release(link);
    return -ENODEV;
} /* tc589_config */
Ejemplo n.º 7
0
static void media_check(u_long arg)
{
    struct el3_private *lp = (struct el3_private *)(arg);
    struct net_device *dev = &lp->dev;
    ioaddr_t ioaddr = dev->base_addr;
    u_short media, errs;
    u_long flags;

    if (!netif_device_present(dev)) goto reschedule;

    EL3WINDOW(1);
    /* Check for pending interrupt with expired latency timer: with
       this, we can limp along even if the interrupt is blocked */
    if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
	(inb(ioaddr + EL3_TIMER) == 0xff)) {
	if (!lp->fast_poll)
	    printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name);
	el3_interrupt(dev->irq, lp, NULL);
	lp->fast_poll = HZ;
    }
    if (lp->fast_poll) {
	lp->fast_poll--;
	lp->media.expires = jiffies + 1;
	add_timer(&lp->media);
	return;
    }
    
    save_flags(flags);
    cli();
    EL3WINDOW(4);
    media = inw(ioaddr+WN4_MEDIA) & 0xc810;

    /* Ignore collisions unless we've had no irq's recently */
    if (jiffies - lp->last_irq < HZ) {
	media &= ~0x0010;
    } else {
	/* Try harder to detect carrier errors */
	EL3WINDOW(6);
	outw(StatsDisable, ioaddr + EL3_CMD);
	errs = inb(ioaddr + 0);
	outw(StatsEnable, ioaddr + EL3_CMD);
	lp->stats.tx_carrier_errors += errs;
	if (errs || (lp->media_status & 0x0010)) media |= 0x0010;
    }

    if (media != lp->media_status) {
	if ((media & lp->media_status & 0x8000) &&
	    ((lp->media_status ^ media) & 0x0800))
	    printk(KERN_INFO "%s: %s link beat\n", dev->name,
		   (lp->media_status & 0x0800 ? "lost" : "found"));
	else if ((media & lp->media_status & 0x4000) &&
		 ((lp->media_status ^ media) & 0x0010))
	    printk(KERN_INFO "%s: coax cable %s\n", dev->name,
		   (lp->media_status & 0x0010 ? "ok" : "problem"));
	if (dev->if_port == 0) {
	    if (media & 0x8000) {
		if (media & 0x0800)
		    printk(KERN_INFO "%s: flipped to 10baseT\n",
			   dev->name);
		else
		    tc589_set_xcvr(dev, 2);
	    } else if (media & 0x4000) {
		if (media & 0x0010)
		    tc589_set_xcvr(dev, 1);
		else
		    printk(KERN_INFO "%s: flipped to 10base2\n",
			   dev->name);
	    }
	}
	lp->media_status = media;
    }
    
    EL3WINDOW(1);
    restore_flags(flags);

reschedule:
    lp->media.expires = jiffies + HZ;
    add_timer(&lp->media);
}
Ejemplo n.º 8
0
/* The EL3 interrupt handler. */
static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    struct el3_private *lp = dev_id;
    struct net_device *dev = &lp->dev;
    ioaddr_t ioaddr, status;
    int i = 0;
    
    if (!netif_device_present(dev))
	return;
    ioaddr = dev->base_addr;

    DEBUG(3, "%s: interrupt, status %4.4x.\n",
	  dev->name, inw(ioaddr + EL3_STATUS));
    
    while ((status = inw(ioaddr + EL3_STATUS)) &
	(IntLatch | RxComplete | StatsFull)) {
	if (!netif_device_present(dev) ||
	    ((status & 0xe000) != 0x2000)) {
	    DEBUG(1, "%s: interrupt from dead card\n", dev->name);
	    break;
	}
	
	if (status & RxComplete)
	    el3_rx(dev);
	
	if (status & TxAvailable) {
	    DEBUG(3, "    TX room bit was handled.\n");
	    /* There's room in the FIFO for a full-sized packet. */
	    outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
	    netif_wake_queue(dev);
	}
	
	if (status & TxComplete)
	    pop_tx_status(dev);

	if (status & (AdapterFailure | RxEarly | StatsFull)) {
	    /* Handle all uncommon interrupts. */
	    if (status & StatsFull)		/* Empty statistics. */
		update_stats(dev);
	    if (status & RxEarly) {		/* Rx early is unused. */
		el3_rx(dev);
		outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
	    }
	    if (status & AdapterFailure) {
		u16 fifo_diag;
		EL3WINDOW(4);
		fifo_diag = inw(ioaddr + 4);
		EL3WINDOW(1);
		printk(KERN_NOTICE "%s: adapter failure, FIFO diagnostic"
		       " register %04x.\n", dev->name, fifo_diag);
		if (fifo_diag & 0x0400) {
		    /* Tx overrun */
		    tc589_wait_for_completion(dev, TxReset);
		    outw(TxEnable, ioaddr + EL3_CMD);
		}
		if (fifo_diag & 0x2000) {
		    /* Rx underrun */
		    tc589_wait_for_completion(dev, RxReset);
		    set_multicast_list(dev);
		    outw(RxEnable, ioaddr + EL3_CMD);
		}
		outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
	    }
	}
	
	if (++i > 10) {
	    printk(KERN_NOTICE "%s: infinite loop in interrupt, "
		   "status %4.4x.\n", dev->name, status);
	    /* Clear all interrupts */
	    outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
	    break;
	}
	/* Acknowledge the IRQ. */
	outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
    }

    lp->last_irq = jiffies;
    DEBUG(3, "%s: exiting interrupt, status %4.4x.\n",
	  dev->name, inw(ioaddr + EL3_STATUS));
    return;
}
Ejemplo n.º 9
0
static void tc589_config(dev_link_t *link)
{
    client_handle_t handle = link->handle;
    struct el3_private *lp = link->priv;
    struct net_device *dev = &lp->dev;
    tuple_t tuple;
    cisparse_t parse;
    u_short buf[32], *phys_addr;
    int last_fn, last_ret, i, j, multi = 0;
    ioaddr_t ioaddr;
    char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
    
    DEBUG(0, "3c589_config(0x%p)\n", link);

    phys_addr = (u_short *)dev->dev_addr;
    tuple.Attributes = 0;
    tuple.DesiredTuple = CISTPL_CONFIG;
    CS_CHECK(GetFirstTuple, handle, &tuple);
    tuple.TupleData = (cisdata_t *)buf;
    tuple.TupleDataMax = sizeof(buf);
    tuple.TupleOffset = 0;
    CS_CHECK(GetTupleData, handle, &tuple);
    CS_CHECK(ParseTuple, handle, &tuple, &parse);
    link->conf.ConfigBase = parse.config.base;
    link->conf.Present = parse.config.rmask[0];
    
    /* Is this a 3c562? */
    tuple.DesiredTuple = CISTPL_MANFID;
    tuple.Attributes = TUPLE_RETURN_COMMON;
    if ((CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) &&
	(CardServices(GetTupleData, handle, &tuple) == CS_SUCCESS)) {
	if (le16_to_cpu(buf[0]) != MANFID_3COM)
	    printk(KERN_INFO "3c589_cs: hmmm, is this really a "
		   "3Com card??\n");
	multi = (le16_to_cpu(buf[1]) == PRODID_3COM_3C562);
    }
    
    /* Configure card */
    link->state |= DEV_CONFIG;

    /* For the 3c562, the base address must be xx00-xx7f */
    link->io.IOAddrLines = 16;
    for (i = j = 0; j < 0x400; j += 0x10) {
	if (multi && (j & 0x80)) continue;
	link->io.BasePort1 = j ^ 0x300;
	i = CardServices(RequestIO, link->handle, &link->io);
	if (i == CS_SUCCESS) break;
    }
    if (i != CS_SUCCESS) {
	cs_error(link->handle, RequestIO, i);
	goto failed;
    }
    CS_CHECK(RequestIRQ, link->handle, &link->irq);
    CS_CHECK(RequestConfiguration, link->handle, &link->conf);
	
    dev->irq = link->irq.AssignedIRQ;
    dev->base_addr = link->io.BasePort1;
    if (register_netdev(dev) != 0) {
	printk(KERN_NOTICE "3c589_cs: register_netdev() failed\n");
	goto failed;
    }
    
    ioaddr = dev->base_addr;
    EL3WINDOW(0);

    /* The 3c589 has an extra EEPROM for configuration info, including
       the hardware address.  The 3c562 puts the address in the CIS. */
    tuple.DesiredTuple = 0x88;
    if (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) {
	CardServices(GetTupleData, handle, &tuple);
	for (i = 0; i < 3; i++)
	    phys_addr[i] = htons(buf[i]);
    } else {
	for (i = 0; i < 3; i++)
	    phys_addr[i] = htons(read_eeprom(ioaddr, i));
	if (phys_addr[0] == 0x6060) {
	    printk(KERN_NOTICE "3c589_cs: IO port conflict at 0x%03lx"
		   "-0x%03lx\n", dev->base_addr, dev->base_addr+15);
	    goto failed;
	}
    }

    strcpy(lp->node.dev_name, dev->name);
    link->dev = &lp->node;
    link->state &= ~DEV_CONFIG_PENDING;
    
    /* The address and resource configuration register aren't loaded from
       the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version. */
    outw(0x3f00, ioaddr + 8);

    /* The if_port symbol can be set when the module is loaded */
    if ((if_port >= 0) && (if_port <= 3))
	dev->if_port = if_port;
    else
	printk(KERN_NOTICE "3c589_cs: invalid if_port requested\n");
    
    printk(KERN_INFO "%s: 3Com 3c%s, io %#3lx, irq %d, hw_addr ",
	   dev->name, (multi ? "562" : "589"), dev->base_addr,
	   dev->irq);
    for (i = 0; i < 6; i++)
	printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
    i = inl(ioaddr);
    printk(KERN_INFO "  %dK FIFO split %s Rx:Tx, %s xcvr\n",
	   (i & 7) ? 32 : 8, ram_split[(i >> 16) & 3],
	   if_names[dev->if_port]);
    return;

cs_failed:
    cs_error(link->handle, last_fn, last_ret);
failed:
    tc589_release((u_long)link);
    return;
    
} /* tc589_config */