| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* lasi_82596.c -- driver for the intel 82596 ethernet controller, as | 
 | 2 |    munged into HPPA boxen . | 
 | 3 |  | 
 | 4 |    This driver is based upon 82596.c, original credits are below... | 
 | 5 |    but there were too many hoops which HP wants jumped through to | 
 | 6 |    keep this code in there in a sane manner. | 
 | 7 |  | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 8 |    3 primary sources of the mess -- | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 9 |    1) hppa needs *lots* of cacheline flushing to keep this kind of | 
 | 10 |    MMIO running. | 
 | 11 |  | 
 | 12 |    2) The 82596 needs to see all of its pointers as their physical | 
 | 13 |    address.  Thus virt_to_bus/bus_to_virt are *everywhere*. | 
 | 14 |  | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 15 |    3) The implementation HP is using seems to be significantly pickier | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 16 |    about when and how the command and RX units are started.  some | 
 | 17 |    command ordering was changed. | 
 | 18 |  | 
 | 19 |    Examination of the mach driver leads one to believe that there | 
 | 20 |    might be a saner way to pull this off...  anyone who feels like a | 
 | 21 |    full rewrite can be my guest. | 
 | 22 |  | 
 | 23 |    Split 02/13/2000 Sam Creasey (sammy@oh.verio.com) | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 24 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 25 |    02/01/2000  Initial modifications for parisc by Helge Deller (deller@gmx.de) | 
 | 26 |    03/02/2000  changes for better/correct(?) cache-flushing (deller) | 
 | 27 | */ | 
 | 28 |  | 
 | 29 | /* 82596.c: A generic 82596 ethernet driver for linux. */ | 
 | 30 | /* | 
 | 31 |    Based on Apricot.c | 
 | 32 |    Written 1994 by Mark Evans. | 
 | 33 |    This driver is for the Apricot 82596 bus-master interface | 
 | 34 |  | 
 | 35 |    Modularised 12/94 Mark Evans | 
 | 36 |  | 
 | 37 |  | 
 | 38 |    Modified to support the 82596 ethernet chips on 680x0 VME boards. | 
 | 39 |    by Richard Hirst <richard@sleepie.demon.co.uk> | 
 | 40 |    Renamed to be 82596.c | 
 | 41 |  | 
 | 42 |    980825:  Changed to receive directly in to sk_buffs which are | 
 | 43 |    allocated at open() time.  Eliminates copy on incoming frames | 
 | 44 |    (small ones are still copied).  Shared data now held in a | 
 | 45 |    non-cached page, so we can run on 68060 in copyback mode. | 
 | 46 |  | 
 | 47 |    TBD: | 
 | 48 |    * look at deferring rx frames rather than discarding (as per tulip) | 
 | 49 |    * handle tx ring full as per tulip | 
| André Goddard Rosa | af901ca | 2009-11-14 13:09:05 -0200 | [diff] [blame] | 50 |    * performance test to tune rx_copybreak | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 51 |  | 
 | 52 |    Most of my modifications relate to the braindead big-endian | 
 | 53 |    implementation by Intel.  When the i596 is operating in | 
 | 54 |    'big-endian' mode, it thinks a 32 bit value of 0x12345678 | 
 | 55 |    should be stored as 0x56781234.  This is a real pain, when | 
 | 56 |    you have linked lists which are shared by the 680x0 and the | 
 | 57 |    i596. | 
 | 58 |  | 
 | 59 |    Driver skeleton | 
 | 60 |    Written 1993 by Donald Becker. | 
 | 61 |    Copyright 1993 United States Government as represented by the Director, | 
 | 62 |    National Security Agency. This software may only be used and distributed | 
 | 63 |    according to the terms of the GNU General Public License as modified by SRC, | 
 | 64 |    incorporated herein by reference. | 
 | 65 |  | 
 | 66 |    The author may be reached as becker@scyld.com, or C/O | 
 | 67 |    Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403 | 
 | 68 |  | 
 | 69 |  */ | 
 | 70 |  | 
 | 71 | #include <linux/module.h> | 
 | 72 | #include <linux/kernel.h> | 
 | 73 | #include <linux/string.h> | 
 | 74 | #include <linux/ptrace.h> | 
 | 75 | #include <linux/errno.h> | 
 | 76 | #include <linux/ioport.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 77 | #include <linux/interrupt.h> | 
 | 78 | #include <linux/delay.h> | 
 | 79 | #include <linux/netdevice.h> | 
 | 80 | #include <linux/etherdevice.h> | 
 | 81 | #include <linux/skbuff.h> | 
 | 82 | #include <linux/init.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 83 | #include <linux/types.h> | 
 | 84 | #include <linux/bitops.h> | 
