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