// SPDX-License-Identifier: GPL-2.0-only /* * linux/drivers/acorn/net/ether3.c * * Copyright (C) 1995-2000 Russell King * * SEEQ nq8005 ethernet driver for Acorn/ANT Ether3 card * for Acorn machines * * By Russell King, with some suggestions from borris@ant.co.uk * * Changelog: * 1.04 RMK 29/02/1996 Won't pass packets that are from our ethernet * address up to the higher levels - they're * silently ignored. I/F can now be put into * multicast mode. Receiver routine optimised. * 1.05 RMK 30/02/1996 Now claims interrupt at open when part of * the kernel rather than when a module. * 1.06 RMK 02/03/1996 Various code cleanups * 1.07 RMK 13/10/1996 Optimised interrupt routine and transmit * routines. * 1.08 RMK 14/10/1996 Fixed problem with too many packets, * prevented the kernel message about dropped * packets appearing too many times a second. * Now does not disable all IRQs, only the IRQ * used by this card. * 1.09 RMK 10/11/1996 Only enables TX irq when buffer space is low, * but we still service the TX queue if we get a * RX interrupt. * 1.10 RMK 15/07/1997 Fixed autoprobing of NQ8004. * 1.11 RMK 16/11/1997 Fixed autoprobing of NQ8005A. * 1.12 RMK 31/12/1997 Removed reference to dev_tint for Linux 2.1. * RMK 27/06/1998 Changed asm/delay.h to linux/delay.h. * 1.13 RMK 29/06/1998 Fixed problem with transmission of packets. * Chip seems to have a bug in, whereby if the * packet starts two bytes from the end of the * buffer, it corrupts the receiver chain, and * never updates the transmit status correctly. * 1.14 RMK 07/01/1998 Added initial code for ETHERB addressing. * 1.15 RMK 30/04/1999 More fixes to the transmit routine for buggy * hardware. * 1.16 RMK 10/02/2000 Updated for 2.3.43 * 1.17 RMK 13/05/2000 Updated for 2.3.99-pre8
*/
/* * ether3 read/write. Slow things down a bit... * The SEEQ8005 doesn't like us writing to its registers * too quickly.
*/ staticinlinevoid ether3_outb(int v, void __iomem *r)
{
writeb(v, r);
udelay(1);
}
/* * switch LED on...
*/ staticinlinevoid ether3_ledon(struct net_device *dev)
{
timer_delete(&priv(dev)->timer);
priv(dev)->timer.expires = jiffies + HZ / 50; /* leave on for 1/50th second */
add_timer(&priv(dev)->timer); if (priv(dev)->regs.config2 & CFG2_CTRLO)
ether3_outw(priv(dev)->regs.config2 &= ~CFG2_CTRLO, REG_CONFIG2);
}
/* * Read the ethernet address string from the on board rom. * This is an ascii string!!!
*/ staticint
ether3_addr(char *addr, struct expansion_card *ec)
{ struct in_chunk_dir cd; char *s;
if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) { int i; for (i = 0; i<6; i++) {
addr[i] = simple_strtoul(s + 1, &s, 0x10); if (*s != (i==5?')' : ':' )) break;
} if (i == 6) return 0;
} /* I wonder if we should even let the user continue in this case * - no, it would be better to disable the device
*/
printk(KERN_ERR "ether3: Couldn't read a valid MAC address from card.\n"); return -ENODEV;
}
for (i = 0; i < RX_END; i++) { if (buffer[i] != byte) { if (max_errors > 0 && bad != buffer[i]) {
printk("%s: RAM failed with (%02X instead of %02X) at 0x%04X",
dev->name, buffer[i], byte, i);
ret = 2;
max_errors--;
bad = i;
}
} else { if (bad != -1) { if (bad != i - 1)
printk(" - 0x%04X\n", i - 1);
printk("\n");
bad = -1;
}
}
} if (bad != -1)
printk(" - 0xffff\n");
kfree(buffer);
/* * Set up our hardware address
*/
ether3_outw(priv(dev)->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1); for (i = 0; i < 6; i++)
ether3_outb(dev->dev_addr[i], REG_BUFWIN);
/* * There is a problem with the NQ8005 in that it occasionally loses the * last two bytes. To get round this problem, we receive the CRC as * well. That way, if we do lose the last two, then it doesn't matter.
*/
ether3_outw(priv(dev)->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
ether3_outw((TX_END>>8) - 1, REG_BUFWIN);
ether3_outw(priv(dev)->rx_head, REG_RECVPTR);
ether3_outw(0, REG_TRANSMITPTR);
ether3_outw(priv(dev)->rx_head >> 8, REG_RECVEND);
ether3_outw(priv(dev)->regs.config2, REG_CONFIG2);
ether3_outw(priv(dev)->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
ether3_outw(priv(dev)->regs.command, REG_COMMAND);
i = ether3_ramtest(dev, 0x5A); if(i) return i;
i = ether3_ramtest(dev, 0x1E); if(i) return i;
/* * Open/initialize the board. This is called (in the current kernel) * sometime after booting when the 'ifconfig' program is run. * * This routine should set everything up anew at each open, even * registers that "should" only need to be set once at boot, so that * there is non-reboot way to recover if something goes wrong.
*/ staticint
ether3_open(struct net_device *dev)
{ if (request_irq(dev->irq, ether3_interrupt, 0, "ether3", dev)) return -EAGAIN;
ether3_init_for_open(dev);
netif_start_queue(dev);
return 0;
}
/* * The inverse routine to ether3_open().
*/ staticint
ether3_close(struct net_device *dev)
{
netif_stop_queue(dev);
/* * Set or clear promiscuous/multicast mode filter for this adaptor. * * We don't attempt any packet filtering. The card may have a SEEQ 8004 * in which does not have the other ethernet address registers present...
*/ staticvoid ether3_setmulticastlist(struct net_device *dev)
{
priv(dev)->regs.config1 &= ~CFG1_RECVPROMISC;
/* * If we have a good packet(s), get it/them out of the buffers.
*/ staticint ether3_rx(struct net_device *dev, unsignedint maxcnt)
{ unsignedint next_ptr = priv(dev)->rx_head, received = 0;
ether3_ledon(dev);
do { unsignedint this_ptr, status; unsignedchar addrs[16];
/* * read the first 16 bytes from the buffer. * This contains the status bytes etc and ethernet addresses, * and we also check the source ethernet address to see if * it originated from us.
*/
{ unsignedint temp_ptr;
ether3_setbuffer(dev, buffer_read, next_ptr);
temp_ptr = ether3_readword(dev);
status = ether3_readword(dev); if ((status & (RXSTAT_DONE | RXHDR_CHAINCONTINUE | RXHDR_RECEIVE)) !=
(RXSTAT_DONE | RXHDR_CHAINCONTINUE) || !temp_ptr) break;
done:
dev->stats.rx_packets += received;
priv(dev)->rx_head = next_ptr; /* * If rx went off line, then that means that the buffer may be full. We * have dropped at least one packet.
*/ if (!(ether3_inw(REG_STATUS) & STAT_RXON)) {
dev->stats.rx_dropped++;
ether3_outw(next_ptr, REG_RECVPTR);
ether3_outw(priv(dev)->regs.command | CMD_RXON, REG_COMMAND);
}
return maxcnt;
}
/* * Update stats for the transmitted packet(s)
*/ staticvoid ether3_tx(struct net_device *dev)
{ unsignedint tx_tail = priv(dev)->tx_tail; int max_work = 14;
do { unsignedlong status;
/* * Read the packet header
*/
ether3_setbuffer(dev, buffer_read, tx_tail * 0x600);
status = ether3_readlong(dev);
/* * Check to see if this packet has been transmitted
*/ if ((status & (TXSTAT_DONE | TXHDR_TRANSMIT)) !=
(TXSTAT_DONE | TXHDR_TRANSMIT)) break;
/* * Update errors
*/ if (!(status & (TXSTAT_BABBLED | TXSTAT_16COLLISIONS)))
dev->stats.tx_packets++; else {
dev->stats.tx_errors++; if (status & TXSTAT_16COLLISIONS)
dev->stats.collisions += 16; if (status & TXSTAT_BABBLED)
dev->stats.tx_fifo_errors++;
}
tx_tail = (tx_tail + 1) & 15;
} while (--max_work);
/* Test using Receive Pointer (16-bit register) to find out * how the ether3 is connected to the bus...
*/ if (ether3_probe_bus_8(dev, 0x100) &&
ether3_probe_bus_8(dev, 0x201))
bus_type = BUS_8;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.