diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-02-15 02:15:32 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-02-15 02:15:32 +0000 |
commit | 86464aed71025541805e7b1515541aee89879e33 (patch) | |
tree | e01a457a4912a8553bc65524aa3125d51f29f810 /drivers/net/at1700.c | |
parent | 88f99939ecc6a95a79614574cb7d95ffccfc3466 (diff) |
Merge with Linux 2.2.1.
Diffstat (limited to 'drivers/net/at1700.c')
-rw-r--r-- | drivers/net/at1700.c | 218 |
1 files changed, 144 insertions, 74 deletions
diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c index 77641a0f4..9ba40dd13 100644 --- a/drivers/net/at1700.c +++ b/drivers/net/at1700.c @@ -12,8 +12,12 @@ Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 - This is a device driver for the Allied Telesis AT1700, which is a - straight-forward Fujitsu MB86965 implementation. + This is a device driver for the Allied Telesis AT1700, and + Fujitsu FMV-181/182/181A/182A/183/184/183A/184A, which are + straight-forward Fujitsu MB86965 implementations. + + Modification for Fujitsu FMV-18X cards is done by Yutaka Tamiya + (tamy@flab.fujitsu.co.jp). Sources: The Fujitsu MB86965 datasheet. @@ -81,7 +85,7 @@ static int at1700_probe_list[] = { /* * MCA */ - +#ifdef CONFIG_MCA static int at1700_ioaddr_pattern[] = { 0x00, 0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07 }; @@ -94,6 +98,7 @@ static int at1700_irq_pattern[] = { 0x00, 0x00, 0x00, 0x30, 0x70, 0xb0, 0x00, 0x00, 0x00, 0xf0, 0x34, 0x74, 0xb4, 0x00, 0x00, 0xf4, 0x00 }; +#endif /* use 0 for production, 1 for verification, >2 for debug */ #ifndef NET_DEBUG @@ -109,6 +114,8 @@ struct net_local { unsigned char mc_filter[8]; uint jumpered:1; /* Set iff the board has jumper config. */ uint tx_started:1; /* Packets are on the Tx queue. */ + uint tx_queue_ready:1; /* Tx queue is ready to be sent. */ + uint rx_started:1; /* Packets are Rxing. */ uint invalid_irq:1; uchar tx_queue; /* Number of packet on the Tx queue. */ char mca_slot; /* -1 means ISA */ @@ -129,10 +136,13 @@ struct net_local { /* Run-time register bank 2 definitions. */ #define DATAPORT 8 /* Word-wide DMA or programmed-I/O dataport. */ #define TX_START 10 +#define COL16CNTL 11 /* Controll Reg for 16 collisions */ #define MODE13 13 /* Configuration registers only on the '865A/B chips. */ #define EEPROM_Ctrl 16 #define EEPROM_Data 17 +#define CARDSTATUS 16 /* FMV-18x Card Status */ +#define CARDSTATUS1 17 /* FMV-18x Card Status */ #define IOCONFIG 18 /* Either read the jumper, or move the I/O. */ #define IOCONFIG1 19 #define SAPROM 20 /* The station address PROM, if no EEPROM. */ @@ -214,9 +224,9 @@ int at1700_probe(struct device *dev) int at1700_probe1(struct device *dev, int ioaddr) { char fmv_irqmap[4] = {3, 7, 10, 15}; + char fmv_irqmap_pnp[8] = {3, 4, 5, 7, 9, 10, 11, 15}; char at1700_irqmap[8] = {3, 4, 5, 9, 10, 11, 14, 15}; unsigned int i, irq, is_fmv18x = 0, is_at1700 = 0; - int l_i; int slot; /* Resetting the chip doesn't reset the ISA interface, so don't bother. @@ -238,16 +248,17 @@ int at1700_probe1(struct device *dev, int ioaddr) /* redone for multi-card detection by ZP Gu (zpg@castle.net) */ /* now works as a module */ - if( MCA_bus ) { + if (MCA_bus) { int j; + int l_i; u_char pos3, pos4; - for( j = 0; at1720_mca_adapters[j].name != NULL; j ++ ) { + for (j = 0; at1720_mca_adapters[j].name != NULL; j ++) { slot = 0; - while( slot != MCA_NOTFOUND ) { + while (slot != MCA_NOTFOUND) { slot = mca_find_unused_adapter( at1720_mca_adapters[j].id, slot ); - if( slot == MCA_NOTFOUND ) break; + if (slot == MCA_NOTFOUND) break; /* if we get this far, an adapter has been detected and is enabled */ @@ -292,15 +303,16 @@ int at1700_probe1(struct device *dev, int ioaddr) && read_eeprom(ioaddr, 4) == 0x0000 && (read_eeprom(ioaddr, 5) & 0xff00) == 0xF400) is_at1700 = 1; - else if (fmv18x_probe_list[inb(ioaddr + IOCONFIG) & 0x07] == ioaddr - && inb(ioaddr + SAPROM ) == 0x00 + else if (inb(ioaddr + SAPROM ) == 0x00 && inb(ioaddr + SAPROM + 1) == 0x00 && inb(ioaddr + SAPROM + 2) == 0x0e) is_fmv18x = 1; else return -ENODEV; +#ifdef CONFIG_MCA found: +#endif /* Reset the internal state machines. */ outb(0, ioaddr + RESET); @@ -312,24 +324,46 @@ found: if (is_at1700) irq = at1700_irqmap[(read_eeprom(ioaddr, 12)&0x04) | (read_eeprom(ioaddr, 0)>>14)]; - else - if (is_fmv18x) + else { + /* Check PnP mode for FMV-183/184/183A/184A. */ + /* This PnP routine is very poor. IO and IRQ should be known. */ + if (inb(ioaddr + CARDSTATUS1) & 0x20) { + irq = dev->irq; + for (i = 0; i < 8; i++) { + if (irq == fmv_irqmap_pnp[i]) + break; + } + if (i == 8) + return -ENODEV; + } else { + if (fmv18x_probe_list[inb(ioaddr + IOCONFIG) & 0x07] != ioaddr) + return -ENODEV; irq = fmv_irqmap[(inb(ioaddr + IOCONFIG)>>6) & 0x03]; - + } + } + /* Grab the region so that we can find another board if the IRQ request fails. */ request_region(ioaddr, AT1700_IO_EXTENT, dev->name); - printk("%s: AT1700 found at %#3x, IRQ %d, address ", dev->name, - ioaddr, irq); + printk("%s: %s found at %#3x, IRQ %d, address ", dev->name, + is_at1700 ? "AT1700" : "FMV-18X", ioaddr, irq); dev->base_addr = ioaddr; dev->irq = irq; - for(i = 0; i < 3; i++) { - unsigned short eeprom_val = read_eeprom(ioaddr, 4+i); - printk("%04x", eeprom_val); - ((unsigned short *)dev->dev_addr)[i] = ntohs(eeprom_val); + if (is_at1700) { + for(i = 0; i < 3; i++) { + unsigned short eeprom_val = read_eeprom(ioaddr, 4+i); + printk("%04x", eeprom_val); + ((unsigned short *)dev->dev_addr)[i] = ntohs(eeprom_val); + } + } else { + for(i = 0; i < 6; i++) { + unsigned char val = inb(ioaddr + SAPROM + i); + printk("%02x", val); + dev->dev_addr[i] = val; + } } /* The EEPROM word 12 bit 0x0400 means use regular 100 ohm 10baseT signals, @@ -340,32 +374,44 @@ found: */ { const char *porttype[] = {"auto-sense", "10baseT", "auto-sense", "10base2"}; - ushort setup_value = read_eeprom(ioaddr, 12); - - dev->if_port = setup_value >> 8; + if (is_at1700) { + ushort setup_value = read_eeprom(ioaddr, 12); + dev->if_port = setup_value >> 8; + } else { + ushort setup_value = inb(ioaddr + CARDSTATUS); + switch (setup_value & 0x07) { + case 0x01: /* 10base5 */ + case 0x02: /* 10base2 */ + dev->if_port = 0x18; break; + case 0x04: /* 10baseT */ + dev->if_port = 0x08; break; + default: /* auto-sense */ + dev->if_port = 0x00; break; + } + } printk(" %s interface.\n", porttype[(dev->if_port>>3) & 3]); } + /* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit + bus access, two 4K Tx queues, and disabled Tx and Rx. */ + outb(0xda, ioaddr + CONFIG_0); + /* Set the station address in bank zero. */ - outb(0xe0, ioaddr + CONFIG_1); + outb(0x00, ioaddr + CONFIG_1); for (i = 0; i < 6; i++) outb(dev->dev_addr[i], ioaddr + 8 + i); /* Switch to bank 1 and set the multicast table to accept none. */ - outb(0xe4, ioaddr + CONFIG_1); + outb(0x04, ioaddr + CONFIG_1); for (i = 0; i < 8; i++) outb(0x00, ioaddr + 8 + i); - /* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit - bus access, two 4K Tx queues, and disabled Tx and Rx. */ - outb(0xda, ioaddr + CONFIG_0); - - /* Switch to bank 2 and lock our I/O address. */ - outb(0xe8, ioaddr + CONFIG_1); - outb(dev->if_port, MODE13); - /* Power-down the chip. Aren't we green! */ - outb(0x00, ioaddr + CONFIG_1); + /* Switch to bank 2 */ + /* Lock our I/O address, and set manual processing mode for 16 collisions. */ + outb(0x08, ioaddr + CONFIG_1); + outb(dev->if_port, ioaddr + MODE13); + outb(0x00, ioaddr + COL16CNTL); if (net_debug) printk(version); @@ -456,35 +502,29 @@ static int net_open(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; - int i; - - /* Powerup the chip, initialize config register 1, and select bank 0. */ - outb(0xe0, ioaddr + CONFIG_1); - - /* Set the station address in bank zero. */ - for (i = 0; i < 6; i++) - outb(dev->dev_addr[i], ioaddr + 8 + i); - - /* Switch to bank 1 and set the multicast table to accept none. */ - outb(0xe4, ioaddr + CONFIG_1); - for (i = 0; i < 8; i++) - outb(0x00, ioaddr + 8 + i); /* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit bus access, and two 4K Tx queues. */ - outb(0xda, ioaddr + CONFIG_0); + outb(0x5a, ioaddr + CONFIG_0); - /* Switch to register bank 2, enable the Rx and Tx. */ - outw(0xe85a, ioaddr + CONFIG_0); + /* Powerup, switch to register bank 2, and enable the Rx and Tx. */ + outb(0xe8, ioaddr + CONFIG_1); lp->tx_started = 0; + lp->tx_queue_ready = 1; + lp->rx_started = 0; lp->tx_queue = 0; lp->tx_queue_len = 0; - /* Turn on Rx interrupts, leave Tx interrupts off until packet Tx. */ - outb(0x00, ioaddr + TX_INTR); + /* Turn on hardware Tx and Rx interrupts. */ + outb(0x82, ioaddr + TX_INTR); outb(0x81, ioaddr + RX_INTR); + /* Enable the IRQ on boards of fmv18x it is feasible. */ + if (lp->jumpered) { + outb(0x80, ioaddr + IOCONFIG1); + } + dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; @@ -518,10 +558,14 @@ net_send_packet(struct sk_buff *skb, struct device *dev) outw(0xffff, ioaddr + 24); outw(0xffff, ioaddr + TX_STATUS); outw(0xe85a, ioaddr + CONFIG_0); - outw(0x8100, ioaddr + TX_INTR); + outw(0x8182, ioaddr + TX_INTR); + outb(0x00, ioaddr + TX_START); + outb(0x03, ioaddr + COL16CNTL); dev->tbusy=0; dev->trans_start = jiffies; lp->tx_started = 0; + lp->tx_queue_ready = 1; + lp->rx_started = 0; lp->tx_queue = 0; lp->tx_queue_len = 0; } @@ -534,14 +578,20 @@ net_send_packet(struct sk_buff *skb, struct device *dev) short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data; - /* Turn off the possible Tx interrupts. */ - outb(0x00, ioaddr + TX_INTR); - - outw(length, ioaddr + DATAPORT); - outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); - - lp->tx_queue++; - lp->tx_queue_len += length + 2; + /* We may not start transmitting unless we finish transferring + a packet into the Tx queue. During executing the following + codes we possibly catch a Tx interrupt. Thus we flag off + tx_queue_ready, so that we prevent the interrupt routine + (net_interrupt) to start transmitting. */ + lp->tx_queue_ready = 0; + { + outw(length, ioaddr + DATAPORT); + outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); + + lp->tx_queue++; + lp->tx_queue_len += length + 2; + } + lp->tx_queue_ready = 1; if (lp->tx_started == 0) { /* If the Tx is idle, always trigger a transmit. */ @@ -554,9 +604,6 @@ net_send_packet(struct sk_buff *skb, struct device *dev) } else if (lp->tx_queue_len < 4096 - 1502) /* Yes, there is room for one more packet. */ dev->tbusy = 0; - - /* Turn on Tx interrupts back on. */ - outb(0x82, ioaddr + TX_INTR); } dev_kfree_skb (skb); @@ -585,14 +632,35 @@ net_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (net_debug > 4) printk("%s: Interrupt with status %04x.\n", dev->name, status); - if (status & 0xff00 - || (inb(ioaddr + RX_MODE) & 0x40) == 0) { /* Got a packet(s). */ + if (lp->rx_started == 0 && + (status & 0xff00 || (inb(ioaddr + RX_MODE) & 0x40) == 0)) { + /* Got a packet(s). + We cannot execute net_rx more than once at the same time for + the same device. During executing net_rx, we possibly catch a + Tx interrupt. Thus we flag on rx_started, so that we prevent + the interrupt routine (net_interrupt) to dive into net_rx + again. */ + lp->rx_started = 1; + outb(0x00, ioaddr + RX_INTR); /* Disable RX intr. */ net_rx(dev); + outb(0x81, ioaddr + RX_INTR); /* Enable RX intr. */ + lp->rx_started = 0; } if (status & 0x00ff) { - if (status & 0x80) { + if (status & 0x02) { + /* More than 16 collisions occurred */ + if (net_debug > 4) + printk("%s: 16 Collision occur during Txing.\n", dev->name); + /* Cancel sending a packet. */ + outb(0x03, ioaddr + COL16CNTL); + lp->stats.collisions++; + } + if (status & 0x82) { lp->stats.tx_packets++; - if (lp->tx_queue) { + /* The Tx queue has any packets and is not being + transferred a packet from the host, start + transmitting. */ + if (lp->tx_queue && lp->tx_queue_ready) { outb(0x80 | lp->tx_queue, ioaddr + TX_START); lp->tx_queue = 0; lp->tx_queue_len = 0; @@ -601,8 +669,6 @@ net_interrupt(int irq, void *dev_id, struct pt_regs *regs) mark_bh(NET_BH); /* Inform upper layers. */ } else { lp->tx_started = 0; - /* Turn on Tx interrupts off. */ - outb(0x00, ioaddr + TX_INTR); dev->tbusy = 0; mark_bh(NET_BH); /* Inform upper layers. */ } @@ -698,7 +764,7 @@ net_rx(struct device *dev) /* The inverse routine to net_open(). */ static int net_close(struct device *dev) { -/* struct net_local *lp = (struct net_local *)dev->priv;*/ + struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; dev->tbusy = 1; @@ -709,13 +775,11 @@ static int net_close(struct device *dev) /* No statistic counters on the chip to update. */ -#if 0 - /* Disable the IRQ on boards where it is feasible. */ + /* Disable the IRQ on boards of fmv18x where it is feasible. */ if (lp->jumpered) { outb(0x00, ioaddr + IOCONFIG1); free_irq(dev->irq, dev); } -#endif /* Power-down the chip. Green, green, green! */ outb(0x00, ioaddr + CONFIG_1); @@ -820,6 +884,10 @@ static struct device dev_at1700 = { static int io = 0x260; static int irq = 0; +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(net_debug, "i"); + int init_module(void) { if (io == 0) @@ -836,12 +904,14 @@ int init_module(void) void cleanup_module(void) { +#ifdef CONFIG_MCA struct net_local *lp = dev_at1700.priv; - unregister_netdev(&dev_at1700); if(lp->mca_slot) { mca_mark_as_unused(lp->mca_slot); } +#endif + unregister_netdev(&dev_at1700); kfree(dev_at1700.priv); dev_at1700.priv = NULL; |