xref: /linux/drivers/net/ethernet/cirrus/mac89x0.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
10a3360e1SGeert Uytterhoeven /* mac89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */
20a3360e1SGeert Uytterhoeven /*
30a3360e1SGeert Uytterhoeven 	Written 1996 by Russell Nelson, with reference to skeleton.c
40a3360e1SGeert Uytterhoeven 	written 1993-1994 by Donald Becker.
50a3360e1SGeert Uytterhoeven 
60a3360e1SGeert Uytterhoeven 	This software may be used and distributed according to the terms
70a3360e1SGeert Uytterhoeven 	of the GNU General Public License, incorporated herein by reference.
80a3360e1SGeert Uytterhoeven 
90a3360e1SGeert Uytterhoeven 	The author may be reached at nelson@crynwr.com, Crynwr
100a3360e1SGeert Uytterhoeven 	Software, 11 Grant St., Potsdam, NY 13676
110a3360e1SGeert Uytterhoeven 
120a3360e1SGeert Uytterhoeven   Changelog:
130a3360e1SGeert Uytterhoeven 
140a3360e1SGeert Uytterhoeven   Mike Cruse        : mcruse@cti-ltd.com
150a3360e1SGeert Uytterhoeven                     : Changes for Linux 2.0 compatibility.
160a3360e1SGeert Uytterhoeven                     : Added dev_id parameter in net_interrupt(),
170a3360e1SGeert Uytterhoeven                     : request_irq() and free_irq(). Just NULL for now.
180a3360e1SGeert Uytterhoeven 
190a3360e1SGeert Uytterhoeven   Mike Cruse        : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros
200a3360e1SGeert Uytterhoeven                     : in net_open() and net_close() so kerneld would know
210a3360e1SGeert Uytterhoeven                     : that the module is in use and wouldn't eject the
220a3360e1SGeert Uytterhoeven                     : driver prematurely.
230a3360e1SGeert Uytterhoeven 
240a3360e1SGeert Uytterhoeven   Mike Cruse        : Rewrote init_module() and cleanup_module using 8390.c
250a3360e1SGeert Uytterhoeven                     : as an example. Disabled autoprobing in init_module(),
260a3360e1SGeert Uytterhoeven                     : not a good thing to do to other devices while Linux
270a3360e1SGeert Uytterhoeven                     : is running from all accounts.
280a3360e1SGeert Uytterhoeven 
290a3360e1SGeert Uytterhoeven   Alan Cox          : Removed 1.2 support, added 2.1 extra counters.
300a3360e1SGeert Uytterhoeven 
310a3360e1SGeert Uytterhoeven   David Huggins-Daines <dhd@debian.org>
320a3360e1SGeert Uytterhoeven 
330a3360e1SGeert Uytterhoeven   Split this off into mac89x0.c, and gutted it of all parts which are
340a3360e1SGeert Uytterhoeven   not relevant to the existing CS8900 cards on the Macintosh
350a3360e1SGeert Uytterhoeven   (i.e. basically the Daynaport CS and LC cards).  To be precise:
360a3360e1SGeert Uytterhoeven 
370a3360e1SGeert Uytterhoeven     * Removed all the media-detection stuff, because these cards are
380a3360e1SGeert Uytterhoeven     TP-only.
390a3360e1SGeert Uytterhoeven 
400a3360e1SGeert Uytterhoeven     * Lobotomized the ISA interrupt bogosity, because these cards use
410a3360e1SGeert Uytterhoeven     a hardwired NuBus interrupt and a magic ISAIRQ value in the card.
420a3360e1SGeert Uytterhoeven 
430a3360e1SGeert Uytterhoeven     * Basically eliminated everything not relevant to getting the
440a3360e1SGeert Uytterhoeven     cards minimally functioning on the Macintosh.
450a3360e1SGeert Uytterhoeven 
460a3360e1SGeert Uytterhoeven   I might add that these cards are badly designed even from the Mac
470a3360e1SGeert Uytterhoeven   standpoint, in that Dayna, in their infinite wisdom, used NuBus slot
480a3360e1SGeert Uytterhoeven   I/O space and NuBus interrupts for these cards, but neglected to
490a3360e1SGeert Uytterhoeven   provide anything even remotely resembling a NuBus ROM.  Therefore we
500a3360e1SGeert Uytterhoeven   have to probe for them in a brain-damaged ISA-like fashion.
510a3360e1SGeert Uytterhoeven 
520a3360e1SGeert Uytterhoeven   Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001
530a3360e1SGeert Uytterhoeven   check kmalloc and release the allocated memory on failure in
540a3360e1SGeert Uytterhoeven   mac89x0_probe and in init_module
550a3360e1SGeert Uytterhoeven   use local_irq_{save,restore}(flags) in net_get_stat, not just
560a3360e1SGeert Uytterhoeven   local_irq_{dis,en}able()
570a3360e1SGeert Uytterhoeven */
580a3360e1SGeert Uytterhoeven 
5986c2666eSFinn Thain #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
6086c2666eSFinn Thain 
6106324664SKees Cook static const char version[] =
620a3360e1SGeert Uytterhoeven "cs89x0.c:v1.02 11/26/96 Russell Nelson <nelson@crynwr.com>\n";
630a3360e1SGeert Uytterhoeven 
640a3360e1SGeert Uytterhoeven #include <linux/module.h>
650a3360e1SGeert Uytterhoeven 
660a3360e1SGeert Uytterhoeven /*
670a3360e1SGeert Uytterhoeven   Sources:
680a3360e1SGeert Uytterhoeven 
690a3360e1SGeert Uytterhoeven 	Crynwr packet driver epktisa.
700a3360e1SGeert Uytterhoeven 
710a3360e1SGeert Uytterhoeven 	Crystal Semiconductor data sheets.
720a3360e1SGeert Uytterhoeven 
730a3360e1SGeert Uytterhoeven */
740a3360e1SGeert Uytterhoeven 
750a3360e1SGeert Uytterhoeven #include <linux/kernel.h>
760a3360e1SGeert Uytterhoeven #include <linux/types.h>
770a3360e1SGeert Uytterhoeven #include <linux/fcntl.h>
780a3360e1SGeert Uytterhoeven #include <linux/interrupt.h>
790a3360e1SGeert Uytterhoeven #include <linux/ioport.h>
800a3360e1SGeert Uytterhoeven #include <linux/in.h>
810a3360e1SGeert Uytterhoeven #include <linux/string.h>
820a3360e1SGeert Uytterhoeven #include <linux/nubus.h>
830a3360e1SGeert Uytterhoeven #include <linux/errno.h>
840a3360e1SGeert Uytterhoeven #include <linux/init.h>
850a3360e1SGeert Uytterhoeven #include <linux/netdevice.h>
8643bf2e6dSFinn Thain #include <linux/platform_device.h>
870a3360e1SGeert Uytterhoeven #include <linux/etherdevice.h>
880a3360e1SGeert Uytterhoeven #include <linux/skbuff.h>
890a3360e1SGeert Uytterhoeven #include <linux/delay.h>
900a3360e1SGeert Uytterhoeven #include <linux/bitops.h>
910a3360e1SGeert Uytterhoeven #include <linux/gfp.h>
920a3360e1SGeert Uytterhoeven 
930a3360e1SGeert Uytterhoeven #include <asm/io.h>
940a3360e1SGeert Uytterhoeven #include <asm/hwtest.h>
950a3360e1SGeert Uytterhoeven #include <asm/macints.h>
960a3360e1SGeert Uytterhoeven 
970a3360e1SGeert Uytterhoeven #include "cs89x0.h"
980a3360e1SGeert Uytterhoeven 
995a3b7504SFinn Thain static int debug = -1;
10043bf2e6dSFinn Thain module_param(debug, int, 0);
1015a3b7504SFinn Thain MODULE_PARM_DESC(debug, "debug message level");
1020a3360e1SGeert Uytterhoeven 
1030a3360e1SGeert Uytterhoeven /* Information that need to be kept for each board. */
1040a3360e1SGeert Uytterhoeven struct net_local {
1055a3b7504SFinn Thain 	int msg_enable;
1060a3360e1SGeert Uytterhoeven 	int chip_type;		/* one of: CS8900, CS8920, CS8920M */
1070a3360e1SGeert Uytterhoeven 	char chip_revision;	/* revision letter of the chip ('A'...) */
1080a3360e1SGeert Uytterhoeven 	int send_cmd;		/* the propercommand used to send a packet. */
1090a3360e1SGeert Uytterhoeven 	int rx_mode;
1100a3360e1SGeert Uytterhoeven 	int curr_rx_cfg;
1110a3360e1SGeert Uytterhoeven         int send_underrun;      /* keep track of how many underruns in a row we get */
1120a3360e1SGeert Uytterhoeven };
1130a3360e1SGeert Uytterhoeven 
1140a3360e1SGeert Uytterhoeven /* Index to functions, as function prototypes. */
1150a3360e1SGeert Uytterhoeven static int net_open(struct net_device *dev);
116f3bf939fSYueHaibing static netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev);
1170a3360e1SGeert Uytterhoeven static irqreturn_t net_interrupt(int irq, void *dev_id);
1180a3360e1SGeert Uytterhoeven static void set_multicast_list(struct net_device *dev);
1190a3360e1SGeert Uytterhoeven static void net_rx(struct net_device *dev);
1200a3360e1SGeert Uytterhoeven static int net_close(struct net_device *dev);
1210a3360e1SGeert Uytterhoeven static struct net_device_stats *net_get_stats(struct net_device *dev);
1220a3360e1SGeert Uytterhoeven static int set_mac_address(struct net_device *dev, void *addr);
1230a3360e1SGeert Uytterhoeven 
1240a3360e1SGeert Uytterhoeven /* For reading/writing registers ISA-style */
1250a3360e1SGeert Uytterhoeven static inline int
readreg_io(struct net_device * dev,int portno)1260a3360e1SGeert Uytterhoeven readreg_io(struct net_device *dev, int portno)
1270a3360e1SGeert Uytterhoeven {
1280a3360e1SGeert Uytterhoeven 	nubus_writew(swab16(portno), dev->base_addr + ADD_PORT);
1290a3360e1SGeert Uytterhoeven 	return swab16(nubus_readw(dev->base_addr + DATA_PORT));
1300a3360e1SGeert Uytterhoeven }
1310a3360e1SGeert Uytterhoeven 
1320a3360e1SGeert Uytterhoeven static inline void
writereg_io(struct net_device * dev,int portno,int value)1330a3360e1SGeert Uytterhoeven writereg_io(struct net_device *dev, int portno, int value)
1340a3360e1SGeert Uytterhoeven {
1350a3360e1SGeert Uytterhoeven 	nubus_writew(swab16(portno), dev->base_addr + ADD_PORT);
1360a3360e1SGeert Uytterhoeven 	nubus_writew(swab16(value), dev->base_addr + DATA_PORT);
1370a3360e1SGeert Uytterhoeven }
1380a3360e1SGeert Uytterhoeven 
1390a3360e1SGeert Uytterhoeven /* These are for reading/writing registers in shared memory */
1400a3360e1SGeert Uytterhoeven static inline int
readreg(struct net_device * dev,int portno)1410a3360e1SGeert Uytterhoeven readreg(struct net_device *dev, int portno)
1420a3360e1SGeert Uytterhoeven {
1430a3360e1SGeert Uytterhoeven 	return swab16(nubus_readw(dev->mem_start + portno));
1440a3360e1SGeert Uytterhoeven }
1450a3360e1SGeert Uytterhoeven 
1460a3360e1SGeert Uytterhoeven static inline void
writereg(struct net_device * dev,int portno,int value)1470a3360e1SGeert Uytterhoeven writereg(struct net_device *dev, int portno, int value)
1480a3360e1SGeert Uytterhoeven {
1490a3360e1SGeert Uytterhoeven 	nubus_writew(swab16(value), dev->mem_start + portno);
1500a3360e1SGeert Uytterhoeven }
1510a3360e1SGeert Uytterhoeven 
1520a3360e1SGeert Uytterhoeven static const struct net_device_ops mac89x0_netdev_ops = {
1530a3360e1SGeert Uytterhoeven 	.ndo_open		= net_open,
1540a3360e1SGeert Uytterhoeven 	.ndo_stop		= net_close,
1550a3360e1SGeert Uytterhoeven 	.ndo_start_xmit		= net_send_packet,
1560a3360e1SGeert Uytterhoeven 	.ndo_get_stats		= net_get_stats,
1570a3360e1SGeert Uytterhoeven 	.ndo_set_rx_mode	= set_multicast_list,
1580a3360e1SGeert Uytterhoeven 	.ndo_set_mac_address	= set_mac_address,
1590a3360e1SGeert Uytterhoeven 	.ndo_validate_addr	= eth_validate_addr,
1600a3360e1SGeert Uytterhoeven };
1610a3360e1SGeert Uytterhoeven 
1620a3360e1SGeert Uytterhoeven /* Probe for the CS8900 card in slot E.  We won't bother looking
1630a3360e1SGeert Uytterhoeven    anywhere else until we have a really good reason to do so. */
mac89x0_device_probe(struct platform_device * pdev)16443bf2e6dSFinn Thain static int mac89x0_device_probe(struct platform_device *pdev)
1650a3360e1SGeert Uytterhoeven {
1660a3360e1SGeert Uytterhoeven 	struct net_device *dev;
1670a3360e1SGeert Uytterhoeven 	struct net_local *lp;
1680a3360e1SGeert Uytterhoeven 	int i, slot;
1690a3360e1SGeert Uytterhoeven 	unsigned rev_type = 0;
1700a3360e1SGeert Uytterhoeven 	unsigned long ioaddr;
1710a3360e1SGeert Uytterhoeven 	unsigned short sig;
1720a3360e1SGeert Uytterhoeven 	int err = -ENODEV;
17341b84816SFinn Thain 	struct nubus_rsrc *fres;
1740a3360e1SGeert Uytterhoeven 
1750a3360e1SGeert Uytterhoeven 	dev = alloc_etherdev(sizeof(struct net_local));
1760a3360e1SGeert Uytterhoeven 	if (!dev)
17743bf2e6dSFinn Thain 		return -ENOMEM;
1780a3360e1SGeert Uytterhoeven 
1790a3360e1SGeert Uytterhoeven 	/* We might have to parameterize this later */
1800a3360e1SGeert Uytterhoeven 	slot = 0xE;
1810a3360e1SGeert Uytterhoeven 	/* Get out now if there's a real NuBus card in slot E */
18241b84816SFinn Thain 	for_each_func_rsrc(fres)
18341b84816SFinn Thain 		if (fres->board->slot == slot)
1840a3360e1SGeert Uytterhoeven 			goto out;
1850a3360e1SGeert Uytterhoeven 
1860a3360e1SGeert Uytterhoeven 	/* The pseudo-ISA bits always live at offset 0x300 (gee,
1870a3360e1SGeert Uytterhoeven            wonder why...) */
1880a3360e1SGeert Uytterhoeven 	ioaddr = (unsigned long)
1890a3360e1SGeert Uytterhoeven 		nubus_slot_addr(slot) | (((slot&0xf) << 20) + DEFAULTIOBASE);
1900a3360e1SGeert Uytterhoeven 	{
1910a3360e1SGeert Uytterhoeven 		int card_present;
1920a3360e1SGeert Uytterhoeven 
1930a3360e1SGeert Uytterhoeven 		card_present = (hwreg_present((void *)ioaddr + 4) &&
1940a3360e1SGeert Uytterhoeven 				hwreg_present((void *)ioaddr + DATA_PORT));
1950a3360e1SGeert Uytterhoeven 		if (!card_present)
1960a3360e1SGeert Uytterhoeven 			goto out;
1970a3360e1SGeert Uytterhoeven 	}
1980a3360e1SGeert Uytterhoeven 
1990a3360e1SGeert Uytterhoeven 	nubus_writew(0, ioaddr + ADD_PORT);
2000a3360e1SGeert Uytterhoeven 	sig = nubus_readw(ioaddr + DATA_PORT);
2010a3360e1SGeert Uytterhoeven 	if (sig != swab16(CHIP_EISA_ID_SIG))
2020a3360e1SGeert Uytterhoeven 		goto out;
2030a3360e1SGeert Uytterhoeven 
20443bf2e6dSFinn Thain 	SET_NETDEV_DEV(dev, &pdev->dev);
20543bf2e6dSFinn Thain 
2060a3360e1SGeert Uytterhoeven 	/* Initialize the net_device structure. */
2070a3360e1SGeert Uytterhoeven 	lp = netdev_priv(dev);
2080a3360e1SGeert Uytterhoeven 
2095a3b7504SFinn Thain 	lp->msg_enable = netif_msg_init(debug, 0);
2105a3b7504SFinn Thain 
2110a3360e1SGeert Uytterhoeven 	/* Fill in the 'dev' fields. */
2120a3360e1SGeert Uytterhoeven 	dev->base_addr = ioaddr;
2130a3360e1SGeert Uytterhoeven 	dev->mem_start = (unsigned long)
2140a3360e1SGeert Uytterhoeven 		nubus_slot_addr(slot) | (((slot&0xf) << 20) + MMIOBASE);
2150a3360e1SGeert Uytterhoeven 	dev->mem_end = dev->mem_start + 0x1000;
2160a3360e1SGeert Uytterhoeven 
2170a3360e1SGeert Uytterhoeven 	/* Turn on shared memory */
2180a3360e1SGeert Uytterhoeven 	writereg_io(dev, PP_BusCTL, MEMORY_ON);
2190a3360e1SGeert Uytterhoeven 
2200a3360e1SGeert Uytterhoeven 	/* get the chip type */
2210a3360e1SGeert Uytterhoeven 	rev_type = readreg(dev, PRODUCT_ID_ADD);
2220a3360e1SGeert Uytterhoeven 	lp->chip_type = rev_type &~ REVISON_BITS;
2230a3360e1SGeert Uytterhoeven 	lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
2240a3360e1SGeert Uytterhoeven 
2250a3360e1SGeert Uytterhoeven 	/* Check the chip type and revision in order to set the correct send command
2260a3360e1SGeert Uytterhoeven 	CS8920 revision C and CS8900 revision F can use the faster send. */
2270a3360e1SGeert Uytterhoeven 	lp->send_cmd = TX_AFTER_381;
2280a3360e1SGeert Uytterhoeven 	if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
2290a3360e1SGeert Uytterhoeven 		lp->send_cmd = TX_NOW;
2300a3360e1SGeert Uytterhoeven 	if (lp->chip_type != CS8900 && lp->chip_revision >= 'C')
2310a3360e1SGeert Uytterhoeven 		lp->send_cmd = TX_NOW;
2320a3360e1SGeert Uytterhoeven 
2335a3b7504SFinn Thain 	netif_dbg(lp, drv, dev, "%s", version);
2340a3360e1SGeert Uytterhoeven 
23586c2666eSFinn Thain 	pr_info("cs89%c0%s rev %c found at %#8lx\n",
2360a3360e1SGeert Uytterhoeven 		lp->chip_type == CS8900 ? '0' : '2',
2370a3360e1SGeert Uytterhoeven 		lp->chip_type == CS8920M ? "M" : "",
23886c2666eSFinn Thain 		lp->chip_revision, dev->base_addr);
2390a3360e1SGeert Uytterhoeven 
2400a3360e1SGeert Uytterhoeven 	/* Try to read the MAC address */
2410a3360e1SGeert Uytterhoeven 	if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) {
24286c2666eSFinn Thain 		pr_info("No EEPROM, giving up now.\n");
2430a3360e1SGeert Uytterhoeven 		goto out1;
2440a3360e1SGeert Uytterhoeven         } else {
2459a962aedSJakub Kicinski 		u8 addr[ETH_ALEN];
2469a962aedSJakub Kicinski 
2470a3360e1SGeert Uytterhoeven                 for (i = 0; i < ETH_ALEN; i += 2) {
2480a3360e1SGeert Uytterhoeven 			/* Big-endian (why??!) */
2490a3360e1SGeert Uytterhoeven 			unsigned short s = readreg(dev, PP_IA + i);
2509a962aedSJakub Kicinski 			addr[i] = s >> 8;
2519a962aedSJakub Kicinski 			addr[i+1] = s & 0xff;
2520a3360e1SGeert Uytterhoeven                 }
2539a962aedSJakub Kicinski 		eth_hw_addr_set(dev, addr);
2540a3360e1SGeert Uytterhoeven         }
2550a3360e1SGeert Uytterhoeven 
2560a3360e1SGeert Uytterhoeven 	dev->irq = SLOT2IRQ(slot);
2570a3360e1SGeert Uytterhoeven 
2580a3360e1SGeert Uytterhoeven 	/* print the IRQ and ethernet address. */
2590a3360e1SGeert Uytterhoeven 
26086c2666eSFinn Thain 	pr_info("MAC %pM, IRQ %d\n", dev->dev_addr, dev->irq);
2610a3360e1SGeert Uytterhoeven 
2620a3360e1SGeert Uytterhoeven 	dev->netdev_ops		= &mac89x0_netdev_ops;
2630a3360e1SGeert Uytterhoeven 
2640a3360e1SGeert Uytterhoeven 	err = register_netdev(dev);
2650a3360e1SGeert Uytterhoeven 	if (err)
2660a3360e1SGeert Uytterhoeven 		goto out1;
26743bf2e6dSFinn Thain 
26843bf2e6dSFinn Thain 	platform_set_drvdata(pdev, dev);
26943bf2e6dSFinn Thain 	return 0;
2700a3360e1SGeert Uytterhoeven out1:
2710a3360e1SGeert Uytterhoeven 	nubus_writew(0, dev->base_addr + ADD_PORT);
2720a3360e1SGeert Uytterhoeven out:
2730a3360e1SGeert Uytterhoeven 	free_netdev(dev);
27443bf2e6dSFinn Thain 	return err;
2750a3360e1SGeert Uytterhoeven }
2760a3360e1SGeert Uytterhoeven 
2770a3360e1SGeert Uytterhoeven /* Open/initialize the board.  This is called (in the current kernel)
2780a3360e1SGeert Uytterhoeven    sometime after booting when the 'ifconfig' program is run.
2790a3360e1SGeert Uytterhoeven 
2800a3360e1SGeert Uytterhoeven    This routine should set everything up anew at each open, even
2810a3360e1SGeert Uytterhoeven    registers that "should" only need to be set once at boot, so that
2820a3360e1SGeert Uytterhoeven    there is non-reboot way to recover if something goes wrong.
2830a3360e1SGeert Uytterhoeven    */
2840a3360e1SGeert Uytterhoeven static int
net_open(struct net_device * dev)2850a3360e1SGeert Uytterhoeven net_open(struct net_device *dev)
2860a3360e1SGeert Uytterhoeven {
2870a3360e1SGeert Uytterhoeven 	struct net_local *lp = netdev_priv(dev);
2880a3360e1SGeert Uytterhoeven 	int i;
2890a3360e1SGeert Uytterhoeven 
2900a3360e1SGeert Uytterhoeven 	/* Disable the interrupt for now */
2910a3360e1SGeert Uytterhoeven 	writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ);
2920a3360e1SGeert Uytterhoeven 
2930a3360e1SGeert Uytterhoeven 	/* Grab the interrupt */
2940a3360e1SGeert Uytterhoeven 	if (request_irq(dev->irq, net_interrupt, 0, "cs89x0", dev))
2950a3360e1SGeert Uytterhoeven 		return -EAGAIN;
2960a3360e1SGeert Uytterhoeven 
2970a3360e1SGeert Uytterhoeven 	/* Set up the IRQ - Apparently magic */
2980a3360e1SGeert Uytterhoeven 	if (lp->chip_type == CS8900)
2990a3360e1SGeert Uytterhoeven 		writereg(dev, PP_CS8900_ISAINT, 0);
3000a3360e1SGeert Uytterhoeven 	else
3010a3360e1SGeert Uytterhoeven 		writereg(dev, PP_CS8920_ISAINT, 0);
3020a3360e1SGeert Uytterhoeven 
3030a3360e1SGeert Uytterhoeven 	/* set the Ethernet address */
3040a3360e1SGeert Uytterhoeven 	for (i=0; i < ETH_ALEN/2; i++)
3050a3360e1SGeert Uytterhoeven 		writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
3060a3360e1SGeert Uytterhoeven 
3070a3360e1SGeert Uytterhoeven 	/* Turn on both receive and transmit operations */
3080a3360e1SGeert Uytterhoeven 	writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
3090a3360e1SGeert Uytterhoeven 
3100a3360e1SGeert Uytterhoeven 	/* Receive only error free packets addressed to this card */
3110a3360e1SGeert Uytterhoeven 	lp->rx_mode = 0;
3120a3360e1SGeert Uytterhoeven 	writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
3130a3360e1SGeert Uytterhoeven 
3140a3360e1SGeert Uytterhoeven 	lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
3150a3360e1SGeert Uytterhoeven 
3160a3360e1SGeert Uytterhoeven 	writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
3170a3360e1SGeert Uytterhoeven 
3180a3360e1SGeert Uytterhoeven 	writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
3190a3360e1SGeert Uytterhoeven 	       TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
3200a3360e1SGeert Uytterhoeven 
3210a3360e1SGeert Uytterhoeven 	writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
3220a3360e1SGeert Uytterhoeven 		 TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
3230a3360e1SGeert Uytterhoeven 
3240a3360e1SGeert Uytterhoeven 	/* now that we've got our act together, enable everything */
3250a3360e1SGeert Uytterhoeven 	writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ);
3260a3360e1SGeert Uytterhoeven 	netif_start_queue(dev);
3270a3360e1SGeert Uytterhoeven 	return 0;
3280a3360e1SGeert Uytterhoeven }
3290a3360e1SGeert Uytterhoeven 
330f3bf939fSYueHaibing static netdev_tx_t
net_send_packet(struct sk_buff * skb,struct net_device * dev)3310a3360e1SGeert Uytterhoeven net_send_packet(struct sk_buff *skb, struct net_device *dev)
3320a3360e1SGeert Uytterhoeven {
3330a3360e1SGeert Uytterhoeven 	struct net_local *lp = netdev_priv(dev);
3340a3360e1SGeert Uytterhoeven 	unsigned long flags;
3350a3360e1SGeert Uytterhoeven 
3365a3b7504SFinn Thain 	netif_dbg(lp, tx_queued, dev, "sent %d byte packet of type %x\n",
3375a3b7504SFinn Thain 		  skb->len, skb->data[ETH_ALEN + ETH_ALEN] << 8 |
3385a3b7504SFinn Thain 		  skb->data[ETH_ALEN + ETH_ALEN + 1]);
3390a3360e1SGeert Uytterhoeven 
3400a3360e1SGeert Uytterhoeven 	/* keep the upload from being interrupted, since we
3410a3360e1SGeert Uytterhoeven 	   ask the chip to start transmitting before the
3420a3360e1SGeert Uytterhoeven 	   whole packet has been completely uploaded. */
3430a3360e1SGeert Uytterhoeven 	local_irq_save(flags);
3440a3360e1SGeert Uytterhoeven 	netif_stop_queue(dev);
3450a3360e1SGeert Uytterhoeven 
3460a3360e1SGeert Uytterhoeven 	/* initiate a transmit sequence */
3470a3360e1SGeert Uytterhoeven 	writereg(dev, PP_TxCMD, lp->send_cmd);
3480a3360e1SGeert Uytterhoeven 	writereg(dev, PP_TxLength, skb->len);
3490a3360e1SGeert Uytterhoeven 
3500a3360e1SGeert Uytterhoeven 	/* Test to see if the chip has allocated memory for the packet */
3510a3360e1SGeert Uytterhoeven 	if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
3520a3360e1SGeert Uytterhoeven 		/* Gasp!  It hasn't.  But that shouldn't happen since
3530a3360e1SGeert Uytterhoeven 		   we're waiting for TxOk, so return 1 and requeue this packet. */
3540a3360e1SGeert Uytterhoeven 		local_irq_restore(flags);
3550a3360e1SGeert Uytterhoeven 		return NETDEV_TX_BUSY;
3560a3360e1SGeert Uytterhoeven 	}
3570a3360e1SGeert Uytterhoeven 
3580a3360e1SGeert Uytterhoeven 	/* Write the contents of the packet */
3590a3360e1SGeert Uytterhoeven 	skb_copy_from_linear_data(skb, (void *)(dev->mem_start + PP_TxFrame),
3600a3360e1SGeert Uytterhoeven 				  skb->len+1);
3610a3360e1SGeert Uytterhoeven 
3620a3360e1SGeert Uytterhoeven 	local_irq_restore(flags);
3630a3360e1SGeert Uytterhoeven 	dev_kfree_skb (skb);
3640a3360e1SGeert Uytterhoeven 
3650a3360e1SGeert Uytterhoeven 	return NETDEV_TX_OK;
3660a3360e1SGeert Uytterhoeven }
3670a3360e1SGeert Uytterhoeven 
3680a3360e1SGeert Uytterhoeven /* The typical workload of the driver:
3690a3360e1SGeert Uytterhoeven    Handle the network interface interrupts. */
net_interrupt(int irq,void * dev_id)3700a3360e1SGeert Uytterhoeven static irqreturn_t net_interrupt(int irq, void *dev_id)
3710a3360e1SGeert Uytterhoeven {
3720a3360e1SGeert Uytterhoeven 	struct net_device *dev = dev_id;
3730a3360e1SGeert Uytterhoeven 	struct net_local *lp;
3740a3360e1SGeert Uytterhoeven 	int ioaddr, status;
3750a3360e1SGeert Uytterhoeven 
3760a3360e1SGeert Uytterhoeven 	ioaddr = dev->base_addr;
3770a3360e1SGeert Uytterhoeven 	lp = netdev_priv(dev);
3780a3360e1SGeert Uytterhoeven 
3790a3360e1SGeert Uytterhoeven 	/* we MUST read all the events out of the ISQ, otherwise we'll never
3800a3360e1SGeert Uytterhoeven            get interrupted again.  As a consequence, we can't have any limit
3810a3360e1SGeert Uytterhoeven            on the number of times we loop in the interrupt handler.  The
3820a3360e1SGeert Uytterhoeven            hardware guarantees that eventually we'll run out of events.  Of
3830a3360e1SGeert Uytterhoeven            course, if you're on a slow machine, and packets are arriving
3840a3360e1SGeert Uytterhoeven            faster than you can read them off, you're screwed.  Hasta la
3850a3360e1SGeert Uytterhoeven            vista, baby!  */
3860a3360e1SGeert Uytterhoeven 	while ((status = swab16(nubus_readw(dev->base_addr + ISQ_PORT)))) {
3875a3b7504SFinn Thain 		netif_dbg(lp, intr, dev, "status=%04x\n", status);
3880a3360e1SGeert Uytterhoeven 		switch(status & ISQ_EVENT_MASK) {
3890a3360e1SGeert Uytterhoeven 		case ISQ_RECEIVER_EVENT:
3900a3360e1SGeert Uytterhoeven 			/* Got a packet(s). */
3910a3360e1SGeert Uytterhoeven 			net_rx(dev);
3920a3360e1SGeert Uytterhoeven 			break;
3930a3360e1SGeert Uytterhoeven 		case ISQ_TRANSMITTER_EVENT:
3940a3360e1SGeert Uytterhoeven 			dev->stats.tx_packets++;
3950a3360e1SGeert Uytterhoeven 			netif_wake_queue(dev);
3960a3360e1SGeert Uytterhoeven 			if ((status & TX_OK) == 0)
3970a3360e1SGeert Uytterhoeven 				dev->stats.tx_errors++;
3980a3360e1SGeert Uytterhoeven 			if (status & TX_LOST_CRS)
3990a3360e1SGeert Uytterhoeven 				dev->stats.tx_carrier_errors++;
4000a3360e1SGeert Uytterhoeven 			if (status & TX_SQE_ERROR)
4010a3360e1SGeert Uytterhoeven 				dev->stats.tx_heartbeat_errors++;
4020a3360e1SGeert Uytterhoeven 			if (status & TX_LATE_COL)
4030a3360e1SGeert Uytterhoeven 				dev->stats.tx_window_errors++;
4040a3360e1SGeert Uytterhoeven 			if (status & TX_16_COL)
4050a3360e1SGeert Uytterhoeven 				dev->stats.tx_aborted_errors++;
4060a3360e1SGeert Uytterhoeven 			break;
4070a3360e1SGeert Uytterhoeven 		case ISQ_BUFFER_EVENT:
4080a3360e1SGeert Uytterhoeven 			if (status & READY_FOR_TX) {
4090a3360e1SGeert Uytterhoeven 				/* we tried to transmit a packet earlier,
4100a3360e1SGeert Uytterhoeven                                    but inexplicably ran out of buffers.
4110a3360e1SGeert Uytterhoeven                                    That shouldn't happen since we only ever
4120a3360e1SGeert Uytterhoeven                                    load one packet.  Shrug.  Do the right
4130a3360e1SGeert Uytterhoeven                                    thing anyway. */
4140a3360e1SGeert Uytterhoeven 				netif_wake_queue(dev);
4150a3360e1SGeert Uytterhoeven 			}
4160a3360e1SGeert Uytterhoeven 			if (status & TX_UNDERRUN) {
4175a3b7504SFinn Thain 				netif_dbg(lp, tx_err, dev, "transmit underrun\n");
4180a3360e1SGeert Uytterhoeven                                 lp->send_underrun++;
4190a3360e1SGeert Uytterhoeven                                 if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381;
4200a3360e1SGeert Uytterhoeven                                 else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL;
4210a3360e1SGeert Uytterhoeven                         }
4220a3360e1SGeert Uytterhoeven 			break;
4230a3360e1SGeert Uytterhoeven 		case ISQ_RX_MISS_EVENT:
4240a3360e1SGeert Uytterhoeven 			dev->stats.rx_missed_errors += (status >> 6);
4250a3360e1SGeert Uytterhoeven 			break;
4260a3360e1SGeert Uytterhoeven 		case ISQ_TX_COL_EVENT:
4270a3360e1SGeert Uytterhoeven 			dev->stats.collisions += (status >> 6);
4280a3360e1SGeert Uytterhoeven 			break;
4290a3360e1SGeert Uytterhoeven 		}
4300a3360e1SGeert Uytterhoeven 	}
4310a3360e1SGeert Uytterhoeven 	return IRQ_HANDLED;
4320a3360e1SGeert Uytterhoeven }
4330a3360e1SGeert Uytterhoeven 
4340a3360e1SGeert Uytterhoeven /* We have a good packet(s), get it/them out of the buffers. */
4350a3360e1SGeert Uytterhoeven static void
net_rx(struct net_device * dev)4360a3360e1SGeert Uytterhoeven net_rx(struct net_device *dev)
4370a3360e1SGeert Uytterhoeven {
4385a3b7504SFinn Thain 	struct net_local *lp = netdev_priv(dev);
4390a3360e1SGeert Uytterhoeven 	struct sk_buff *skb;
4400a3360e1SGeert Uytterhoeven 	int status, length;
4410a3360e1SGeert Uytterhoeven 
4420a3360e1SGeert Uytterhoeven 	status = readreg(dev, PP_RxStatus);
4430a3360e1SGeert Uytterhoeven 	if ((status & RX_OK) == 0) {
4440a3360e1SGeert Uytterhoeven 		dev->stats.rx_errors++;
4450a3360e1SGeert Uytterhoeven 		if (status & RX_RUNT)
4460a3360e1SGeert Uytterhoeven 				dev->stats.rx_length_errors++;
4470a3360e1SGeert Uytterhoeven 		if (status & RX_EXTRA_DATA)
4480a3360e1SGeert Uytterhoeven 				dev->stats.rx_length_errors++;
4490a3360e1SGeert Uytterhoeven 		if ((status & RX_CRC_ERROR) &&
4500a3360e1SGeert Uytterhoeven 		    !(status & (RX_EXTRA_DATA|RX_RUNT)))
4510a3360e1SGeert Uytterhoeven 			/* per str 172 */
4520a3360e1SGeert Uytterhoeven 			dev->stats.rx_crc_errors++;
4530a3360e1SGeert Uytterhoeven 		if (status & RX_DRIBBLE)
4540a3360e1SGeert Uytterhoeven 				dev->stats.rx_frame_errors++;
4550a3360e1SGeert Uytterhoeven 		return;
4560a3360e1SGeert Uytterhoeven 	}
4570a3360e1SGeert Uytterhoeven 
4580a3360e1SGeert Uytterhoeven 	length = readreg(dev, PP_RxLength);
4590a3360e1SGeert Uytterhoeven 	/* Malloc up new buffer. */
4600a3360e1SGeert Uytterhoeven 	skb = alloc_skb(length, GFP_ATOMIC);
4610a3360e1SGeert Uytterhoeven 	if (skb == NULL) {
4620a3360e1SGeert Uytterhoeven 		dev->stats.rx_dropped++;
4630a3360e1SGeert Uytterhoeven 		return;
4640a3360e1SGeert Uytterhoeven 	}
4650a3360e1SGeert Uytterhoeven 	skb_put(skb, length);
4660a3360e1SGeert Uytterhoeven 
4670a3360e1SGeert Uytterhoeven 	skb_copy_to_linear_data(skb, (void *)(dev->mem_start + PP_RxFrame),
4680a3360e1SGeert Uytterhoeven 				length);
4690a3360e1SGeert Uytterhoeven 
4705a3b7504SFinn Thain 	netif_dbg(lp, rx_status, dev, "received %d byte packet of type %x\n",
4715a3b7504SFinn Thain 		  length, skb->data[ETH_ALEN + ETH_ALEN] << 8 |
4725a3b7504SFinn Thain 		  skb->data[ETH_ALEN + ETH_ALEN + 1]);
4730a3360e1SGeert Uytterhoeven 
4740a3360e1SGeert Uytterhoeven         skb->protocol=eth_type_trans(skb,dev);
4750a3360e1SGeert Uytterhoeven 	netif_rx(skb);
4760a3360e1SGeert Uytterhoeven 	dev->stats.rx_packets++;
4770a3360e1SGeert Uytterhoeven 	dev->stats.rx_bytes += length;
4780a3360e1SGeert Uytterhoeven }
4790a3360e1SGeert Uytterhoeven 
4800a3360e1SGeert Uytterhoeven /* The inverse routine to net_open(). */
4810a3360e1SGeert Uytterhoeven static int
net_close(struct net_device * dev)4820a3360e1SGeert Uytterhoeven net_close(struct net_device *dev)
4830a3360e1SGeert Uytterhoeven {
4840a3360e1SGeert Uytterhoeven 
4850a3360e1SGeert Uytterhoeven 	writereg(dev, PP_RxCFG, 0);
4860a3360e1SGeert Uytterhoeven 	writereg(dev, PP_TxCFG, 0);
4870a3360e1SGeert Uytterhoeven 	writereg(dev, PP_BufCFG, 0);
4880a3360e1SGeert Uytterhoeven 	writereg(dev, PP_BusCTL, 0);
4890a3360e1SGeert Uytterhoeven 
4900a3360e1SGeert Uytterhoeven 	netif_stop_queue(dev);
4910a3360e1SGeert Uytterhoeven 
4920a3360e1SGeert Uytterhoeven 	free_irq(dev->irq, dev);
4930a3360e1SGeert Uytterhoeven 
4940a3360e1SGeert Uytterhoeven 	/* Update the statistics here. */
4950a3360e1SGeert Uytterhoeven 
4960a3360e1SGeert Uytterhoeven 	return 0;
4970a3360e1SGeert Uytterhoeven 
4980a3360e1SGeert Uytterhoeven }
4990a3360e1SGeert Uytterhoeven 
5000a3360e1SGeert Uytterhoeven /* Get the current statistics.	This may be called with the card open or
5010a3360e1SGeert Uytterhoeven    closed. */
5020a3360e1SGeert Uytterhoeven static struct net_device_stats *
net_get_stats(struct net_device * dev)5030a3360e1SGeert Uytterhoeven net_get_stats(struct net_device *dev)
5040a3360e1SGeert Uytterhoeven {
5050a3360e1SGeert Uytterhoeven 	unsigned long flags;
5060a3360e1SGeert Uytterhoeven 
5070a3360e1SGeert Uytterhoeven 	local_irq_save(flags);
5080a3360e1SGeert Uytterhoeven 	/* Update the statistics from the device registers. */
5090a3360e1SGeert Uytterhoeven 	dev->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
5100a3360e1SGeert Uytterhoeven 	dev->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
5110a3360e1SGeert Uytterhoeven 	local_irq_restore(flags);
5120a3360e1SGeert Uytterhoeven 
5130a3360e1SGeert Uytterhoeven 	return &dev->stats;
5140a3360e1SGeert Uytterhoeven }
5150a3360e1SGeert Uytterhoeven 
set_multicast_list(struct net_device * dev)5160a3360e1SGeert Uytterhoeven static void set_multicast_list(struct net_device *dev)
5170a3360e1SGeert Uytterhoeven {
5180a3360e1SGeert Uytterhoeven 	struct net_local *lp = netdev_priv(dev);
5190a3360e1SGeert Uytterhoeven 
5200a3360e1SGeert Uytterhoeven 	if(dev->flags&IFF_PROMISC)
5210a3360e1SGeert Uytterhoeven 	{
5220a3360e1SGeert Uytterhoeven 		lp->rx_mode = RX_ALL_ACCEPT;
5230a3360e1SGeert Uytterhoeven 	} else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
5240a3360e1SGeert Uytterhoeven 		/* The multicast-accept list is initialized to accept-all, and we
5250a3360e1SGeert Uytterhoeven 		   rely on higher-level filtering for now. */
5260a3360e1SGeert Uytterhoeven 		lp->rx_mode = RX_MULTCAST_ACCEPT;
5270a3360e1SGeert Uytterhoeven 	}
5280a3360e1SGeert Uytterhoeven 	else
5290a3360e1SGeert Uytterhoeven 		lp->rx_mode = 0;
5300a3360e1SGeert Uytterhoeven 
5310a3360e1SGeert Uytterhoeven 	writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode);
5320a3360e1SGeert Uytterhoeven 
5330a3360e1SGeert Uytterhoeven 	/* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */
5340a3360e1SGeert Uytterhoeven 	writereg(dev, PP_RxCFG, lp->curr_rx_cfg |
5350a3360e1SGeert Uytterhoeven 	     (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0));
5360a3360e1SGeert Uytterhoeven }
5370a3360e1SGeert Uytterhoeven 
5380a3360e1SGeert Uytterhoeven 
set_mac_address(struct net_device * dev,void * addr)5390a3360e1SGeert Uytterhoeven static int set_mac_address(struct net_device *dev, void *addr)
5400a3360e1SGeert Uytterhoeven {
54166dc92edSDanny Kukawka 	struct sockaddr *saddr = addr;
5429100eb01SDavid S. Miller 	int i;
54366dc92edSDanny Kukawka 
5449100eb01SDavid S. Miller 	if (!is_valid_ether_addr(saddr->sa_data))
54566dc92edSDanny Kukawka 		return -EADDRNOTAVAIL;
54666dc92edSDanny Kukawka 
547a96d317fSJakub Kicinski 	eth_hw_addr_set(dev, saddr->sa_data);
54886c2666eSFinn Thain 	netdev_info(dev, "Setting MAC address to %pM\n", dev->dev_addr);
54966dc92edSDanny Kukawka 
5500a3360e1SGeert Uytterhoeven 	/* set the Ethernet address */
5510a3360e1SGeert Uytterhoeven 	for (i=0; i < ETH_ALEN/2; i++)
5520a3360e1SGeert Uytterhoeven 		writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
5530a3360e1SGeert Uytterhoeven 
5540a3360e1SGeert Uytterhoeven 	return 0;
5550a3360e1SGeert Uytterhoeven }
5560a3360e1SGeert Uytterhoeven 
557*5e736135SJeff Johnson MODULE_DESCRIPTION("Macintosh CS89x0-based Ethernet driver");
5580a3360e1SGeert Uytterhoeven MODULE_LICENSE("GPL");
5590a3360e1SGeert Uytterhoeven 
mac89x0_device_remove(struct platform_device * pdev)5608ca0ae6aSUwe Kleine-König static void mac89x0_device_remove(struct platform_device *pdev)
5610a3360e1SGeert Uytterhoeven {
56243bf2e6dSFinn Thain 	struct net_device *dev = platform_get_drvdata(pdev);
56343bf2e6dSFinn Thain 
56443bf2e6dSFinn Thain 	unregister_netdev(dev);
56543bf2e6dSFinn Thain 	nubus_writew(0, dev->base_addr + ADD_PORT);
56643bf2e6dSFinn Thain 	free_netdev(dev);
5670a3360e1SGeert Uytterhoeven }
5680a3360e1SGeert Uytterhoeven 
56943bf2e6dSFinn Thain static struct platform_driver mac89x0_platform_driver = {
57043bf2e6dSFinn Thain 	.probe = mac89x0_device_probe,
5718ca0ae6aSUwe Kleine-König 	.remove_new = mac89x0_device_remove,
57243bf2e6dSFinn Thain 	.driver = {
57343bf2e6dSFinn Thain 		.name = "mac89x0",
57443bf2e6dSFinn Thain 	},
57543bf2e6dSFinn Thain };
57643bf2e6dSFinn Thain 
57743bf2e6dSFinn Thain module_platform_driver(mac89x0_platform_driver);
578