| Helge Deller | 76fb927 | 2007-05-27 14:27:23 +0200 | [diff] [blame] | 85 | #include <linux/dma-mapping.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 86 |  | 
 | 87 | #include <asm/io.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 88 | #include <asm/irq.h> | 
 | 89 | #include <asm/pdc.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 90 | #include <asm/parisc-device.h> | 
 | 91 |  | 
 | 92 | #define LASI_82596_DRIVER_VERSION "LASI 82596 driver - Revision: 1.30" | 
 | 93 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 94 | #define PA_I82596_RESET		0	/* Offsets relative to LASI-LAN-Addr.*/ | 
 | 95 | #define PA_CPU_PORT_L_ACCESS	4 | 
 | 96 | #define PA_CHANNEL_ATTENTION	8 | 
 | 97 |  | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 98 | #define OPT_SWAP_PORT	0x0001	/* Need to wordswp on the MPU port */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 99 |  | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 100 | #define DMA_ALLOC                        dma_alloc_noncoherent | 
 | 101 | #define DMA_FREE                         dma_free_noncoherent | 
 | 102 | #define DMA_WBACK(ndev, addr, len) \ | 
 | 103 | 	do { dma_cache_sync((ndev)->dev.parent, (void *)addr, len, DMA_TO_DEVICE); } while (0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 104 |  | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 105 | #define DMA_INV(ndev, addr, len) \ | 
 | 106 | 	do { dma_cache_sync((ndev)->dev.parent, (void *)addr, len, DMA_FROM_DEVICE); } while (0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 107 |  | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 108 | #define DMA_WBACK_INV(ndev, addr, len) \ | 
 | 109 | 	do { dma_cache_sync((ndev)->dev.parent, (void *)addr, len, DMA_BIDIRECTIONAL); } while (0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 110 |  | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 111 | #define SYSBUS      0x0000006c; | 
 | 112 |  | 
 | 113 | /* big endian CPU, 82596 "big" endian mode */ | 
 | 114 | #define SWAP32(x)   (((u32)(x)<<16) | ((((u32)(x)))>>16)) | 
 | 115 | #define SWAP16(x)   (x) | 
 | 116 |  | 
 | 117 | #include "lib82596.c" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 118 |  | 
 | 119 | MODULE_AUTHOR("Richard Hirst"); | 
 | 120 | MODULE_DESCRIPTION("i82596 driver"); | 
 | 121 | MODULE_LICENSE("GPL"); | 
| Rusty Russell | 8d3b33f | 2006-03-25 03:07:05 -0800 | [diff] [blame] | 122 | module_param(i596_debug, int, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 123 | MODULE_PARM_DESC(i596_debug, "lasi_82596 debug mask"); | 
 | 124 |  | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 125 | static inline void ca(struct net_device *dev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 126 | { | 
 | 127 | 	gsc_writel(0, dev->base_addr + PA_CHANNEL_ATTENTION); | 
 | 128 | } | 
 | 129 |  | 
 | 130 |  | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 131 | static void mpu_port(struct net_device *dev, int c, dma_addr_t x) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 132 | { | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 133 | 	struct i596_private *lp = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 134 |  | 
 | 135 | 	u32 v = (u32) (c) | (u32) (x); | 
 | 136 | 	u16 a, b; | 
 | 137 |  | 
 | 138 | 	if (lp->options & OPT_SWAP_PORT) { | 
 | 139 | 		a = v >> 16; | 
 | 140 | 		b = v & 0xffff; | 
 | 141 | 	} else { | 
 | 142 | 		a = v & 0xffff; | 
 | 143 | 		b = v >> 16; | 
 | 144 | 	} | 
 | 145 |  | 
 | 146 | 	gsc_writel(a, dev->base_addr + PA_CPU_PORT_L_ACCESS); | 
 | 147 | 	udelay(1); | 
 | 148 | 	gsc_writel(b, dev->base_addr + PA_CPU_PORT_L_ACCESS); | 
 | 149 | } | 
 | 150 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 151 | #define LAN_PROM_ADDR	0xF0810000 | 
 | 152 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 153 | static int __devinit | 
 | 154 | lan_init_chip(struct parisc_device *dev) | 
 | 155 | { | 
 | 156 | 	struct	net_device *netdevice; | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 157 | 	struct i596_private *lp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 158 | 	int	retval; | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 159 | 	int i; | 
| Jeff Garzik | 6aa20a2 | 2006-09-13 13:24:59 -0400 | [diff] [blame] | 160 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 161 | 	if (!dev->irq) { | 
 | 162 | 		printk(KERN_ERR "%s: IRQ not found for i82596 at 0x%lx\n", | 
| Alexander Beregalov | 78a658d | 2009-05-05 01:03:32 +0000 | [diff] [blame] | 163 | 			__FILE__, (unsigned long)dev->hpa.start); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 164 | 		return -ENODEV; | 
 | 165 | 	} | 
 | 166 |  | 
| Alexander Beregalov | 78a658d | 2009-05-05 01:03:32 +0000 | [diff] [blame] | 167 | 	printk(KERN_INFO "Found i82596 at 0x%lx, IRQ %d\n", | 
 | 168 | 			(unsigned long)dev->hpa.start, dev->irq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 169 |  | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 170 | 	netdevice = alloc_etherdev(sizeof(struct i596_private)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 171 | 	if (!netdevice) | 
 | 172 | 		return -ENOMEM; | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 173 | 	SET_NETDEV_DEV(netdevice, &dev->dev); | 
 | 174 | 	parisc_set_drvdata (dev, netdevice); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 175 |  | 
| Matthew Wilcox | 53f01bb | 2005-10-21 22:36:40 -0400 | [diff] [blame] | 176 | 	netdevice->base_addr = dev->hpa.start; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 177 | 	netdevice->irq = dev->irq; | 
 | 178 |  | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 179 | 	if (pdc_lan_station_id(netdevice->dev_addr, netdevice->base_addr)) { | 
 | 180 | 		for (i = 0; i < 6; i++) { | 
 | 181 | 			netdevice->dev_addr[i] = gsc_readb(LAN_PROM_ADDR + i); | 
 | 182 | 		} | 
 | 183 | 		printk(KERN_INFO | 
 | 184 | 		       "%s: MAC of HP700 LAN read from EEPROM\n", __FILE__); | 
 | 185 | 	} | 
 | 186 |  | 
 | 187 | 	lp = netdev_priv(netdevice); | 
 | 188 | 	lp->options = dev->id.sversion == 0x72 ? OPT_SWAP_PORT : 0; | 
 | 189 |  | 
 | 190 | 	retval = i82596_probe(netdevice); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 191 | 	if (retval) { | 
 | 192 | 		free_netdev(netdevice); | 
 | 193 | 		return -ENODEV; | 
 | 194 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 195 | 	return retval; | 
 | 196 | } | 
 | 197 |  | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 198 | static int __devexit lan_remove_chip (struct parisc_device *pdev) | 
 | 199 | { | 
 | 200 | 	struct net_device *dev = parisc_get_drvdata(pdev); | 
 | 201 | 	struct i596_private *lp = netdev_priv(dev); | 
 | 202 |  | 
 | 203 | 	unregister_netdev (dev); | 
 | 204 | 	DMA_FREE(&pdev->dev, sizeof(struct i596_private), | 
 | 205 | 		 (void *)lp->dma, lp->dma_addr); | 
 | 206 | 	free_netdev (dev); | 
 | 207 | 	return 0; | 
 | 208 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 209 |  | 
 | 210 | static struct parisc_device_id lan_tbl[] = { | 
 | 211 | 	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008a }, | 
 | 212 | 	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00072 }, | 
 | 213 | 	{ 0, } | 
 | 214 | }; | 
 | 215 |  | 
 | 216 | MODULE_DEVICE_TABLE(parisc, lan_tbl); | 
 | 217 |  | 
 | 218 | static struct parisc_driver lan_driver = { | 
| Matthew Wilcox | bdad1f8 | 2005-10-21 22:36:23 -0400 | [diff] [blame] | 219 | 	.name		= "lasi_82596", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 220 | 	.id_table	= lan_tbl, | 
 | 221 | 	.probe		= lan_init_chip, | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 222 | 	.remove         = __devexit_p(lan_remove_chip), | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 223 | }; | 
 | 224 |  | 
 | 225 | static int __devinit lasi_82596_init(void) | 
 | 226 | { | 
| Thomas Bogendoerfer | 2187f28 | 2007-06-28 00:46:22 +0200 | [diff] [blame] | 227 | 	printk(KERN_INFO LASI_82596_DRIVER_VERSION "\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 228 | 	return register_parisc_driver(&lan_driver); | 
 | 229 | } | 
 | 230 |  | 
 | 231 | module_init(lasi_82596_init); | 
 | 232 |  | 
 | 233 | static void __exit lasi_82596_exit(void) | 
 | 234 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 235 | 	unregister_parisc_driver(&lan_driver); | 
 | 236 | } | 
 | 237 |  | 
 | 238 | module_exit(lasi_82596_exit); |