162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Driver for SGI's IOC3 based Ethernet cards as found in the PCI card.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 1999, 2000, 01, 03, 06 Ralf Baechle
562306a36Sopenharmony_ci * Copyright (C) 1995, 1999, 2000, 2001 by Silicon Graphics, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * References:
862306a36Sopenharmony_ci *  o IOC3 ASIC specification 4.51, 1996-04-18
962306a36Sopenharmony_ci *  o IEEE 802.3 specification, 2000 edition
1062306a36Sopenharmony_ci *  o DP38840A Specification, National Semiconductor, March 1997
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * To do:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *  o Use prefetching for large packets.  What is a good lower limit for
1562306a36Sopenharmony_ci *    prefetching?
1662306a36Sopenharmony_ci *  o Use hardware checksums.
1762306a36Sopenharmony_ci *  o Which PHYs might possibly be attached to the IOC3 in real live,
1862306a36Sopenharmony_ci *    which workarounds are required for them?  Do we ever have Lucent's?
1962306a36Sopenharmony_ci *  o For the 2.5 branch kill the mii-tool ioctls.
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define IOC3_NAME	"ioc3-eth"
2362306a36Sopenharmony_ci#define IOC3_VERSION	"2.6.3-4"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/delay.h>
2662306a36Sopenharmony_ci#include <linux/kernel.h>
2762306a36Sopenharmony_ci#include <linux/mm.h>
2862306a36Sopenharmony_ci#include <linux/errno.h>
2962306a36Sopenharmony_ci#include <linux/module.h>
3062306a36Sopenharmony_ci#include <linux/init.h>
3162306a36Sopenharmony_ci#include <linux/crc16.h>
3262306a36Sopenharmony_ci#include <linux/crc32.h>
3362306a36Sopenharmony_ci#include <linux/mii.h>
3462306a36Sopenharmony_ci#include <linux/in.h>
3562306a36Sopenharmony_ci#include <linux/io.h>
3662306a36Sopenharmony_ci#include <linux/ip.h>
3762306a36Sopenharmony_ci#include <linux/tcp.h>
3862306a36Sopenharmony_ci#include <linux/udp.h>
3962306a36Sopenharmony_ci#include <linux/gfp.h>
4062306a36Sopenharmony_ci#include <linux/netdevice.h>
4162306a36Sopenharmony_ci#include <linux/etherdevice.h>
4262306a36Sopenharmony_ci#include <linux/ethtool.h>
4362306a36Sopenharmony_ci#include <linux/skbuff.h>
4462306a36Sopenharmony_ci#include <linux/dma-mapping.h>
4562306a36Sopenharmony_ci#include <linux/platform_device.h>
4662306a36Sopenharmony_ci#include <linux/nvmem-consumer.h>
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#include <net/ip.h>
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#include <asm/sn/ioc3.h>
5162306a36Sopenharmony_ci#include <asm/pci/bridge.h>
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define CRC16_INIT	0
5462306a36Sopenharmony_ci#define CRC16_VALID	0xb001
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* Number of RX buffers.  This is tunable in the range of 16 <= x < 512.
5762306a36Sopenharmony_ci * The value must be a power of two.
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ci#define RX_BUFFS		64
6062306a36Sopenharmony_ci#define RX_RING_ENTRIES		512		/* fixed in hardware */
6162306a36Sopenharmony_ci#define RX_RING_MASK		(RX_RING_ENTRIES - 1)
6262306a36Sopenharmony_ci#define RX_RING_SIZE		(RX_RING_ENTRIES * sizeof(u64))
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* 128 TX buffers (not tunable) */
6562306a36Sopenharmony_ci#define TX_RING_ENTRIES		128
6662306a36Sopenharmony_ci#define TX_RING_MASK		(TX_RING_ENTRIES - 1)
6762306a36Sopenharmony_ci#define TX_RING_SIZE		(TX_RING_ENTRIES * sizeof(struct ioc3_etxd))
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* IOC3 does dma transfers in 128 byte blocks */
7062306a36Sopenharmony_ci#define IOC3_DMA_XFER_LEN	128UL
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* Every RX buffer starts with 8 byte descriptor data */
7362306a36Sopenharmony_ci#define RX_OFFSET		(sizeof(struct ioc3_erxbuf) + NET_IP_ALIGN)
7462306a36Sopenharmony_ci#define RX_BUF_SIZE		(13 * IOC3_DMA_XFER_LEN)
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#define ETCSR_FD   ((21 << ETCSR_IPGR2_SHIFT) | (21 << ETCSR_IPGR1_SHIFT) | 21)
7762306a36Sopenharmony_ci#define ETCSR_HD   ((17 << ETCSR_IPGR2_SHIFT) | (11 << ETCSR_IPGR1_SHIFT) | 21)
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/* Private per NIC data of the driver.  */
8062306a36Sopenharmony_cistruct ioc3_private {
8162306a36Sopenharmony_ci	struct ioc3_ethregs *regs;
8262306a36Sopenharmony_ci	struct device *dma_dev;
8362306a36Sopenharmony_ci	u32 *ssram;
8462306a36Sopenharmony_ci	unsigned long *rxr;		/* pointer to receiver ring */
8562306a36Sopenharmony_ci	void *tx_ring;
8662306a36Sopenharmony_ci	struct ioc3_etxd *txr;
8762306a36Sopenharmony_ci	dma_addr_t rxr_dma;
8862306a36Sopenharmony_ci	dma_addr_t txr_dma;
8962306a36Sopenharmony_ci	struct sk_buff *rx_skbs[RX_RING_ENTRIES];
9062306a36Sopenharmony_ci	struct sk_buff *tx_skbs[TX_RING_ENTRIES];
9162306a36Sopenharmony_ci	int rx_ci;			/* RX consumer index */
9262306a36Sopenharmony_ci	int rx_pi;			/* RX producer index */
9362306a36Sopenharmony_ci	int tx_ci;			/* TX consumer index */
9462306a36Sopenharmony_ci	int tx_pi;			/* TX producer index */
9562306a36Sopenharmony_ci	int txqlen;
9662306a36Sopenharmony_ci	u32 emcr, ehar_h, ehar_l;
9762306a36Sopenharmony_ci	spinlock_t ioc3_lock;
9862306a36Sopenharmony_ci	struct mii_if_info mii;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* Members used by autonegotiation  */
10162306a36Sopenharmony_ci	struct timer_list ioc3_timer;
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
10562306a36Sopenharmony_cistatic void ioc3_set_multicast_list(struct net_device *dev);
10662306a36Sopenharmony_cistatic netdev_tx_t ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev);
10762306a36Sopenharmony_cistatic void ioc3_timeout(struct net_device *dev, unsigned int txqueue);
10862306a36Sopenharmony_cistatic inline unsigned int ioc3_hash(const unsigned char *addr);
10962306a36Sopenharmony_cistatic void ioc3_start(struct ioc3_private *ip);
11062306a36Sopenharmony_cistatic inline void ioc3_stop(struct ioc3_private *ip);
11162306a36Sopenharmony_cistatic void ioc3_init(struct net_device *dev);
11262306a36Sopenharmony_cistatic int ioc3_alloc_rx_bufs(struct net_device *dev);
11362306a36Sopenharmony_cistatic void ioc3_free_rx_bufs(struct ioc3_private *ip);
11462306a36Sopenharmony_cistatic inline void ioc3_clean_tx_ring(struct ioc3_private *ip);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic const struct ethtool_ops ioc3_ethtool_ops;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic inline unsigned long aligned_rx_skb_addr(unsigned long addr)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	return (~addr + 1) & (IOC3_DMA_XFER_LEN - 1UL);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic inline int ioc3_alloc_skb(struct ioc3_private *ip, struct sk_buff **skb,
12462306a36Sopenharmony_ci				 struct ioc3_erxbuf **rxb, dma_addr_t *rxb_dma)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct sk_buff *new_skb;
12762306a36Sopenharmony_ci	dma_addr_t d;
12862306a36Sopenharmony_ci	int offset;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	new_skb = alloc_skb(RX_BUF_SIZE + IOC3_DMA_XFER_LEN - 1, GFP_ATOMIC);
13162306a36Sopenharmony_ci	if (!new_skb)
13262306a36Sopenharmony_ci		return -ENOMEM;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* ensure buffer is aligned to IOC3_DMA_XFER_LEN */
13562306a36Sopenharmony_ci	offset = aligned_rx_skb_addr((unsigned long)new_skb->data);
13662306a36Sopenharmony_ci	if (offset)
13762306a36Sopenharmony_ci		skb_reserve(new_skb, offset);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	d = dma_map_single(ip->dma_dev, new_skb->data,
14062306a36Sopenharmony_ci			   RX_BUF_SIZE, DMA_FROM_DEVICE);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (dma_mapping_error(ip->dma_dev, d)) {
14362306a36Sopenharmony_ci		dev_kfree_skb_any(new_skb);
14462306a36Sopenharmony_ci		return -ENOMEM;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci	*rxb_dma = d;
14762306a36Sopenharmony_ci	*rxb = (struct ioc3_erxbuf *)new_skb->data;
14862306a36Sopenharmony_ci	skb_reserve(new_skb, RX_OFFSET);
14962306a36Sopenharmony_ci	*skb = new_skb;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return 0;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci#ifdef CONFIG_PCI_XTALK_BRIDGE
15562306a36Sopenharmony_cistatic inline unsigned long ioc3_map(dma_addr_t addr, unsigned long attr)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	return (addr & ~PCI64_ATTR_BAR) | attr;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci#define ERBAR_VAL	(ERBAR_BARRIER_BIT << ERBAR_RXBARR_SHIFT)
16162306a36Sopenharmony_ci#else
16262306a36Sopenharmony_cistatic inline unsigned long ioc3_map(dma_addr_t addr, unsigned long attr)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	return addr;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci#define ERBAR_VAL	0
16862306a36Sopenharmony_ci#endif
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic int ioc3eth_nvmem_match(struct device *dev, const void *data)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	const char *name = dev_name(dev);
17362306a36Sopenharmony_ci	const char *prefix = data;
17462306a36Sopenharmony_ci	int prefix_len;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	prefix_len = strlen(prefix);
17762306a36Sopenharmony_ci	if (strlen(name) < (prefix_len + 3))
17862306a36Sopenharmony_ci		return 0;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (memcmp(prefix, name, prefix_len) != 0)
18162306a36Sopenharmony_ci		return 0;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* found nvmem device which is attached to our ioc3
18462306a36Sopenharmony_ci	 * now check for one wire family code 09, 89 and 91
18562306a36Sopenharmony_ci	 */
18662306a36Sopenharmony_ci	if (memcmp(name + prefix_len, "09-", 3) == 0)
18762306a36Sopenharmony_ci		return 1;
18862306a36Sopenharmony_ci	if (memcmp(name + prefix_len, "89-", 3) == 0)
18962306a36Sopenharmony_ci		return 1;
19062306a36Sopenharmony_ci	if (memcmp(name + prefix_len, "91-", 3) == 0)
19162306a36Sopenharmony_ci		return 1;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return 0;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic int ioc3eth_get_mac_addr(struct resource *res, u8 mac_addr[6])
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct nvmem_device *nvmem;
19962306a36Sopenharmony_ci	char prefix[24];
20062306a36Sopenharmony_ci	u8 prom[16];
20162306a36Sopenharmony_ci	int ret;
20262306a36Sopenharmony_ci	int i;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	snprintf(prefix, sizeof(prefix), "ioc3-%012llx-",
20562306a36Sopenharmony_ci		 res->start & ~0xffff);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	nvmem = nvmem_device_find(prefix, ioc3eth_nvmem_match);
20862306a36Sopenharmony_ci	if (IS_ERR(nvmem))
20962306a36Sopenharmony_ci		return PTR_ERR(nvmem);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ret = nvmem_device_read(nvmem, 0, 16, prom);
21262306a36Sopenharmony_ci	nvmem_device_put(nvmem);
21362306a36Sopenharmony_ci	if (ret < 0)
21462306a36Sopenharmony_ci		return ret;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* check, if content is valid */
21762306a36Sopenharmony_ci	if (prom[0] != 0x0a ||
21862306a36Sopenharmony_ci	    crc16(CRC16_INIT, prom, 13) != CRC16_VALID)
21962306a36Sopenharmony_ci		return -EINVAL;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	for (i = 0; i < 6; i++)
22262306a36Sopenharmony_ci		mac_addr[i] = prom[10 - i];
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return 0;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void __ioc3_set_mac_address(struct net_device *dev)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	writel((dev->dev_addr[5] <<  8) |
23262306a36Sopenharmony_ci	       dev->dev_addr[4],
23362306a36Sopenharmony_ci	       &ip->regs->emar_h);
23462306a36Sopenharmony_ci	writel((dev->dev_addr[3] << 24) |
23562306a36Sopenharmony_ci	       (dev->dev_addr[2] << 16) |
23662306a36Sopenharmony_ci	       (dev->dev_addr[1] <<  8) |
23762306a36Sopenharmony_ci	       dev->dev_addr[0],
23862306a36Sopenharmony_ci	       &ip->regs->emar_l);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int ioc3_set_mac_address(struct net_device *dev, void *addr)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
24462306a36Sopenharmony_ci	struct sockaddr *sa = addr;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	eth_hw_addr_set(dev, sa->sa_data);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	spin_lock_irq(&ip->ioc3_lock);
24962306a36Sopenharmony_ci	__ioc3_set_mac_address(dev);
25062306a36Sopenharmony_ci	spin_unlock_irq(&ip->ioc3_lock);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return 0;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci/* Caller must hold the ioc3_lock ever for MII readers.  This is also
25662306a36Sopenharmony_ci * used to protect the transmitter side but it's low contention.
25762306a36Sopenharmony_ci */
25862306a36Sopenharmony_cistatic int ioc3_mdio_read(struct net_device *dev, int phy, int reg)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
26162306a36Sopenharmony_ci	struct ioc3_ethregs *regs = ip->regs;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	while (readl(&regs->micr) & MICR_BUSY)
26462306a36Sopenharmony_ci		;
26562306a36Sopenharmony_ci	writel((phy << MICR_PHYADDR_SHIFT) | reg | MICR_READTRIG,
26662306a36Sopenharmony_ci	       &regs->micr);
26762306a36Sopenharmony_ci	while (readl(&regs->micr) & MICR_BUSY)
26862306a36Sopenharmony_ci		;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return readl(&regs->midr_r) & MIDR_DATA_MASK;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void ioc3_mdio_write(struct net_device *dev, int phy, int reg, int data)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
27662306a36Sopenharmony_ci	struct ioc3_ethregs *regs = ip->regs;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	while (readl(&regs->micr) & MICR_BUSY)
27962306a36Sopenharmony_ci		;
28062306a36Sopenharmony_ci	writel(data, &regs->midr_w);
28162306a36Sopenharmony_ci	writel((phy << MICR_PHYADDR_SHIFT) | reg, &regs->micr);
28262306a36Sopenharmony_ci	while (readl(&regs->micr) & MICR_BUSY)
28362306a36Sopenharmony_ci		;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic int ioc3_mii_init(struct ioc3_private *ip);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic struct net_device_stats *ioc3_get_stats(struct net_device *dev)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
29162306a36Sopenharmony_ci	struct ioc3_ethregs *regs = ip->regs;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	dev->stats.collisions += readl(&regs->etcdc) & ETCDC_COLLCNT_MASK;
29462306a36Sopenharmony_ci	return &dev->stats;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic void ioc3_tcpudp_checksum(struct sk_buff *skb, u32 hwsum, int len)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct ethhdr *eh = eth_hdr(skb);
30062306a36Sopenharmony_ci	unsigned int proto;
30162306a36Sopenharmony_ci	unsigned char *cp;
30262306a36Sopenharmony_ci	struct iphdr *ih;
30362306a36Sopenharmony_ci	u32 csum, ehsum;
30462306a36Sopenharmony_ci	u16 *ew;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/* Did hardware handle the checksum at all?  The cases we can handle
30762306a36Sopenharmony_ci	 * are:
30862306a36Sopenharmony_ci	 *
30962306a36Sopenharmony_ci	 * - TCP and UDP checksums of IPv4 only.
31062306a36Sopenharmony_ci	 * - IPv6 would be doable but we keep that for later ...
31162306a36Sopenharmony_ci	 * - Only unfragmented packets.  Did somebody already tell you
31262306a36Sopenharmony_ci	 *   fragmentation is evil?
31362306a36Sopenharmony_ci	 * - don't care about packet size.  Worst case when processing a
31462306a36Sopenharmony_ci	 *   malformed packet we'll try to access the packet at ip header +
31562306a36Sopenharmony_ci	 *   64 bytes which is still inside the skb.  Even in the unlikely
31662306a36Sopenharmony_ci	 *   case where the checksum is right the higher layers will still
31762306a36Sopenharmony_ci	 *   drop the packet as appropriate.
31862306a36Sopenharmony_ci	 */
31962306a36Sopenharmony_ci	if (eh->h_proto != htons(ETH_P_IP))
32062306a36Sopenharmony_ci		return;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	ih = (struct iphdr *)((char *)eh + ETH_HLEN);
32362306a36Sopenharmony_ci	if (ip_is_fragment(ih))
32462306a36Sopenharmony_ci		return;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	proto = ih->protocol;
32762306a36Sopenharmony_ci	if (proto != IPPROTO_TCP && proto != IPPROTO_UDP)
32862306a36Sopenharmony_ci		return;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* Same as tx - compute csum of pseudo header  */
33162306a36Sopenharmony_ci	csum = hwsum +
33262306a36Sopenharmony_ci	       (ih->tot_len - (ih->ihl << 2)) +
33362306a36Sopenharmony_ci	       htons((u16)ih->protocol) +
33462306a36Sopenharmony_ci	       (ih->saddr >> 16) + (ih->saddr & 0xffff) +
33562306a36Sopenharmony_ci	       (ih->daddr >> 16) + (ih->daddr & 0xffff);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Sum up ethernet dest addr, src addr and protocol  */
33862306a36Sopenharmony_ci	ew = (u16 *)eh;
33962306a36Sopenharmony_ci	ehsum = ew[0] + ew[1] + ew[2] + ew[3] + ew[4] + ew[5] + ew[6];
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	ehsum = (ehsum & 0xffff) + (ehsum >> 16);
34262306a36Sopenharmony_ci	ehsum = (ehsum & 0xffff) + (ehsum >> 16);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	csum += 0xffff ^ ehsum;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/* In the next step we also subtract the 1's complement
34762306a36Sopenharmony_ci	 * checksum of the trailing ethernet CRC.
34862306a36Sopenharmony_ci	 */
34962306a36Sopenharmony_ci	cp = (char *)eh + len;	/* points at trailing CRC */
35062306a36Sopenharmony_ci	if (len & 1) {
35162306a36Sopenharmony_ci		csum += 0xffff ^ (u16)((cp[1] << 8) | cp[0]);
35262306a36Sopenharmony_ci		csum += 0xffff ^ (u16)((cp[3] << 8) | cp[2]);
35362306a36Sopenharmony_ci	} else {
35462306a36Sopenharmony_ci		csum += 0xffff ^ (u16)((cp[0] << 8) | cp[1]);
35562306a36Sopenharmony_ci		csum += 0xffff ^ (u16)((cp[2] << 8) | cp[3]);
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	csum = (csum & 0xffff) + (csum >> 16);
35962306a36Sopenharmony_ci	csum = (csum & 0xffff) + (csum >> 16);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (csum == 0xffff)
36262306a36Sopenharmony_ci		skb->ip_summed = CHECKSUM_UNNECESSARY;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic inline void ioc3_rx(struct net_device *dev)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
36862306a36Sopenharmony_ci	struct sk_buff *skb, *new_skb;
36962306a36Sopenharmony_ci	int rx_entry, n_entry, len;
37062306a36Sopenharmony_ci	struct ioc3_erxbuf *rxb;
37162306a36Sopenharmony_ci	unsigned long *rxr;
37262306a36Sopenharmony_ci	dma_addr_t d;
37362306a36Sopenharmony_ci	u32 w0, err;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	rxr = ip->rxr;		/* Ring base */
37662306a36Sopenharmony_ci	rx_entry = ip->rx_ci;				/* RX consume index */
37762306a36Sopenharmony_ci	n_entry = ip->rx_pi;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	skb = ip->rx_skbs[rx_entry];
38062306a36Sopenharmony_ci	rxb = (struct ioc3_erxbuf *)(skb->data - RX_OFFSET);
38162306a36Sopenharmony_ci	w0 = be32_to_cpu(rxb->w0);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	while (w0 & ERXBUF_V) {
38462306a36Sopenharmony_ci		err = be32_to_cpu(rxb->err);		/* It's valid ...  */
38562306a36Sopenharmony_ci		if (err & ERXBUF_GOODPKT) {
38662306a36Sopenharmony_ci			len = ((w0 >> ERXBUF_BYTECNT_SHIFT) & 0x7ff) - 4;
38762306a36Sopenharmony_ci			skb_put(skb, len);
38862306a36Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci			if (ioc3_alloc_skb(ip, &new_skb, &rxb, &d)) {
39162306a36Sopenharmony_ci				/* Ouch, drop packet and just recycle packet
39262306a36Sopenharmony_ci				 * to keep the ring filled.
39362306a36Sopenharmony_ci				 */
39462306a36Sopenharmony_ci				dev->stats.rx_dropped++;
39562306a36Sopenharmony_ci				new_skb = skb;
39662306a36Sopenharmony_ci				d = rxr[rx_entry];
39762306a36Sopenharmony_ci				goto next;
39862306a36Sopenharmony_ci			}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci			if (likely(dev->features & NETIF_F_RXCSUM))
40162306a36Sopenharmony_ci				ioc3_tcpudp_checksum(skb,
40262306a36Sopenharmony_ci						     w0 & ERXBUF_IPCKSUM_MASK,
40362306a36Sopenharmony_ci						     len);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci			dma_unmap_single(ip->dma_dev, rxr[rx_entry],
40662306a36Sopenharmony_ci					 RX_BUF_SIZE, DMA_FROM_DEVICE);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci			netif_rx(skb);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci			ip->rx_skbs[rx_entry] = NULL;	/* Poison  */
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci			dev->stats.rx_packets++;		/* Statistics */
41362306a36Sopenharmony_ci			dev->stats.rx_bytes += len;
41462306a36Sopenharmony_ci		} else {
41562306a36Sopenharmony_ci			/* The frame is invalid and the skb never
41662306a36Sopenharmony_ci			 * reached the network layer so we can just
41762306a36Sopenharmony_ci			 * recycle it.
41862306a36Sopenharmony_ci			 */
41962306a36Sopenharmony_ci			new_skb = skb;
42062306a36Sopenharmony_ci			d = rxr[rx_entry];
42162306a36Sopenharmony_ci			dev->stats.rx_errors++;
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci		if (err & ERXBUF_CRCERR)	/* Statistics */
42462306a36Sopenharmony_ci			dev->stats.rx_crc_errors++;
42562306a36Sopenharmony_ci		if (err & ERXBUF_FRAMERR)
42662306a36Sopenharmony_ci			dev->stats.rx_frame_errors++;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cinext:
42962306a36Sopenharmony_ci		ip->rx_skbs[n_entry] = new_skb;
43062306a36Sopenharmony_ci		rxr[n_entry] = cpu_to_be64(ioc3_map(d, PCI64_ATTR_BAR));
43162306a36Sopenharmony_ci		rxb->w0 = 0;				/* Clear valid flag */
43262306a36Sopenharmony_ci		n_entry = (n_entry + 1) & RX_RING_MASK;	/* Update erpir */
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		/* Now go on to the next ring entry.  */
43562306a36Sopenharmony_ci		rx_entry = (rx_entry + 1) & RX_RING_MASK;
43662306a36Sopenharmony_ci		skb = ip->rx_skbs[rx_entry];
43762306a36Sopenharmony_ci		rxb = (struct ioc3_erxbuf *)(skb->data - RX_OFFSET);
43862306a36Sopenharmony_ci		w0 = be32_to_cpu(rxb->w0);
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci	writel((n_entry << 3) | ERPIR_ARM, &ip->regs->erpir);
44162306a36Sopenharmony_ci	ip->rx_pi = n_entry;
44262306a36Sopenharmony_ci	ip->rx_ci = rx_entry;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic inline void ioc3_tx(struct net_device *dev)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
44862306a36Sopenharmony_ci	struct ioc3_ethregs *regs = ip->regs;
44962306a36Sopenharmony_ci	unsigned long packets, bytes;
45062306a36Sopenharmony_ci	int tx_entry, o_entry;
45162306a36Sopenharmony_ci	struct sk_buff *skb;
45262306a36Sopenharmony_ci	u32 etcir;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	spin_lock(&ip->ioc3_lock);
45562306a36Sopenharmony_ci	etcir = readl(&regs->etcir);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	tx_entry = (etcir >> 7) & TX_RING_MASK;
45862306a36Sopenharmony_ci	o_entry = ip->tx_ci;
45962306a36Sopenharmony_ci	packets = 0;
46062306a36Sopenharmony_ci	bytes = 0;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	while (o_entry != tx_entry) {
46362306a36Sopenharmony_ci		packets++;
46462306a36Sopenharmony_ci		skb = ip->tx_skbs[o_entry];
46562306a36Sopenharmony_ci		bytes += skb->len;
46662306a36Sopenharmony_ci		dev_consume_skb_irq(skb);
46762306a36Sopenharmony_ci		ip->tx_skbs[o_entry] = NULL;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		o_entry = (o_entry + 1) & TX_RING_MASK;	/* Next */
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		etcir = readl(&regs->etcir);		/* More pkts sent?  */
47262306a36Sopenharmony_ci		tx_entry = (etcir >> 7) & TX_RING_MASK;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	dev->stats.tx_packets += packets;
47662306a36Sopenharmony_ci	dev->stats.tx_bytes += bytes;
47762306a36Sopenharmony_ci	ip->txqlen -= packets;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (netif_queue_stopped(dev) && ip->txqlen < TX_RING_ENTRIES)
48062306a36Sopenharmony_ci		netif_wake_queue(dev);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	ip->tx_ci = o_entry;
48362306a36Sopenharmony_ci	spin_unlock(&ip->ioc3_lock);
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/* Deal with fatal IOC3 errors.  This condition might be caused by a hard or
48762306a36Sopenharmony_ci * software problems, so we should try to recover
48862306a36Sopenharmony_ci * more gracefully if this ever happens.  In theory we might be flooded
48962306a36Sopenharmony_ci * with such error interrupts if something really goes wrong, so we might
49062306a36Sopenharmony_ci * also consider to take the interface down.
49162306a36Sopenharmony_ci */
49262306a36Sopenharmony_cistatic void ioc3_error(struct net_device *dev, u32 eisr)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	spin_lock(&ip->ioc3_lock);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	if (eisr & EISR_RXOFLO)
49962306a36Sopenharmony_ci		net_err_ratelimited("%s: RX overflow.\n", dev->name);
50062306a36Sopenharmony_ci	if (eisr & EISR_RXBUFOFLO)
50162306a36Sopenharmony_ci		net_err_ratelimited("%s: RX buffer overflow.\n", dev->name);
50262306a36Sopenharmony_ci	if (eisr & EISR_RXMEMERR)
50362306a36Sopenharmony_ci		net_err_ratelimited("%s: RX PCI error.\n", dev->name);
50462306a36Sopenharmony_ci	if (eisr & EISR_RXPARERR)
50562306a36Sopenharmony_ci		net_err_ratelimited("%s: RX SSRAM parity error.\n", dev->name);
50662306a36Sopenharmony_ci	if (eisr & EISR_TXBUFUFLO)
50762306a36Sopenharmony_ci		net_err_ratelimited("%s: TX buffer underflow.\n", dev->name);
50862306a36Sopenharmony_ci	if (eisr & EISR_TXMEMERR)
50962306a36Sopenharmony_ci		net_err_ratelimited("%s: TX PCI error.\n", dev->name);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	ioc3_stop(ip);
51262306a36Sopenharmony_ci	ioc3_free_rx_bufs(ip);
51362306a36Sopenharmony_ci	ioc3_clean_tx_ring(ip);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	ioc3_init(dev);
51662306a36Sopenharmony_ci	if (ioc3_alloc_rx_bufs(dev)) {
51762306a36Sopenharmony_ci		netdev_err(dev, "%s: rx buffer allocation failed\n", __func__);
51862306a36Sopenharmony_ci		spin_unlock(&ip->ioc3_lock);
51962306a36Sopenharmony_ci		return;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci	ioc3_start(ip);
52262306a36Sopenharmony_ci	ioc3_mii_init(ip);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	netif_wake_queue(dev);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	spin_unlock(&ip->ioc3_lock);
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci/* The interrupt handler does all of the Rx thread work and cleans up
53062306a36Sopenharmony_ci * after the Tx thread.
53162306a36Sopenharmony_ci */
53262306a36Sopenharmony_cistatic irqreturn_t ioc3_interrupt(int irq, void *dev_id)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev_id);
53562306a36Sopenharmony_ci	struct ioc3_ethregs *regs = ip->regs;
53662306a36Sopenharmony_ci	u32 eisr;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	eisr = readl(&regs->eisr);
53962306a36Sopenharmony_ci	writel(eisr, &regs->eisr);
54062306a36Sopenharmony_ci	readl(&regs->eisr);				/* Flush */
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (eisr & (EISR_RXOFLO | EISR_RXBUFOFLO | EISR_RXMEMERR |
54362306a36Sopenharmony_ci		    EISR_RXPARERR | EISR_TXBUFUFLO | EISR_TXMEMERR))
54462306a36Sopenharmony_ci		ioc3_error(dev_id, eisr);
54562306a36Sopenharmony_ci	if (eisr & EISR_RXTIMERINT)
54662306a36Sopenharmony_ci		ioc3_rx(dev_id);
54762306a36Sopenharmony_ci	if (eisr & EISR_TXEXPLICIT)
54862306a36Sopenharmony_ci		ioc3_tx(dev_id);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	return IRQ_HANDLED;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic inline void ioc3_setup_duplex(struct ioc3_private *ip)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	struct ioc3_ethregs *regs = ip->regs;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	spin_lock_irq(&ip->ioc3_lock);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (ip->mii.full_duplex) {
56062306a36Sopenharmony_ci		writel(ETCSR_FD, &regs->etcsr);
56162306a36Sopenharmony_ci		ip->emcr |= EMCR_DUPLEX;
56262306a36Sopenharmony_ci	} else {
56362306a36Sopenharmony_ci		writel(ETCSR_HD, &regs->etcsr);
56462306a36Sopenharmony_ci		ip->emcr &= ~EMCR_DUPLEX;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci	writel(ip->emcr, &regs->emcr);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	spin_unlock_irq(&ip->ioc3_lock);
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cistatic void ioc3_timer(struct timer_list *t)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	struct ioc3_private *ip = from_timer(ip, t, ioc3_timer);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* Print the link status if it has changed */
57662306a36Sopenharmony_ci	mii_check_media(&ip->mii, 1, 0);
57762306a36Sopenharmony_ci	ioc3_setup_duplex(ip);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	ip->ioc3_timer.expires = jiffies + ((12 * HZ) / 10); /* 1.2s */
58062306a36Sopenharmony_ci	add_timer(&ip->ioc3_timer);
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci/* Try to find a PHY.  There is no apparent relation between the MII addresses
58462306a36Sopenharmony_ci * in the SGI documentation and what we find in reality, so we simply probe
58562306a36Sopenharmony_ci * for the PHY.
58662306a36Sopenharmony_ci */
58762306a36Sopenharmony_cistatic int ioc3_mii_init(struct ioc3_private *ip)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	u16 word;
59062306a36Sopenharmony_ci	int i;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
59362306a36Sopenharmony_ci		word = ioc3_mdio_read(ip->mii.dev, i, MII_PHYSID1);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci		if (word != 0xffff && word != 0x0000) {
59662306a36Sopenharmony_ci			ip->mii.phy_id = i;
59762306a36Sopenharmony_ci			return 0;
59862306a36Sopenharmony_ci		}
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci	ip->mii.phy_id = -1;
60162306a36Sopenharmony_ci	return -ENODEV;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic void ioc3_mii_start(struct ioc3_private *ip)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	ip->ioc3_timer.expires = jiffies + (12 * HZ) / 10;  /* 1.2 sec. */
60762306a36Sopenharmony_ci	add_timer(&ip->ioc3_timer);
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic inline void ioc3_tx_unmap(struct ioc3_private *ip, int entry)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct ioc3_etxd *desc;
61362306a36Sopenharmony_ci	u32 cmd, bufcnt, len;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	desc = &ip->txr[entry];
61662306a36Sopenharmony_ci	cmd = be32_to_cpu(desc->cmd);
61762306a36Sopenharmony_ci	bufcnt = be32_to_cpu(desc->bufcnt);
61862306a36Sopenharmony_ci	if (cmd & ETXD_B1V) {
61962306a36Sopenharmony_ci		len = (bufcnt & ETXD_B1CNT_MASK) >> ETXD_B1CNT_SHIFT;
62062306a36Sopenharmony_ci		dma_unmap_single(ip->dma_dev, be64_to_cpu(desc->p1),
62162306a36Sopenharmony_ci				 len, DMA_TO_DEVICE);
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci	if (cmd & ETXD_B2V) {
62462306a36Sopenharmony_ci		len = (bufcnt & ETXD_B2CNT_MASK) >> ETXD_B2CNT_SHIFT;
62562306a36Sopenharmony_ci		dma_unmap_single(ip->dma_dev, be64_to_cpu(desc->p2),
62662306a36Sopenharmony_ci				 len, DMA_TO_DEVICE);
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic inline void ioc3_clean_tx_ring(struct ioc3_private *ip)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	struct sk_buff *skb;
63362306a36Sopenharmony_ci	int i;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	for (i = 0; i < TX_RING_ENTRIES; i++) {
63662306a36Sopenharmony_ci		skb = ip->tx_skbs[i];
63762306a36Sopenharmony_ci		if (skb) {
63862306a36Sopenharmony_ci			ioc3_tx_unmap(ip, i);
63962306a36Sopenharmony_ci			ip->tx_skbs[i] = NULL;
64062306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
64162306a36Sopenharmony_ci		}
64262306a36Sopenharmony_ci		ip->txr[i].cmd = 0;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci	ip->tx_pi = 0;
64562306a36Sopenharmony_ci	ip->tx_ci = 0;
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic void ioc3_free_rx_bufs(struct ioc3_private *ip)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	int rx_entry, n_entry;
65162306a36Sopenharmony_ci	struct sk_buff *skb;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	n_entry = ip->rx_ci;
65462306a36Sopenharmony_ci	rx_entry = ip->rx_pi;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	while (n_entry != rx_entry) {
65762306a36Sopenharmony_ci		skb = ip->rx_skbs[n_entry];
65862306a36Sopenharmony_ci		if (skb) {
65962306a36Sopenharmony_ci			dma_unmap_single(ip->dma_dev,
66062306a36Sopenharmony_ci					 be64_to_cpu(ip->rxr[n_entry]),
66162306a36Sopenharmony_ci					 RX_BUF_SIZE, DMA_FROM_DEVICE);
66262306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
66362306a36Sopenharmony_ci		}
66462306a36Sopenharmony_ci		n_entry = (n_entry + 1) & RX_RING_MASK;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic int ioc3_alloc_rx_bufs(struct net_device *dev)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
67162306a36Sopenharmony_ci	struct ioc3_erxbuf *rxb;
67262306a36Sopenharmony_ci	dma_addr_t d;
67362306a36Sopenharmony_ci	int i;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	/* Now the rx buffers.  The RX ring may be larger but
67662306a36Sopenharmony_ci	 * we only allocate 16 buffers for now.  Need to tune
67762306a36Sopenharmony_ci	 * this for performance and memory later.
67862306a36Sopenharmony_ci	 */
67962306a36Sopenharmony_ci	for (i = 0; i < RX_BUFFS; i++) {
68062306a36Sopenharmony_ci		if (ioc3_alloc_skb(ip, &ip->rx_skbs[i], &rxb, &d))
68162306a36Sopenharmony_ci			return -ENOMEM;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		rxb->w0 = 0;	/* Clear valid flag */
68462306a36Sopenharmony_ci		ip->rxr[i] = cpu_to_be64(ioc3_map(d, PCI64_ATTR_BAR));
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci	ip->rx_ci = 0;
68762306a36Sopenharmony_ci	ip->rx_pi = RX_BUFFS;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	return 0;
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic inline void ioc3_ssram_disc(struct ioc3_private *ip)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	struct ioc3_ethregs *regs = ip->regs;
69562306a36Sopenharmony_ci	u32 *ssram0 = &ip->ssram[0x0000];
69662306a36Sopenharmony_ci	u32 *ssram1 = &ip->ssram[0x4000];
69762306a36Sopenharmony_ci	u32 pattern = 0x5555;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* Assume the larger size SSRAM and enable parity checking */
70062306a36Sopenharmony_ci	writel(readl(&regs->emcr) | (EMCR_BUFSIZ | EMCR_RAMPAR), &regs->emcr);
70162306a36Sopenharmony_ci	readl(&regs->emcr); /* Flush */
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	writel(pattern, ssram0);
70462306a36Sopenharmony_ci	writel(~pattern & IOC3_SSRAM_DM, ssram1);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if ((readl(ssram0) & IOC3_SSRAM_DM) != pattern ||
70762306a36Sopenharmony_ci	    (readl(ssram1) & IOC3_SSRAM_DM) != (~pattern & IOC3_SSRAM_DM)) {
70862306a36Sopenharmony_ci		/* set ssram size to 64 KB */
70962306a36Sopenharmony_ci		ip->emcr |= EMCR_RAMPAR;
71062306a36Sopenharmony_ci		writel(readl(&regs->emcr) & ~EMCR_BUFSIZ, &regs->emcr);
71162306a36Sopenharmony_ci	} else {
71262306a36Sopenharmony_ci		ip->emcr |= EMCR_BUFSIZ | EMCR_RAMPAR;
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic void ioc3_init(struct net_device *dev)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
71962306a36Sopenharmony_ci	struct ioc3_ethregs *regs = ip->regs;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	del_timer_sync(&ip->ioc3_timer);	/* Kill if running	*/
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	writel(EMCR_RST, &regs->emcr);		/* Reset		*/
72462306a36Sopenharmony_ci	readl(&regs->emcr);			/* Flush WB		*/
72562306a36Sopenharmony_ci	udelay(4);				/* Give it time ...	*/
72662306a36Sopenharmony_ci	writel(0, &regs->emcr);
72762306a36Sopenharmony_ci	readl(&regs->emcr);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	/* Misc registers  */
73062306a36Sopenharmony_ci	writel(ERBAR_VAL, &regs->erbar);
73162306a36Sopenharmony_ci	readl(&regs->etcdc);			/* Clear on read */
73262306a36Sopenharmony_ci	writel(15, &regs->ercsr);		/* RX low watermark  */
73362306a36Sopenharmony_ci	writel(0, &regs->ertr);			/* Interrupt immediately */
73462306a36Sopenharmony_ci	__ioc3_set_mac_address(dev);
73562306a36Sopenharmony_ci	writel(ip->ehar_h, &regs->ehar_h);
73662306a36Sopenharmony_ci	writel(ip->ehar_l, &regs->ehar_l);
73762306a36Sopenharmony_ci	writel(42, &regs->ersr);		/* XXX should be random */
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic void ioc3_start(struct ioc3_private *ip)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	struct ioc3_ethregs *regs = ip->regs;
74362306a36Sopenharmony_ci	unsigned long ring;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	/* Now the rx ring base, consume & produce registers.  */
74662306a36Sopenharmony_ci	ring = ioc3_map(ip->rxr_dma, PCI64_ATTR_PREC);
74762306a36Sopenharmony_ci	writel(ring >> 32, &regs->erbr_h);
74862306a36Sopenharmony_ci	writel(ring & 0xffffffff, &regs->erbr_l);
74962306a36Sopenharmony_ci	writel(ip->rx_ci << 3, &regs->ercir);
75062306a36Sopenharmony_ci	writel((ip->rx_pi << 3) | ERPIR_ARM, &regs->erpir);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	ring = ioc3_map(ip->txr_dma, PCI64_ATTR_PREC);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	ip->txqlen = 0;					/* nothing queued  */
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	/* Now the tx ring base, consume & produce registers.  */
75762306a36Sopenharmony_ci	writel(ring >> 32, &regs->etbr_h);
75862306a36Sopenharmony_ci	writel(ring & 0xffffffff, &regs->etbr_l);
75962306a36Sopenharmony_ci	writel(ip->tx_pi << 7, &regs->etpir);
76062306a36Sopenharmony_ci	writel(ip->tx_ci << 7, &regs->etcir);
76162306a36Sopenharmony_ci	readl(&regs->etcir);				/* Flush */
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	ip->emcr |= ((RX_OFFSET / 2) << EMCR_RXOFF_SHIFT) | EMCR_TXDMAEN |
76462306a36Sopenharmony_ci		    EMCR_TXEN | EMCR_RXDMAEN | EMCR_RXEN | EMCR_PADEN;
76562306a36Sopenharmony_ci	writel(ip->emcr, &regs->emcr);
76662306a36Sopenharmony_ci	writel(EISR_RXTIMERINT | EISR_RXOFLO | EISR_RXBUFOFLO |
76762306a36Sopenharmony_ci	       EISR_RXMEMERR | EISR_RXPARERR | EISR_TXBUFUFLO |
76862306a36Sopenharmony_ci	       EISR_TXEXPLICIT | EISR_TXMEMERR, &regs->eier);
76962306a36Sopenharmony_ci	readl(&regs->eier);
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cistatic inline void ioc3_stop(struct ioc3_private *ip)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct ioc3_ethregs *regs = ip->regs;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	writel(0, &regs->emcr);			/* Shutup */
77762306a36Sopenharmony_ci	writel(0, &regs->eier);			/* Disable interrupts */
77862306a36Sopenharmony_ci	readl(&regs->eier);			/* Flush */
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic int ioc3_open(struct net_device *dev)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	ip->ehar_h = 0;
78662306a36Sopenharmony_ci	ip->ehar_l = 0;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	ioc3_init(dev);
78962306a36Sopenharmony_ci	if (ioc3_alloc_rx_bufs(dev)) {
79062306a36Sopenharmony_ci		netdev_err(dev, "%s: rx buffer allocation failed\n", __func__);
79162306a36Sopenharmony_ci		return -ENOMEM;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci	ioc3_start(ip);
79462306a36Sopenharmony_ci	ioc3_mii_start(ip);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	netif_start_queue(dev);
79762306a36Sopenharmony_ci	return 0;
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic int ioc3_close(struct net_device *dev)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	del_timer_sync(&ip->ioc3_timer);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	netif_stop_queue(dev);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	ioc3_stop(ip);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	ioc3_free_rx_bufs(ip);
81162306a36Sopenharmony_ci	ioc3_clean_tx_ring(ip);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	return 0;
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic const struct net_device_ops ioc3_netdev_ops = {
81762306a36Sopenharmony_ci	.ndo_open		= ioc3_open,
81862306a36Sopenharmony_ci	.ndo_stop		= ioc3_close,
81962306a36Sopenharmony_ci	.ndo_start_xmit		= ioc3_start_xmit,
82062306a36Sopenharmony_ci	.ndo_tx_timeout		= ioc3_timeout,
82162306a36Sopenharmony_ci	.ndo_get_stats		= ioc3_get_stats,
82262306a36Sopenharmony_ci	.ndo_set_rx_mode	= ioc3_set_multicast_list,
82362306a36Sopenharmony_ci	.ndo_eth_ioctl		= ioc3_ioctl,
82462306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
82562306a36Sopenharmony_ci	.ndo_set_mac_address	= ioc3_set_mac_address,
82662306a36Sopenharmony_ci};
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic int ioc3eth_probe(struct platform_device *pdev)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	u32 sw_physid1, sw_physid2, vendor, model, rev;
83162306a36Sopenharmony_ci	struct ioc3_private *ip;
83262306a36Sopenharmony_ci	struct net_device *dev;
83362306a36Sopenharmony_ci	struct resource *regs;
83462306a36Sopenharmony_ci	u8 mac_addr[6];
83562306a36Sopenharmony_ci	int err;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
83862306a36Sopenharmony_ci	if (!regs) {
83962306a36Sopenharmony_ci		dev_err(&pdev->dev, "Invalid resource\n");
84062306a36Sopenharmony_ci		return -EINVAL;
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci	/* get mac addr from one wire prom */
84362306a36Sopenharmony_ci	if (ioc3eth_get_mac_addr(regs, mac_addr))
84462306a36Sopenharmony_ci		return -EPROBE_DEFER; /* not available yet */
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct ioc3_private));
84762306a36Sopenharmony_ci	if (!dev)
84862306a36Sopenharmony_ci		return -ENOMEM;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	ip = netdev_priv(dev);
85362306a36Sopenharmony_ci	ip->dma_dev = pdev->dev.parent;
85462306a36Sopenharmony_ci	ip->regs = devm_platform_ioremap_resource(pdev, 0);
85562306a36Sopenharmony_ci	if (IS_ERR(ip->regs)) {
85662306a36Sopenharmony_ci		err = PTR_ERR(ip->regs);
85762306a36Sopenharmony_ci		goto out_free;
85862306a36Sopenharmony_ci	}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	ip->ssram = devm_platform_ioremap_resource(pdev, 1);
86162306a36Sopenharmony_ci	if (IS_ERR(ip->ssram)) {
86262306a36Sopenharmony_ci		err = PTR_ERR(ip->ssram);
86362306a36Sopenharmony_ci		goto out_free;
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	dev->irq = platform_get_irq(pdev, 0);
86762306a36Sopenharmony_ci	if (dev->irq < 0) {
86862306a36Sopenharmony_ci		err = dev->irq;
86962306a36Sopenharmony_ci		goto out_free;
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	if (devm_request_irq(&pdev->dev, dev->irq, ioc3_interrupt,
87362306a36Sopenharmony_ci			     IRQF_SHARED, "ioc3-eth", dev)) {
87462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Can't get irq %d\n", dev->irq);
87562306a36Sopenharmony_ci		err = -ENODEV;
87662306a36Sopenharmony_ci		goto out_free;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	spin_lock_init(&ip->ioc3_lock);
88062306a36Sopenharmony_ci	timer_setup(&ip->ioc3_timer, ioc3_timer, 0);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	ioc3_stop(ip);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	/* Allocate rx ring.  4kb = 512 entries, must be 4kb aligned */
88562306a36Sopenharmony_ci	ip->rxr = dma_alloc_coherent(ip->dma_dev, RX_RING_SIZE, &ip->rxr_dma,
88662306a36Sopenharmony_ci				     GFP_KERNEL);
88762306a36Sopenharmony_ci	if (!ip->rxr) {
88862306a36Sopenharmony_ci		pr_err("ioc3-eth: rx ring allocation failed\n");
88962306a36Sopenharmony_ci		err = -ENOMEM;
89062306a36Sopenharmony_ci		goto out_stop;
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	/* Allocate tx rings.  16kb = 128 bufs, must be 16kb aligned  */
89462306a36Sopenharmony_ci	ip->tx_ring = dma_alloc_coherent(ip->dma_dev, TX_RING_SIZE + SZ_16K - 1,
89562306a36Sopenharmony_ci					 &ip->txr_dma, GFP_KERNEL);
89662306a36Sopenharmony_ci	if (!ip->tx_ring) {
89762306a36Sopenharmony_ci		pr_err("ioc3-eth: tx ring allocation failed\n");
89862306a36Sopenharmony_ci		err = -ENOMEM;
89962306a36Sopenharmony_ci		goto out_stop;
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci	/* Align TX ring */
90262306a36Sopenharmony_ci	ip->txr = PTR_ALIGN(ip->tx_ring, SZ_16K);
90362306a36Sopenharmony_ci	ip->txr_dma = ALIGN(ip->txr_dma, SZ_16K);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	ioc3_init(dev);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	ip->mii.phy_id_mask = 0x1f;
90862306a36Sopenharmony_ci	ip->mii.reg_num_mask = 0x1f;
90962306a36Sopenharmony_ci	ip->mii.dev = dev;
91062306a36Sopenharmony_ci	ip->mii.mdio_read = ioc3_mdio_read;
91162306a36Sopenharmony_ci	ip->mii.mdio_write = ioc3_mdio_write;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	ioc3_mii_init(ip);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	if (ip->mii.phy_id == -1) {
91662306a36Sopenharmony_ci		netdev_err(dev, "Didn't find a PHY, goodbye.\n");
91762306a36Sopenharmony_ci		err = -ENODEV;
91862306a36Sopenharmony_ci		goto out_stop;
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	ioc3_mii_start(ip);
92262306a36Sopenharmony_ci	ioc3_ssram_disc(ip);
92362306a36Sopenharmony_ci	eth_hw_addr_set(dev, mac_addr);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	/* The IOC3-specific entries in the device structure. */
92662306a36Sopenharmony_ci	dev->watchdog_timeo	= 5 * HZ;
92762306a36Sopenharmony_ci	dev->netdev_ops		= &ioc3_netdev_ops;
92862306a36Sopenharmony_ci	dev->ethtool_ops	= &ioc3_ethtool_ops;
92962306a36Sopenharmony_ci	dev->hw_features	= NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
93062306a36Sopenharmony_ci	dev->features		= NETIF_F_IP_CSUM | NETIF_F_HIGHDMA;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	sw_physid1 = ioc3_mdio_read(dev, ip->mii.phy_id, MII_PHYSID1);
93362306a36Sopenharmony_ci	sw_physid2 = ioc3_mdio_read(dev, ip->mii.phy_id, MII_PHYSID2);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	err = register_netdev(dev);
93662306a36Sopenharmony_ci	if (err)
93762306a36Sopenharmony_ci		goto out_stop;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	mii_check_media(&ip->mii, 1, 1);
94062306a36Sopenharmony_ci	ioc3_setup_duplex(ip);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	vendor = (sw_physid1 << 12) | (sw_physid2 >> 4);
94362306a36Sopenharmony_ci	model  = (sw_physid2 >> 4) & 0x3f;
94462306a36Sopenharmony_ci	rev    = sw_physid2 & 0xf;
94562306a36Sopenharmony_ci	netdev_info(dev, "Using PHY %d, vendor 0x%x, model %d, rev %d.\n",
94662306a36Sopenharmony_ci		    ip->mii.phy_id, vendor, model, rev);
94762306a36Sopenharmony_ci	netdev_info(dev, "IOC3 SSRAM has %d kbyte.\n",
94862306a36Sopenharmony_ci		    ip->emcr & EMCR_BUFSIZ ? 128 : 64);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	return 0;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ciout_stop:
95362306a36Sopenharmony_ci	del_timer_sync(&ip->ioc3_timer);
95462306a36Sopenharmony_ci	if (ip->rxr)
95562306a36Sopenharmony_ci		dma_free_coherent(ip->dma_dev, RX_RING_SIZE, ip->rxr,
95662306a36Sopenharmony_ci				  ip->rxr_dma);
95762306a36Sopenharmony_ci	if (ip->tx_ring)
95862306a36Sopenharmony_ci		dma_free_coherent(ip->dma_dev, TX_RING_SIZE + SZ_16K - 1, ip->tx_ring,
95962306a36Sopenharmony_ci				  ip->txr_dma);
96062306a36Sopenharmony_ciout_free:
96162306a36Sopenharmony_ci	free_netdev(dev);
96262306a36Sopenharmony_ci	return err;
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_cistatic int ioc3eth_remove(struct platform_device *pdev)
96662306a36Sopenharmony_ci{
96762306a36Sopenharmony_ci	struct net_device *dev = platform_get_drvdata(pdev);
96862306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	dma_free_coherent(ip->dma_dev, RX_RING_SIZE, ip->rxr, ip->rxr_dma);
97162306a36Sopenharmony_ci	dma_free_coherent(ip->dma_dev, TX_RING_SIZE + SZ_16K - 1, ip->tx_ring, ip->txr_dma);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	unregister_netdev(dev);
97462306a36Sopenharmony_ci	del_timer_sync(&ip->ioc3_timer);
97562306a36Sopenharmony_ci	free_netdev(dev);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	return 0;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_cistatic netdev_tx_t ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev)
98262306a36Sopenharmony_ci{
98362306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
98462306a36Sopenharmony_ci	struct ioc3_etxd *desc;
98562306a36Sopenharmony_ci	unsigned long data;
98662306a36Sopenharmony_ci	unsigned int len;
98762306a36Sopenharmony_ci	int produce;
98862306a36Sopenharmony_ci	u32 w0 = 0;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	/* IOC3 has a fairly simple minded checksumming hardware which simply
99162306a36Sopenharmony_ci	 * adds up the 1's complement checksum for the entire packet and
99262306a36Sopenharmony_ci	 * inserts it at an offset which can be specified in the descriptor
99362306a36Sopenharmony_ci	 * into the transmit packet.  This means we have to compensate for the
99462306a36Sopenharmony_ci	 * MAC header which should not be summed and the TCP/UDP pseudo headers
99562306a36Sopenharmony_ci	 * manually.
99662306a36Sopenharmony_ci	 */
99762306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL) {
99862306a36Sopenharmony_ci		const struct iphdr *ih = ip_hdr(skb);
99962306a36Sopenharmony_ci		const int proto = ntohs(ih->protocol);
100062306a36Sopenharmony_ci		unsigned int csoff;
100162306a36Sopenharmony_ci		u32 csum, ehsum;
100262306a36Sopenharmony_ci		u16 *eh;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci		/* The MAC header.  skb->mac seem the logic approach
100562306a36Sopenharmony_ci		 * to find the MAC header - except it's a NULL pointer ...
100662306a36Sopenharmony_ci		 */
100762306a36Sopenharmony_ci		eh = (u16 *)skb->data;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci		/* Sum up dest addr, src addr and protocol  */
101062306a36Sopenharmony_ci		ehsum = eh[0] + eh[1] + eh[2] + eh[3] + eh[4] + eh[5] + eh[6];
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci		/* Skip IP header; it's sum is always zero and was
101362306a36Sopenharmony_ci		 * already filled in by ip_output.c
101462306a36Sopenharmony_ci		 */
101562306a36Sopenharmony_ci		csum = csum_tcpudp_nofold(ih->saddr, ih->daddr,
101662306a36Sopenharmony_ci					  ih->tot_len - (ih->ihl << 2),
101762306a36Sopenharmony_ci					  proto, csum_fold(ehsum));
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci		csum = (csum & 0xffff) + (csum >> 16);	/* Fold again */
102062306a36Sopenharmony_ci		csum = (csum & 0xffff) + (csum >> 16);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci		csoff = ETH_HLEN + (ih->ihl << 2);
102362306a36Sopenharmony_ci		if (proto == IPPROTO_UDP) {
102462306a36Sopenharmony_ci			csoff += offsetof(struct udphdr, check);
102562306a36Sopenharmony_ci			udp_hdr(skb)->check = csum;
102662306a36Sopenharmony_ci		}
102762306a36Sopenharmony_ci		if (proto == IPPROTO_TCP) {
102862306a36Sopenharmony_ci			csoff += offsetof(struct tcphdr, check);
102962306a36Sopenharmony_ci			tcp_hdr(skb)->check = csum;
103062306a36Sopenharmony_ci		}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci		w0 = ETXD_DOCHECKSUM | (csoff << ETXD_CHKOFF_SHIFT);
103362306a36Sopenharmony_ci	}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	spin_lock_irq(&ip->ioc3_lock);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	data = (unsigned long)skb->data;
103862306a36Sopenharmony_ci	len = skb->len;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	produce = ip->tx_pi;
104162306a36Sopenharmony_ci	desc = &ip->txr[produce];
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	if (len <= 104) {
104462306a36Sopenharmony_ci		/* Short packet, let's copy it directly into the ring.  */
104562306a36Sopenharmony_ci		skb_copy_from_linear_data(skb, desc->data, skb->len);
104662306a36Sopenharmony_ci		if (len < ETH_ZLEN) {
104762306a36Sopenharmony_ci			/* Very short packet, pad with zeros at the end. */
104862306a36Sopenharmony_ci			memset(desc->data + len, 0, ETH_ZLEN - len);
104962306a36Sopenharmony_ci			len = ETH_ZLEN;
105062306a36Sopenharmony_ci		}
105162306a36Sopenharmony_ci		desc->cmd = cpu_to_be32(len | ETXD_INTWHENDONE | ETXD_D0V | w0);
105262306a36Sopenharmony_ci		desc->bufcnt = cpu_to_be32(len);
105362306a36Sopenharmony_ci	} else if ((data ^ (data + len - 1)) & 0x4000) {
105462306a36Sopenharmony_ci		unsigned long b2 = (data | 0x3fffUL) + 1UL;
105562306a36Sopenharmony_ci		unsigned long s1 = b2 - data;
105662306a36Sopenharmony_ci		unsigned long s2 = data + len - b2;
105762306a36Sopenharmony_ci		dma_addr_t d1, d2;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci		desc->cmd    = cpu_to_be32(len | ETXD_INTWHENDONE |
106062306a36Sopenharmony_ci					   ETXD_B1V | ETXD_B2V | w0);
106162306a36Sopenharmony_ci		desc->bufcnt = cpu_to_be32((s1 << ETXD_B1CNT_SHIFT) |
106262306a36Sopenharmony_ci					   (s2 << ETXD_B2CNT_SHIFT));
106362306a36Sopenharmony_ci		d1 = dma_map_single(ip->dma_dev, skb->data, s1, DMA_TO_DEVICE);
106462306a36Sopenharmony_ci		if (dma_mapping_error(ip->dma_dev, d1))
106562306a36Sopenharmony_ci			goto drop_packet;
106662306a36Sopenharmony_ci		d2 = dma_map_single(ip->dma_dev, (void *)b2, s1, DMA_TO_DEVICE);
106762306a36Sopenharmony_ci		if (dma_mapping_error(ip->dma_dev, d2)) {
106862306a36Sopenharmony_ci			dma_unmap_single(ip->dma_dev, d1, len, DMA_TO_DEVICE);
106962306a36Sopenharmony_ci			goto drop_packet;
107062306a36Sopenharmony_ci		}
107162306a36Sopenharmony_ci		desc->p1     = cpu_to_be64(ioc3_map(d1, PCI64_ATTR_PREF));
107262306a36Sopenharmony_ci		desc->p2     = cpu_to_be64(ioc3_map(d2, PCI64_ATTR_PREF));
107362306a36Sopenharmony_ci	} else {
107462306a36Sopenharmony_ci		dma_addr_t d;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci		/* Normal sized packet that doesn't cross a page boundary. */
107762306a36Sopenharmony_ci		desc->cmd = cpu_to_be32(len | ETXD_INTWHENDONE | ETXD_B1V | w0);
107862306a36Sopenharmony_ci		desc->bufcnt = cpu_to_be32(len << ETXD_B1CNT_SHIFT);
107962306a36Sopenharmony_ci		d = dma_map_single(ip->dma_dev, skb->data, len, DMA_TO_DEVICE);
108062306a36Sopenharmony_ci		if (dma_mapping_error(ip->dma_dev, d))
108162306a36Sopenharmony_ci			goto drop_packet;
108262306a36Sopenharmony_ci		desc->p1     = cpu_to_be64(ioc3_map(d, PCI64_ATTR_PREF));
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	mb(); /* make sure all descriptor changes are visible */
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	ip->tx_skbs[produce] = skb;			/* Remember skb */
108862306a36Sopenharmony_ci	produce = (produce + 1) & TX_RING_MASK;
108962306a36Sopenharmony_ci	ip->tx_pi = produce;
109062306a36Sopenharmony_ci	writel(produce << 7, &ip->regs->etpir);		/* Fire ... */
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	ip->txqlen++;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	if (ip->txqlen >= (TX_RING_ENTRIES - 1))
109562306a36Sopenharmony_ci		netif_stop_queue(dev);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	spin_unlock_irq(&ip->ioc3_lock);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	return NETDEV_TX_OK;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_cidrop_packet:
110262306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
110362306a36Sopenharmony_ci	dev->stats.tx_dropped++;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	spin_unlock_irq(&ip->ioc3_lock);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	return NETDEV_TX_OK;
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_cistatic void ioc3_timeout(struct net_device *dev, unsigned int txqueue)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	netdev_err(dev, "transmit timed out, resetting\n");
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	spin_lock_irq(&ip->ioc3_lock);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	ioc3_stop(ip);
111962306a36Sopenharmony_ci	ioc3_free_rx_bufs(ip);
112062306a36Sopenharmony_ci	ioc3_clean_tx_ring(ip);
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	ioc3_init(dev);
112362306a36Sopenharmony_ci	if (ioc3_alloc_rx_bufs(dev)) {
112462306a36Sopenharmony_ci		netdev_err(dev, "%s: rx buffer allocation failed\n", __func__);
112562306a36Sopenharmony_ci		spin_unlock_irq(&ip->ioc3_lock);
112662306a36Sopenharmony_ci		return;
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci	ioc3_start(ip);
112962306a36Sopenharmony_ci	ioc3_mii_init(ip);
113062306a36Sopenharmony_ci	ioc3_mii_start(ip);
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	spin_unlock_irq(&ip->ioc3_lock);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	netif_wake_queue(dev);
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci/* Given a multicast ethernet address, this routine calculates the
113862306a36Sopenharmony_ci * address's bit index in the logical address filter mask
113962306a36Sopenharmony_ci */
114062306a36Sopenharmony_cistatic inline unsigned int ioc3_hash(const unsigned char *addr)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	unsigned int temp = 0;
114362306a36Sopenharmony_ci	int bits;
114462306a36Sopenharmony_ci	u32 crc;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	crc = ether_crc_le(ETH_ALEN, addr);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	crc &= 0x3f;    /* bit reverse lowest 6 bits for hash index */
114962306a36Sopenharmony_ci	for (bits = 6; --bits >= 0; ) {
115062306a36Sopenharmony_ci		temp <<= 1;
115162306a36Sopenharmony_ci		temp |= (crc & 0x1);
115262306a36Sopenharmony_ci		crc >>= 1;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	return temp;
115662306a36Sopenharmony_ci}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_cistatic void ioc3_get_drvinfo(struct net_device *dev,
115962306a36Sopenharmony_ci			     struct ethtool_drvinfo *info)
116062306a36Sopenharmony_ci{
116162306a36Sopenharmony_ci	strscpy(info->driver, IOC3_NAME, sizeof(info->driver));
116262306a36Sopenharmony_ci	strscpy(info->version, IOC3_VERSION, sizeof(info->version));
116362306a36Sopenharmony_ci	strscpy(info->bus_info, pci_name(to_pci_dev(dev->dev.parent)),
116462306a36Sopenharmony_ci		sizeof(info->bus_info));
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_cistatic int ioc3_get_link_ksettings(struct net_device *dev,
116862306a36Sopenharmony_ci				   struct ethtool_link_ksettings *cmd)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	spin_lock_irq(&ip->ioc3_lock);
117362306a36Sopenharmony_ci	mii_ethtool_get_link_ksettings(&ip->mii, cmd);
117462306a36Sopenharmony_ci	spin_unlock_irq(&ip->ioc3_lock);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	return 0;
117762306a36Sopenharmony_ci}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_cistatic int ioc3_set_link_ksettings(struct net_device *dev,
118062306a36Sopenharmony_ci				   const struct ethtool_link_ksettings *cmd)
118162306a36Sopenharmony_ci{
118262306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
118362306a36Sopenharmony_ci	int rc;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	spin_lock_irq(&ip->ioc3_lock);
118662306a36Sopenharmony_ci	rc = mii_ethtool_set_link_ksettings(&ip->mii, cmd);
118762306a36Sopenharmony_ci	spin_unlock_irq(&ip->ioc3_lock);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	return rc;
119062306a36Sopenharmony_ci}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_cistatic int ioc3_nway_reset(struct net_device *dev)
119362306a36Sopenharmony_ci{
119462306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
119562306a36Sopenharmony_ci	int rc;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	spin_lock_irq(&ip->ioc3_lock);
119862306a36Sopenharmony_ci	rc = mii_nway_restart(&ip->mii);
119962306a36Sopenharmony_ci	spin_unlock_irq(&ip->ioc3_lock);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	return rc;
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_cistatic u32 ioc3_get_link(struct net_device *dev)
120562306a36Sopenharmony_ci{
120662306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
120762306a36Sopenharmony_ci	int rc;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	spin_lock_irq(&ip->ioc3_lock);
121062306a36Sopenharmony_ci	rc = mii_link_ok(&ip->mii);
121162306a36Sopenharmony_ci	spin_unlock_irq(&ip->ioc3_lock);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	return rc;
121462306a36Sopenharmony_ci}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_cistatic const struct ethtool_ops ioc3_ethtool_ops = {
121762306a36Sopenharmony_ci	.get_drvinfo		= ioc3_get_drvinfo,
121862306a36Sopenharmony_ci	.nway_reset		= ioc3_nway_reset,
121962306a36Sopenharmony_ci	.get_link		= ioc3_get_link,
122062306a36Sopenharmony_ci	.get_link_ksettings	= ioc3_get_link_ksettings,
122162306a36Sopenharmony_ci	.set_link_ksettings	= ioc3_set_link_ksettings,
122262306a36Sopenharmony_ci};
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_cistatic int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
122562306a36Sopenharmony_ci{
122662306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
122762306a36Sopenharmony_ci	int rc;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	spin_lock_irq(&ip->ioc3_lock);
123062306a36Sopenharmony_ci	rc = generic_mii_ioctl(&ip->mii, if_mii(rq), cmd, NULL);
123162306a36Sopenharmony_ci	spin_unlock_irq(&ip->ioc3_lock);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	return rc;
123462306a36Sopenharmony_ci}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_cistatic void ioc3_set_multicast_list(struct net_device *dev)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	struct ioc3_private *ip = netdev_priv(dev);
123962306a36Sopenharmony_ci	struct ioc3_ethregs *regs = ip->regs;
124062306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
124162306a36Sopenharmony_ci	u64 ehar = 0;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	spin_lock_irq(&ip->ioc3_lock);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous.  */
124662306a36Sopenharmony_ci		ip->emcr |= EMCR_PROMISC;
124762306a36Sopenharmony_ci		writel(ip->emcr, &regs->emcr);
124862306a36Sopenharmony_ci		readl(&regs->emcr);
124962306a36Sopenharmony_ci	} else {
125062306a36Sopenharmony_ci		ip->emcr &= ~EMCR_PROMISC;
125162306a36Sopenharmony_ci		writel(ip->emcr, &regs->emcr);		/* Clear promiscuous. */
125262306a36Sopenharmony_ci		readl(&regs->emcr);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci		if ((dev->flags & IFF_ALLMULTI) ||
125562306a36Sopenharmony_ci		    (netdev_mc_count(dev) > 64)) {
125662306a36Sopenharmony_ci			/* Too many for hashing to make sense or we want all
125762306a36Sopenharmony_ci			 * multicast packets anyway,  so skip computing all the
125862306a36Sopenharmony_ci			 * hashes and just accept all packets.
125962306a36Sopenharmony_ci			 */
126062306a36Sopenharmony_ci			ip->ehar_h = 0xffffffff;
126162306a36Sopenharmony_ci			ip->ehar_l = 0xffffffff;
126262306a36Sopenharmony_ci		} else {
126362306a36Sopenharmony_ci			netdev_for_each_mc_addr(ha, dev) {
126462306a36Sopenharmony_ci				ehar |= (1UL << ioc3_hash(ha->addr));
126562306a36Sopenharmony_ci			}
126662306a36Sopenharmony_ci			ip->ehar_h = ehar >> 32;
126762306a36Sopenharmony_ci			ip->ehar_l = ehar & 0xffffffff;
126862306a36Sopenharmony_ci		}
126962306a36Sopenharmony_ci		writel(ip->ehar_h, &regs->ehar_h);
127062306a36Sopenharmony_ci		writel(ip->ehar_l, &regs->ehar_l);
127162306a36Sopenharmony_ci	}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	spin_unlock_irq(&ip->ioc3_lock);
127462306a36Sopenharmony_ci}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_cistatic struct platform_driver ioc3eth_driver = {
127762306a36Sopenharmony_ci	.probe  = ioc3eth_probe,
127862306a36Sopenharmony_ci	.remove = ioc3eth_remove,
127962306a36Sopenharmony_ci	.driver = {
128062306a36Sopenharmony_ci		.name = "ioc3-eth",
128162306a36Sopenharmony_ci	}
128262306a36Sopenharmony_ci};
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_cimodule_platform_driver(ioc3eth_driver);
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ciMODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
128762306a36Sopenharmony_ciMODULE_DESCRIPTION("SGI IOC3 Ethernet driver");
128862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1289