patch-2.3.13 linux/drivers/net/macsonic.c

Next file: linux/drivers/net/mvme147.c
Previous file: linux/drivers/net/lance.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.12/linux/drivers/net/macsonic.c linux/drivers/net/macsonic.c
@@ -0,0 +1,485 @@
+/*
+ * macsonic.c
+ *
+ * (C) 1998 Alan Cox
+ *
+ * Debugging Andreas Ehliar, Michael Schmitz
+ *
+ * Based on code
+ * (C) 1996 by Thomas Bogendoerfer (tsbogend@bigbug.franken.de)
+ * 
+ * This driver is based on work from Andreas Busse, but most of
+ * the code is rewritten.
+ * 
+ * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
+ *
+ * A driver for the Mac onboard Sonic ethernet chip.
+ *
+ * 98/12/21 MSch: judged from tests on Q800, it's basically working, 
+ *		  but eating up both receive and transmit resources
+ *		  and duplicating packets. Needs more testing.
+ *
+ * 99/01/03 MSch: upgraded to version 0.92 of the core driver, fixed.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/nubus.h>
+#include <asm/bootinfo.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/macintosh.h>
+
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <config/macsonic.h>
+
+#define SREGS_PAD(n)    u16 n;
+
+#include "sonic.h"
+
+extern int mac_onboard_sonic_probe(void);
+
+static int setup_debug = -1;
+static int setup_offset = -1;
+static int setup_shift = -1;
+
+/*
+ * This seems to be the right default for the Q800
+ */
+
+static int reg_offset = 0;
+static int reg_shift = 0;
+
+/*
+ * Macros to access SONIC registers
+ */
+ 
+#define MAC_SONIC_REGISTERS	0x50F0A000
+#define MAC_SONIC_PROM_BASE	0x50f08000
+#define MAC_SONIC_IRQ		9	/* Nubus 9 */
+
+/*
+ *    FIXME: We may need to invert the byte ordering. These should
+ *      be ok for other aspects as they are uncached spaces.
+ *      The original macros from jazzsonic.c works for me
+ *      on my LC 630, YMMV /Andreas Ehliar
+ */
+
+#if 0
+#define SONIC_READ(reg) \
+	*((volatile unsigned int *)base_addr+((reg)<<2)+2)
+
+#define SONIC_WRITE(reg,val) \
+	*((volatile unsigned int *)base_addr+((reg)<<2)+2) = val
+#else
+#define SONIC_READ(reg) \
+	*((volatile unsigned int *)base_addr+reg)
+
+#define SONIC_WRITE(reg,val) \
+	*((volatile unsigned int *)base_addr+reg) = val
+#endif
+
+#define SONIC_READ_PROM(addr) \
+	*((volatile unsigned char *)prom_addr+addr)
+/*
+ * Function : mac_sonic_setup(char *str, int *ints)
+ *
+ * Purpose : booter command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ *	equal to the number of ints.
+ *
+ * Currently unused in the new driver; need to add settable parameters to the 
+ * detect function.
+ *
+ */
+
+void mac_sonic_setup(char *str, int *ints) {
+	/* Format of macsonic parameter is:
+	 *   macsonic=<debug>,<offset>,<shift>
+	 * Negative values mean don't change.
+	 */
+	
+	/* Grmbl... the standard parameter parsing can't handle negative numbers
+	 * :-( So let's do it ourselves!
+	 */
+
+	int i = ints[0]+1, fact;
+
+	while( str && (isdigit(*str) || *str == '-') && i <= 10) {
+		if (*str == '-')
+			fact = -1, ++str;
+		else
+			fact = 1;
+		ints[i++] = simple_strtoul( str, NULL, 0 ) * fact;
+		if ((str = strchr( str, ',' )) != NULL)
+			++str;
+	}
+	ints[0] = i-1;
+	
+	if (ints[0] < 1) {
+		printk( "mac_sonic_setup: no arguments!\n" );
+		return;
+	}
+
+	if (ints[0] >= 1) {
+	       	/* 0 <= n <= 2 */
+		if (ints[1] >= 0 && ints[1] <= 8)
+			setup_debug = ints[1];
+		else if (ints[1] > 16)
+			printk( "mac_sonic_setup: invalid debug level %d !\n", ints[1] );
+	}
+	if (ints[0] >= 2) {
+	       	/* 0 <= n <= 2 */
+		if (ints[2] >= 0 && ints[2] <= 16)
+			setup_offset = ints[2];
+		else if (ints[2] > 16)
+			printk( "mac_sonic_setup: invalid offset %d !\n", ints[2] );
+	}
+	if (ints[0] >= 3) {
+	       	/* 0 <= n <= 2 */
+		if (ints[3] >= 0 && ints[3] <= 16)
+			setup_shift = ints[3];
+		else if (ints[3] > 16)
+			printk( "mac_sonic_setup: invalid shift %d !\n", ints[3] );
+	}
+}
+
+static int sonic_debug = 0;
+
+/*
+ * For reversing the PROM address
+ */
+
+static unsigned char nibbletab[] = {0, 8, 4, 12, 2, 10, 6, 14,
+				    1, 9, 5, 13, 3, 11, 7, 15};
+
+__initfunc(int mac_onboard_sonic_probe(void))
+{
+	struct device *dev;
+	unsigned int silicon_revision;
+	unsigned int val;
+	struct sonic_local *lp;
+	int i;
+	int base_addr = MAC_SONIC_REGISTERS;
+	int prom_addr = MAC_SONIC_PROM_BASE;
+	static int one=0;
+	
+	if (!MACH_IS_MAC)
+		return -ENODEV;
+
+	if(++one!=1)	/* Only one is allowed */
+		return -ENODEV;
+
+	printk(KERN_INFO "Checking for internal Macintosh ethernet (SONIC).. ");
+
+	if (macintosh_config->ether_type != MAC_ETHER_SONIC)
+	{
+		printk("none.\n");
+		return -ENODEV;
+	}
+	
+	printk("yes\n");
+	
+	if (setup_debug >= 0)
+	  sonic_debug = setup_debug;
+
+	/*
+	 * This may depend on the actual Mac model ... works for me.
+	 */
+	reg_offset = 
+	  (setup_offset >= 0) ? setup_offset : 0;
+	reg_shift = 
+	  (setup_shift >= 0) ? setup_shift : 0;
+
+	/*
+	 * get the Silicon Revision ID. If this is one of the known
+	 * one assume that we found a SONIC ethernet controller at
+	 * the expected location.
+	 * (This is not implemented in the Macintosh driver yet; need
+	 * to collect values from various sources. Mine is 0x4 ...)
+	 */
+
+	silicon_revision = SONIC_READ(SONIC_SR);
+	if (sonic_debug > 1)
+		printk("SONIC Silicon Revision = 0x%04x\n", silicon_revision);
+
+	/*
+	 * We need to allocate sonic_local later on, making sure it's
+	 * aligned on a 64k boundary. So, no space for dev->priv allocated
+	 * here ...
+	 */
+	dev = init_etherdev(0,0);
+	
+	if(dev==NULL)
+		return -ENOMEM;
+
+	printk("%s: %s found at 0x%08x, ",
+	       dev->name, "SONIC ethernet", base_addr);
+
+	if (sonic_debug > 1)
+		printk("using offset %d shift %d,", reg_offset, reg_shift);
+
+	/* Fill in the 'dev' fields. */
+	dev->base_addr = base_addr;
+	dev->irq = MAC_SONIC_IRQ;
+
+	/*
+	 * Put the sonic into software reset, then
+	 * retrieve and print the ethernet address.
+	 */
+
+	SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
+
+	/*
+	 *        We can't trust MacOS to initialise things it seems.
+	 */
+
+	if (sonic_debug > 1)
+		printk("SONIC_DCR was %X\n",SONIC_READ(SONIC_DCR));
+	
+	SONIC_WRITE(SONIC_DCR,
+		    SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_EXBUS | SONIC_DCR_DW);
+
+	/*
+	 *  We don't want floating spare IRQ's around, not on
+	 *  level triggered systems!
+	 *  Strange though - writing to the ISR only clears currently
+	 *  pending IRQs, but doesn't disable them... Does this make 
+	 *  a difference?? Seems it does ...
+	 */
+#if 1
+	SONIC_WRITE(SONIC_ISR,0x7fff);
+	SONIC_WRITE(SONIC_IMR,0);
+#else
+	SONIC_WRITE(SONIC_ISR, SONIC_IMR_DEFAULT);
+#endif
+	
+	/* This is how it is done in jazzsonic.c
+	 * It doesn't seem to work here though.
+	 */
+	if (sonic_debug > 2) {
+		printk("Retreiving CAM entry 0. This should be the HW address.\n");
+		
+		SONIC_WRITE(SONIC_CEP, 0);
+		for (i = 0; i < 3; i++)
+		{
+			val = SONIC_READ(SONIC_CAP0 - i);
+			dev->dev_addr[i * 2] = val;
+			dev->dev_addr[i * 2 + 1] = val >> 8;
+		}
+
+		printk("HW Address from CAM 0: ");
+		for (i = 0; i < 6; i++)
+		{
+			printk("%2.2x", dev->dev_addr[i]);
+			if (i < 5)
+				printk(":");
+		}
+		printk("\n");
+
+		printk("Retreiving CAM entry 15. Another candidate...\n");
+
+		/*
+		 * MacOS seems to use CAM entry 15 ...
+		 */
+	       	SONIC_WRITE(SONIC_CEP, 15);
+		for (i = 0; i < 3; i++)
+		{
+			val = SONIC_READ(SONIC_CAP0 - i);
+			dev->dev_addr[i * 2] = val;
+			dev->dev_addr[i * 2 + 1] = val >> 8;
+		}
+
+		printk("HW Address from CAM 15: ");
+		for (i = 0; i < 6; i++)
+		{
+			printk("%2.2x", dev->dev_addr[i]);
+			if (i < 5)
+				printk(":");
+		}
+		printk("\n");
+	}
+
+	/*
+	 * if we can read the PROM, we're safe :-)
+	 */
+	if (sonic_debug > 1)
+		printk("Retreiving HW address from the PROM: ");
+
+	for(i=0;i<6;i++){
+                dev->dev_addr[i]=SONIC_READ_PROM(i);
+        }                             
+	if (sonic_debug > 1) {
+	        for (i = 0; i < 6; i++)
+		{
+			printk("%2.2x", dev->dev_addr[i]);
+			if (i <	5)
+				printk(":");
+		}
+		printk("\n");
+	}
+	/*
+	 *                If its not one of these we have
+	 *          screwed up on this Mac model
+	 */
+
+	if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) &&
+	    memcmp(dev->dev_addr, "\x00\xA0\x40", 3) &&
+	    memcmp(dev->dev_addr, "\x00\x05\x02", 3))
+	{
+		/*
+		 * Try bit reversed
+		 */
+		for(i=0;i<6;i++){
+			val = SONIC_READ_PROM(i);
+			dev->dev_addr[i]=(nibbletab[val & 0xf] << 4) | 
+					  nibbletab[(val >> 4) &0xf];
+		}
+		if (sonic_debug > 1) {
+			printk("Trying bit reversed:  ");
+			for (i = 0; i < 6; i++)
+			{
+				printk("%2.2x", dev->dev_addr[i]);
+				if (i < 5)
+					printk(":");
+			}
+			printk("\n");
+		}
+		if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) &&
+		    memcmp(dev->dev_addr, "\x00\xA0\x40", 3) &&
+		    memcmp(dev->dev_addr, "\x00\x05\x02", 3))
+		{
+		        /*
+			 * Still nonsense ... messed up someplace!
+			 */
+			printk("ERROR (INVALID MAC)\n");
+			return -EIO;
+		}
+	}
+
+	printk(" MAC ");
+	for (i = 0; i < 6; i++)
+	{
+		printk("%2.2x", dev->dev_addr[i]);
+		if (i < 5)
+			printk(":");
+	}
+
+	printk(" IRQ %d\n", MAC_SONIC_IRQ);
+
+	/* Initialize the device structure. */
+	if (dev->priv == NULL)
+	{
+		if (sonic_debug > 2) {
+			printk("Allocating memory for dev->priv aka lp\n");
+			printk("Memory to allocate: %d\n",sizeof(*lp));
+		}
+		/*
+		 * the memory be located in the same 64kb segment
+		 */
+		lp = NULL;
+		i = 0;
+		do
+		{
+			lp = (struct sonic_local *) kmalloc(sizeof(*lp), GFP_KERNEL);
+			if ((unsigned long) lp >> 16 != ((unsigned long) lp + sizeof(*lp)) >> 16)
+			{
+				/* FIXME, free the memory later */
+				kfree(lp);
+				lp = NULL;
+			}
+		}
+		while (lp == NULL && i++ < 20);
+
+		if (lp == NULL)
+		{
+			printk("%s: couldn't allocate memory for descriptors\n",
+			       dev->name);
+			return -ENOMEM;
+		}
+
+		if (sonic_debug > 2) {
+			printk("Memory allocated after %d tries\n",i);
+		}
+
+		/* XXX sonic_local has the TDA, RRA, RDA, don't cache */
+		kernel_set_cachemode((u32)lp, 8192, IOMAP_NOCACHE_SER);
+		memset(lp, 0, sizeof(struct sonic_local));
+
+		lp->cda_laddr = (u32)lp;
+		if (sonic_debug > 2) {
+			printk("memory allocated for sonic at 0x%x\n",lp);
+		}
+		lp->tda_laddr = lp->cda_laddr + sizeof(lp->cda);
+		lp->rra_laddr = lp->tda_laddr + sizeof(lp->tda);
+		lp->rda_laddr = lp->rra_laddr + sizeof(lp->rra);
+
+		/* allocate receive buffer area */
+		/* FIXME, maybe we should use skbs */
+		if ((lp->rba = (char *) kmalloc(SONIC_NUM_RRS * SONIC_RBSIZE, GFP_KERNEL)) == NULL)
+		{
+			printk("%s: couldn't allocate receive buffers\n", dev->name);
+			return -ENOMEM;
+		}
+		/* XXX RBA written by Sonic, not cached either */
+		kernel_set_cachemode((u32)lp->rba, 6*8192, IOMAP_NOCACHE_SER);
+		lp->rba_laddr = (u32)lp->rba;
+		flush_cache_all();
+		dev->priv = (struct sonic_local *) lp;
+	}
+	lp = (struct sonic_local *) dev->priv;
+	dev->open = sonic_open;
+	dev->stop = sonic_close;
+	dev->hard_start_xmit = sonic_send_packet;
+	dev->get_stats = sonic_get_stats;
+	dev->set_multicast_list = &sonic_multicast_list;
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+	return 0;
+}
+
+/*
+ *    SONIC uses a nubus IRQ
+ */
+
+#define sonic_request_irq(irq, vec, flags, name, dev) \
+		nubus_request_irq(irq, dev, vec)
+#define sonic_free_irq(irq,id)	nubus_free_irq(irq)
+
+/*
+ *    No funnies on memory mapping.
+ */
+
+#define sonic_chiptomem(x)	(x)
+
+/*
+ *    No VDMA on a Macintosh. So we need request no such facility.
+ */
+
+#define vdma_alloc(x,y)		((u32)(x))
+#define vdma_free(x)
+#define PHYSADDR(x)		(x)
+
+#include "sonic.c"

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)