162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/* cassini.c: Sun Microsystems Cassini(+) ethernet driver.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2004 Sun Microsystems Inc.
562306a36Sopenharmony_ci * Copyright (C) 2003 Adrian Sun (asun@darksunrising.com)
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This driver uses the sungem driver (c) David Miller
862306a36Sopenharmony_ci * (davem@redhat.com) as its basis.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * The cassini chip has a number of features that distinguish it from
1162306a36Sopenharmony_ci * the gem chip:
1262306a36Sopenharmony_ci *  4 transmit descriptor rings that are used for either QoS (VLAN) or
1362306a36Sopenharmony_ci *      load balancing (non-VLAN mode)
1462306a36Sopenharmony_ci *  batching of multiple packets
1562306a36Sopenharmony_ci *  multiple CPU dispatching
1662306a36Sopenharmony_ci *  page-based RX descriptor engine with separate completion rings
1762306a36Sopenharmony_ci *  Gigabit support (GMII and PCS interface)
1862306a36Sopenharmony_ci *  MIF link up/down detection works
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * RX is handled by page sized buffers that are attached as fragments to
2162306a36Sopenharmony_ci * the skb. here's what's done:
2262306a36Sopenharmony_ci *  -- driver allocates pages at a time and keeps reference counts
2362306a36Sopenharmony_ci *     on them.
2462306a36Sopenharmony_ci *  -- the upper protocol layers assume that the header is in the skb
2562306a36Sopenharmony_ci *     itself. as a result, cassini will copy a small amount (64 bytes)
2662306a36Sopenharmony_ci *     to make them happy.
2762306a36Sopenharmony_ci *  -- driver appends the rest of the data pages as frags to skbuffs
2862306a36Sopenharmony_ci *     and increments the reference count
2962306a36Sopenharmony_ci *  -- on page reclamation, the driver swaps the page with a spare page.
3062306a36Sopenharmony_ci *     if that page is still in use, it frees its reference to that page,
3162306a36Sopenharmony_ci *     and allocates a new page for use. otherwise, it just recycles the
3262306a36Sopenharmony_ci *     page.
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * NOTE: cassini can parse the header. however, it's not worth it
3562306a36Sopenharmony_ci *       as long as the network stack requires a header copy.
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * TX has 4 queues. currently these queues are used in a round-robin
3862306a36Sopenharmony_ci * fashion for load balancing. They can also be used for QoS. for that
3962306a36Sopenharmony_ci * to work, however, QoS information needs to be exposed down to the driver
4062306a36Sopenharmony_ci * level so that subqueues get targeted to particular transmit rings.
4162306a36Sopenharmony_ci * alternatively, the queues can be configured via use of the all-purpose
4262306a36Sopenharmony_ci * ioctl.
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * RX DATA: the rx completion ring has all the info, but the rx desc
4562306a36Sopenharmony_ci * ring has all of the data. RX can conceivably come in under multiple
4662306a36Sopenharmony_ci * interrupts, but the INT# assignment needs to be set up properly by
4762306a36Sopenharmony_ci * the BIOS and conveyed to the driver. PCI BIOSes don't know how to do
4862306a36Sopenharmony_ci * that. also, the two descriptor rings are designed to distinguish between
4962306a36Sopenharmony_ci * encrypted and non-encrypted packets, but we use them for buffering
5062306a36Sopenharmony_ci * instead.
5162306a36Sopenharmony_ci *
5262306a36Sopenharmony_ci * by default, the selective clear mask is set up to process rx packets.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#include <linux/module.h>
5862306a36Sopenharmony_ci#include <linux/kernel.h>
5962306a36Sopenharmony_ci#include <linux/types.h>
6062306a36Sopenharmony_ci#include <linux/compiler.h>
6162306a36Sopenharmony_ci#include <linux/slab.h>
6262306a36Sopenharmony_ci#include <linux/delay.h>
6362306a36Sopenharmony_ci#include <linux/init.h>
6462306a36Sopenharmony_ci#include <linux/interrupt.h>
6562306a36Sopenharmony_ci#include <linux/vmalloc.h>
6662306a36Sopenharmony_ci#include <linux/ioport.h>
6762306a36Sopenharmony_ci#include <linux/pci.h>
6862306a36Sopenharmony_ci#include <linux/mm.h>
6962306a36Sopenharmony_ci#include <linux/highmem.h>
7062306a36Sopenharmony_ci#include <linux/list.h>
7162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#include <linux/netdevice.h>
7462306a36Sopenharmony_ci#include <linux/etherdevice.h>
7562306a36Sopenharmony_ci#include <linux/skbuff.h>
7662306a36Sopenharmony_ci#include <linux/ethtool.h>
7762306a36Sopenharmony_ci#include <linux/crc32.h>
7862306a36Sopenharmony_ci#include <linux/random.h>
7962306a36Sopenharmony_ci#include <linux/mii.h>
8062306a36Sopenharmony_ci#include <linux/ip.h>
8162306a36Sopenharmony_ci#include <linux/tcp.h>
8262306a36Sopenharmony_ci#include <linux/mutex.h>
8362306a36Sopenharmony_ci#include <linux/firmware.h>
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#include <net/checksum.h>
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#include <linux/atomic.h>
8862306a36Sopenharmony_ci#include <asm/io.h>
8962306a36Sopenharmony_ci#include <asm/byteorder.h>
9062306a36Sopenharmony_ci#include <linux/uaccess.h>
9162306a36Sopenharmony_ci#include <linux/jiffies.h>
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define CAS_NCPUS            num_online_cpus()
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#define cas_skb_release(x)  netif_rx(x)
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* select which firmware to use */
9862306a36Sopenharmony_ci#define USE_HP_WORKAROUND
9962306a36Sopenharmony_ci#define HP_WORKAROUND_DEFAULT /* select which firmware to use as default */
10062306a36Sopenharmony_ci#define CAS_HP_ALT_FIRMWARE   cas_prog_null /* alternate firmware */
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci#include "cassini.h"
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#define USE_TX_COMPWB      /* use completion writeback registers */
10562306a36Sopenharmony_ci#define USE_CSMA_CD_PROTO  /* standard CSMA/CD */
10662306a36Sopenharmony_ci#define USE_RX_BLANK       /* hw interrupt mitigation */
10762306a36Sopenharmony_ci#undef USE_ENTROPY_DEV     /* don't test for entropy device */
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/* NOTE: these aren't useable unless PCI interrupts can be assigned.
11062306a36Sopenharmony_ci * also, we need to make cp->lock finer-grained.
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_ci#undef  USE_PCI_INTB
11362306a36Sopenharmony_ci#undef  USE_PCI_INTC
11462306a36Sopenharmony_ci#undef  USE_PCI_INTD
11562306a36Sopenharmony_ci#undef  USE_QOS
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#undef  USE_VPD_DEBUG       /* debug vpd information if defined */
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/* rx processing options */
12062306a36Sopenharmony_ci#define USE_PAGE_ORDER      /* specify to allocate large rx pages */
12162306a36Sopenharmony_ci#define RX_DONT_BATCH  0    /* if 1, don't batch flows */
12262306a36Sopenharmony_ci#define RX_COPY_ALWAYS 0    /* if 0, use frags */
12362306a36Sopenharmony_ci#define RX_COPY_MIN    64   /* copy a little to make upper layers happy */
12462306a36Sopenharmony_ci#undef  RX_COUNT_BUFFERS    /* define to calculate RX buffer stats */
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci#define DRV_MODULE_NAME		"cassini"
12762306a36Sopenharmony_ci#define DRV_MODULE_VERSION	"1.6"
12862306a36Sopenharmony_ci#define DRV_MODULE_RELDATE	"21 May 2008"
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#define CAS_DEF_MSG_ENABLE	  \
13162306a36Sopenharmony_ci	(NETIF_MSG_DRV		| \
13262306a36Sopenharmony_ci	 NETIF_MSG_PROBE	| \
13362306a36Sopenharmony_ci	 NETIF_MSG_LINK		| \
13462306a36Sopenharmony_ci	 NETIF_MSG_TIMER	| \
13562306a36Sopenharmony_ci	 NETIF_MSG_IFDOWN	| \
13662306a36Sopenharmony_ci	 NETIF_MSG_IFUP		| \
13762306a36Sopenharmony_ci	 NETIF_MSG_RX_ERR	| \
13862306a36Sopenharmony_ci	 NETIF_MSG_TX_ERR)
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/* length of time before we decide the hardware is borked,
14162306a36Sopenharmony_ci * and dev->tx_timeout() should be called to fix the problem
14262306a36Sopenharmony_ci */
14362306a36Sopenharmony_ci#define CAS_TX_TIMEOUT			(HZ)
14462306a36Sopenharmony_ci#define CAS_LINK_TIMEOUT                (22*HZ/10)
14562306a36Sopenharmony_ci#define CAS_LINK_FAST_TIMEOUT           (1)
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/* timeout values for state changing. these specify the number
14862306a36Sopenharmony_ci * of 10us delays to be used before giving up.
14962306a36Sopenharmony_ci */
15062306a36Sopenharmony_ci#define STOP_TRIES_PHY 1000
15162306a36Sopenharmony_ci#define STOP_TRIES     5000
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/* specify a minimum frame size to deal with some fifo issues
15462306a36Sopenharmony_ci * max mtu == 2 * page size - ethernet header - 64 - swivel =
15562306a36Sopenharmony_ci *            2 * page_size - 0x50
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_ci#define CAS_MIN_FRAME			97
15862306a36Sopenharmony_ci#define CAS_1000MB_MIN_FRAME            255
15962306a36Sopenharmony_ci#define CAS_MIN_MTU                     60
16062306a36Sopenharmony_ci#define CAS_MAX_MTU                     min(((cp->page_size << 1) - 0x50), 9000)
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci#if 1
16362306a36Sopenharmony_ci/*
16462306a36Sopenharmony_ci * Eliminate these and use separate atomic counters for each, to
16562306a36Sopenharmony_ci * avoid a race condition.
16662306a36Sopenharmony_ci */
16762306a36Sopenharmony_ci#else
16862306a36Sopenharmony_ci#define CAS_RESET_MTU                   1
16962306a36Sopenharmony_ci#define CAS_RESET_ALL                   2
17062306a36Sopenharmony_ci#define CAS_RESET_SPARE                 3
17162306a36Sopenharmony_ci#endif
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic char version[] =
17462306a36Sopenharmony_ci	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int cassini_debug = -1;	/* -1 == use CAS_DEF_MSG_ENABLE as value */
17762306a36Sopenharmony_cistatic int link_mode;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ciMODULE_AUTHOR("Adrian Sun (asun@darksunrising.com)");
18062306a36Sopenharmony_ciMODULE_DESCRIPTION("Sun Cassini(+) ethernet driver");
18162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
18262306a36Sopenharmony_ciMODULE_FIRMWARE("sun/cassini.bin");
18362306a36Sopenharmony_cimodule_param(cassini_debug, int, 0);
18462306a36Sopenharmony_ciMODULE_PARM_DESC(cassini_debug, "Cassini bitmapped debugging message enable value");
18562306a36Sopenharmony_cimodule_param(link_mode, int, 0);
18662306a36Sopenharmony_ciMODULE_PARM_DESC(link_mode, "default link mode");
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/*
18962306a36Sopenharmony_ci * Work around for a PCS bug in which the link goes down due to the chip
19062306a36Sopenharmony_ci * being confused and never showing a link status of "up."
19162306a36Sopenharmony_ci */
19262306a36Sopenharmony_ci#define DEFAULT_LINKDOWN_TIMEOUT 5
19362306a36Sopenharmony_ci/*
19462306a36Sopenharmony_ci * Value in seconds, for user input.
19562306a36Sopenharmony_ci */
19662306a36Sopenharmony_cistatic int linkdown_timeout = DEFAULT_LINKDOWN_TIMEOUT;
19762306a36Sopenharmony_cimodule_param(linkdown_timeout, int, 0);
19862306a36Sopenharmony_ciMODULE_PARM_DESC(linkdown_timeout,
19962306a36Sopenharmony_ci"min reset interval in sec. for PCS linkdown issue; disabled if not positive");
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/*
20262306a36Sopenharmony_ci * value in 'ticks' (units used by jiffies). Set when we init the
20362306a36Sopenharmony_ci * module because 'HZ' in actually a function call on some flavors of
20462306a36Sopenharmony_ci * Linux.  This will default to DEFAULT_LINKDOWN_TIMEOUT * HZ.
20562306a36Sopenharmony_ci */
20662306a36Sopenharmony_cistatic int link_transition_timeout;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic u16 link_modes[] = {
21162306a36Sopenharmony_ci	BMCR_ANENABLE,			 /* 0 : autoneg */
21262306a36Sopenharmony_ci	0,				 /* 1 : 10bt half duplex */
21362306a36Sopenharmony_ci	BMCR_SPEED100,			 /* 2 : 100bt half duplex */
21462306a36Sopenharmony_ci	BMCR_FULLDPLX,			 /* 3 : 10bt full duplex */
21562306a36Sopenharmony_ci	BMCR_SPEED100|BMCR_FULLDPLX,	 /* 4 : 100bt full duplex */
21662306a36Sopenharmony_ci	CAS_BMCR_SPEED1000|BMCR_FULLDPLX /* 5 : 1000bt full duplex */
21762306a36Sopenharmony_ci};
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic const struct pci_device_id cas_pci_tbl[] = {
22062306a36Sopenharmony_ci	{ PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_CASSINI,
22162306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
22262306a36Sopenharmony_ci	{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SATURN,
22362306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
22462306a36Sopenharmony_ci	{ 0, }
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cas_pci_tbl);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic void cas_set_link_modes(struct cas *cp);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic inline void cas_lock_tx(struct cas *cp)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	int i;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	for (i = 0; i < N_TX_RINGS; i++)
23662306a36Sopenharmony_ci		spin_lock_nested(&cp->tx_lock[i], i);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/* WTZ: QA was finding deadlock problems with the previous
24062306a36Sopenharmony_ci * versions after long test runs with multiple cards per machine.
24162306a36Sopenharmony_ci * See if replacing cas_lock_all with safer versions helps. The
24262306a36Sopenharmony_ci * symptoms QA is reporting match those we'd expect if interrupts
24362306a36Sopenharmony_ci * aren't being properly restored, and we fixed a previous deadlock
24462306a36Sopenharmony_ci * with similar symptoms by using save/restore versions in other
24562306a36Sopenharmony_ci * places.
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_ci#define cas_lock_all_save(cp, flags) \
24862306a36Sopenharmony_cido { \
24962306a36Sopenharmony_ci	struct cas *xxxcp = (cp); \
25062306a36Sopenharmony_ci	spin_lock_irqsave(&xxxcp->lock, flags); \
25162306a36Sopenharmony_ci	cas_lock_tx(xxxcp); \
25262306a36Sopenharmony_ci} while (0)
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic inline void cas_unlock_tx(struct cas *cp)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	int i;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	for (i = N_TX_RINGS; i > 0; i--)
25962306a36Sopenharmony_ci		spin_unlock(&cp->tx_lock[i - 1]);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci#define cas_unlock_all_restore(cp, flags) \
26362306a36Sopenharmony_cido { \
26462306a36Sopenharmony_ci	struct cas *xxxcp = (cp); \
26562306a36Sopenharmony_ci	cas_unlock_tx(xxxcp); \
26662306a36Sopenharmony_ci	spin_unlock_irqrestore(&xxxcp->lock, flags); \
26762306a36Sopenharmony_ci} while (0)
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic void cas_disable_irq(struct cas *cp, const int ring)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	/* Make sure we won't get any more interrupts */
27262306a36Sopenharmony_ci	if (ring == 0) {
27362306a36Sopenharmony_ci		writel(0xFFFFFFFF, cp->regs + REG_INTR_MASK);
27462306a36Sopenharmony_ci		return;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	/* disable completion interrupts and selectively mask */
27862306a36Sopenharmony_ci	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {
27962306a36Sopenharmony_ci		switch (ring) {
28062306a36Sopenharmony_ci#if defined (USE_PCI_INTB) || defined(USE_PCI_INTC) || defined(USE_PCI_INTD)
28162306a36Sopenharmony_ci#ifdef USE_PCI_INTB
28262306a36Sopenharmony_ci		case 1:
28362306a36Sopenharmony_ci#endif
28462306a36Sopenharmony_ci#ifdef USE_PCI_INTC
28562306a36Sopenharmony_ci		case 2:
28662306a36Sopenharmony_ci#endif
28762306a36Sopenharmony_ci#ifdef USE_PCI_INTD
28862306a36Sopenharmony_ci		case 3:
28962306a36Sopenharmony_ci#endif
29062306a36Sopenharmony_ci			writel(INTRN_MASK_CLEAR_ALL | INTRN_MASK_RX_EN,
29162306a36Sopenharmony_ci			       cp->regs + REG_PLUS_INTRN_MASK(ring));
29262306a36Sopenharmony_ci			break;
29362306a36Sopenharmony_ci#endif
29462306a36Sopenharmony_ci		default:
29562306a36Sopenharmony_ci			writel(INTRN_MASK_CLEAR_ALL, cp->regs +
29662306a36Sopenharmony_ci			       REG_PLUS_INTRN_MASK(ring));
29762306a36Sopenharmony_ci			break;
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic inline void cas_mask_intr(struct cas *cp)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	int i;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	for (i = 0; i < N_RX_COMP_RINGS; i++)
30762306a36Sopenharmony_ci		cas_disable_irq(cp, i);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic void cas_enable_irq(struct cas *cp, const int ring)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	if (ring == 0) { /* all but TX_DONE */
31362306a36Sopenharmony_ci		writel(INTR_TX_DONE, cp->regs + REG_INTR_MASK);
31462306a36Sopenharmony_ci		return;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {
31862306a36Sopenharmony_ci		switch (ring) {
31962306a36Sopenharmony_ci#if defined (USE_PCI_INTB) || defined(USE_PCI_INTC) || defined(USE_PCI_INTD)
32062306a36Sopenharmony_ci#ifdef USE_PCI_INTB
32162306a36Sopenharmony_ci		case 1:
32262306a36Sopenharmony_ci#endif
32362306a36Sopenharmony_ci#ifdef USE_PCI_INTC
32462306a36Sopenharmony_ci		case 2:
32562306a36Sopenharmony_ci#endif
32662306a36Sopenharmony_ci#ifdef USE_PCI_INTD
32762306a36Sopenharmony_ci		case 3:
32862306a36Sopenharmony_ci#endif
32962306a36Sopenharmony_ci			writel(INTRN_MASK_RX_EN, cp->regs +
33062306a36Sopenharmony_ci			       REG_PLUS_INTRN_MASK(ring));
33162306a36Sopenharmony_ci			break;
33262306a36Sopenharmony_ci#endif
33362306a36Sopenharmony_ci		default:
33462306a36Sopenharmony_ci			break;
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic inline void cas_unmask_intr(struct cas *cp)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	int i;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	for (i = 0; i < N_RX_COMP_RINGS; i++)
34462306a36Sopenharmony_ci		cas_enable_irq(cp, i);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic inline void cas_entropy_gather(struct cas *cp)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci#ifdef USE_ENTROPY_DEV
35062306a36Sopenharmony_ci	if ((cp->cas_flags & CAS_FLAG_ENTROPY_DEV) == 0)
35162306a36Sopenharmony_ci		return;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	batch_entropy_store(readl(cp->regs + REG_ENTROPY_IV),
35462306a36Sopenharmony_ci			    readl(cp->regs + REG_ENTROPY_IV),
35562306a36Sopenharmony_ci			    sizeof(uint64_t)*8);
35662306a36Sopenharmony_ci#endif
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic inline void cas_entropy_reset(struct cas *cp)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci#ifdef USE_ENTROPY_DEV
36262306a36Sopenharmony_ci	if ((cp->cas_flags & CAS_FLAG_ENTROPY_DEV) == 0)
36362306a36Sopenharmony_ci		return;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	writel(BIM_LOCAL_DEV_PAD | BIM_LOCAL_DEV_PROM | BIM_LOCAL_DEV_EXT,
36662306a36Sopenharmony_ci	       cp->regs + REG_BIM_LOCAL_DEV_EN);
36762306a36Sopenharmony_ci	writeb(ENTROPY_RESET_STC_MODE, cp->regs + REG_ENTROPY_RESET);
36862306a36Sopenharmony_ci	writeb(0x55, cp->regs + REG_ENTROPY_RAND_REG);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* if we read back 0x0, we don't have an entropy device */
37162306a36Sopenharmony_ci	if (readb(cp->regs + REG_ENTROPY_RAND_REG) == 0)
37262306a36Sopenharmony_ci		cp->cas_flags &= ~CAS_FLAG_ENTROPY_DEV;
37362306a36Sopenharmony_ci#endif
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci/* access to the phy. the following assumes that we've initialized the MIF to
37762306a36Sopenharmony_ci * be in frame rather than bit-bang mode
37862306a36Sopenharmony_ci */
37962306a36Sopenharmony_cistatic u16 cas_phy_read(struct cas *cp, int reg)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	u32 cmd;
38262306a36Sopenharmony_ci	int limit = STOP_TRIES_PHY;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	cmd = MIF_FRAME_ST | MIF_FRAME_OP_READ;
38562306a36Sopenharmony_ci	cmd |= CAS_BASE(MIF_FRAME_PHY_ADDR, cp->phy_addr);
38662306a36Sopenharmony_ci	cmd |= CAS_BASE(MIF_FRAME_REG_ADDR, reg);
38762306a36Sopenharmony_ci	cmd |= MIF_FRAME_TURN_AROUND_MSB;
38862306a36Sopenharmony_ci	writel(cmd, cp->regs + REG_MIF_FRAME);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	/* poll for completion */
39162306a36Sopenharmony_ci	while (limit-- > 0) {
39262306a36Sopenharmony_ci		udelay(10);
39362306a36Sopenharmony_ci		cmd = readl(cp->regs + REG_MIF_FRAME);
39462306a36Sopenharmony_ci		if (cmd & MIF_FRAME_TURN_AROUND_LSB)
39562306a36Sopenharmony_ci			return cmd & MIF_FRAME_DATA_MASK;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci	return 0xFFFF; /* -1 */
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int cas_phy_write(struct cas *cp, int reg, u16 val)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	int limit = STOP_TRIES_PHY;
40362306a36Sopenharmony_ci	u32 cmd;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	cmd = MIF_FRAME_ST | MIF_FRAME_OP_WRITE;
40662306a36Sopenharmony_ci	cmd |= CAS_BASE(MIF_FRAME_PHY_ADDR, cp->phy_addr);
40762306a36Sopenharmony_ci	cmd |= CAS_BASE(MIF_FRAME_REG_ADDR, reg);
40862306a36Sopenharmony_ci	cmd |= MIF_FRAME_TURN_AROUND_MSB;
40962306a36Sopenharmony_ci	cmd |= val & MIF_FRAME_DATA_MASK;
41062306a36Sopenharmony_ci	writel(cmd, cp->regs + REG_MIF_FRAME);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/* poll for completion */
41362306a36Sopenharmony_ci	while (limit-- > 0) {
41462306a36Sopenharmony_ci		udelay(10);
41562306a36Sopenharmony_ci		cmd = readl(cp->regs + REG_MIF_FRAME);
41662306a36Sopenharmony_ci		if (cmd & MIF_FRAME_TURN_AROUND_LSB)
41762306a36Sopenharmony_ci			return 0;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci	return -1;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic void cas_phy_powerup(struct cas *cp)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	u16 ctl = cas_phy_read(cp, MII_BMCR);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if ((ctl & BMCR_PDOWN) == 0)
42762306a36Sopenharmony_ci		return;
42862306a36Sopenharmony_ci	ctl &= ~BMCR_PDOWN;
42962306a36Sopenharmony_ci	cas_phy_write(cp, MII_BMCR, ctl);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic void cas_phy_powerdown(struct cas *cp)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	u16 ctl = cas_phy_read(cp, MII_BMCR);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (ctl & BMCR_PDOWN)
43762306a36Sopenharmony_ci		return;
43862306a36Sopenharmony_ci	ctl |= BMCR_PDOWN;
43962306a36Sopenharmony_ci	cas_phy_write(cp, MII_BMCR, ctl);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci/* cp->lock held. note: the last put_page will free the buffer */
44362306a36Sopenharmony_cistatic int cas_page_free(struct cas *cp, cas_page_t *page)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	dma_unmap_page(&cp->pdev->dev, page->dma_addr, cp->page_size,
44662306a36Sopenharmony_ci		       DMA_FROM_DEVICE);
44762306a36Sopenharmony_ci	__free_pages(page->buffer, cp->page_order);
44862306a36Sopenharmony_ci	kfree(page);
44962306a36Sopenharmony_ci	return 0;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci#ifdef RX_COUNT_BUFFERS
45362306a36Sopenharmony_ci#define RX_USED_ADD(x, y)       ((x)->used += (y))
45462306a36Sopenharmony_ci#define RX_USED_SET(x, y)       ((x)->used  = (y))
45562306a36Sopenharmony_ci#else
45662306a36Sopenharmony_ci#define RX_USED_ADD(x, y) do { } while(0)
45762306a36Sopenharmony_ci#define RX_USED_SET(x, y) do { } while(0)
45862306a36Sopenharmony_ci#endif
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci/* local page allocation routines for the receive buffers. jumbo pages
46162306a36Sopenharmony_ci * require at least 8K contiguous and 8K aligned buffers.
46262306a36Sopenharmony_ci */
46362306a36Sopenharmony_cistatic cas_page_t *cas_page_alloc(struct cas *cp, const gfp_t flags)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	cas_page_t *page;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	page = kmalloc(sizeof(cas_page_t), flags);
46862306a36Sopenharmony_ci	if (!page)
46962306a36Sopenharmony_ci		return NULL;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	INIT_LIST_HEAD(&page->list);
47262306a36Sopenharmony_ci	RX_USED_SET(page, 0);
47362306a36Sopenharmony_ci	page->buffer = alloc_pages(flags, cp->page_order);
47462306a36Sopenharmony_ci	if (!page->buffer)
47562306a36Sopenharmony_ci		goto page_err;
47662306a36Sopenharmony_ci	page->dma_addr = dma_map_page(&cp->pdev->dev, page->buffer, 0,
47762306a36Sopenharmony_ci				      cp->page_size, DMA_FROM_DEVICE);
47862306a36Sopenharmony_ci	return page;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cipage_err:
48162306a36Sopenharmony_ci	kfree(page);
48262306a36Sopenharmony_ci	return NULL;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci/* initialize spare pool of rx buffers, but allocate during the open */
48662306a36Sopenharmony_cistatic void cas_spare_init(struct cas *cp)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	spin_lock(&cp->rx_inuse_lock);
48962306a36Sopenharmony_ci	INIT_LIST_HEAD(&cp->rx_inuse_list);
49062306a36Sopenharmony_ci	spin_unlock(&cp->rx_inuse_lock);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	spin_lock(&cp->rx_spare_lock);
49362306a36Sopenharmony_ci	INIT_LIST_HEAD(&cp->rx_spare_list);
49462306a36Sopenharmony_ci	cp->rx_spares_needed = RX_SPARE_COUNT;
49562306a36Sopenharmony_ci	spin_unlock(&cp->rx_spare_lock);
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci/* used on close. free all the spare buffers. */
49962306a36Sopenharmony_cistatic void cas_spare_free(struct cas *cp)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	struct list_head list, *elem, *tmp;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	/* free spare buffers */
50462306a36Sopenharmony_ci	INIT_LIST_HEAD(&list);
50562306a36Sopenharmony_ci	spin_lock(&cp->rx_spare_lock);
50662306a36Sopenharmony_ci	list_splice_init(&cp->rx_spare_list, &list);
50762306a36Sopenharmony_ci	spin_unlock(&cp->rx_spare_lock);
50862306a36Sopenharmony_ci	list_for_each_safe(elem, tmp, &list) {
50962306a36Sopenharmony_ci		cas_page_free(cp, list_entry(elem, cas_page_t, list));
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	INIT_LIST_HEAD(&list);
51362306a36Sopenharmony_ci#if 1
51462306a36Sopenharmony_ci	/*
51562306a36Sopenharmony_ci	 * Looks like Adrian had protected this with a different
51662306a36Sopenharmony_ci	 * lock than used everywhere else to manipulate this list.
51762306a36Sopenharmony_ci	 */
51862306a36Sopenharmony_ci	spin_lock(&cp->rx_inuse_lock);
51962306a36Sopenharmony_ci	list_splice_init(&cp->rx_inuse_list, &list);
52062306a36Sopenharmony_ci	spin_unlock(&cp->rx_inuse_lock);
52162306a36Sopenharmony_ci#else
52262306a36Sopenharmony_ci	spin_lock(&cp->rx_spare_lock);
52362306a36Sopenharmony_ci	list_splice_init(&cp->rx_inuse_list, &list);
52462306a36Sopenharmony_ci	spin_unlock(&cp->rx_spare_lock);
52562306a36Sopenharmony_ci#endif
52662306a36Sopenharmony_ci	list_for_each_safe(elem, tmp, &list) {
52762306a36Sopenharmony_ci		cas_page_free(cp, list_entry(elem, cas_page_t, list));
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci/* replenish spares if needed */
53262306a36Sopenharmony_cistatic void cas_spare_recover(struct cas *cp, const gfp_t flags)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct list_head list, *elem, *tmp;
53562306a36Sopenharmony_ci	int needed, i;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* check inuse list. if we don't need any more free buffers,
53862306a36Sopenharmony_ci	 * just free it
53962306a36Sopenharmony_ci	 */
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	/* make a local copy of the list */
54262306a36Sopenharmony_ci	INIT_LIST_HEAD(&list);
54362306a36Sopenharmony_ci	spin_lock(&cp->rx_inuse_lock);
54462306a36Sopenharmony_ci	list_splice_init(&cp->rx_inuse_list, &list);
54562306a36Sopenharmony_ci	spin_unlock(&cp->rx_inuse_lock);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	list_for_each_safe(elem, tmp, &list) {
54862306a36Sopenharmony_ci		cas_page_t *page = list_entry(elem, cas_page_t, list);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		/*
55162306a36Sopenharmony_ci		 * With the lockless pagecache, cassini buffering scheme gets
55262306a36Sopenharmony_ci		 * slightly less accurate: we might find that a page has an
55362306a36Sopenharmony_ci		 * elevated reference count here, due to a speculative ref,
55462306a36Sopenharmony_ci		 * and skip it as in-use. Ideally we would be able to reclaim
55562306a36Sopenharmony_ci		 * it. However this would be such a rare case, it doesn't
55662306a36Sopenharmony_ci		 * matter too much as we should pick it up the next time round.
55762306a36Sopenharmony_ci		 *
55862306a36Sopenharmony_ci		 * Importantly, if we find that the page has a refcount of 1
55962306a36Sopenharmony_ci		 * here (our refcount), then we know it is definitely not inuse
56062306a36Sopenharmony_ci		 * so we can reuse it.
56162306a36Sopenharmony_ci		 */
56262306a36Sopenharmony_ci		if (page_count(page->buffer) > 1)
56362306a36Sopenharmony_ci			continue;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci		list_del(elem);
56662306a36Sopenharmony_ci		spin_lock(&cp->rx_spare_lock);
56762306a36Sopenharmony_ci		if (cp->rx_spares_needed > 0) {
56862306a36Sopenharmony_ci			list_add(elem, &cp->rx_spare_list);
56962306a36Sopenharmony_ci			cp->rx_spares_needed--;
57062306a36Sopenharmony_ci			spin_unlock(&cp->rx_spare_lock);
57162306a36Sopenharmony_ci		} else {
57262306a36Sopenharmony_ci			spin_unlock(&cp->rx_spare_lock);
57362306a36Sopenharmony_ci			cas_page_free(cp, page);
57462306a36Sopenharmony_ci		}
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	/* put any inuse buffers back on the list */
57862306a36Sopenharmony_ci	if (!list_empty(&list)) {
57962306a36Sopenharmony_ci		spin_lock(&cp->rx_inuse_lock);
58062306a36Sopenharmony_ci		list_splice(&list, &cp->rx_inuse_list);
58162306a36Sopenharmony_ci		spin_unlock(&cp->rx_inuse_lock);
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	spin_lock(&cp->rx_spare_lock);
58562306a36Sopenharmony_ci	needed = cp->rx_spares_needed;
58662306a36Sopenharmony_ci	spin_unlock(&cp->rx_spare_lock);
58762306a36Sopenharmony_ci	if (!needed)
58862306a36Sopenharmony_ci		return;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	/* we still need spares, so try to allocate some */
59162306a36Sopenharmony_ci	INIT_LIST_HEAD(&list);
59262306a36Sopenharmony_ci	i = 0;
59362306a36Sopenharmony_ci	while (i < needed) {
59462306a36Sopenharmony_ci		cas_page_t *spare = cas_page_alloc(cp, flags);
59562306a36Sopenharmony_ci		if (!spare)
59662306a36Sopenharmony_ci			break;
59762306a36Sopenharmony_ci		list_add(&spare->list, &list);
59862306a36Sopenharmony_ci		i++;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	spin_lock(&cp->rx_spare_lock);
60262306a36Sopenharmony_ci	list_splice(&list, &cp->rx_spare_list);
60362306a36Sopenharmony_ci	cp->rx_spares_needed -= i;
60462306a36Sopenharmony_ci	spin_unlock(&cp->rx_spare_lock);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci/* pull a page from the list. */
60862306a36Sopenharmony_cistatic cas_page_t *cas_page_dequeue(struct cas *cp)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	struct list_head *entry;
61162306a36Sopenharmony_ci	int recover;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	spin_lock(&cp->rx_spare_lock);
61462306a36Sopenharmony_ci	if (list_empty(&cp->rx_spare_list)) {
61562306a36Sopenharmony_ci		/* try to do a quick recovery */
61662306a36Sopenharmony_ci		spin_unlock(&cp->rx_spare_lock);
61762306a36Sopenharmony_ci		cas_spare_recover(cp, GFP_ATOMIC);
61862306a36Sopenharmony_ci		spin_lock(&cp->rx_spare_lock);
61962306a36Sopenharmony_ci		if (list_empty(&cp->rx_spare_list)) {
62062306a36Sopenharmony_ci			netif_err(cp, rx_err, cp->dev,
62162306a36Sopenharmony_ci				  "no spare buffers available\n");
62262306a36Sopenharmony_ci			spin_unlock(&cp->rx_spare_lock);
62362306a36Sopenharmony_ci			return NULL;
62462306a36Sopenharmony_ci		}
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	entry = cp->rx_spare_list.next;
62862306a36Sopenharmony_ci	list_del(entry);
62962306a36Sopenharmony_ci	recover = ++cp->rx_spares_needed;
63062306a36Sopenharmony_ci	spin_unlock(&cp->rx_spare_lock);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	/* trigger the timer to do the recovery */
63362306a36Sopenharmony_ci	if ((recover & (RX_SPARE_RECOVER_VAL - 1)) == 0) {
63462306a36Sopenharmony_ci#if 1
63562306a36Sopenharmony_ci		atomic_inc(&cp->reset_task_pending);
63662306a36Sopenharmony_ci		atomic_inc(&cp->reset_task_pending_spare);
63762306a36Sopenharmony_ci		schedule_work(&cp->reset_task);
63862306a36Sopenharmony_ci#else
63962306a36Sopenharmony_ci		atomic_set(&cp->reset_task_pending, CAS_RESET_SPARE);
64062306a36Sopenharmony_ci		schedule_work(&cp->reset_task);
64162306a36Sopenharmony_ci#endif
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci	return list_entry(entry, cas_page_t, list);
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic void cas_mif_poll(struct cas *cp, const int enable)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	u32 cfg;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	cfg  = readl(cp->regs + REG_MIF_CFG);
65262306a36Sopenharmony_ci	cfg &= (MIF_CFG_MDIO_0 | MIF_CFG_MDIO_1);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (cp->phy_type & CAS_PHY_MII_MDIO1)
65562306a36Sopenharmony_ci		cfg |= MIF_CFG_PHY_SELECT;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/* poll and interrupt on link status change. */
65862306a36Sopenharmony_ci	if (enable) {
65962306a36Sopenharmony_ci		cfg |= MIF_CFG_POLL_EN;
66062306a36Sopenharmony_ci		cfg |= CAS_BASE(MIF_CFG_POLL_REG, MII_BMSR);
66162306a36Sopenharmony_ci		cfg |= CAS_BASE(MIF_CFG_POLL_PHY, cp->phy_addr);
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci	writel((enable) ? ~(BMSR_LSTATUS | BMSR_ANEGCOMPLETE) : 0xFFFF,
66462306a36Sopenharmony_ci	       cp->regs + REG_MIF_MASK);
66562306a36Sopenharmony_ci	writel(cfg, cp->regs + REG_MIF_CFG);
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci/* Must be invoked under cp->lock */
66962306a36Sopenharmony_cistatic void cas_begin_auto_negotiation(struct cas *cp,
67062306a36Sopenharmony_ci				       const struct ethtool_link_ksettings *ep)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	u16 ctl;
67362306a36Sopenharmony_ci#if 1
67462306a36Sopenharmony_ci	int lcntl;
67562306a36Sopenharmony_ci	int changed = 0;
67662306a36Sopenharmony_ci	int oldstate = cp->lstate;
67762306a36Sopenharmony_ci	int link_was_not_down = !(oldstate == link_down);
67862306a36Sopenharmony_ci#endif
67962306a36Sopenharmony_ci	/* Setup link parameters */
68062306a36Sopenharmony_ci	if (!ep)
68162306a36Sopenharmony_ci		goto start_aneg;
68262306a36Sopenharmony_ci	lcntl = cp->link_cntl;
68362306a36Sopenharmony_ci	if (ep->base.autoneg == AUTONEG_ENABLE) {
68462306a36Sopenharmony_ci		cp->link_cntl = BMCR_ANENABLE;
68562306a36Sopenharmony_ci	} else {
68662306a36Sopenharmony_ci		u32 speed = ep->base.speed;
68762306a36Sopenharmony_ci		cp->link_cntl = 0;
68862306a36Sopenharmony_ci		if (speed == SPEED_100)
68962306a36Sopenharmony_ci			cp->link_cntl |= BMCR_SPEED100;
69062306a36Sopenharmony_ci		else if (speed == SPEED_1000)
69162306a36Sopenharmony_ci			cp->link_cntl |= CAS_BMCR_SPEED1000;
69262306a36Sopenharmony_ci		if (ep->base.duplex == DUPLEX_FULL)
69362306a36Sopenharmony_ci			cp->link_cntl |= BMCR_FULLDPLX;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci#if 1
69662306a36Sopenharmony_ci	changed = (lcntl != cp->link_cntl);
69762306a36Sopenharmony_ci#endif
69862306a36Sopenharmony_cistart_aneg:
69962306a36Sopenharmony_ci	if (cp->lstate == link_up) {
70062306a36Sopenharmony_ci		netdev_info(cp->dev, "PCS link down\n");
70162306a36Sopenharmony_ci	} else {
70262306a36Sopenharmony_ci		if (changed) {
70362306a36Sopenharmony_ci			netdev_info(cp->dev, "link configuration changed\n");
70462306a36Sopenharmony_ci		}
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci	cp->lstate = link_down;
70762306a36Sopenharmony_ci	cp->link_transition = LINK_TRANSITION_LINK_DOWN;
70862306a36Sopenharmony_ci	if (!cp->hw_running)
70962306a36Sopenharmony_ci		return;
71062306a36Sopenharmony_ci#if 1
71162306a36Sopenharmony_ci	/*
71262306a36Sopenharmony_ci	 * WTZ: If the old state was link_up, we turn off the carrier
71362306a36Sopenharmony_ci	 * to replicate everything we do elsewhere on a link-down
71462306a36Sopenharmony_ci	 * event when we were already in a link-up state..
71562306a36Sopenharmony_ci	 */
71662306a36Sopenharmony_ci	if (oldstate == link_up)
71762306a36Sopenharmony_ci		netif_carrier_off(cp->dev);
71862306a36Sopenharmony_ci	if (changed  && link_was_not_down) {
71962306a36Sopenharmony_ci		/*
72062306a36Sopenharmony_ci		 * WTZ: This branch will simply schedule a full reset after
72162306a36Sopenharmony_ci		 * we explicitly changed link modes in an ioctl. See if this
72262306a36Sopenharmony_ci		 * fixes the link-problems we were having for forced mode.
72362306a36Sopenharmony_ci		 */
72462306a36Sopenharmony_ci		atomic_inc(&cp->reset_task_pending);
72562306a36Sopenharmony_ci		atomic_inc(&cp->reset_task_pending_all);
72662306a36Sopenharmony_ci		schedule_work(&cp->reset_task);
72762306a36Sopenharmony_ci		cp->timer_ticks = 0;
72862306a36Sopenharmony_ci		mod_timer(&cp->link_timer, jiffies + CAS_LINK_TIMEOUT);
72962306a36Sopenharmony_ci		return;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci#endif
73262306a36Sopenharmony_ci	if (cp->phy_type & CAS_PHY_SERDES) {
73362306a36Sopenharmony_ci		u32 val = readl(cp->regs + REG_PCS_MII_CTRL);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		if (cp->link_cntl & BMCR_ANENABLE) {
73662306a36Sopenharmony_ci			val |= (PCS_MII_RESTART_AUTONEG | PCS_MII_AUTONEG_EN);
73762306a36Sopenharmony_ci			cp->lstate = link_aneg;
73862306a36Sopenharmony_ci		} else {
73962306a36Sopenharmony_ci			if (cp->link_cntl & BMCR_FULLDPLX)
74062306a36Sopenharmony_ci				val |= PCS_MII_CTRL_DUPLEX;
74162306a36Sopenharmony_ci			val &= ~PCS_MII_AUTONEG_EN;
74262306a36Sopenharmony_ci			cp->lstate = link_force_ok;
74362306a36Sopenharmony_ci		}
74462306a36Sopenharmony_ci		cp->link_transition = LINK_TRANSITION_LINK_CONFIG;
74562306a36Sopenharmony_ci		writel(val, cp->regs + REG_PCS_MII_CTRL);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	} else {
74862306a36Sopenharmony_ci		cas_mif_poll(cp, 0);
74962306a36Sopenharmony_ci		ctl = cas_phy_read(cp, MII_BMCR);
75062306a36Sopenharmony_ci		ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 |
75162306a36Sopenharmony_ci			 CAS_BMCR_SPEED1000 | BMCR_ANENABLE);
75262306a36Sopenharmony_ci		ctl |= cp->link_cntl;
75362306a36Sopenharmony_ci		if (ctl & BMCR_ANENABLE) {
75462306a36Sopenharmony_ci			ctl |= BMCR_ANRESTART;
75562306a36Sopenharmony_ci			cp->lstate = link_aneg;
75662306a36Sopenharmony_ci		} else {
75762306a36Sopenharmony_ci			cp->lstate = link_force_ok;
75862306a36Sopenharmony_ci		}
75962306a36Sopenharmony_ci		cp->link_transition = LINK_TRANSITION_LINK_CONFIG;
76062306a36Sopenharmony_ci		cas_phy_write(cp, MII_BMCR, ctl);
76162306a36Sopenharmony_ci		cas_mif_poll(cp, 1);
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	cp->timer_ticks = 0;
76562306a36Sopenharmony_ci	mod_timer(&cp->link_timer, jiffies + CAS_LINK_TIMEOUT);
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci/* Must be invoked under cp->lock. */
76962306a36Sopenharmony_cistatic int cas_reset_mii_phy(struct cas *cp)
77062306a36Sopenharmony_ci{
77162306a36Sopenharmony_ci	int limit = STOP_TRIES_PHY;
77262306a36Sopenharmony_ci	u16 val;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	cas_phy_write(cp, MII_BMCR, BMCR_RESET);
77562306a36Sopenharmony_ci	udelay(100);
77662306a36Sopenharmony_ci	while (--limit) {
77762306a36Sopenharmony_ci		val = cas_phy_read(cp, MII_BMCR);
77862306a36Sopenharmony_ci		if ((val & BMCR_RESET) == 0)
77962306a36Sopenharmony_ci			break;
78062306a36Sopenharmony_ci		udelay(10);
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci	return limit <= 0;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic void cas_saturn_firmware_init(struct cas *cp)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	const struct firmware *fw;
78862306a36Sopenharmony_ci	const char fw_name[] = "sun/cassini.bin";
78962306a36Sopenharmony_ci	int err;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (PHY_NS_DP83065 != cp->phy_id)
79262306a36Sopenharmony_ci		return;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	err = request_firmware(&fw, fw_name, &cp->pdev->dev);
79562306a36Sopenharmony_ci	if (err) {
79662306a36Sopenharmony_ci		pr_err("Failed to load firmware \"%s\"\n",
79762306a36Sopenharmony_ci		       fw_name);
79862306a36Sopenharmony_ci		return;
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci	if (fw->size < 2) {
80162306a36Sopenharmony_ci		pr_err("bogus length %zu in \"%s\"\n",
80262306a36Sopenharmony_ci		       fw->size, fw_name);
80362306a36Sopenharmony_ci		goto out;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci	cp->fw_load_addr= fw->data[1] << 8 | fw->data[0];
80662306a36Sopenharmony_ci	cp->fw_size = fw->size - 2;
80762306a36Sopenharmony_ci	cp->fw_data = vmalloc(cp->fw_size);
80862306a36Sopenharmony_ci	if (!cp->fw_data)
80962306a36Sopenharmony_ci		goto out;
81062306a36Sopenharmony_ci	memcpy(cp->fw_data, &fw->data[2], cp->fw_size);
81162306a36Sopenharmony_ciout:
81262306a36Sopenharmony_ci	release_firmware(fw);
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic void cas_saturn_firmware_load(struct cas *cp)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	int i;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if (!cp->fw_data)
82062306a36Sopenharmony_ci		return;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	cas_phy_powerdown(cp);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	/* expanded memory access mode */
82562306a36Sopenharmony_ci	cas_phy_write(cp, DP83065_MII_MEM, 0x0);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	/* pointer configuration for new firmware */
82862306a36Sopenharmony_ci	cas_phy_write(cp, DP83065_MII_REGE, 0x8ff9);
82962306a36Sopenharmony_ci	cas_phy_write(cp, DP83065_MII_REGD, 0xbd);
83062306a36Sopenharmony_ci	cas_phy_write(cp, DP83065_MII_REGE, 0x8ffa);
83162306a36Sopenharmony_ci	cas_phy_write(cp, DP83065_MII_REGD, 0x82);
83262306a36Sopenharmony_ci	cas_phy_write(cp, DP83065_MII_REGE, 0x8ffb);
83362306a36Sopenharmony_ci	cas_phy_write(cp, DP83065_MII_REGD, 0x0);
83462306a36Sopenharmony_ci	cas_phy_write(cp, DP83065_MII_REGE, 0x8ffc);
83562306a36Sopenharmony_ci	cas_phy_write(cp, DP83065_MII_REGD, 0x39);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	/* download new firmware */
83862306a36Sopenharmony_ci	cas_phy_write(cp, DP83065_MII_MEM, 0x1);
83962306a36Sopenharmony_ci	cas_phy_write(cp, DP83065_MII_REGE, cp->fw_load_addr);
84062306a36Sopenharmony_ci	for (i = 0; i < cp->fw_size; i++)
84162306a36Sopenharmony_ci		cas_phy_write(cp, DP83065_MII_REGD, cp->fw_data[i]);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/* enable firmware */
84462306a36Sopenharmony_ci	cas_phy_write(cp, DP83065_MII_REGE, 0x8ff8);
84562306a36Sopenharmony_ci	cas_phy_write(cp, DP83065_MII_REGD, 0x1);
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci/* phy initialization */
85062306a36Sopenharmony_cistatic void cas_phy_init(struct cas *cp)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	u16 val;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	/* if we're in MII/GMII mode, set up phy */
85562306a36Sopenharmony_ci	if (CAS_PHY_MII(cp->phy_type)) {
85662306a36Sopenharmony_ci		writel(PCS_DATAPATH_MODE_MII,
85762306a36Sopenharmony_ci		       cp->regs + REG_PCS_DATAPATH_MODE);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		cas_mif_poll(cp, 0);
86062306a36Sopenharmony_ci		cas_reset_mii_phy(cp); /* take out of isolate mode */
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci		if (PHY_LUCENT_B0 == cp->phy_id) {
86362306a36Sopenharmony_ci			/* workaround link up/down issue with lucent */
86462306a36Sopenharmony_ci			cas_phy_write(cp, LUCENT_MII_REG, 0x8000);
86562306a36Sopenharmony_ci			cas_phy_write(cp, MII_BMCR, 0x00f1);
86662306a36Sopenharmony_ci			cas_phy_write(cp, LUCENT_MII_REG, 0x0);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		} else if (PHY_BROADCOM_B0 == (cp->phy_id & 0xFFFFFFFC)) {
86962306a36Sopenharmony_ci			/* workarounds for broadcom phy */
87062306a36Sopenharmony_ci			cas_phy_write(cp, BROADCOM_MII_REG8, 0x0C20);
87162306a36Sopenharmony_ci			cas_phy_write(cp, BROADCOM_MII_REG7, 0x0012);
87262306a36Sopenharmony_ci			cas_phy_write(cp, BROADCOM_MII_REG5, 0x1804);
87362306a36Sopenharmony_ci			cas_phy_write(cp, BROADCOM_MII_REG7, 0x0013);
87462306a36Sopenharmony_ci			cas_phy_write(cp, BROADCOM_MII_REG5, 0x1204);
87562306a36Sopenharmony_ci			cas_phy_write(cp, BROADCOM_MII_REG7, 0x8006);
87662306a36Sopenharmony_ci			cas_phy_write(cp, BROADCOM_MII_REG5, 0x0132);
87762306a36Sopenharmony_ci			cas_phy_write(cp, BROADCOM_MII_REG7, 0x8006);
87862306a36Sopenharmony_ci			cas_phy_write(cp, BROADCOM_MII_REG5, 0x0232);
87962306a36Sopenharmony_ci			cas_phy_write(cp, BROADCOM_MII_REG7, 0x201F);
88062306a36Sopenharmony_ci			cas_phy_write(cp, BROADCOM_MII_REG5, 0x0A20);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci		} else if (PHY_BROADCOM_5411 == cp->phy_id) {
88362306a36Sopenharmony_ci			val = cas_phy_read(cp, BROADCOM_MII_REG4);
88462306a36Sopenharmony_ci			val = cas_phy_read(cp, BROADCOM_MII_REG4);
88562306a36Sopenharmony_ci			if (val & 0x0080) {
88662306a36Sopenharmony_ci				/* link workaround */
88762306a36Sopenharmony_ci				cas_phy_write(cp, BROADCOM_MII_REG4,
88862306a36Sopenharmony_ci					      val & ~0x0080);
88962306a36Sopenharmony_ci			}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci		} else if (cp->cas_flags & CAS_FLAG_SATURN) {
89262306a36Sopenharmony_ci			writel((cp->phy_type & CAS_PHY_MII_MDIO0) ?
89362306a36Sopenharmony_ci			       SATURN_PCFG_FSI : 0x0,
89462306a36Sopenharmony_ci			       cp->regs + REG_SATURN_PCFG);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci			/* load firmware to address 10Mbps auto-negotiation
89762306a36Sopenharmony_ci			 * issue. NOTE: this will need to be changed if the
89862306a36Sopenharmony_ci			 * default firmware gets fixed.
89962306a36Sopenharmony_ci			 */
90062306a36Sopenharmony_ci			if (PHY_NS_DP83065 == cp->phy_id) {
90162306a36Sopenharmony_ci				cas_saturn_firmware_load(cp);
90262306a36Sopenharmony_ci			}
90362306a36Sopenharmony_ci			cas_phy_powerup(cp);
90462306a36Sopenharmony_ci		}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci		/* advertise capabilities */
90762306a36Sopenharmony_ci		val = cas_phy_read(cp, MII_BMCR);
90862306a36Sopenharmony_ci		val &= ~BMCR_ANENABLE;
90962306a36Sopenharmony_ci		cas_phy_write(cp, MII_BMCR, val);
91062306a36Sopenharmony_ci		udelay(10);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci		cas_phy_write(cp, MII_ADVERTISE,
91362306a36Sopenharmony_ci			      cas_phy_read(cp, MII_ADVERTISE) |
91462306a36Sopenharmony_ci			      (ADVERTISE_10HALF | ADVERTISE_10FULL |
91562306a36Sopenharmony_ci			       ADVERTISE_100HALF | ADVERTISE_100FULL |
91662306a36Sopenharmony_ci			       CAS_ADVERTISE_PAUSE |
91762306a36Sopenharmony_ci			       CAS_ADVERTISE_ASYM_PAUSE));
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci		if (cp->cas_flags & CAS_FLAG_1000MB_CAP) {
92062306a36Sopenharmony_ci			/* make sure that we don't advertise half
92162306a36Sopenharmony_ci			 * duplex to avoid a chip issue
92262306a36Sopenharmony_ci			 */
92362306a36Sopenharmony_ci			val  = cas_phy_read(cp, CAS_MII_1000_CTRL);
92462306a36Sopenharmony_ci			val &= ~CAS_ADVERTISE_1000HALF;
92562306a36Sopenharmony_ci			val |= CAS_ADVERTISE_1000FULL;
92662306a36Sopenharmony_ci			cas_phy_write(cp, CAS_MII_1000_CTRL, val);
92762306a36Sopenharmony_ci		}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	} else {
93062306a36Sopenharmony_ci		/* reset pcs for serdes */
93162306a36Sopenharmony_ci		u32 val;
93262306a36Sopenharmony_ci		int limit;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci		writel(PCS_DATAPATH_MODE_SERDES,
93562306a36Sopenharmony_ci		       cp->regs + REG_PCS_DATAPATH_MODE);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci		/* enable serdes pins on saturn */
93862306a36Sopenharmony_ci		if (cp->cas_flags & CAS_FLAG_SATURN)
93962306a36Sopenharmony_ci			writel(0, cp->regs + REG_SATURN_PCFG);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci		/* Reset PCS unit. */
94262306a36Sopenharmony_ci		val = readl(cp->regs + REG_PCS_MII_CTRL);
94362306a36Sopenharmony_ci		val |= PCS_MII_RESET;
94462306a36Sopenharmony_ci		writel(val, cp->regs + REG_PCS_MII_CTRL);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci		limit = STOP_TRIES;
94762306a36Sopenharmony_ci		while (--limit > 0) {
94862306a36Sopenharmony_ci			udelay(10);
94962306a36Sopenharmony_ci			if ((readl(cp->regs + REG_PCS_MII_CTRL) &
95062306a36Sopenharmony_ci			     PCS_MII_RESET) == 0)
95162306a36Sopenharmony_ci				break;
95262306a36Sopenharmony_ci		}
95362306a36Sopenharmony_ci		if (limit <= 0)
95462306a36Sopenharmony_ci			netdev_warn(cp->dev, "PCS reset bit would not clear [%08x]\n",
95562306a36Sopenharmony_ci				    readl(cp->regs + REG_PCS_STATE_MACHINE));
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci		/* Make sure PCS is disabled while changing advertisement
95862306a36Sopenharmony_ci		 * configuration.
95962306a36Sopenharmony_ci		 */
96062306a36Sopenharmony_ci		writel(0x0, cp->regs + REG_PCS_CFG);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci		/* Advertise all capabilities except half-duplex. */
96362306a36Sopenharmony_ci		val  = readl(cp->regs + REG_PCS_MII_ADVERT);
96462306a36Sopenharmony_ci		val &= ~PCS_MII_ADVERT_HD;
96562306a36Sopenharmony_ci		val |= (PCS_MII_ADVERT_FD | PCS_MII_ADVERT_SYM_PAUSE |
96662306a36Sopenharmony_ci			PCS_MII_ADVERT_ASYM_PAUSE);
96762306a36Sopenharmony_ci		writel(val, cp->regs + REG_PCS_MII_ADVERT);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci		/* enable PCS */
97062306a36Sopenharmony_ci		writel(PCS_CFG_EN, cp->regs + REG_PCS_CFG);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci		/* pcs workaround: enable sync detect */
97362306a36Sopenharmony_ci		writel(PCS_SERDES_CTRL_SYNCD_EN,
97462306a36Sopenharmony_ci		       cp->regs + REG_PCS_SERDES_CTRL);
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_cistatic int cas_pcs_link_check(struct cas *cp)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	u32 stat, state_machine;
98262306a36Sopenharmony_ci	int retval = 0;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	/* The link status bit latches on zero, so you must
98562306a36Sopenharmony_ci	 * read it twice in such a case to see a transition
98662306a36Sopenharmony_ci	 * to the link being up.
98762306a36Sopenharmony_ci	 */
98862306a36Sopenharmony_ci	stat = readl(cp->regs + REG_PCS_MII_STATUS);
98962306a36Sopenharmony_ci	if ((stat & PCS_MII_STATUS_LINK_STATUS) == 0)
99062306a36Sopenharmony_ci		stat = readl(cp->regs + REG_PCS_MII_STATUS);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	/* The remote-fault indication is only valid
99362306a36Sopenharmony_ci	 * when autoneg has completed.
99462306a36Sopenharmony_ci	 */
99562306a36Sopenharmony_ci	if ((stat & (PCS_MII_STATUS_AUTONEG_COMP |
99662306a36Sopenharmony_ci		     PCS_MII_STATUS_REMOTE_FAULT)) ==
99762306a36Sopenharmony_ci	    (PCS_MII_STATUS_AUTONEG_COMP | PCS_MII_STATUS_REMOTE_FAULT))
99862306a36Sopenharmony_ci		netif_info(cp, link, cp->dev, "PCS RemoteFault\n");
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	/* work around link detection issue by querying the PCS state
100162306a36Sopenharmony_ci	 * machine directly.
100262306a36Sopenharmony_ci	 */
100362306a36Sopenharmony_ci	state_machine = readl(cp->regs + REG_PCS_STATE_MACHINE);
100462306a36Sopenharmony_ci	if ((state_machine & PCS_SM_LINK_STATE_MASK) != SM_LINK_STATE_UP) {
100562306a36Sopenharmony_ci		stat &= ~PCS_MII_STATUS_LINK_STATUS;
100662306a36Sopenharmony_ci	} else if (state_machine & PCS_SM_WORD_SYNC_STATE_MASK) {
100762306a36Sopenharmony_ci		stat |= PCS_MII_STATUS_LINK_STATUS;
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (stat & PCS_MII_STATUS_LINK_STATUS) {
101162306a36Sopenharmony_ci		if (cp->lstate != link_up) {
101262306a36Sopenharmony_ci			if (cp->opened) {
101362306a36Sopenharmony_ci				cp->lstate = link_up;
101462306a36Sopenharmony_ci				cp->link_transition = LINK_TRANSITION_LINK_UP;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci				cas_set_link_modes(cp);
101762306a36Sopenharmony_ci				netif_carrier_on(cp->dev);
101862306a36Sopenharmony_ci			}
101962306a36Sopenharmony_ci		}
102062306a36Sopenharmony_ci	} else if (cp->lstate == link_up) {
102162306a36Sopenharmony_ci		cp->lstate = link_down;
102262306a36Sopenharmony_ci		if (link_transition_timeout != 0 &&
102362306a36Sopenharmony_ci		    cp->link_transition != LINK_TRANSITION_REQUESTED_RESET &&
102462306a36Sopenharmony_ci		    !cp->link_transition_jiffies_valid) {
102562306a36Sopenharmony_ci			/*
102662306a36Sopenharmony_ci			 * force a reset, as a workaround for the
102762306a36Sopenharmony_ci			 * link-failure problem. May want to move this to a
102862306a36Sopenharmony_ci			 * point a bit earlier in the sequence. If we had
102962306a36Sopenharmony_ci			 * generated a reset a short time ago, we'll wait for
103062306a36Sopenharmony_ci			 * the link timer to check the status until a
103162306a36Sopenharmony_ci			 * timer expires (link_transistion_jiffies_valid is
103262306a36Sopenharmony_ci			 * true when the timer is running.)  Instead of using
103362306a36Sopenharmony_ci			 * a system timer, we just do a check whenever the
103462306a36Sopenharmony_ci			 * link timer is running - this clears the flag after
103562306a36Sopenharmony_ci			 * a suitable delay.
103662306a36Sopenharmony_ci			 */
103762306a36Sopenharmony_ci			retval = 1;
103862306a36Sopenharmony_ci			cp->link_transition = LINK_TRANSITION_REQUESTED_RESET;
103962306a36Sopenharmony_ci			cp->link_transition_jiffies = jiffies;
104062306a36Sopenharmony_ci			cp->link_transition_jiffies_valid = 1;
104162306a36Sopenharmony_ci		} else {
104262306a36Sopenharmony_ci			cp->link_transition = LINK_TRANSITION_ON_FAILURE;
104362306a36Sopenharmony_ci		}
104462306a36Sopenharmony_ci		netif_carrier_off(cp->dev);
104562306a36Sopenharmony_ci		if (cp->opened)
104662306a36Sopenharmony_ci			netif_info(cp, link, cp->dev, "PCS link down\n");
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci		/* Cassini only: if you force a mode, there can be
104962306a36Sopenharmony_ci		 * sync problems on link down. to fix that, the following
105062306a36Sopenharmony_ci		 * things need to be checked:
105162306a36Sopenharmony_ci		 * 1) read serialink state register
105262306a36Sopenharmony_ci		 * 2) read pcs status register to verify link down.
105362306a36Sopenharmony_ci		 * 3) if link down and serial link == 0x03, then you need
105462306a36Sopenharmony_ci		 *    to global reset the chip.
105562306a36Sopenharmony_ci		 */
105662306a36Sopenharmony_ci		if ((cp->cas_flags & CAS_FLAG_REG_PLUS) == 0) {
105762306a36Sopenharmony_ci			/* should check to see if we're in a forced mode */
105862306a36Sopenharmony_ci			stat = readl(cp->regs + REG_PCS_SERDES_STATE);
105962306a36Sopenharmony_ci			if (stat == 0x03)
106062306a36Sopenharmony_ci				return 1;
106162306a36Sopenharmony_ci		}
106262306a36Sopenharmony_ci	} else if (cp->lstate == link_down) {
106362306a36Sopenharmony_ci		if (link_transition_timeout != 0 &&
106462306a36Sopenharmony_ci		    cp->link_transition != LINK_TRANSITION_REQUESTED_RESET &&
106562306a36Sopenharmony_ci		    !cp->link_transition_jiffies_valid) {
106662306a36Sopenharmony_ci			/* force a reset, as a workaround for the
106762306a36Sopenharmony_ci			 * link-failure problem.  May want to move
106862306a36Sopenharmony_ci			 * this to a point a bit earlier in the
106962306a36Sopenharmony_ci			 * sequence.
107062306a36Sopenharmony_ci			 */
107162306a36Sopenharmony_ci			retval = 1;
107262306a36Sopenharmony_ci			cp->link_transition = LINK_TRANSITION_REQUESTED_RESET;
107362306a36Sopenharmony_ci			cp->link_transition_jiffies = jiffies;
107462306a36Sopenharmony_ci			cp->link_transition_jiffies_valid = 1;
107562306a36Sopenharmony_ci		} else {
107662306a36Sopenharmony_ci			cp->link_transition = LINK_TRANSITION_STILL_FAILED;
107762306a36Sopenharmony_ci		}
107862306a36Sopenharmony_ci	}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	return retval;
108162306a36Sopenharmony_ci}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cistatic int cas_pcs_interrupt(struct net_device *dev,
108462306a36Sopenharmony_ci			     struct cas *cp, u32 status)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	u32 stat = readl(cp->regs + REG_PCS_INTR_STATUS);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if ((stat & PCS_INTR_STATUS_LINK_CHANGE) == 0)
108962306a36Sopenharmony_ci		return 0;
109062306a36Sopenharmony_ci	return cas_pcs_link_check(cp);
109162306a36Sopenharmony_ci}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_cistatic int cas_txmac_interrupt(struct net_device *dev,
109462306a36Sopenharmony_ci			       struct cas *cp, u32 status)
109562306a36Sopenharmony_ci{
109662306a36Sopenharmony_ci	u32 txmac_stat = readl(cp->regs + REG_MAC_TX_STATUS);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (!txmac_stat)
109962306a36Sopenharmony_ci		return 0;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	netif_printk(cp, intr, KERN_DEBUG, cp->dev,
110262306a36Sopenharmony_ci		     "txmac interrupt, txmac_stat: 0x%x\n", txmac_stat);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	/* Defer timer expiration is quite normal,
110562306a36Sopenharmony_ci	 * don't even log the event.
110662306a36Sopenharmony_ci	 */
110762306a36Sopenharmony_ci	if ((txmac_stat & MAC_TX_DEFER_TIMER) &&
110862306a36Sopenharmony_ci	    !(txmac_stat & ~MAC_TX_DEFER_TIMER))
110962306a36Sopenharmony_ci		return 0;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	spin_lock(&cp->stat_lock[0]);
111262306a36Sopenharmony_ci	if (txmac_stat & MAC_TX_UNDERRUN) {
111362306a36Sopenharmony_ci		netdev_err(dev, "TX MAC xmit underrun\n");
111462306a36Sopenharmony_ci		cp->net_stats[0].tx_fifo_errors++;
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	if (txmac_stat & MAC_TX_MAX_PACKET_ERR) {
111862306a36Sopenharmony_ci		netdev_err(dev, "TX MAC max packet size error\n");
111962306a36Sopenharmony_ci		cp->net_stats[0].tx_errors++;
112062306a36Sopenharmony_ci	}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	/* The rest are all cases of one of the 16-bit TX
112362306a36Sopenharmony_ci	 * counters expiring.
112462306a36Sopenharmony_ci	 */
112562306a36Sopenharmony_ci	if (txmac_stat & MAC_TX_COLL_NORMAL)
112662306a36Sopenharmony_ci		cp->net_stats[0].collisions += 0x10000;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	if (txmac_stat & MAC_TX_COLL_EXCESS) {
112962306a36Sopenharmony_ci		cp->net_stats[0].tx_aborted_errors += 0x10000;
113062306a36Sopenharmony_ci		cp->net_stats[0].collisions += 0x10000;
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	if (txmac_stat & MAC_TX_COLL_LATE) {
113462306a36Sopenharmony_ci		cp->net_stats[0].tx_aborted_errors += 0x10000;
113562306a36Sopenharmony_ci		cp->net_stats[0].collisions += 0x10000;
113662306a36Sopenharmony_ci	}
113762306a36Sopenharmony_ci	spin_unlock(&cp->stat_lock[0]);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	/* We do not keep track of MAC_TX_COLL_FIRST and
114062306a36Sopenharmony_ci	 * MAC_TX_PEAK_ATTEMPTS events.
114162306a36Sopenharmony_ci	 */
114262306a36Sopenharmony_ci	return 0;
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_cistatic void cas_load_firmware(struct cas *cp, cas_hp_inst_t *firmware)
114662306a36Sopenharmony_ci{
114762306a36Sopenharmony_ci	cas_hp_inst_t *inst;
114862306a36Sopenharmony_ci	u32 val;
114962306a36Sopenharmony_ci	int i;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	i = 0;
115262306a36Sopenharmony_ci	while ((inst = firmware) && inst->note) {
115362306a36Sopenharmony_ci		writel(i, cp->regs + REG_HP_INSTR_RAM_ADDR);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci		val = CAS_BASE(HP_INSTR_RAM_HI_VAL, inst->val);
115662306a36Sopenharmony_ci		val |= CAS_BASE(HP_INSTR_RAM_HI_MASK, inst->mask);
115762306a36Sopenharmony_ci		writel(val, cp->regs + REG_HP_INSTR_RAM_DATA_HI);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci		val = CAS_BASE(HP_INSTR_RAM_MID_OUTARG, inst->outarg >> 10);
116062306a36Sopenharmony_ci		val |= CAS_BASE(HP_INSTR_RAM_MID_OUTOP, inst->outop);
116162306a36Sopenharmony_ci		val |= CAS_BASE(HP_INSTR_RAM_MID_FNEXT, inst->fnext);
116262306a36Sopenharmony_ci		val |= CAS_BASE(HP_INSTR_RAM_MID_FOFF, inst->foff);
116362306a36Sopenharmony_ci		val |= CAS_BASE(HP_INSTR_RAM_MID_SNEXT, inst->snext);
116462306a36Sopenharmony_ci		val |= CAS_BASE(HP_INSTR_RAM_MID_SOFF, inst->soff);
116562306a36Sopenharmony_ci		val |= CAS_BASE(HP_INSTR_RAM_MID_OP, inst->op);
116662306a36Sopenharmony_ci		writel(val, cp->regs + REG_HP_INSTR_RAM_DATA_MID);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci		val = CAS_BASE(HP_INSTR_RAM_LOW_OUTMASK, inst->outmask);
116962306a36Sopenharmony_ci		val |= CAS_BASE(HP_INSTR_RAM_LOW_OUTSHIFT, inst->outshift);
117062306a36Sopenharmony_ci		val |= CAS_BASE(HP_INSTR_RAM_LOW_OUTEN, inst->outenab);
117162306a36Sopenharmony_ci		val |= CAS_BASE(HP_INSTR_RAM_LOW_OUTARG, inst->outarg);
117262306a36Sopenharmony_ci		writel(val, cp->regs + REG_HP_INSTR_RAM_DATA_LOW);
117362306a36Sopenharmony_ci		++firmware;
117462306a36Sopenharmony_ci		++i;
117562306a36Sopenharmony_ci	}
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_cistatic void cas_init_rx_dma(struct cas *cp)
117962306a36Sopenharmony_ci{
118062306a36Sopenharmony_ci	u64 desc_dma = cp->block_dvma;
118162306a36Sopenharmony_ci	u32 val;
118262306a36Sopenharmony_ci	int i, size;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	/* rx free descriptors */
118562306a36Sopenharmony_ci	val = CAS_BASE(RX_CFG_SWIVEL, RX_SWIVEL_OFF_VAL);
118662306a36Sopenharmony_ci	val |= CAS_BASE(RX_CFG_DESC_RING, RX_DESC_RINGN_INDEX(0));
118762306a36Sopenharmony_ci	val |= CAS_BASE(RX_CFG_COMP_RING, RX_COMP_RINGN_INDEX(0));
118862306a36Sopenharmony_ci	if ((N_RX_DESC_RINGS > 1) &&
118962306a36Sopenharmony_ci	    (cp->cas_flags & CAS_FLAG_REG_PLUS))  /* do desc 2 */
119062306a36Sopenharmony_ci		val |= CAS_BASE(RX_CFG_DESC_RING1, RX_DESC_RINGN_INDEX(1));
119162306a36Sopenharmony_ci	writel(val, cp->regs + REG_RX_CFG);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	val = (unsigned long) cp->init_rxds[0] -
119462306a36Sopenharmony_ci		(unsigned long) cp->init_block;
119562306a36Sopenharmony_ci	writel((desc_dma + val) >> 32, cp->regs + REG_RX_DB_HI);
119662306a36Sopenharmony_ci	writel((desc_dma + val) & 0xffffffff, cp->regs + REG_RX_DB_LOW);
119762306a36Sopenharmony_ci	writel(RX_DESC_RINGN_SIZE(0) - 4, cp->regs + REG_RX_KICK);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {
120062306a36Sopenharmony_ci		/* rx desc 2 is for IPSEC packets. however,
120162306a36Sopenharmony_ci		 * we don't it that for that purpose.
120262306a36Sopenharmony_ci		 */
120362306a36Sopenharmony_ci		val = (unsigned long) cp->init_rxds[1] -
120462306a36Sopenharmony_ci			(unsigned long) cp->init_block;
120562306a36Sopenharmony_ci		writel((desc_dma + val) >> 32, cp->regs + REG_PLUS_RX_DB1_HI);
120662306a36Sopenharmony_ci		writel((desc_dma + val) & 0xffffffff, cp->regs +
120762306a36Sopenharmony_ci		       REG_PLUS_RX_DB1_LOW);
120862306a36Sopenharmony_ci		writel(RX_DESC_RINGN_SIZE(1) - 4, cp->regs +
120962306a36Sopenharmony_ci		       REG_PLUS_RX_KICK1);
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	/* rx completion registers */
121362306a36Sopenharmony_ci	val = (unsigned long) cp->init_rxcs[0] -
121462306a36Sopenharmony_ci		(unsigned long) cp->init_block;
121562306a36Sopenharmony_ci	writel((desc_dma + val) >> 32, cp->regs + REG_RX_CB_HI);
121662306a36Sopenharmony_ci	writel((desc_dma + val) & 0xffffffff, cp->regs + REG_RX_CB_LOW);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {
121962306a36Sopenharmony_ci		/* rx comp 2-4 */
122062306a36Sopenharmony_ci		for (i = 1; i < MAX_RX_COMP_RINGS; i++) {
122162306a36Sopenharmony_ci			val = (unsigned long) cp->init_rxcs[i] -
122262306a36Sopenharmony_ci				(unsigned long) cp->init_block;
122362306a36Sopenharmony_ci			writel((desc_dma + val) >> 32, cp->regs +
122462306a36Sopenharmony_ci			       REG_PLUS_RX_CBN_HI(i));
122562306a36Sopenharmony_ci			writel((desc_dma + val) & 0xffffffff, cp->regs +
122662306a36Sopenharmony_ci			       REG_PLUS_RX_CBN_LOW(i));
122762306a36Sopenharmony_ci		}
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	/* read selective clear regs to prevent spurious interrupts
123162306a36Sopenharmony_ci	 * on reset because complete == kick.
123262306a36Sopenharmony_ci	 * selective clear set up to prevent interrupts on resets
123362306a36Sopenharmony_ci	 */
123462306a36Sopenharmony_ci	readl(cp->regs + REG_INTR_STATUS_ALIAS);
123562306a36Sopenharmony_ci	writel(INTR_RX_DONE | INTR_RX_BUF_UNAVAIL, cp->regs + REG_ALIAS_CLEAR);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	/* set up pause thresholds */
123862306a36Sopenharmony_ci	val  = CAS_BASE(RX_PAUSE_THRESH_OFF,
123962306a36Sopenharmony_ci			cp->rx_pause_off / RX_PAUSE_THRESH_QUANTUM);
124062306a36Sopenharmony_ci	val |= CAS_BASE(RX_PAUSE_THRESH_ON,
124162306a36Sopenharmony_ci			cp->rx_pause_on / RX_PAUSE_THRESH_QUANTUM);
124262306a36Sopenharmony_ci	writel(val, cp->regs + REG_RX_PAUSE_THRESH);
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	/* zero out dma reassembly buffers */
124562306a36Sopenharmony_ci	for (i = 0; i < 64; i++) {
124662306a36Sopenharmony_ci		writel(i, cp->regs + REG_RX_TABLE_ADDR);
124762306a36Sopenharmony_ci		writel(0x0, cp->regs + REG_RX_TABLE_DATA_LOW);
124862306a36Sopenharmony_ci		writel(0x0, cp->regs + REG_RX_TABLE_DATA_MID);
124962306a36Sopenharmony_ci		writel(0x0, cp->regs + REG_RX_TABLE_DATA_HI);
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	/* make sure address register is 0 for normal operation */
125362306a36Sopenharmony_ci	writel(0x0, cp->regs + REG_RX_CTRL_FIFO_ADDR);
125462306a36Sopenharmony_ci	writel(0x0, cp->regs + REG_RX_IPP_FIFO_ADDR);
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	/* interrupt mitigation */
125762306a36Sopenharmony_ci#ifdef USE_RX_BLANK
125862306a36Sopenharmony_ci	val = CAS_BASE(RX_BLANK_INTR_TIME, RX_BLANK_INTR_TIME_VAL);
125962306a36Sopenharmony_ci	val |= CAS_BASE(RX_BLANK_INTR_PKT, RX_BLANK_INTR_PKT_VAL);
126062306a36Sopenharmony_ci	writel(val, cp->regs + REG_RX_BLANK);
126162306a36Sopenharmony_ci#else
126262306a36Sopenharmony_ci	writel(0x0, cp->regs + REG_RX_BLANK);
126362306a36Sopenharmony_ci#endif
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	/* interrupt generation as a function of low water marks for
126662306a36Sopenharmony_ci	 * free desc and completion entries. these are used to trigger
126762306a36Sopenharmony_ci	 * housekeeping for rx descs. we don't use the free interrupt
126862306a36Sopenharmony_ci	 * as it's not very useful
126962306a36Sopenharmony_ci	 */
127062306a36Sopenharmony_ci	/* val = CAS_BASE(RX_AE_THRESH_FREE, RX_AE_FREEN_VAL(0)); */
127162306a36Sopenharmony_ci	val = CAS_BASE(RX_AE_THRESH_COMP, RX_AE_COMP_VAL);
127262306a36Sopenharmony_ci	writel(val, cp->regs + REG_RX_AE_THRESH);
127362306a36Sopenharmony_ci	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {
127462306a36Sopenharmony_ci		val = CAS_BASE(RX_AE1_THRESH_FREE, RX_AE_FREEN_VAL(1));
127562306a36Sopenharmony_ci		writel(val, cp->regs + REG_PLUS_RX_AE1_THRESH);
127662306a36Sopenharmony_ci	}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	/* Random early detect registers. useful for congestion avoidance.
127962306a36Sopenharmony_ci	 * this should be tunable.
128062306a36Sopenharmony_ci	 */
128162306a36Sopenharmony_ci	writel(0x0, cp->regs + REG_RX_RED);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	/* receive page sizes. default == 2K (0x800) */
128462306a36Sopenharmony_ci	val = 0;
128562306a36Sopenharmony_ci	if (cp->page_size == 0x1000)
128662306a36Sopenharmony_ci		val = 0x1;
128762306a36Sopenharmony_ci	else if (cp->page_size == 0x2000)
128862306a36Sopenharmony_ci		val = 0x2;
128962306a36Sopenharmony_ci	else if (cp->page_size == 0x4000)
129062306a36Sopenharmony_ci		val = 0x3;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	/* round mtu + offset. constrain to page size. */
129362306a36Sopenharmony_ci	size = cp->dev->mtu + 64;
129462306a36Sopenharmony_ci	if (size > cp->page_size)
129562306a36Sopenharmony_ci		size = cp->page_size;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	if (size <= 0x400)
129862306a36Sopenharmony_ci		i = 0x0;
129962306a36Sopenharmony_ci	else if (size <= 0x800)
130062306a36Sopenharmony_ci		i = 0x1;
130162306a36Sopenharmony_ci	else if (size <= 0x1000)
130262306a36Sopenharmony_ci		i = 0x2;
130362306a36Sopenharmony_ci	else
130462306a36Sopenharmony_ci		i = 0x3;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	cp->mtu_stride = 1 << (i + 10);
130762306a36Sopenharmony_ci	val  = CAS_BASE(RX_PAGE_SIZE, val);
130862306a36Sopenharmony_ci	val |= CAS_BASE(RX_PAGE_SIZE_MTU_STRIDE, i);
130962306a36Sopenharmony_ci	val |= CAS_BASE(RX_PAGE_SIZE_MTU_COUNT, cp->page_size >> (i + 10));
131062306a36Sopenharmony_ci	val |= CAS_BASE(RX_PAGE_SIZE_MTU_OFF, 0x1);
131162306a36Sopenharmony_ci	writel(val, cp->regs + REG_RX_PAGE_SIZE);
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	/* enable the header parser if desired */
131462306a36Sopenharmony_ci	if (&CAS_HP_FIRMWARE[0] == &cas_prog_null[0])
131562306a36Sopenharmony_ci		return;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	val = CAS_BASE(HP_CFG_NUM_CPU, CAS_NCPUS > 63 ? 0 : CAS_NCPUS);
131862306a36Sopenharmony_ci	val |= HP_CFG_PARSE_EN | HP_CFG_SYN_INC_MASK;
131962306a36Sopenharmony_ci	val |= CAS_BASE(HP_CFG_TCP_THRESH, HP_TCP_THRESH_VAL);
132062306a36Sopenharmony_ci	writel(val, cp->regs + REG_HP_CFG);
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_cistatic inline void cas_rxc_init(struct cas_rx_comp *rxc)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	memset(rxc, 0, sizeof(*rxc));
132662306a36Sopenharmony_ci	rxc->word4 = cpu_to_le64(RX_COMP4_ZERO);
132762306a36Sopenharmony_ci}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci/* NOTE: we use the ENC RX DESC ring for spares. the rx_page[0,1]
133062306a36Sopenharmony_ci * flipping is protected by the fact that the chip will not
133162306a36Sopenharmony_ci * hand back the same page index while it's being processed.
133262306a36Sopenharmony_ci */
133362306a36Sopenharmony_cistatic inline cas_page_t *cas_page_spare(struct cas *cp, const int index)
133462306a36Sopenharmony_ci{
133562306a36Sopenharmony_ci	cas_page_t *page = cp->rx_pages[1][index];
133662306a36Sopenharmony_ci	cas_page_t *new;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	if (page_count(page->buffer) == 1)
133962306a36Sopenharmony_ci		return page;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	new = cas_page_dequeue(cp);
134262306a36Sopenharmony_ci	if (new) {
134362306a36Sopenharmony_ci		spin_lock(&cp->rx_inuse_lock);
134462306a36Sopenharmony_ci		list_add(&page->list, &cp->rx_inuse_list);
134562306a36Sopenharmony_ci		spin_unlock(&cp->rx_inuse_lock);
134662306a36Sopenharmony_ci	}
134762306a36Sopenharmony_ci	return new;
134862306a36Sopenharmony_ci}
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci/* this needs to be changed if we actually use the ENC RX DESC ring */
135162306a36Sopenharmony_cistatic cas_page_t *cas_page_swap(struct cas *cp, const int ring,
135262306a36Sopenharmony_ci				 const int index)
135362306a36Sopenharmony_ci{
135462306a36Sopenharmony_ci	cas_page_t **page0 = cp->rx_pages[0];
135562306a36Sopenharmony_ci	cas_page_t **page1 = cp->rx_pages[1];
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	/* swap if buffer is in use */
135862306a36Sopenharmony_ci	if (page_count(page0[index]->buffer) > 1) {
135962306a36Sopenharmony_ci		cas_page_t *new = cas_page_spare(cp, index);
136062306a36Sopenharmony_ci		if (new) {
136162306a36Sopenharmony_ci			page1[index] = page0[index];
136262306a36Sopenharmony_ci			page0[index] = new;
136362306a36Sopenharmony_ci		}
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci	RX_USED_SET(page0[index], 0);
136662306a36Sopenharmony_ci	return page0[index];
136762306a36Sopenharmony_ci}
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_cistatic void cas_clean_rxds(struct cas *cp)
137062306a36Sopenharmony_ci{
137162306a36Sopenharmony_ci	/* only clean ring 0 as ring 1 is used for spare buffers */
137262306a36Sopenharmony_ci        struct cas_rx_desc *rxd = cp->init_rxds[0];
137362306a36Sopenharmony_ci	int i, size;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	/* release all rx flows */
137662306a36Sopenharmony_ci	for (i = 0; i < N_RX_FLOWS; i++) {
137762306a36Sopenharmony_ci		struct sk_buff *skb;
137862306a36Sopenharmony_ci		while ((skb = __skb_dequeue(&cp->rx_flows[i]))) {
137962306a36Sopenharmony_ci			cas_skb_release(skb);
138062306a36Sopenharmony_ci		}
138162306a36Sopenharmony_ci	}
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	/* initialize descriptors */
138462306a36Sopenharmony_ci	size = RX_DESC_RINGN_SIZE(0);
138562306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
138662306a36Sopenharmony_ci		cas_page_t *page = cas_page_swap(cp, 0, i);
138762306a36Sopenharmony_ci		rxd[i].buffer = cpu_to_le64(page->dma_addr);
138862306a36Sopenharmony_ci		rxd[i].index  = cpu_to_le64(CAS_BASE(RX_INDEX_NUM, i) |
138962306a36Sopenharmony_ci					    CAS_BASE(RX_INDEX_RING, 0));
139062306a36Sopenharmony_ci	}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	cp->rx_old[0]  = RX_DESC_RINGN_SIZE(0) - 4;
139362306a36Sopenharmony_ci	cp->rx_last[0] = 0;
139462306a36Sopenharmony_ci	cp->cas_flags &= ~CAS_FLAG_RXD_POST(0);
139562306a36Sopenharmony_ci}
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_cistatic void cas_clean_rxcs(struct cas *cp)
139862306a36Sopenharmony_ci{
139962306a36Sopenharmony_ci	int i, j;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	/* take ownership of rx comp descriptors */
140262306a36Sopenharmony_ci	memset(cp->rx_cur, 0, sizeof(*cp->rx_cur)*N_RX_COMP_RINGS);
140362306a36Sopenharmony_ci	memset(cp->rx_new, 0, sizeof(*cp->rx_new)*N_RX_COMP_RINGS);
140462306a36Sopenharmony_ci	for (i = 0; i < N_RX_COMP_RINGS; i++) {
140562306a36Sopenharmony_ci		struct cas_rx_comp *rxc = cp->init_rxcs[i];
140662306a36Sopenharmony_ci		for (j = 0; j < RX_COMP_RINGN_SIZE(i); j++) {
140762306a36Sopenharmony_ci			cas_rxc_init(rxc + j);
140862306a36Sopenharmony_ci		}
140962306a36Sopenharmony_ci	}
141062306a36Sopenharmony_ci}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci#if 0
141362306a36Sopenharmony_ci/* When we get a RX fifo overflow, the RX unit is probably hung
141462306a36Sopenharmony_ci * so we do the following.
141562306a36Sopenharmony_ci *
141662306a36Sopenharmony_ci * If any part of the reset goes wrong, we return 1 and that causes the
141762306a36Sopenharmony_ci * whole chip to be reset.
141862306a36Sopenharmony_ci */
141962306a36Sopenharmony_cistatic int cas_rxmac_reset(struct cas *cp)
142062306a36Sopenharmony_ci{
142162306a36Sopenharmony_ci	struct net_device *dev = cp->dev;
142262306a36Sopenharmony_ci	int limit;
142362306a36Sopenharmony_ci	u32 val;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	/* First, reset MAC RX. */
142662306a36Sopenharmony_ci	writel(cp->mac_rx_cfg & ~MAC_RX_CFG_EN, cp->regs + REG_MAC_RX_CFG);
142762306a36Sopenharmony_ci	for (limit = 0; limit < STOP_TRIES; limit++) {
142862306a36Sopenharmony_ci		if (!(readl(cp->regs + REG_MAC_RX_CFG) & MAC_RX_CFG_EN))
142962306a36Sopenharmony_ci			break;
143062306a36Sopenharmony_ci		udelay(10);
143162306a36Sopenharmony_ci	}
143262306a36Sopenharmony_ci	if (limit == STOP_TRIES) {
143362306a36Sopenharmony_ci		netdev_err(dev, "RX MAC will not disable, resetting whole chip\n");
143462306a36Sopenharmony_ci		return 1;
143562306a36Sopenharmony_ci	}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	/* Second, disable RX DMA. */
143862306a36Sopenharmony_ci	writel(0, cp->regs + REG_RX_CFG);
143962306a36Sopenharmony_ci	for (limit = 0; limit < STOP_TRIES; limit++) {
144062306a36Sopenharmony_ci		if (!(readl(cp->regs + REG_RX_CFG) & RX_CFG_DMA_EN))
144162306a36Sopenharmony_ci			break;
144262306a36Sopenharmony_ci		udelay(10);
144362306a36Sopenharmony_ci	}
144462306a36Sopenharmony_ci	if (limit == STOP_TRIES) {
144562306a36Sopenharmony_ci		netdev_err(dev, "RX DMA will not disable, resetting whole chip\n");
144662306a36Sopenharmony_ci		return 1;
144762306a36Sopenharmony_ci	}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	mdelay(5);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	/* Execute RX reset command. */
145262306a36Sopenharmony_ci	writel(SW_RESET_RX, cp->regs + REG_SW_RESET);
145362306a36Sopenharmony_ci	for (limit = 0; limit < STOP_TRIES; limit++) {
145462306a36Sopenharmony_ci		if (!(readl(cp->regs + REG_SW_RESET) & SW_RESET_RX))
145562306a36Sopenharmony_ci			break;
145662306a36Sopenharmony_ci		udelay(10);
145762306a36Sopenharmony_ci	}
145862306a36Sopenharmony_ci	if (limit == STOP_TRIES) {
145962306a36Sopenharmony_ci		netdev_err(dev, "RX reset command will not execute, resetting whole chip\n");
146062306a36Sopenharmony_ci		return 1;
146162306a36Sopenharmony_ci	}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	/* reset driver rx state */
146462306a36Sopenharmony_ci	cas_clean_rxds(cp);
146562306a36Sopenharmony_ci	cas_clean_rxcs(cp);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	/* Now, reprogram the rest of RX unit. */
146862306a36Sopenharmony_ci	cas_init_rx_dma(cp);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	/* re-enable */
147162306a36Sopenharmony_ci	val = readl(cp->regs + REG_RX_CFG);
147262306a36Sopenharmony_ci	writel(val | RX_CFG_DMA_EN, cp->regs + REG_RX_CFG);
147362306a36Sopenharmony_ci	writel(MAC_RX_FRAME_RECV, cp->regs + REG_MAC_RX_MASK);
147462306a36Sopenharmony_ci	val = readl(cp->regs + REG_MAC_RX_CFG);
147562306a36Sopenharmony_ci	writel(val | MAC_RX_CFG_EN, cp->regs + REG_MAC_RX_CFG);
147662306a36Sopenharmony_ci	return 0;
147762306a36Sopenharmony_ci}
147862306a36Sopenharmony_ci#endif
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_cistatic int cas_rxmac_interrupt(struct net_device *dev, struct cas *cp,
148162306a36Sopenharmony_ci			       u32 status)
148262306a36Sopenharmony_ci{
148362306a36Sopenharmony_ci	u32 stat = readl(cp->regs + REG_MAC_RX_STATUS);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	if (!stat)
148662306a36Sopenharmony_ci		return 0;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	netif_dbg(cp, intr, cp->dev, "rxmac interrupt, stat: 0x%x\n", stat);
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	/* these are all rollovers */
149162306a36Sopenharmony_ci	spin_lock(&cp->stat_lock[0]);
149262306a36Sopenharmony_ci	if (stat & MAC_RX_ALIGN_ERR)
149362306a36Sopenharmony_ci		cp->net_stats[0].rx_frame_errors += 0x10000;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	if (stat & MAC_RX_CRC_ERR)
149662306a36Sopenharmony_ci		cp->net_stats[0].rx_crc_errors += 0x10000;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	if (stat & MAC_RX_LEN_ERR)
149962306a36Sopenharmony_ci		cp->net_stats[0].rx_length_errors += 0x10000;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	if (stat & MAC_RX_OVERFLOW) {
150262306a36Sopenharmony_ci		cp->net_stats[0].rx_over_errors++;
150362306a36Sopenharmony_ci		cp->net_stats[0].rx_fifo_errors++;
150462306a36Sopenharmony_ci	}
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	/* We do not track MAC_RX_FRAME_COUNT and MAC_RX_VIOL_ERR
150762306a36Sopenharmony_ci	 * events.
150862306a36Sopenharmony_ci	 */
150962306a36Sopenharmony_ci	spin_unlock(&cp->stat_lock[0]);
151062306a36Sopenharmony_ci	return 0;
151162306a36Sopenharmony_ci}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic int cas_mac_interrupt(struct net_device *dev, struct cas *cp,
151462306a36Sopenharmony_ci			     u32 status)
151562306a36Sopenharmony_ci{
151662306a36Sopenharmony_ci	u32 stat = readl(cp->regs + REG_MAC_CTRL_STATUS);
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	if (!stat)
151962306a36Sopenharmony_ci		return 0;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	netif_printk(cp, intr, KERN_DEBUG, cp->dev,
152262306a36Sopenharmony_ci		     "mac interrupt, stat: 0x%x\n", stat);
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	/* This interrupt is just for pause frame and pause
152562306a36Sopenharmony_ci	 * tracking.  It is useful for diagnostics and debug
152662306a36Sopenharmony_ci	 * but probably by default we will mask these events.
152762306a36Sopenharmony_ci	 */
152862306a36Sopenharmony_ci	if (stat & MAC_CTRL_PAUSE_STATE)
152962306a36Sopenharmony_ci		cp->pause_entered++;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	if (stat & MAC_CTRL_PAUSE_RECEIVED)
153262306a36Sopenharmony_ci		cp->pause_last_time_recvd = (stat >> 16);
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	return 0;
153562306a36Sopenharmony_ci}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci/* Must be invoked under cp->lock. */
153962306a36Sopenharmony_cistatic inline int cas_mdio_link_not_up(struct cas *cp)
154062306a36Sopenharmony_ci{
154162306a36Sopenharmony_ci	u16 val;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	switch (cp->lstate) {
154462306a36Sopenharmony_ci	case link_force_ret:
154562306a36Sopenharmony_ci		netif_info(cp, link, cp->dev, "Autoneg failed again, keeping forced mode\n");
154662306a36Sopenharmony_ci		cas_phy_write(cp, MII_BMCR, cp->link_fcntl);
154762306a36Sopenharmony_ci		cp->timer_ticks = 5;
154862306a36Sopenharmony_ci		cp->lstate = link_force_ok;
154962306a36Sopenharmony_ci		cp->link_transition = LINK_TRANSITION_LINK_CONFIG;
155062306a36Sopenharmony_ci		break;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	case link_aneg:
155362306a36Sopenharmony_ci		val = cas_phy_read(cp, MII_BMCR);
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci		/* Try forced modes. we try things in the following order:
155662306a36Sopenharmony_ci		 * 1000 full -> 100 full/half -> 10 half
155762306a36Sopenharmony_ci		 */
155862306a36Sopenharmony_ci		val &= ~(BMCR_ANRESTART | BMCR_ANENABLE);
155962306a36Sopenharmony_ci		val |= BMCR_FULLDPLX;
156062306a36Sopenharmony_ci		val |= (cp->cas_flags & CAS_FLAG_1000MB_CAP) ?
156162306a36Sopenharmony_ci			CAS_BMCR_SPEED1000 : BMCR_SPEED100;
156262306a36Sopenharmony_ci		cas_phy_write(cp, MII_BMCR, val);
156362306a36Sopenharmony_ci		cp->timer_ticks = 5;
156462306a36Sopenharmony_ci		cp->lstate = link_force_try;
156562306a36Sopenharmony_ci		cp->link_transition = LINK_TRANSITION_LINK_CONFIG;
156662306a36Sopenharmony_ci		break;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	case link_force_try:
156962306a36Sopenharmony_ci		/* Downgrade from 1000 to 100 to 10 Mbps if necessary. */
157062306a36Sopenharmony_ci		val = cas_phy_read(cp, MII_BMCR);
157162306a36Sopenharmony_ci		cp->timer_ticks = 5;
157262306a36Sopenharmony_ci		if (val & CAS_BMCR_SPEED1000) { /* gigabit */
157362306a36Sopenharmony_ci			val &= ~CAS_BMCR_SPEED1000;
157462306a36Sopenharmony_ci			val |= (BMCR_SPEED100 | BMCR_FULLDPLX);
157562306a36Sopenharmony_ci			cas_phy_write(cp, MII_BMCR, val);
157662306a36Sopenharmony_ci			break;
157762306a36Sopenharmony_ci		}
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci		if (val & BMCR_SPEED100) {
158062306a36Sopenharmony_ci			if (val & BMCR_FULLDPLX) /* fd failed */
158162306a36Sopenharmony_ci				val &= ~BMCR_FULLDPLX;
158262306a36Sopenharmony_ci			else { /* 100Mbps failed */
158362306a36Sopenharmony_ci				val &= ~BMCR_SPEED100;
158462306a36Sopenharmony_ci			}
158562306a36Sopenharmony_ci			cas_phy_write(cp, MII_BMCR, val);
158662306a36Sopenharmony_ci			break;
158762306a36Sopenharmony_ci		}
158862306a36Sopenharmony_ci		break;
158962306a36Sopenharmony_ci	default:
159062306a36Sopenharmony_ci		break;
159162306a36Sopenharmony_ci	}
159262306a36Sopenharmony_ci	return 0;
159362306a36Sopenharmony_ci}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci/* must be invoked with cp->lock held */
159762306a36Sopenharmony_cistatic int cas_mii_link_check(struct cas *cp, const u16 bmsr)
159862306a36Sopenharmony_ci{
159962306a36Sopenharmony_ci	int restart;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	if (bmsr & BMSR_LSTATUS) {
160262306a36Sopenharmony_ci		/* Ok, here we got a link. If we had it due to a forced
160362306a36Sopenharmony_ci		 * fallback, and we were configured for autoneg, we
160462306a36Sopenharmony_ci		 * retry a short autoneg pass. If you know your hub is
160562306a36Sopenharmony_ci		 * broken, use ethtool ;)
160662306a36Sopenharmony_ci		 */
160762306a36Sopenharmony_ci		if ((cp->lstate == link_force_try) &&
160862306a36Sopenharmony_ci		    (cp->link_cntl & BMCR_ANENABLE)) {
160962306a36Sopenharmony_ci			cp->lstate = link_force_ret;
161062306a36Sopenharmony_ci			cp->link_transition = LINK_TRANSITION_LINK_CONFIG;
161162306a36Sopenharmony_ci			cas_mif_poll(cp, 0);
161262306a36Sopenharmony_ci			cp->link_fcntl = cas_phy_read(cp, MII_BMCR);
161362306a36Sopenharmony_ci			cp->timer_ticks = 5;
161462306a36Sopenharmony_ci			if (cp->opened)
161562306a36Sopenharmony_ci				netif_info(cp, link, cp->dev,
161662306a36Sopenharmony_ci					   "Got link after fallback, retrying autoneg once...\n");
161762306a36Sopenharmony_ci			cas_phy_write(cp, MII_BMCR,
161862306a36Sopenharmony_ci				      cp->link_fcntl | BMCR_ANENABLE |
161962306a36Sopenharmony_ci				      BMCR_ANRESTART);
162062306a36Sopenharmony_ci			cas_mif_poll(cp, 1);
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci		} else if (cp->lstate != link_up) {
162362306a36Sopenharmony_ci			cp->lstate = link_up;
162462306a36Sopenharmony_ci			cp->link_transition = LINK_TRANSITION_LINK_UP;
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci			if (cp->opened) {
162762306a36Sopenharmony_ci				cas_set_link_modes(cp);
162862306a36Sopenharmony_ci				netif_carrier_on(cp->dev);
162962306a36Sopenharmony_ci			}
163062306a36Sopenharmony_ci		}
163162306a36Sopenharmony_ci		return 0;
163262306a36Sopenharmony_ci	}
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	/* link not up. if the link was previously up, we restart the
163562306a36Sopenharmony_ci	 * whole process
163662306a36Sopenharmony_ci	 */
163762306a36Sopenharmony_ci	restart = 0;
163862306a36Sopenharmony_ci	if (cp->lstate == link_up) {
163962306a36Sopenharmony_ci		cp->lstate = link_down;
164062306a36Sopenharmony_ci		cp->link_transition = LINK_TRANSITION_LINK_DOWN;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci		netif_carrier_off(cp->dev);
164362306a36Sopenharmony_ci		if (cp->opened)
164462306a36Sopenharmony_ci			netif_info(cp, link, cp->dev, "Link down\n");
164562306a36Sopenharmony_ci		restart = 1;
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	} else if (++cp->timer_ticks > 10)
164862306a36Sopenharmony_ci		cas_mdio_link_not_up(cp);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	return restart;
165162306a36Sopenharmony_ci}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_cistatic int cas_mif_interrupt(struct net_device *dev, struct cas *cp,
165462306a36Sopenharmony_ci			     u32 status)
165562306a36Sopenharmony_ci{
165662306a36Sopenharmony_ci	u32 stat = readl(cp->regs + REG_MIF_STATUS);
165762306a36Sopenharmony_ci	u16 bmsr;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	/* check for a link change */
166062306a36Sopenharmony_ci	if (CAS_VAL(MIF_STATUS_POLL_STATUS, stat) == 0)
166162306a36Sopenharmony_ci		return 0;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	bmsr = CAS_VAL(MIF_STATUS_POLL_DATA, stat);
166462306a36Sopenharmony_ci	return cas_mii_link_check(cp, bmsr);
166562306a36Sopenharmony_ci}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_cistatic int cas_pci_interrupt(struct net_device *dev, struct cas *cp,
166862306a36Sopenharmony_ci			     u32 status)
166962306a36Sopenharmony_ci{
167062306a36Sopenharmony_ci	u32 stat = readl(cp->regs + REG_PCI_ERR_STATUS);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	if (!stat)
167362306a36Sopenharmony_ci		return 0;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	netdev_err(dev, "PCI error [%04x:%04x]",
167662306a36Sopenharmony_ci		   stat, readl(cp->regs + REG_BIM_DIAG));
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	/* cassini+ has this reserved */
167962306a36Sopenharmony_ci	if ((stat & PCI_ERR_BADACK) &&
168062306a36Sopenharmony_ci	    ((cp->cas_flags & CAS_FLAG_REG_PLUS) == 0))
168162306a36Sopenharmony_ci		pr_cont(" <No ACK64# during ABS64 cycle>");
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	if (stat & PCI_ERR_DTRTO)
168462306a36Sopenharmony_ci		pr_cont(" <Delayed transaction timeout>");
168562306a36Sopenharmony_ci	if (stat & PCI_ERR_OTHER)
168662306a36Sopenharmony_ci		pr_cont(" <other>");
168762306a36Sopenharmony_ci	if (stat & PCI_ERR_BIM_DMA_WRITE)
168862306a36Sopenharmony_ci		pr_cont(" <BIM DMA 0 write req>");
168962306a36Sopenharmony_ci	if (stat & PCI_ERR_BIM_DMA_READ)
169062306a36Sopenharmony_ci		pr_cont(" <BIM DMA 0 read req>");
169162306a36Sopenharmony_ci	pr_cont("\n");
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	if (stat & PCI_ERR_OTHER) {
169462306a36Sopenharmony_ci		int pci_errs;
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci		/* Interrogate PCI config space for the
169762306a36Sopenharmony_ci		 * true cause.
169862306a36Sopenharmony_ci		 */
169962306a36Sopenharmony_ci		pci_errs = pci_status_get_and_clear_errors(cp->pdev);
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci		netdev_err(dev, "PCI status errors[%04x]\n", pci_errs);
170262306a36Sopenharmony_ci		if (pci_errs & PCI_STATUS_PARITY)
170362306a36Sopenharmony_ci			netdev_err(dev, "PCI parity error detected\n");
170462306a36Sopenharmony_ci		if (pci_errs & PCI_STATUS_SIG_TARGET_ABORT)
170562306a36Sopenharmony_ci			netdev_err(dev, "PCI target abort\n");
170662306a36Sopenharmony_ci		if (pci_errs & PCI_STATUS_REC_TARGET_ABORT)
170762306a36Sopenharmony_ci			netdev_err(dev, "PCI master acks target abort\n");
170862306a36Sopenharmony_ci		if (pci_errs & PCI_STATUS_REC_MASTER_ABORT)
170962306a36Sopenharmony_ci			netdev_err(dev, "PCI master abort\n");
171062306a36Sopenharmony_ci		if (pci_errs & PCI_STATUS_SIG_SYSTEM_ERROR)
171162306a36Sopenharmony_ci			netdev_err(dev, "PCI system error SERR#\n");
171262306a36Sopenharmony_ci		if (pci_errs & PCI_STATUS_DETECTED_PARITY)
171362306a36Sopenharmony_ci			netdev_err(dev, "PCI parity error\n");
171462306a36Sopenharmony_ci	}
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	/* For all PCI errors, we should reset the chip. */
171762306a36Sopenharmony_ci	return 1;
171862306a36Sopenharmony_ci}
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci/* All non-normal interrupt conditions get serviced here.
172162306a36Sopenharmony_ci * Returns non-zero if we should just exit the interrupt
172262306a36Sopenharmony_ci * handler right now (ie. if we reset the card which invalidates
172362306a36Sopenharmony_ci * all of the other original irq status bits).
172462306a36Sopenharmony_ci */
172562306a36Sopenharmony_cistatic int cas_abnormal_irq(struct net_device *dev, struct cas *cp,
172662306a36Sopenharmony_ci			    u32 status)
172762306a36Sopenharmony_ci{
172862306a36Sopenharmony_ci	if (status & INTR_RX_TAG_ERROR) {
172962306a36Sopenharmony_ci		/* corrupt RX tag framing */
173062306a36Sopenharmony_ci		netif_printk(cp, rx_err, KERN_DEBUG, cp->dev,
173162306a36Sopenharmony_ci			     "corrupt rx tag framing\n");
173262306a36Sopenharmony_ci		spin_lock(&cp->stat_lock[0]);
173362306a36Sopenharmony_ci		cp->net_stats[0].rx_errors++;
173462306a36Sopenharmony_ci		spin_unlock(&cp->stat_lock[0]);
173562306a36Sopenharmony_ci		goto do_reset;
173662306a36Sopenharmony_ci	}
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	if (status & INTR_RX_LEN_MISMATCH) {
173962306a36Sopenharmony_ci		/* length mismatch. */
174062306a36Sopenharmony_ci		netif_printk(cp, rx_err, KERN_DEBUG, cp->dev,
174162306a36Sopenharmony_ci			     "length mismatch for rx frame\n");
174262306a36Sopenharmony_ci		spin_lock(&cp->stat_lock[0]);
174362306a36Sopenharmony_ci		cp->net_stats[0].rx_errors++;
174462306a36Sopenharmony_ci		spin_unlock(&cp->stat_lock[0]);
174562306a36Sopenharmony_ci		goto do_reset;
174662306a36Sopenharmony_ci	}
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	if (status & INTR_PCS_STATUS) {
174962306a36Sopenharmony_ci		if (cas_pcs_interrupt(dev, cp, status))
175062306a36Sopenharmony_ci			goto do_reset;
175162306a36Sopenharmony_ci	}
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	if (status & INTR_TX_MAC_STATUS) {
175462306a36Sopenharmony_ci		if (cas_txmac_interrupt(dev, cp, status))
175562306a36Sopenharmony_ci			goto do_reset;
175662306a36Sopenharmony_ci	}
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	if (status & INTR_RX_MAC_STATUS) {
175962306a36Sopenharmony_ci		if (cas_rxmac_interrupt(dev, cp, status))
176062306a36Sopenharmony_ci			goto do_reset;
176162306a36Sopenharmony_ci	}
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	if (status & INTR_MAC_CTRL_STATUS) {
176462306a36Sopenharmony_ci		if (cas_mac_interrupt(dev, cp, status))
176562306a36Sopenharmony_ci			goto do_reset;
176662306a36Sopenharmony_ci	}
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	if (status & INTR_MIF_STATUS) {
176962306a36Sopenharmony_ci		if (cas_mif_interrupt(dev, cp, status))
177062306a36Sopenharmony_ci			goto do_reset;
177162306a36Sopenharmony_ci	}
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	if (status & INTR_PCI_ERROR_STATUS) {
177462306a36Sopenharmony_ci		if (cas_pci_interrupt(dev, cp, status))
177562306a36Sopenharmony_ci			goto do_reset;
177662306a36Sopenharmony_ci	}
177762306a36Sopenharmony_ci	return 0;
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_cido_reset:
178062306a36Sopenharmony_ci#if 1
178162306a36Sopenharmony_ci	atomic_inc(&cp->reset_task_pending);
178262306a36Sopenharmony_ci	atomic_inc(&cp->reset_task_pending_all);
178362306a36Sopenharmony_ci	netdev_err(dev, "reset called in cas_abnormal_irq [0x%x]\n", status);
178462306a36Sopenharmony_ci	schedule_work(&cp->reset_task);
178562306a36Sopenharmony_ci#else
178662306a36Sopenharmony_ci	atomic_set(&cp->reset_task_pending, CAS_RESET_ALL);
178762306a36Sopenharmony_ci	netdev_err(dev, "reset called in cas_abnormal_irq\n");
178862306a36Sopenharmony_ci	schedule_work(&cp->reset_task);
178962306a36Sopenharmony_ci#endif
179062306a36Sopenharmony_ci	return 1;
179162306a36Sopenharmony_ci}
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci/* NOTE: CAS_TABORT returns 1 or 2 so that it can be used when
179462306a36Sopenharmony_ci *       determining whether to do a netif_stop/wakeup
179562306a36Sopenharmony_ci */
179662306a36Sopenharmony_ci#define CAS_TABORT(x)      (((x)->cas_flags & CAS_FLAG_TARGET_ABORT) ? 2 : 1)
179762306a36Sopenharmony_ci#define CAS_ROUND_PAGE(x)  (((x) + PAGE_SIZE - 1) & PAGE_MASK)
179862306a36Sopenharmony_cistatic inline int cas_calc_tabort(struct cas *cp, const unsigned long addr,
179962306a36Sopenharmony_ci				  const int len)
180062306a36Sopenharmony_ci{
180162306a36Sopenharmony_ci	unsigned long off = addr + len;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	if (CAS_TABORT(cp) == 1)
180462306a36Sopenharmony_ci		return 0;
180562306a36Sopenharmony_ci	if ((CAS_ROUND_PAGE(off) - off) > TX_TARGET_ABORT_LEN)
180662306a36Sopenharmony_ci		return 0;
180762306a36Sopenharmony_ci	return TX_TARGET_ABORT_LEN;
180862306a36Sopenharmony_ci}
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_cistatic inline void cas_tx_ringN(struct cas *cp, int ring, int limit)
181162306a36Sopenharmony_ci{
181262306a36Sopenharmony_ci	struct cas_tx_desc *txds;
181362306a36Sopenharmony_ci	struct sk_buff **skbs;
181462306a36Sopenharmony_ci	struct net_device *dev = cp->dev;
181562306a36Sopenharmony_ci	int entry, count;
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	spin_lock(&cp->tx_lock[ring]);
181862306a36Sopenharmony_ci	txds = cp->init_txds[ring];
181962306a36Sopenharmony_ci	skbs = cp->tx_skbs[ring];
182062306a36Sopenharmony_ci	entry = cp->tx_old[ring];
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	count = TX_BUFF_COUNT(ring, entry, limit);
182362306a36Sopenharmony_ci	while (entry != limit) {
182462306a36Sopenharmony_ci		struct sk_buff *skb = skbs[entry];
182562306a36Sopenharmony_ci		dma_addr_t daddr;
182662306a36Sopenharmony_ci		u32 dlen;
182762306a36Sopenharmony_ci		int frag;
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci		if (!skb) {
183062306a36Sopenharmony_ci			/* this should never occur */
183162306a36Sopenharmony_ci			entry = TX_DESC_NEXT(ring, entry);
183262306a36Sopenharmony_ci			continue;
183362306a36Sopenharmony_ci		}
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci		/* however, we might get only a partial skb release. */
183662306a36Sopenharmony_ci		count -= skb_shinfo(skb)->nr_frags +
183762306a36Sopenharmony_ci			+ cp->tx_tiny_use[ring][entry].nbufs + 1;
183862306a36Sopenharmony_ci		if (count < 0)
183962306a36Sopenharmony_ci			break;
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci		netif_printk(cp, tx_done, KERN_DEBUG, cp->dev,
184262306a36Sopenharmony_ci			     "tx[%d] done, slot %d\n", ring, entry);
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci		skbs[entry] = NULL;
184562306a36Sopenharmony_ci		cp->tx_tiny_use[ring][entry].nbufs = 0;
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci		for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) {
184862306a36Sopenharmony_ci			struct cas_tx_desc *txd = txds + entry;
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci			daddr = le64_to_cpu(txd->buffer);
185162306a36Sopenharmony_ci			dlen = CAS_VAL(TX_DESC_BUFLEN,
185262306a36Sopenharmony_ci				       le64_to_cpu(txd->control));
185362306a36Sopenharmony_ci			dma_unmap_page(&cp->pdev->dev, daddr, dlen,
185462306a36Sopenharmony_ci				       DMA_TO_DEVICE);
185562306a36Sopenharmony_ci			entry = TX_DESC_NEXT(ring, entry);
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci			/* tiny buffer may follow */
185862306a36Sopenharmony_ci			if (cp->tx_tiny_use[ring][entry].used) {
185962306a36Sopenharmony_ci				cp->tx_tiny_use[ring][entry].used = 0;
186062306a36Sopenharmony_ci				entry = TX_DESC_NEXT(ring, entry);
186162306a36Sopenharmony_ci			}
186262306a36Sopenharmony_ci		}
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci		spin_lock(&cp->stat_lock[ring]);
186562306a36Sopenharmony_ci		cp->net_stats[ring].tx_packets++;
186662306a36Sopenharmony_ci		cp->net_stats[ring].tx_bytes += skb->len;
186762306a36Sopenharmony_ci		spin_unlock(&cp->stat_lock[ring]);
186862306a36Sopenharmony_ci		dev_consume_skb_irq(skb);
186962306a36Sopenharmony_ci	}
187062306a36Sopenharmony_ci	cp->tx_old[ring] = entry;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	/* this is wrong for multiple tx rings. the net device needs
187362306a36Sopenharmony_ci	 * multiple queues for this to do the right thing.  we wait
187462306a36Sopenharmony_ci	 * for 2*packets to be available when using tiny buffers
187562306a36Sopenharmony_ci	 */
187662306a36Sopenharmony_ci	if (netif_queue_stopped(dev) &&
187762306a36Sopenharmony_ci	    (TX_BUFFS_AVAIL(cp, ring) > CAS_TABORT(cp)*(MAX_SKB_FRAGS + 1)))
187862306a36Sopenharmony_ci		netif_wake_queue(dev);
187962306a36Sopenharmony_ci	spin_unlock(&cp->tx_lock[ring]);
188062306a36Sopenharmony_ci}
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_cistatic void cas_tx(struct net_device *dev, struct cas *cp,
188362306a36Sopenharmony_ci		   u32 status)
188462306a36Sopenharmony_ci{
188562306a36Sopenharmony_ci        int limit, ring;
188662306a36Sopenharmony_ci#ifdef USE_TX_COMPWB
188762306a36Sopenharmony_ci	u64 compwb = le64_to_cpu(cp->init_block->tx_compwb);
188862306a36Sopenharmony_ci#endif
188962306a36Sopenharmony_ci	netif_printk(cp, intr, KERN_DEBUG, cp->dev,
189062306a36Sopenharmony_ci		     "tx interrupt, status: 0x%x, %llx\n",
189162306a36Sopenharmony_ci		     status, (unsigned long long)compwb);
189262306a36Sopenharmony_ci	/* process all the rings */
189362306a36Sopenharmony_ci	for (ring = 0; ring < N_TX_RINGS; ring++) {
189462306a36Sopenharmony_ci#ifdef USE_TX_COMPWB
189562306a36Sopenharmony_ci		/* use the completion writeback registers */
189662306a36Sopenharmony_ci		limit = (CAS_VAL(TX_COMPWB_MSB, compwb) << 8) |
189762306a36Sopenharmony_ci			CAS_VAL(TX_COMPWB_LSB, compwb);
189862306a36Sopenharmony_ci		compwb = TX_COMPWB_NEXT(compwb);
189962306a36Sopenharmony_ci#else
190062306a36Sopenharmony_ci		limit = readl(cp->regs + REG_TX_COMPN(ring));
190162306a36Sopenharmony_ci#endif
190262306a36Sopenharmony_ci		if (cp->tx_old[ring] != limit)
190362306a36Sopenharmony_ci			cas_tx_ringN(cp, ring, limit);
190462306a36Sopenharmony_ci	}
190562306a36Sopenharmony_ci}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_cistatic int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc,
190962306a36Sopenharmony_ci			      int entry, const u64 *words,
191062306a36Sopenharmony_ci			      struct sk_buff **skbref)
191162306a36Sopenharmony_ci{
191262306a36Sopenharmony_ci	int dlen, hlen, len, i, alloclen;
191362306a36Sopenharmony_ci	int off, swivel = RX_SWIVEL_OFF_VAL;
191462306a36Sopenharmony_ci	struct cas_page *page;
191562306a36Sopenharmony_ci	struct sk_buff *skb;
191662306a36Sopenharmony_ci	void *crcaddr;
191762306a36Sopenharmony_ci	__sum16 csum;
191862306a36Sopenharmony_ci	char *p;
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	hlen = CAS_VAL(RX_COMP2_HDR_SIZE, words[1]);
192162306a36Sopenharmony_ci	dlen = CAS_VAL(RX_COMP1_DATA_SIZE, words[0]);
192262306a36Sopenharmony_ci	len  = hlen + dlen;
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	if (RX_COPY_ALWAYS || (words[2] & RX_COMP3_SMALL_PKT))
192562306a36Sopenharmony_ci		alloclen = len;
192662306a36Sopenharmony_ci	else
192762306a36Sopenharmony_ci		alloclen = max(hlen, RX_COPY_MIN);
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	skb = netdev_alloc_skb(cp->dev, alloclen + swivel + cp->crc_size);
193062306a36Sopenharmony_ci	if (skb == NULL)
193162306a36Sopenharmony_ci		return -1;
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	*skbref = skb;
193462306a36Sopenharmony_ci	skb_reserve(skb, swivel);
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	p = skb->data;
193762306a36Sopenharmony_ci	crcaddr = NULL;
193862306a36Sopenharmony_ci	if (hlen) { /* always copy header pages */
193962306a36Sopenharmony_ci		i = CAS_VAL(RX_COMP2_HDR_INDEX, words[1]);
194062306a36Sopenharmony_ci		page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)];
194162306a36Sopenharmony_ci		off = CAS_VAL(RX_COMP2_HDR_OFF, words[1]) * 0x100 +
194262306a36Sopenharmony_ci			swivel;
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci		i = hlen;
194562306a36Sopenharmony_ci		if (!dlen) /* attach FCS */
194662306a36Sopenharmony_ci			i += cp->crc_size;
194762306a36Sopenharmony_ci		dma_sync_single_for_cpu(&cp->pdev->dev, page->dma_addr + off,
194862306a36Sopenharmony_ci					i, DMA_FROM_DEVICE);
194962306a36Sopenharmony_ci		memcpy(p, page_address(page->buffer) + off, i);
195062306a36Sopenharmony_ci		dma_sync_single_for_device(&cp->pdev->dev,
195162306a36Sopenharmony_ci					   page->dma_addr + off, i,
195262306a36Sopenharmony_ci					   DMA_FROM_DEVICE);
195362306a36Sopenharmony_ci		RX_USED_ADD(page, 0x100);
195462306a36Sopenharmony_ci		p += hlen;
195562306a36Sopenharmony_ci		swivel = 0;
195662306a36Sopenharmony_ci	}
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	if (alloclen < (hlen + dlen)) {
196062306a36Sopenharmony_ci		skb_frag_t *frag = skb_shinfo(skb)->frags;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci		/* normal or jumbo packets. we use frags */
196362306a36Sopenharmony_ci		i = CAS_VAL(RX_COMP1_DATA_INDEX, words[0]);
196462306a36Sopenharmony_ci		page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)];
196562306a36Sopenharmony_ci		off = CAS_VAL(RX_COMP1_DATA_OFF, words[0]) + swivel;
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci		hlen = min(cp->page_size - off, dlen);
196862306a36Sopenharmony_ci		if (hlen < 0) {
196962306a36Sopenharmony_ci			netif_printk(cp, rx_err, KERN_DEBUG, cp->dev,
197062306a36Sopenharmony_ci				     "rx page overflow: %d\n", hlen);
197162306a36Sopenharmony_ci			dev_kfree_skb_irq(skb);
197262306a36Sopenharmony_ci			return -1;
197362306a36Sopenharmony_ci		}
197462306a36Sopenharmony_ci		i = hlen;
197562306a36Sopenharmony_ci		if (i == dlen)  /* attach FCS */
197662306a36Sopenharmony_ci			i += cp->crc_size;
197762306a36Sopenharmony_ci		dma_sync_single_for_cpu(&cp->pdev->dev, page->dma_addr + off,
197862306a36Sopenharmony_ci					i, DMA_FROM_DEVICE);
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci		/* make sure we always copy a header */
198162306a36Sopenharmony_ci		swivel = 0;
198262306a36Sopenharmony_ci		if (p == (char *) skb->data) { /* not split */
198362306a36Sopenharmony_ci			memcpy(p, page_address(page->buffer) + off,
198462306a36Sopenharmony_ci			       RX_COPY_MIN);
198562306a36Sopenharmony_ci			dma_sync_single_for_device(&cp->pdev->dev,
198662306a36Sopenharmony_ci						   page->dma_addr + off, i,
198762306a36Sopenharmony_ci						   DMA_FROM_DEVICE);
198862306a36Sopenharmony_ci			off += RX_COPY_MIN;
198962306a36Sopenharmony_ci			swivel = RX_COPY_MIN;
199062306a36Sopenharmony_ci			RX_USED_ADD(page, cp->mtu_stride);
199162306a36Sopenharmony_ci		} else {
199262306a36Sopenharmony_ci			RX_USED_ADD(page, hlen);
199362306a36Sopenharmony_ci		}
199462306a36Sopenharmony_ci		skb_put(skb, alloclen);
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci		skb_shinfo(skb)->nr_frags++;
199762306a36Sopenharmony_ci		skb->data_len += hlen - swivel;
199862306a36Sopenharmony_ci		skb->truesize += hlen - swivel;
199962306a36Sopenharmony_ci		skb->len      += hlen - swivel;
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci		skb_frag_fill_page_desc(frag, page->buffer, off, hlen - swivel);
200262306a36Sopenharmony_ci		__skb_frag_ref(frag);
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci		/* any more data? */
200562306a36Sopenharmony_ci		if ((words[0] & RX_COMP1_SPLIT_PKT) && ((dlen -= hlen) > 0)) {
200662306a36Sopenharmony_ci			hlen = dlen;
200762306a36Sopenharmony_ci			off = 0;
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci			i = CAS_VAL(RX_COMP2_NEXT_INDEX, words[1]);
201062306a36Sopenharmony_ci			page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)];
201162306a36Sopenharmony_ci			dma_sync_single_for_cpu(&cp->pdev->dev,
201262306a36Sopenharmony_ci						page->dma_addr,
201362306a36Sopenharmony_ci						hlen + cp->crc_size,
201462306a36Sopenharmony_ci						DMA_FROM_DEVICE);
201562306a36Sopenharmony_ci			dma_sync_single_for_device(&cp->pdev->dev,
201662306a36Sopenharmony_ci						   page->dma_addr,
201762306a36Sopenharmony_ci						   hlen + cp->crc_size,
201862306a36Sopenharmony_ci						   DMA_FROM_DEVICE);
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci			skb_shinfo(skb)->nr_frags++;
202162306a36Sopenharmony_ci			skb->data_len += hlen;
202262306a36Sopenharmony_ci			skb->len      += hlen;
202362306a36Sopenharmony_ci			frag++;
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci			skb_frag_fill_page_desc(frag, page->buffer, 0, hlen);
202662306a36Sopenharmony_ci			__skb_frag_ref(frag);
202762306a36Sopenharmony_ci			RX_USED_ADD(page, hlen + cp->crc_size);
202862306a36Sopenharmony_ci		}
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci		if (cp->crc_size)
203162306a36Sopenharmony_ci			crcaddr = page_address(page->buffer) + off + hlen;
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci	} else {
203462306a36Sopenharmony_ci		/* copying packet */
203562306a36Sopenharmony_ci		if (!dlen)
203662306a36Sopenharmony_ci			goto end_copy_pkt;
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci		i = CAS_VAL(RX_COMP1_DATA_INDEX, words[0]);
203962306a36Sopenharmony_ci		page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)];
204062306a36Sopenharmony_ci		off = CAS_VAL(RX_COMP1_DATA_OFF, words[0]) + swivel;
204162306a36Sopenharmony_ci		hlen = min(cp->page_size - off, dlen);
204262306a36Sopenharmony_ci		if (hlen < 0) {
204362306a36Sopenharmony_ci			netif_printk(cp, rx_err, KERN_DEBUG, cp->dev,
204462306a36Sopenharmony_ci				     "rx page overflow: %d\n", hlen);
204562306a36Sopenharmony_ci			dev_kfree_skb_irq(skb);
204662306a36Sopenharmony_ci			return -1;
204762306a36Sopenharmony_ci		}
204862306a36Sopenharmony_ci		i = hlen;
204962306a36Sopenharmony_ci		if (i == dlen) /* attach FCS */
205062306a36Sopenharmony_ci			i += cp->crc_size;
205162306a36Sopenharmony_ci		dma_sync_single_for_cpu(&cp->pdev->dev, page->dma_addr + off,
205262306a36Sopenharmony_ci					i, DMA_FROM_DEVICE);
205362306a36Sopenharmony_ci		memcpy(p, page_address(page->buffer) + off, i);
205462306a36Sopenharmony_ci		dma_sync_single_for_device(&cp->pdev->dev,
205562306a36Sopenharmony_ci					   page->dma_addr + off, i,
205662306a36Sopenharmony_ci					   DMA_FROM_DEVICE);
205762306a36Sopenharmony_ci		if (p == (char *) skb->data) /* not split */
205862306a36Sopenharmony_ci			RX_USED_ADD(page, cp->mtu_stride);
205962306a36Sopenharmony_ci		else
206062306a36Sopenharmony_ci			RX_USED_ADD(page, i);
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci		/* any more data? */
206362306a36Sopenharmony_ci		if ((words[0] & RX_COMP1_SPLIT_PKT) && ((dlen -= hlen) > 0)) {
206462306a36Sopenharmony_ci			p += hlen;
206562306a36Sopenharmony_ci			i = CAS_VAL(RX_COMP2_NEXT_INDEX, words[1]);
206662306a36Sopenharmony_ci			page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)];
206762306a36Sopenharmony_ci			dma_sync_single_for_cpu(&cp->pdev->dev,
206862306a36Sopenharmony_ci						page->dma_addr,
206962306a36Sopenharmony_ci						dlen + cp->crc_size,
207062306a36Sopenharmony_ci						DMA_FROM_DEVICE);
207162306a36Sopenharmony_ci			memcpy(p, page_address(page->buffer), dlen + cp->crc_size);
207262306a36Sopenharmony_ci			dma_sync_single_for_device(&cp->pdev->dev,
207362306a36Sopenharmony_ci						   page->dma_addr,
207462306a36Sopenharmony_ci						   dlen + cp->crc_size,
207562306a36Sopenharmony_ci						   DMA_FROM_DEVICE);
207662306a36Sopenharmony_ci			RX_USED_ADD(page, dlen + cp->crc_size);
207762306a36Sopenharmony_ci		}
207862306a36Sopenharmony_ciend_copy_pkt:
207962306a36Sopenharmony_ci		if (cp->crc_size)
208062306a36Sopenharmony_ci			crcaddr = skb->data + alloclen;
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci		skb_put(skb, alloclen);
208362306a36Sopenharmony_ci	}
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	csum = (__force __sum16)htons(CAS_VAL(RX_COMP4_TCP_CSUM, words[3]));
208662306a36Sopenharmony_ci	if (cp->crc_size) {
208762306a36Sopenharmony_ci		/* checksum includes FCS. strip it out. */
208862306a36Sopenharmony_ci		csum = csum_fold(csum_partial(crcaddr, cp->crc_size,
208962306a36Sopenharmony_ci					      csum_unfold(csum)));
209062306a36Sopenharmony_ci	}
209162306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, cp->dev);
209262306a36Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IP)) {
209362306a36Sopenharmony_ci		skb->csum = csum_unfold(~csum);
209462306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_COMPLETE;
209562306a36Sopenharmony_ci	} else
209662306a36Sopenharmony_ci		skb_checksum_none_assert(skb);
209762306a36Sopenharmony_ci	return len;
209862306a36Sopenharmony_ci}
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci/* we can handle up to 64 rx flows at a time. we do the same thing
210262306a36Sopenharmony_ci * as nonreassm except that we batch up the buffers.
210362306a36Sopenharmony_ci * NOTE: we currently just treat each flow as a bunch of packets that
210462306a36Sopenharmony_ci *       we pass up. a better way would be to coalesce the packets
210562306a36Sopenharmony_ci *       into a jumbo packet. to do that, we need to do the following:
210662306a36Sopenharmony_ci *       1) the first packet will have a clean split between header and
210762306a36Sopenharmony_ci *          data. save both.
210862306a36Sopenharmony_ci *       2) each time the next flow packet comes in, extend the
210962306a36Sopenharmony_ci *          data length and merge the checksums.
211062306a36Sopenharmony_ci *       3) on flow release, fix up the header.
211162306a36Sopenharmony_ci *       4) make sure the higher layer doesn't care.
211262306a36Sopenharmony_ci * because packets get coalesced, we shouldn't run into fragment count
211362306a36Sopenharmony_ci * issues.
211462306a36Sopenharmony_ci */
211562306a36Sopenharmony_cistatic inline void cas_rx_flow_pkt(struct cas *cp, const u64 *words,
211662306a36Sopenharmony_ci				   struct sk_buff *skb)
211762306a36Sopenharmony_ci{
211862306a36Sopenharmony_ci	int flowid = CAS_VAL(RX_COMP3_FLOWID, words[2]) & (N_RX_FLOWS - 1);
211962306a36Sopenharmony_ci	struct sk_buff_head *flow = &cp->rx_flows[flowid];
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	/* this is protected at a higher layer, so no need to
212262306a36Sopenharmony_ci	 * do any additional locking here. stick the buffer
212362306a36Sopenharmony_ci	 * at the end.
212462306a36Sopenharmony_ci	 */
212562306a36Sopenharmony_ci	__skb_queue_tail(flow, skb);
212662306a36Sopenharmony_ci	if (words[0] & RX_COMP1_RELEASE_FLOW) {
212762306a36Sopenharmony_ci		while ((skb = __skb_dequeue(flow))) {
212862306a36Sopenharmony_ci			cas_skb_release(skb);
212962306a36Sopenharmony_ci		}
213062306a36Sopenharmony_ci	}
213162306a36Sopenharmony_ci}
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci/* put rx descriptor back on ring. if a buffer is in use by a higher
213462306a36Sopenharmony_ci * layer, this will need to put in a replacement.
213562306a36Sopenharmony_ci */
213662306a36Sopenharmony_cistatic void cas_post_page(struct cas *cp, const int ring, const int index)
213762306a36Sopenharmony_ci{
213862306a36Sopenharmony_ci	cas_page_t *new;
213962306a36Sopenharmony_ci	int entry;
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	entry = cp->rx_old[ring];
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	new = cas_page_swap(cp, ring, index);
214462306a36Sopenharmony_ci	cp->init_rxds[ring][entry].buffer = cpu_to_le64(new->dma_addr);
214562306a36Sopenharmony_ci	cp->init_rxds[ring][entry].index  =
214662306a36Sopenharmony_ci		cpu_to_le64(CAS_BASE(RX_INDEX_NUM, index) |
214762306a36Sopenharmony_ci			    CAS_BASE(RX_INDEX_RING, ring));
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	entry = RX_DESC_ENTRY(ring, entry + 1);
215062306a36Sopenharmony_ci	cp->rx_old[ring] = entry;
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	if (entry % 4)
215362306a36Sopenharmony_ci		return;
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci	if (ring == 0)
215662306a36Sopenharmony_ci		writel(entry, cp->regs + REG_RX_KICK);
215762306a36Sopenharmony_ci	else if ((N_RX_DESC_RINGS > 1) &&
215862306a36Sopenharmony_ci		 (cp->cas_flags & CAS_FLAG_REG_PLUS))
215962306a36Sopenharmony_ci		writel(entry, cp->regs + REG_PLUS_RX_KICK1);
216062306a36Sopenharmony_ci}
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci/* only when things are bad */
216462306a36Sopenharmony_cistatic int cas_post_rxds_ringN(struct cas *cp, int ring, int num)
216562306a36Sopenharmony_ci{
216662306a36Sopenharmony_ci	unsigned int entry, last, count, released;
216762306a36Sopenharmony_ci	int cluster;
216862306a36Sopenharmony_ci	cas_page_t **page = cp->rx_pages[ring];
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	entry = cp->rx_old[ring];
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	netif_printk(cp, intr, KERN_DEBUG, cp->dev,
217362306a36Sopenharmony_ci		     "rxd[%d] interrupt, done: %d\n", ring, entry);
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	cluster = -1;
217662306a36Sopenharmony_ci	count = entry & 0x3;
217762306a36Sopenharmony_ci	last = RX_DESC_ENTRY(ring, num ? entry + num - 4: entry - 4);
217862306a36Sopenharmony_ci	released = 0;
217962306a36Sopenharmony_ci	while (entry != last) {
218062306a36Sopenharmony_ci		/* make a new buffer if it's still in use */
218162306a36Sopenharmony_ci		if (page_count(page[entry]->buffer) > 1) {
218262306a36Sopenharmony_ci			cas_page_t *new = cas_page_dequeue(cp);
218362306a36Sopenharmony_ci			if (!new) {
218462306a36Sopenharmony_ci				/* let the timer know that we need to
218562306a36Sopenharmony_ci				 * do this again
218662306a36Sopenharmony_ci				 */
218762306a36Sopenharmony_ci				cp->cas_flags |= CAS_FLAG_RXD_POST(ring);
218862306a36Sopenharmony_ci				if (!timer_pending(&cp->link_timer))
218962306a36Sopenharmony_ci					mod_timer(&cp->link_timer, jiffies +
219062306a36Sopenharmony_ci						  CAS_LINK_FAST_TIMEOUT);
219162306a36Sopenharmony_ci				cp->rx_old[ring]  = entry;
219262306a36Sopenharmony_ci				cp->rx_last[ring] = num ? num - released : 0;
219362306a36Sopenharmony_ci				return -ENOMEM;
219462306a36Sopenharmony_ci			}
219562306a36Sopenharmony_ci			spin_lock(&cp->rx_inuse_lock);
219662306a36Sopenharmony_ci			list_add(&page[entry]->list, &cp->rx_inuse_list);
219762306a36Sopenharmony_ci			spin_unlock(&cp->rx_inuse_lock);
219862306a36Sopenharmony_ci			cp->init_rxds[ring][entry].buffer =
219962306a36Sopenharmony_ci				cpu_to_le64(new->dma_addr);
220062306a36Sopenharmony_ci			page[entry] = new;
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci		}
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci		if (++count == 4) {
220562306a36Sopenharmony_ci			cluster = entry;
220662306a36Sopenharmony_ci			count = 0;
220762306a36Sopenharmony_ci		}
220862306a36Sopenharmony_ci		released++;
220962306a36Sopenharmony_ci		entry = RX_DESC_ENTRY(ring, entry + 1);
221062306a36Sopenharmony_ci	}
221162306a36Sopenharmony_ci	cp->rx_old[ring] = entry;
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	if (cluster < 0)
221462306a36Sopenharmony_ci		return 0;
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	if (ring == 0)
221762306a36Sopenharmony_ci		writel(cluster, cp->regs + REG_RX_KICK);
221862306a36Sopenharmony_ci	else if ((N_RX_DESC_RINGS > 1) &&
221962306a36Sopenharmony_ci		 (cp->cas_flags & CAS_FLAG_REG_PLUS))
222062306a36Sopenharmony_ci		writel(cluster, cp->regs + REG_PLUS_RX_KICK1);
222162306a36Sopenharmony_ci	return 0;
222262306a36Sopenharmony_ci}
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci/* process a completion ring. packets are set up in three basic ways:
222662306a36Sopenharmony_ci * small packets: should be copied header + data in single buffer.
222762306a36Sopenharmony_ci * large packets: header and data in a single buffer.
222862306a36Sopenharmony_ci * split packets: header in a separate buffer from data.
222962306a36Sopenharmony_ci *                data may be in multiple pages. data may be > 256
223062306a36Sopenharmony_ci *                bytes but in a single page.
223162306a36Sopenharmony_ci *
223262306a36Sopenharmony_ci * NOTE: RX page posting is done in this routine as well. while there's
223362306a36Sopenharmony_ci *       the capability of using multiple RX completion rings, it isn't
223462306a36Sopenharmony_ci *       really worthwhile due to the fact that the page posting will
223562306a36Sopenharmony_ci *       force serialization on the single descriptor ring.
223662306a36Sopenharmony_ci */
223762306a36Sopenharmony_cistatic int cas_rx_ringN(struct cas *cp, int ring, int budget)
223862306a36Sopenharmony_ci{
223962306a36Sopenharmony_ci	struct cas_rx_comp *rxcs = cp->init_rxcs[ring];
224062306a36Sopenharmony_ci	int entry, drops;
224162306a36Sopenharmony_ci	int npackets = 0;
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	netif_printk(cp, intr, KERN_DEBUG, cp->dev,
224462306a36Sopenharmony_ci		     "rx[%d] interrupt, done: %d/%d\n",
224562306a36Sopenharmony_ci		     ring,
224662306a36Sopenharmony_ci		     readl(cp->regs + REG_RX_COMP_HEAD), cp->rx_new[ring]);
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	entry = cp->rx_new[ring];
224962306a36Sopenharmony_ci	drops = 0;
225062306a36Sopenharmony_ci	while (1) {
225162306a36Sopenharmony_ci		struct cas_rx_comp *rxc = rxcs + entry;
225262306a36Sopenharmony_ci		struct sk_buff *skb;
225362306a36Sopenharmony_ci		int type, len;
225462306a36Sopenharmony_ci		u64 words[4];
225562306a36Sopenharmony_ci		int i, dring;
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci		words[0] = le64_to_cpu(rxc->word1);
225862306a36Sopenharmony_ci		words[1] = le64_to_cpu(rxc->word2);
225962306a36Sopenharmony_ci		words[2] = le64_to_cpu(rxc->word3);
226062306a36Sopenharmony_ci		words[3] = le64_to_cpu(rxc->word4);
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci		/* don't touch if still owned by hw */
226362306a36Sopenharmony_ci		type = CAS_VAL(RX_COMP1_TYPE, words[0]);
226462306a36Sopenharmony_ci		if (type == 0)
226562306a36Sopenharmony_ci			break;
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci		/* hw hasn't cleared the zero bit yet */
226862306a36Sopenharmony_ci		if (words[3] & RX_COMP4_ZERO) {
226962306a36Sopenharmony_ci			break;
227062306a36Sopenharmony_ci		}
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci		/* get info on the packet */
227362306a36Sopenharmony_ci		if (words[3] & (RX_COMP4_LEN_MISMATCH | RX_COMP4_BAD)) {
227462306a36Sopenharmony_ci			spin_lock(&cp->stat_lock[ring]);
227562306a36Sopenharmony_ci			cp->net_stats[ring].rx_errors++;
227662306a36Sopenharmony_ci			if (words[3] & RX_COMP4_LEN_MISMATCH)
227762306a36Sopenharmony_ci				cp->net_stats[ring].rx_length_errors++;
227862306a36Sopenharmony_ci			if (words[3] & RX_COMP4_BAD)
227962306a36Sopenharmony_ci				cp->net_stats[ring].rx_crc_errors++;
228062306a36Sopenharmony_ci			spin_unlock(&cp->stat_lock[ring]);
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci			/* We'll just return it to Cassini. */
228362306a36Sopenharmony_ci		drop_it:
228462306a36Sopenharmony_ci			spin_lock(&cp->stat_lock[ring]);
228562306a36Sopenharmony_ci			++cp->net_stats[ring].rx_dropped;
228662306a36Sopenharmony_ci			spin_unlock(&cp->stat_lock[ring]);
228762306a36Sopenharmony_ci			goto next;
228862306a36Sopenharmony_ci		}
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci		len = cas_rx_process_pkt(cp, rxc, entry, words, &skb);
229162306a36Sopenharmony_ci		if (len < 0) {
229262306a36Sopenharmony_ci			++drops;
229362306a36Sopenharmony_ci			goto drop_it;
229462306a36Sopenharmony_ci		}
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci		/* see if it's a flow re-assembly or not. the driver
229762306a36Sopenharmony_ci		 * itself handles release back up.
229862306a36Sopenharmony_ci		 */
229962306a36Sopenharmony_ci		if (RX_DONT_BATCH || (type == 0x2)) {
230062306a36Sopenharmony_ci			/* non-reassm: these always get released */
230162306a36Sopenharmony_ci			cas_skb_release(skb);
230262306a36Sopenharmony_ci		} else {
230362306a36Sopenharmony_ci			cas_rx_flow_pkt(cp, words, skb);
230462306a36Sopenharmony_ci		}
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_ci		spin_lock(&cp->stat_lock[ring]);
230762306a36Sopenharmony_ci		cp->net_stats[ring].rx_packets++;
230862306a36Sopenharmony_ci		cp->net_stats[ring].rx_bytes += len;
230962306a36Sopenharmony_ci		spin_unlock(&cp->stat_lock[ring]);
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	next:
231262306a36Sopenharmony_ci		npackets++;
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci		/* should it be released? */
231562306a36Sopenharmony_ci		if (words[0] & RX_COMP1_RELEASE_HDR) {
231662306a36Sopenharmony_ci			i = CAS_VAL(RX_COMP2_HDR_INDEX, words[1]);
231762306a36Sopenharmony_ci			dring = CAS_VAL(RX_INDEX_RING, i);
231862306a36Sopenharmony_ci			i = CAS_VAL(RX_INDEX_NUM, i);
231962306a36Sopenharmony_ci			cas_post_page(cp, dring, i);
232062306a36Sopenharmony_ci		}
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci		if (words[0] & RX_COMP1_RELEASE_DATA) {
232362306a36Sopenharmony_ci			i = CAS_VAL(RX_COMP1_DATA_INDEX, words[0]);
232462306a36Sopenharmony_ci			dring = CAS_VAL(RX_INDEX_RING, i);
232562306a36Sopenharmony_ci			i = CAS_VAL(RX_INDEX_NUM, i);
232662306a36Sopenharmony_ci			cas_post_page(cp, dring, i);
232762306a36Sopenharmony_ci		}
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci		if (words[0] & RX_COMP1_RELEASE_NEXT) {
233062306a36Sopenharmony_ci			i = CAS_VAL(RX_COMP2_NEXT_INDEX, words[1]);
233162306a36Sopenharmony_ci			dring = CAS_VAL(RX_INDEX_RING, i);
233262306a36Sopenharmony_ci			i = CAS_VAL(RX_INDEX_NUM, i);
233362306a36Sopenharmony_ci			cas_post_page(cp, dring, i);
233462306a36Sopenharmony_ci		}
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci		/* skip to the next entry */
233762306a36Sopenharmony_ci		entry = RX_COMP_ENTRY(ring, entry + 1 +
233862306a36Sopenharmony_ci				      CAS_VAL(RX_COMP1_SKIP, words[0]));
233962306a36Sopenharmony_ci#ifdef USE_NAPI
234062306a36Sopenharmony_ci		if (budget && (npackets >= budget))
234162306a36Sopenharmony_ci			break;
234262306a36Sopenharmony_ci#endif
234362306a36Sopenharmony_ci	}
234462306a36Sopenharmony_ci	cp->rx_new[ring] = entry;
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci	if (drops)
234762306a36Sopenharmony_ci		netdev_info(cp->dev, "Memory squeeze, deferring packet\n");
234862306a36Sopenharmony_ci	return npackets;
234962306a36Sopenharmony_ci}
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci/* put completion entries back on the ring */
235362306a36Sopenharmony_cistatic void cas_post_rxcs_ringN(struct net_device *dev,
235462306a36Sopenharmony_ci				struct cas *cp, int ring)
235562306a36Sopenharmony_ci{
235662306a36Sopenharmony_ci	struct cas_rx_comp *rxc = cp->init_rxcs[ring];
235762306a36Sopenharmony_ci	int last, entry;
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	last = cp->rx_cur[ring];
236062306a36Sopenharmony_ci	entry = cp->rx_new[ring];
236162306a36Sopenharmony_ci	netif_printk(cp, intr, KERN_DEBUG, dev,
236262306a36Sopenharmony_ci		     "rxc[%d] interrupt, done: %d/%d\n",
236362306a36Sopenharmony_ci		     ring, readl(cp->regs + REG_RX_COMP_HEAD), entry);
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	/* zero and re-mark descriptors */
236662306a36Sopenharmony_ci	while (last != entry) {
236762306a36Sopenharmony_ci		cas_rxc_init(rxc + last);
236862306a36Sopenharmony_ci		last = RX_COMP_ENTRY(ring, last + 1);
236962306a36Sopenharmony_ci	}
237062306a36Sopenharmony_ci	cp->rx_cur[ring] = last;
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci	if (ring == 0)
237362306a36Sopenharmony_ci		writel(last, cp->regs + REG_RX_COMP_TAIL);
237462306a36Sopenharmony_ci	else if (cp->cas_flags & CAS_FLAG_REG_PLUS)
237562306a36Sopenharmony_ci		writel(last, cp->regs + REG_PLUS_RX_COMPN_TAIL(ring));
237662306a36Sopenharmony_ci}
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci/* cassini can use all four PCI interrupts for the completion ring.
238162306a36Sopenharmony_ci * rings 3 and 4 are identical
238262306a36Sopenharmony_ci */
238362306a36Sopenharmony_ci#if defined(USE_PCI_INTC) || defined(USE_PCI_INTD)
238462306a36Sopenharmony_cistatic inline void cas_handle_irqN(struct net_device *dev,
238562306a36Sopenharmony_ci				   struct cas *cp, const u32 status,
238662306a36Sopenharmony_ci				   const int ring)
238762306a36Sopenharmony_ci{
238862306a36Sopenharmony_ci	if (status & (INTR_RX_COMP_FULL_ALT | INTR_RX_COMP_AF_ALT))
238962306a36Sopenharmony_ci		cas_post_rxcs_ringN(dev, cp, ring);
239062306a36Sopenharmony_ci}
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_cistatic irqreturn_t cas_interruptN(int irq, void *dev_id)
239362306a36Sopenharmony_ci{
239462306a36Sopenharmony_ci	struct net_device *dev = dev_id;
239562306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
239662306a36Sopenharmony_ci	unsigned long flags;
239762306a36Sopenharmony_ci	int ring = (irq == cp->pci_irq_INTC) ? 2 : 3;
239862306a36Sopenharmony_ci	u32 status = readl(cp->regs + REG_PLUS_INTRN_STATUS(ring));
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	/* check for shared irq */
240162306a36Sopenharmony_ci	if (status == 0)
240262306a36Sopenharmony_ci		return IRQ_NONE;
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_ci	spin_lock_irqsave(&cp->lock, flags);
240562306a36Sopenharmony_ci	if (status & INTR_RX_DONE_ALT) { /* handle rx separately */
240662306a36Sopenharmony_ci#ifdef USE_NAPI
240762306a36Sopenharmony_ci		cas_mask_intr(cp);
240862306a36Sopenharmony_ci		napi_schedule(&cp->napi);
240962306a36Sopenharmony_ci#else
241062306a36Sopenharmony_ci		cas_rx_ringN(cp, ring, 0);
241162306a36Sopenharmony_ci#endif
241262306a36Sopenharmony_ci		status &= ~INTR_RX_DONE_ALT;
241362306a36Sopenharmony_ci	}
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	if (status)
241662306a36Sopenharmony_ci		cas_handle_irqN(dev, cp, status, ring);
241762306a36Sopenharmony_ci	spin_unlock_irqrestore(&cp->lock, flags);
241862306a36Sopenharmony_ci	return IRQ_HANDLED;
241962306a36Sopenharmony_ci}
242062306a36Sopenharmony_ci#endif
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci#ifdef USE_PCI_INTB
242362306a36Sopenharmony_ci/* everything but rx packets */
242462306a36Sopenharmony_cistatic inline void cas_handle_irq1(struct cas *cp, const u32 status)
242562306a36Sopenharmony_ci{
242662306a36Sopenharmony_ci	if (status & INTR_RX_BUF_UNAVAIL_1) {
242762306a36Sopenharmony_ci		/* Frame arrived, no free RX buffers available.
242862306a36Sopenharmony_ci		 * NOTE: we can get this on a link transition. */
242962306a36Sopenharmony_ci		cas_post_rxds_ringN(cp, 1, 0);
243062306a36Sopenharmony_ci		spin_lock(&cp->stat_lock[1]);
243162306a36Sopenharmony_ci		cp->net_stats[1].rx_dropped++;
243262306a36Sopenharmony_ci		spin_unlock(&cp->stat_lock[1]);
243362306a36Sopenharmony_ci	}
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	if (status & INTR_RX_BUF_AE_1)
243662306a36Sopenharmony_ci		cas_post_rxds_ringN(cp, 1, RX_DESC_RINGN_SIZE(1) -
243762306a36Sopenharmony_ci				    RX_AE_FREEN_VAL(1));
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci	if (status & (INTR_RX_COMP_AF | INTR_RX_COMP_FULL))
244062306a36Sopenharmony_ci		cas_post_rxcs_ringN(cp, 1);
244162306a36Sopenharmony_ci}
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci/* ring 2 handles a few more events than 3 and 4 */
244462306a36Sopenharmony_cistatic irqreturn_t cas_interrupt1(int irq, void *dev_id)
244562306a36Sopenharmony_ci{
244662306a36Sopenharmony_ci	struct net_device *dev = dev_id;
244762306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
244862306a36Sopenharmony_ci	unsigned long flags;
244962306a36Sopenharmony_ci	u32 status = readl(cp->regs + REG_PLUS_INTRN_STATUS(1));
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	/* check for shared interrupt */
245262306a36Sopenharmony_ci	if (status == 0)
245362306a36Sopenharmony_ci		return IRQ_NONE;
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	spin_lock_irqsave(&cp->lock, flags);
245662306a36Sopenharmony_ci	if (status & INTR_RX_DONE_ALT) { /* handle rx separately */
245762306a36Sopenharmony_ci#ifdef USE_NAPI
245862306a36Sopenharmony_ci		cas_mask_intr(cp);
245962306a36Sopenharmony_ci		napi_schedule(&cp->napi);
246062306a36Sopenharmony_ci#else
246162306a36Sopenharmony_ci		cas_rx_ringN(cp, 1, 0);
246262306a36Sopenharmony_ci#endif
246362306a36Sopenharmony_ci		status &= ~INTR_RX_DONE_ALT;
246462306a36Sopenharmony_ci	}
246562306a36Sopenharmony_ci	if (status)
246662306a36Sopenharmony_ci		cas_handle_irq1(cp, status);
246762306a36Sopenharmony_ci	spin_unlock_irqrestore(&cp->lock, flags);
246862306a36Sopenharmony_ci	return IRQ_HANDLED;
246962306a36Sopenharmony_ci}
247062306a36Sopenharmony_ci#endif
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_cistatic inline void cas_handle_irq(struct net_device *dev,
247362306a36Sopenharmony_ci				  struct cas *cp, const u32 status)
247462306a36Sopenharmony_ci{
247562306a36Sopenharmony_ci	/* housekeeping interrupts */
247662306a36Sopenharmony_ci	if (status & INTR_ERROR_MASK)
247762306a36Sopenharmony_ci		cas_abnormal_irq(dev, cp, status);
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	if (status & INTR_RX_BUF_UNAVAIL) {
248062306a36Sopenharmony_ci		/* Frame arrived, no free RX buffers available.
248162306a36Sopenharmony_ci		 * NOTE: we can get this on a link transition.
248262306a36Sopenharmony_ci		 */
248362306a36Sopenharmony_ci		cas_post_rxds_ringN(cp, 0, 0);
248462306a36Sopenharmony_ci		spin_lock(&cp->stat_lock[0]);
248562306a36Sopenharmony_ci		cp->net_stats[0].rx_dropped++;
248662306a36Sopenharmony_ci		spin_unlock(&cp->stat_lock[0]);
248762306a36Sopenharmony_ci	} else if (status & INTR_RX_BUF_AE) {
248862306a36Sopenharmony_ci		cas_post_rxds_ringN(cp, 0, RX_DESC_RINGN_SIZE(0) -
248962306a36Sopenharmony_ci				    RX_AE_FREEN_VAL(0));
249062306a36Sopenharmony_ci	}
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci	if (status & (INTR_RX_COMP_AF | INTR_RX_COMP_FULL))
249362306a36Sopenharmony_ci		cas_post_rxcs_ringN(dev, cp, 0);
249462306a36Sopenharmony_ci}
249562306a36Sopenharmony_ci
249662306a36Sopenharmony_cistatic irqreturn_t cas_interrupt(int irq, void *dev_id)
249762306a36Sopenharmony_ci{
249862306a36Sopenharmony_ci	struct net_device *dev = dev_id;
249962306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
250062306a36Sopenharmony_ci	unsigned long flags;
250162306a36Sopenharmony_ci	u32 status = readl(cp->regs + REG_INTR_STATUS);
250262306a36Sopenharmony_ci
250362306a36Sopenharmony_ci	if (status == 0)
250462306a36Sopenharmony_ci		return IRQ_NONE;
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	spin_lock_irqsave(&cp->lock, flags);
250762306a36Sopenharmony_ci	if (status & (INTR_TX_ALL | INTR_TX_INTME)) {
250862306a36Sopenharmony_ci		cas_tx(dev, cp, status);
250962306a36Sopenharmony_ci		status &= ~(INTR_TX_ALL | INTR_TX_INTME);
251062306a36Sopenharmony_ci	}
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_ci	if (status & INTR_RX_DONE) {
251362306a36Sopenharmony_ci#ifdef USE_NAPI
251462306a36Sopenharmony_ci		cas_mask_intr(cp);
251562306a36Sopenharmony_ci		napi_schedule(&cp->napi);
251662306a36Sopenharmony_ci#else
251762306a36Sopenharmony_ci		cas_rx_ringN(cp, 0, 0);
251862306a36Sopenharmony_ci#endif
251962306a36Sopenharmony_ci		status &= ~INTR_RX_DONE;
252062306a36Sopenharmony_ci	}
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	if (status)
252362306a36Sopenharmony_ci		cas_handle_irq(dev, cp, status);
252462306a36Sopenharmony_ci	spin_unlock_irqrestore(&cp->lock, flags);
252562306a36Sopenharmony_ci	return IRQ_HANDLED;
252662306a36Sopenharmony_ci}
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci#ifdef USE_NAPI
253062306a36Sopenharmony_cistatic int cas_poll(struct napi_struct *napi, int budget)
253162306a36Sopenharmony_ci{
253262306a36Sopenharmony_ci	struct cas *cp = container_of(napi, struct cas, napi);
253362306a36Sopenharmony_ci	struct net_device *dev = cp->dev;
253462306a36Sopenharmony_ci	int i, enable_intr, credits;
253562306a36Sopenharmony_ci	u32 status = readl(cp->regs + REG_INTR_STATUS);
253662306a36Sopenharmony_ci	unsigned long flags;
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	spin_lock_irqsave(&cp->lock, flags);
253962306a36Sopenharmony_ci	cas_tx(dev, cp, status);
254062306a36Sopenharmony_ci	spin_unlock_irqrestore(&cp->lock, flags);
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	/* NAPI rx packets. we spread the credits across all of the
254362306a36Sopenharmony_ci	 * rxc rings
254462306a36Sopenharmony_ci	 *
254562306a36Sopenharmony_ci	 * to make sure we're fair with the work we loop through each
254662306a36Sopenharmony_ci	 * ring N_RX_COMP_RING times with a request of
254762306a36Sopenharmony_ci	 * budget / N_RX_COMP_RINGS
254862306a36Sopenharmony_ci	 */
254962306a36Sopenharmony_ci	enable_intr = 1;
255062306a36Sopenharmony_ci	credits = 0;
255162306a36Sopenharmony_ci	for (i = 0; i < N_RX_COMP_RINGS; i++) {
255262306a36Sopenharmony_ci		int j;
255362306a36Sopenharmony_ci		for (j = 0; j < N_RX_COMP_RINGS; j++) {
255462306a36Sopenharmony_ci			credits += cas_rx_ringN(cp, j, budget / N_RX_COMP_RINGS);
255562306a36Sopenharmony_ci			if (credits >= budget) {
255662306a36Sopenharmony_ci				enable_intr = 0;
255762306a36Sopenharmony_ci				goto rx_comp;
255862306a36Sopenharmony_ci			}
255962306a36Sopenharmony_ci		}
256062306a36Sopenharmony_ci	}
256162306a36Sopenharmony_ci
256262306a36Sopenharmony_cirx_comp:
256362306a36Sopenharmony_ci	/* final rx completion */
256462306a36Sopenharmony_ci	spin_lock_irqsave(&cp->lock, flags);
256562306a36Sopenharmony_ci	if (status)
256662306a36Sopenharmony_ci		cas_handle_irq(dev, cp, status);
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ci#ifdef USE_PCI_INTB
256962306a36Sopenharmony_ci	if (N_RX_COMP_RINGS > 1) {
257062306a36Sopenharmony_ci		status = readl(cp->regs + REG_PLUS_INTRN_STATUS(1));
257162306a36Sopenharmony_ci		if (status)
257262306a36Sopenharmony_ci			cas_handle_irq1(dev, cp, status);
257362306a36Sopenharmony_ci	}
257462306a36Sopenharmony_ci#endif
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci#ifdef USE_PCI_INTC
257762306a36Sopenharmony_ci	if (N_RX_COMP_RINGS > 2) {
257862306a36Sopenharmony_ci		status = readl(cp->regs + REG_PLUS_INTRN_STATUS(2));
257962306a36Sopenharmony_ci		if (status)
258062306a36Sopenharmony_ci			cas_handle_irqN(dev, cp, status, 2);
258162306a36Sopenharmony_ci	}
258262306a36Sopenharmony_ci#endif
258362306a36Sopenharmony_ci
258462306a36Sopenharmony_ci#ifdef USE_PCI_INTD
258562306a36Sopenharmony_ci	if (N_RX_COMP_RINGS > 3) {
258662306a36Sopenharmony_ci		status = readl(cp->regs + REG_PLUS_INTRN_STATUS(3));
258762306a36Sopenharmony_ci		if (status)
258862306a36Sopenharmony_ci			cas_handle_irqN(dev, cp, status, 3);
258962306a36Sopenharmony_ci	}
259062306a36Sopenharmony_ci#endif
259162306a36Sopenharmony_ci	spin_unlock_irqrestore(&cp->lock, flags);
259262306a36Sopenharmony_ci	if (enable_intr) {
259362306a36Sopenharmony_ci		napi_complete(napi);
259462306a36Sopenharmony_ci		cas_unmask_intr(cp);
259562306a36Sopenharmony_ci	}
259662306a36Sopenharmony_ci	return credits;
259762306a36Sopenharmony_ci}
259862306a36Sopenharmony_ci#endif
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
260162306a36Sopenharmony_cistatic void cas_netpoll(struct net_device *dev)
260262306a36Sopenharmony_ci{
260362306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_ci	cas_disable_irq(cp, 0);
260662306a36Sopenharmony_ci	cas_interrupt(cp->pdev->irq, dev);
260762306a36Sopenharmony_ci	cas_enable_irq(cp, 0);
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_ci#ifdef USE_PCI_INTB
261062306a36Sopenharmony_ci	if (N_RX_COMP_RINGS > 1) {
261162306a36Sopenharmony_ci		/* cas_interrupt1(); */
261262306a36Sopenharmony_ci	}
261362306a36Sopenharmony_ci#endif
261462306a36Sopenharmony_ci#ifdef USE_PCI_INTC
261562306a36Sopenharmony_ci	if (N_RX_COMP_RINGS > 2) {
261662306a36Sopenharmony_ci		/* cas_interruptN(); */
261762306a36Sopenharmony_ci	}
261862306a36Sopenharmony_ci#endif
261962306a36Sopenharmony_ci#ifdef USE_PCI_INTD
262062306a36Sopenharmony_ci	if (N_RX_COMP_RINGS > 3) {
262162306a36Sopenharmony_ci		/* cas_interruptN(); */
262262306a36Sopenharmony_ci	}
262362306a36Sopenharmony_ci#endif
262462306a36Sopenharmony_ci}
262562306a36Sopenharmony_ci#endif
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_cistatic void cas_tx_timeout(struct net_device *dev, unsigned int txqueue)
262862306a36Sopenharmony_ci{
262962306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci	netdev_err(dev, "transmit timed out, resetting\n");
263262306a36Sopenharmony_ci	if (!cp->hw_running) {
263362306a36Sopenharmony_ci		netdev_err(dev, "hrm.. hw not running!\n");
263462306a36Sopenharmony_ci		return;
263562306a36Sopenharmony_ci	}
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci	netdev_err(dev, "MIF_STATE[%08x]\n",
263862306a36Sopenharmony_ci		   readl(cp->regs + REG_MIF_STATE_MACHINE));
263962306a36Sopenharmony_ci
264062306a36Sopenharmony_ci	netdev_err(dev, "MAC_STATE[%08x]\n",
264162306a36Sopenharmony_ci		   readl(cp->regs + REG_MAC_STATE_MACHINE));
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_ci	netdev_err(dev, "TX_STATE[%08x:%08x:%08x] FIFO[%08x:%08x:%08x] SM1[%08x] SM2[%08x]\n",
264462306a36Sopenharmony_ci		   readl(cp->regs + REG_TX_CFG),
264562306a36Sopenharmony_ci		   readl(cp->regs + REG_MAC_TX_STATUS),
264662306a36Sopenharmony_ci		   readl(cp->regs + REG_MAC_TX_CFG),
264762306a36Sopenharmony_ci		   readl(cp->regs + REG_TX_FIFO_PKT_CNT),
264862306a36Sopenharmony_ci		   readl(cp->regs + REG_TX_FIFO_WRITE_PTR),
264962306a36Sopenharmony_ci		   readl(cp->regs + REG_TX_FIFO_READ_PTR),
265062306a36Sopenharmony_ci		   readl(cp->regs + REG_TX_SM_1),
265162306a36Sopenharmony_ci		   readl(cp->regs + REG_TX_SM_2));
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_ci	netdev_err(dev, "RX_STATE[%08x:%08x:%08x]\n",
265462306a36Sopenharmony_ci		   readl(cp->regs + REG_RX_CFG),
265562306a36Sopenharmony_ci		   readl(cp->regs + REG_MAC_RX_STATUS),
265662306a36Sopenharmony_ci		   readl(cp->regs + REG_MAC_RX_CFG));
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci	netdev_err(dev, "HP_STATE[%08x:%08x:%08x:%08x]\n",
265962306a36Sopenharmony_ci		   readl(cp->regs + REG_HP_STATE_MACHINE),
266062306a36Sopenharmony_ci		   readl(cp->regs + REG_HP_STATUS0),
266162306a36Sopenharmony_ci		   readl(cp->regs + REG_HP_STATUS1),
266262306a36Sopenharmony_ci		   readl(cp->regs + REG_HP_STATUS2));
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_ci#if 1
266562306a36Sopenharmony_ci	atomic_inc(&cp->reset_task_pending);
266662306a36Sopenharmony_ci	atomic_inc(&cp->reset_task_pending_all);
266762306a36Sopenharmony_ci	schedule_work(&cp->reset_task);
266862306a36Sopenharmony_ci#else
266962306a36Sopenharmony_ci	atomic_set(&cp->reset_task_pending, CAS_RESET_ALL);
267062306a36Sopenharmony_ci	schedule_work(&cp->reset_task);
267162306a36Sopenharmony_ci#endif
267262306a36Sopenharmony_ci}
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_cistatic inline int cas_intme(int ring, int entry)
267562306a36Sopenharmony_ci{
267662306a36Sopenharmony_ci	/* Algorithm: IRQ every 1/2 of descriptors. */
267762306a36Sopenharmony_ci	if (!(entry & ((TX_DESC_RINGN_SIZE(ring) >> 1) - 1)))
267862306a36Sopenharmony_ci		return 1;
267962306a36Sopenharmony_ci	return 0;
268062306a36Sopenharmony_ci}
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci
268362306a36Sopenharmony_cistatic void cas_write_txd(struct cas *cp, int ring, int entry,
268462306a36Sopenharmony_ci			  dma_addr_t mapping, int len, u64 ctrl, int last)
268562306a36Sopenharmony_ci{
268662306a36Sopenharmony_ci	struct cas_tx_desc *txd = cp->init_txds[ring] + entry;
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_ci	ctrl |= CAS_BASE(TX_DESC_BUFLEN, len);
268962306a36Sopenharmony_ci	if (cas_intme(ring, entry))
269062306a36Sopenharmony_ci		ctrl |= TX_DESC_INTME;
269162306a36Sopenharmony_ci	if (last)
269262306a36Sopenharmony_ci		ctrl |= TX_DESC_EOF;
269362306a36Sopenharmony_ci	txd->control = cpu_to_le64(ctrl);
269462306a36Sopenharmony_ci	txd->buffer = cpu_to_le64(mapping);
269562306a36Sopenharmony_ci}
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_cistatic inline void *tx_tiny_buf(struct cas *cp, const int ring,
269862306a36Sopenharmony_ci				const int entry)
269962306a36Sopenharmony_ci{
270062306a36Sopenharmony_ci	return cp->tx_tiny_bufs[ring] + TX_TINY_BUF_LEN*entry;
270162306a36Sopenharmony_ci}
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_cistatic inline dma_addr_t tx_tiny_map(struct cas *cp, const int ring,
270462306a36Sopenharmony_ci				     const int entry, const int tentry)
270562306a36Sopenharmony_ci{
270662306a36Sopenharmony_ci	cp->tx_tiny_use[ring][tentry].nbufs++;
270762306a36Sopenharmony_ci	cp->tx_tiny_use[ring][entry].used = 1;
270862306a36Sopenharmony_ci	return cp->tx_tiny_dvma[ring] + TX_TINY_BUF_LEN*entry;
270962306a36Sopenharmony_ci}
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_cistatic inline int cas_xmit_tx_ringN(struct cas *cp, int ring,
271262306a36Sopenharmony_ci				    struct sk_buff *skb)
271362306a36Sopenharmony_ci{
271462306a36Sopenharmony_ci	struct net_device *dev = cp->dev;
271562306a36Sopenharmony_ci	int entry, nr_frags, frag, tabort, tentry;
271662306a36Sopenharmony_ci	dma_addr_t mapping;
271762306a36Sopenharmony_ci	unsigned long flags;
271862306a36Sopenharmony_ci	u64 ctrl;
271962306a36Sopenharmony_ci	u32 len;
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci	spin_lock_irqsave(&cp->tx_lock[ring], flags);
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_ci	/* This is a hard error, log it. */
272462306a36Sopenharmony_ci	if (TX_BUFFS_AVAIL(cp, ring) <=
272562306a36Sopenharmony_ci	    CAS_TABORT(cp)*(skb_shinfo(skb)->nr_frags + 1)) {
272662306a36Sopenharmony_ci		netif_stop_queue(dev);
272762306a36Sopenharmony_ci		spin_unlock_irqrestore(&cp->tx_lock[ring], flags);
272862306a36Sopenharmony_ci		netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
272962306a36Sopenharmony_ci		return 1;
273062306a36Sopenharmony_ci	}
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_ci	ctrl = 0;
273362306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL) {
273462306a36Sopenharmony_ci		const u64 csum_start_off = skb_checksum_start_offset(skb);
273562306a36Sopenharmony_ci		const u64 csum_stuff_off = csum_start_off + skb->csum_offset;
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci		ctrl =  TX_DESC_CSUM_EN |
273862306a36Sopenharmony_ci			CAS_BASE(TX_DESC_CSUM_START, csum_start_off) |
273962306a36Sopenharmony_ci			CAS_BASE(TX_DESC_CSUM_STUFF, csum_stuff_off);
274062306a36Sopenharmony_ci	}
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci	entry = cp->tx_new[ring];
274362306a36Sopenharmony_ci	cp->tx_skbs[ring][entry] = skb;
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	nr_frags = skb_shinfo(skb)->nr_frags;
274662306a36Sopenharmony_ci	len = skb_headlen(skb);
274762306a36Sopenharmony_ci	mapping = dma_map_page(&cp->pdev->dev, virt_to_page(skb->data),
274862306a36Sopenharmony_ci			       offset_in_page(skb->data), len, DMA_TO_DEVICE);
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci	tentry = entry;
275162306a36Sopenharmony_ci	tabort = cas_calc_tabort(cp, (unsigned long) skb->data, len);
275262306a36Sopenharmony_ci	if (unlikely(tabort)) {
275362306a36Sopenharmony_ci		/* NOTE: len is always >  tabort */
275462306a36Sopenharmony_ci		cas_write_txd(cp, ring, entry, mapping, len - tabort,
275562306a36Sopenharmony_ci			      ctrl | TX_DESC_SOF, 0);
275662306a36Sopenharmony_ci		entry = TX_DESC_NEXT(ring, entry);
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci		skb_copy_from_linear_data_offset(skb, len - tabort,
275962306a36Sopenharmony_ci			      tx_tiny_buf(cp, ring, entry), tabort);
276062306a36Sopenharmony_ci		mapping = tx_tiny_map(cp, ring, entry, tentry);
276162306a36Sopenharmony_ci		cas_write_txd(cp, ring, entry, mapping, tabort, ctrl,
276262306a36Sopenharmony_ci			      (nr_frags == 0));
276362306a36Sopenharmony_ci	} else {
276462306a36Sopenharmony_ci		cas_write_txd(cp, ring, entry, mapping, len, ctrl |
276562306a36Sopenharmony_ci			      TX_DESC_SOF, (nr_frags == 0));
276662306a36Sopenharmony_ci	}
276762306a36Sopenharmony_ci	entry = TX_DESC_NEXT(ring, entry);
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci	for (frag = 0; frag < nr_frags; frag++) {
277062306a36Sopenharmony_ci		const skb_frag_t *fragp = &skb_shinfo(skb)->frags[frag];
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci		len = skb_frag_size(fragp);
277362306a36Sopenharmony_ci		mapping = skb_frag_dma_map(&cp->pdev->dev, fragp, 0, len,
277462306a36Sopenharmony_ci					   DMA_TO_DEVICE);
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci		tabort = cas_calc_tabort(cp, skb_frag_off(fragp), len);
277762306a36Sopenharmony_ci		if (unlikely(tabort)) {
277862306a36Sopenharmony_ci			/* NOTE: len is always > tabort */
277962306a36Sopenharmony_ci			cas_write_txd(cp, ring, entry, mapping, len - tabort,
278062306a36Sopenharmony_ci				      ctrl, 0);
278162306a36Sopenharmony_ci			entry = TX_DESC_NEXT(ring, entry);
278262306a36Sopenharmony_ci			memcpy_from_page(tx_tiny_buf(cp, ring, entry),
278362306a36Sopenharmony_ci					 skb_frag_page(fragp),
278462306a36Sopenharmony_ci					 skb_frag_off(fragp) + len - tabort,
278562306a36Sopenharmony_ci					 tabort);
278662306a36Sopenharmony_ci			mapping = tx_tiny_map(cp, ring, entry, tentry);
278762306a36Sopenharmony_ci			len     = tabort;
278862306a36Sopenharmony_ci		}
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci		cas_write_txd(cp, ring, entry, mapping, len, ctrl,
279162306a36Sopenharmony_ci			      (frag + 1 == nr_frags));
279262306a36Sopenharmony_ci		entry = TX_DESC_NEXT(ring, entry);
279362306a36Sopenharmony_ci	}
279462306a36Sopenharmony_ci
279562306a36Sopenharmony_ci	cp->tx_new[ring] = entry;
279662306a36Sopenharmony_ci	if (TX_BUFFS_AVAIL(cp, ring) <= CAS_TABORT(cp)*(MAX_SKB_FRAGS + 1))
279762306a36Sopenharmony_ci		netif_stop_queue(dev);
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci	netif_printk(cp, tx_queued, KERN_DEBUG, dev,
280062306a36Sopenharmony_ci		     "tx[%d] queued, slot %d, skblen %d, avail %d\n",
280162306a36Sopenharmony_ci		     ring, entry, skb->len, TX_BUFFS_AVAIL(cp, ring));
280262306a36Sopenharmony_ci	writel(entry, cp->regs + REG_TX_KICKN(ring));
280362306a36Sopenharmony_ci	spin_unlock_irqrestore(&cp->tx_lock[ring], flags);
280462306a36Sopenharmony_ci	return 0;
280562306a36Sopenharmony_ci}
280662306a36Sopenharmony_ci
280762306a36Sopenharmony_cistatic netdev_tx_t cas_start_xmit(struct sk_buff *skb, struct net_device *dev)
280862306a36Sopenharmony_ci{
280962306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_ci	/* this is only used as a load-balancing hint, so it doesn't
281262306a36Sopenharmony_ci	 * need to be SMP safe
281362306a36Sopenharmony_ci	 */
281462306a36Sopenharmony_ci	static int ring;
281562306a36Sopenharmony_ci
281662306a36Sopenharmony_ci	if (skb_padto(skb, cp->min_frame_size))
281762306a36Sopenharmony_ci		return NETDEV_TX_OK;
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_ci	/* XXX: we need some higher-level QoS hooks to steer packets to
282062306a36Sopenharmony_ci	 *      individual queues.
282162306a36Sopenharmony_ci	 */
282262306a36Sopenharmony_ci	if (cas_xmit_tx_ringN(cp, ring++ & N_TX_RINGS_MASK, skb))
282362306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
282462306a36Sopenharmony_ci	return NETDEV_TX_OK;
282562306a36Sopenharmony_ci}
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_cistatic void cas_init_tx_dma(struct cas *cp)
282862306a36Sopenharmony_ci{
282962306a36Sopenharmony_ci	u64 desc_dma = cp->block_dvma;
283062306a36Sopenharmony_ci	unsigned long off;
283162306a36Sopenharmony_ci	u32 val;
283262306a36Sopenharmony_ci	int i;
283362306a36Sopenharmony_ci
283462306a36Sopenharmony_ci	/* set up tx completion writeback registers. must be 8-byte aligned */
283562306a36Sopenharmony_ci#ifdef USE_TX_COMPWB
283662306a36Sopenharmony_ci	off = offsetof(struct cas_init_block, tx_compwb);
283762306a36Sopenharmony_ci	writel((desc_dma + off) >> 32, cp->regs + REG_TX_COMPWB_DB_HI);
283862306a36Sopenharmony_ci	writel((desc_dma + off) & 0xffffffff, cp->regs + REG_TX_COMPWB_DB_LOW);
283962306a36Sopenharmony_ci#endif
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	/* enable completion writebacks, enable paced mode,
284262306a36Sopenharmony_ci	 * disable read pipe, and disable pre-interrupt compwbs
284362306a36Sopenharmony_ci	 */
284462306a36Sopenharmony_ci	val =   TX_CFG_COMPWB_Q1 | TX_CFG_COMPWB_Q2 |
284562306a36Sopenharmony_ci		TX_CFG_COMPWB_Q3 | TX_CFG_COMPWB_Q4 |
284662306a36Sopenharmony_ci		TX_CFG_DMA_RDPIPE_DIS | TX_CFG_PACED_MODE |
284762306a36Sopenharmony_ci		TX_CFG_INTR_COMPWB_DIS;
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ci	/* write out tx ring info and tx desc bases */
285062306a36Sopenharmony_ci	for (i = 0; i < MAX_TX_RINGS; i++) {
285162306a36Sopenharmony_ci		off = (unsigned long) cp->init_txds[i] -
285262306a36Sopenharmony_ci			(unsigned long) cp->init_block;
285362306a36Sopenharmony_ci
285462306a36Sopenharmony_ci		val |= CAS_TX_RINGN_BASE(i);
285562306a36Sopenharmony_ci		writel((desc_dma + off) >> 32, cp->regs + REG_TX_DBN_HI(i));
285662306a36Sopenharmony_ci		writel((desc_dma + off) & 0xffffffff, cp->regs +
285762306a36Sopenharmony_ci		       REG_TX_DBN_LOW(i));
285862306a36Sopenharmony_ci		/* don't zero out the kick register here as the system
285962306a36Sopenharmony_ci		 * will wedge
286062306a36Sopenharmony_ci		 */
286162306a36Sopenharmony_ci	}
286262306a36Sopenharmony_ci	writel(val, cp->regs + REG_TX_CFG);
286362306a36Sopenharmony_ci
286462306a36Sopenharmony_ci	/* program max burst sizes. these numbers should be different
286562306a36Sopenharmony_ci	 * if doing QoS.
286662306a36Sopenharmony_ci	 */
286762306a36Sopenharmony_ci#ifdef USE_QOS
286862306a36Sopenharmony_ci	writel(0x800, cp->regs + REG_TX_MAXBURST_0);
286962306a36Sopenharmony_ci	writel(0x1600, cp->regs + REG_TX_MAXBURST_1);
287062306a36Sopenharmony_ci	writel(0x2400, cp->regs + REG_TX_MAXBURST_2);
287162306a36Sopenharmony_ci	writel(0x4800, cp->regs + REG_TX_MAXBURST_3);
287262306a36Sopenharmony_ci#else
287362306a36Sopenharmony_ci	writel(0x800, cp->regs + REG_TX_MAXBURST_0);
287462306a36Sopenharmony_ci	writel(0x800, cp->regs + REG_TX_MAXBURST_1);
287562306a36Sopenharmony_ci	writel(0x800, cp->regs + REG_TX_MAXBURST_2);
287662306a36Sopenharmony_ci	writel(0x800, cp->regs + REG_TX_MAXBURST_3);
287762306a36Sopenharmony_ci#endif
287862306a36Sopenharmony_ci}
287962306a36Sopenharmony_ci
288062306a36Sopenharmony_ci/* Must be invoked under cp->lock. */
288162306a36Sopenharmony_cistatic inline void cas_init_dma(struct cas *cp)
288262306a36Sopenharmony_ci{
288362306a36Sopenharmony_ci	cas_init_tx_dma(cp);
288462306a36Sopenharmony_ci	cas_init_rx_dma(cp);
288562306a36Sopenharmony_ci}
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_cistatic void cas_process_mc_list(struct cas *cp)
288862306a36Sopenharmony_ci{
288962306a36Sopenharmony_ci	u16 hash_table[16];
289062306a36Sopenharmony_ci	u32 crc;
289162306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
289262306a36Sopenharmony_ci	int i = 1;
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci	memset(hash_table, 0, sizeof(hash_table));
289562306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, cp->dev) {
289662306a36Sopenharmony_ci		if (i <= CAS_MC_EXACT_MATCH_SIZE) {
289762306a36Sopenharmony_ci			/* use the alternate mac address registers for the
289862306a36Sopenharmony_ci			 * first 15 multicast addresses
289962306a36Sopenharmony_ci			 */
290062306a36Sopenharmony_ci			writel((ha->addr[4] << 8) | ha->addr[5],
290162306a36Sopenharmony_ci			       cp->regs + REG_MAC_ADDRN(i*3 + 0));
290262306a36Sopenharmony_ci			writel((ha->addr[2] << 8) | ha->addr[3],
290362306a36Sopenharmony_ci			       cp->regs + REG_MAC_ADDRN(i*3 + 1));
290462306a36Sopenharmony_ci			writel((ha->addr[0] << 8) | ha->addr[1],
290562306a36Sopenharmony_ci			       cp->regs + REG_MAC_ADDRN(i*3 + 2));
290662306a36Sopenharmony_ci			i++;
290762306a36Sopenharmony_ci		}
290862306a36Sopenharmony_ci		else {
290962306a36Sopenharmony_ci			/* use hw hash table for the next series of
291062306a36Sopenharmony_ci			 * multicast addresses
291162306a36Sopenharmony_ci			 */
291262306a36Sopenharmony_ci			crc = ether_crc_le(ETH_ALEN, ha->addr);
291362306a36Sopenharmony_ci			crc >>= 24;
291462306a36Sopenharmony_ci			hash_table[crc >> 4] |= 1 << (15 - (crc & 0xf));
291562306a36Sopenharmony_ci		}
291662306a36Sopenharmony_ci	}
291762306a36Sopenharmony_ci	for (i = 0; i < 16; i++)
291862306a36Sopenharmony_ci		writel(hash_table[i], cp->regs + REG_MAC_HASH_TABLEN(i));
291962306a36Sopenharmony_ci}
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci/* Must be invoked under cp->lock. */
292262306a36Sopenharmony_cistatic u32 cas_setup_multicast(struct cas *cp)
292362306a36Sopenharmony_ci{
292462306a36Sopenharmony_ci	u32 rxcfg = 0;
292562306a36Sopenharmony_ci	int i;
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci	if (cp->dev->flags & IFF_PROMISC) {
292862306a36Sopenharmony_ci		rxcfg |= MAC_RX_CFG_PROMISC_EN;
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci	} else if (cp->dev->flags & IFF_ALLMULTI) {
293162306a36Sopenharmony_ci	    	for (i=0; i < 16; i++)
293262306a36Sopenharmony_ci			writel(0xFFFF, cp->regs + REG_MAC_HASH_TABLEN(i));
293362306a36Sopenharmony_ci		rxcfg |= MAC_RX_CFG_HASH_FILTER_EN;
293462306a36Sopenharmony_ci
293562306a36Sopenharmony_ci	} else {
293662306a36Sopenharmony_ci		cas_process_mc_list(cp);
293762306a36Sopenharmony_ci		rxcfg |= MAC_RX_CFG_HASH_FILTER_EN;
293862306a36Sopenharmony_ci	}
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	return rxcfg;
294162306a36Sopenharmony_ci}
294262306a36Sopenharmony_ci
294362306a36Sopenharmony_ci/* must be invoked under cp->stat_lock[N_TX_RINGS] */
294462306a36Sopenharmony_cistatic void cas_clear_mac_err(struct cas *cp)
294562306a36Sopenharmony_ci{
294662306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_COLL_NORMAL);
294762306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_COLL_FIRST);
294862306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_COLL_EXCESS);
294962306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_COLL_LATE);
295062306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_TIMER_DEFER);
295162306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_ATTEMPTS_PEAK);
295262306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_RECV_FRAME);
295362306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_LEN_ERR);
295462306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_ALIGN_ERR);
295562306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_FCS_ERR);
295662306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_RX_CODE_ERR);
295762306a36Sopenharmony_ci}
295862306a36Sopenharmony_ci
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_cistatic void cas_mac_reset(struct cas *cp)
296162306a36Sopenharmony_ci{
296262306a36Sopenharmony_ci	int i;
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci	/* do both TX and RX reset */
296562306a36Sopenharmony_ci	writel(0x1, cp->regs + REG_MAC_TX_RESET);
296662306a36Sopenharmony_ci	writel(0x1, cp->regs + REG_MAC_RX_RESET);
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci	/* wait for TX */
296962306a36Sopenharmony_ci	i = STOP_TRIES;
297062306a36Sopenharmony_ci	while (i-- > 0) {
297162306a36Sopenharmony_ci		if (readl(cp->regs + REG_MAC_TX_RESET) == 0)
297262306a36Sopenharmony_ci			break;
297362306a36Sopenharmony_ci		udelay(10);
297462306a36Sopenharmony_ci	}
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci	/* wait for RX */
297762306a36Sopenharmony_ci	i = STOP_TRIES;
297862306a36Sopenharmony_ci	while (i-- > 0) {
297962306a36Sopenharmony_ci		if (readl(cp->regs + REG_MAC_RX_RESET) == 0)
298062306a36Sopenharmony_ci			break;
298162306a36Sopenharmony_ci		udelay(10);
298262306a36Sopenharmony_ci	}
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci	if (readl(cp->regs + REG_MAC_TX_RESET) |
298562306a36Sopenharmony_ci	    readl(cp->regs + REG_MAC_RX_RESET))
298662306a36Sopenharmony_ci		netdev_err(cp->dev, "mac tx[%d]/rx[%d] reset failed [%08x]\n",
298762306a36Sopenharmony_ci			   readl(cp->regs + REG_MAC_TX_RESET),
298862306a36Sopenharmony_ci			   readl(cp->regs + REG_MAC_RX_RESET),
298962306a36Sopenharmony_ci			   readl(cp->regs + REG_MAC_STATE_MACHINE));
299062306a36Sopenharmony_ci}
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci
299362306a36Sopenharmony_ci/* Must be invoked under cp->lock. */
299462306a36Sopenharmony_cistatic void cas_init_mac(struct cas *cp)
299562306a36Sopenharmony_ci{
299662306a36Sopenharmony_ci	const unsigned char *e = &cp->dev->dev_addr[0];
299762306a36Sopenharmony_ci	int i;
299862306a36Sopenharmony_ci	cas_mac_reset(cp);
299962306a36Sopenharmony_ci
300062306a36Sopenharmony_ci	/* setup core arbitration weight register */
300162306a36Sopenharmony_ci	writel(CAWR_RR_DIS, cp->regs + REG_CAWR);
300262306a36Sopenharmony_ci
300362306a36Sopenharmony_ci#if !defined(CONFIG_SPARC64) && !defined(CONFIG_ALPHA)
300462306a36Sopenharmony_ci	/* set the infinite burst register for chips that don't have
300562306a36Sopenharmony_ci	 * pci issues.
300662306a36Sopenharmony_ci	 */
300762306a36Sopenharmony_ci	if ((cp->cas_flags & CAS_FLAG_TARGET_ABORT) == 0)
300862306a36Sopenharmony_ci		writel(INF_BURST_EN, cp->regs + REG_INF_BURST);
300962306a36Sopenharmony_ci#endif
301062306a36Sopenharmony_ci
301162306a36Sopenharmony_ci	writel(0x1BF0, cp->regs + REG_MAC_SEND_PAUSE);
301262306a36Sopenharmony_ci
301362306a36Sopenharmony_ci	writel(0x00, cp->regs + REG_MAC_IPG0);
301462306a36Sopenharmony_ci	writel(0x08, cp->regs + REG_MAC_IPG1);
301562306a36Sopenharmony_ci	writel(0x04, cp->regs + REG_MAC_IPG2);
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_ci	/* change later for 802.3z */
301862306a36Sopenharmony_ci	writel(0x40, cp->regs + REG_MAC_SLOT_TIME);
301962306a36Sopenharmony_ci
302062306a36Sopenharmony_ci	/* min frame + FCS */
302162306a36Sopenharmony_ci	writel(ETH_ZLEN + 4, cp->regs + REG_MAC_FRAMESIZE_MIN);
302262306a36Sopenharmony_ci
302362306a36Sopenharmony_ci	/* Ethernet payload + header + FCS + optional VLAN tag. NOTE: we
302462306a36Sopenharmony_ci	 * specify the maximum frame size to prevent RX tag errors on
302562306a36Sopenharmony_ci	 * oversized frames.
302662306a36Sopenharmony_ci	 */
302762306a36Sopenharmony_ci	writel(CAS_BASE(MAC_FRAMESIZE_MAX_BURST, 0x2000) |
302862306a36Sopenharmony_ci	       CAS_BASE(MAC_FRAMESIZE_MAX_FRAME,
302962306a36Sopenharmony_ci			(CAS_MAX_MTU + ETH_HLEN + 4 + 4)),
303062306a36Sopenharmony_ci	       cp->regs + REG_MAC_FRAMESIZE_MAX);
303162306a36Sopenharmony_ci
303262306a36Sopenharmony_ci	/* NOTE: crc_size is used as a surrogate for half-duplex.
303362306a36Sopenharmony_ci	 * workaround saturn half-duplex issue by increasing preamble
303462306a36Sopenharmony_ci	 * size to 65 bytes.
303562306a36Sopenharmony_ci	 */
303662306a36Sopenharmony_ci	if ((cp->cas_flags & CAS_FLAG_SATURN) && cp->crc_size)
303762306a36Sopenharmony_ci		writel(0x41, cp->regs + REG_MAC_PA_SIZE);
303862306a36Sopenharmony_ci	else
303962306a36Sopenharmony_ci		writel(0x07, cp->regs + REG_MAC_PA_SIZE);
304062306a36Sopenharmony_ci	writel(0x04, cp->regs + REG_MAC_JAM_SIZE);
304162306a36Sopenharmony_ci	writel(0x10, cp->regs + REG_MAC_ATTEMPT_LIMIT);
304262306a36Sopenharmony_ci	writel(0x8808, cp->regs + REG_MAC_CTRL_TYPE);
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_ci	writel((e[5] | (e[4] << 8)) & 0x3ff, cp->regs + REG_MAC_RANDOM_SEED);
304562306a36Sopenharmony_ci
304662306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_ADDR_FILTER0);
304762306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_ADDR_FILTER1);
304862306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_ADDR_FILTER2);
304962306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_ADDR_FILTER2_1_MASK);
305062306a36Sopenharmony_ci	writel(0, cp->regs + REG_MAC_ADDR_FILTER0_MASK);
305162306a36Sopenharmony_ci
305262306a36Sopenharmony_ci	/* setup mac address in perfect filter array */
305362306a36Sopenharmony_ci	for (i = 0; i < 45; i++)
305462306a36Sopenharmony_ci		writel(0x0, cp->regs + REG_MAC_ADDRN(i));
305562306a36Sopenharmony_ci
305662306a36Sopenharmony_ci	writel((e[4] << 8) | e[5], cp->regs + REG_MAC_ADDRN(0));
305762306a36Sopenharmony_ci	writel((e[2] << 8) | e[3], cp->regs + REG_MAC_ADDRN(1));
305862306a36Sopenharmony_ci	writel((e[0] << 8) | e[1], cp->regs + REG_MAC_ADDRN(2));
305962306a36Sopenharmony_ci
306062306a36Sopenharmony_ci	writel(0x0001, cp->regs + REG_MAC_ADDRN(42));
306162306a36Sopenharmony_ci	writel(0xc200, cp->regs + REG_MAC_ADDRN(43));
306262306a36Sopenharmony_ci	writel(0x0180, cp->regs + REG_MAC_ADDRN(44));
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	cp->mac_rx_cfg = cas_setup_multicast(cp);
306562306a36Sopenharmony_ci
306662306a36Sopenharmony_ci	spin_lock(&cp->stat_lock[N_TX_RINGS]);
306762306a36Sopenharmony_ci	cas_clear_mac_err(cp);
306862306a36Sopenharmony_ci	spin_unlock(&cp->stat_lock[N_TX_RINGS]);
306962306a36Sopenharmony_ci
307062306a36Sopenharmony_ci	/* Setup MAC interrupts.  We want to get all of the interesting
307162306a36Sopenharmony_ci	 * counter expiration events, but we do not want to hear about
307262306a36Sopenharmony_ci	 * normal rx/tx as the DMA engine tells us that.
307362306a36Sopenharmony_ci	 */
307462306a36Sopenharmony_ci	writel(MAC_TX_FRAME_XMIT, cp->regs + REG_MAC_TX_MASK);
307562306a36Sopenharmony_ci	writel(MAC_RX_FRAME_RECV, cp->regs + REG_MAC_RX_MASK);
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_ci	/* Don't enable even the PAUSE interrupts for now, we
307862306a36Sopenharmony_ci	 * make no use of those events other than to record them.
307962306a36Sopenharmony_ci	 */
308062306a36Sopenharmony_ci	writel(0xffffffff, cp->regs + REG_MAC_CTRL_MASK);
308162306a36Sopenharmony_ci}
308262306a36Sopenharmony_ci
308362306a36Sopenharmony_ci/* Must be invoked under cp->lock. */
308462306a36Sopenharmony_cistatic void cas_init_pause_thresholds(struct cas *cp)
308562306a36Sopenharmony_ci{
308662306a36Sopenharmony_ci	/* Calculate pause thresholds.  Setting the OFF threshold to the
308762306a36Sopenharmony_ci	 * full RX fifo size effectively disables PAUSE generation
308862306a36Sopenharmony_ci	 */
308962306a36Sopenharmony_ci	if (cp->rx_fifo_size <= (2 * 1024)) {
309062306a36Sopenharmony_ci		cp->rx_pause_off = cp->rx_pause_on = cp->rx_fifo_size;
309162306a36Sopenharmony_ci	} else {
309262306a36Sopenharmony_ci		int max_frame = (cp->dev->mtu + ETH_HLEN + 4 + 4 + 64) & ~63;
309362306a36Sopenharmony_ci		if (max_frame * 3 > cp->rx_fifo_size) {
309462306a36Sopenharmony_ci			cp->rx_pause_off = 7104;
309562306a36Sopenharmony_ci			cp->rx_pause_on  = 960;
309662306a36Sopenharmony_ci		} else {
309762306a36Sopenharmony_ci			int off = (cp->rx_fifo_size - (max_frame * 2));
309862306a36Sopenharmony_ci			int on = off - max_frame;
309962306a36Sopenharmony_ci			cp->rx_pause_off = off;
310062306a36Sopenharmony_ci			cp->rx_pause_on = on;
310162306a36Sopenharmony_ci		}
310262306a36Sopenharmony_ci	}
310362306a36Sopenharmony_ci}
310462306a36Sopenharmony_ci
310562306a36Sopenharmony_cistatic int cas_vpd_match(const void __iomem *p, const char *str)
310662306a36Sopenharmony_ci{
310762306a36Sopenharmony_ci	int len = strlen(str) + 1;
310862306a36Sopenharmony_ci	int i;
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
311162306a36Sopenharmony_ci		if (readb(p + i) != str[i])
311262306a36Sopenharmony_ci			return 0;
311362306a36Sopenharmony_ci	}
311462306a36Sopenharmony_ci	return 1;
311562306a36Sopenharmony_ci}
311662306a36Sopenharmony_ci
311762306a36Sopenharmony_ci
311862306a36Sopenharmony_ci/* get the mac address by reading the vpd information in the rom.
311962306a36Sopenharmony_ci * also get the phy type and determine if there's an entropy generator.
312062306a36Sopenharmony_ci * NOTE: this is a bit convoluted for the following reasons:
312162306a36Sopenharmony_ci *  1) vpd info has order-dependent mac addresses for multinic cards
312262306a36Sopenharmony_ci *  2) the only way to determine the nic order is to use the slot
312362306a36Sopenharmony_ci *     number.
312462306a36Sopenharmony_ci *  3) fiber cards don't have bridges, so their slot numbers don't
312562306a36Sopenharmony_ci *     mean anything.
312662306a36Sopenharmony_ci *  4) we don't actually know we have a fiber card until after
312762306a36Sopenharmony_ci *     the mac addresses are parsed.
312862306a36Sopenharmony_ci */
312962306a36Sopenharmony_cistatic int cas_get_vpd_info(struct cas *cp, unsigned char *dev_addr,
313062306a36Sopenharmony_ci			    const int offset)
313162306a36Sopenharmony_ci{
313262306a36Sopenharmony_ci	void __iomem *p = cp->regs + REG_EXPANSION_ROM_RUN_START;
313362306a36Sopenharmony_ci	void __iomem *base, *kstart;
313462306a36Sopenharmony_ci	int i, len;
313562306a36Sopenharmony_ci	int found = 0;
313662306a36Sopenharmony_ci#define VPD_FOUND_MAC        0x01
313762306a36Sopenharmony_ci#define VPD_FOUND_PHY        0x02
313862306a36Sopenharmony_ci
313962306a36Sopenharmony_ci	int phy_type = CAS_PHY_MII_MDIO0; /* default phy type */
314062306a36Sopenharmony_ci	int mac_off  = 0;
314162306a36Sopenharmony_ci
314262306a36Sopenharmony_ci#if defined(CONFIG_SPARC)
314362306a36Sopenharmony_ci	const unsigned char *addr;
314462306a36Sopenharmony_ci#endif
314562306a36Sopenharmony_ci
314662306a36Sopenharmony_ci	/* give us access to the PROM */
314762306a36Sopenharmony_ci	writel(BIM_LOCAL_DEV_PROM | BIM_LOCAL_DEV_PAD,
314862306a36Sopenharmony_ci	       cp->regs + REG_BIM_LOCAL_DEV_EN);
314962306a36Sopenharmony_ci
315062306a36Sopenharmony_ci	/* check for an expansion rom */
315162306a36Sopenharmony_ci	if (readb(p) != 0x55 || readb(p + 1) != 0xaa)
315262306a36Sopenharmony_ci		goto use_random_mac_addr;
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci	/* search for beginning of vpd */
315562306a36Sopenharmony_ci	base = NULL;
315662306a36Sopenharmony_ci	for (i = 2; i < EXPANSION_ROM_SIZE; i++) {
315762306a36Sopenharmony_ci		/* check for PCIR */
315862306a36Sopenharmony_ci		if ((readb(p + i + 0) == 0x50) &&
315962306a36Sopenharmony_ci		    (readb(p + i + 1) == 0x43) &&
316062306a36Sopenharmony_ci		    (readb(p + i + 2) == 0x49) &&
316162306a36Sopenharmony_ci		    (readb(p + i + 3) == 0x52)) {
316262306a36Sopenharmony_ci			base = p + (readb(p + i + 8) |
316362306a36Sopenharmony_ci				    (readb(p + i + 9) << 8));
316462306a36Sopenharmony_ci			break;
316562306a36Sopenharmony_ci		}
316662306a36Sopenharmony_ci	}
316762306a36Sopenharmony_ci
316862306a36Sopenharmony_ci	if (!base || (readb(base) != 0x82))
316962306a36Sopenharmony_ci		goto use_random_mac_addr;
317062306a36Sopenharmony_ci
317162306a36Sopenharmony_ci	i = (readb(base + 1) | (readb(base + 2) << 8)) + 3;
317262306a36Sopenharmony_ci	while (i < EXPANSION_ROM_SIZE) {
317362306a36Sopenharmony_ci		if (readb(base + i) != 0x90) /* no vpd found */
317462306a36Sopenharmony_ci			goto use_random_mac_addr;
317562306a36Sopenharmony_ci
317662306a36Sopenharmony_ci		/* found a vpd field */
317762306a36Sopenharmony_ci		len = readb(base + i + 1) | (readb(base + i + 2) << 8);
317862306a36Sopenharmony_ci
317962306a36Sopenharmony_ci		/* extract keywords */
318062306a36Sopenharmony_ci		kstart = base + i + 3;
318162306a36Sopenharmony_ci		p = kstart;
318262306a36Sopenharmony_ci		while ((p - kstart) < len) {
318362306a36Sopenharmony_ci			int klen = readb(p + 2);
318462306a36Sopenharmony_ci			int j;
318562306a36Sopenharmony_ci			char type;
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci			p += 3;
318862306a36Sopenharmony_ci
318962306a36Sopenharmony_ci			/* look for the following things:
319062306a36Sopenharmony_ci			 * -- correct length == 29
319162306a36Sopenharmony_ci			 * 3 (type) + 2 (size) +
319262306a36Sopenharmony_ci			 * 18 (strlen("local-mac-address") + 1) +
319362306a36Sopenharmony_ci			 * 6 (mac addr)
319462306a36Sopenharmony_ci			 * -- VPD Instance 'I'
319562306a36Sopenharmony_ci			 * -- VPD Type Bytes 'B'
319662306a36Sopenharmony_ci			 * -- VPD data length == 6
319762306a36Sopenharmony_ci			 * -- property string == local-mac-address
319862306a36Sopenharmony_ci			 *
319962306a36Sopenharmony_ci			 * -- correct length == 24
320062306a36Sopenharmony_ci			 * 3 (type) + 2 (size) +
320162306a36Sopenharmony_ci			 * 12 (strlen("entropy-dev") + 1) +
320262306a36Sopenharmony_ci			 * 7 (strlen("vms110") + 1)
320362306a36Sopenharmony_ci			 * -- VPD Instance 'I'
320462306a36Sopenharmony_ci			 * -- VPD Type String 'B'
320562306a36Sopenharmony_ci			 * -- VPD data length == 7
320662306a36Sopenharmony_ci			 * -- property string == entropy-dev
320762306a36Sopenharmony_ci			 *
320862306a36Sopenharmony_ci			 * -- correct length == 18
320962306a36Sopenharmony_ci			 * 3 (type) + 2 (size) +
321062306a36Sopenharmony_ci			 * 9 (strlen("phy-type") + 1) +
321162306a36Sopenharmony_ci			 * 4 (strlen("pcs") + 1)
321262306a36Sopenharmony_ci			 * -- VPD Instance 'I'
321362306a36Sopenharmony_ci			 * -- VPD Type String 'S'
321462306a36Sopenharmony_ci			 * -- VPD data length == 4
321562306a36Sopenharmony_ci			 * -- property string == phy-type
321662306a36Sopenharmony_ci			 *
321762306a36Sopenharmony_ci			 * -- correct length == 23
321862306a36Sopenharmony_ci			 * 3 (type) + 2 (size) +
321962306a36Sopenharmony_ci			 * 14 (strlen("phy-interface") + 1) +
322062306a36Sopenharmony_ci			 * 4 (strlen("pcs") + 1)
322162306a36Sopenharmony_ci			 * -- VPD Instance 'I'
322262306a36Sopenharmony_ci			 * -- VPD Type String 'S'
322362306a36Sopenharmony_ci			 * -- VPD data length == 4
322462306a36Sopenharmony_ci			 * -- property string == phy-interface
322562306a36Sopenharmony_ci			 */
322662306a36Sopenharmony_ci			if (readb(p) != 'I')
322762306a36Sopenharmony_ci				goto next;
322862306a36Sopenharmony_ci
322962306a36Sopenharmony_ci			/* finally, check string and length */
323062306a36Sopenharmony_ci			type = readb(p + 3);
323162306a36Sopenharmony_ci			if (type == 'B') {
323262306a36Sopenharmony_ci				if ((klen == 29) && readb(p + 4) == 6 &&
323362306a36Sopenharmony_ci				    cas_vpd_match(p + 5,
323462306a36Sopenharmony_ci						  "local-mac-address")) {
323562306a36Sopenharmony_ci					if (mac_off++ > offset)
323662306a36Sopenharmony_ci						goto next;
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_ci					/* set mac address */
323962306a36Sopenharmony_ci					for (j = 0; j < 6; j++)
324062306a36Sopenharmony_ci						dev_addr[j] =
324162306a36Sopenharmony_ci							readb(p + 23 + j);
324262306a36Sopenharmony_ci					goto found_mac;
324362306a36Sopenharmony_ci				}
324462306a36Sopenharmony_ci			}
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_ci			if (type != 'S')
324762306a36Sopenharmony_ci				goto next;
324862306a36Sopenharmony_ci
324962306a36Sopenharmony_ci#ifdef USE_ENTROPY_DEV
325062306a36Sopenharmony_ci			if ((klen == 24) &&
325162306a36Sopenharmony_ci			    cas_vpd_match(p + 5, "entropy-dev") &&
325262306a36Sopenharmony_ci			    cas_vpd_match(p + 17, "vms110")) {
325362306a36Sopenharmony_ci				cp->cas_flags |= CAS_FLAG_ENTROPY_DEV;
325462306a36Sopenharmony_ci				goto next;
325562306a36Sopenharmony_ci			}
325662306a36Sopenharmony_ci#endif
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_ci			if (found & VPD_FOUND_PHY)
325962306a36Sopenharmony_ci				goto next;
326062306a36Sopenharmony_ci
326162306a36Sopenharmony_ci			if ((klen == 18) && readb(p + 4) == 4 &&
326262306a36Sopenharmony_ci			    cas_vpd_match(p + 5, "phy-type")) {
326362306a36Sopenharmony_ci				if (cas_vpd_match(p + 14, "pcs")) {
326462306a36Sopenharmony_ci					phy_type = CAS_PHY_SERDES;
326562306a36Sopenharmony_ci					goto found_phy;
326662306a36Sopenharmony_ci				}
326762306a36Sopenharmony_ci			}
326862306a36Sopenharmony_ci
326962306a36Sopenharmony_ci			if ((klen == 23) && readb(p + 4) == 4 &&
327062306a36Sopenharmony_ci			    cas_vpd_match(p + 5, "phy-interface")) {
327162306a36Sopenharmony_ci				if (cas_vpd_match(p + 19, "pcs")) {
327262306a36Sopenharmony_ci					phy_type = CAS_PHY_SERDES;
327362306a36Sopenharmony_ci					goto found_phy;
327462306a36Sopenharmony_ci				}
327562306a36Sopenharmony_ci			}
327662306a36Sopenharmony_cifound_mac:
327762306a36Sopenharmony_ci			found |= VPD_FOUND_MAC;
327862306a36Sopenharmony_ci			goto next;
327962306a36Sopenharmony_ci
328062306a36Sopenharmony_cifound_phy:
328162306a36Sopenharmony_ci			found |= VPD_FOUND_PHY;
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_cinext:
328462306a36Sopenharmony_ci			p += klen;
328562306a36Sopenharmony_ci		}
328662306a36Sopenharmony_ci		i += len + 3;
328762306a36Sopenharmony_ci	}
328862306a36Sopenharmony_ci
328962306a36Sopenharmony_ciuse_random_mac_addr:
329062306a36Sopenharmony_ci	if (found & VPD_FOUND_MAC)
329162306a36Sopenharmony_ci		goto done;
329262306a36Sopenharmony_ci
329362306a36Sopenharmony_ci#if defined(CONFIG_SPARC)
329462306a36Sopenharmony_ci	addr = of_get_property(cp->of_node, "local-mac-address", NULL);
329562306a36Sopenharmony_ci	if (addr != NULL) {
329662306a36Sopenharmony_ci		memcpy(dev_addr, addr, ETH_ALEN);
329762306a36Sopenharmony_ci		goto done;
329862306a36Sopenharmony_ci	}
329962306a36Sopenharmony_ci#endif
330062306a36Sopenharmony_ci
330162306a36Sopenharmony_ci	/* Sun MAC prefix then 3 random bytes. */
330262306a36Sopenharmony_ci	pr_info("MAC address not found in ROM VPD\n");
330362306a36Sopenharmony_ci	dev_addr[0] = 0x08;
330462306a36Sopenharmony_ci	dev_addr[1] = 0x00;
330562306a36Sopenharmony_ci	dev_addr[2] = 0x20;
330662306a36Sopenharmony_ci	get_random_bytes(dev_addr + 3, 3);
330762306a36Sopenharmony_ci
330862306a36Sopenharmony_cidone:
330962306a36Sopenharmony_ci	writel(0, cp->regs + REG_BIM_LOCAL_DEV_EN);
331062306a36Sopenharmony_ci	return phy_type;
331162306a36Sopenharmony_ci}
331262306a36Sopenharmony_ci
331362306a36Sopenharmony_ci/* check pci invariants */
331462306a36Sopenharmony_cistatic void cas_check_pci_invariants(struct cas *cp)
331562306a36Sopenharmony_ci{
331662306a36Sopenharmony_ci	struct pci_dev *pdev = cp->pdev;
331762306a36Sopenharmony_ci
331862306a36Sopenharmony_ci	cp->cas_flags = 0;
331962306a36Sopenharmony_ci	if ((pdev->vendor == PCI_VENDOR_ID_SUN) &&
332062306a36Sopenharmony_ci	    (pdev->device == PCI_DEVICE_ID_SUN_CASSINI)) {
332162306a36Sopenharmony_ci		if (pdev->revision >= CAS_ID_REVPLUS)
332262306a36Sopenharmony_ci			cp->cas_flags |= CAS_FLAG_REG_PLUS;
332362306a36Sopenharmony_ci		if (pdev->revision < CAS_ID_REVPLUS02u)
332462306a36Sopenharmony_ci			cp->cas_flags |= CAS_FLAG_TARGET_ABORT;
332562306a36Sopenharmony_ci
332662306a36Sopenharmony_ci		/* Original Cassini supports HW CSUM, but it's not
332762306a36Sopenharmony_ci		 * enabled by default as it can trigger TX hangs.
332862306a36Sopenharmony_ci		 */
332962306a36Sopenharmony_ci		if (pdev->revision < CAS_ID_REV2)
333062306a36Sopenharmony_ci			cp->cas_flags |= CAS_FLAG_NO_HW_CSUM;
333162306a36Sopenharmony_ci	} else {
333262306a36Sopenharmony_ci		/* Only sun has original cassini chips.  */
333362306a36Sopenharmony_ci		cp->cas_flags |= CAS_FLAG_REG_PLUS;
333462306a36Sopenharmony_ci
333562306a36Sopenharmony_ci		/* We use a flag because the same phy might be externally
333662306a36Sopenharmony_ci		 * connected.
333762306a36Sopenharmony_ci		 */
333862306a36Sopenharmony_ci		if ((pdev->vendor == PCI_VENDOR_ID_NS) &&
333962306a36Sopenharmony_ci		    (pdev->device == PCI_DEVICE_ID_NS_SATURN))
334062306a36Sopenharmony_ci			cp->cas_flags |= CAS_FLAG_SATURN;
334162306a36Sopenharmony_ci	}
334262306a36Sopenharmony_ci}
334362306a36Sopenharmony_ci
334462306a36Sopenharmony_ci
334562306a36Sopenharmony_cistatic int cas_check_invariants(struct cas *cp)
334662306a36Sopenharmony_ci{
334762306a36Sopenharmony_ci	struct pci_dev *pdev = cp->pdev;
334862306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
334962306a36Sopenharmony_ci	u32 cfg;
335062306a36Sopenharmony_ci	int i;
335162306a36Sopenharmony_ci
335262306a36Sopenharmony_ci	/* get page size for rx buffers. */
335362306a36Sopenharmony_ci	cp->page_order = 0;
335462306a36Sopenharmony_ci#ifdef USE_PAGE_ORDER
335562306a36Sopenharmony_ci	if (PAGE_SHIFT < CAS_JUMBO_PAGE_SHIFT) {
335662306a36Sopenharmony_ci		/* see if we can allocate larger pages */
335762306a36Sopenharmony_ci		struct page *page = alloc_pages(GFP_ATOMIC,
335862306a36Sopenharmony_ci						CAS_JUMBO_PAGE_SHIFT -
335962306a36Sopenharmony_ci						PAGE_SHIFT);
336062306a36Sopenharmony_ci		if (page) {
336162306a36Sopenharmony_ci			__free_pages(page, CAS_JUMBO_PAGE_SHIFT - PAGE_SHIFT);
336262306a36Sopenharmony_ci			cp->page_order = CAS_JUMBO_PAGE_SHIFT - PAGE_SHIFT;
336362306a36Sopenharmony_ci		} else {
336462306a36Sopenharmony_ci			printk("MTU limited to %d bytes\n", CAS_MAX_MTU);
336562306a36Sopenharmony_ci		}
336662306a36Sopenharmony_ci	}
336762306a36Sopenharmony_ci#endif
336862306a36Sopenharmony_ci	cp->page_size = (PAGE_SIZE << cp->page_order);
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_ci	/* Fetch the FIFO configurations. */
337162306a36Sopenharmony_ci	cp->tx_fifo_size = readl(cp->regs + REG_TX_FIFO_SIZE) * 64;
337262306a36Sopenharmony_ci	cp->rx_fifo_size = RX_FIFO_SIZE;
337362306a36Sopenharmony_ci
337462306a36Sopenharmony_ci	/* finish phy determination. MDIO1 takes precedence over MDIO0 if
337562306a36Sopenharmony_ci	 * they're both connected.
337662306a36Sopenharmony_ci	 */
337762306a36Sopenharmony_ci	cp->phy_type = cas_get_vpd_info(cp, addr, PCI_SLOT(pdev->devfn));
337862306a36Sopenharmony_ci	eth_hw_addr_set(cp->dev, addr);
337962306a36Sopenharmony_ci	if (cp->phy_type & CAS_PHY_SERDES) {
338062306a36Sopenharmony_ci		cp->cas_flags |= CAS_FLAG_1000MB_CAP;
338162306a36Sopenharmony_ci		return 0; /* no more checking needed */
338262306a36Sopenharmony_ci	}
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_ci	/* MII */
338562306a36Sopenharmony_ci	cfg = readl(cp->regs + REG_MIF_CFG);
338662306a36Sopenharmony_ci	if (cfg & MIF_CFG_MDIO_1) {
338762306a36Sopenharmony_ci		cp->phy_type = CAS_PHY_MII_MDIO1;
338862306a36Sopenharmony_ci	} else if (cfg & MIF_CFG_MDIO_0) {
338962306a36Sopenharmony_ci		cp->phy_type = CAS_PHY_MII_MDIO0;
339062306a36Sopenharmony_ci	}
339162306a36Sopenharmony_ci
339262306a36Sopenharmony_ci	cas_mif_poll(cp, 0);
339362306a36Sopenharmony_ci	writel(PCS_DATAPATH_MODE_MII, cp->regs + REG_PCS_DATAPATH_MODE);
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
339662306a36Sopenharmony_ci		u32 phy_id;
339762306a36Sopenharmony_ci		int j;
339862306a36Sopenharmony_ci
339962306a36Sopenharmony_ci		for (j = 0; j < 3; j++) {
340062306a36Sopenharmony_ci			cp->phy_addr = i;
340162306a36Sopenharmony_ci			phy_id = cas_phy_read(cp, MII_PHYSID1) << 16;
340262306a36Sopenharmony_ci			phy_id |= cas_phy_read(cp, MII_PHYSID2);
340362306a36Sopenharmony_ci			if (phy_id && (phy_id != 0xFFFFFFFF)) {
340462306a36Sopenharmony_ci				cp->phy_id = phy_id;
340562306a36Sopenharmony_ci				goto done;
340662306a36Sopenharmony_ci			}
340762306a36Sopenharmony_ci		}
340862306a36Sopenharmony_ci	}
340962306a36Sopenharmony_ci	pr_err("MII phy did not respond [%08x]\n",
341062306a36Sopenharmony_ci	       readl(cp->regs + REG_MIF_STATE_MACHINE));
341162306a36Sopenharmony_ci	return -1;
341262306a36Sopenharmony_ci
341362306a36Sopenharmony_cidone:
341462306a36Sopenharmony_ci	/* see if we can do gigabit */
341562306a36Sopenharmony_ci	cfg = cas_phy_read(cp, MII_BMSR);
341662306a36Sopenharmony_ci	if ((cfg & CAS_BMSR_1000_EXTEND) &&
341762306a36Sopenharmony_ci	    cas_phy_read(cp, CAS_MII_1000_EXTEND))
341862306a36Sopenharmony_ci		cp->cas_flags |= CAS_FLAG_1000MB_CAP;
341962306a36Sopenharmony_ci	return 0;
342062306a36Sopenharmony_ci}
342162306a36Sopenharmony_ci
342262306a36Sopenharmony_ci/* Must be invoked under cp->lock. */
342362306a36Sopenharmony_cistatic inline void cas_start_dma(struct cas *cp)
342462306a36Sopenharmony_ci{
342562306a36Sopenharmony_ci	int i;
342662306a36Sopenharmony_ci	u32 val;
342762306a36Sopenharmony_ci	int txfailed = 0;
342862306a36Sopenharmony_ci
342962306a36Sopenharmony_ci	/* enable dma */
343062306a36Sopenharmony_ci	val = readl(cp->regs + REG_TX_CFG) | TX_CFG_DMA_EN;
343162306a36Sopenharmony_ci	writel(val, cp->regs + REG_TX_CFG);
343262306a36Sopenharmony_ci	val = readl(cp->regs + REG_RX_CFG) | RX_CFG_DMA_EN;
343362306a36Sopenharmony_ci	writel(val, cp->regs + REG_RX_CFG);
343462306a36Sopenharmony_ci
343562306a36Sopenharmony_ci	/* enable the mac */
343662306a36Sopenharmony_ci	val = readl(cp->regs + REG_MAC_TX_CFG) | MAC_TX_CFG_EN;
343762306a36Sopenharmony_ci	writel(val, cp->regs + REG_MAC_TX_CFG);
343862306a36Sopenharmony_ci	val = readl(cp->regs + REG_MAC_RX_CFG) | MAC_RX_CFG_EN;
343962306a36Sopenharmony_ci	writel(val, cp->regs + REG_MAC_RX_CFG);
344062306a36Sopenharmony_ci
344162306a36Sopenharmony_ci	i = STOP_TRIES;
344262306a36Sopenharmony_ci	while (i-- > 0) {
344362306a36Sopenharmony_ci		val = readl(cp->regs + REG_MAC_TX_CFG);
344462306a36Sopenharmony_ci		if ((val & MAC_TX_CFG_EN))
344562306a36Sopenharmony_ci			break;
344662306a36Sopenharmony_ci		udelay(10);
344762306a36Sopenharmony_ci	}
344862306a36Sopenharmony_ci	if (i < 0) txfailed = 1;
344962306a36Sopenharmony_ci	i = STOP_TRIES;
345062306a36Sopenharmony_ci	while (i-- > 0) {
345162306a36Sopenharmony_ci		val = readl(cp->regs + REG_MAC_RX_CFG);
345262306a36Sopenharmony_ci		if ((val & MAC_RX_CFG_EN)) {
345362306a36Sopenharmony_ci			if (txfailed) {
345462306a36Sopenharmony_ci				netdev_err(cp->dev,
345562306a36Sopenharmony_ci					   "enabling mac failed [tx:%08x:%08x]\n",
345662306a36Sopenharmony_ci					   readl(cp->regs + REG_MIF_STATE_MACHINE),
345762306a36Sopenharmony_ci					   readl(cp->regs + REG_MAC_STATE_MACHINE));
345862306a36Sopenharmony_ci			}
345962306a36Sopenharmony_ci			goto enable_rx_done;
346062306a36Sopenharmony_ci		}
346162306a36Sopenharmony_ci		udelay(10);
346262306a36Sopenharmony_ci	}
346362306a36Sopenharmony_ci	netdev_err(cp->dev, "enabling mac failed [%s:%08x:%08x]\n",
346462306a36Sopenharmony_ci		   (txfailed ? "tx,rx" : "rx"),
346562306a36Sopenharmony_ci		   readl(cp->regs + REG_MIF_STATE_MACHINE),
346662306a36Sopenharmony_ci		   readl(cp->regs + REG_MAC_STATE_MACHINE));
346762306a36Sopenharmony_ci
346862306a36Sopenharmony_cienable_rx_done:
346962306a36Sopenharmony_ci	cas_unmask_intr(cp); /* enable interrupts */
347062306a36Sopenharmony_ci	writel(RX_DESC_RINGN_SIZE(0) - 4, cp->regs + REG_RX_KICK);
347162306a36Sopenharmony_ci	writel(0, cp->regs + REG_RX_COMP_TAIL);
347262306a36Sopenharmony_ci
347362306a36Sopenharmony_ci	if (cp->cas_flags & CAS_FLAG_REG_PLUS) {
347462306a36Sopenharmony_ci		if (N_RX_DESC_RINGS > 1)
347562306a36Sopenharmony_ci			writel(RX_DESC_RINGN_SIZE(1) - 4,
347662306a36Sopenharmony_ci			       cp->regs + REG_PLUS_RX_KICK1);
347762306a36Sopenharmony_ci	}
347862306a36Sopenharmony_ci}
347962306a36Sopenharmony_ci
348062306a36Sopenharmony_ci/* Must be invoked under cp->lock. */
348162306a36Sopenharmony_cistatic void cas_read_pcs_link_mode(struct cas *cp, int *fd, int *spd,
348262306a36Sopenharmony_ci				   int *pause)
348362306a36Sopenharmony_ci{
348462306a36Sopenharmony_ci	u32 val = readl(cp->regs + REG_PCS_MII_LPA);
348562306a36Sopenharmony_ci	*fd     = (val & PCS_MII_LPA_FD) ? 1 : 0;
348662306a36Sopenharmony_ci	*pause  = (val & PCS_MII_LPA_SYM_PAUSE) ? 0x01 : 0x00;
348762306a36Sopenharmony_ci	if (val & PCS_MII_LPA_ASYM_PAUSE)
348862306a36Sopenharmony_ci		*pause |= 0x10;
348962306a36Sopenharmony_ci	*spd = 1000;
349062306a36Sopenharmony_ci}
349162306a36Sopenharmony_ci
349262306a36Sopenharmony_ci/* Must be invoked under cp->lock. */
349362306a36Sopenharmony_cistatic void cas_read_mii_link_mode(struct cas *cp, int *fd, int *spd,
349462306a36Sopenharmony_ci				   int *pause)
349562306a36Sopenharmony_ci{
349662306a36Sopenharmony_ci	u32 val;
349762306a36Sopenharmony_ci
349862306a36Sopenharmony_ci	*fd = 0;
349962306a36Sopenharmony_ci	*spd = 10;
350062306a36Sopenharmony_ci	*pause = 0;
350162306a36Sopenharmony_ci
350262306a36Sopenharmony_ci	/* use GMII registers */
350362306a36Sopenharmony_ci	val = cas_phy_read(cp, MII_LPA);
350462306a36Sopenharmony_ci	if (val & CAS_LPA_PAUSE)
350562306a36Sopenharmony_ci		*pause = 0x01;
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_ci	if (val & CAS_LPA_ASYM_PAUSE)
350862306a36Sopenharmony_ci		*pause |= 0x10;
350962306a36Sopenharmony_ci
351062306a36Sopenharmony_ci	if (val & LPA_DUPLEX)
351162306a36Sopenharmony_ci		*fd = 1;
351262306a36Sopenharmony_ci	if (val & LPA_100)
351362306a36Sopenharmony_ci		*spd = 100;
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_ci	if (cp->cas_flags & CAS_FLAG_1000MB_CAP) {
351662306a36Sopenharmony_ci		val = cas_phy_read(cp, CAS_MII_1000_STATUS);
351762306a36Sopenharmony_ci		if (val & (CAS_LPA_1000FULL | CAS_LPA_1000HALF))
351862306a36Sopenharmony_ci			*spd = 1000;
351962306a36Sopenharmony_ci		if (val & CAS_LPA_1000FULL)
352062306a36Sopenharmony_ci			*fd = 1;
352162306a36Sopenharmony_ci	}
352262306a36Sopenharmony_ci}
352362306a36Sopenharmony_ci
352462306a36Sopenharmony_ci/* A link-up condition has occurred, initialize and enable the
352562306a36Sopenharmony_ci * rest of the chip.
352662306a36Sopenharmony_ci *
352762306a36Sopenharmony_ci * Must be invoked under cp->lock.
352862306a36Sopenharmony_ci */
352962306a36Sopenharmony_cistatic void cas_set_link_modes(struct cas *cp)
353062306a36Sopenharmony_ci{
353162306a36Sopenharmony_ci	u32 val;
353262306a36Sopenharmony_ci	int full_duplex, speed, pause;
353362306a36Sopenharmony_ci
353462306a36Sopenharmony_ci	full_duplex = 0;
353562306a36Sopenharmony_ci	speed = 10;
353662306a36Sopenharmony_ci	pause = 0;
353762306a36Sopenharmony_ci
353862306a36Sopenharmony_ci	if (CAS_PHY_MII(cp->phy_type)) {
353962306a36Sopenharmony_ci		cas_mif_poll(cp, 0);
354062306a36Sopenharmony_ci		val = cas_phy_read(cp, MII_BMCR);
354162306a36Sopenharmony_ci		if (val & BMCR_ANENABLE) {
354262306a36Sopenharmony_ci			cas_read_mii_link_mode(cp, &full_duplex, &speed,
354362306a36Sopenharmony_ci					       &pause);
354462306a36Sopenharmony_ci		} else {
354562306a36Sopenharmony_ci			if (val & BMCR_FULLDPLX)
354662306a36Sopenharmony_ci				full_duplex = 1;
354762306a36Sopenharmony_ci
354862306a36Sopenharmony_ci			if (val & BMCR_SPEED100)
354962306a36Sopenharmony_ci				speed = 100;
355062306a36Sopenharmony_ci			else if (val & CAS_BMCR_SPEED1000)
355162306a36Sopenharmony_ci				speed = (cp->cas_flags & CAS_FLAG_1000MB_CAP) ?
355262306a36Sopenharmony_ci					1000 : 100;
355362306a36Sopenharmony_ci		}
355462306a36Sopenharmony_ci		cas_mif_poll(cp, 1);
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci	} else {
355762306a36Sopenharmony_ci		val = readl(cp->regs + REG_PCS_MII_CTRL);
355862306a36Sopenharmony_ci		cas_read_pcs_link_mode(cp, &full_duplex, &speed, &pause);
355962306a36Sopenharmony_ci		if ((val & PCS_MII_AUTONEG_EN) == 0) {
356062306a36Sopenharmony_ci			if (val & PCS_MII_CTRL_DUPLEX)
356162306a36Sopenharmony_ci				full_duplex = 1;
356262306a36Sopenharmony_ci		}
356362306a36Sopenharmony_ci	}
356462306a36Sopenharmony_ci
356562306a36Sopenharmony_ci	netif_info(cp, link, cp->dev, "Link up at %d Mbps, %s-duplex\n",
356662306a36Sopenharmony_ci		   speed, full_duplex ? "full" : "half");
356762306a36Sopenharmony_ci
356862306a36Sopenharmony_ci	val = MAC_XIF_TX_MII_OUTPUT_EN | MAC_XIF_LINK_LED;
356962306a36Sopenharmony_ci	if (CAS_PHY_MII(cp->phy_type)) {
357062306a36Sopenharmony_ci		val |= MAC_XIF_MII_BUFFER_OUTPUT_EN;
357162306a36Sopenharmony_ci		if (!full_duplex)
357262306a36Sopenharmony_ci			val |= MAC_XIF_DISABLE_ECHO;
357362306a36Sopenharmony_ci	}
357462306a36Sopenharmony_ci	if (full_duplex)
357562306a36Sopenharmony_ci		val |= MAC_XIF_FDPLX_LED;
357662306a36Sopenharmony_ci	if (speed == 1000)
357762306a36Sopenharmony_ci		val |= MAC_XIF_GMII_MODE;
357862306a36Sopenharmony_ci	writel(val, cp->regs + REG_MAC_XIF_CFG);
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_ci	/* deal with carrier and collision detect. */
358162306a36Sopenharmony_ci	val = MAC_TX_CFG_IPG_EN;
358262306a36Sopenharmony_ci	if (full_duplex) {
358362306a36Sopenharmony_ci		val |= MAC_TX_CFG_IGNORE_CARRIER;
358462306a36Sopenharmony_ci		val |= MAC_TX_CFG_IGNORE_COLL;
358562306a36Sopenharmony_ci	} else {
358662306a36Sopenharmony_ci#ifndef USE_CSMA_CD_PROTO
358762306a36Sopenharmony_ci		val |= MAC_TX_CFG_NEVER_GIVE_UP_EN;
358862306a36Sopenharmony_ci		val |= MAC_TX_CFG_NEVER_GIVE_UP_LIM;
358962306a36Sopenharmony_ci#endif
359062306a36Sopenharmony_ci	}
359162306a36Sopenharmony_ci	/* val now set up for REG_MAC_TX_CFG */
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_ci	/* If gigabit and half-duplex, enable carrier extension
359462306a36Sopenharmony_ci	 * mode.  increase slot time to 512 bytes as well.
359562306a36Sopenharmony_ci	 * else, disable it and make sure slot time is 64 bytes.
359662306a36Sopenharmony_ci	 * also activate checksum bug workaround
359762306a36Sopenharmony_ci	 */
359862306a36Sopenharmony_ci	if ((speed == 1000) && !full_duplex) {
359962306a36Sopenharmony_ci		writel(val | MAC_TX_CFG_CARRIER_EXTEND,
360062306a36Sopenharmony_ci		       cp->regs + REG_MAC_TX_CFG);
360162306a36Sopenharmony_ci
360262306a36Sopenharmony_ci		val = readl(cp->regs + REG_MAC_RX_CFG);
360362306a36Sopenharmony_ci		val &= ~MAC_RX_CFG_STRIP_FCS; /* checksum workaround */
360462306a36Sopenharmony_ci		writel(val | MAC_RX_CFG_CARRIER_EXTEND,
360562306a36Sopenharmony_ci		       cp->regs + REG_MAC_RX_CFG);
360662306a36Sopenharmony_ci
360762306a36Sopenharmony_ci		writel(0x200, cp->regs + REG_MAC_SLOT_TIME);
360862306a36Sopenharmony_ci
360962306a36Sopenharmony_ci		cp->crc_size = 4;
361062306a36Sopenharmony_ci		/* minimum size gigabit frame at half duplex */
361162306a36Sopenharmony_ci		cp->min_frame_size = CAS_1000MB_MIN_FRAME;
361262306a36Sopenharmony_ci
361362306a36Sopenharmony_ci	} else {
361462306a36Sopenharmony_ci		writel(val, cp->regs + REG_MAC_TX_CFG);
361562306a36Sopenharmony_ci
361662306a36Sopenharmony_ci		/* checksum bug workaround. don't strip FCS when in
361762306a36Sopenharmony_ci		 * half-duplex mode
361862306a36Sopenharmony_ci		 */
361962306a36Sopenharmony_ci		val = readl(cp->regs + REG_MAC_RX_CFG);
362062306a36Sopenharmony_ci		if (full_duplex) {
362162306a36Sopenharmony_ci			val |= MAC_RX_CFG_STRIP_FCS;
362262306a36Sopenharmony_ci			cp->crc_size = 0;
362362306a36Sopenharmony_ci			cp->min_frame_size = CAS_MIN_MTU;
362462306a36Sopenharmony_ci		} else {
362562306a36Sopenharmony_ci			val &= ~MAC_RX_CFG_STRIP_FCS;
362662306a36Sopenharmony_ci			cp->crc_size = 4;
362762306a36Sopenharmony_ci			cp->min_frame_size = CAS_MIN_FRAME;
362862306a36Sopenharmony_ci		}
362962306a36Sopenharmony_ci		writel(val & ~MAC_RX_CFG_CARRIER_EXTEND,
363062306a36Sopenharmony_ci		       cp->regs + REG_MAC_RX_CFG);
363162306a36Sopenharmony_ci		writel(0x40, cp->regs + REG_MAC_SLOT_TIME);
363262306a36Sopenharmony_ci	}
363362306a36Sopenharmony_ci
363462306a36Sopenharmony_ci	if (netif_msg_link(cp)) {
363562306a36Sopenharmony_ci		if (pause & 0x01) {
363662306a36Sopenharmony_ci			netdev_info(cp->dev, "Pause is enabled (rxfifo: %d off: %d on: %d)\n",
363762306a36Sopenharmony_ci				    cp->rx_fifo_size,
363862306a36Sopenharmony_ci				    cp->rx_pause_off,
363962306a36Sopenharmony_ci				    cp->rx_pause_on);
364062306a36Sopenharmony_ci		} else if (pause & 0x10) {
364162306a36Sopenharmony_ci			netdev_info(cp->dev, "TX pause enabled\n");
364262306a36Sopenharmony_ci		} else {
364362306a36Sopenharmony_ci			netdev_info(cp->dev, "Pause is disabled\n");
364462306a36Sopenharmony_ci		}
364562306a36Sopenharmony_ci	}
364662306a36Sopenharmony_ci
364762306a36Sopenharmony_ci	val = readl(cp->regs + REG_MAC_CTRL_CFG);
364862306a36Sopenharmony_ci	val &= ~(MAC_CTRL_CFG_SEND_PAUSE_EN | MAC_CTRL_CFG_RECV_PAUSE_EN);
364962306a36Sopenharmony_ci	if (pause) { /* symmetric or asymmetric pause */
365062306a36Sopenharmony_ci		val |= MAC_CTRL_CFG_SEND_PAUSE_EN;
365162306a36Sopenharmony_ci		if (pause & 0x01) { /* symmetric pause */
365262306a36Sopenharmony_ci			val |= MAC_CTRL_CFG_RECV_PAUSE_EN;
365362306a36Sopenharmony_ci		}
365462306a36Sopenharmony_ci	}
365562306a36Sopenharmony_ci	writel(val, cp->regs + REG_MAC_CTRL_CFG);
365662306a36Sopenharmony_ci	cas_start_dma(cp);
365762306a36Sopenharmony_ci}
365862306a36Sopenharmony_ci
365962306a36Sopenharmony_ci/* Must be invoked under cp->lock. */
366062306a36Sopenharmony_cistatic void cas_init_hw(struct cas *cp, int restart_link)
366162306a36Sopenharmony_ci{
366262306a36Sopenharmony_ci	if (restart_link)
366362306a36Sopenharmony_ci		cas_phy_init(cp);
366462306a36Sopenharmony_ci
366562306a36Sopenharmony_ci	cas_init_pause_thresholds(cp);
366662306a36Sopenharmony_ci	cas_init_mac(cp);
366762306a36Sopenharmony_ci	cas_init_dma(cp);
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_ci	if (restart_link) {
367062306a36Sopenharmony_ci		/* Default aneg parameters */
367162306a36Sopenharmony_ci		cp->timer_ticks = 0;
367262306a36Sopenharmony_ci		cas_begin_auto_negotiation(cp, NULL);
367362306a36Sopenharmony_ci	} else if (cp->lstate == link_up) {
367462306a36Sopenharmony_ci		cas_set_link_modes(cp);
367562306a36Sopenharmony_ci		netif_carrier_on(cp->dev);
367662306a36Sopenharmony_ci	}
367762306a36Sopenharmony_ci}
367862306a36Sopenharmony_ci
367962306a36Sopenharmony_ci/* Must be invoked under cp->lock. on earlier cassini boards,
368062306a36Sopenharmony_ci * SOFT_0 is tied to PCI reset. we use this to force a pci reset,
368162306a36Sopenharmony_ci * let it settle out, and then restore pci state.
368262306a36Sopenharmony_ci */
368362306a36Sopenharmony_cistatic void cas_hard_reset(struct cas *cp)
368462306a36Sopenharmony_ci{
368562306a36Sopenharmony_ci	writel(BIM_LOCAL_DEV_SOFT_0, cp->regs + REG_BIM_LOCAL_DEV_EN);
368662306a36Sopenharmony_ci	udelay(20);
368762306a36Sopenharmony_ci	pci_restore_state(cp->pdev);
368862306a36Sopenharmony_ci}
368962306a36Sopenharmony_ci
369062306a36Sopenharmony_ci
369162306a36Sopenharmony_cistatic void cas_global_reset(struct cas *cp, int blkflag)
369262306a36Sopenharmony_ci{
369362306a36Sopenharmony_ci	int limit;
369462306a36Sopenharmony_ci
369562306a36Sopenharmony_ci	/* issue a global reset. don't use RSTOUT. */
369662306a36Sopenharmony_ci	if (blkflag && !CAS_PHY_MII(cp->phy_type)) {
369762306a36Sopenharmony_ci		/* For PCS, when the blkflag is set, we should set the
369862306a36Sopenharmony_ci		 * SW_REST_BLOCK_PCS_SLINK bit to prevent the results of
369962306a36Sopenharmony_ci		 * the last autonegotiation from being cleared.  We'll
370062306a36Sopenharmony_ci		 * need some special handling if the chip is set into a
370162306a36Sopenharmony_ci		 * loopback mode.
370262306a36Sopenharmony_ci		 */
370362306a36Sopenharmony_ci		writel((SW_RESET_TX | SW_RESET_RX | SW_RESET_BLOCK_PCS_SLINK),
370462306a36Sopenharmony_ci		       cp->regs + REG_SW_RESET);
370562306a36Sopenharmony_ci	} else {
370662306a36Sopenharmony_ci		writel(SW_RESET_TX | SW_RESET_RX, cp->regs + REG_SW_RESET);
370762306a36Sopenharmony_ci	}
370862306a36Sopenharmony_ci
370962306a36Sopenharmony_ci	/* need to wait at least 3ms before polling register */
371062306a36Sopenharmony_ci	mdelay(3);
371162306a36Sopenharmony_ci
371262306a36Sopenharmony_ci	limit = STOP_TRIES;
371362306a36Sopenharmony_ci	while (limit-- > 0) {
371462306a36Sopenharmony_ci		u32 val = readl(cp->regs + REG_SW_RESET);
371562306a36Sopenharmony_ci		if ((val & (SW_RESET_TX | SW_RESET_RX)) == 0)
371662306a36Sopenharmony_ci			goto done;
371762306a36Sopenharmony_ci		udelay(10);
371862306a36Sopenharmony_ci	}
371962306a36Sopenharmony_ci	netdev_err(cp->dev, "sw reset failed\n");
372062306a36Sopenharmony_ci
372162306a36Sopenharmony_cidone:
372262306a36Sopenharmony_ci	/* enable various BIM interrupts */
372362306a36Sopenharmony_ci	writel(BIM_CFG_DPAR_INTR_ENABLE | BIM_CFG_RMA_INTR_ENABLE |
372462306a36Sopenharmony_ci	       BIM_CFG_RTA_INTR_ENABLE, cp->regs + REG_BIM_CFG);
372562306a36Sopenharmony_ci
372662306a36Sopenharmony_ci	/* clear out pci error status mask for handled errors.
372762306a36Sopenharmony_ci	 * we don't deal with DMA counter overflows as they happen
372862306a36Sopenharmony_ci	 * all the time.
372962306a36Sopenharmony_ci	 */
373062306a36Sopenharmony_ci	writel(0xFFFFFFFFU & ~(PCI_ERR_BADACK | PCI_ERR_DTRTO |
373162306a36Sopenharmony_ci			       PCI_ERR_OTHER | PCI_ERR_BIM_DMA_WRITE |
373262306a36Sopenharmony_ci			       PCI_ERR_BIM_DMA_READ), cp->regs +
373362306a36Sopenharmony_ci	       REG_PCI_ERR_STATUS_MASK);
373462306a36Sopenharmony_ci
373562306a36Sopenharmony_ci	/* set up for MII by default to address mac rx reset timeout
373662306a36Sopenharmony_ci	 * issue
373762306a36Sopenharmony_ci	 */
373862306a36Sopenharmony_ci	writel(PCS_DATAPATH_MODE_MII, cp->regs + REG_PCS_DATAPATH_MODE);
373962306a36Sopenharmony_ci}
374062306a36Sopenharmony_ci
374162306a36Sopenharmony_cistatic void cas_reset(struct cas *cp, int blkflag)
374262306a36Sopenharmony_ci{
374362306a36Sopenharmony_ci	u32 val;
374462306a36Sopenharmony_ci
374562306a36Sopenharmony_ci	cas_mask_intr(cp);
374662306a36Sopenharmony_ci	cas_global_reset(cp, blkflag);
374762306a36Sopenharmony_ci	cas_mac_reset(cp);
374862306a36Sopenharmony_ci	cas_entropy_reset(cp);
374962306a36Sopenharmony_ci
375062306a36Sopenharmony_ci	/* disable dma engines. */
375162306a36Sopenharmony_ci	val = readl(cp->regs + REG_TX_CFG);
375262306a36Sopenharmony_ci	val &= ~TX_CFG_DMA_EN;
375362306a36Sopenharmony_ci	writel(val, cp->regs + REG_TX_CFG);
375462306a36Sopenharmony_ci
375562306a36Sopenharmony_ci	val = readl(cp->regs + REG_RX_CFG);
375662306a36Sopenharmony_ci	val &= ~RX_CFG_DMA_EN;
375762306a36Sopenharmony_ci	writel(val, cp->regs + REG_RX_CFG);
375862306a36Sopenharmony_ci
375962306a36Sopenharmony_ci	/* program header parser */
376062306a36Sopenharmony_ci	if ((cp->cas_flags & CAS_FLAG_TARGET_ABORT) ||
376162306a36Sopenharmony_ci	    (&CAS_HP_ALT_FIRMWARE[0] == &cas_prog_null[0])) {
376262306a36Sopenharmony_ci		cas_load_firmware(cp, CAS_HP_FIRMWARE);
376362306a36Sopenharmony_ci	} else {
376462306a36Sopenharmony_ci		cas_load_firmware(cp, CAS_HP_ALT_FIRMWARE);
376562306a36Sopenharmony_ci	}
376662306a36Sopenharmony_ci
376762306a36Sopenharmony_ci	/* clear out error registers */
376862306a36Sopenharmony_ci	spin_lock(&cp->stat_lock[N_TX_RINGS]);
376962306a36Sopenharmony_ci	cas_clear_mac_err(cp);
377062306a36Sopenharmony_ci	spin_unlock(&cp->stat_lock[N_TX_RINGS]);
377162306a36Sopenharmony_ci}
377262306a36Sopenharmony_ci
377362306a36Sopenharmony_ci/* Shut down the chip, must be called with pm_mutex held.  */
377462306a36Sopenharmony_cistatic void cas_shutdown(struct cas *cp)
377562306a36Sopenharmony_ci{
377662306a36Sopenharmony_ci	unsigned long flags;
377762306a36Sopenharmony_ci
377862306a36Sopenharmony_ci	/* Make us not-running to avoid timers respawning */
377962306a36Sopenharmony_ci	cp->hw_running = 0;
378062306a36Sopenharmony_ci
378162306a36Sopenharmony_ci	del_timer_sync(&cp->link_timer);
378262306a36Sopenharmony_ci
378362306a36Sopenharmony_ci	/* Stop the reset task */
378462306a36Sopenharmony_ci#if 0
378562306a36Sopenharmony_ci	while (atomic_read(&cp->reset_task_pending_mtu) ||
378662306a36Sopenharmony_ci	       atomic_read(&cp->reset_task_pending_spare) ||
378762306a36Sopenharmony_ci	       atomic_read(&cp->reset_task_pending_all))
378862306a36Sopenharmony_ci		schedule();
378962306a36Sopenharmony_ci
379062306a36Sopenharmony_ci#else
379162306a36Sopenharmony_ci	while (atomic_read(&cp->reset_task_pending))
379262306a36Sopenharmony_ci		schedule();
379362306a36Sopenharmony_ci#endif
379462306a36Sopenharmony_ci	/* Actually stop the chip */
379562306a36Sopenharmony_ci	cas_lock_all_save(cp, flags);
379662306a36Sopenharmony_ci	cas_reset(cp, 0);
379762306a36Sopenharmony_ci	if (cp->cas_flags & CAS_FLAG_SATURN)
379862306a36Sopenharmony_ci		cas_phy_powerdown(cp);
379962306a36Sopenharmony_ci	cas_unlock_all_restore(cp, flags);
380062306a36Sopenharmony_ci}
380162306a36Sopenharmony_ci
380262306a36Sopenharmony_cistatic int cas_change_mtu(struct net_device *dev, int new_mtu)
380362306a36Sopenharmony_ci{
380462306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
380562306a36Sopenharmony_ci
380662306a36Sopenharmony_ci	dev->mtu = new_mtu;
380762306a36Sopenharmony_ci	if (!netif_running(dev) || !netif_device_present(dev))
380862306a36Sopenharmony_ci		return 0;
380962306a36Sopenharmony_ci
381062306a36Sopenharmony_ci	/* let the reset task handle it */
381162306a36Sopenharmony_ci#if 1
381262306a36Sopenharmony_ci	atomic_inc(&cp->reset_task_pending);
381362306a36Sopenharmony_ci	if ((cp->phy_type & CAS_PHY_SERDES)) {
381462306a36Sopenharmony_ci		atomic_inc(&cp->reset_task_pending_all);
381562306a36Sopenharmony_ci	} else {
381662306a36Sopenharmony_ci		atomic_inc(&cp->reset_task_pending_mtu);
381762306a36Sopenharmony_ci	}
381862306a36Sopenharmony_ci	schedule_work(&cp->reset_task);
381962306a36Sopenharmony_ci#else
382062306a36Sopenharmony_ci	atomic_set(&cp->reset_task_pending, (cp->phy_type & CAS_PHY_SERDES) ?
382162306a36Sopenharmony_ci		   CAS_RESET_ALL : CAS_RESET_MTU);
382262306a36Sopenharmony_ci	pr_err("reset called in cas_change_mtu\n");
382362306a36Sopenharmony_ci	schedule_work(&cp->reset_task);
382462306a36Sopenharmony_ci#endif
382562306a36Sopenharmony_ci
382662306a36Sopenharmony_ci	flush_work(&cp->reset_task);
382762306a36Sopenharmony_ci	return 0;
382862306a36Sopenharmony_ci}
382962306a36Sopenharmony_ci
383062306a36Sopenharmony_cistatic void cas_clean_txd(struct cas *cp, int ring)
383162306a36Sopenharmony_ci{
383262306a36Sopenharmony_ci	struct cas_tx_desc *txd = cp->init_txds[ring];
383362306a36Sopenharmony_ci	struct sk_buff *skb, **skbs = cp->tx_skbs[ring];
383462306a36Sopenharmony_ci	u64 daddr, dlen;
383562306a36Sopenharmony_ci	int i, size;
383662306a36Sopenharmony_ci
383762306a36Sopenharmony_ci	size = TX_DESC_RINGN_SIZE(ring);
383862306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
383962306a36Sopenharmony_ci		int frag;
384062306a36Sopenharmony_ci
384162306a36Sopenharmony_ci		if (skbs[i] == NULL)
384262306a36Sopenharmony_ci			continue;
384362306a36Sopenharmony_ci
384462306a36Sopenharmony_ci		skb = skbs[i];
384562306a36Sopenharmony_ci		skbs[i] = NULL;
384662306a36Sopenharmony_ci
384762306a36Sopenharmony_ci		for (frag = 0; frag <= skb_shinfo(skb)->nr_frags;  frag++) {
384862306a36Sopenharmony_ci			int ent = i & (size - 1);
384962306a36Sopenharmony_ci
385062306a36Sopenharmony_ci			/* first buffer is never a tiny buffer and so
385162306a36Sopenharmony_ci			 * needs to be unmapped.
385262306a36Sopenharmony_ci			 */
385362306a36Sopenharmony_ci			daddr = le64_to_cpu(txd[ent].buffer);
385462306a36Sopenharmony_ci			dlen  =  CAS_VAL(TX_DESC_BUFLEN,
385562306a36Sopenharmony_ci					 le64_to_cpu(txd[ent].control));
385662306a36Sopenharmony_ci			dma_unmap_page(&cp->pdev->dev, daddr, dlen,
385762306a36Sopenharmony_ci				       DMA_TO_DEVICE);
385862306a36Sopenharmony_ci
385962306a36Sopenharmony_ci			if (frag != skb_shinfo(skb)->nr_frags) {
386062306a36Sopenharmony_ci				i++;
386162306a36Sopenharmony_ci
386262306a36Sopenharmony_ci				/* next buffer might by a tiny buffer.
386362306a36Sopenharmony_ci				 * skip past it.
386462306a36Sopenharmony_ci				 */
386562306a36Sopenharmony_ci				ent = i & (size - 1);
386662306a36Sopenharmony_ci				if (cp->tx_tiny_use[ring][ent].used)
386762306a36Sopenharmony_ci					i++;
386862306a36Sopenharmony_ci			}
386962306a36Sopenharmony_ci		}
387062306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
387162306a36Sopenharmony_ci	}
387262306a36Sopenharmony_ci
387362306a36Sopenharmony_ci	/* zero out tiny buf usage */
387462306a36Sopenharmony_ci	memset(cp->tx_tiny_use[ring], 0, size*sizeof(*cp->tx_tiny_use[ring]));
387562306a36Sopenharmony_ci}
387662306a36Sopenharmony_ci
387762306a36Sopenharmony_ci/* freed on close */
387862306a36Sopenharmony_cistatic inline void cas_free_rx_desc(struct cas *cp, int ring)
387962306a36Sopenharmony_ci{
388062306a36Sopenharmony_ci	cas_page_t **page = cp->rx_pages[ring];
388162306a36Sopenharmony_ci	int i, size;
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci	size = RX_DESC_RINGN_SIZE(ring);
388462306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
388562306a36Sopenharmony_ci		if (page[i]) {
388662306a36Sopenharmony_ci			cas_page_free(cp, page[i]);
388762306a36Sopenharmony_ci			page[i] = NULL;
388862306a36Sopenharmony_ci		}
388962306a36Sopenharmony_ci	}
389062306a36Sopenharmony_ci}
389162306a36Sopenharmony_ci
389262306a36Sopenharmony_cistatic void cas_free_rxds(struct cas *cp)
389362306a36Sopenharmony_ci{
389462306a36Sopenharmony_ci	int i;
389562306a36Sopenharmony_ci
389662306a36Sopenharmony_ci	for (i = 0; i < N_RX_DESC_RINGS; i++)
389762306a36Sopenharmony_ci		cas_free_rx_desc(cp, i);
389862306a36Sopenharmony_ci}
389962306a36Sopenharmony_ci
390062306a36Sopenharmony_ci/* Must be invoked under cp->lock. */
390162306a36Sopenharmony_cistatic void cas_clean_rings(struct cas *cp)
390262306a36Sopenharmony_ci{
390362306a36Sopenharmony_ci	int i;
390462306a36Sopenharmony_ci
390562306a36Sopenharmony_ci	/* need to clean all tx rings */
390662306a36Sopenharmony_ci	memset(cp->tx_old, 0, sizeof(*cp->tx_old)*N_TX_RINGS);
390762306a36Sopenharmony_ci	memset(cp->tx_new, 0, sizeof(*cp->tx_new)*N_TX_RINGS);
390862306a36Sopenharmony_ci	for (i = 0; i < N_TX_RINGS; i++)
390962306a36Sopenharmony_ci		cas_clean_txd(cp, i);
391062306a36Sopenharmony_ci
391162306a36Sopenharmony_ci	/* zero out init block */
391262306a36Sopenharmony_ci	memset(cp->init_block, 0, sizeof(struct cas_init_block));
391362306a36Sopenharmony_ci	cas_clean_rxds(cp);
391462306a36Sopenharmony_ci	cas_clean_rxcs(cp);
391562306a36Sopenharmony_ci}
391662306a36Sopenharmony_ci
391762306a36Sopenharmony_ci/* allocated on open */
391862306a36Sopenharmony_cistatic inline int cas_alloc_rx_desc(struct cas *cp, int ring)
391962306a36Sopenharmony_ci{
392062306a36Sopenharmony_ci	cas_page_t **page = cp->rx_pages[ring];
392162306a36Sopenharmony_ci	int size, i = 0;
392262306a36Sopenharmony_ci
392362306a36Sopenharmony_ci	size = RX_DESC_RINGN_SIZE(ring);
392462306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
392562306a36Sopenharmony_ci		if ((page[i] = cas_page_alloc(cp, GFP_KERNEL)) == NULL)
392662306a36Sopenharmony_ci			return -1;
392762306a36Sopenharmony_ci	}
392862306a36Sopenharmony_ci	return 0;
392962306a36Sopenharmony_ci}
393062306a36Sopenharmony_ci
393162306a36Sopenharmony_cistatic int cas_alloc_rxds(struct cas *cp)
393262306a36Sopenharmony_ci{
393362306a36Sopenharmony_ci	int i;
393462306a36Sopenharmony_ci
393562306a36Sopenharmony_ci	for (i = 0; i < N_RX_DESC_RINGS; i++) {
393662306a36Sopenharmony_ci		if (cas_alloc_rx_desc(cp, i) < 0) {
393762306a36Sopenharmony_ci			cas_free_rxds(cp);
393862306a36Sopenharmony_ci			return -1;
393962306a36Sopenharmony_ci		}
394062306a36Sopenharmony_ci	}
394162306a36Sopenharmony_ci	return 0;
394262306a36Sopenharmony_ci}
394362306a36Sopenharmony_ci
394462306a36Sopenharmony_cistatic void cas_reset_task(struct work_struct *work)
394562306a36Sopenharmony_ci{
394662306a36Sopenharmony_ci	struct cas *cp = container_of(work, struct cas, reset_task);
394762306a36Sopenharmony_ci#if 0
394862306a36Sopenharmony_ci	int pending = atomic_read(&cp->reset_task_pending);
394962306a36Sopenharmony_ci#else
395062306a36Sopenharmony_ci	int pending_all = atomic_read(&cp->reset_task_pending_all);
395162306a36Sopenharmony_ci	int pending_spare = atomic_read(&cp->reset_task_pending_spare);
395262306a36Sopenharmony_ci	int pending_mtu = atomic_read(&cp->reset_task_pending_mtu);
395362306a36Sopenharmony_ci
395462306a36Sopenharmony_ci	if (pending_all == 0 && pending_spare == 0 && pending_mtu == 0) {
395562306a36Sopenharmony_ci		/* We can have more tasks scheduled than actually
395662306a36Sopenharmony_ci		 * needed.
395762306a36Sopenharmony_ci		 */
395862306a36Sopenharmony_ci		atomic_dec(&cp->reset_task_pending);
395962306a36Sopenharmony_ci		return;
396062306a36Sopenharmony_ci	}
396162306a36Sopenharmony_ci#endif
396262306a36Sopenharmony_ci	/* The link went down, we reset the ring, but keep
396362306a36Sopenharmony_ci	 * DMA stopped. Use this function for reset
396462306a36Sopenharmony_ci	 * on error as well.
396562306a36Sopenharmony_ci	 */
396662306a36Sopenharmony_ci	if (cp->hw_running) {
396762306a36Sopenharmony_ci		unsigned long flags;
396862306a36Sopenharmony_ci
396962306a36Sopenharmony_ci		/* Make sure we don't get interrupts or tx packets */
397062306a36Sopenharmony_ci		netif_device_detach(cp->dev);
397162306a36Sopenharmony_ci		cas_lock_all_save(cp, flags);
397262306a36Sopenharmony_ci
397362306a36Sopenharmony_ci		if (cp->opened) {
397462306a36Sopenharmony_ci			/* We call cas_spare_recover when we call cas_open.
397562306a36Sopenharmony_ci			 * but we do not initialize the lists cas_spare_recover
397662306a36Sopenharmony_ci			 * uses until cas_open is called.
397762306a36Sopenharmony_ci			 */
397862306a36Sopenharmony_ci			cas_spare_recover(cp, GFP_ATOMIC);
397962306a36Sopenharmony_ci		}
398062306a36Sopenharmony_ci#if 1
398162306a36Sopenharmony_ci		/* test => only pending_spare set */
398262306a36Sopenharmony_ci		if (!pending_all && !pending_mtu)
398362306a36Sopenharmony_ci			goto done;
398462306a36Sopenharmony_ci#else
398562306a36Sopenharmony_ci		if (pending == CAS_RESET_SPARE)
398662306a36Sopenharmony_ci			goto done;
398762306a36Sopenharmony_ci#endif
398862306a36Sopenharmony_ci		/* when pending == CAS_RESET_ALL, the following
398962306a36Sopenharmony_ci		 * call to cas_init_hw will restart auto negotiation.
399062306a36Sopenharmony_ci		 * Setting the second argument of cas_reset to
399162306a36Sopenharmony_ci		 * !(pending == CAS_RESET_ALL) will set this argument
399262306a36Sopenharmony_ci		 * to 1 (avoiding reinitializing the PHY for the normal
399362306a36Sopenharmony_ci		 * PCS case) when auto negotiation is not restarted.
399462306a36Sopenharmony_ci		 */
399562306a36Sopenharmony_ci#if 1
399662306a36Sopenharmony_ci		cas_reset(cp, !(pending_all > 0));
399762306a36Sopenharmony_ci		if (cp->opened)
399862306a36Sopenharmony_ci			cas_clean_rings(cp);
399962306a36Sopenharmony_ci		cas_init_hw(cp, (pending_all > 0));
400062306a36Sopenharmony_ci#else
400162306a36Sopenharmony_ci		cas_reset(cp, !(pending == CAS_RESET_ALL));
400262306a36Sopenharmony_ci		if (cp->opened)
400362306a36Sopenharmony_ci			cas_clean_rings(cp);
400462306a36Sopenharmony_ci		cas_init_hw(cp, pending == CAS_RESET_ALL);
400562306a36Sopenharmony_ci#endif
400662306a36Sopenharmony_ci
400762306a36Sopenharmony_cidone:
400862306a36Sopenharmony_ci		cas_unlock_all_restore(cp, flags);
400962306a36Sopenharmony_ci		netif_device_attach(cp->dev);
401062306a36Sopenharmony_ci	}
401162306a36Sopenharmony_ci#if 1
401262306a36Sopenharmony_ci	atomic_sub(pending_all, &cp->reset_task_pending_all);
401362306a36Sopenharmony_ci	atomic_sub(pending_spare, &cp->reset_task_pending_spare);
401462306a36Sopenharmony_ci	atomic_sub(pending_mtu, &cp->reset_task_pending_mtu);
401562306a36Sopenharmony_ci	atomic_dec(&cp->reset_task_pending);
401662306a36Sopenharmony_ci#else
401762306a36Sopenharmony_ci	atomic_set(&cp->reset_task_pending, 0);
401862306a36Sopenharmony_ci#endif
401962306a36Sopenharmony_ci}
402062306a36Sopenharmony_ci
402162306a36Sopenharmony_cistatic void cas_link_timer(struct timer_list *t)
402262306a36Sopenharmony_ci{
402362306a36Sopenharmony_ci	struct cas *cp = from_timer(cp, t, link_timer);
402462306a36Sopenharmony_ci	int mask, pending = 0, reset = 0;
402562306a36Sopenharmony_ci	unsigned long flags;
402662306a36Sopenharmony_ci
402762306a36Sopenharmony_ci	if (link_transition_timeout != 0 &&
402862306a36Sopenharmony_ci	    cp->link_transition_jiffies_valid &&
402962306a36Sopenharmony_ci	    time_is_before_jiffies(cp->link_transition_jiffies +
403062306a36Sopenharmony_ci	      link_transition_timeout)) {
403162306a36Sopenharmony_ci		/* One-second counter so link-down workaround doesn't
403262306a36Sopenharmony_ci		 * cause resets to occur so fast as to fool the switch
403362306a36Sopenharmony_ci		 * into thinking the link is down.
403462306a36Sopenharmony_ci		 */
403562306a36Sopenharmony_ci		cp->link_transition_jiffies_valid = 0;
403662306a36Sopenharmony_ci	}
403762306a36Sopenharmony_ci
403862306a36Sopenharmony_ci	if (!cp->hw_running)
403962306a36Sopenharmony_ci		return;
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_ci	spin_lock_irqsave(&cp->lock, flags);
404262306a36Sopenharmony_ci	cas_lock_tx(cp);
404362306a36Sopenharmony_ci	cas_entropy_gather(cp);
404462306a36Sopenharmony_ci
404562306a36Sopenharmony_ci	/* If the link task is still pending, we just
404662306a36Sopenharmony_ci	 * reschedule the link timer
404762306a36Sopenharmony_ci	 */
404862306a36Sopenharmony_ci#if 1
404962306a36Sopenharmony_ci	if (atomic_read(&cp->reset_task_pending_all) ||
405062306a36Sopenharmony_ci	    atomic_read(&cp->reset_task_pending_spare) ||
405162306a36Sopenharmony_ci	    atomic_read(&cp->reset_task_pending_mtu))
405262306a36Sopenharmony_ci		goto done;
405362306a36Sopenharmony_ci#else
405462306a36Sopenharmony_ci	if (atomic_read(&cp->reset_task_pending))
405562306a36Sopenharmony_ci		goto done;
405662306a36Sopenharmony_ci#endif
405762306a36Sopenharmony_ci
405862306a36Sopenharmony_ci	/* check for rx cleaning */
405962306a36Sopenharmony_ci	if ((mask = (cp->cas_flags & CAS_FLAG_RXD_POST_MASK))) {
406062306a36Sopenharmony_ci		int i, rmask;
406162306a36Sopenharmony_ci
406262306a36Sopenharmony_ci		for (i = 0; i < MAX_RX_DESC_RINGS; i++) {
406362306a36Sopenharmony_ci			rmask = CAS_FLAG_RXD_POST(i);
406462306a36Sopenharmony_ci			if ((mask & rmask) == 0)
406562306a36Sopenharmony_ci				continue;
406662306a36Sopenharmony_ci
406762306a36Sopenharmony_ci			/* post_rxds will do a mod_timer */
406862306a36Sopenharmony_ci			if (cas_post_rxds_ringN(cp, i, cp->rx_last[i]) < 0) {
406962306a36Sopenharmony_ci				pending = 1;
407062306a36Sopenharmony_ci				continue;
407162306a36Sopenharmony_ci			}
407262306a36Sopenharmony_ci			cp->cas_flags &= ~rmask;
407362306a36Sopenharmony_ci		}
407462306a36Sopenharmony_ci	}
407562306a36Sopenharmony_ci
407662306a36Sopenharmony_ci	if (CAS_PHY_MII(cp->phy_type)) {
407762306a36Sopenharmony_ci		u16 bmsr;
407862306a36Sopenharmony_ci		cas_mif_poll(cp, 0);
407962306a36Sopenharmony_ci		bmsr = cas_phy_read(cp, MII_BMSR);
408062306a36Sopenharmony_ci		/* WTZ: Solaris driver reads this twice, but that
408162306a36Sopenharmony_ci		 * may be due to the PCS case and the use of a
408262306a36Sopenharmony_ci		 * common implementation. Read it twice here to be
408362306a36Sopenharmony_ci		 * safe.
408462306a36Sopenharmony_ci		 */
408562306a36Sopenharmony_ci		bmsr = cas_phy_read(cp, MII_BMSR);
408662306a36Sopenharmony_ci		cas_mif_poll(cp, 1);
408762306a36Sopenharmony_ci		readl(cp->regs + REG_MIF_STATUS); /* avoid dups */
408862306a36Sopenharmony_ci		reset = cas_mii_link_check(cp, bmsr);
408962306a36Sopenharmony_ci	} else {
409062306a36Sopenharmony_ci		reset = cas_pcs_link_check(cp);
409162306a36Sopenharmony_ci	}
409262306a36Sopenharmony_ci
409362306a36Sopenharmony_ci	if (reset)
409462306a36Sopenharmony_ci		goto done;
409562306a36Sopenharmony_ci
409662306a36Sopenharmony_ci	/* check for tx state machine confusion */
409762306a36Sopenharmony_ci	if ((readl(cp->regs + REG_MAC_TX_STATUS) & MAC_TX_FRAME_XMIT) == 0) {
409862306a36Sopenharmony_ci		u32 val = readl(cp->regs + REG_MAC_STATE_MACHINE);
409962306a36Sopenharmony_ci		u32 wptr, rptr;
410062306a36Sopenharmony_ci		int tlm  = CAS_VAL(MAC_SM_TLM, val);
410162306a36Sopenharmony_ci
410262306a36Sopenharmony_ci		if (((tlm == 0x5) || (tlm == 0x3)) &&
410362306a36Sopenharmony_ci		    (CAS_VAL(MAC_SM_ENCAP_SM, val) == 0)) {
410462306a36Sopenharmony_ci			netif_printk(cp, tx_err, KERN_DEBUG, cp->dev,
410562306a36Sopenharmony_ci				     "tx err: MAC_STATE[%08x]\n", val);
410662306a36Sopenharmony_ci			reset = 1;
410762306a36Sopenharmony_ci			goto done;
410862306a36Sopenharmony_ci		}
410962306a36Sopenharmony_ci
411062306a36Sopenharmony_ci		val  = readl(cp->regs + REG_TX_FIFO_PKT_CNT);
411162306a36Sopenharmony_ci		wptr = readl(cp->regs + REG_TX_FIFO_WRITE_PTR);
411262306a36Sopenharmony_ci		rptr = readl(cp->regs + REG_TX_FIFO_READ_PTR);
411362306a36Sopenharmony_ci		if ((val == 0) && (wptr != rptr)) {
411462306a36Sopenharmony_ci			netif_printk(cp, tx_err, KERN_DEBUG, cp->dev,
411562306a36Sopenharmony_ci				     "tx err: TX_FIFO[%08x:%08x:%08x]\n",
411662306a36Sopenharmony_ci				     val, wptr, rptr);
411762306a36Sopenharmony_ci			reset = 1;
411862306a36Sopenharmony_ci		}
411962306a36Sopenharmony_ci
412062306a36Sopenharmony_ci		if (reset)
412162306a36Sopenharmony_ci			cas_hard_reset(cp);
412262306a36Sopenharmony_ci	}
412362306a36Sopenharmony_ci
412462306a36Sopenharmony_cidone:
412562306a36Sopenharmony_ci	if (reset) {
412662306a36Sopenharmony_ci#if 1
412762306a36Sopenharmony_ci		atomic_inc(&cp->reset_task_pending);
412862306a36Sopenharmony_ci		atomic_inc(&cp->reset_task_pending_all);
412962306a36Sopenharmony_ci		schedule_work(&cp->reset_task);
413062306a36Sopenharmony_ci#else
413162306a36Sopenharmony_ci		atomic_set(&cp->reset_task_pending, CAS_RESET_ALL);
413262306a36Sopenharmony_ci		pr_err("reset called in cas_link_timer\n");
413362306a36Sopenharmony_ci		schedule_work(&cp->reset_task);
413462306a36Sopenharmony_ci#endif
413562306a36Sopenharmony_ci	}
413662306a36Sopenharmony_ci
413762306a36Sopenharmony_ci	if (!pending)
413862306a36Sopenharmony_ci		mod_timer(&cp->link_timer, jiffies + CAS_LINK_TIMEOUT);
413962306a36Sopenharmony_ci	cas_unlock_tx(cp);
414062306a36Sopenharmony_ci	spin_unlock_irqrestore(&cp->lock, flags);
414162306a36Sopenharmony_ci}
414262306a36Sopenharmony_ci
414362306a36Sopenharmony_ci/* tiny buffers are used to avoid target abort issues with
414462306a36Sopenharmony_ci * older cassini's
414562306a36Sopenharmony_ci */
414662306a36Sopenharmony_cistatic void cas_tx_tiny_free(struct cas *cp)
414762306a36Sopenharmony_ci{
414862306a36Sopenharmony_ci	struct pci_dev *pdev = cp->pdev;
414962306a36Sopenharmony_ci	int i;
415062306a36Sopenharmony_ci
415162306a36Sopenharmony_ci	for (i = 0; i < N_TX_RINGS; i++) {
415262306a36Sopenharmony_ci		if (!cp->tx_tiny_bufs[i])
415362306a36Sopenharmony_ci			continue;
415462306a36Sopenharmony_ci
415562306a36Sopenharmony_ci		dma_free_coherent(&pdev->dev, TX_TINY_BUF_BLOCK,
415662306a36Sopenharmony_ci				  cp->tx_tiny_bufs[i], cp->tx_tiny_dvma[i]);
415762306a36Sopenharmony_ci		cp->tx_tiny_bufs[i] = NULL;
415862306a36Sopenharmony_ci	}
415962306a36Sopenharmony_ci}
416062306a36Sopenharmony_ci
416162306a36Sopenharmony_cistatic int cas_tx_tiny_alloc(struct cas *cp)
416262306a36Sopenharmony_ci{
416362306a36Sopenharmony_ci	struct pci_dev *pdev = cp->pdev;
416462306a36Sopenharmony_ci	int i;
416562306a36Sopenharmony_ci
416662306a36Sopenharmony_ci	for (i = 0; i < N_TX_RINGS; i++) {
416762306a36Sopenharmony_ci		cp->tx_tiny_bufs[i] =
416862306a36Sopenharmony_ci			dma_alloc_coherent(&pdev->dev, TX_TINY_BUF_BLOCK,
416962306a36Sopenharmony_ci					   &cp->tx_tiny_dvma[i], GFP_KERNEL);
417062306a36Sopenharmony_ci		if (!cp->tx_tiny_bufs[i]) {
417162306a36Sopenharmony_ci			cas_tx_tiny_free(cp);
417262306a36Sopenharmony_ci			return -1;
417362306a36Sopenharmony_ci		}
417462306a36Sopenharmony_ci	}
417562306a36Sopenharmony_ci	return 0;
417662306a36Sopenharmony_ci}
417762306a36Sopenharmony_ci
417862306a36Sopenharmony_ci
417962306a36Sopenharmony_cistatic int cas_open(struct net_device *dev)
418062306a36Sopenharmony_ci{
418162306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
418262306a36Sopenharmony_ci	int hw_was_up, err;
418362306a36Sopenharmony_ci	unsigned long flags;
418462306a36Sopenharmony_ci
418562306a36Sopenharmony_ci	mutex_lock(&cp->pm_mutex);
418662306a36Sopenharmony_ci
418762306a36Sopenharmony_ci	hw_was_up = cp->hw_running;
418862306a36Sopenharmony_ci
418962306a36Sopenharmony_ci	/* The power-management mutex protects the hw_running
419062306a36Sopenharmony_ci	 * etc. state so it is safe to do this bit without cp->lock
419162306a36Sopenharmony_ci	 */
419262306a36Sopenharmony_ci	if (!cp->hw_running) {
419362306a36Sopenharmony_ci		/* Reset the chip */
419462306a36Sopenharmony_ci		cas_lock_all_save(cp, flags);
419562306a36Sopenharmony_ci		/* We set the second arg to cas_reset to zero
419662306a36Sopenharmony_ci		 * because cas_init_hw below will have its second
419762306a36Sopenharmony_ci		 * argument set to non-zero, which will force
419862306a36Sopenharmony_ci		 * autonegotiation to start.
419962306a36Sopenharmony_ci		 */
420062306a36Sopenharmony_ci		cas_reset(cp, 0);
420162306a36Sopenharmony_ci		cp->hw_running = 1;
420262306a36Sopenharmony_ci		cas_unlock_all_restore(cp, flags);
420362306a36Sopenharmony_ci	}
420462306a36Sopenharmony_ci
420562306a36Sopenharmony_ci	err = -ENOMEM;
420662306a36Sopenharmony_ci	if (cas_tx_tiny_alloc(cp) < 0)
420762306a36Sopenharmony_ci		goto err_unlock;
420862306a36Sopenharmony_ci
420962306a36Sopenharmony_ci	/* alloc rx descriptors */
421062306a36Sopenharmony_ci	if (cas_alloc_rxds(cp) < 0)
421162306a36Sopenharmony_ci		goto err_tx_tiny;
421262306a36Sopenharmony_ci
421362306a36Sopenharmony_ci	/* allocate spares */
421462306a36Sopenharmony_ci	cas_spare_init(cp);
421562306a36Sopenharmony_ci	cas_spare_recover(cp, GFP_KERNEL);
421662306a36Sopenharmony_ci
421762306a36Sopenharmony_ci	/* We can now request the interrupt as we know it's masked
421862306a36Sopenharmony_ci	 * on the controller. cassini+ has up to 4 interrupts
421962306a36Sopenharmony_ci	 * that can be used, but you need to do explicit pci interrupt
422062306a36Sopenharmony_ci	 * mapping to expose them
422162306a36Sopenharmony_ci	 */
422262306a36Sopenharmony_ci	if (request_irq(cp->pdev->irq, cas_interrupt,
422362306a36Sopenharmony_ci			IRQF_SHARED, dev->name, (void *) dev)) {
422462306a36Sopenharmony_ci		netdev_err(cp->dev, "failed to request irq !\n");
422562306a36Sopenharmony_ci		err = -EAGAIN;
422662306a36Sopenharmony_ci		goto err_spare;
422762306a36Sopenharmony_ci	}
422862306a36Sopenharmony_ci
422962306a36Sopenharmony_ci#ifdef USE_NAPI
423062306a36Sopenharmony_ci	napi_enable(&cp->napi);
423162306a36Sopenharmony_ci#endif
423262306a36Sopenharmony_ci	/* init hw */
423362306a36Sopenharmony_ci	cas_lock_all_save(cp, flags);
423462306a36Sopenharmony_ci	cas_clean_rings(cp);
423562306a36Sopenharmony_ci	cas_init_hw(cp, !hw_was_up);
423662306a36Sopenharmony_ci	cp->opened = 1;
423762306a36Sopenharmony_ci	cas_unlock_all_restore(cp, flags);
423862306a36Sopenharmony_ci
423962306a36Sopenharmony_ci	netif_start_queue(dev);
424062306a36Sopenharmony_ci	mutex_unlock(&cp->pm_mutex);
424162306a36Sopenharmony_ci	return 0;
424262306a36Sopenharmony_ci
424362306a36Sopenharmony_cierr_spare:
424462306a36Sopenharmony_ci	cas_spare_free(cp);
424562306a36Sopenharmony_ci	cas_free_rxds(cp);
424662306a36Sopenharmony_cierr_tx_tiny:
424762306a36Sopenharmony_ci	cas_tx_tiny_free(cp);
424862306a36Sopenharmony_cierr_unlock:
424962306a36Sopenharmony_ci	mutex_unlock(&cp->pm_mutex);
425062306a36Sopenharmony_ci	return err;
425162306a36Sopenharmony_ci}
425262306a36Sopenharmony_ci
425362306a36Sopenharmony_cistatic int cas_close(struct net_device *dev)
425462306a36Sopenharmony_ci{
425562306a36Sopenharmony_ci	unsigned long flags;
425662306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
425762306a36Sopenharmony_ci
425862306a36Sopenharmony_ci#ifdef USE_NAPI
425962306a36Sopenharmony_ci	napi_disable(&cp->napi);
426062306a36Sopenharmony_ci#endif
426162306a36Sopenharmony_ci	/* Make sure we don't get distracted by suspend/resume */
426262306a36Sopenharmony_ci	mutex_lock(&cp->pm_mutex);
426362306a36Sopenharmony_ci
426462306a36Sopenharmony_ci	netif_stop_queue(dev);
426562306a36Sopenharmony_ci
426662306a36Sopenharmony_ci	/* Stop traffic, mark us closed */
426762306a36Sopenharmony_ci	cas_lock_all_save(cp, flags);
426862306a36Sopenharmony_ci	cp->opened = 0;
426962306a36Sopenharmony_ci	cas_reset(cp, 0);
427062306a36Sopenharmony_ci	cas_phy_init(cp);
427162306a36Sopenharmony_ci	cas_begin_auto_negotiation(cp, NULL);
427262306a36Sopenharmony_ci	cas_clean_rings(cp);
427362306a36Sopenharmony_ci	cas_unlock_all_restore(cp, flags);
427462306a36Sopenharmony_ci
427562306a36Sopenharmony_ci	free_irq(cp->pdev->irq, (void *) dev);
427662306a36Sopenharmony_ci	cas_spare_free(cp);
427762306a36Sopenharmony_ci	cas_free_rxds(cp);
427862306a36Sopenharmony_ci	cas_tx_tiny_free(cp);
427962306a36Sopenharmony_ci	mutex_unlock(&cp->pm_mutex);
428062306a36Sopenharmony_ci	return 0;
428162306a36Sopenharmony_ci}
428262306a36Sopenharmony_ci
428362306a36Sopenharmony_cistatic struct {
428462306a36Sopenharmony_ci	const char name[ETH_GSTRING_LEN];
428562306a36Sopenharmony_ci} ethtool_cassini_statnames[] = {
428662306a36Sopenharmony_ci	{"collisions"},
428762306a36Sopenharmony_ci	{"rx_bytes"},
428862306a36Sopenharmony_ci	{"rx_crc_errors"},
428962306a36Sopenharmony_ci	{"rx_dropped"},
429062306a36Sopenharmony_ci	{"rx_errors"},
429162306a36Sopenharmony_ci	{"rx_fifo_errors"},
429262306a36Sopenharmony_ci	{"rx_frame_errors"},
429362306a36Sopenharmony_ci	{"rx_length_errors"},
429462306a36Sopenharmony_ci	{"rx_over_errors"},
429562306a36Sopenharmony_ci	{"rx_packets"},
429662306a36Sopenharmony_ci	{"tx_aborted_errors"},
429762306a36Sopenharmony_ci	{"tx_bytes"},
429862306a36Sopenharmony_ci	{"tx_dropped"},
429962306a36Sopenharmony_ci	{"tx_errors"},
430062306a36Sopenharmony_ci	{"tx_fifo_errors"},
430162306a36Sopenharmony_ci	{"tx_packets"}
430262306a36Sopenharmony_ci};
430362306a36Sopenharmony_ci#define CAS_NUM_STAT_KEYS ARRAY_SIZE(ethtool_cassini_statnames)
430462306a36Sopenharmony_ci
430562306a36Sopenharmony_cistatic struct {
430662306a36Sopenharmony_ci	const int offsets;	/* neg. values for 2nd arg to cas_read_phy */
430762306a36Sopenharmony_ci} ethtool_register_table[] = {
430862306a36Sopenharmony_ci	{-MII_BMSR},
430962306a36Sopenharmony_ci	{-MII_BMCR},
431062306a36Sopenharmony_ci	{REG_CAWR},
431162306a36Sopenharmony_ci	{REG_INF_BURST},
431262306a36Sopenharmony_ci	{REG_BIM_CFG},
431362306a36Sopenharmony_ci	{REG_RX_CFG},
431462306a36Sopenharmony_ci	{REG_HP_CFG},
431562306a36Sopenharmony_ci	{REG_MAC_TX_CFG},
431662306a36Sopenharmony_ci	{REG_MAC_RX_CFG},
431762306a36Sopenharmony_ci	{REG_MAC_CTRL_CFG},
431862306a36Sopenharmony_ci	{REG_MAC_XIF_CFG},
431962306a36Sopenharmony_ci	{REG_MIF_CFG},
432062306a36Sopenharmony_ci	{REG_PCS_CFG},
432162306a36Sopenharmony_ci	{REG_SATURN_PCFG},
432262306a36Sopenharmony_ci	{REG_PCS_MII_STATUS},
432362306a36Sopenharmony_ci	{REG_PCS_STATE_MACHINE},
432462306a36Sopenharmony_ci	{REG_MAC_COLL_EXCESS},
432562306a36Sopenharmony_ci	{REG_MAC_COLL_LATE}
432662306a36Sopenharmony_ci};
432762306a36Sopenharmony_ci#define CAS_REG_LEN 	ARRAY_SIZE(ethtool_register_table)
432862306a36Sopenharmony_ci#define CAS_MAX_REGS 	(sizeof (u32)*CAS_REG_LEN)
432962306a36Sopenharmony_ci
433062306a36Sopenharmony_cistatic void cas_read_regs(struct cas *cp, u8 *ptr, int len)
433162306a36Sopenharmony_ci{
433262306a36Sopenharmony_ci	u8 *p;
433362306a36Sopenharmony_ci	int i;
433462306a36Sopenharmony_ci	unsigned long flags;
433562306a36Sopenharmony_ci
433662306a36Sopenharmony_ci	spin_lock_irqsave(&cp->lock, flags);
433762306a36Sopenharmony_ci	for (i = 0, p = ptr; i < len ; i ++, p += sizeof(u32)) {
433862306a36Sopenharmony_ci		u16 hval;
433962306a36Sopenharmony_ci		u32 val;
434062306a36Sopenharmony_ci		if (ethtool_register_table[i].offsets < 0) {
434162306a36Sopenharmony_ci			hval = cas_phy_read(cp,
434262306a36Sopenharmony_ci				    -ethtool_register_table[i].offsets);
434362306a36Sopenharmony_ci			val = hval;
434462306a36Sopenharmony_ci		} else {
434562306a36Sopenharmony_ci			val= readl(cp->regs+ethtool_register_table[i].offsets);
434662306a36Sopenharmony_ci		}
434762306a36Sopenharmony_ci		memcpy(p, (u8 *)&val, sizeof(u32));
434862306a36Sopenharmony_ci	}
434962306a36Sopenharmony_ci	spin_unlock_irqrestore(&cp->lock, flags);
435062306a36Sopenharmony_ci}
435162306a36Sopenharmony_ci
435262306a36Sopenharmony_cistatic struct net_device_stats *cas_get_stats(struct net_device *dev)
435362306a36Sopenharmony_ci{
435462306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
435562306a36Sopenharmony_ci	struct net_device_stats *stats = cp->net_stats;
435662306a36Sopenharmony_ci	unsigned long flags;
435762306a36Sopenharmony_ci	int i;
435862306a36Sopenharmony_ci	unsigned long tmp;
435962306a36Sopenharmony_ci
436062306a36Sopenharmony_ci	/* we collate all of the stats into net_stats[N_TX_RING] */
436162306a36Sopenharmony_ci	if (!cp->hw_running)
436262306a36Sopenharmony_ci		return stats + N_TX_RINGS;
436362306a36Sopenharmony_ci
436462306a36Sopenharmony_ci	/* collect outstanding stats */
436562306a36Sopenharmony_ci	/* WTZ: the Cassini spec gives these as 16 bit counters but
436662306a36Sopenharmony_ci	 * stored in 32-bit words.  Added a mask of 0xffff to be safe,
436762306a36Sopenharmony_ci	 * in case the chip somehow puts any garbage in the other bits.
436862306a36Sopenharmony_ci	 * Also, counter usage didn't seem to mach what Adrian did
436962306a36Sopenharmony_ci	 * in the parts of the code that set these quantities. Made
437062306a36Sopenharmony_ci	 * that consistent.
437162306a36Sopenharmony_ci	 */
437262306a36Sopenharmony_ci	spin_lock_irqsave(&cp->stat_lock[N_TX_RINGS], flags);
437362306a36Sopenharmony_ci	stats[N_TX_RINGS].rx_crc_errors +=
437462306a36Sopenharmony_ci	  readl(cp->regs + REG_MAC_FCS_ERR) & 0xffff;
437562306a36Sopenharmony_ci	stats[N_TX_RINGS].rx_frame_errors +=
437662306a36Sopenharmony_ci		readl(cp->regs + REG_MAC_ALIGN_ERR) &0xffff;
437762306a36Sopenharmony_ci	stats[N_TX_RINGS].rx_length_errors +=
437862306a36Sopenharmony_ci		readl(cp->regs + REG_MAC_LEN_ERR) & 0xffff;
437962306a36Sopenharmony_ci#if 1
438062306a36Sopenharmony_ci	tmp = (readl(cp->regs + REG_MAC_COLL_EXCESS) & 0xffff) +
438162306a36Sopenharmony_ci		(readl(cp->regs + REG_MAC_COLL_LATE) & 0xffff);
438262306a36Sopenharmony_ci	stats[N_TX_RINGS].tx_aborted_errors += tmp;
438362306a36Sopenharmony_ci	stats[N_TX_RINGS].collisions +=
438462306a36Sopenharmony_ci	  tmp + (readl(cp->regs + REG_MAC_COLL_NORMAL) & 0xffff);
438562306a36Sopenharmony_ci#else
438662306a36Sopenharmony_ci	stats[N_TX_RINGS].tx_aborted_errors +=
438762306a36Sopenharmony_ci		readl(cp->regs + REG_MAC_COLL_EXCESS);
438862306a36Sopenharmony_ci	stats[N_TX_RINGS].collisions += readl(cp->regs + REG_MAC_COLL_EXCESS) +
438962306a36Sopenharmony_ci		readl(cp->regs + REG_MAC_COLL_LATE);
439062306a36Sopenharmony_ci#endif
439162306a36Sopenharmony_ci	cas_clear_mac_err(cp);
439262306a36Sopenharmony_ci
439362306a36Sopenharmony_ci	/* saved bits that are unique to ring 0 */
439462306a36Sopenharmony_ci	spin_lock(&cp->stat_lock[0]);
439562306a36Sopenharmony_ci	stats[N_TX_RINGS].collisions        += stats[0].collisions;
439662306a36Sopenharmony_ci	stats[N_TX_RINGS].rx_over_errors    += stats[0].rx_over_errors;
439762306a36Sopenharmony_ci	stats[N_TX_RINGS].rx_frame_errors   += stats[0].rx_frame_errors;
439862306a36Sopenharmony_ci	stats[N_TX_RINGS].rx_fifo_errors    += stats[0].rx_fifo_errors;
439962306a36Sopenharmony_ci	stats[N_TX_RINGS].tx_aborted_errors += stats[0].tx_aborted_errors;
440062306a36Sopenharmony_ci	stats[N_TX_RINGS].tx_fifo_errors    += stats[0].tx_fifo_errors;
440162306a36Sopenharmony_ci	spin_unlock(&cp->stat_lock[0]);
440262306a36Sopenharmony_ci
440362306a36Sopenharmony_ci	for (i = 0; i < N_TX_RINGS; i++) {
440462306a36Sopenharmony_ci		spin_lock(&cp->stat_lock[i]);
440562306a36Sopenharmony_ci		stats[N_TX_RINGS].rx_length_errors +=
440662306a36Sopenharmony_ci			stats[i].rx_length_errors;
440762306a36Sopenharmony_ci		stats[N_TX_RINGS].rx_crc_errors += stats[i].rx_crc_errors;
440862306a36Sopenharmony_ci		stats[N_TX_RINGS].rx_packets    += stats[i].rx_packets;
440962306a36Sopenharmony_ci		stats[N_TX_RINGS].tx_packets    += stats[i].tx_packets;
441062306a36Sopenharmony_ci		stats[N_TX_RINGS].rx_bytes      += stats[i].rx_bytes;
441162306a36Sopenharmony_ci		stats[N_TX_RINGS].tx_bytes      += stats[i].tx_bytes;
441262306a36Sopenharmony_ci		stats[N_TX_RINGS].rx_errors     += stats[i].rx_errors;
441362306a36Sopenharmony_ci		stats[N_TX_RINGS].tx_errors     += stats[i].tx_errors;
441462306a36Sopenharmony_ci		stats[N_TX_RINGS].rx_dropped    += stats[i].rx_dropped;
441562306a36Sopenharmony_ci		stats[N_TX_RINGS].tx_dropped    += stats[i].tx_dropped;
441662306a36Sopenharmony_ci		memset(stats + i, 0, sizeof(struct net_device_stats));
441762306a36Sopenharmony_ci		spin_unlock(&cp->stat_lock[i]);
441862306a36Sopenharmony_ci	}
441962306a36Sopenharmony_ci	spin_unlock_irqrestore(&cp->stat_lock[N_TX_RINGS], flags);
442062306a36Sopenharmony_ci	return stats + N_TX_RINGS;
442162306a36Sopenharmony_ci}
442262306a36Sopenharmony_ci
442362306a36Sopenharmony_ci
442462306a36Sopenharmony_cistatic void cas_set_multicast(struct net_device *dev)
442562306a36Sopenharmony_ci{
442662306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
442762306a36Sopenharmony_ci	u32 rxcfg, rxcfg_new;
442862306a36Sopenharmony_ci	unsigned long flags;
442962306a36Sopenharmony_ci	int limit = STOP_TRIES;
443062306a36Sopenharmony_ci
443162306a36Sopenharmony_ci	if (!cp->hw_running)
443262306a36Sopenharmony_ci		return;
443362306a36Sopenharmony_ci
443462306a36Sopenharmony_ci	spin_lock_irqsave(&cp->lock, flags);
443562306a36Sopenharmony_ci	rxcfg = readl(cp->regs + REG_MAC_RX_CFG);
443662306a36Sopenharmony_ci
443762306a36Sopenharmony_ci	/* disable RX MAC and wait for completion */
443862306a36Sopenharmony_ci	writel(rxcfg & ~MAC_RX_CFG_EN, cp->regs + REG_MAC_RX_CFG);
443962306a36Sopenharmony_ci	while (readl(cp->regs + REG_MAC_RX_CFG) & MAC_RX_CFG_EN) {
444062306a36Sopenharmony_ci		if (!limit--)
444162306a36Sopenharmony_ci			break;
444262306a36Sopenharmony_ci		udelay(10);
444362306a36Sopenharmony_ci	}
444462306a36Sopenharmony_ci
444562306a36Sopenharmony_ci	/* disable hash filter and wait for completion */
444662306a36Sopenharmony_ci	limit = STOP_TRIES;
444762306a36Sopenharmony_ci	rxcfg &= ~(MAC_RX_CFG_PROMISC_EN | MAC_RX_CFG_HASH_FILTER_EN);
444862306a36Sopenharmony_ci	writel(rxcfg & ~MAC_RX_CFG_EN, cp->regs + REG_MAC_RX_CFG);
444962306a36Sopenharmony_ci	while (readl(cp->regs + REG_MAC_RX_CFG) & MAC_RX_CFG_HASH_FILTER_EN) {
445062306a36Sopenharmony_ci		if (!limit--)
445162306a36Sopenharmony_ci			break;
445262306a36Sopenharmony_ci		udelay(10);
445362306a36Sopenharmony_ci	}
445462306a36Sopenharmony_ci
445562306a36Sopenharmony_ci	/* program hash filters */
445662306a36Sopenharmony_ci	cp->mac_rx_cfg = rxcfg_new = cas_setup_multicast(cp);
445762306a36Sopenharmony_ci	rxcfg |= rxcfg_new;
445862306a36Sopenharmony_ci	writel(rxcfg, cp->regs + REG_MAC_RX_CFG);
445962306a36Sopenharmony_ci	spin_unlock_irqrestore(&cp->lock, flags);
446062306a36Sopenharmony_ci}
446162306a36Sopenharmony_ci
446262306a36Sopenharmony_cistatic void cas_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
446362306a36Sopenharmony_ci{
446462306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
446562306a36Sopenharmony_ci	strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
446662306a36Sopenharmony_ci	strscpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
446762306a36Sopenharmony_ci	strscpy(info->bus_info, pci_name(cp->pdev), sizeof(info->bus_info));
446862306a36Sopenharmony_ci}
446962306a36Sopenharmony_ci
447062306a36Sopenharmony_cistatic int cas_get_link_ksettings(struct net_device *dev,
447162306a36Sopenharmony_ci				  struct ethtool_link_ksettings *cmd)
447262306a36Sopenharmony_ci{
447362306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
447462306a36Sopenharmony_ci	u16 bmcr;
447562306a36Sopenharmony_ci	int full_duplex, speed, pause;
447662306a36Sopenharmony_ci	unsigned long flags;
447762306a36Sopenharmony_ci	enum link_state linkstate = link_up;
447862306a36Sopenharmony_ci	u32 supported, advertising;
447962306a36Sopenharmony_ci
448062306a36Sopenharmony_ci	advertising = 0;
448162306a36Sopenharmony_ci	supported = SUPPORTED_Autoneg;
448262306a36Sopenharmony_ci	if (cp->cas_flags & CAS_FLAG_1000MB_CAP) {
448362306a36Sopenharmony_ci		supported |= SUPPORTED_1000baseT_Full;
448462306a36Sopenharmony_ci		advertising |= ADVERTISED_1000baseT_Full;
448562306a36Sopenharmony_ci	}
448662306a36Sopenharmony_ci
448762306a36Sopenharmony_ci	/* Record PHY settings if HW is on. */
448862306a36Sopenharmony_ci	spin_lock_irqsave(&cp->lock, flags);
448962306a36Sopenharmony_ci	bmcr = 0;
449062306a36Sopenharmony_ci	linkstate = cp->lstate;
449162306a36Sopenharmony_ci	if (CAS_PHY_MII(cp->phy_type)) {
449262306a36Sopenharmony_ci		cmd->base.port = PORT_MII;
449362306a36Sopenharmony_ci		cmd->base.phy_address = cp->phy_addr;
449462306a36Sopenharmony_ci		advertising |= ADVERTISED_TP | ADVERTISED_MII |
449562306a36Sopenharmony_ci			ADVERTISED_10baseT_Half |
449662306a36Sopenharmony_ci			ADVERTISED_10baseT_Full |
449762306a36Sopenharmony_ci			ADVERTISED_100baseT_Half |
449862306a36Sopenharmony_ci			ADVERTISED_100baseT_Full;
449962306a36Sopenharmony_ci
450062306a36Sopenharmony_ci		supported |=
450162306a36Sopenharmony_ci			(SUPPORTED_10baseT_Half |
450262306a36Sopenharmony_ci			 SUPPORTED_10baseT_Full |
450362306a36Sopenharmony_ci			 SUPPORTED_100baseT_Half |
450462306a36Sopenharmony_ci			 SUPPORTED_100baseT_Full |
450562306a36Sopenharmony_ci			 SUPPORTED_TP | SUPPORTED_MII);
450662306a36Sopenharmony_ci
450762306a36Sopenharmony_ci		if (cp->hw_running) {
450862306a36Sopenharmony_ci			cas_mif_poll(cp, 0);
450962306a36Sopenharmony_ci			bmcr = cas_phy_read(cp, MII_BMCR);
451062306a36Sopenharmony_ci			cas_read_mii_link_mode(cp, &full_duplex,
451162306a36Sopenharmony_ci					       &speed, &pause);
451262306a36Sopenharmony_ci			cas_mif_poll(cp, 1);
451362306a36Sopenharmony_ci		}
451462306a36Sopenharmony_ci
451562306a36Sopenharmony_ci	} else {
451662306a36Sopenharmony_ci		cmd->base.port = PORT_FIBRE;
451762306a36Sopenharmony_ci		cmd->base.phy_address = 0;
451862306a36Sopenharmony_ci		supported   |= SUPPORTED_FIBRE;
451962306a36Sopenharmony_ci		advertising |= ADVERTISED_FIBRE;
452062306a36Sopenharmony_ci
452162306a36Sopenharmony_ci		if (cp->hw_running) {
452262306a36Sopenharmony_ci			/* pcs uses the same bits as mii */
452362306a36Sopenharmony_ci			bmcr = readl(cp->regs + REG_PCS_MII_CTRL);
452462306a36Sopenharmony_ci			cas_read_pcs_link_mode(cp, &full_duplex,
452562306a36Sopenharmony_ci					       &speed, &pause);
452662306a36Sopenharmony_ci		}
452762306a36Sopenharmony_ci	}
452862306a36Sopenharmony_ci	spin_unlock_irqrestore(&cp->lock, flags);
452962306a36Sopenharmony_ci
453062306a36Sopenharmony_ci	if (bmcr & BMCR_ANENABLE) {
453162306a36Sopenharmony_ci		advertising |= ADVERTISED_Autoneg;
453262306a36Sopenharmony_ci		cmd->base.autoneg = AUTONEG_ENABLE;
453362306a36Sopenharmony_ci		cmd->base.speed =  ((speed == 10) ?
453462306a36Sopenharmony_ci					    SPEED_10 :
453562306a36Sopenharmony_ci					    ((speed == 1000) ?
453662306a36Sopenharmony_ci					     SPEED_1000 : SPEED_100));
453762306a36Sopenharmony_ci		cmd->base.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
453862306a36Sopenharmony_ci	} else {
453962306a36Sopenharmony_ci		cmd->base.autoneg = AUTONEG_DISABLE;
454062306a36Sopenharmony_ci		cmd->base.speed = ((bmcr & CAS_BMCR_SPEED1000) ?
454162306a36Sopenharmony_ci					    SPEED_1000 :
454262306a36Sopenharmony_ci					    ((bmcr & BMCR_SPEED100) ?
454362306a36Sopenharmony_ci					     SPEED_100 : SPEED_10));
454462306a36Sopenharmony_ci		cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ?
454562306a36Sopenharmony_ci			DUPLEX_FULL : DUPLEX_HALF;
454662306a36Sopenharmony_ci	}
454762306a36Sopenharmony_ci	if (linkstate != link_up) {
454862306a36Sopenharmony_ci		/* Force these to "unknown" if the link is not up and
454962306a36Sopenharmony_ci		 * autonogotiation in enabled. We can set the link
455062306a36Sopenharmony_ci		 * speed to 0, but not cmd->duplex,
455162306a36Sopenharmony_ci		 * because its legal values are 0 and 1.  Ethtool will
455262306a36Sopenharmony_ci		 * print the value reported in parentheses after the
455362306a36Sopenharmony_ci		 * word "Unknown" for unrecognized values.
455462306a36Sopenharmony_ci		 *
455562306a36Sopenharmony_ci		 * If in forced mode, we report the speed and duplex
455662306a36Sopenharmony_ci		 * settings that we configured.
455762306a36Sopenharmony_ci		 */
455862306a36Sopenharmony_ci		if (cp->link_cntl & BMCR_ANENABLE) {
455962306a36Sopenharmony_ci			cmd->base.speed = 0;
456062306a36Sopenharmony_ci			cmd->base.duplex = 0xff;
456162306a36Sopenharmony_ci		} else {
456262306a36Sopenharmony_ci			cmd->base.speed = SPEED_10;
456362306a36Sopenharmony_ci			if (cp->link_cntl & BMCR_SPEED100) {
456462306a36Sopenharmony_ci				cmd->base.speed = SPEED_100;
456562306a36Sopenharmony_ci			} else if (cp->link_cntl & CAS_BMCR_SPEED1000) {
456662306a36Sopenharmony_ci				cmd->base.speed = SPEED_1000;
456762306a36Sopenharmony_ci			}
456862306a36Sopenharmony_ci			cmd->base.duplex = (cp->link_cntl & BMCR_FULLDPLX) ?
456962306a36Sopenharmony_ci				DUPLEX_FULL : DUPLEX_HALF;
457062306a36Sopenharmony_ci		}
457162306a36Sopenharmony_ci	}
457262306a36Sopenharmony_ci
457362306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
457462306a36Sopenharmony_ci						supported);
457562306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
457662306a36Sopenharmony_ci						advertising);
457762306a36Sopenharmony_ci
457862306a36Sopenharmony_ci	return 0;
457962306a36Sopenharmony_ci}
458062306a36Sopenharmony_ci
458162306a36Sopenharmony_cistatic int cas_set_link_ksettings(struct net_device *dev,
458262306a36Sopenharmony_ci				  const struct ethtool_link_ksettings *cmd)
458362306a36Sopenharmony_ci{
458462306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
458562306a36Sopenharmony_ci	unsigned long flags;
458662306a36Sopenharmony_ci	u32 speed = cmd->base.speed;
458762306a36Sopenharmony_ci
458862306a36Sopenharmony_ci	/* Verify the settings we care about. */
458962306a36Sopenharmony_ci	if (cmd->base.autoneg != AUTONEG_ENABLE &&
459062306a36Sopenharmony_ci	    cmd->base.autoneg != AUTONEG_DISABLE)
459162306a36Sopenharmony_ci		return -EINVAL;
459262306a36Sopenharmony_ci
459362306a36Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_DISABLE &&
459462306a36Sopenharmony_ci	    ((speed != SPEED_1000 &&
459562306a36Sopenharmony_ci	      speed != SPEED_100 &&
459662306a36Sopenharmony_ci	      speed != SPEED_10) ||
459762306a36Sopenharmony_ci	     (cmd->base.duplex != DUPLEX_HALF &&
459862306a36Sopenharmony_ci	      cmd->base.duplex != DUPLEX_FULL)))
459962306a36Sopenharmony_ci		return -EINVAL;
460062306a36Sopenharmony_ci
460162306a36Sopenharmony_ci	/* Apply settings and restart link process. */
460262306a36Sopenharmony_ci	spin_lock_irqsave(&cp->lock, flags);
460362306a36Sopenharmony_ci	cas_begin_auto_negotiation(cp, cmd);
460462306a36Sopenharmony_ci	spin_unlock_irqrestore(&cp->lock, flags);
460562306a36Sopenharmony_ci	return 0;
460662306a36Sopenharmony_ci}
460762306a36Sopenharmony_ci
460862306a36Sopenharmony_cistatic int cas_nway_reset(struct net_device *dev)
460962306a36Sopenharmony_ci{
461062306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
461162306a36Sopenharmony_ci	unsigned long flags;
461262306a36Sopenharmony_ci
461362306a36Sopenharmony_ci	if ((cp->link_cntl & BMCR_ANENABLE) == 0)
461462306a36Sopenharmony_ci		return -EINVAL;
461562306a36Sopenharmony_ci
461662306a36Sopenharmony_ci	/* Restart link process. */
461762306a36Sopenharmony_ci	spin_lock_irqsave(&cp->lock, flags);
461862306a36Sopenharmony_ci	cas_begin_auto_negotiation(cp, NULL);
461962306a36Sopenharmony_ci	spin_unlock_irqrestore(&cp->lock, flags);
462062306a36Sopenharmony_ci
462162306a36Sopenharmony_ci	return 0;
462262306a36Sopenharmony_ci}
462362306a36Sopenharmony_ci
462462306a36Sopenharmony_cistatic u32 cas_get_link(struct net_device *dev)
462562306a36Sopenharmony_ci{
462662306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
462762306a36Sopenharmony_ci	return cp->lstate == link_up;
462862306a36Sopenharmony_ci}
462962306a36Sopenharmony_ci
463062306a36Sopenharmony_cistatic u32 cas_get_msglevel(struct net_device *dev)
463162306a36Sopenharmony_ci{
463262306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
463362306a36Sopenharmony_ci	return cp->msg_enable;
463462306a36Sopenharmony_ci}
463562306a36Sopenharmony_ci
463662306a36Sopenharmony_cistatic void cas_set_msglevel(struct net_device *dev, u32 value)
463762306a36Sopenharmony_ci{
463862306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
463962306a36Sopenharmony_ci	cp->msg_enable = value;
464062306a36Sopenharmony_ci}
464162306a36Sopenharmony_ci
464262306a36Sopenharmony_cistatic int cas_get_regs_len(struct net_device *dev)
464362306a36Sopenharmony_ci{
464462306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
464562306a36Sopenharmony_ci	return min_t(int, cp->casreg_len, CAS_MAX_REGS);
464662306a36Sopenharmony_ci}
464762306a36Sopenharmony_ci
464862306a36Sopenharmony_cistatic void cas_get_regs(struct net_device *dev, struct ethtool_regs *regs,
464962306a36Sopenharmony_ci			     void *p)
465062306a36Sopenharmony_ci{
465162306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
465262306a36Sopenharmony_ci	regs->version = 0;
465362306a36Sopenharmony_ci	/* cas_read_regs handles locks (cp->lock).  */
465462306a36Sopenharmony_ci	cas_read_regs(cp, p, regs->len / sizeof(u32));
465562306a36Sopenharmony_ci}
465662306a36Sopenharmony_ci
465762306a36Sopenharmony_cistatic int cas_get_sset_count(struct net_device *dev, int sset)
465862306a36Sopenharmony_ci{
465962306a36Sopenharmony_ci	switch (sset) {
466062306a36Sopenharmony_ci	case ETH_SS_STATS:
466162306a36Sopenharmony_ci		return CAS_NUM_STAT_KEYS;
466262306a36Sopenharmony_ci	default:
466362306a36Sopenharmony_ci		return -EOPNOTSUPP;
466462306a36Sopenharmony_ci	}
466562306a36Sopenharmony_ci}
466662306a36Sopenharmony_ci
466762306a36Sopenharmony_cistatic void cas_get_strings(struct net_device *dev, u32 stringset, u8 *data)
466862306a36Sopenharmony_ci{
466962306a36Sopenharmony_ci	 memcpy(data, &ethtool_cassini_statnames,
467062306a36Sopenharmony_ci					 CAS_NUM_STAT_KEYS * ETH_GSTRING_LEN);
467162306a36Sopenharmony_ci}
467262306a36Sopenharmony_ci
467362306a36Sopenharmony_cistatic void cas_get_ethtool_stats(struct net_device *dev,
467462306a36Sopenharmony_ci				      struct ethtool_stats *estats, u64 *data)
467562306a36Sopenharmony_ci{
467662306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
467762306a36Sopenharmony_ci	struct net_device_stats *stats = cas_get_stats(cp->dev);
467862306a36Sopenharmony_ci	int i = 0;
467962306a36Sopenharmony_ci	data[i++] = stats->collisions;
468062306a36Sopenharmony_ci	data[i++] = stats->rx_bytes;
468162306a36Sopenharmony_ci	data[i++] = stats->rx_crc_errors;
468262306a36Sopenharmony_ci	data[i++] = stats->rx_dropped;
468362306a36Sopenharmony_ci	data[i++] = stats->rx_errors;
468462306a36Sopenharmony_ci	data[i++] = stats->rx_fifo_errors;
468562306a36Sopenharmony_ci	data[i++] = stats->rx_frame_errors;
468662306a36Sopenharmony_ci	data[i++] = stats->rx_length_errors;
468762306a36Sopenharmony_ci	data[i++] = stats->rx_over_errors;
468862306a36Sopenharmony_ci	data[i++] = stats->rx_packets;
468962306a36Sopenharmony_ci	data[i++] = stats->tx_aborted_errors;
469062306a36Sopenharmony_ci	data[i++] = stats->tx_bytes;
469162306a36Sopenharmony_ci	data[i++] = stats->tx_dropped;
469262306a36Sopenharmony_ci	data[i++] = stats->tx_errors;
469362306a36Sopenharmony_ci	data[i++] = stats->tx_fifo_errors;
469462306a36Sopenharmony_ci	data[i++] = stats->tx_packets;
469562306a36Sopenharmony_ci	BUG_ON(i != CAS_NUM_STAT_KEYS);
469662306a36Sopenharmony_ci}
469762306a36Sopenharmony_ci
469862306a36Sopenharmony_cistatic const struct ethtool_ops cas_ethtool_ops = {
469962306a36Sopenharmony_ci	.get_drvinfo		= cas_get_drvinfo,
470062306a36Sopenharmony_ci	.nway_reset		= cas_nway_reset,
470162306a36Sopenharmony_ci	.get_link		= cas_get_link,
470262306a36Sopenharmony_ci	.get_msglevel		= cas_get_msglevel,
470362306a36Sopenharmony_ci	.set_msglevel		= cas_set_msglevel,
470462306a36Sopenharmony_ci	.get_regs_len		= cas_get_regs_len,
470562306a36Sopenharmony_ci	.get_regs		= cas_get_regs,
470662306a36Sopenharmony_ci	.get_sset_count		= cas_get_sset_count,
470762306a36Sopenharmony_ci	.get_strings		= cas_get_strings,
470862306a36Sopenharmony_ci	.get_ethtool_stats	= cas_get_ethtool_stats,
470962306a36Sopenharmony_ci	.get_link_ksettings	= cas_get_link_ksettings,
471062306a36Sopenharmony_ci	.set_link_ksettings	= cas_set_link_ksettings,
471162306a36Sopenharmony_ci};
471262306a36Sopenharmony_ci
471362306a36Sopenharmony_cistatic int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
471462306a36Sopenharmony_ci{
471562306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
471662306a36Sopenharmony_ci	struct mii_ioctl_data *data = if_mii(ifr);
471762306a36Sopenharmony_ci	unsigned long flags;
471862306a36Sopenharmony_ci	int rc = -EOPNOTSUPP;
471962306a36Sopenharmony_ci
472062306a36Sopenharmony_ci	/* Hold the PM mutex while doing ioctl's or we may collide
472162306a36Sopenharmony_ci	 * with open/close and power management and oops.
472262306a36Sopenharmony_ci	 */
472362306a36Sopenharmony_ci	mutex_lock(&cp->pm_mutex);
472462306a36Sopenharmony_ci	switch (cmd) {
472562306a36Sopenharmony_ci	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
472662306a36Sopenharmony_ci		data->phy_id = cp->phy_addr;
472762306a36Sopenharmony_ci		fallthrough;
472862306a36Sopenharmony_ci
472962306a36Sopenharmony_ci	case SIOCGMIIREG:		/* Read MII PHY register. */
473062306a36Sopenharmony_ci		spin_lock_irqsave(&cp->lock, flags);
473162306a36Sopenharmony_ci		cas_mif_poll(cp, 0);
473262306a36Sopenharmony_ci		data->val_out = cas_phy_read(cp, data->reg_num & 0x1f);
473362306a36Sopenharmony_ci		cas_mif_poll(cp, 1);
473462306a36Sopenharmony_ci		spin_unlock_irqrestore(&cp->lock, flags);
473562306a36Sopenharmony_ci		rc = 0;
473662306a36Sopenharmony_ci		break;
473762306a36Sopenharmony_ci
473862306a36Sopenharmony_ci	case SIOCSMIIREG:		/* Write MII PHY register. */
473962306a36Sopenharmony_ci		spin_lock_irqsave(&cp->lock, flags);
474062306a36Sopenharmony_ci		cas_mif_poll(cp, 0);
474162306a36Sopenharmony_ci		rc = cas_phy_write(cp, data->reg_num & 0x1f, data->val_in);
474262306a36Sopenharmony_ci		cas_mif_poll(cp, 1);
474362306a36Sopenharmony_ci		spin_unlock_irqrestore(&cp->lock, flags);
474462306a36Sopenharmony_ci		break;
474562306a36Sopenharmony_ci	default:
474662306a36Sopenharmony_ci		break;
474762306a36Sopenharmony_ci	}
474862306a36Sopenharmony_ci
474962306a36Sopenharmony_ci	mutex_unlock(&cp->pm_mutex);
475062306a36Sopenharmony_ci	return rc;
475162306a36Sopenharmony_ci}
475262306a36Sopenharmony_ci
475362306a36Sopenharmony_ci/* When this chip sits underneath an Intel 31154 bridge, it is the
475462306a36Sopenharmony_ci * only subordinate device and we can tweak the bridge settings to
475562306a36Sopenharmony_ci * reflect that fact.
475662306a36Sopenharmony_ci */
475762306a36Sopenharmony_cistatic void cas_program_bridge(struct pci_dev *cas_pdev)
475862306a36Sopenharmony_ci{
475962306a36Sopenharmony_ci	struct pci_dev *pdev = cas_pdev->bus->self;
476062306a36Sopenharmony_ci	u32 val;
476162306a36Sopenharmony_ci
476262306a36Sopenharmony_ci	if (!pdev)
476362306a36Sopenharmony_ci		return;
476462306a36Sopenharmony_ci
476562306a36Sopenharmony_ci	if (pdev->vendor != 0x8086 || pdev->device != 0x537c)
476662306a36Sopenharmony_ci		return;
476762306a36Sopenharmony_ci
476862306a36Sopenharmony_ci	/* Clear bit 10 (Bus Parking Control) in the Secondary
476962306a36Sopenharmony_ci	 * Arbiter Control/Status Register which lives at offset
477062306a36Sopenharmony_ci	 * 0x41.  Using a 32-bit word read/modify/write at 0x40
477162306a36Sopenharmony_ci	 * is much simpler so that's how we do this.
477262306a36Sopenharmony_ci	 */
477362306a36Sopenharmony_ci	pci_read_config_dword(pdev, 0x40, &val);
477462306a36Sopenharmony_ci	val &= ~0x00040000;
477562306a36Sopenharmony_ci	pci_write_config_dword(pdev, 0x40, val);
477662306a36Sopenharmony_ci
477762306a36Sopenharmony_ci	/* Max out the Multi-Transaction Timer settings since
477862306a36Sopenharmony_ci	 * Cassini is the only device present.
477962306a36Sopenharmony_ci	 *
478062306a36Sopenharmony_ci	 * The register is 16-bit and lives at 0x50.  When the
478162306a36Sopenharmony_ci	 * settings are enabled, it extends the GRANT# signal
478262306a36Sopenharmony_ci	 * for a requestor after a transaction is complete.  This
478362306a36Sopenharmony_ci	 * allows the next request to run without first needing
478462306a36Sopenharmony_ci	 * to negotiate the GRANT# signal back.
478562306a36Sopenharmony_ci	 *
478662306a36Sopenharmony_ci	 * Bits 12:10 define the grant duration:
478762306a36Sopenharmony_ci	 *
478862306a36Sopenharmony_ci	 *	1	--	16 clocks
478962306a36Sopenharmony_ci	 *	2	--	32 clocks
479062306a36Sopenharmony_ci	 *	3	--	64 clocks
479162306a36Sopenharmony_ci	 *	4	--	128 clocks
479262306a36Sopenharmony_ci	 *	5	--	256 clocks
479362306a36Sopenharmony_ci	 *
479462306a36Sopenharmony_ci	 * All other values are illegal.
479562306a36Sopenharmony_ci	 *
479662306a36Sopenharmony_ci	 * Bits 09:00 define which REQ/GNT signal pairs get the
479762306a36Sopenharmony_ci	 * GRANT# signal treatment.  We set them all.
479862306a36Sopenharmony_ci	 */
479962306a36Sopenharmony_ci	pci_write_config_word(pdev, 0x50, (5 << 10) | 0x3ff);
480062306a36Sopenharmony_ci
480162306a36Sopenharmony_ci	/* The Read Prefecth Policy register is 16-bit and sits at
480262306a36Sopenharmony_ci	 * offset 0x52.  It enables a "smart" pre-fetch policy.  We
480362306a36Sopenharmony_ci	 * enable it and max out all of the settings since only one
480462306a36Sopenharmony_ci	 * device is sitting underneath and thus bandwidth sharing is
480562306a36Sopenharmony_ci	 * not an issue.
480662306a36Sopenharmony_ci	 *
480762306a36Sopenharmony_ci	 * The register has several 3 bit fields, which indicates a
480862306a36Sopenharmony_ci	 * multiplier applied to the base amount of prefetching the
480962306a36Sopenharmony_ci	 * chip would do.  These fields are at:
481062306a36Sopenharmony_ci	 *
481162306a36Sopenharmony_ci	 *	15:13	---	ReRead Primary Bus
481262306a36Sopenharmony_ci	 *	12:10	---	FirstRead Primary Bus
481362306a36Sopenharmony_ci	 *	09:07	---	ReRead Secondary Bus
481462306a36Sopenharmony_ci	 *	06:04	---	FirstRead Secondary Bus
481562306a36Sopenharmony_ci	 *
481662306a36Sopenharmony_ci	 * Bits 03:00 control which REQ/GNT pairs the prefetch settings
481762306a36Sopenharmony_ci	 * get enabled on.  Bit 3 is a grouped enabler which controls
481862306a36Sopenharmony_ci	 * all of the REQ/GNT pairs from [8:3].  Bits 2 to 0 control
481962306a36Sopenharmony_ci	 * the individual REQ/GNT pairs [2:0].
482062306a36Sopenharmony_ci	 */
482162306a36Sopenharmony_ci	pci_write_config_word(pdev, 0x52,
482262306a36Sopenharmony_ci			      (0x7 << 13) |
482362306a36Sopenharmony_ci			      (0x7 << 10) |
482462306a36Sopenharmony_ci			      (0x7 <<  7) |
482562306a36Sopenharmony_ci			      (0x7 <<  4) |
482662306a36Sopenharmony_ci			      (0xf <<  0));
482762306a36Sopenharmony_ci
482862306a36Sopenharmony_ci	/* Force cacheline size to 0x8 */
482962306a36Sopenharmony_ci	pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x08);
483062306a36Sopenharmony_ci
483162306a36Sopenharmony_ci	/* Force latency timer to maximum setting so Cassini can
483262306a36Sopenharmony_ci	 * sit on the bus as long as it likes.
483362306a36Sopenharmony_ci	 */
483462306a36Sopenharmony_ci	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xff);
483562306a36Sopenharmony_ci}
483662306a36Sopenharmony_ci
483762306a36Sopenharmony_cistatic const struct net_device_ops cas_netdev_ops = {
483862306a36Sopenharmony_ci	.ndo_open		= cas_open,
483962306a36Sopenharmony_ci	.ndo_stop		= cas_close,
484062306a36Sopenharmony_ci	.ndo_start_xmit		= cas_start_xmit,
484162306a36Sopenharmony_ci	.ndo_get_stats 		= cas_get_stats,
484262306a36Sopenharmony_ci	.ndo_set_rx_mode	= cas_set_multicast,
484362306a36Sopenharmony_ci	.ndo_eth_ioctl		= cas_ioctl,
484462306a36Sopenharmony_ci	.ndo_tx_timeout		= cas_tx_timeout,
484562306a36Sopenharmony_ci	.ndo_change_mtu		= cas_change_mtu,
484662306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
484762306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
484862306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
484962306a36Sopenharmony_ci	.ndo_poll_controller	= cas_netpoll,
485062306a36Sopenharmony_ci#endif
485162306a36Sopenharmony_ci};
485262306a36Sopenharmony_ci
485362306a36Sopenharmony_cistatic int cas_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
485462306a36Sopenharmony_ci{
485562306a36Sopenharmony_ci	static int cas_version_printed = 0;
485662306a36Sopenharmony_ci	unsigned long casreg_len;
485762306a36Sopenharmony_ci	struct net_device *dev;
485862306a36Sopenharmony_ci	struct cas *cp;
485962306a36Sopenharmony_ci	u16 pci_cmd;
486062306a36Sopenharmony_ci	int i, err;
486162306a36Sopenharmony_ci	u8 orig_cacheline_size = 0, cas_cacheline_size = 0;
486262306a36Sopenharmony_ci
486362306a36Sopenharmony_ci	if (cas_version_printed++ == 0)
486462306a36Sopenharmony_ci		pr_info("%s", version);
486562306a36Sopenharmony_ci
486662306a36Sopenharmony_ci	err = pci_enable_device(pdev);
486762306a36Sopenharmony_ci	if (err) {
486862306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n");
486962306a36Sopenharmony_ci		return err;
487062306a36Sopenharmony_ci	}
487162306a36Sopenharmony_ci
487262306a36Sopenharmony_ci	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
487362306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot find proper PCI device "
487462306a36Sopenharmony_ci		       "base address, aborting\n");
487562306a36Sopenharmony_ci		err = -ENODEV;
487662306a36Sopenharmony_ci		goto err_out_disable_pdev;
487762306a36Sopenharmony_ci	}
487862306a36Sopenharmony_ci
487962306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(*cp));
488062306a36Sopenharmony_ci	if (!dev) {
488162306a36Sopenharmony_ci		err = -ENOMEM;
488262306a36Sopenharmony_ci		goto err_out_disable_pdev;
488362306a36Sopenharmony_ci	}
488462306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
488562306a36Sopenharmony_ci
488662306a36Sopenharmony_ci	err = pci_request_regions(pdev, dev->name);
488762306a36Sopenharmony_ci	if (err) {
488862306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting\n");
488962306a36Sopenharmony_ci		goto err_out_free_netdev;
489062306a36Sopenharmony_ci	}
489162306a36Sopenharmony_ci	pci_set_master(pdev);
489262306a36Sopenharmony_ci
489362306a36Sopenharmony_ci	/* we must always turn on parity response or else parity
489462306a36Sopenharmony_ci	 * doesn't get generated properly. disable SERR/PERR as well.
489562306a36Sopenharmony_ci	 * in addition, we want to turn MWI on.
489662306a36Sopenharmony_ci	 */
489762306a36Sopenharmony_ci	pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
489862306a36Sopenharmony_ci	pci_cmd &= ~PCI_COMMAND_SERR;
489962306a36Sopenharmony_ci	pci_cmd |= PCI_COMMAND_PARITY;
490062306a36Sopenharmony_ci	pci_write_config_word(pdev, PCI_COMMAND, pci_cmd);
490162306a36Sopenharmony_ci	if (pci_try_set_mwi(pdev))
490262306a36Sopenharmony_ci		pr_warn("Could not enable MWI for %s\n", pci_name(pdev));
490362306a36Sopenharmony_ci
490462306a36Sopenharmony_ci	cas_program_bridge(pdev);
490562306a36Sopenharmony_ci
490662306a36Sopenharmony_ci	/*
490762306a36Sopenharmony_ci	 * On some architectures, the default cache line size set
490862306a36Sopenharmony_ci	 * by pci_try_set_mwi reduces perforamnce.  We have to increase
490962306a36Sopenharmony_ci	 * it for this case.  To start, we'll print some configuration
491062306a36Sopenharmony_ci	 * data.
491162306a36Sopenharmony_ci	 */
491262306a36Sopenharmony_ci#if 1
491362306a36Sopenharmony_ci	pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE,
491462306a36Sopenharmony_ci			     &orig_cacheline_size);
491562306a36Sopenharmony_ci	if (orig_cacheline_size < CAS_PREF_CACHELINE_SIZE) {
491662306a36Sopenharmony_ci		cas_cacheline_size =
491762306a36Sopenharmony_ci			(CAS_PREF_CACHELINE_SIZE < SMP_CACHE_BYTES) ?
491862306a36Sopenharmony_ci			CAS_PREF_CACHELINE_SIZE : SMP_CACHE_BYTES;
491962306a36Sopenharmony_ci		if (pci_write_config_byte(pdev,
492062306a36Sopenharmony_ci					  PCI_CACHE_LINE_SIZE,
492162306a36Sopenharmony_ci					  cas_cacheline_size)) {
492262306a36Sopenharmony_ci			dev_err(&pdev->dev, "Could not set PCI cache "
492362306a36Sopenharmony_ci			       "line size\n");
492462306a36Sopenharmony_ci			goto err_out_free_res;
492562306a36Sopenharmony_ci		}
492662306a36Sopenharmony_ci	}
492762306a36Sopenharmony_ci#endif
492862306a36Sopenharmony_ci
492962306a36Sopenharmony_ci
493062306a36Sopenharmony_ci	/* Configure DMA attributes. */
493162306a36Sopenharmony_ci	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
493262306a36Sopenharmony_ci	if (err) {
493362306a36Sopenharmony_ci		dev_err(&pdev->dev, "No usable DMA configuration, aborting\n");
493462306a36Sopenharmony_ci		goto err_out_free_res;
493562306a36Sopenharmony_ci	}
493662306a36Sopenharmony_ci
493762306a36Sopenharmony_ci	casreg_len = pci_resource_len(pdev, 0);
493862306a36Sopenharmony_ci
493962306a36Sopenharmony_ci	cp = netdev_priv(dev);
494062306a36Sopenharmony_ci	cp->pdev = pdev;
494162306a36Sopenharmony_ci#if 1
494262306a36Sopenharmony_ci	/* A value of 0 indicates we never explicitly set it */
494362306a36Sopenharmony_ci	cp->orig_cacheline_size = cas_cacheline_size ? orig_cacheline_size: 0;
494462306a36Sopenharmony_ci#endif
494562306a36Sopenharmony_ci	cp->dev = dev;
494662306a36Sopenharmony_ci	cp->msg_enable = (cassini_debug < 0) ? CAS_DEF_MSG_ENABLE :
494762306a36Sopenharmony_ci	  cassini_debug;
494862306a36Sopenharmony_ci
494962306a36Sopenharmony_ci#if defined(CONFIG_SPARC)
495062306a36Sopenharmony_ci	cp->of_node = pci_device_to_OF_node(pdev);
495162306a36Sopenharmony_ci#endif
495262306a36Sopenharmony_ci
495362306a36Sopenharmony_ci	cp->link_transition = LINK_TRANSITION_UNKNOWN;
495462306a36Sopenharmony_ci	cp->link_transition_jiffies_valid = 0;
495562306a36Sopenharmony_ci
495662306a36Sopenharmony_ci	spin_lock_init(&cp->lock);
495762306a36Sopenharmony_ci	spin_lock_init(&cp->rx_inuse_lock);
495862306a36Sopenharmony_ci	spin_lock_init(&cp->rx_spare_lock);
495962306a36Sopenharmony_ci	for (i = 0; i < N_TX_RINGS; i++) {
496062306a36Sopenharmony_ci		spin_lock_init(&cp->stat_lock[i]);
496162306a36Sopenharmony_ci		spin_lock_init(&cp->tx_lock[i]);
496262306a36Sopenharmony_ci	}
496362306a36Sopenharmony_ci	spin_lock_init(&cp->stat_lock[N_TX_RINGS]);
496462306a36Sopenharmony_ci	mutex_init(&cp->pm_mutex);
496562306a36Sopenharmony_ci
496662306a36Sopenharmony_ci	timer_setup(&cp->link_timer, cas_link_timer, 0);
496762306a36Sopenharmony_ci
496862306a36Sopenharmony_ci#if 1
496962306a36Sopenharmony_ci	/* Just in case the implementation of atomic operations
497062306a36Sopenharmony_ci	 * change so that an explicit initialization is necessary.
497162306a36Sopenharmony_ci	 */
497262306a36Sopenharmony_ci	atomic_set(&cp->reset_task_pending, 0);
497362306a36Sopenharmony_ci	atomic_set(&cp->reset_task_pending_all, 0);
497462306a36Sopenharmony_ci	atomic_set(&cp->reset_task_pending_spare, 0);
497562306a36Sopenharmony_ci	atomic_set(&cp->reset_task_pending_mtu, 0);
497662306a36Sopenharmony_ci#endif
497762306a36Sopenharmony_ci	INIT_WORK(&cp->reset_task, cas_reset_task);
497862306a36Sopenharmony_ci
497962306a36Sopenharmony_ci	/* Default link parameters */
498062306a36Sopenharmony_ci	if (link_mode >= 0 && link_mode < 6)
498162306a36Sopenharmony_ci		cp->link_cntl = link_modes[link_mode];
498262306a36Sopenharmony_ci	else
498362306a36Sopenharmony_ci		cp->link_cntl = BMCR_ANENABLE;
498462306a36Sopenharmony_ci	cp->lstate = link_down;
498562306a36Sopenharmony_ci	cp->link_transition = LINK_TRANSITION_LINK_DOWN;
498662306a36Sopenharmony_ci	netif_carrier_off(cp->dev);
498762306a36Sopenharmony_ci	cp->timer_ticks = 0;
498862306a36Sopenharmony_ci
498962306a36Sopenharmony_ci	/* give us access to cassini registers */
499062306a36Sopenharmony_ci	cp->regs = pci_iomap(pdev, 0, casreg_len);
499162306a36Sopenharmony_ci	if (!cp->regs) {
499262306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot map device registers, aborting\n");
499362306a36Sopenharmony_ci		goto err_out_free_res;
499462306a36Sopenharmony_ci	}
499562306a36Sopenharmony_ci	cp->casreg_len = casreg_len;
499662306a36Sopenharmony_ci
499762306a36Sopenharmony_ci	pci_save_state(pdev);
499862306a36Sopenharmony_ci	cas_check_pci_invariants(cp);
499962306a36Sopenharmony_ci	cas_hard_reset(cp);
500062306a36Sopenharmony_ci	cas_reset(cp, 0);
500162306a36Sopenharmony_ci	if (cas_check_invariants(cp))
500262306a36Sopenharmony_ci		goto err_out_iounmap;
500362306a36Sopenharmony_ci	if (cp->cas_flags & CAS_FLAG_SATURN)
500462306a36Sopenharmony_ci		cas_saturn_firmware_init(cp);
500562306a36Sopenharmony_ci
500662306a36Sopenharmony_ci	cp->init_block =
500762306a36Sopenharmony_ci		dma_alloc_coherent(&pdev->dev, sizeof(struct cas_init_block),
500862306a36Sopenharmony_ci				   &cp->block_dvma, GFP_KERNEL);
500962306a36Sopenharmony_ci	if (!cp->init_block) {
501062306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot allocate init block, aborting\n");
501162306a36Sopenharmony_ci		goto err_out_iounmap;
501262306a36Sopenharmony_ci	}
501362306a36Sopenharmony_ci
501462306a36Sopenharmony_ci	for (i = 0; i < N_TX_RINGS; i++)
501562306a36Sopenharmony_ci		cp->init_txds[i] = cp->init_block->txds[i];
501662306a36Sopenharmony_ci
501762306a36Sopenharmony_ci	for (i = 0; i < N_RX_DESC_RINGS; i++)
501862306a36Sopenharmony_ci		cp->init_rxds[i] = cp->init_block->rxds[i];
501962306a36Sopenharmony_ci
502062306a36Sopenharmony_ci	for (i = 0; i < N_RX_COMP_RINGS; i++)
502162306a36Sopenharmony_ci		cp->init_rxcs[i] = cp->init_block->rxcs[i];
502262306a36Sopenharmony_ci
502362306a36Sopenharmony_ci	for (i = 0; i < N_RX_FLOWS; i++)
502462306a36Sopenharmony_ci		skb_queue_head_init(&cp->rx_flows[i]);
502562306a36Sopenharmony_ci
502662306a36Sopenharmony_ci	dev->netdev_ops = &cas_netdev_ops;
502762306a36Sopenharmony_ci	dev->ethtool_ops = &cas_ethtool_ops;
502862306a36Sopenharmony_ci	dev->watchdog_timeo = CAS_TX_TIMEOUT;
502962306a36Sopenharmony_ci
503062306a36Sopenharmony_ci#ifdef USE_NAPI
503162306a36Sopenharmony_ci	netif_napi_add(dev, &cp->napi, cas_poll);
503262306a36Sopenharmony_ci#endif
503362306a36Sopenharmony_ci	dev->irq = pdev->irq;
503462306a36Sopenharmony_ci	dev->dma = 0;
503562306a36Sopenharmony_ci
503662306a36Sopenharmony_ci	/* Cassini features. */
503762306a36Sopenharmony_ci	if ((cp->cas_flags & CAS_FLAG_NO_HW_CSUM) == 0)
503862306a36Sopenharmony_ci		dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG;
503962306a36Sopenharmony_ci
504062306a36Sopenharmony_ci	dev->features |= NETIF_F_HIGHDMA;
504162306a36Sopenharmony_ci
504262306a36Sopenharmony_ci	/* MTU range: 60 - varies or 9000 */
504362306a36Sopenharmony_ci	dev->min_mtu = CAS_MIN_MTU;
504462306a36Sopenharmony_ci	dev->max_mtu = CAS_MAX_MTU;
504562306a36Sopenharmony_ci
504662306a36Sopenharmony_ci	if (register_netdev(dev)) {
504762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Cannot register net device, aborting\n");
504862306a36Sopenharmony_ci		goto err_out_free_consistent;
504962306a36Sopenharmony_ci	}
505062306a36Sopenharmony_ci
505162306a36Sopenharmony_ci	i = readl(cp->regs + REG_BIM_CFG);
505262306a36Sopenharmony_ci	netdev_info(dev, "Sun Cassini%s (%sbit/%sMHz PCI/%s) Ethernet[%d] %pM\n",
505362306a36Sopenharmony_ci		    (cp->cas_flags & CAS_FLAG_REG_PLUS) ? "+" : "",
505462306a36Sopenharmony_ci		    (i & BIM_CFG_32BIT) ? "32" : "64",
505562306a36Sopenharmony_ci		    (i & BIM_CFG_66MHZ) ? "66" : "33",
505662306a36Sopenharmony_ci		    (cp->phy_type == CAS_PHY_SERDES) ? "Fi" : "Cu", pdev->irq,
505762306a36Sopenharmony_ci		    dev->dev_addr);
505862306a36Sopenharmony_ci
505962306a36Sopenharmony_ci	pci_set_drvdata(pdev, dev);
506062306a36Sopenharmony_ci	cp->hw_running = 1;
506162306a36Sopenharmony_ci	cas_entropy_reset(cp);
506262306a36Sopenharmony_ci	cas_phy_init(cp);
506362306a36Sopenharmony_ci	cas_begin_auto_negotiation(cp, NULL);
506462306a36Sopenharmony_ci	return 0;
506562306a36Sopenharmony_ci
506662306a36Sopenharmony_cierr_out_free_consistent:
506762306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, sizeof(struct cas_init_block),
506862306a36Sopenharmony_ci			  cp->init_block, cp->block_dvma);
506962306a36Sopenharmony_ci
507062306a36Sopenharmony_cierr_out_iounmap:
507162306a36Sopenharmony_ci	mutex_lock(&cp->pm_mutex);
507262306a36Sopenharmony_ci	if (cp->hw_running)
507362306a36Sopenharmony_ci		cas_shutdown(cp);
507462306a36Sopenharmony_ci	mutex_unlock(&cp->pm_mutex);
507562306a36Sopenharmony_ci
507662306a36Sopenharmony_ci	vfree(cp->fw_data);
507762306a36Sopenharmony_ci
507862306a36Sopenharmony_ci	pci_iounmap(pdev, cp->regs);
507962306a36Sopenharmony_ci
508062306a36Sopenharmony_ci
508162306a36Sopenharmony_cierr_out_free_res:
508262306a36Sopenharmony_ci	pci_release_regions(pdev);
508362306a36Sopenharmony_ci
508462306a36Sopenharmony_ci	/* Try to restore it in case the error occurred after we
508562306a36Sopenharmony_ci	 * set it.
508662306a36Sopenharmony_ci	 */
508762306a36Sopenharmony_ci	pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, orig_cacheline_size);
508862306a36Sopenharmony_ci
508962306a36Sopenharmony_cierr_out_free_netdev:
509062306a36Sopenharmony_ci	free_netdev(dev);
509162306a36Sopenharmony_ci
509262306a36Sopenharmony_cierr_out_disable_pdev:
509362306a36Sopenharmony_ci	pci_disable_device(pdev);
509462306a36Sopenharmony_ci	return -ENODEV;
509562306a36Sopenharmony_ci}
509662306a36Sopenharmony_ci
509762306a36Sopenharmony_cistatic void cas_remove_one(struct pci_dev *pdev)
509862306a36Sopenharmony_ci{
509962306a36Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
510062306a36Sopenharmony_ci	struct cas *cp;
510162306a36Sopenharmony_ci	if (!dev)
510262306a36Sopenharmony_ci		return;
510362306a36Sopenharmony_ci
510462306a36Sopenharmony_ci	cp = netdev_priv(dev);
510562306a36Sopenharmony_ci	unregister_netdev(dev);
510662306a36Sopenharmony_ci
510762306a36Sopenharmony_ci	vfree(cp->fw_data);
510862306a36Sopenharmony_ci
510962306a36Sopenharmony_ci	mutex_lock(&cp->pm_mutex);
511062306a36Sopenharmony_ci	cancel_work_sync(&cp->reset_task);
511162306a36Sopenharmony_ci	if (cp->hw_running)
511262306a36Sopenharmony_ci		cas_shutdown(cp);
511362306a36Sopenharmony_ci	mutex_unlock(&cp->pm_mutex);
511462306a36Sopenharmony_ci
511562306a36Sopenharmony_ci#if 1
511662306a36Sopenharmony_ci	if (cp->orig_cacheline_size) {
511762306a36Sopenharmony_ci		/* Restore the cache line size if we had modified
511862306a36Sopenharmony_ci		 * it.
511962306a36Sopenharmony_ci		 */
512062306a36Sopenharmony_ci		pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE,
512162306a36Sopenharmony_ci				      cp->orig_cacheline_size);
512262306a36Sopenharmony_ci	}
512362306a36Sopenharmony_ci#endif
512462306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, sizeof(struct cas_init_block),
512562306a36Sopenharmony_ci			  cp->init_block, cp->block_dvma);
512662306a36Sopenharmony_ci	pci_iounmap(pdev, cp->regs);
512762306a36Sopenharmony_ci	free_netdev(dev);
512862306a36Sopenharmony_ci	pci_release_regions(pdev);
512962306a36Sopenharmony_ci	pci_disable_device(pdev);
513062306a36Sopenharmony_ci}
513162306a36Sopenharmony_ci
513262306a36Sopenharmony_cistatic int __maybe_unused cas_suspend(struct device *dev_d)
513362306a36Sopenharmony_ci{
513462306a36Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(dev_d);
513562306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
513662306a36Sopenharmony_ci	unsigned long flags;
513762306a36Sopenharmony_ci
513862306a36Sopenharmony_ci	mutex_lock(&cp->pm_mutex);
513962306a36Sopenharmony_ci
514062306a36Sopenharmony_ci	/* If the driver is opened, we stop the DMA */
514162306a36Sopenharmony_ci	if (cp->opened) {
514262306a36Sopenharmony_ci		netif_device_detach(dev);
514362306a36Sopenharmony_ci
514462306a36Sopenharmony_ci		cas_lock_all_save(cp, flags);
514562306a36Sopenharmony_ci
514662306a36Sopenharmony_ci		/* We can set the second arg of cas_reset to 0
514762306a36Sopenharmony_ci		 * because on resume, we'll call cas_init_hw with
514862306a36Sopenharmony_ci		 * its second arg set so that autonegotiation is
514962306a36Sopenharmony_ci		 * restarted.
515062306a36Sopenharmony_ci		 */
515162306a36Sopenharmony_ci		cas_reset(cp, 0);
515262306a36Sopenharmony_ci		cas_clean_rings(cp);
515362306a36Sopenharmony_ci		cas_unlock_all_restore(cp, flags);
515462306a36Sopenharmony_ci	}
515562306a36Sopenharmony_ci
515662306a36Sopenharmony_ci	if (cp->hw_running)
515762306a36Sopenharmony_ci		cas_shutdown(cp);
515862306a36Sopenharmony_ci	mutex_unlock(&cp->pm_mutex);
515962306a36Sopenharmony_ci
516062306a36Sopenharmony_ci	return 0;
516162306a36Sopenharmony_ci}
516262306a36Sopenharmony_ci
516362306a36Sopenharmony_cistatic int __maybe_unused cas_resume(struct device *dev_d)
516462306a36Sopenharmony_ci{
516562306a36Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(dev_d);
516662306a36Sopenharmony_ci	struct cas *cp = netdev_priv(dev);
516762306a36Sopenharmony_ci
516862306a36Sopenharmony_ci	netdev_info(dev, "resuming\n");
516962306a36Sopenharmony_ci
517062306a36Sopenharmony_ci	mutex_lock(&cp->pm_mutex);
517162306a36Sopenharmony_ci	cas_hard_reset(cp);
517262306a36Sopenharmony_ci	if (cp->opened) {
517362306a36Sopenharmony_ci		unsigned long flags;
517462306a36Sopenharmony_ci		cas_lock_all_save(cp, flags);
517562306a36Sopenharmony_ci		cas_reset(cp, 0);
517662306a36Sopenharmony_ci		cp->hw_running = 1;
517762306a36Sopenharmony_ci		cas_clean_rings(cp);
517862306a36Sopenharmony_ci		cas_init_hw(cp, 1);
517962306a36Sopenharmony_ci		cas_unlock_all_restore(cp, flags);
518062306a36Sopenharmony_ci
518162306a36Sopenharmony_ci		netif_device_attach(dev);
518262306a36Sopenharmony_ci	}
518362306a36Sopenharmony_ci	mutex_unlock(&cp->pm_mutex);
518462306a36Sopenharmony_ci	return 0;
518562306a36Sopenharmony_ci}
518662306a36Sopenharmony_ci
518762306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cas_pm_ops, cas_suspend, cas_resume);
518862306a36Sopenharmony_ci
518962306a36Sopenharmony_cistatic struct pci_driver cas_driver = {
519062306a36Sopenharmony_ci	.name		= DRV_MODULE_NAME,
519162306a36Sopenharmony_ci	.id_table	= cas_pci_tbl,
519262306a36Sopenharmony_ci	.probe		= cas_init_one,
519362306a36Sopenharmony_ci	.remove		= cas_remove_one,
519462306a36Sopenharmony_ci	.driver.pm	= &cas_pm_ops,
519562306a36Sopenharmony_ci};
519662306a36Sopenharmony_ci
519762306a36Sopenharmony_cistatic int __init cas_init(void)
519862306a36Sopenharmony_ci{
519962306a36Sopenharmony_ci	if (linkdown_timeout > 0)
520062306a36Sopenharmony_ci		link_transition_timeout = linkdown_timeout * HZ;
520162306a36Sopenharmony_ci	else
520262306a36Sopenharmony_ci		link_transition_timeout = 0;
520362306a36Sopenharmony_ci
520462306a36Sopenharmony_ci	return pci_register_driver(&cas_driver);
520562306a36Sopenharmony_ci}
520662306a36Sopenharmony_ci
520762306a36Sopenharmony_cistatic void __exit cas_cleanup(void)
520862306a36Sopenharmony_ci{
520962306a36Sopenharmony_ci	pci_unregister_driver(&cas_driver);
521062306a36Sopenharmony_ci}
521162306a36Sopenharmony_ci
521262306a36Sopenharmony_cimodule_init(cas_init);
521362306a36Sopenharmony_cimodule_exit(cas_cleanup);
5214