18c2ecf20Sopenharmony_ci/* 82596.c: A generic 82596 ethernet driver for linux. */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci   Based on Apricot.c
48c2ecf20Sopenharmony_ci   Written 1994 by Mark Evans.
58c2ecf20Sopenharmony_ci   This driver is for the Apricot 82596 bus-master interface
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci   Modularised 12/94 Mark Evans
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci   Modified to support the 82596 ethernet chips on 680x0 VME boards.
118c2ecf20Sopenharmony_ci   by Richard Hirst <richard@sleepie.demon.co.uk>
128c2ecf20Sopenharmony_ci   Renamed to be 82596.c
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci   980825:  Changed to receive directly in to sk_buffs which are
158c2ecf20Sopenharmony_ci   allocated at open() time.  Eliminates copy on incoming frames
168c2ecf20Sopenharmony_ci   (small ones are still copied).  Shared data now held in a
178c2ecf20Sopenharmony_ci   non-cached page, so we can run on 68060 in copyback mode.
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci   TBD:
208c2ecf20Sopenharmony_ci   * look at deferring rx frames rather than discarding (as per tulip)
218c2ecf20Sopenharmony_ci   * handle tx ring full as per tulip
228c2ecf20Sopenharmony_ci   * performance test to tune rx_copybreak
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci   Most of my modifications relate to the braindead big-endian
258c2ecf20Sopenharmony_ci   implementation by Intel.  When the i596 is operating in
268c2ecf20Sopenharmony_ci   'big-endian' mode, it thinks a 32 bit value of 0x12345678
278c2ecf20Sopenharmony_ci   should be stored as 0x56781234.  This is a real pain, when
288c2ecf20Sopenharmony_ci   you have linked lists which are shared by the 680x0 and the
298c2ecf20Sopenharmony_ci   i596.
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci   Driver skeleton
328c2ecf20Sopenharmony_ci   Written 1993 by Donald Becker.
338c2ecf20Sopenharmony_ci   Copyright 1993 United States Government as represented by the Director,
348c2ecf20Sopenharmony_ci   National Security Agency. This software may only be used and distributed
358c2ecf20Sopenharmony_ci   according to the terms of the GNU General Public License as modified by SRC,
368c2ecf20Sopenharmony_ci   incorporated herein by reference.
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci   The author may be reached as becker@scyld.com, or C/O
398c2ecf20Sopenharmony_ci   Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#include <linux/module.h>
448c2ecf20Sopenharmony_ci#include <linux/kernel.h>
458c2ecf20Sopenharmony_ci#include <linux/string.h>
468c2ecf20Sopenharmony_ci#include <linux/errno.h>
478c2ecf20Sopenharmony_ci#include <linux/ioport.h>
488c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
498c2ecf20Sopenharmony_ci#include <linux/delay.h>
508c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
518c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
528c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
538c2ecf20Sopenharmony_ci#include <linux/init.h>
548c2ecf20Sopenharmony_ci#include <linux/bitops.h>
558c2ecf20Sopenharmony_ci#include <linux/gfp.h>
568c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#include <asm/io.h>
598c2ecf20Sopenharmony_ci#include <asm/dma.h>
608c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic char version[] __initdata =
638c2ecf20Sopenharmony_ci	"82596.c $Revision: 1.5 $\n";
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define DRV_NAME	"82596"
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/* DEBUG flags
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define DEB_INIT	0x0001
718c2ecf20Sopenharmony_ci#define DEB_PROBE	0x0002
728c2ecf20Sopenharmony_ci#define DEB_SERIOUS	0x0004
738c2ecf20Sopenharmony_ci#define DEB_ERRORS	0x0008
748c2ecf20Sopenharmony_ci#define DEB_MULTI	0x0010
758c2ecf20Sopenharmony_ci#define DEB_TDR		0x0020
768c2ecf20Sopenharmony_ci#define DEB_OPEN	0x0040
778c2ecf20Sopenharmony_ci#define DEB_RESET	0x0080
788c2ecf20Sopenharmony_ci#define DEB_ADDCMD	0x0100
798c2ecf20Sopenharmony_ci#define DEB_STATUS	0x0200
808c2ecf20Sopenharmony_ci#define DEB_STARTTX	0x0400
818c2ecf20Sopenharmony_ci#define DEB_RXADDR	0x0800
828c2ecf20Sopenharmony_ci#define DEB_TXADDR	0x1000
838c2ecf20Sopenharmony_ci#define DEB_RXFRAME	0x2000
848c2ecf20Sopenharmony_ci#define DEB_INTS	0x4000
858c2ecf20Sopenharmony_ci#define DEB_STRUCT	0x8000
868c2ecf20Sopenharmony_ci#define DEB_ANY		0xffff
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#define DEB(x,y)	if (i596_debug & (x)) y
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_MVME16x_NET)
938c2ecf20Sopenharmony_ci#define ENABLE_MVME16x_NET
948c2ecf20Sopenharmony_ci#endif
958c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_BVME6000_NET)
968c2ecf20Sopenharmony_ci#define ENABLE_BVME6000_NET
978c2ecf20Sopenharmony_ci#endif
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#ifdef ENABLE_MVME16x_NET
1008c2ecf20Sopenharmony_ci#include <asm/mvme16xhw.h>
1018c2ecf20Sopenharmony_ci#endif
1028c2ecf20Sopenharmony_ci#ifdef ENABLE_BVME6000_NET
1038c2ecf20Sopenharmony_ci#include <asm/bvme6000hw.h>
1048c2ecf20Sopenharmony_ci#endif
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/*
1078c2ecf20Sopenharmony_ci * Define various macros for Channel Attention, word swapping etc., dependent
1088c2ecf20Sopenharmony_ci * on architecture.  MVME and BVME are 680x0 based, otherwise it is Intel.
1098c2ecf20Sopenharmony_ci */
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci#ifdef __mc68000__
1128c2ecf20Sopenharmony_ci#define WSWAPrfd(x)  ((struct i596_rfd *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
1138c2ecf20Sopenharmony_ci#define WSWAPrbd(x)  ((struct i596_rbd *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
1148c2ecf20Sopenharmony_ci#define WSWAPiscp(x) ((struct i596_iscp *)(((u32)(x)<<16) | ((((u32)(x)))>>16)))
1158c2ecf20Sopenharmony_ci#define WSWAPscb(x)  ((struct i596_scb *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
1168c2ecf20Sopenharmony_ci#define WSWAPcmd(x)  ((struct i596_cmd *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
1178c2ecf20Sopenharmony_ci#define WSWAPtbd(x)  ((struct i596_tbd *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
1188c2ecf20Sopenharmony_ci#define WSWAPchar(x) ((char *)            (((u32)(x)<<16) | ((((u32)(x)))>>16)))
1198c2ecf20Sopenharmony_ci#define ISCP_BUSY	0x00010000
1208c2ecf20Sopenharmony_ci#else
1218c2ecf20Sopenharmony_ci#error 82596.c: unknown architecture
1228c2ecf20Sopenharmony_ci#endif
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/*
1258c2ecf20Sopenharmony_ci * These were the intel versions, left here for reference. There
1268c2ecf20Sopenharmony_ci * are currently no x86 users of this legacy i82596 chip.
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_ci#if 0
1298c2ecf20Sopenharmony_ci#define WSWAPrfd(x)     ((struct i596_rfd *)((long)x))
1308c2ecf20Sopenharmony_ci#define WSWAPrbd(x)     ((struct i596_rbd *)((long)x))
1318c2ecf20Sopenharmony_ci#define WSWAPiscp(x)    ((struct i596_iscp *)((long)x))
1328c2ecf20Sopenharmony_ci#define WSWAPscb(x)     ((struct i596_scb *)((long)x))
1338c2ecf20Sopenharmony_ci#define WSWAPcmd(x)     ((struct i596_cmd *)((long)x))
1348c2ecf20Sopenharmony_ci#define WSWAPtbd(x)     ((struct i596_tbd *)((long)x))
1358c2ecf20Sopenharmony_ci#define WSWAPchar(x)    ((char *)((long)x))
1368c2ecf20Sopenharmony_ci#define ISCP_BUSY	0x0001
1378c2ecf20Sopenharmony_ci#endif
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/*
1408c2ecf20Sopenharmony_ci * The MPU_PORT command allows direct access to the 82596. With PORT access
1418c2ecf20Sopenharmony_ci * the following commands are available (p5-18). The 32-bit port command
1428c2ecf20Sopenharmony_ci * must be word-swapped with the most significant word written first.
1438c2ecf20Sopenharmony_ci * This only applies to VME boards.
1448c2ecf20Sopenharmony_ci */
1458c2ecf20Sopenharmony_ci#define PORT_RESET		0x00	/* reset 82596 */
1468c2ecf20Sopenharmony_ci#define PORT_SELFTEST		0x01	/* selftest */
1478c2ecf20Sopenharmony_ci#define PORT_ALTSCP		0x02	/* alternate SCB address */
1488c2ecf20Sopenharmony_ci#define PORT_ALTDUMP		0x03	/* Alternate DUMP address */
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int i596_debug = (DEB_SERIOUS|DEB_PROBE);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Richard Hirst");
1538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("i82596 driver");
1548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cimodule_param(i596_debug, int, 0);
1578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(i596_debug, "i82596 debug mask");
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/* Copy frames shorter than rx_copybreak, otherwise pass on up in
1618c2ecf20Sopenharmony_ci * a full sized sk_buff.  Value of 100 stolen from tulip.c (!alpha).
1628c2ecf20Sopenharmony_ci */
1638c2ecf20Sopenharmony_cistatic int rx_copybreak = 100;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci#define PKT_BUF_SZ	1536
1668c2ecf20Sopenharmony_ci#define MAX_MC_CNT	64
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci#define I596_TOTAL_SIZE 17
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci#define I596_NULL ((void *)0xffffffff)
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci#define CMD_EOL		0x8000	/* The last command of the list, stop. */
1738c2ecf20Sopenharmony_ci#define CMD_SUSP	0x4000	/* Suspend after doing cmd. */
1748c2ecf20Sopenharmony_ci#define CMD_INTR	0x2000	/* Interrupt after doing cmd. */
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci#define CMD_FLEX	0x0008	/* Enable flexible memory model */
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cienum commands {
1798c2ecf20Sopenharmony_ci	CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
1808c2ecf20Sopenharmony_ci	CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci#define STAT_C		0x8000	/* Set to 0 after execution */
1848c2ecf20Sopenharmony_ci#define STAT_B		0x4000	/* Command being executed */
1858c2ecf20Sopenharmony_ci#define STAT_OK		0x2000	/* Command executed ok */
1868c2ecf20Sopenharmony_ci#define STAT_A		0x1000	/* Command aborted */
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci#define	 CUC_START	0x0100
1898c2ecf20Sopenharmony_ci#define	 CUC_RESUME	0x0200
1908c2ecf20Sopenharmony_ci#define	 CUC_SUSPEND    0x0300
1918c2ecf20Sopenharmony_ci#define	 CUC_ABORT	0x0400
1928c2ecf20Sopenharmony_ci#define	 RX_START	0x0010
1938c2ecf20Sopenharmony_ci#define	 RX_RESUME	0x0020
1948c2ecf20Sopenharmony_ci#define	 RX_SUSPEND	0x0030
1958c2ecf20Sopenharmony_ci#define	 RX_ABORT	0x0040
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci#define TX_TIMEOUT	(HZ/20)
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistruct i596_reg {
2018c2ecf20Sopenharmony_ci	unsigned short porthi;
2028c2ecf20Sopenharmony_ci	unsigned short portlo;
2038c2ecf20Sopenharmony_ci	unsigned long ca;
2048c2ecf20Sopenharmony_ci};
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci#define EOF		0x8000
2078c2ecf20Sopenharmony_ci#define SIZE_MASK	0x3fff
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistruct i596_tbd {
2108c2ecf20Sopenharmony_ci	unsigned short size;
2118c2ecf20Sopenharmony_ci	unsigned short pad;
2128c2ecf20Sopenharmony_ci	struct i596_tbd *next;
2138c2ecf20Sopenharmony_ci	char *data;
2148c2ecf20Sopenharmony_ci};
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/* The command structure has two 'next' pointers; v_next is the address of
2178c2ecf20Sopenharmony_ci * the next command as seen by the CPU, b_next is the address of the next
2188c2ecf20Sopenharmony_ci * command as seen by the 82596.  The b_next pointer, as used by the 82596
2198c2ecf20Sopenharmony_ci * always references the status field of the next command, rather than the
2208c2ecf20Sopenharmony_ci * v_next field, because the 82596 is unaware of v_next.  It may seem more
2218c2ecf20Sopenharmony_ci * logical to put v_next at the end of the structure, but we cannot do that
2228c2ecf20Sopenharmony_ci * because the 82596 expects other fields to be there, depending on command
2238c2ecf20Sopenharmony_ci * type.
2248c2ecf20Sopenharmony_ci */
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistruct i596_cmd {
2278c2ecf20Sopenharmony_ci	struct i596_cmd *v_next;	/* Address from CPUs viewpoint */
2288c2ecf20Sopenharmony_ci	unsigned short status;
2298c2ecf20Sopenharmony_ci	unsigned short command;
2308c2ecf20Sopenharmony_ci	struct i596_cmd *b_next;	/* Address from i596 viewpoint */
2318c2ecf20Sopenharmony_ci};
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistruct tx_cmd {
2348c2ecf20Sopenharmony_ci	struct i596_cmd cmd;
2358c2ecf20Sopenharmony_ci	struct i596_tbd *tbd;
2368c2ecf20Sopenharmony_ci	unsigned short size;
2378c2ecf20Sopenharmony_ci	unsigned short pad;
2388c2ecf20Sopenharmony_ci	struct sk_buff *skb;	/* So we can free it after tx */
2398c2ecf20Sopenharmony_ci};
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistruct tdr_cmd {
2428c2ecf20Sopenharmony_ci	struct i596_cmd cmd;
2438c2ecf20Sopenharmony_ci	unsigned short status;
2448c2ecf20Sopenharmony_ci	unsigned short pad;
2458c2ecf20Sopenharmony_ci};
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistruct mc_cmd {
2488c2ecf20Sopenharmony_ci	struct i596_cmd cmd;
2498c2ecf20Sopenharmony_ci	short mc_cnt;
2508c2ecf20Sopenharmony_ci	char mc_addrs[MAX_MC_CNT*6];
2518c2ecf20Sopenharmony_ci};
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistruct sa_cmd {
2548c2ecf20Sopenharmony_ci	struct i596_cmd cmd;
2558c2ecf20Sopenharmony_ci	char eth_addr[8];
2568c2ecf20Sopenharmony_ci};
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistruct cf_cmd {
2598c2ecf20Sopenharmony_ci	struct i596_cmd cmd;
2608c2ecf20Sopenharmony_ci	char i596_config[16];
2618c2ecf20Sopenharmony_ci};
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistruct i596_rfd {
2648c2ecf20Sopenharmony_ci	unsigned short stat;
2658c2ecf20Sopenharmony_ci	unsigned short cmd;
2668c2ecf20Sopenharmony_ci	struct i596_rfd *b_next;	/* Address from i596 viewpoint */
2678c2ecf20Sopenharmony_ci	struct i596_rbd *rbd;
2688c2ecf20Sopenharmony_ci	unsigned short count;
2698c2ecf20Sopenharmony_ci	unsigned short size;
2708c2ecf20Sopenharmony_ci	struct i596_rfd *v_next;	/* Address from CPUs viewpoint */
2718c2ecf20Sopenharmony_ci	struct i596_rfd *v_prev;
2728c2ecf20Sopenharmony_ci};
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistruct i596_rbd {
2758c2ecf20Sopenharmony_ci    unsigned short count;
2768c2ecf20Sopenharmony_ci    unsigned short zero1;
2778c2ecf20Sopenharmony_ci    struct i596_rbd *b_next;
2788c2ecf20Sopenharmony_ci    unsigned char *b_data;		/* Address from i596 viewpoint */
2798c2ecf20Sopenharmony_ci    unsigned short size;
2808c2ecf20Sopenharmony_ci    unsigned short zero2;
2818c2ecf20Sopenharmony_ci    struct sk_buff *skb;
2828c2ecf20Sopenharmony_ci    struct i596_rbd *v_next;
2838c2ecf20Sopenharmony_ci    struct i596_rbd *b_addr;		/* This rbd addr from i596 view */
2848c2ecf20Sopenharmony_ci    unsigned char *v_data;		/* Address from CPUs viewpoint */
2858c2ecf20Sopenharmony_ci};
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci#define TX_RING_SIZE 64
2888c2ecf20Sopenharmony_ci#define RX_RING_SIZE 16
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistruct i596_scb {
2918c2ecf20Sopenharmony_ci	unsigned short status;
2928c2ecf20Sopenharmony_ci	unsigned short command;
2938c2ecf20Sopenharmony_ci	struct i596_cmd *cmd;
2948c2ecf20Sopenharmony_ci	struct i596_rfd *rfd;
2958c2ecf20Sopenharmony_ci	unsigned long crc_err;
2968c2ecf20Sopenharmony_ci	unsigned long align_err;
2978c2ecf20Sopenharmony_ci	unsigned long resource_err;
2988c2ecf20Sopenharmony_ci	unsigned long over_err;
2998c2ecf20Sopenharmony_ci	unsigned long rcvdt_err;
3008c2ecf20Sopenharmony_ci	unsigned long short_err;
3018c2ecf20Sopenharmony_ci	unsigned short t_on;
3028c2ecf20Sopenharmony_ci	unsigned short t_off;
3038c2ecf20Sopenharmony_ci};
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistruct i596_iscp {
3068c2ecf20Sopenharmony_ci	unsigned long stat;
3078c2ecf20Sopenharmony_ci	struct i596_scb *scb;
3088c2ecf20Sopenharmony_ci};
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistruct i596_scp {
3118c2ecf20Sopenharmony_ci	unsigned long sysbus;
3128c2ecf20Sopenharmony_ci	unsigned long pad;
3138c2ecf20Sopenharmony_ci	struct i596_iscp *iscp;
3148c2ecf20Sopenharmony_ci};
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistruct i596_private {
3178c2ecf20Sopenharmony_ci	volatile struct i596_scp scp;
3188c2ecf20Sopenharmony_ci	volatile struct i596_iscp iscp;
3198c2ecf20Sopenharmony_ci	volatile struct i596_scb scb;
3208c2ecf20Sopenharmony_ci	struct sa_cmd sa_cmd;
3218c2ecf20Sopenharmony_ci	struct cf_cmd cf_cmd;
3228c2ecf20Sopenharmony_ci	struct tdr_cmd tdr_cmd;
3238c2ecf20Sopenharmony_ci	struct mc_cmd mc_cmd;
3248c2ecf20Sopenharmony_ci	unsigned long stat;
3258c2ecf20Sopenharmony_ci	int last_restart __attribute__((aligned(4)));
3268c2ecf20Sopenharmony_ci	struct i596_rfd *rfd_head;
3278c2ecf20Sopenharmony_ci	struct i596_rbd *rbd_head;
3288c2ecf20Sopenharmony_ci	struct i596_cmd *cmd_tail;
3298c2ecf20Sopenharmony_ci	struct i596_cmd *cmd_head;
3308c2ecf20Sopenharmony_ci	int cmd_backlog;
3318c2ecf20Sopenharmony_ci	unsigned long last_cmd;
3328c2ecf20Sopenharmony_ci	struct i596_rfd rfds[RX_RING_SIZE];
3338c2ecf20Sopenharmony_ci	struct i596_rbd rbds[RX_RING_SIZE];
3348c2ecf20Sopenharmony_ci	struct tx_cmd tx_cmds[TX_RING_SIZE];
3358c2ecf20Sopenharmony_ci	struct i596_tbd tbds[TX_RING_SIZE];
3368c2ecf20Sopenharmony_ci	int next_tx_cmd;
3378c2ecf20Sopenharmony_ci	spinlock_t lock;
3388c2ecf20Sopenharmony_ci};
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic char init_setup[] =
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	0x8E,			/* length, prefetch on */
3438c2ecf20Sopenharmony_ci	0xC8,			/* fifo to 8, monitor off */
3448c2ecf20Sopenharmony_ci#ifdef CONFIG_VME
3458c2ecf20Sopenharmony_ci	0xc0,			/* don't save bad frames */
3468c2ecf20Sopenharmony_ci#else
3478c2ecf20Sopenharmony_ci	0x80,			/* don't save bad frames */
3488c2ecf20Sopenharmony_ci#endif
3498c2ecf20Sopenharmony_ci	0x2E,			/* No source address insertion, 8 byte preamble */
3508c2ecf20Sopenharmony_ci	0x00,			/* priority and backoff defaults */
3518c2ecf20Sopenharmony_ci	0x60,			/* interframe spacing */
3528c2ecf20Sopenharmony_ci	0x00,			/* slot time LSB */
3538c2ecf20Sopenharmony_ci	0xf2,			/* slot time and retries */
3548c2ecf20Sopenharmony_ci	0x00,			/* promiscuous mode */
3558c2ecf20Sopenharmony_ci	0x00,			/* collision detect */
3568c2ecf20Sopenharmony_ci	0x40,			/* minimum frame length */
3578c2ecf20Sopenharmony_ci	0xff,
3588c2ecf20Sopenharmony_ci	0x00,
3598c2ecf20Sopenharmony_ci	0x7f /*  *multi IA */ };
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int i596_open(struct net_device *dev);
3628c2ecf20Sopenharmony_cistatic netdev_tx_t i596_start_xmit(struct sk_buff *skb, struct net_device *dev);
3638c2ecf20Sopenharmony_cistatic irqreturn_t i596_interrupt(int irq, void *dev_id);
3648c2ecf20Sopenharmony_cistatic int i596_close(struct net_device *dev);
3658c2ecf20Sopenharmony_cistatic void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd);
3668c2ecf20Sopenharmony_cistatic void i596_tx_timeout (struct net_device *dev, unsigned int txqueue);
3678c2ecf20Sopenharmony_cistatic void print_eth(unsigned char *buf, char *str);
3688c2ecf20Sopenharmony_cistatic void set_multicast_list(struct net_device *dev);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic int rx_ring_size = RX_RING_SIZE;
3718c2ecf20Sopenharmony_cistatic int ticks_limit = 25;
3728c2ecf20Sopenharmony_cistatic int max_cmd_backlog = TX_RING_SIZE-1;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic inline void CA(struct net_device *dev)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci#ifdef ENABLE_MVME16x_NET
3788c2ecf20Sopenharmony_ci	if (MACH_IS_MVME16x) {
3798c2ecf20Sopenharmony_ci		((struct i596_reg *) dev->base_addr)->ca = 1;
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci#endif
3828c2ecf20Sopenharmony_ci#ifdef ENABLE_BVME6000_NET
3838c2ecf20Sopenharmony_ci	if (MACH_IS_BVME6000) {
3848c2ecf20Sopenharmony_ci		volatile u32 i;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		i = *(volatile u32 *) (dev->base_addr);
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci#endif
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic inline void MPU_PORT(struct net_device *dev, int c, volatile void *x)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci#ifdef ENABLE_MVME16x_NET
3958c2ecf20Sopenharmony_ci	if (MACH_IS_MVME16x) {
3968c2ecf20Sopenharmony_ci		struct i596_reg *p = (struct i596_reg *) (dev->base_addr);
3978c2ecf20Sopenharmony_ci		p->porthi = ((c) | (u32) (x)) & 0xffff;
3988c2ecf20Sopenharmony_ci		p->portlo = ((c) | (u32) (x)) >> 16;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci#endif
4018c2ecf20Sopenharmony_ci#ifdef ENABLE_BVME6000_NET
4028c2ecf20Sopenharmony_ci	if (MACH_IS_BVME6000) {
4038c2ecf20Sopenharmony_ci		u32 v = (u32) (c) | (u32) (x);
4048c2ecf20Sopenharmony_ci		v = ((u32) (v) << 16) | ((u32) (v) >> 16);
4058c2ecf20Sopenharmony_ci		*(volatile u32 *) dev->base_addr = v;
4068c2ecf20Sopenharmony_ci		udelay(1);
4078c2ecf20Sopenharmony_ci		*(volatile u32 *) dev->base_addr = v;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci#endif
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic inline int wait_istat(struct net_device *dev, struct i596_private *lp, int delcnt, char *str)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	while (--delcnt && lp->iscp.stat)
4168c2ecf20Sopenharmony_ci		udelay(10);
4178c2ecf20Sopenharmony_ci	if (!delcnt) {
4188c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: %s, status %4.4x, cmd %4.4x.\n",
4198c2ecf20Sopenharmony_ci		     dev->name, str, lp->scb.status, lp->scb.command);
4208c2ecf20Sopenharmony_ci		return -1;
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci	else
4238c2ecf20Sopenharmony_ci		return 0;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic inline int wait_cmd(struct net_device *dev, struct i596_private *lp, int delcnt, char *str)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	while (--delcnt && lp->scb.command)
4308c2ecf20Sopenharmony_ci		udelay(10);
4318c2ecf20Sopenharmony_ci	if (!delcnt) {
4328c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: %s, status %4.4x, cmd %4.4x.\n",
4338c2ecf20Sopenharmony_ci		     dev->name, str, lp->scb.status, lp->scb.command);
4348c2ecf20Sopenharmony_ci		return -1;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci	else
4378c2ecf20Sopenharmony_ci		return 0;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic inline int wait_cfg(struct net_device *dev, struct i596_cmd *cmd, int delcnt, char *str)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	volatile struct i596_cmd *c = cmd;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	while (--delcnt && c->command)
4468c2ecf20Sopenharmony_ci		udelay(10);
4478c2ecf20Sopenharmony_ci	if (!delcnt) {
4488c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: %s.\n", dev->name, str);
4498c2ecf20Sopenharmony_ci		return -1;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci	else
4528c2ecf20Sopenharmony_ci		return 0;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic void i596_display_data(struct net_device *dev)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	struct i596_private *lp = dev->ml_priv;
4598c2ecf20Sopenharmony_ci	struct i596_cmd *cmd;
4608c2ecf20Sopenharmony_ci	struct i596_rfd *rfd;
4618c2ecf20Sopenharmony_ci	struct i596_rbd *rbd;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	printk(KERN_ERR "lp and scp at %p, .sysbus = %08lx, .iscp = %p\n",
4648c2ecf20Sopenharmony_ci	       &lp->scp, lp->scp.sysbus, lp->scp.iscp);
4658c2ecf20Sopenharmony_ci	printk(KERN_ERR "iscp at %p, iscp.stat = %08lx, .scb = %p\n",
4668c2ecf20Sopenharmony_ci	       &lp->iscp, lp->iscp.stat, lp->iscp.scb);
4678c2ecf20Sopenharmony_ci	printk(KERN_ERR "scb at %p, scb.status = %04x, .command = %04x,"
4688c2ecf20Sopenharmony_ci		" .cmd = %p, .rfd = %p\n",
4698c2ecf20Sopenharmony_ci	       &lp->scb, lp->scb.status, lp->scb.command,
4708c2ecf20Sopenharmony_ci		lp->scb.cmd, lp->scb.rfd);
4718c2ecf20Sopenharmony_ci	printk(KERN_ERR "   errors: crc %lx, align %lx, resource %lx,"
4728c2ecf20Sopenharmony_ci               " over %lx, rcvdt %lx, short %lx\n",
4738c2ecf20Sopenharmony_ci		lp->scb.crc_err, lp->scb.align_err, lp->scb.resource_err,
4748c2ecf20Sopenharmony_ci		lp->scb.over_err, lp->scb.rcvdt_err, lp->scb.short_err);
4758c2ecf20Sopenharmony_ci	cmd = lp->cmd_head;
4768c2ecf20Sopenharmony_ci	while (cmd != I596_NULL) {
4778c2ecf20Sopenharmony_ci		printk(KERN_ERR "cmd at %p, .status = %04x, .command = %04x, .b_next = %p\n",
4788c2ecf20Sopenharmony_ci		  cmd, cmd->status, cmd->command, cmd->b_next);
4798c2ecf20Sopenharmony_ci		cmd = cmd->v_next;
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci	rfd = lp->rfd_head;
4828c2ecf20Sopenharmony_ci	printk(KERN_ERR "rfd_head = %p\n", rfd);
4838c2ecf20Sopenharmony_ci	do {
4848c2ecf20Sopenharmony_ci		printk(KERN_ERR "   %p .stat %04x, .cmd %04x, b_next %p, rbd %p,"
4858c2ecf20Sopenharmony_ci                        " count %04x\n",
4868c2ecf20Sopenharmony_ci			rfd, rfd->stat, rfd->cmd, rfd->b_next, rfd->rbd,
4878c2ecf20Sopenharmony_ci			rfd->count);
4888c2ecf20Sopenharmony_ci		rfd = rfd->v_next;
4898c2ecf20Sopenharmony_ci	} while (rfd != lp->rfd_head);
4908c2ecf20Sopenharmony_ci	rbd = lp->rbd_head;
4918c2ecf20Sopenharmony_ci	printk(KERN_ERR "rbd_head = %p\n", rbd);
4928c2ecf20Sopenharmony_ci	do {
4938c2ecf20Sopenharmony_ci		printk(KERN_ERR "   %p .count %04x, b_next %p, b_data %p, size %04x\n",
4948c2ecf20Sopenharmony_ci			rbd, rbd->count, rbd->b_next, rbd->b_data, rbd->size);
4958c2ecf20Sopenharmony_ci		rbd = rbd->v_next;
4968c2ecf20Sopenharmony_ci	} while (rbd != lp->rbd_head);
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci#if defined(ENABLE_MVME16x_NET) || defined(ENABLE_BVME6000_NET)
5018c2ecf20Sopenharmony_cistatic irqreturn_t i596_error(int irq, void *dev_id)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
5048c2ecf20Sopenharmony_ci#ifdef ENABLE_MVME16x_NET
5058c2ecf20Sopenharmony_ci	if (MACH_IS_MVME16x) {
5068c2ecf20Sopenharmony_ci		volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci		pcc2[0x28] = 1;
5098c2ecf20Sopenharmony_ci		pcc2[0x2b] = 0x1d;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci#endif
5128c2ecf20Sopenharmony_ci#ifdef ENABLE_BVME6000_NET
5138c2ecf20Sopenharmony_ci	if (MACH_IS_BVME6000) {
5148c2ecf20Sopenharmony_ci		volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci		*ethirq = 1;
5178c2ecf20Sopenharmony_ci		*ethirq = 3;
5188c2ecf20Sopenharmony_ci	}
5198c2ecf20Sopenharmony_ci#endif
5208c2ecf20Sopenharmony_ci	printk(KERN_ERR "%s: Error interrupt\n", dev->name);
5218c2ecf20Sopenharmony_ci	i596_display_data(dev);
5228c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci#endif
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic inline void remove_rx_bufs(struct net_device *dev)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	struct i596_private *lp = dev->ml_priv;
5298c2ecf20Sopenharmony_ci	struct i596_rbd *rbd;
5308c2ecf20Sopenharmony_ci	int i;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) {
5338c2ecf20Sopenharmony_ci		if (rbd->skb == NULL)
5348c2ecf20Sopenharmony_ci			break;
5358c2ecf20Sopenharmony_ci		dev_kfree_skb(rbd->skb);
5368c2ecf20Sopenharmony_ci		rbd->skb = NULL;
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic inline int init_rx_bufs(struct net_device *dev)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	struct i596_private *lp = dev->ml_priv;
5438c2ecf20Sopenharmony_ci	int i;
5448c2ecf20Sopenharmony_ci	struct i596_rfd *rfd;
5458c2ecf20Sopenharmony_ci	struct i596_rbd *rbd;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	/* First build the Receive Buffer Descriptor List */
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) {
5508c2ecf20Sopenharmony_ci		struct sk_buff *skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci		if (skb == NULL) {
5538c2ecf20Sopenharmony_ci			remove_rx_bufs(dev);
5548c2ecf20Sopenharmony_ci			return -ENOMEM;
5558c2ecf20Sopenharmony_ci		}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci		rbd->v_next = rbd+1;
5588c2ecf20Sopenharmony_ci		rbd->b_next = WSWAPrbd(virt_to_bus(rbd+1));
5598c2ecf20Sopenharmony_ci		rbd->b_addr = WSWAPrbd(virt_to_bus(rbd));
5608c2ecf20Sopenharmony_ci		rbd->skb = skb;
5618c2ecf20Sopenharmony_ci		rbd->v_data = skb->data;
5628c2ecf20Sopenharmony_ci		rbd->b_data = WSWAPchar(virt_to_bus(skb->data));
5638c2ecf20Sopenharmony_ci		rbd->size = PKT_BUF_SZ;
5648c2ecf20Sopenharmony_ci#ifdef __mc68000__
5658c2ecf20Sopenharmony_ci		cache_clear(virt_to_phys(skb->data), PKT_BUF_SZ);
5668c2ecf20Sopenharmony_ci#endif
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci	lp->rbd_head = lp->rbds;
5698c2ecf20Sopenharmony_ci	rbd = lp->rbds + rx_ring_size - 1;
5708c2ecf20Sopenharmony_ci	rbd->v_next = lp->rbds;
5718c2ecf20Sopenharmony_ci	rbd->b_next = WSWAPrbd(virt_to_bus(lp->rbds));
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	/* Now build the Receive Frame Descriptor List */
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	for (i = 0, rfd = lp->rfds; i < rx_ring_size; i++, rfd++) {
5768c2ecf20Sopenharmony_ci		rfd->rbd = I596_NULL;
5778c2ecf20Sopenharmony_ci		rfd->v_next = rfd+1;
5788c2ecf20Sopenharmony_ci		rfd->v_prev = rfd-1;
5798c2ecf20Sopenharmony_ci		rfd->b_next = WSWAPrfd(virt_to_bus(rfd+1));
5808c2ecf20Sopenharmony_ci		rfd->cmd = CMD_FLEX;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci	lp->rfd_head = lp->rfds;
5838c2ecf20Sopenharmony_ci	lp->scb.rfd = WSWAPrfd(virt_to_bus(lp->rfds));
5848c2ecf20Sopenharmony_ci	rfd = lp->rfds;
5858c2ecf20Sopenharmony_ci	rfd->rbd = lp->rbd_head;
5868c2ecf20Sopenharmony_ci	rfd->v_prev = lp->rfds + rx_ring_size - 1;
5878c2ecf20Sopenharmony_ci	rfd = lp->rfds + rx_ring_size - 1;
5888c2ecf20Sopenharmony_ci	rfd->v_next = lp->rfds;
5898c2ecf20Sopenharmony_ci	rfd->b_next = WSWAPrfd(virt_to_bus(lp->rfds));
5908c2ecf20Sopenharmony_ci	rfd->cmd = CMD_EOL|CMD_FLEX;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	return 0;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic void rebuild_rx_bufs(struct net_device *dev)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	struct i596_private *lp = dev->ml_priv;
5998c2ecf20Sopenharmony_ci	int i;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	/* Ensure rx frame/buffer descriptors are tidy */
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	for (i = 0; i < rx_ring_size; i++) {
6048c2ecf20Sopenharmony_ci		lp->rfds[i].rbd = I596_NULL;
6058c2ecf20Sopenharmony_ci		lp->rfds[i].cmd = CMD_FLEX;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci	lp->rfds[rx_ring_size-1].cmd = CMD_EOL|CMD_FLEX;
6088c2ecf20Sopenharmony_ci	lp->rfd_head = lp->rfds;
6098c2ecf20Sopenharmony_ci	lp->scb.rfd = WSWAPrfd(virt_to_bus(lp->rfds));
6108c2ecf20Sopenharmony_ci	lp->rbd_head = lp->rbds;
6118c2ecf20Sopenharmony_ci	lp->rfds[0].rbd = WSWAPrbd(virt_to_bus(lp->rbds));
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic int init_i596_mem(struct net_device *dev)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	struct i596_private *lp = dev->ml_priv;
6188c2ecf20Sopenharmony_ci	unsigned long flags;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	MPU_PORT(dev, PORT_RESET, NULL);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	udelay(100);		/* Wait 100us - seems to help */
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci#if defined(ENABLE_MVME16x_NET) || defined(ENABLE_BVME6000_NET)
6258c2ecf20Sopenharmony_ci#ifdef ENABLE_MVME16x_NET
6268c2ecf20Sopenharmony_ci	if (MACH_IS_MVME16x) {
6278c2ecf20Sopenharmony_ci		volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci		/* Disable all ints for now */
6308c2ecf20Sopenharmony_ci		pcc2[0x28] = 1;
6318c2ecf20Sopenharmony_ci		pcc2[0x2a] = 0x48;
6328c2ecf20Sopenharmony_ci		/* Following disables snooping.  Snooping is not required
6338c2ecf20Sopenharmony_ci		 * as we make appropriate use of non-cached pages for
6348c2ecf20Sopenharmony_ci		 * shared data, and cache_push/cache_clear.
6358c2ecf20Sopenharmony_ci		 */
6368c2ecf20Sopenharmony_ci		pcc2[0x2b] = 0x08;
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci#endif
6398c2ecf20Sopenharmony_ci#ifdef ENABLE_BVME6000_NET
6408c2ecf20Sopenharmony_ci	if (MACH_IS_BVME6000) {
6418c2ecf20Sopenharmony_ci		volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci		*ethirq = 1;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci#endif
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	/* change the scp address */
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	MPU_PORT(dev, PORT_ALTSCP, (void *)virt_to_bus((void *)&lp->scp));
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci#endif
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	lp->last_cmd = jiffies;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci#ifdef ENABLE_MVME16x_NET
6568c2ecf20Sopenharmony_ci	if (MACH_IS_MVME16x)
6578c2ecf20Sopenharmony_ci		lp->scp.sysbus = 0x00000054;
6588c2ecf20Sopenharmony_ci#endif
6598c2ecf20Sopenharmony_ci#ifdef ENABLE_BVME6000_NET
6608c2ecf20Sopenharmony_ci	if (MACH_IS_BVME6000)
6618c2ecf20Sopenharmony_ci		lp->scp.sysbus = 0x0000004c;
6628c2ecf20Sopenharmony_ci#endif
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	lp->scp.iscp = WSWAPiscp(virt_to_bus((void *)&lp->iscp));
6658c2ecf20Sopenharmony_ci	lp->iscp.scb = WSWAPscb(virt_to_bus((void *)&lp->scb));
6668c2ecf20Sopenharmony_ci	lp->iscp.stat = ISCP_BUSY;
6678c2ecf20Sopenharmony_ci	lp->cmd_backlog = 0;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	lp->cmd_head = lp->scb.cmd = I596_NULL;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci#ifdef ENABLE_BVME6000_NET
6728c2ecf20Sopenharmony_ci	if (MACH_IS_BVME6000) {
6738c2ecf20Sopenharmony_ci		lp->scb.t_on  = 7 * 25;
6748c2ecf20Sopenharmony_ci		lp->scb.t_off = 1 * 25;
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci#endif
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	DEB(DEB_INIT,printk(KERN_DEBUG "%s: starting i82596.\n", dev->name));
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	CA(dev);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	if (wait_istat(dev,lp,1000,"initialization timed out"))
6838c2ecf20Sopenharmony_ci		goto failed;
6848c2ecf20Sopenharmony_ci	DEB(DEB_INIT,printk(KERN_DEBUG "%s: i82596 initialization successful\n", dev->name));
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	/* Ensure rx frame/buffer descriptors are tidy */
6878c2ecf20Sopenharmony_ci	rebuild_rx_bufs(dev);
6888c2ecf20Sopenharmony_ci	lp->scb.command = 0;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci#ifdef ENABLE_MVME16x_NET
6918c2ecf20Sopenharmony_ci	if (MACH_IS_MVME16x) {
6928c2ecf20Sopenharmony_ci		volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci		/* Enable ints, etc. now */
6958c2ecf20Sopenharmony_ci		pcc2[0x2a] = 0x55;	/* Edge sensitive */
6968c2ecf20Sopenharmony_ci		pcc2[0x2b] = 0x15;
6978c2ecf20Sopenharmony_ci	}
6988c2ecf20Sopenharmony_ci#endif
6998c2ecf20Sopenharmony_ci#ifdef ENABLE_BVME6000_NET
7008c2ecf20Sopenharmony_ci	if (MACH_IS_BVME6000) {
7018c2ecf20Sopenharmony_ci		volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci		*ethirq = 3;
7048c2ecf20Sopenharmony_ci	}
7058c2ecf20Sopenharmony_ci#endif
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	DEB(DEB_INIT,printk(KERN_DEBUG "%s: queuing CmdConfigure\n", dev->name));
7098c2ecf20Sopenharmony_ci	memcpy(lp->cf_cmd.i596_config, init_setup, 14);
7108c2ecf20Sopenharmony_ci	lp->cf_cmd.cmd.command = CmdConfigure;
7118c2ecf20Sopenharmony_ci	i596_add_cmd(dev, &lp->cf_cmd.cmd);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	DEB(DEB_INIT,printk(KERN_DEBUG "%s: queuing CmdSASetup\n", dev->name));
7148c2ecf20Sopenharmony_ci	memcpy(lp->sa_cmd.eth_addr, dev->dev_addr, ETH_ALEN);
7158c2ecf20Sopenharmony_ci	lp->sa_cmd.cmd.command = CmdSASetup;
7168c2ecf20Sopenharmony_ci	i596_add_cmd(dev, &lp->sa_cmd.cmd);
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	DEB(DEB_INIT,printk(KERN_DEBUG "%s: queuing CmdTDR\n", dev->name));
7198c2ecf20Sopenharmony_ci	lp->tdr_cmd.cmd.command = CmdTDR;
7208c2ecf20Sopenharmony_ci	i596_add_cmd(dev, &lp->tdr_cmd.cmd);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	spin_lock_irqsave (&lp->lock, flags);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	if (wait_cmd(dev,lp,1000,"timed out waiting to issue RX_START")) {
7258c2ecf20Sopenharmony_ci		spin_unlock_irqrestore (&lp->lock, flags);
7268c2ecf20Sopenharmony_ci		goto failed;
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_ci	DEB(DEB_INIT,printk(KERN_DEBUG "%s: Issuing RX_START\n", dev->name));
7298c2ecf20Sopenharmony_ci	lp->scb.command = RX_START;
7308c2ecf20Sopenharmony_ci	CA(dev);
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore (&lp->lock, flags);
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	if (wait_cmd(dev,lp,1000,"RX_START not processed"))
7358c2ecf20Sopenharmony_ci		goto failed;
7368c2ecf20Sopenharmony_ci	DEB(DEB_INIT,printk(KERN_DEBUG "%s: Receive unit started OK\n", dev->name));
7378c2ecf20Sopenharmony_ci	return 0;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_cifailed:
7408c2ecf20Sopenharmony_ci	printk(KERN_CRIT "%s: Failed to initialise 82596\n", dev->name);
7418c2ecf20Sopenharmony_ci	MPU_PORT(dev, PORT_RESET, NULL);
7428c2ecf20Sopenharmony_ci	return -1;
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_cistatic inline int i596_rx(struct net_device *dev)
7468c2ecf20Sopenharmony_ci{
7478c2ecf20Sopenharmony_ci	struct i596_private *lp = dev->ml_priv;
7488c2ecf20Sopenharmony_ci	struct i596_rfd *rfd;
7498c2ecf20Sopenharmony_ci	struct i596_rbd *rbd;
7508c2ecf20Sopenharmony_ci	int frames = 0;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	DEB(DEB_RXFRAME,printk(KERN_DEBUG "i596_rx(), rfd_head %p, rbd_head %p\n",
7538c2ecf20Sopenharmony_ci			lp->rfd_head, lp->rbd_head));
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	rfd = lp->rfd_head;		/* Ref next frame to check */
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	while ((rfd->stat) & STAT_C) {	/* Loop while complete frames */
7588c2ecf20Sopenharmony_ci		if (rfd->rbd == I596_NULL)
7598c2ecf20Sopenharmony_ci			rbd = I596_NULL;
7608c2ecf20Sopenharmony_ci		else if (rfd->rbd == lp->rbd_head->b_addr)
7618c2ecf20Sopenharmony_ci			rbd = lp->rbd_head;
7628c2ecf20Sopenharmony_ci		else {
7638c2ecf20Sopenharmony_ci			printk(KERN_CRIT "%s: rbd chain broken!\n", dev->name);
7648c2ecf20Sopenharmony_ci			/* XXX Now what? */
7658c2ecf20Sopenharmony_ci			rbd = I596_NULL;
7668c2ecf20Sopenharmony_ci		}
7678c2ecf20Sopenharmony_ci		DEB(DEB_RXFRAME, printk(KERN_DEBUG "  rfd %p, rfd.rbd %p, rfd.stat %04x\n",
7688c2ecf20Sopenharmony_ci			rfd, rfd->rbd, rfd->stat));
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci		if (rbd != I596_NULL && ((rfd->stat) & STAT_OK)) {
7718c2ecf20Sopenharmony_ci			/* a good frame */
7728c2ecf20Sopenharmony_ci			int pkt_len = rbd->count & 0x3fff;
7738c2ecf20Sopenharmony_ci			struct sk_buff *skb = rbd->skb;
7748c2ecf20Sopenharmony_ci			int rx_in_place = 0;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci			DEB(DEB_RXADDR,print_eth(rbd->v_data, "received"));
7778c2ecf20Sopenharmony_ci			frames++;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci			/* Check if the packet is long enough to just accept
7808c2ecf20Sopenharmony_ci			 * without copying to a properly sized skbuff.
7818c2ecf20Sopenharmony_ci			 */
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci			if (pkt_len > rx_copybreak) {
7848c2ecf20Sopenharmony_ci				struct sk_buff *newskb;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci				/* Get fresh skbuff to replace filled one. */
7878c2ecf20Sopenharmony_ci				newskb = netdev_alloc_skb(dev, PKT_BUF_SZ);
7888c2ecf20Sopenharmony_ci				if (newskb == NULL) {
7898c2ecf20Sopenharmony_ci					skb = NULL;	/* drop pkt */
7908c2ecf20Sopenharmony_ci					goto memory_squeeze;
7918c2ecf20Sopenharmony_ci				}
7928c2ecf20Sopenharmony_ci				/* Pass up the skb already on the Rx ring. */
7938c2ecf20Sopenharmony_ci				skb_put(skb, pkt_len);
7948c2ecf20Sopenharmony_ci				rx_in_place = 1;
7958c2ecf20Sopenharmony_ci				rbd->skb = newskb;
7968c2ecf20Sopenharmony_ci				rbd->v_data = newskb->data;
7978c2ecf20Sopenharmony_ci				rbd->b_data = WSWAPchar(virt_to_bus(newskb->data));
7988c2ecf20Sopenharmony_ci#ifdef __mc68000__
7998c2ecf20Sopenharmony_ci				cache_clear(virt_to_phys(newskb->data), PKT_BUF_SZ);
8008c2ecf20Sopenharmony_ci#endif
8018c2ecf20Sopenharmony_ci			} else {
8028c2ecf20Sopenharmony_ci				skb = netdev_alloc_skb(dev, pkt_len + 2);
8038c2ecf20Sopenharmony_ci			}
8048c2ecf20Sopenharmony_cimemory_squeeze:
8058c2ecf20Sopenharmony_ci			if (skb == NULL) {
8068c2ecf20Sopenharmony_ci				/* XXX tulip.c can defer packets here!! */
8078c2ecf20Sopenharmony_ci				dev->stats.rx_dropped++;
8088c2ecf20Sopenharmony_ci			} else {
8098c2ecf20Sopenharmony_ci				if (!rx_in_place) {
8108c2ecf20Sopenharmony_ci					/* 16 byte align the data fields */
8118c2ecf20Sopenharmony_ci					skb_reserve(skb, 2);
8128c2ecf20Sopenharmony_ci					skb_put_data(skb, rbd->v_data,
8138c2ecf20Sopenharmony_ci						     pkt_len);
8148c2ecf20Sopenharmony_ci				}
8158c2ecf20Sopenharmony_ci				skb->protocol=eth_type_trans(skb,dev);
8168c2ecf20Sopenharmony_ci				skb->len = pkt_len;
8178c2ecf20Sopenharmony_ci#ifdef __mc68000__
8188c2ecf20Sopenharmony_ci				cache_clear(virt_to_phys(rbd->skb->data),
8198c2ecf20Sopenharmony_ci						pkt_len);
8208c2ecf20Sopenharmony_ci#endif
8218c2ecf20Sopenharmony_ci				netif_rx(skb);
8228c2ecf20Sopenharmony_ci				dev->stats.rx_packets++;
8238c2ecf20Sopenharmony_ci				dev->stats.rx_bytes+=pkt_len;
8248c2ecf20Sopenharmony_ci			}
8258c2ecf20Sopenharmony_ci		}
8268c2ecf20Sopenharmony_ci		else {
8278c2ecf20Sopenharmony_ci			DEB(DEB_ERRORS, printk(KERN_DEBUG "%s: Error, rfd.stat = 0x%04x\n",
8288c2ecf20Sopenharmony_ci					dev->name, rfd->stat));
8298c2ecf20Sopenharmony_ci			dev->stats.rx_errors++;
8308c2ecf20Sopenharmony_ci			if ((rfd->stat) & 0x0001)
8318c2ecf20Sopenharmony_ci				dev->stats.collisions++;
8328c2ecf20Sopenharmony_ci			if ((rfd->stat) & 0x0080)
8338c2ecf20Sopenharmony_ci				dev->stats.rx_length_errors++;
8348c2ecf20Sopenharmony_ci			if ((rfd->stat) & 0x0100)
8358c2ecf20Sopenharmony_ci				dev->stats.rx_over_errors++;
8368c2ecf20Sopenharmony_ci			if ((rfd->stat) & 0x0200)
8378c2ecf20Sopenharmony_ci				dev->stats.rx_fifo_errors++;
8388c2ecf20Sopenharmony_ci			if ((rfd->stat) & 0x0400)
8398c2ecf20Sopenharmony_ci				dev->stats.rx_frame_errors++;
8408c2ecf20Sopenharmony_ci			if ((rfd->stat) & 0x0800)
8418c2ecf20Sopenharmony_ci				dev->stats.rx_crc_errors++;
8428c2ecf20Sopenharmony_ci			if ((rfd->stat) & 0x1000)
8438c2ecf20Sopenharmony_ci				dev->stats.rx_length_errors++;
8448c2ecf20Sopenharmony_ci		}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci		/* Clear the buffer descriptor count and EOF + F flags */
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci		if (rbd != I596_NULL && (rbd->count & 0x4000)) {
8498c2ecf20Sopenharmony_ci			rbd->count = 0;
8508c2ecf20Sopenharmony_ci			lp->rbd_head = rbd->v_next;
8518c2ecf20Sopenharmony_ci		}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci		/* Tidy the frame descriptor, marking it as end of list */
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci		rfd->rbd = I596_NULL;
8568c2ecf20Sopenharmony_ci		rfd->stat = 0;
8578c2ecf20Sopenharmony_ci		rfd->cmd = CMD_EOL|CMD_FLEX;
8588c2ecf20Sopenharmony_ci		rfd->count = 0;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci		/* Remove end-of-list from old end descriptor */
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci		rfd->v_prev->cmd = CMD_FLEX;
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci		/* Update record of next frame descriptor to process */
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci		lp->scb.rfd = rfd->b_next;
8678c2ecf20Sopenharmony_ci		lp->rfd_head = rfd->v_next;
8688c2ecf20Sopenharmony_ci		rfd = lp->rfd_head;
8698c2ecf20Sopenharmony_ci	}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	DEB(DEB_RXFRAME,printk(KERN_DEBUG "frames %d\n", frames));
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	return 0;
8748c2ecf20Sopenharmony_ci}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_cistatic void i596_cleanup_cmd(struct net_device *dev, struct i596_private *lp)
8788c2ecf20Sopenharmony_ci{
8798c2ecf20Sopenharmony_ci	struct i596_cmd *ptr;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	while (lp->cmd_head != I596_NULL) {
8828c2ecf20Sopenharmony_ci		ptr = lp->cmd_head;
8838c2ecf20Sopenharmony_ci		lp->cmd_head = ptr->v_next;
8848c2ecf20Sopenharmony_ci		lp->cmd_backlog--;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci		switch ((ptr->command) & 0x7) {
8878c2ecf20Sopenharmony_ci		case CmdTx:
8888c2ecf20Sopenharmony_ci			{
8898c2ecf20Sopenharmony_ci				struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr;
8908c2ecf20Sopenharmony_ci				struct sk_buff *skb = tx_cmd->skb;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci				dev_kfree_skb(skb);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci				dev->stats.tx_errors++;
8958c2ecf20Sopenharmony_ci				dev->stats.tx_aborted_errors++;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci				ptr->v_next = ptr->b_next = I596_NULL;
8988c2ecf20Sopenharmony_ci				tx_cmd->cmd.command = 0;  /* Mark as free */
8998c2ecf20Sopenharmony_ci				break;
9008c2ecf20Sopenharmony_ci			}
9018c2ecf20Sopenharmony_ci		default:
9028c2ecf20Sopenharmony_ci			ptr->v_next = ptr->b_next = I596_NULL;
9038c2ecf20Sopenharmony_ci		}
9048c2ecf20Sopenharmony_ci	}
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	wait_cmd(dev,lp,100,"i596_cleanup_cmd timed out");
9078c2ecf20Sopenharmony_ci	lp->scb.cmd = I596_NULL;
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_cistatic void i596_reset(struct net_device *dev, struct i596_private *lp,
9118c2ecf20Sopenharmony_ci			int ioaddr)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	unsigned long flags;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	DEB(DEB_RESET,printk(KERN_DEBUG "i596_reset\n"));
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	spin_lock_irqsave (&lp->lock, flags);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	wait_cmd(dev,lp,100,"i596_reset timed out");
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	lp->scb.command = CUC_ABORT | RX_ABORT;
9248c2ecf20Sopenharmony_ci	CA(dev);
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	/* wait for shutdown */
9278c2ecf20Sopenharmony_ci	wait_cmd(dev,lp,1000,"i596_reset 2 timed out");
9288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore (&lp->lock, flags);
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	i596_cleanup_cmd(dev,lp);
9318c2ecf20Sopenharmony_ci	i596_rx(dev);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	netif_start_queue(dev);
9348c2ecf20Sopenharmony_ci	init_i596_mem(dev);
9358c2ecf20Sopenharmony_ci}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_cistatic void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd)
9388c2ecf20Sopenharmony_ci{
9398c2ecf20Sopenharmony_ci	struct i596_private *lp = dev->ml_priv;
9408c2ecf20Sopenharmony_ci	int ioaddr = dev->base_addr;
9418c2ecf20Sopenharmony_ci	unsigned long flags;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	DEB(DEB_ADDCMD,printk(KERN_DEBUG "i596_add_cmd\n"));
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	cmd->status = 0;
9468c2ecf20Sopenharmony_ci	cmd->command |= (CMD_EOL | CMD_INTR);
9478c2ecf20Sopenharmony_ci	cmd->v_next = cmd->b_next = I596_NULL;
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	spin_lock_irqsave (&lp->lock, flags);
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	if (lp->cmd_head != I596_NULL) {
9528c2ecf20Sopenharmony_ci		lp->cmd_tail->v_next = cmd;
9538c2ecf20Sopenharmony_ci		lp->cmd_tail->b_next = WSWAPcmd(virt_to_bus(&cmd->status));
9548c2ecf20Sopenharmony_ci	} else {
9558c2ecf20Sopenharmony_ci		lp->cmd_head = cmd;
9568c2ecf20Sopenharmony_ci		wait_cmd(dev,lp,100,"i596_add_cmd timed out");
9578c2ecf20Sopenharmony_ci		lp->scb.cmd = WSWAPcmd(virt_to_bus(&cmd->status));
9588c2ecf20Sopenharmony_ci		lp->scb.command = CUC_START;
9598c2ecf20Sopenharmony_ci		CA(dev);
9608c2ecf20Sopenharmony_ci	}
9618c2ecf20Sopenharmony_ci	lp->cmd_tail = cmd;
9628c2ecf20Sopenharmony_ci	lp->cmd_backlog++;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore (&lp->lock, flags);
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	if (lp->cmd_backlog > max_cmd_backlog) {
9678c2ecf20Sopenharmony_ci		unsigned long tickssofar = jiffies - lp->last_cmd;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci		if (tickssofar < ticks_limit)
9708c2ecf20Sopenharmony_ci			return;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci		printk(KERN_NOTICE "%s: command unit timed out, status resetting.\n", dev->name);
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci		i596_reset(dev, lp, ioaddr);
9758c2ecf20Sopenharmony_ci	}
9768c2ecf20Sopenharmony_ci}
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_cistatic int i596_open(struct net_device *dev)
9798c2ecf20Sopenharmony_ci{
9808c2ecf20Sopenharmony_ci	int res = 0;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	DEB(DEB_OPEN,printk(KERN_DEBUG "%s: i596_open() irq %d.\n", dev->name, dev->irq));
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	if (request_irq(dev->irq, i596_interrupt, 0, "i82596", dev)) {
9858c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: IRQ %d not free\n", dev->name, dev->irq);
9868c2ecf20Sopenharmony_ci		return -EAGAIN;
9878c2ecf20Sopenharmony_ci	}
9888c2ecf20Sopenharmony_ci#ifdef ENABLE_MVME16x_NET
9898c2ecf20Sopenharmony_ci	if (MACH_IS_MVME16x) {
9908c2ecf20Sopenharmony_ci		if (request_irq(0x56, i596_error, 0, "i82596_error", dev)) {
9918c2ecf20Sopenharmony_ci			res = -EAGAIN;
9928c2ecf20Sopenharmony_ci			goto err_irq_dev;
9938c2ecf20Sopenharmony_ci		}
9948c2ecf20Sopenharmony_ci	}
9958c2ecf20Sopenharmony_ci#endif
9968c2ecf20Sopenharmony_ci	res = init_rx_bufs(dev);
9978c2ecf20Sopenharmony_ci	if (res)
9988c2ecf20Sopenharmony_ci		goto err_irq_56;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	netif_start_queue(dev);
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	if (init_i596_mem(dev)) {
10038c2ecf20Sopenharmony_ci		res = -EAGAIN;
10048c2ecf20Sopenharmony_ci		goto err_queue;
10058c2ecf20Sopenharmony_ci	}
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	return 0;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_cierr_queue:
10108c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
10118c2ecf20Sopenharmony_ci	remove_rx_bufs(dev);
10128c2ecf20Sopenharmony_cierr_irq_56:
10138c2ecf20Sopenharmony_ci#ifdef ENABLE_MVME16x_NET
10148c2ecf20Sopenharmony_ci	free_irq(0x56, dev);
10158c2ecf20Sopenharmony_cierr_irq_dev:
10168c2ecf20Sopenharmony_ci#endif
10178c2ecf20Sopenharmony_ci	free_irq(dev->irq, dev);
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	return res;
10208c2ecf20Sopenharmony_ci}
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_cistatic void i596_tx_timeout (struct net_device *dev, unsigned int txqueue)
10238c2ecf20Sopenharmony_ci{
10248c2ecf20Sopenharmony_ci	struct i596_private *lp = dev->ml_priv;
10258c2ecf20Sopenharmony_ci	int ioaddr = dev->base_addr;
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	/* Transmitter timeout, serious problems. */
10288c2ecf20Sopenharmony_ci	DEB(DEB_ERRORS,printk(KERN_ERR "%s: transmit timed out, status resetting.\n",
10298c2ecf20Sopenharmony_ci			dev->name));
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	dev->stats.tx_errors++;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	/* Try to restart the adaptor */
10348c2ecf20Sopenharmony_ci	if (lp->last_restart == dev->stats.tx_packets) {
10358c2ecf20Sopenharmony_ci		DEB(DEB_ERRORS,printk(KERN_ERR "Resetting board.\n"));
10368c2ecf20Sopenharmony_ci		/* Shutdown and restart */
10378c2ecf20Sopenharmony_ci		i596_reset (dev, lp, ioaddr);
10388c2ecf20Sopenharmony_ci	} else {
10398c2ecf20Sopenharmony_ci		/* Issue a channel attention signal */
10408c2ecf20Sopenharmony_ci		DEB(DEB_ERRORS,printk(KERN_ERR "Kicking board.\n"));
10418c2ecf20Sopenharmony_ci		lp->scb.command = CUC_START | RX_START;
10428c2ecf20Sopenharmony_ci		CA (dev);
10438c2ecf20Sopenharmony_ci		lp->last_restart = dev->stats.tx_packets;
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
10478c2ecf20Sopenharmony_ci	netif_wake_queue (dev);
10488c2ecf20Sopenharmony_ci}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_cistatic netdev_tx_t i596_start_xmit(struct sk_buff *skb, struct net_device *dev)
10518c2ecf20Sopenharmony_ci{
10528c2ecf20Sopenharmony_ci	struct i596_private *lp = dev->ml_priv;
10538c2ecf20Sopenharmony_ci	struct tx_cmd *tx_cmd;
10548c2ecf20Sopenharmony_ci	struct i596_tbd *tbd;
10558c2ecf20Sopenharmony_ci	short length = skb->len;
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	DEB(DEB_STARTTX,printk(KERN_DEBUG "%s: i596_start_xmit(%x,%p) called\n",
10588c2ecf20Sopenharmony_ci				dev->name, skb->len, skb->data));
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	if (skb->len < ETH_ZLEN) {
10618c2ecf20Sopenharmony_ci		if (skb_padto(skb, ETH_ZLEN))
10628c2ecf20Sopenharmony_ci			return NETDEV_TX_OK;
10638c2ecf20Sopenharmony_ci		length = ETH_ZLEN;
10648c2ecf20Sopenharmony_ci	}
10658c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	tx_cmd = lp->tx_cmds + lp->next_tx_cmd;
10688c2ecf20Sopenharmony_ci	tbd = lp->tbds + lp->next_tx_cmd;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	if (tx_cmd->cmd.command) {
10718c2ecf20Sopenharmony_ci		printk(KERN_NOTICE "%s: xmit ring full, dropping packet.\n",
10728c2ecf20Sopenharmony_ci				dev->name);
10738c2ecf20Sopenharmony_ci		dev->stats.tx_dropped++;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
10768c2ecf20Sopenharmony_ci	} else {
10778c2ecf20Sopenharmony_ci		if (++lp->next_tx_cmd == TX_RING_SIZE)
10788c2ecf20Sopenharmony_ci			lp->next_tx_cmd = 0;
10798c2ecf20Sopenharmony_ci		tx_cmd->tbd = WSWAPtbd(virt_to_bus(tbd));
10808c2ecf20Sopenharmony_ci		tbd->next = I596_NULL;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci		tx_cmd->cmd.command = CMD_FLEX | CmdTx;
10838c2ecf20Sopenharmony_ci		tx_cmd->skb = skb;
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci		tx_cmd->pad = 0;
10868c2ecf20Sopenharmony_ci		tx_cmd->size = 0;
10878c2ecf20Sopenharmony_ci		tbd->pad = 0;
10888c2ecf20Sopenharmony_ci		tbd->size = EOF | length;
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci		tbd->data = WSWAPchar(virt_to_bus(skb->data));
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci#ifdef __mc68000__
10938c2ecf20Sopenharmony_ci		cache_push(virt_to_phys(skb->data), length);
10948c2ecf20Sopenharmony_ci#endif
10958c2ecf20Sopenharmony_ci		DEB(DEB_TXADDR,print_eth(skb->data, "tx-queued"));
10968c2ecf20Sopenharmony_ci		i596_add_cmd(dev, &tx_cmd->cmd);
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci		dev->stats.tx_packets++;
10998c2ecf20Sopenharmony_ci		dev->stats.tx_bytes += length;
11008c2ecf20Sopenharmony_ci	}
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	netif_start_queue(dev);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
11058c2ecf20Sopenharmony_ci}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_cistatic void print_eth(unsigned char *add, char *str)
11088c2ecf20Sopenharmony_ci{
11098c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "i596 0x%p, %pM --> %pM %02X%02X, %s\n",
11108c2ecf20Sopenharmony_ci	       add, add + 6, add, add[12], add[13], str);
11118c2ecf20Sopenharmony_ci}
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_cistatic int io = 0x300;
11148c2ecf20Sopenharmony_cistatic int irq = 10;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_cistatic const struct net_device_ops i596_netdev_ops = {
11178c2ecf20Sopenharmony_ci	.ndo_open 		= i596_open,
11188c2ecf20Sopenharmony_ci	.ndo_stop		= i596_close,
11198c2ecf20Sopenharmony_ci	.ndo_start_xmit		= i596_start_xmit,
11208c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= set_multicast_list,
11218c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= i596_tx_timeout,
11228c2ecf20Sopenharmony_ci	.ndo_set_mac_address 	= eth_mac_addr,
11238c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
11248c2ecf20Sopenharmony_ci};
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_cistruct net_device * __init i82596_probe(int unit)
11278c2ecf20Sopenharmony_ci{
11288c2ecf20Sopenharmony_ci	struct net_device *dev;
11298c2ecf20Sopenharmony_ci	int i;
11308c2ecf20Sopenharmony_ci	struct i596_private *lp;
11318c2ecf20Sopenharmony_ci	char eth_addr[8];
11328c2ecf20Sopenharmony_ci	static int probed;
11338c2ecf20Sopenharmony_ci	int err;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	if (probed)
11368c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
11378c2ecf20Sopenharmony_ci	probed++;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	dev = alloc_etherdev(0);
11408c2ecf20Sopenharmony_ci	if (!dev)
11418c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	if (unit >= 0) {
11448c2ecf20Sopenharmony_ci		sprintf(dev->name, "eth%d", unit);
11458c2ecf20Sopenharmony_ci		netdev_boot_setup_check(dev);
11468c2ecf20Sopenharmony_ci	} else {
11478c2ecf20Sopenharmony_ci		dev->base_addr = io;
11488c2ecf20Sopenharmony_ci		dev->irq = irq;
11498c2ecf20Sopenharmony_ci	}
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci#ifdef ENABLE_MVME16x_NET
11528c2ecf20Sopenharmony_ci	if (MACH_IS_MVME16x) {
11538c2ecf20Sopenharmony_ci		if (mvme16x_config & MVME16x_CONFIG_NO_ETHERNET) {
11548c2ecf20Sopenharmony_ci			printk(KERN_NOTICE "Ethernet probe disabled - chip not present\n");
11558c2ecf20Sopenharmony_ci			err = -ENODEV;
11568c2ecf20Sopenharmony_ci			goto out;
11578c2ecf20Sopenharmony_ci		}
11588c2ecf20Sopenharmony_ci		memcpy(eth_addr, absolute_pointer(0xfffc1f2c), ETH_ALEN); /* YUCK! Get addr from NOVRAM */
11598c2ecf20Sopenharmony_ci		dev->base_addr = MVME_I596_BASE;
11608c2ecf20Sopenharmony_ci		dev->irq = (unsigned) MVME16x_IRQ_I596;
11618c2ecf20Sopenharmony_ci		goto found;
11628c2ecf20Sopenharmony_ci	}
11638c2ecf20Sopenharmony_ci#endif
11648c2ecf20Sopenharmony_ci#ifdef ENABLE_BVME6000_NET
11658c2ecf20Sopenharmony_ci	if (MACH_IS_BVME6000) {
11668c2ecf20Sopenharmony_ci		volatile unsigned char *rtc = (unsigned char *) BVME_RTC_BASE;
11678c2ecf20Sopenharmony_ci		unsigned char msr = rtc[3];
11688c2ecf20Sopenharmony_ci		int i;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci		rtc[3] |= 0x80;
11718c2ecf20Sopenharmony_ci		for (i = 0; i < 6; i++)
11728c2ecf20Sopenharmony_ci			eth_addr[i] = rtc[i * 4 + 7];	/* Stored in RTC RAM at offset 1 */
11738c2ecf20Sopenharmony_ci		rtc[3] = msr;
11748c2ecf20Sopenharmony_ci		dev->base_addr = BVME_I596_BASE;
11758c2ecf20Sopenharmony_ci		dev->irq = (unsigned) BVME_IRQ_I596;
11768c2ecf20Sopenharmony_ci		goto found;
11778c2ecf20Sopenharmony_ci	}
11788c2ecf20Sopenharmony_ci#endif
11798c2ecf20Sopenharmony_ci	err = -ENODEV;
11808c2ecf20Sopenharmony_ci	goto out;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_cifound:
11838c2ecf20Sopenharmony_ci	dev->mem_start = (int)__get_free_pages(GFP_ATOMIC, 0);
11848c2ecf20Sopenharmony_ci	if (!dev->mem_start) {
11858c2ecf20Sopenharmony_ci		err = -ENOMEM;
11868c2ecf20Sopenharmony_ci		goto out1;
11878c2ecf20Sopenharmony_ci	}
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	DEB(DEB_PROBE,printk(KERN_INFO "%s: 82596 at %#3lx,", dev->name, dev->base_addr));
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++)
11928c2ecf20Sopenharmony_ci		DEB(DEB_PROBE,printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]));
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	DEB(DEB_PROBE,printk(" IRQ %d.\n", dev->irq));
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	DEB(DEB_PROBE,printk(KERN_INFO "%s", version));
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	/* The 82596-specific entries in the device structure. */
11998c2ecf20Sopenharmony_ci	dev->netdev_ops = &i596_netdev_ops;
12008c2ecf20Sopenharmony_ci	dev->watchdog_timeo = TX_TIMEOUT;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	dev->ml_priv = (void *)(dev->mem_start);
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	lp = dev->ml_priv;
12058c2ecf20Sopenharmony_ci	DEB(DEB_INIT,printk(KERN_DEBUG "%s: lp at 0x%08lx (%zd bytes), "
12068c2ecf20Sopenharmony_ci			"lp->scb at 0x%08lx\n",
12078c2ecf20Sopenharmony_ci			dev->name, (unsigned long)lp,
12088c2ecf20Sopenharmony_ci			sizeof(struct i596_private), (unsigned long)&lp->scb));
12098c2ecf20Sopenharmony_ci	memset((void *) lp, 0, sizeof(struct i596_private));
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci#ifdef __mc68000__
12128c2ecf20Sopenharmony_ci	cache_push(virt_to_phys((void *)(dev->mem_start)), 4096);
12138c2ecf20Sopenharmony_ci	cache_clear(virt_to_phys((void *)(dev->mem_start)), 4096);
12148c2ecf20Sopenharmony_ci	kernel_set_cachemode((void *)(dev->mem_start), 4096, IOMAP_NOCACHE_SER);
12158c2ecf20Sopenharmony_ci#endif
12168c2ecf20Sopenharmony_ci	lp->scb.command = 0;
12178c2ecf20Sopenharmony_ci	lp->scb.cmd = I596_NULL;
12188c2ecf20Sopenharmony_ci	lp->scb.rfd = I596_NULL;
12198c2ecf20Sopenharmony_ci	spin_lock_init(&lp->lock);
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	err = register_netdev(dev);
12228c2ecf20Sopenharmony_ci	if (err)
12238c2ecf20Sopenharmony_ci		goto out2;
12248c2ecf20Sopenharmony_ci	return dev;
12258c2ecf20Sopenharmony_ciout2:
12268c2ecf20Sopenharmony_ci#ifdef __mc68000__
12278c2ecf20Sopenharmony_ci	/* XXX This assumes default cache mode to be IOMAP_FULL_CACHING,
12288c2ecf20Sopenharmony_ci	 * XXX which may be invalid (CONFIG_060_WRITETHROUGH)
12298c2ecf20Sopenharmony_ci	 */
12308c2ecf20Sopenharmony_ci	kernel_set_cachemode((void *)(dev->mem_start), 4096,
12318c2ecf20Sopenharmony_ci			IOMAP_FULL_CACHING);
12328c2ecf20Sopenharmony_ci#endif
12338c2ecf20Sopenharmony_ci	free_page ((u32)(dev->mem_start));
12348c2ecf20Sopenharmony_ciout1:
12358c2ecf20Sopenharmony_ciout:
12368c2ecf20Sopenharmony_ci	free_netdev(dev);
12378c2ecf20Sopenharmony_ci	return ERR_PTR(err);
12388c2ecf20Sopenharmony_ci}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_cistatic irqreturn_t i596_interrupt(int irq, void *dev_id)
12418c2ecf20Sopenharmony_ci{
12428c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
12438c2ecf20Sopenharmony_ci	struct i596_private *lp;
12448c2ecf20Sopenharmony_ci	short ioaddr;
12458c2ecf20Sopenharmony_ci	unsigned short status, ack_cmd = 0;
12468c2ecf20Sopenharmony_ci	int handled = 0;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci#ifdef ENABLE_BVME6000_NET
12498c2ecf20Sopenharmony_ci	if (MACH_IS_BVME6000) {
12508c2ecf20Sopenharmony_ci		if (*(char *) BVME_LOCAL_IRQ_STAT & BVME_ETHERR) {
12518c2ecf20Sopenharmony_ci			i596_error(irq, dev_id);
12528c2ecf20Sopenharmony_ci			return IRQ_HANDLED;
12538c2ecf20Sopenharmony_ci		}
12548c2ecf20Sopenharmony_ci	}
12558c2ecf20Sopenharmony_ci#endif
12568c2ecf20Sopenharmony_ci	if (dev == NULL) {
12578c2ecf20Sopenharmony_ci		printk(KERN_ERR "i596_interrupt(): irq %d for unknown device.\n", irq);
12588c2ecf20Sopenharmony_ci		return IRQ_NONE;
12598c2ecf20Sopenharmony_ci	}
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	ioaddr = dev->base_addr;
12628c2ecf20Sopenharmony_ci	lp = dev->ml_priv;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	spin_lock (&lp->lock);
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	wait_cmd(dev,lp,100,"i596 interrupt, timeout");
12678c2ecf20Sopenharmony_ci	status = lp->scb.status;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt, IRQ %d, status %4.4x.\n",
12708c2ecf20Sopenharmony_ci			dev->name, irq, status));
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	ack_cmd = status & 0xf000;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	if ((status & 0x8000) || (status & 0x2000)) {
12758c2ecf20Sopenharmony_ci		struct i596_cmd *ptr;
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci		handled = 1;
12788c2ecf20Sopenharmony_ci		if ((status & 0x8000))
12798c2ecf20Sopenharmony_ci			DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt completed command.\n", dev->name));
12808c2ecf20Sopenharmony_ci		if ((status & 0x2000))
12818c2ecf20Sopenharmony_ci			DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt command unit inactive %x.\n", dev->name, status & 0x0700));
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci		while ((lp->cmd_head != I596_NULL) && (lp->cmd_head->status & STAT_C)) {
12848c2ecf20Sopenharmony_ci			ptr = lp->cmd_head;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci			DEB(DEB_STATUS,printk(KERN_DEBUG "cmd_head->status = %04x, ->command = %04x\n",
12878c2ecf20Sopenharmony_ci				       lp->cmd_head->status, lp->cmd_head->command));
12888c2ecf20Sopenharmony_ci			lp->cmd_head = ptr->v_next;
12898c2ecf20Sopenharmony_ci			lp->cmd_backlog--;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci			switch ((ptr->command) & 0x7) {
12928c2ecf20Sopenharmony_ci			case CmdTx:
12938c2ecf20Sopenharmony_ci			    {
12948c2ecf20Sopenharmony_ci				struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr;
12958c2ecf20Sopenharmony_ci				struct sk_buff *skb = tx_cmd->skb;
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci				if ((ptr->status) & STAT_OK) {
12988c2ecf20Sopenharmony_ci					DEB(DEB_TXADDR,print_eth(skb->data, "tx-done"));
12998c2ecf20Sopenharmony_ci				} else {
13008c2ecf20Sopenharmony_ci					dev->stats.tx_errors++;
13018c2ecf20Sopenharmony_ci					if ((ptr->status) & 0x0020)
13028c2ecf20Sopenharmony_ci						dev->stats.collisions++;
13038c2ecf20Sopenharmony_ci					if (!((ptr->status) & 0x0040))
13048c2ecf20Sopenharmony_ci						dev->stats.tx_heartbeat_errors++;
13058c2ecf20Sopenharmony_ci					if ((ptr->status) & 0x0400)
13068c2ecf20Sopenharmony_ci						dev->stats.tx_carrier_errors++;
13078c2ecf20Sopenharmony_ci					if ((ptr->status) & 0x0800)
13088c2ecf20Sopenharmony_ci						dev->stats.collisions++;
13098c2ecf20Sopenharmony_ci					if ((ptr->status) & 0x1000)
13108c2ecf20Sopenharmony_ci						dev->stats.tx_aborted_errors++;
13118c2ecf20Sopenharmony_ci				}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci				dev_consume_skb_irq(skb);
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci				tx_cmd->cmd.command = 0; /* Mark free */
13168c2ecf20Sopenharmony_ci				break;
13178c2ecf20Sopenharmony_ci			    }
13188c2ecf20Sopenharmony_ci			case CmdTDR:
13198c2ecf20Sopenharmony_ci			    {
13208c2ecf20Sopenharmony_ci				unsigned short status = ((struct tdr_cmd *)ptr)->status;
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci				if (status & 0x8000) {
13238c2ecf20Sopenharmony_ci					DEB(DEB_TDR,printk(KERN_INFO "%s: link ok.\n", dev->name));
13248c2ecf20Sopenharmony_ci				} else {
13258c2ecf20Sopenharmony_ci					if (status & 0x4000)
13268c2ecf20Sopenharmony_ci						printk(KERN_ERR "%s: Transceiver problem.\n", dev->name);
13278c2ecf20Sopenharmony_ci					if (status & 0x2000)
13288c2ecf20Sopenharmony_ci						printk(KERN_ERR "%s: Termination problem.\n", dev->name);
13298c2ecf20Sopenharmony_ci					if (status & 0x1000)
13308c2ecf20Sopenharmony_ci						printk(KERN_ERR "%s: Short circuit.\n", dev->name);
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci					DEB(DEB_TDR,printk(KERN_INFO "%s: Time %d.\n", dev->name, status & 0x07ff));
13338c2ecf20Sopenharmony_ci				}
13348c2ecf20Sopenharmony_ci				break;
13358c2ecf20Sopenharmony_ci			    }
13368c2ecf20Sopenharmony_ci			case CmdConfigure:
13378c2ecf20Sopenharmony_ci			case CmdMulticastList:
13388c2ecf20Sopenharmony_ci				/* Zap command so set_multicast_list() knows it is free */
13398c2ecf20Sopenharmony_ci				ptr->command = 0;
13408c2ecf20Sopenharmony_ci				break;
13418c2ecf20Sopenharmony_ci			}
13428c2ecf20Sopenharmony_ci			ptr->v_next = ptr->b_next = I596_NULL;
13438c2ecf20Sopenharmony_ci			lp->last_cmd = jiffies;
13448c2ecf20Sopenharmony_ci		}
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci		ptr = lp->cmd_head;
13478c2ecf20Sopenharmony_ci		while ((ptr != I596_NULL) && (ptr != lp->cmd_tail)) {
13488c2ecf20Sopenharmony_ci			ptr->command &= 0x1fff;
13498c2ecf20Sopenharmony_ci			ptr = ptr->v_next;
13508c2ecf20Sopenharmony_ci		}
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci		if ((lp->cmd_head != I596_NULL))
13538c2ecf20Sopenharmony_ci			ack_cmd |= CUC_START;
13548c2ecf20Sopenharmony_ci		lp->scb.cmd = WSWAPcmd(virt_to_bus(&lp->cmd_head->status));
13558c2ecf20Sopenharmony_ci	}
13568c2ecf20Sopenharmony_ci	if ((status & 0x1000) || (status & 0x4000)) {
13578c2ecf20Sopenharmony_ci		if ((status & 0x4000))
13588c2ecf20Sopenharmony_ci			DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt received a frame.\n", dev->name));
13598c2ecf20Sopenharmony_ci		i596_rx(dev);
13608c2ecf20Sopenharmony_ci		/* Only RX_START if stopped - RGH 07-07-96 */
13618c2ecf20Sopenharmony_ci		if (status & 0x1000) {
13628c2ecf20Sopenharmony_ci			if (netif_running(dev)) {
13638c2ecf20Sopenharmony_ci				DEB(DEB_ERRORS,printk(KERN_ERR "%s: i596 interrupt receive unit inactive, status 0x%x\n", dev->name, status));
13648c2ecf20Sopenharmony_ci				ack_cmd |= RX_START;
13658c2ecf20Sopenharmony_ci				dev->stats.rx_errors++;
13668c2ecf20Sopenharmony_ci				dev->stats.rx_fifo_errors++;
13678c2ecf20Sopenharmony_ci				rebuild_rx_bufs(dev);
13688c2ecf20Sopenharmony_ci			}
13698c2ecf20Sopenharmony_ci		}
13708c2ecf20Sopenharmony_ci	}
13718c2ecf20Sopenharmony_ci	wait_cmd(dev,lp,100,"i596 interrupt, timeout");
13728c2ecf20Sopenharmony_ci	lp->scb.command = ack_cmd;
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci#ifdef ENABLE_MVME16x_NET
13758c2ecf20Sopenharmony_ci	if (MACH_IS_MVME16x) {
13768c2ecf20Sopenharmony_ci		/* Ack the interrupt */
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci		volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci		pcc2[0x2a] |= 0x08;
13818c2ecf20Sopenharmony_ci	}
13828c2ecf20Sopenharmony_ci#endif
13838c2ecf20Sopenharmony_ci#ifdef ENABLE_BVME6000_NET
13848c2ecf20Sopenharmony_ci	if (MACH_IS_BVME6000) {
13858c2ecf20Sopenharmony_ci		volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci		*ethirq = 1;
13888c2ecf20Sopenharmony_ci		*ethirq = 3;
13898c2ecf20Sopenharmony_ci	}
13908c2ecf20Sopenharmony_ci#endif
13918c2ecf20Sopenharmony_ci	CA(dev);
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	DEB(DEB_INTS,printk(KERN_DEBUG "%s: exiting interrupt.\n", dev->name));
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	spin_unlock (&lp->lock);
13968c2ecf20Sopenharmony_ci	return IRQ_RETVAL(handled);
13978c2ecf20Sopenharmony_ci}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_cistatic int i596_close(struct net_device *dev)
14008c2ecf20Sopenharmony_ci{
14018c2ecf20Sopenharmony_ci	struct i596_private *lp = dev->ml_priv;
14028c2ecf20Sopenharmony_ci	unsigned long flags;
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	DEB(DEB_INIT,printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
14078c2ecf20Sopenharmony_ci		       dev->name, lp->scb.status));
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	wait_cmd(dev,lp,100,"close1 timed out");
14128c2ecf20Sopenharmony_ci	lp->scb.command = CUC_ABORT | RX_ABORT;
14138c2ecf20Sopenharmony_ci	CA(dev);
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	wait_cmd(dev,lp,100,"close2 timed out");
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&lp->lock, flags);
14188c2ecf20Sopenharmony_ci	DEB(DEB_STRUCT,i596_display_data(dev));
14198c2ecf20Sopenharmony_ci	i596_cleanup_cmd(dev,lp);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci#ifdef ENABLE_MVME16x_NET
14228c2ecf20Sopenharmony_ci	if (MACH_IS_MVME16x) {
14238c2ecf20Sopenharmony_ci		volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci		/* Disable all ints */
14268c2ecf20Sopenharmony_ci		pcc2[0x28] = 1;
14278c2ecf20Sopenharmony_ci		pcc2[0x2a] = 0x40;
14288c2ecf20Sopenharmony_ci		pcc2[0x2b] = 0x40;	/* Set snooping bits now! */
14298c2ecf20Sopenharmony_ci	}
14308c2ecf20Sopenharmony_ci#endif
14318c2ecf20Sopenharmony_ci#ifdef ENABLE_BVME6000_NET
14328c2ecf20Sopenharmony_ci	if (MACH_IS_BVME6000) {
14338c2ecf20Sopenharmony_ci		volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci		*ethirq = 1;
14368c2ecf20Sopenharmony_ci	}
14378c2ecf20Sopenharmony_ci#endif
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci#ifdef ENABLE_MVME16x_NET
14408c2ecf20Sopenharmony_ci	free_irq(0x56, dev);
14418c2ecf20Sopenharmony_ci#endif
14428c2ecf20Sopenharmony_ci	free_irq(dev->irq, dev);
14438c2ecf20Sopenharmony_ci	remove_rx_bufs(dev);
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	return 0;
14468c2ecf20Sopenharmony_ci}
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci/*
14498c2ecf20Sopenharmony_ci *    Set or clear the multicast filter for this adaptor.
14508c2ecf20Sopenharmony_ci */
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_cistatic void set_multicast_list(struct net_device *dev)
14538c2ecf20Sopenharmony_ci{
14548c2ecf20Sopenharmony_ci	struct i596_private *lp = dev->ml_priv;
14558c2ecf20Sopenharmony_ci	int config = 0, cnt;
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	DEB(DEB_MULTI,printk(KERN_DEBUG "%s: set multicast list, %d entries, promisc %s, allmulti %s\n",
14588c2ecf20Sopenharmony_ci		dev->name, netdev_mc_count(dev),
14598c2ecf20Sopenharmony_ci		dev->flags & IFF_PROMISC  ? "ON" : "OFF",
14608c2ecf20Sopenharmony_ci		dev->flags & IFF_ALLMULTI ? "ON" : "OFF"));
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	if (wait_cfg(dev, &lp->cf_cmd.cmd, 1000, "config change request timed out"))
14638c2ecf20Sopenharmony_ci		return;
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	if ((dev->flags & IFF_PROMISC) && !(lp->cf_cmd.i596_config[8] & 0x01)) {
14668c2ecf20Sopenharmony_ci		lp->cf_cmd.i596_config[8] |= 0x01;
14678c2ecf20Sopenharmony_ci		config = 1;
14688c2ecf20Sopenharmony_ci	}
14698c2ecf20Sopenharmony_ci	if (!(dev->flags & IFF_PROMISC) && (lp->cf_cmd.i596_config[8] & 0x01)) {
14708c2ecf20Sopenharmony_ci		lp->cf_cmd.i596_config[8] &= ~0x01;
14718c2ecf20Sopenharmony_ci		config = 1;
14728c2ecf20Sopenharmony_ci	}
14738c2ecf20Sopenharmony_ci	if ((dev->flags & IFF_ALLMULTI) && (lp->cf_cmd.i596_config[11] & 0x20)) {
14748c2ecf20Sopenharmony_ci		lp->cf_cmd.i596_config[11] &= ~0x20;
14758c2ecf20Sopenharmony_ci		config = 1;
14768c2ecf20Sopenharmony_ci	}
14778c2ecf20Sopenharmony_ci	if (!(dev->flags & IFF_ALLMULTI) && !(lp->cf_cmd.i596_config[11] & 0x20)) {
14788c2ecf20Sopenharmony_ci		lp->cf_cmd.i596_config[11] |= 0x20;
14798c2ecf20Sopenharmony_ci		config = 1;
14808c2ecf20Sopenharmony_ci	}
14818c2ecf20Sopenharmony_ci	if (config) {
14828c2ecf20Sopenharmony_ci		lp->cf_cmd.cmd.command = CmdConfigure;
14838c2ecf20Sopenharmony_ci		i596_add_cmd(dev, &lp->cf_cmd.cmd);
14848c2ecf20Sopenharmony_ci	}
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	cnt = netdev_mc_count(dev);
14878c2ecf20Sopenharmony_ci	if (cnt > MAX_MC_CNT)
14888c2ecf20Sopenharmony_ci	{
14898c2ecf20Sopenharmony_ci		cnt = MAX_MC_CNT;
14908c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: Only %d multicast addresses supported",
14918c2ecf20Sopenharmony_ci			dev->name, cnt);
14928c2ecf20Sopenharmony_ci	}
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	if (!netdev_mc_empty(dev)) {
14958c2ecf20Sopenharmony_ci		struct netdev_hw_addr *ha;
14968c2ecf20Sopenharmony_ci		unsigned char *cp;
14978c2ecf20Sopenharmony_ci		struct mc_cmd *cmd;
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci		if (wait_cfg(dev, &lp->mc_cmd.cmd, 1000, "multicast list change request timed out"))
15008c2ecf20Sopenharmony_ci			return;
15018c2ecf20Sopenharmony_ci		cmd = &lp->mc_cmd;
15028c2ecf20Sopenharmony_ci		cmd->cmd.command = CmdMulticastList;
15038c2ecf20Sopenharmony_ci		cmd->mc_cnt = cnt * ETH_ALEN;
15048c2ecf20Sopenharmony_ci		cp = cmd->mc_addrs;
15058c2ecf20Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev) {
15068c2ecf20Sopenharmony_ci			if (!cnt--)
15078c2ecf20Sopenharmony_ci				break;
15088c2ecf20Sopenharmony_ci			memcpy(cp, ha->addr, ETH_ALEN);
15098c2ecf20Sopenharmony_ci			if (i596_debug > 1)
15108c2ecf20Sopenharmony_ci				DEB(DEB_MULTI,printk(KERN_INFO "%s: Adding address %pM\n",
15118c2ecf20Sopenharmony_ci						dev->name, cp));
15128c2ecf20Sopenharmony_ci			cp += ETH_ALEN;
15138c2ecf20Sopenharmony_ci		}
15148c2ecf20Sopenharmony_ci		i596_add_cmd(dev, &cmd->cmd);
15158c2ecf20Sopenharmony_ci	}
15168c2ecf20Sopenharmony_ci}
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci#ifdef MODULE
15198c2ecf20Sopenharmony_cistatic struct net_device *dev_82596;
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_cistatic int debug = -1;
15228c2ecf20Sopenharmony_cimodule_param(debug, int, 0);
15238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "i82596 debug mask");
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ciint __init init_module(void)
15268c2ecf20Sopenharmony_ci{
15278c2ecf20Sopenharmony_ci	if (debug >= 0)
15288c2ecf20Sopenharmony_ci		i596_debug = debug;
15298c2ecf20Sopenharmony_ci	dev_82596 = i82596_probe(-1);
15308c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(dev_82596);
15318c2ecf20Sopenharmony_ci}
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_civoid __exit cleanup_module(void)
15348c2ecf20Sopenharmony_ci{
15358c2ecf20Sopenharmony_ci	unregister_netdev(dev_82596);
15368c2ecf20Sopenharmony_ci#ifdef __mc68000__
15378c2ecf20Sopenharmony_ci	/* XXX This assumes default cache mode to be IOMAP_FULL_CACHING,
15388c2ecf20Sopenharmony_ci	 * XXX which may be invalid (CONFIG_060_WRITETHROUGH)
15398c2ecf20Sopenharmony_ci	 */
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	kernel_set_cachemode((void *)(dev_82596->mem_start), 4096,
15428c2ecf20Sopenharmony_ci			IOMAP_FULL_CACHING);
15438c2ecf20Sopenharmony_ci#endif
15448c2ecf20Sopenharmony_ci	free_page ((u32)(dev_82596->mem_start));
15458c2ecf20Sopenharmony_ci	free_netdev(dev_82596);
15468c2ecf20Sopenharmony_ci}
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci#endif				/* MODULE */
1549