162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2006-2007 PA Semi, Inc
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Driver for the PA Semi PWRficient onchip 1G/10G Ethernet MACs
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/pci.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/dmaengine.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/netdevice.h>
1562306a36Sopenharmony_ci#include <linux/of_mdio.h>
1662306a36Sopenharmony_ci#include <linux/etherdevice.h>
1762306a36Sopenharmony_ci#include <asm/dma-mapping.h>
1862306a36Sopenharmony_ci#include <linux/in.h>
1962306a36Sopenharmony_ci#include <linux/skbuff.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/ip.h>
2262306a36Sopenharmony_ci#include <net/checksum.h>
2362306a36Sopenharmony_ci#include <linux/prefetch.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <asm/irq.h>
2662306a36Sopenharmony_ci#include <asm/firmware.h>
2762306a36Sopenharmony_ci#include <asm/pasemi_dma.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "pasemi_mac.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* We have our own align, since ppc64 in general has it at 0 because
3262306a36Sopenharmony_ci * of design flaws in some of the server bridge chips. However, for
3362306a36Sopenharmony_ci * PWRficient doing the unaligned copies is more expensive than doing
3462306a36Sopenharmony_ci * unaligned DMA, so make sure the data is aligned instead.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci#define LOCAL_SKB_ALIGN	2
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* TODO list
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * - Multicast support
4162306a36Sopenharmony_ci * - Large MTU support
4262306a36Sopenharmony_ci * - Multiqueue RX/TX
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define PE_MIN_MTU	(ETH_ZLEN + ETH_HLEN)
4662306a36Sopenharmony_ci#define PE_MAX_MTU	9000
4762306a36Sopenharmony_ci#define PE_DEF_MTU	ETH_DATA_LEN
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define DEFAULT_MSG_ENABLE	  \
5062306a36Sopenharmony_ci	(NETIF_MSG_DRV		| \
5162306a36Sopenharmony_ci	 NETIF_MSG_PROBE	| \
5262306a36Sopenharmony_ci	 NETIF_MSG_LINK		| \
5362306a36Sopenharmony_ci	 NETIF_MSG_TIMER	| \
5462306a36Sopenharmony_ci	 NETIF_MSG_IFDOWN	| \
5562306a36Sopenharmony_ci	 NETIF_MSG_IFUP		| \
5662306a36Sopenharmony_ci	 NETIF_MSG_RX_ERR	| \
5762306a36Sopenharmony_ci	 NETIF_MSG_TX_ERR)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
6062306a36Sopenharmony_ciMODULE_AUTHOR ("Olof Johansson <olof@lixom.net>");
6162306a36Sopenharmony_ciMODULE_DESCRIPTION("PA Semi PWRficient Ethernet driver");
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int debug = -1;	/* -1 == use DEFAULT_MSG_ENABLE as value */
6462306a36Sopenharmony_cimodule_param(debug, int, 0);
6562306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "PA Semi MAC bitmapped debugging message enable value");
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ciextern const struct ethtool_ops pasemi_mac_ethtool_ops;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int translation_enabled(void)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci#if defined(CONFIG_PPC_PASEMI_IOMMU_DMA_FORCE)
7262306a36Sopenharmony_ci	return 1;
7362306a36Sopenharmony_ci#else
7462306a36Sopenharmony_ci	return firmware_has_feature(FW_FEATURE_LPAR);
7562306a36Sopenharmony_ci#endif
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void write_iob_reg(unsigned int reg, unsigned int val)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	pasemi_write_iob_reg(reg, val);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic unsigned int read_mac_reg(const struct pasemi_mac *mac, unsigned int reg)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	return pasemi_read_mac_reg(mac->dma_if, reg);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void write_mac_reg(const struct pasemi_mac *mac, unsigned int reg,
8962306a36Sopenharmony_ci			  unsigned int val)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	pasemi_write_mac_reg(mac->dma_if, reg, val);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic unsigned int read_dma_reg(unsigned int reg)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	return pasemi_read_dma_reg(reg);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void write_dma_reg(unsigned int reg, unsigned int val)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	pasemi_write_dma_reg(reg, val);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic struct pasemi_mac_rxring *rx_ring(const struct pasemi_mac *mac)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	return mac->rx;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic struct pasemi_mac_txring *tx_ring(const struct pasemi_mac *mac)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	return mac->tx;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic inline void prefetch_skb(const struct sk_buff *skb)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	const void *d = skb;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	prefetch(d);
11962306a36Sopenharmony_ci	prefetch(d+64);
12062306a36Sopenharmony_ci	prefetch(d+128);
12162306a36Sopenharmony_ci	prefetch(d+192);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic int mac_to_intf(struct pasemi_mac *mac)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct pci_dev *pdev = mac->pdev;
12762306a36Sopenharmony_ci	u32 tmp;
12862306a36Sopenharmony_ci	int nintf, off, i, j;
12962306a36Sopenharmony_ci	int devfn = pdev->devfn;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	tmp = read_dma_reg(PAS_DMA_CAP_IFI);
13262306a36Sopenharmony_ci	nintf = (tmp & PAS_DMA_CAP_IFI_NIN_M) >> PAS_DMA_CAP_IFI_NIN_S;
13362306a36Sopenharmony_ci	off = (tmp & PAS_DMA_CAP_IFI_IOFF_M) >> PAS_DMA_CAP_IFI_IOFF_S;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/* IOFF contains the offset to the registers containing the
13662306a36Sopenharmony_ci	 * DMA interface-to-MAC-pci-id mappings, and NIN contains number
13762306a36Sopenharmony_ci	 * of total interfaces. Each register contains 4 devfns.
13862306a36Sopenharmony_ci	 * Just do a linear search until we find the devfn of the MAC
13962306a36Sopenharmony_ci	 * we're trying to look up.
14062306a36Sopenharmony_ci	 */
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	for (i = 0; i < (nintf+3)/4; i++) {
14362306a36Sopenharmony_ci		tmp = read_dma_reg(off+4*i);
14462306a36Sopenharmony_ci		for (j = 0; j < 4; j++) {
14562306a36Sopenharmony_ci			if (((tmp >> (8*j)) & 0xff) == devfn)
14662306a36Sopenharmony_ci				return i*4 + j;
14762306a36Sopenharmony_ci		}
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci	return -1;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void pasemi_mac_intf_disable(struct pasemi_mac *mac)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	unsigned int flags;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG);
15762306a36Sopenharmony_ci	flags &= ~PAS_MAC_CFG_PCFG_PE;
15862306a36Sopenharmony_ci	write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void pasemi_mac_intf_enable(struct pasemi_mac *mac)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	unsigned int flags;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG);
16662306a36Sopenharmony_ci	flags |= PAS_MAC_CFG_PCFG_PE;
16762306a36Sopenharmony_ci	write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic int pasemi_get_mac_addr(struct pasemi_mac *mac)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct pci_dev *pdev = mac->pdev;
17362306a36Sopenharmony_ci	struct device_node *dn = pci_device_to_OF_node(pdev);
17462306a36Sopenharmony_ci	int len;
17562306a36Sopenharmony_ci	const u8 *maddr;
17662306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (!dn) {
17962306a36Sopenharmony_ci		dev_dbg(&pdev->dev,
18062306a36Sopenharmony_ci			  "No device node for mac, not configuring\n");
18162306a36Sopenharmony_ci		return -ENOENT;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	maddr = of_get_property(dn, "local-mac-address", &len);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (maddr && len == ETH_ALEN) {
18762306a36Sopenharmony_ci		memcpy(mac->mac_addr, maddr, ETH_ALEN);
18862306a36Sopenharmony_ci		return 0;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* Some old versions of firmware mistakenly uses mac-address
19262306a36Sopenharmony_ci	 * (and as a string) instead of a byte array in local-mac-address.
19362306a36Sopenharmony_ci	 */
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (maddr == NULL)
19662306a36Sopenharmony_ci		maddr = of_get_property(dn, "mac-address", NULL);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (maddr == NULL) {
19962306a36Sopenharmony_ci		dev_warn(&pdev->dev,
20062306a36Sopenharmony_ci			 "no mac address in device tree, not configuring\n");
20162306a36Sopenharmony_ci		return -ENOENT;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (!mac_pton(maddr, addr)) {
20562306a36Sopenharmony_ci		dev_warn(&pdev->dev,
20662306a36Sopenharmony_ci			 "can't parse mac address, not configuring\n");
20762306a36Sopenharmony_ci		return -EINVAL;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	memcpy(mac->mac_addr, addr, ETH_ALEN);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	return 0;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int pasemi_mac_set_mac_addr(struct net_device *dev, void *p)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct pasemi_mac *mac = netdev_priv(dev);
21862306a36Sopenharmony_ci	struct sockaddr *addr = p;
21962306a36Sopenharmony_ci	unsigned int adr0, adr1;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
22262306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr->sa_data);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	adr0 = dev->dev_addr[2] << 24 |
22762306a36Sopenharmony_ci	       dev->dev_addr[3] << 16 |
22862306a36Sopenharmony_ci	       dev->dev_addr[4] << 8 |
22962306a36Sopenharmony_ci	       dev->dev_addr[5];
23062306a36Sopenharmony_ci	adr1 = read_mac_reg(mac, PAS_MAC_CFG_ADR1);
23162306a36Sopenharmony_ci	adr1 &= ~0xffff;
23262306a36Sopenharmony_ci	adr1 |= dev->dev_addr[0] << 8 | dev->dev_addr[1];
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	pasemi_mac_intf_disable(mac);
23562306a36Sopenharmony_ci	write_mac_reg(mac, PAS_MAC_CFG_ADR0, adr0);
23662306a36Sopenharmony_ci	write_mac_reg(mac, PAS_MAC_CFG_ADR1, adr1);
23762306a36Sopenharmony_ci	pasemi_mac_intf_enable(mac);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	return 0;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic int pasemi_mac_unmap_tx_skb(struct pasemi_mac *mac,
24362306a36Sopenharmony_ci				    const int nfrags,
24462306a36Sopenharmony_ci				    struct sk_buff *skb,
24562306a36Sopenharmony_ci				    const dma_addr_t *dmas)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	int f;
24862306a36Sopenharmony_ci	struct pci_dev *pdev = mac->dma_pdev;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	dma_unmap_single(&pdev->dev, dmas[0], skb_headlen(skb), DMA_TO_DEVICE);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	for (f = 0; f < nfrags; f++) {
25362306a36Sopenharmony_ci		const skb_frag_t *frag = &skb_shinfo(skb)->frags[f];
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		dma_unmap_page(&pdev->dev, dmas[f + 1], skb_frag_size(frag),
25662306a36Sopenharmony_ci			       DMA_TO_DEVICE);
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci	dev_kfree_skb_irq(skb);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* Freed descriptor slot + main SKB ptr + nfrags additional ptrs,
26162306a36Sopenharmony_ci	 * aligned up to a power of 2
26262306a36Sopenharmony_ci	 */
26362306a36Sopenharmony_ci	return (nfrags + 3) & ~1;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic struct pasemi_mac_csring *pasemi_mac_setup_csring(struct pasemi_mac *mac)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct pasemi_mac_csring *ring;
26962306a36Sopenharmony_ci	u32 val;
27062306a36Sopenharmony_ci	unsigned int cfg;
27162306a36Sopenharmony_ci	int chno;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	ring = pasemi_dma_alloc_chan(TXCHAN, sizeof(struct pasemi_mac_csring),
27462306a36Sopenharmony_ci				       offsetof(struct pasemi_mac_csring, chan));
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (!ring) {
27762306a36Sopenharmony_ci		dev_err(&mac->pdev->dev, "Can't allocate checksum channel\n");
27862306a36Sopenharmony_ci		goto out_chan;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	chno = ring->chan.chno;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	ring->size = CS_RING_SIZE;
28462306a36Sopenharmony_ci	ring->next_to_fill = 0;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* Allocate descriptors */
28762306a36Sopenharmony_ci	if (pasemi_dma_alloc_ring(&ring->chan, CS_RING_SIZE))
28862306a36Sopenharmony_ci		goto out_ring_desc;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_TXCHAN_BASEL(chno),
29162306a36Sopenharmony_ci		      PAS_DMA_TXCHAN_BASEL_BRBL(ring->chan.ring_dma));
29262306a36Sopenharmony_ci	val = PAS_DMA_TXCHAN_BASEU_BRBH(ring->chan.ring_dma >> 32);
29362306a36Sopenharmony_ci	val |= PAS_DMA_TXCHAN_BASEU_SIZ(CS_RING_SIZE >> 3);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_TXCHAN_BASEU(chno), val);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	ring->events[0] = pasemi_dma_alloc_flag();
29862306a36Sopenharmony_ci	ring->events[1] = pasemi_dma_alloc_flag();
29962306a36Sopenharmony_ci	if (ring->events[0] < 0 || ring->events[1] < 0)
30062306a36Sopenharmony_ci		goto out_flags;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	pasemi_dma_clear_flag(ring->events[0]);
30362306a36Sopenharmony_ci	pasemi_dma_clear_flag(ring->events[1]);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	ring->fun = pasemi_dma_alloc_fun();
30662306a36Sopenharmony_ci	if (ring->fun < 0)
30762306a36Sopenharmony_ci		goto out_fun;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	cfg = PAS_DMA_TXCHAN_CFG_TY_FUNC | PAS_DMA_TXCHAN_CFG_UP |
31062306a36Sopenharmony_ci	      PAS_DMA_TXCHAN_CFG_TATTR(ring->fun) |
31162306a36Sopenharmony_ci	      PAS_DMA_TXCHAN_CFG_LPSQ | PAS_DMA_TXCHAN_CFG_LPDQ;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (translation_enabled())
31462306a36Sopenharmony_ci		cfg |= PAS_DMA_TXCHAN_CFG_TRD | PAS_DMA_TXCHAN_CFG_TRR;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_TXCHAN_CFG(chno), cfg);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	/* enable channel */
31962306a36Sopenharmony_ci	pasemi_dma_start_chan(&ring->chan, PAS_DMA_TXCHAN_TCMDSTA_SZ |
32062306a36Sopenharmony_ci					   PAS_DMA_TXCHAN_TCMDSTA_DB |
32162306a36Sopenharmony_ci					   PAS_DMA_TXCHAN_TCMDSTA_DE |
32262306a36Sopenharmony_ci					   PAS_DMA_TXCHAN_TCMDSTA_DA);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return ring;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ciout_fun:
32762306a36Sopenharmony_ciout_flags:
32862306a36Sopenharmony_ci	if (ring->events[0] >= 0)
32962306a36Sopenharmony_ci		pasemi_dma_free_flag(ring->events[0]);
33062306a36Sopenharmony_ci	if (ring->events[1] >= 0)
33162306a36Sopenharmony_ci		pasemi_dma_free_flag(ring->events[1]);
33262306a36Sopenharmony_ci	pasemi_dma_free_ring(&ring->chan);
33362306a36Sopenharmony_ciout_ring_desc:
33462306a36Sopenharmony_ci	pasemi_dma_free_chan(&ring->chan);
33562306a36Sopenharmony_ciout_chan:
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return NULL;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic void pasemi_mac_setup_csrings(struct pasemi_mac *mac)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	int i;
34362306a36Sopenharmony_ci	mac->cs[0] = pasemi_mac_setup_csring(mac);
34462306a36Sopenharmony_ci	if (mac->type == MAC_TYPE_XAUI)
34562306a36Sopenharmony_ci		mac->cs[1] = pasemi_mac_setup_csring(mac);
34662306a36Sopenharmony_ci	else
34762306a36Sopenharmony_ci		mac->cs[1] = 0;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	for (i = 0; i < MAX_CS; i++)
35062306a36Sopenharmony_ci		if (mac->cs[i])
35162306a36Sopenharmony_ci			mac->num_cs++;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic void pasemi_mac_free_csring(struct pasemi_mac_csring *csring)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	pasemi_dma_stop_chan(&csring->chan);
35762306a36Sopenharmony_ci	pasemi_dma_free_flag(csring->events[0]);
35862306a36Sopenharmony_ci	pasemi_dma_free_flag(csring->events[1]);
35962306a36Sopenharmony_ci	pasemi_dma_free_ring(&csring->chan);
36062306a36Sopenharmony_ci	pasemi_dma_free_chan(&csring->chan);
36162306a36Sopenharmony_ci	pasemi_dma_free_fun(csring->fun);
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic int pasemi_mac_setup_rx_resources(const struct net_device *dev)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct pasemi_mac_rxring *ring;
36762306a36Sopenharmony_ci	struct pasemi_mac *mac = netdev_priv(dev);
36862306a36Sopenharmony_ci	int chno;
36962306a36Sopenharmony_ci	unsigned int cfg;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	ring = pasemi_dma_alloc_chan(RXCHAN, sizeof(struct pasemi_mac_rxring),
37262306a36Sopenharmony_ci				     offsetof(struct pasemi_mac_rxring, chan));
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (!ring) {
37562306a36Sopenharmony_ci		dev_err(&mac->pdev->dev, "Can't allocate RX channel\n");
37662306a36Sopenharmony_ci		goto out_chan;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci	chno = ring->chan.chno;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	spin_lock_init(&ring->lock);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	ring->size = RX_RING_SIZE;
38362306a36Sopenharmony_ci	ring->ring_info = kcalloc(RX_RING_SIZE,
38462306a36Sopenharmony_ci				  sizeof(struct pasemi_mac_buffer),
38562306a36Sopenharmony_ci				  GFP_KERNEL);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (!ring->ring_info)
38862306a36Sopenharmony_ci		goto out_ring_info;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	/* Allocate descriptors */
39162306a36Sopenharmony_ci	if (pasemi_dma_alloc_ring(&ring->chan, RX_RING_SIZE))
39262306a36Sopenharmony_ci		goto out_ring_desc;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	ring->buffers = dma_alloc_coherent(&mac->dma_pdev->dev,
39562306a36Sopenharmony_ci					   RX_RING_SIZE * sizeof(u64),
39662306a36Sopenharmony_ci					   &ring->buf_dma, GFP_KERNEL);
39762306a36Sopenharmony_ci	if (!ring->buffers)
39862306a36Sopenharmony_ci		goto out_ring_desc;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXCHAN_BASEL(chno),
40162306a36Sopenharmony_ci		      PAS_DMA_RXCHAN_BASEL_BRBL(ring->chan.ring_dma));
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXCHAN_BASEU(chno),
40462306a36Sopenharmony_ci		      PAS_DMA_RXCHAN_BASEU_BRBH(ring->chan.ring_dma >> 32) |
40562306a36Sopenharmony_ci		      PAS_DMA_RXCHAN_BASEU_SIZ(RX_RING_SIZE >> 3));
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	cfg = PAS_DMA_RXCHAN_CFG_HBU(2);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (translation_enabled())
41062306a36Sopenharmony_ci		cfg |= PAS_DMA_RXCHAN_CFG_CTR;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXCHAN_CFG(chno), cfg);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXINT_BASEL(mac->dma_if),
41562306a36Sopenharmony_ci		      PAS_DMA_RXINT_BASEL_BRBL(ring->buf_dma));
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXINT_BASEU(mac->dma_if),
41862306a36Sopenharmony_ci		      PAS_DMA_RXINT_BASEU_BRBH(ring->buf_dma >> 32) |
41962306a36Sopenharmony_ci		      PAS_DMA_RXINT_BASEU_SIZ(RX_RING_SIZE >> 3));
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	cfg = PAS_DMA_RXINT_CFG_DHL(2) | PAS_DMA_RXINT_CFG_L2 |
42262306a36Sopenharmony_ci	      PAS_DMA_RXINT_CFG_LW | PAS_DMA_RXINT_CFG_RBP |
42362306a36Sopenharmony_ci	      PAS_DMA_RXINT_CFG_HEN;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (translation_enabled())
42662306a36Sopenharmony_ci		cfg |= PAS_DMA_RXINT_CFG_ITRR | PAS_DMA_RXINT_CFG_ITR;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXINT_CFG(mac->dma_if), cfg);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	ring->next_to_fill = 0;
43162306a36Sopenharmony_ci	ring->next_to_clean = 0;
43262306a36Sopenharmony_ci	ring->mac = mac;
43362306a36Sopenharmony_ci	mac->rx = ring;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	return 0;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ciout_ring_desc:
43862306a36Sopenharmony_ci	kfree(ring->ring_info);
43962306a36Sopenharmony_ciout_ring_info:
44062306a36Sopenharmony_ci	pasemi_dma_free_chan(&ring->chan);
44162306a36Sopenharmony_ciout_chan:
44262306a36Sopenharmony_ci	return -ENOMEM;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic struct pasemi_mac_txring *
44662306a36Sopenharmony_cipasemi_mac_setup_tx_resources(const struct net_device *dev)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct pasemi_mac *mac = netdev_priv(dev);
44962306a36Sopenharmony_ci	u32 val;
45062306a36Sopenharmony_ci	struct pasemi_mac_txring *ring;
45162306a36Sopenharmony_ci	unsigned int cfg;
45262306a36Sopenharmony_ci	int chno;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	ring = pasemi_dma_alloc_chan(TXCHAN, sizeof(struct pasemi_mac_txring),
45562306a36Sopenharmony_ci				     offsetof(struct pasemi_mac_txring, chan));
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (!ring) {
45862306a36Sopenharmony_ci		dev_err(&mac->pdev->dev, "Can't allocate TX channel\n");
45962306a36Sopenharmony_ci		goto out_chan;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	chno = ring->chan.chno;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	spin_lock_init(&ring->lock);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	ring->size = TX_RING_SIZE;
46762306a36Sopenharmony_ci	ring->ring_info = kcalloc(TX_RING_SIZE,
46862306a36Sopenharmony_ci				  sizeof(struct pasemi_mac_buffer),
46962306a36Sopenharmony_ci				  GFP_KERNEL);
47062306a36Sopenharmony_ci	if (!ring->ring_info)
47162306a36Sopenharmony_ci		goto out_ring_info;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/* Allocate descriptors */
47462306a36Sopenharmony_ci	if (pasemi_dma_alloc_ring(&ring->chan, TX_RING_SIZE))
47562306a36Sopenharmony_ci		goto out_ring_desc;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_TXCHAN_BASEL(chno),
47862306a36Sopenharmony_ci		      PAS_DMA_TXCHAN_BASEL_BRBL(ring->chan.ring_dma));
47962306a36Sopenharmony_ci	val = PAS_DMA_TXCHAN_BASEU_BRBH(ring->chan.ring_dma >> 32);
48062306a36Sopenharmony_ci	val |= PAS_DMA_TXCHAN_BASEU_SIZ(TX_RING_SIZE >> 3);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_TXCHAN_BASEU(chno), val);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	cfg = PAS_DMA_TXCHAN_CFG_TY_IFACE |
48562306a36Sopenharmony_ci	      PAS_DMA_TXCHAN_CFG_TATTR(mac->dma_if) |
48662306a36Sopenharmony_ci	      PAS_DMA_TXCHAN_CFG_UP |
48762306a36Sopenharmony_ci	      PAS_DMA_TXCHAN_CFG_WT(4);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (translation_enabled())
49062306a36Sopenharmony_ci		cfg |= PAS_DMA_TXCHAN_CFG_TRD | PAS_DMA_TXCHAN_CFG_TRR;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_TXCHAN_CFG(chno), cfg);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	ring->next_to_fill = 0;
49562306a36Sopenharmony_ci	ring->next_to_clean = 0;
49662306a36Sopenharmony_ci	ring->mac = mac;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	return ring;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ciout_ring_desc:
50162306a36Sopenharmony_ci	kfree(ring->ring_info);
50262306a36Sopenharmony_ciout_ring_info:
50362306a36Sopenharmony_ci	pasemi_dma_free_chan(&ring->chan);
50462306a36Sopenharmony_ciout_chan:
50562306a36Sopenharmony_ci	return NULL;
50662306a36Sopenharmony_ci}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cistatic void pasemi_mac_free_tx_resources(struct pasemi_mac *mac)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	struct pasemi_mac_txring *txring = tx_ring(mac);
51162306a36Sopenharmony_ci	unsigned int i, j;
51262306a36Sopenharmony_ci	struct pasemi_mac_buffer *info;
51362306a36Sopenharmony_ci	dma_addr_t dmas[MAX_SKB_FRAGS+1];
51462306a36Sopenharmony_ci	int freed, nfrags;
51562306a36Sopenharmony_ci	int start, limit;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	start = txring->next_to_clean;
51862306a36Sopenharmony_ci	limit = txring->next_to_fill;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/* Compensate for when fill has wrapped and clean has not */
52162306a36Sopenharmony_ci	if (start > limit)
52262306a36Sopenharmony_ci		limit += TX_RING_SIZE;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	for (i = start; i < limit; i += freed) {
52562306a36Sopenharmony_ci		info = &txring->ring_info[(i+1) & (TX_RING_SIZE-1)];
52662306a36Sopenharmony_ci		if (info->dma && info->skb) {
52762306a36Sopenharmony_ci			nfrags = skb_shinfo(info->skb)->nr_frags;
52862306a36Sopenharmony_ci			for (j = 0; j <= nfrags; j++)
52962306a36Sopenharmony_ci				dmas[j] = txring->ring_info[(i+1+j) &
53062306a36Sopenharmony_ci						(TX_RING_SIZE-1)].dma;
53162306a36Sopenharmony_ci			freed = pasemi_mac_unmap_tx_skb(mac, nfrags,
53262306a36Sopenharmony_ci							info->skb, dmas);
53362306a36Sopenharmony_ci		} else {
53462306a36Sopenharmony_ci			freed = 2;
53562306a36Sopenharmony_ci		}
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	kfree(txring->ring_info);
53962306a36Sopenharmony_ci	pasemi_dma_free_chan(&txring->chan);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic void pasemi_mac_free_rx_buffers(struct pasemi_mac *mac)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct pasemi_mac_rxring *rx = rx_ring(mac);
54662306a36Sopenharmony_ci	unsigned int i;
54762306a36Sopenharmony_ci	struct pasemi_mac_buffer *info;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
55062306a36Sopenharmony_ci		info = &RX_DESC_INFO(rx, i);
55162306a36Sopenharmony_ci		if (info->skb && info->dma) {
55262306a36Sopenharmony_ci			dma_unmap_single(&mac->dma_pdev->dev, info->dma,
55362306a36Sopenharmony_ci					 info->skb->len, DMA_FROM_DEVICE);
55462306a36Sopenharmony_ci			dev_kfree_skb_any(info->skb);
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci		info->dma = 0;
55762306a36Sopenharmony_ci		info->skb = NULL;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++)
56162306a36Sopenharmony_ci		RX_BUFF(rx, i) = 0;
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic void pasemi_mac_free_rx_resources(struct pasemi_mac *mac)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	pasemi_mac_free_rx_buffers(mac);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	dma_free_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(u64),
56962306a36Sopenharmony_ci			  rx_ring(mac)->buffers, rx_ring(mac)->buf_dma);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	kfree(rx_ring(mac)->ring_info);
57262306a36Sopenharmony_ci	pasemi_dma_free_chan(&rx_ring(mac)->chan);
57362306a36Sopenharmony_ci	mac->rx = NULL;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic void pasemi_mac_replenish_rx_ring(struct net_device *dev,
57762306a36Sopenharmony_ci					 const int limit)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	const struct pasemi_mac *mac = netdev_priv(dev);
58062306a36Sopenharmony_ci	struct pasemi_mac_rxring *rx = rx_ring(mac);
58162306a36Sopenharmony_ci	int fill, count;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	if (limit <= 0)
58462306a36Sopenharmony_ci		return;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	fill = rx_ring(mac)->next_to_fill;
58762306a36Sopenharmony_ci	for (count = 0; count < limit; count++) {
58862306a36Sopenharmony_ci		struct pasemi_mac_buffer *info = &RX_DESC_INFO(rx, fill);
58962306a36Sopenharmony_ci		u64 *buff = &RX_BUFF(rx, fill);
59062306a36Sopenharmony_ci		struct sk_buff *skb;
59162306a36Sopenharmony_ci		dma_addr_t dma;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci		/* Entry in use? */
59462306a36Sopenharmony_ci		WARN_ON(*buff);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		skb = netdev_alloc_skb(dev, mac->bufsz);
59762306a36Sopenharmony_ci		skb_reserve(skb, LOCAL_SKB_ALIGN);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci		if (unlikely(!skb))
60062306a36Sopenharmony_ci			break;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		dma = dma_map_single(&mac->dma_pdev->dev, skb->data,
60362306a36Sopenharmony_ci				     mac->bufsz - LOCAL_SKB_ALIGN,
60462306a36Sopenharmony_ci				     DMA_FROM_DEVICE);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci		if (dma_mapping_error(&mac->dma_pdev->dev, dma)) {
60762306a36Sopenharmony_ci			dev_kfree_skb_irq(info->skb);
60862306a36Sopenharmony_ci			break;
60962306a36Sopenharmony_ci		}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		info->skb = skb;
61262306a36Sopenharmony_ci		info->dma = dma;
61362306a36Sopenharmony_ci		*buff = XCT_RXB_LEN(mac->bufsz) | XCT_RXB_ADDR(dma);
61462306a36Sopenharmony_ci		fill++;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	wmb();
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXINT_INCR(mac->dma_if), count);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	rx_ring(mac)->next_to_fill = (rx_ring(mac)->next_to_fill + count) &
62262306a36Sopenharmony_ci				(RX_RING_SIZE - 1);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic void pasemi_mac_restart_rx_intr(const struct pasemi_mac *mac)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	struct pasemi_mac_rxring *rx = rx_ring(mac);
62862306a36Sopenharmony_ci	unsigned int reg, pcnt;
62962306a36Sopenharmony_ci	/* Re-enable packet count interrupts: finally
63062306a36Sopenharmony_ci	 * ack the packet count interrupt we got in rx_intr.
63162306a36Sopenharmony_ci	 */
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	pcnt = *rx->chan.status & PAS_STATUS_PCNT_M;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	reg = PAS_IOB_DMA_RXCH_RESET_PCNT(pcnt) | PAS_IOB_DMA_RXCH_RESET_PINTC;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	if (*rx->chan.status & PAS_STATUS_TIMER)
63862306a36Sopenharmony_ci		reg |= PAS_IOB_DMA_RXCH_RESET_TINTC;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	write_iob_reg(PAS_IOB_DMA_RXCH_RESET(mac->rx->chan.chno), reg);
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic void pasemi_mac_restart_tx_intr(const struct pasemi_mac *mac)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	unsigned int reg, pcnt;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	/* Re-enable packet count interrupts */
64862306a36Sopenharmony_ci	pcnt = *tx_ring(mac)->chan.status & PAS_STATUS_PCNT_M;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	reg = PAS_IOB_DMA_TXCH_RESET_PCNT(pcnt) | PAS_IOB_DMA_TXCH_RESET_PINTC;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	write_iob_reg(PAS_IOB_DMA_TXCH_RESET(tx_ring(mac)->chan.chno), reg);
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic inline void pasemi_mac_rx_error(const struct pasemi_mac *mac,
65762306a36Sopenharmony_ci				       const u64 macrx)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	unsigned int rcmdsta, ccmdsta;
66062306a36Sopenharmony_ci	struct pasemi_dmachan *chan = &rx_ring(mac)->chan;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (!netif_msg_rx_err(mac))
66362306a36Sopenharmony_ci		return;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	rcmdsta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if));
66662306a36Sopenharmony_ci	ccmdsta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(chan->chno));
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	printk(KERN_ERR "pasemi_mac: rx error. macrx %016llx, rx status %llx\n",
66962306a36Sopenharmony_ci		macrx, *chan->status);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	printk(KERN_ERR "pasemi_mac: rcmdsta %08x ccmdsta %08x\n",
67262306a36Sopenharmony_ci		rcmdsta, ccmdsta);
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic inline void pasemi_mac_tx_error(const struct pasemi_mac *mac,
67662306a36Sopenharmony_ci				       const u64 mactx)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	unsigned int cmdsta;
67962306a36Sopenharmony_ci	struct pasemi_dmachan *chan = &tx_ring(mac)->chan;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (!netif_msg_tx_err(mac))
68262306a36Sopenharmony_ci		return;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	cmdsta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(chan->chno));
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	printk(KERN_ERR "pasemi_mac: tx error. mactx 0x%016llx, "\
68762306a36Sopenharmony_ci		"tx status 0x%016llx\n", mactx, *chan->status);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	printk(KERN_ERR "pasemi_mac: tcmdsta 0x%08x\n", cmdsta);
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic int pasemi_mac_clean_rx(struct pasemi_mac_rxring *rx,
69362306a36Sopenharmony_ci			       const int limit)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	const struct pasemi_dmachan *chan = &rx->chan;
69662306a36Sopenharmony_ci	struct pasemi_mac *mac = rx->mac;
69762306a36Sopenharmony_ci	struct pci_dev *pdev = mac->dma_pdev;
69862306a36Sopenharmony_ci	unsigned int n;
69962306a36Sopenharmony_ci	int count, buf_index, tot_bytes, packets;
70062306a36Sopenharmony_ci	struct pasemi_mac_buffer *info;
70162306a36Sopenharmony_ci	struct sk_buff *skb;
70262306a36Sopenharmony_ci	unsigned int len;
70362306a36Sopenharmony_ci	u64 macrx, eval;
70462306a36Sopenharmony_ci	dma_addr_t dma;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	tot_bytes = 0;
70762306a36Sopenharmony_ci	packets = 0;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	spin_lock(&rx->lock);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	n = rx->next_to_clean;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	prefetch(&RX_DESC(rx, n));
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	for (count = 0; count < limit; count++) {
71662306a36Sopenharmony_ci		macrx = RX_DESC(rx, n);
71762306a36Sopenharmony_ci		prefetch(&RX_DESC(rx, n+4));
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci		if ((macrx & XCT_MACRX_E) ||
72062306a36Sopenharmony_ci		    (*chan->status & PAS_STATUS_ERROR))
72162306a36Sopenharmony_ci			pasemi_mac_rx_error(mac, macrx);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		if (!(macrx & XCT_MACRX_O))
72462306a36Sopenharmony_ci			break;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci		info = NULL;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		BUG_ON(!(macrx & XCT_MACRX_RR_8BRES));
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		eval = (RX_DESC(rx, n+1) & XCT_RXRES_8B_EVAL_M) >>
73162306a36Sopenharmony_ci			XCT_RXRES_8B_EVAL_S;
73262306a36Sopenharmony_ci		buf_index = eval-1;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci		dma = (RX_DESC(rx, n+2) & XCT_PTR_ADDR_M);
73562306a36Sopenharmony_ci		info = &RX_DESC_INFO(rx, buf_index);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci		skb = info->skb;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci		prefetch_skb(skb);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci		len = (macrx & XCT_MACRX_LLEN_M) >> XCT_MACRX_LLEN_S;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci		dma_unmap_single(&pdev->dev, dma,
74462306a36Sopenharmony_ci				 mac->bufsz - LOCAL_SKB_ALIGN,
74562306a36Sopenharmony_ci				 DMA_FROM_DEVICE);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		if (macrx & XCT_MACRX_CRC) {
74862306a36Sopenharmony_ci			/* CRC error flagged */
74962306a36Sopenharmony_ci			mac->netdev->stats.rx_errors++;
75062306a36Sopenharmony_ci			mac->netdev->stats.rx_crc_errors++;
75162306a36Sopenharmony_ci			/* No need to free skb, it'll be reused */
75262306a36Sopenharmony_ci			goto next;
75362306a36Sopenharmony_ci		}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci		info->skb = NULL;
75662306a36Sopenharmony_ci		info->dma = 0;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci		if (likely((macrx & XCT_MACRX_HTY_M) == XCT_MACRX_HTY_IPV4_OK)) {
75962306a36Sopenharmony_ci			skb->ip_summed = CHECKSUM_UNNECESSARY;
76062306a36Sopenharmony_ci			skb->csum = (macrx & XCT_MACRX_CSUM_M) >>
76162306a36Sopenharmony_ci					   XCT_MACRX_CSUM_S;
76262306a36Sopenharmony_ci		} else {
76362306a36Sopenharmony_ci			skb_checksum_none_assert(skb);
76462306a36Sopenharmony_ci		}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci		packets++;
76762306a36Sopenharmony_ci		tot_bytes += len;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		/* Don't include CRC */
77062306a36Sopenharmony_ci		skb_put(skb, len-4);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, mac->netdev);
77362306a36Sopenharmony_ci		napi_gro_receive(&mac->napi, skb);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cinext:
77662306a36Sopenharmony_ci		RX_DESC(rx, n) = 0;
77762306a36Sopenharmony_ci		RX_DESC(rx, n+1) = 0;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		/* Need to zero it out since hardware doesn't, since the
78062306a36Sopenharmony_ci		 * replenish loop uses it to tell when it's done.
78162306a36Sopenharmony_ci		 */
78262306a36Sopenharmony_ci		RX_BUFF(rx, buf_index) = 0;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		n += 4;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (n > RX_RING_SIZE) {
78862306a36Sopenharmony_ci		/* Errata 5971 workaround: L2 target of headers */
78962306a36Sopenharmony_ci		write_iob_reg(PAS_IOB_COM_PKTHDRCNT, 0);
79062306a36Sopenharmony_ci		n &= (RX_RING_SIZE-1);
79162306a36Sopenharmony_ci	}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	rx_ring(mac)->next_to_clean = n;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	/* Increase is in number of 16-byte entries, and since each descriptor
79662306a36Sopenharmony_ci	 * with an 8BRES takes up 3x8 bytes (padded to 4x8), increase with
79762306a36Sopenharmony_ci	 * count*2.
79862306a36Sopenharmony_ci	 */
79962306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXCHAN_INCR(mac->rx->chan.chno), count << 1);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	pasemi_mac_replenish_rx_ring(mac->netdev, count);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	mac->netdev->stats.rx_bytes += tot_bytes;
80462306a36Sopenharmony_ci	mac->netdev->stats.rx_packets += packets;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	spin_unlock(&rx_ring(mac)->lock);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	return count;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci/* Can't make this too large or we blow the kernel stack limits */
81262306a36Sopenharmony_ci#define TX_CLEAN_BATCHSIZE (128/MAX_SKB_FRAGS)
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic int pasemi_mac_clean_tx(struct pasemi_mac_txring *txring)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	struct pasemi_dmachan *chan = &txring->chan;
81762306a36Sopenharmony_ci	struct pasemi_mac *mac = txring->mac;
81862306a36Sopenharmony_ci	int i, j;
81962306a36Sopenharmony_ci	unsigned int start, descr_count, buf_count, batch_limit;
82062306a36Sopenharmony_ci	unsigned int ring_limit;
82162306a36Sopenharmony_ci	unsigned int total_count;
82262306a36Sopenharmony_ci	unsigned long flags;
82362306a36Sopenharmony_ci	struct sk_buff *skbs[TX_CLEAN_BATCHSIZE];
82462306a36Sopenharmony_ci	dma_addr_t dmas[TX_CLEAN_BATCHSIZE][MAX_SKB_FRAGS+1];
82562306a36Sopenharmony_ci	int nf[TX_CLEAN_BATCHSIZE];
82662306a36Sopenharmony_ci	int nr_frags;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	total_count = 0;
82962306a36Sopenharmony_ci	batch_limit = TX_CLEAN_BATCHSIZE;
83062306a36Sopenharmony_cirestart:
83162306a36Sopenharmony_ci	spin_lock_irqsave(&txring->lock, flags);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	start = txring->next_to_clean;
83462306a36Sopenharmony_ci	ring_limit = txring->next_to_fill;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	prefetch(&TX_DESC_INFO(txring, start+1).skb);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/* Compensate for when fill has wrapped but clean has not */
83962306a36Sopenharmony_ci	if (start > ring_limit)
84062306a36Sopenharmony_ci		ring_limit += TX_RING_SIZE;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	buf_count = 0;
84362306a36Sopenharmony_ci	descr_count = 0;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	for (i = start;
84662306a36Sopenharmony_ci	     descr_count < batch_limit && i < ring_limit;
84762306a36Sopenharmony_ci	     i += buf_count) {
84862306a36Sopenharmony_ci		u64 mactx = TX_DESC(txring, i);
84962306a36Sopenharmony_ci		struct sk_buff *skb;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci		if ((mactx  & XCT_MACTX_E) ||
85262306a36Sopenharmony_ci		    (*chan->status & PAS_STATUS_ERROR))
85362306a36Sopenharmony_ci			pasemi_mac_tx_error(mac, mactx);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci		/* Skip over control descriptors */
85662306a36Sopenharmony_ci		if (!(mactx & XCT_MACTX_LLEN_M)) {
85762306a36Sopenharmony_ci			TX_DESC(txring, i) = 0;
85862306a36Sopenharmony_ci			TX_DESC(txring, i+1) = 0;
85962306a36Sopenharmony_ci			buf_count = 2;
86062306a36Sopenharmony_ci			continue;
86162306a36Sopenharmony_ci		}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci		skb = TX_DESC_INFO(txring, i+1).skb;
86462306a36Sopenharmony_ci		nr_frags = TX_DESC_INFO(txring, i).dma;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci		if (unlikely(mactx & XCT_MACTX_O))
86762306a36Sopenharmony_ci			/* Not yet transmitted */
86862306a36Sopenharmony_ci			break;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci		buf_count = 2 + nr_frags;
87162306a36Sopenharmony_ci		/* Since we always fill with an even number of entries, make
87262306a36Sopenharmony_ci		 * sure we skip any unused one at the end as well.
87362306a36Sopenharmony_ci		 */
87462306a36Sopenharmony_ci		if (buf_count & 1)
87562306a36Sopenharmony_ci			buf_count++;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci		for (j = 0; j <= nr_frags; j++)
87862306a36Sopenharmony_ci			dmas[descr_count][j] = TX_DESC_INFO(txring, i+1+j).dma;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci		skbs[descr_count] = skb;
88162306a36Sopenharmony_ci		nf[descr_count] = nr_frags;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci		TX_DESC(txring, i) = 0;
88462306a36Sopenharmony_ci		TX_DESC(txring, i+1) = 0;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci		descr_count++;
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci	txring->next_to_clean = i & (TX_RING_SIZE-1);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	spin_unlock_irqrestore(&txring->lock, flags);
89162306a36Sopenharmony_ci	netif_wake_queue(mac->netdev);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	for (i = 0; i < descr_count; i++)
89462306a36Sopenharmony_ci		pasemi_mac_unmap_tx_skb(mac, nf[i], skbs[i], dmas[i]);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	total_count += descr_count;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	/* If the batch was full, try to clean more */
89962306a36Sopenharmony_ci	if (descr_count == batch_limit)
90062306a36Sopenharmony_ci		goto restart;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	return total_count;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_cistatic irqreturn_t pasemi_mac_rx_intr(int irq, void *data)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	const struct pasemi_mac_rxring *rxring = data;
90962306a36Sopenharmony_ci	struct pasemi_mac *mac = rxring->mac;
91062306a36Sopenharmony_ci	const struct pasemi_dmachan *chan = &rxring->chan;
91162306a36Sopenharmony_ci	unsigned int reg;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	if (!(*chan->status & PAS_STATUS_CAUSE_M))
91462306a36Sopenharmony_ci		return IRQ_NONE;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/* Don't reset packet count so it won't fire again but clear
91762306a36Sopenharmony_ci	 * all others.
91862306a36Sopenharmony_ci	 */
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	reg = 0;
92162306a36Sopenharmony_ci	if (*chan->status & PAS_STATUS_SOFT)
92262306a36Sopenharmony_ci		reg |= PAS_IOB_DMA_RXCH_RESET_SINTC;
92362306a36Sopenharmony_ci	if (*chan->status & PAS_STATUS_ERROR)
92462306a36Sopenharmony_ci		reg |= PAS_IOB_DMA_RXCH_RESET_DINTC;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	napi_schedule(&mac->napi);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	write_iob_reg(PAS_IOB_DMA_RXCH_RESET(chan->chno), reg);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	return IRQ_HANDLED;
93162306a36Sopenharmony_ci}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci#define TX_CLEAN_INTERVAL HZ
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic void pasemi_mac_tx_timer(struct timer_list *t)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	struct pasemi_mac_txring *txring = from_timer(txring, t, clean_timer);
93862306a36Sopenharmony_ci	struct pasemi_mac *mac = txring->mac;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	pasemi_mac_clean_tx(txring);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	mod_timer(&txring->clean_timer, jiffies + TX_CLEAN_INTERVAL);
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	pasemi_mac_restart_tx_intr(mac);
94562306a36Sopenharmony_ci}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_cistatic irqreturn_t pasemi_mac_tx_intr(int irq, void *data)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	struct pasemi_mac_txring *txring = data;
95062306a36Sopenharmony_ci	const struct pasemi_dmachan *chan = &txring->chan;
95162306a36Sopenharmony_ci	struct pasemi_mac *mac = txring->mac;
95262306a36Sopenharmony_ci	unsigned int reg;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	if (!(*chan->status & PAS_STATUS_CAUSE_M))
95562306a36Sopenharmony_ci		return IRQ_NONE;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	reg = 0;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	if (*chan->status & PAS_STATUS_SOFT)
96062306a36Sopenharmony_ci		reg |= PAS_IOB_DMA_TXCH_RESET_SINTC;
96162306a36Sopenharmony_ci	if (*chan->status & PAS_STATUS_ERROR)
96262306a36Sopenharmony_ci		reg |= PAS_IOB_DMA_TXCH_RESET_DINTC;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	mod_timer(&txring->clean_timer, jiffies + (TX_CLEAN_INTERVAL)*2);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	napi_schedule(&mac->napi);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (reg)
96962306a36Sopenharmony_ci		write_iob_reg(PAS_IOB_DMA_TXCH_RESET(chan->chno), reg);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	return IRQ_HANDLED;
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic void pasemi_adjust_link(struct net_device *dev)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	struct pasemi_mac *mac = netdev_priv(dev);
97762306a36Sopenharmony_ci	int msg;
97862306a36Sopenharmony_ci	unsigned int flags;
97962306a36Sopenharmony_ci	unsigned int new_flags;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	if (!dev->phydev->link) {
98262306a36Sopenharmony_ci		/* If no link, MAC speed settings don't matter. Just report
98362306a36Sopenharmony_ci		 * link down and return.
98462306a36Sopenharmony_ci		 */
98562306a36Sopenharmony_ci		if (mac->link && netif_msg_link(mac))
98662306a36Sopenharmony_ci			printk(KERN_INFO "%s: Link is down.\n", dev->name);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci		netif_carrier_off(dev);
98962306a36Sopenharmony_ci		pasemi_mac_intf_disable(mac);
99062306a36Sopenharmony_ci		mac->link = 0;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci		return;
99362306a36Sopenharmony_ci	} else {
99462306a36Sopenharmony_ci		pasemi_mac_intf_enable(mac);
99562306a36Sopenharmony_ci		netif_carrier_on(dev);
99662306a36Sopenharmony_ci	}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG);
99962306a36Sopenharmony_ci	new_flags = flags & ~(PAS_MAC_CFG_PCFG_HD | PAS_MAC_CFG_PCFG_SPD_M |
100062306a36Sopenharmony_ci			      PAS_MAC_CFG_PCFG_TSR_M);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	if (!dev->phydev->duplex)
100362306a36Sopenharmony_ci		new_flags |= PAS_MAC_CFG_PCFG_HD;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	switch (dev->phydev->speed) {
100662306a36Sopenharmony_ci	case 1000:
100762306a36Sopenharmony_ci		new_flags |= PAS_MAC_CFG_PCFG_SPD_1G |
100862306a36Sopenharmony_ci			     PAS_MAC_CFG_PCFG_TSR_1G;
100962306a36Sopenharmony_ci		break;
101062306a36Sopenharmony_ci	case 100:
101162306a36Sopenharmony_ci		new_flags |= PAS_MAC_CFG_PCFG_SPD_100M |
101262306a36Sopenharmony_ci			     PAS_MAC_CFG_PCFG_TSR_100M;
101362306a36Sopenharmony_ci		break;
101462306a36Sopenharmony_ci	case 10:
101562306a36Sopenharmony_ci		new_flags |= PAS_MAC_CFG_PCFG_SPD_10M |
101662306a36Sopenharmony_ci			     PAS_MAC_CFG_PCFG_TSR_10M;
101762306a36Sopenharmony_ci		break;
101862306a36Sopenharmony_ci	default:
101962306a36Sopenharmony_ci		printk("Unsupported speed %d\n", dev->phydev->speed);
102062306a36Sopenharmony_ci	}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	/* Print on link or speed/duplex change */
102362306a36Sopenharmony_ci	msg = mac->link != dev->phydev->link || flags != new_flags;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	mac->duplex = dev->phydev->duplex;
102662306a36Sopenharmony_ci	mac->speed = dev->phydev->speed;
102762306a36Sopenharmony_ci	mac->link = dev->phydev->link;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	if (new_flags != flags)
103062306a36Sopenharmony_ci		write_mac_reg(mac, PAS_MAC_CFG_PCFG, new_flags);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	if (msg && netif_msg_link(mac))
103362306a36Sopenharmony_ci		printk(KERN_INFO "%s: Link is up at %d Mbps, %s duplex.\n",
103462306a36Sopenharmony_ci		       dev->name, mac->speed, mac->duplex ? "full" : "half");
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_cistatic int pasemi_mac_phy_init(struct net_device *dev)
103862306a36Sopenharmony_ci{
103962306a36Sopenharmony_ci	struct pasemi_mac *mac = netdev_priv(dev);
104062306a36Sopenharmony_ci	struct device_node *dn, *phy_dn;
104162306a36Sopenharmony_ci	struct phy_device *phydev;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	dn = pci_device_to_OF_node(mac->pdev);
104462306a36Sopenharmony_ci	phy_dn = of_parse_phandle(dn, "phy-handle", 0);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	mac->link = 0;
104762306a36Sopenharmony_ci	mac->speed = 0;
104862306a36Sopenharmony_ci	mac->duplex = -1;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	phydev = of_phy_connect(dev, phy_dn, &pasemi_adjust_link, 0,
105162306a36Sopenharmony_ci				PHY_INTERFACE_MODE_SGMII);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	of_node_put(phy_dn);
105462306a36Sopenharmony_ci	if (!phydev) {
105562306a36Sopenharmony_ci		printk(KERN_ERR "%s: Could not attach to phy\n", dev->name);
105662306a36Sopenharmony_ci		return -ENODEV;
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	return 0;
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_cistatic int pasemi_mac_open(struct net_device *dev)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	struct pasemi_mac *mac = netdev_priv(dev);
106662306a36Sopenharmony_ci	unsigned int flags;
106762306a36Sopenharmony_ci	int i, ret;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	flags = PAS_MAC_CFG_TXP_FCE | PAS_MAC_CFG_TXP_FPC(3) |
107062306a36Sopenharmony_ci		PAS_MAC_CFG_TXP_SL(3) | PAS_MAC_CFG_TXP_COB(0xf) |
107162306a36Sopenharmony_ci		PAS_MAC_CFG_TXP_TIFT(8) | PAS_MAC_CFG_TXP_TIFG(12);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	write_mac_reg(mac, PAS_MAC_CFG_TXP, flags);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	ret = pasemi_mac_setup_rx_resources(dev);
107662306a36Sopenharmony_ci	if (ret)
107762306a36Sopenharmony_ci		goto out_rx_resources;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	mac->tx = pasemi_mac_setup_tx_resources(dev);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	if (!mac->tx) {
108262306a36Sopenharmony_ci		ret = -ENOMEM;
108362306a36Sopenharmony_ci		goto out_tx_ring;
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	/* We might already have allocated rings in case mtu was changed
108762306a36Sopenharmony_ci	 * before interface was brought up.
108862306a36Sopenharmony_ci	 */
108962306a36Sopenharmony_ci	if (dev->mtu > 1500 && !mac->num_cs) {
109062306a36Sopenharmony_ci		pasemi_mac_setup_csrings(mac);
109162306a36Sopenharmony_ci		if (!mac->num_cs) {
109262306a36Sopenharmony_ci			ret = -ENOMEM;
109362306a36Sopenharmony_ci			goto out_tx_ring;
109462306a36Sopenharmony_ci		}
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	/* Zero out rmon counters */
109862306a36Sopenharmony_ci	for (i = 0; i < 32; i++)
109962306a36Sopenharmony_ci		write_mac_reg(mac, PAS_MAC_RMON(i), 0);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	/* 0x3ff with 33MHz clock is about 31us */
110262306a36Sopenharmony_ci	write_iob_reg(PAS_IOB_DMA_COM_TIMEOUTCFG,
110362306a36Sopenharmony_ci		      PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT(0x3ff));
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	write_iob_reg(PAS_IOB_DMA_RXCH_CFG(mac->rx->chan.chno),
110662306a36Sopenharmony_ci		      PAS_IOB_DMA_RXCH_CFG_CNTTH(256));
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	write_iob_reg(PAS_IOB_DMA_TXCH_CFG(mac->tx->chan.chno),
110962306a36Sopenharmony_ci		      PAS_IOB_DMA_TXCH_CFG_CNTTH(32));
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	write_mac_reg(mac, PAS_MAC_IPC_CHNL,
111262306a36Sopenharmony_ci		      PAS_MAC_IPC_CHNL_DCHNO(mac->rx->chan.chno) |
111362306a36Sopenharmony_ci		      PAS_MAC_IPC_CHNL_BCH(mac->rx->chan.chno));
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	/* enable rx if */
111662306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if),
111762306a36Sopenharmony_ci		      PAS_DMA_RXINT_RCMDSTA_EN |
111862306a36Sopenharmony_ci		      PAS_DMA_RXINT_RCMDSTA_DROPS_M |
111962306a36Sopenharmony_ci		      PAS_DMA_RXINT_RCMDSTA_BP |
112062306a36Sopenharmony_ci		      PAS_DMA_RXINT_RCMDSTA_OO |
112162306a36Sopenharmony_ci		      PAS_DMA_RXINT_RCMDSTA_BT);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	/* enable rx channel */
112462306a36Sopenharmony_ci	pasemi_dma_start_chan(&rx_ring(mac)->chan, PAS_DMA_RXCHAN_CCMDSTA_DU |
112562306a36Sopenharmony_ci						   PAS_DMA_RXCHAN_CCMDSTA_OD |
112662306a36Sopenharmony_ci						   PAS_DMA_RXCHAN_CCMDSTA_FD |
112762306a36Sopenharmony_ci						   PAS_DMA_RXCHAN_CCMDSTA_DT);
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	/* enable tx channel */
113062306a36Sopenharmony_ci	pasemi_dma_start_chan(&tx_ring(mac)->chan, PAS_DMA_TXCHAN_TCMDSTA_SZ |
113162306a36Sopenharmony_ci						   PAS_DMA_TXCHAN_TCMDSTA_DB |
113262306a36Sopenharmony_ci						   PAS_DMA_TXCHAN_TCMDSTA_DE |
113362306a36Sopenharmony_ci						   PAS_DMA_TXCHAN_TCMDSTA_DA);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	pasemi_mac_replenish_rx_ring(dev, RX_RING_SIZE);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXCHAN_INCR(rx_ring(mac)->chan.chno),
113862306a36Sopenharmony_ci		      RX_RING_SIZE>>1);
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	/* Clear out any residual packet count state from firmware */
114162306a36Sopenharmony_ci	pasemi_mac_restart_rx_intr(mac);
114262306a36Sopenharmony_ci	pasemi_mac_restart_tx_intr(mac);
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	flags = PAS_MAC_CFG_PCFG_S1 | PAS_MAC_CFG_PCFG_PR | PAS_MAC_CFG_PCFG_CE;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	if (mac->type == MAC_TYPE_GMAC)
114762306a36Sopenharmony_ci		flags |= PAS_MAC_CFG_PCFG_TSR_1G | PAS_MAC_CFG_PCFG_SPD_1G;
114862306a36Sopenharmony_ci	else
114962306a36Sopenharmony_ci		flags |= PAS_MAC_CFG_PCFG_TSR_10G | PAS_MAC_CFG_PCFG_SPD_10G;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	/* Enable interface in MAC */
115262306a36Sopenharmony_ci	write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	ret = pasemi_mac_phy_init(dev);
115562306a36Sopenharmony_ci	if (ret) {
115662306a36Sopenharmony_ci		/* Since we won't get link notification, just enable RX */
115762306a36Sopenharmony_ci		pasemi_mac_intf_enable(mac);
115862306a36Sopenharmony_ci		if (mac->type == MAC_TYPE_GMAC) {
115962306a36Sopenharmony_ci			/* Warn for missing PHY on SGMII (1Gig) ports */
116062306a36Sopenharmony_ci			dev_warn(&mac->pdev->dev,
116162306a36Sopenharmony_ci				 "PHY init failed: %d.\n", ret);
116262306a36Sopenharmony_ci			dev_warn(&mac->pdev->dev,
116362306a36Sopenharmony_ci				 "Defaulting to 1Gbit full duplex\n");
116462306a36Sopenharmony_ci		}
116562306a36Sopenharmony_ci	}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	netif_start_queue(dev);
116862306a36Sopenharmony_ci	napi_enable(&mac->napi);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	snprintf(mac->tx_irq_name, sizeof(mac->tx_irq_name), "%s tx",
117162306a36Sopenharmony_ci		 dev->name);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	ret = request_irq(mac->tx->chan.irq, pasemi_mac_tx_intr, 0,
117462306a36Sopenharmony_ci			  mac->tx_irq_name, mac->tx);
117562306a36Sopenharmony_ci	if (ret) {
117662306a36Sopenharmony_ci		dev_err(&mac->pdev->dev, "request_irq of irq %d failed: %d\n",
117762306a36Sopenharmony_ci			mac->tx->chan.irq, ret);
117862306a36Sopenharmony_ci		goto out_tx_int;
117962306a36Sopenharmony_ci	}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	snprintf(mac->rx_irq_name, sizeof(mac->rx_irq_name), "%s rx",
118262306a36Sopenharmony_ci		 dev->name);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	ret = request_irq(mac->rx->chan.irq, pasemi_mac_rx_intr, 0,
118562306a36Sopenharmony_ci			  mac->rx_irq_name, mac->rx);
118662306a36Sopenharmony_ci	if (ret) {
118762306a36Sopenharmony_ci		dev_err(&mac->pdev->dev, "request_irq of irq %d failed: %d\n",
118862306a36Sopenharmony_ci			mac->rx->chan.irq, ret);
118962306a36Sopenharmony_ci		goto out_rx_int;
119062306a36Sopenharmony_ci	}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	if (dev->phydev)
119362306a36Sopenharmony_ci		phy_start(dev->phydev);
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	timer_setup(&mac->tx->clean_timer, pasemi_mac_tx_timer, 0);
119662306a36Sopenharmony_ci	mod_timer(&mac->tx->clean_timer, jiffies + HZ);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	return 0;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ciout_rx_int:
120162306a36Sopenharmony_ci	free_irq(mac->tx->chan.irq, mac->tx);
120262306a36Sopenharmony_ciout_tx_int:
120362306a36Sopenharmony_ci	napi_disable(&mac->napi);
120462306a36Sopenharmony_ci	netif_stop_queue(dev);
120562306a36Sopenharmony_ciout_tx_ring:
120662306a36Sopenharmony_ci	if (mac->tx)
120762306a36Sopenharmony_ci		pasemi_mac_free_tx_resources(mac);
120862306a36Sopenharmony_ci	pasemi_mac_free_rx_resources(mac);
120962306a36Sopenharmony_ciout_rx_resources:
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	return ret;
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci#define MAX_RETRIES 5000
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_cistatic void pasemi_mac_pause_txchan(struct pasemi_mac *mac)
121762306a36Sopenharmony_ci{
121862306a36Sopenharmony_ci	unsigned int sta, retries;
121962306a36Sopenharmony_ci	int txch = tx_ring(mac)->chan.chno;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch),
122262306a36Sopenharmony_ci		      PAS_DMA_TXCHAN_TCMDSTA_ST);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	for (retries = 0; retries < MAX_RETRIES; retries++) {
122562306a36Sopenharmony_ci		sta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch));
122662306a36Sopenharmony_ci		if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT))
122762306a36Sopenharmony_ci			break;
122862306a36Sopenharmony_ci		cond_resched();
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	if (sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)
123262306a36Sopenharmony_ci		dev_err(&mac->dma_pdev->dev,
123362306a36Sopenharmony_ci			"Failed to stop tx channel, tcmdsta %08x\n", sta);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), 0);
123662306a36Sopenharmony_ci}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_cistatic void pasemi_mac_pause_rxchan(struct pasemi_mac *mac)
123962306a36Sopenharmony_ci{
124062306a36Sopenharmony_ci	unsigned int sta, retries;
124162306a36Sopenharmony_ci	int rxch = rx_ring(mac)->chan.chno;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch),
124462306a36Sopenharmony_ci		      PAS_DMA_RXCHAN_CCMDSTA_ST);
124562306a36Sopenharmony_ci	for (retries = 0; retries < MAX_RETRIES; retries++) {
124662306a36Sopenharmony_ci		sta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch));
124762306a36Sopenharmony_ci		if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT))
124862306a36Sopenharmony_ci			break;
124962306a36Sopenharmony_ci		cond_resched();
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	if (sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)
125362306a36Sopenharmony_ci		dev_err(&mac->dma_pdev->dev,
125462306a36Sopenharmony_ci			"Failed to stop rx channel, ccmdsta 08%x\n", sta);
125562306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), 0);
125662306a36Sopenharmony_ci}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_cistatic void pasemi_mac_pause_rxint(struct pasemi_mac *mac)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	unsigned int sta, retries;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if),
126362306a36Sopenharmony_ci		      PAS_DMA_RXINT_RCMDSTA_ST);
126462306a36Sopenharmony_ci	for (retries = 0; retries < MAX_RETRIES; retries++) {
126562306a36Sopenharmony_ci		sta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if));
126662306a36Sopenharmony_ci		if (!(sta & PAS_DMA_RXINT_RCMDSTA_ACT))
126762306a36Sopenharmony_ci			break;
126862306a36Sopenharmony_ci		cond_resched();
126962306a36Sopenharmony_ci	}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	if (sta & PAS_DMA_RXINT_RCMDSTA_ACT)
127262306a36Sopenharmony_ci		dev_err(&mac->dma_pdev->dev,
127362306a36Sopenharmony_ci			"Failed to stop rx interface, rcmdsta %08x\n", sta);
127462306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 0);
127562306a36Sopenharmony_ci}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_cistatic int pasemi_mac_close(struct net_device *dev)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	struct pasemi_mac *mac = netdev_priv(dev);
128062306a36Sopenharmony_ci	unsigned int sta;
128162306a36Sopenharmony_ci	int rxch, txch, i;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	rxch = rx_ring(mac)->chan.chno;
128462306a36Sopenharmony_ci	txch = tx_ring(mac)->chan.chno;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	if (dev->phydev) {
128762306a36Sopenharmony_ci		phy_stop(dev->phydev);
128862306a36Sopenharmony_ci		phy_disconnect(dev->phydev);
128962306a36Sopenharmony_ci	}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	del_timer_sync(&mac->tx->clean_timer);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	netif_stop_queue(dev);
129462306a36Sopenharmony_ci	napi_disable(&mac->napi);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	sta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if));
129762306a36Sopenharmony_ci	if (sta & (PAS_DMA_RXINT_RCMDSTA_BP |
129862306a36Sopenharmony_ci		      PAS_DMA_RXINT_RCMDSTA_OO |
129962306a36Sopenharmony_ci		      PAS_DMA_RXINT_RCMDSTA_BT))
130062306a36Sopenharmony_ci		printk(KERN_DEBUG "pasemi_mac: rcmdsta error: 0x%08x\n", sta);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	sta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch));
130362306a36Sopenharmony_ci	if (sta & (PAS_DMA_RXCHAN_CCMDSTA_DU |
130462306a36Sopenharmony_ci		     PAS_DMA_RXCHAN_CCMDSTA_OD |
130562306a36Sopenharmony_ci		     PAS_DMA_RXCHAN_CCMDSTA_FD |
130662306a36Sopenharmony_ci		     PAS_DMA_RXCHAN_CCMDSTA_DT))
130762306a36Sopenharmony_ci		printk(KERN_DEBUG "pasemi_mac: ccmdsta error: 0x%08x\n", sta);
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	sta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch));
131062306a36Sopenharmony_ci	if (sta & (PAS_DMA_TXCHAN_TCMDSTA_SZ | PAS_DMA_TXCHAN_TCMDSTA_DB |
131162306a36Sopenharmony_ci		      PAS_DMA_TXCHAN_TCMDSTA_DE | PAS_DMA_TXCHAN_TCMDSTA_DA))
131262306a36Sopenharmony_ci		printk(KERN_DEBUG "pasemi_mac: tcmdsta error: 0x%08x\n", sta);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	/* Clean out any pending buffers */
131562306a36Sopenharmony_ci	pasemi_mac_clean_tx(tx_ring(mac));
131662306a36Sopenharmony_ci	pasemi_mac_clean_rx(rx_ring(mac), RX_RING_SIZE);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	pasemi_mac_pause_txchan(mac);
131962306a36Sopenharmony_ci	pasemi_mac_pause_rxint(mac);
132062306a36Sopenharmony_ci	pasemi_mac_pause_rxchan(mac);
132162306a36Sopenharmony_ci	pasemi_mac_intf_disable(mac);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	free_irq(mac->tx->chan.irq, mac->tx);
132462306a36Sopenharmony_ci	free_irq(mac->rx->chan.irq, mac->rx);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	for (i = 0; i < mac->num_cs; i++) {
132762306a36Sopenharmony_ci		pasemi_mac_free_csring(mac->cs[i]);
132862306a36Sopenharmony_ci		mac->cs[i] = NULL;
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	mac->num_cs = 0;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	/* Free resources */
133462306a36Sopenharmony_ci	pasemi_mac_free_rx_resources(mac);
133562306a36Sopenharmony_ci	pasemi_mac_free_tx_resources(mac);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	return 0;
133862306a36Sopenharmony_ci}
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_cistatic void pasemi_mac_queue_csdesc(const struct sk_buff *skb,
134162306a36Sopenharmony_ci				    const dma_addr_t *map,
134262306a36Sopenharmony_ci				    const unsigned int *map_size,
134362306a36Sopenharmony_ci				    struct pasemi_mac_txring *txring,
134462306a36Sopenharmony_ci				    struct pasemi_mac_csring *csring)
134562306a36Sopenharmony_ci{
134662306a36Sopenharmony_ci	u64 fund;
134762306a36Sopenharmony_ci	dma_addr_t cs_dest;
134862306a36Sopenharmony_ci	const int nh_off = skb_network_offset(skb);
134962306a36Sopenharmony_ci	const int nh_len = skb_network_header_len(skb);
135062306a36Sopenharmony_ci	const int nfrags = skb_shinfo(skb)->nr_frags;
135162306a36Sopenharmony_ci	int cs_size, i, fill, hdr, evt;
135262306a36Sopenharmony_ci	dma_addr_t csdma;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	fund = XCT_FUN_ST | XCT_FUN_RR_8BRES |
135562306a36Sopenharmony_ci	       XCT_FUN_O | XCT_FUN_FUN(csring->fun) |
135662306a36Sopenharmony_ci	       XCT_FUN_CRM_SIG | XCT_FUN_LLEN(skb->len - nh_off) |
135762306a36Sopenharmony_ci	       XCT_FUN_SHL(nh_len >> 2) | XCT_FUN_SE;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	switch (ip_hdr(skb)->protocol) {
136062306a36Sopenharmony_ci	case IPPROTO_TCP:
136162306a36Sopenharmony_ci		fund |= XCT_FUN_SIG_TCP4;
136262306a36Sopenharmony_ci		/* TCP checksum is 16 bytes into the header */
136362306a36Sopenharmony_ci		cs_dest = map[0] + skb_transport_offset(skb) + 16;
136462306a36Sopenharmony_ci		break;
136562306a36Sopenharmony_ci	case IPPROTO_UDP:
136662306a36Sopenharmony_ci		fund |= XCT_FUN_SIG_UDP4;
136762306a36Sopenharmony_ci		/* UDP checksum is 6 bytes into the header */
136862306a36Sopenharmony_ci		cs_dest = map[0] + skb_transport_offset(skb) + 6;
136962306a36Sopenharmony_ci		break;
137062306a36Sopenharmony_ci	default:
137162306a36Sopenharmony_ci		BUG();
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	/* Do the checksum offloaded */
137562306a36Sopenharmony_ci	fill = csring->next_to_fill;
137662306a36Sopenharmony_ci	hdr = fill;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	CS_DESC(csring, fill++) = fund;
137962306a36Sopenharmony_ci	/* Room for 8BRES. Checksum result is really 2 bytes into it */
138062306a36Sopenharmony_ci	csdma = csring->chan.ring_dma + (fill & (CS_RING_SIZE-1)) * 8 + 2;
138162306a36Sopenharmony_ci	CS_DESC(csring, fill++) = 0;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	CS_DESC(csring, fill) = XCT_PTR_LEN(map_size[0]-nh_off) | XCT_PTR_ADDR(map[0]+nh_off);
138462306a36Sopenharmony_ci	for (i = 1; i <= nfrags; i++)
138562306a36Sopenharmony_ci		CS_DESC(csring, fill+i) = XCT_PTR_LEN(map_size[i]) | XCT_PTR_ADDR(map[i]);
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	fill += i;
138862306a36Sopenharmony_ci	if (fill & 1)
138962306a36Sopenharmony_ci		fill++;
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	/* Copy the result into the TCP packet */
139262306a36Sopenharmony_ci	CS_DESC(csring, fill++) = XCT_FUN_O | XCT_FUN_FUN(csring->fun) |
139362306a36Sopenharmony_ci				  XCT_FUN_LLEN(2) | XCT_FUN_SE;
139462306a36Sopenharmony_ci	CS_DESC(csring, fill++) = XCT_PTR_LEN(2) | XCT_PTR_ADDR(cs_dest) | XCT_PTR_T;
139562306a36Sopenharmony_ci	CS_DESC(csring, fill++) = XCT_PTR_LEN(2) | XCT_PTR_ADDR(csdma);
139662306a36Sopenharmony_ci	fill++;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	evt = !csring->last_event;
139962306a36Sopenharmony_ci	csring->last_event = evt;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	/* Event handshaking with MAC TX */
140262306a36Sopenharmony_ci	CS_DESC(csring, fill++) = CTRL_CMD_T | CTRL_CMD_META_EVT | CTRL_CMD_O |
140362306a36Sopenharmony_ci				  CTRL_CMD_ETYPE_SET | CTRL_CMD_REG(csring->events[evt]);
140462306a36Sopenharmony_ci	CS_DESC(csring, fill++) = 0;
140562306a36Sopenharmony_ci	CS_DESC(csring, fill++) = CTRL_CMD_T | CTRL_CMD_META_EVT | CTRL_CMD_O |
140662306a36Sopenharmony_ci				  CTRL_CMD_ETYPE_WCLR | CTRL_CMD_REG(csring->events[!evt]);
140762306a36Sopenharmony_ci	CS_DESC(csring, fill++) = 0;
140862306a36Sopenharmony_ci	csring->next_to_fill = fill & (CS_RING_SIZE-1);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	cs_size = fill - hdr;
141162306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_TXCHAN_INCR(csring->chan.chno), (cs_size) >> 1);
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	/* TX-side event handshaking */
141462306a36Sopenharmony_ci	fill = txring->next_to_fill;
141562306a36Sopenharmony_ci	TX_DESC(txring, fill++) = CTRL_CMD_T | CTRL_CMD_META_EVT | CTRL_CMD_O |
141662306a36Sopenharmony_ci				  CTRL_CMD_ETYPE_WSET | CTRL_CMD_REG(csring->events[evt]);
141762306a36Sopenharmony_ci	TX_DESC(txring, fill++) = 0;
141862306a36Sopenharmony_ci	TX_DESC(txring, fill++) = CTRL_CMD_T | CTRL_CMD_META_EVT | CTRL_CMD_O |
141962306a36Sopenharmony_ci				  CTRL_CMD_ETYPE_CLR | CTRL_CMD_REG(csring->events[!evt]);
142062306a36Sopenharmony_ci	TX_DESC(txring, fill++) = 0;
142162306a36Sopenharmony_ci	txring->next_to_fill = fill;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_TXCHAN_INCR(txring->chan.chno), 2);
142462306a36Sopenharmony_ci}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_cistatic netdev_tx_t pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev)
142762306a36Sopenharmony_ci{
142862306a36Sopenharmony_ci	struct pasemi_mac * const mac = netdev_priv(dev);
142962306a36Sopenharmony_ci	struct pasemi_mac_txring * const txring = tx_ring(mac);
143062306a36Sopenharmony_ci	struct pasemi_mac_csring *csring;
143162306a36Sopenharmony_ci	u64 dflags = 0;
143262306a36Sopenharmony_ci	u64 mactx;
143362306a36Sopenharmony_ci	dma_addr_t map[MAX_SKB_FRAGS+1];
143462306a36Sopenharmony_ci	unsigned int map_size[MAX_SKB_FRAGS+1];
143562306a36Sopenharmony_ci	unsigned long flags;
143662306a36Sopenharmony_ci	int i, nfrags;
143762306a36Sopenharmony_ci	int fill;
143862306a36Sopenharmony_ci	const int nh_off = skb_network_offset(skb);
143962306a36Sopenharmony_ci	const int nh_len = skb_network_header_len(skb);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	prefetch(&txring->ring_info);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	dflags = XCT_MACTX_O | XCT_MACTX_ST | XCT_MACTX_CRC_PAD;
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	nfrags = skb_shinfo(skb)->nr_frags;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	map[0] = dma_map_single(&mac->dma_pdev->dev, skb->data,
144862306a36Sopenharmony_ci				skb_headlen(skb), DMA_TO_DEVICE);
144962306a36Sopenharmony_ci	map_size[0] = skb_headlen(skb);
145062306a36Sopenharmony_ci	if (dma_mapping_error(&mac->dma_pdev->dev, map[0]))
145162306a36Sopenharmony_ci		goto out_err_nolock;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	for (i = 0; i < nfrags; i++) {
145462306a36Sopenharmony_ci		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci		map[i + 1] = skb_frag_dma_map(&mac->dma_pdev->dev, frag, 0,
145762306a36Sopenharmony_ci					      skb_frag_size(frag), DMA_TO_DEVICE);
145862306a36Sopenharmony_ci		map_size[i+1] = skb_frag_size(frag);
145962306a36Sopenharmony_ci		if (dma_mapping_error(&mac->dma_pdev->dev, map[i + 1])) {
146062306a36Sopenharmony_ci			nfrags = i;
146162306a36Sopenharmony_ci			goto out_err_nolock;
146262306a36Sopenharmony_ci		}
146362306a36Sopenharmony_ci	}
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL && skb->len <= 1540) {
146662306a36Sopenharmony_ci		switch (ip_hdr(skb)->protocol) {
146762306a36Sopenharmony_ci		case IPPROTO_TCP:
146862306a36Sopenharmony_ci			dflags |= XCT_MACTX_CSUM_TCP;
146962306a36Sopenharmony_ci			dflags |= XCT_MACTX_IPH(nh_len >> 2);
147062306a36Sopenharmony_ci			dflags |= XCT_MACTX_IPO(nh_off);
147162306a36Sopenharmony_ci			break;
147262306a36Sopenharmony_ci		case IPPROTO_UDP:
147362306a36Sopenharmony_ci			dflags |= XCT_MACTX_CSUM_UDP;
147462306a36Sopenharmony_ci			dflags |= XCT_MACTX_IPH(nh_len >> 2);
147562306a36Sopenharmony_ci			dflags |= XCT_MACTX_IPO(nh_off);
147662306a36Sopenharmony_ci			break;
147762306a36Sopenharmony_ci		default:
147862306a36Sopenharmony_ci			WARN_ON(1);
147962306a36Sopenharmony_ci		}
148062306a36Sopenharmony_ci	}
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	mactx = dflags | XCT_MACTX_LLEN(skb->len);
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	spin_lock_irqsave(&txring->lock, flags);
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	/* Avoid stepping on the same cache line that the DMA controller
148762306a36Sopenharmony_ci	 * is currently about to send, so leave at least 8 words available.
148862306a36Sopenharmony_ci	 * Total free space needed is mactx + fragments + 8
148962306a36Sopenharmony_ci	 */
149062306a36Sopenharmony_ci	if (RING_AVAIL(txring) < nfrags + 14) {
149162306a36Sopenharmony_ci		/* no room -- stop the queue and wait for tx intr */
149262306a36Sopenharmony_ci		netif_stop_queue(dev);
149362306a36Sopenharmony_ci		goto out_err;
149462306a36Sopenharmony_ci	}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	/* Queue up checksum + event descriptors, if needed */
149762306a36Sopenharmony_ci	if (mac->num_cs && skb->ip_summed == CHECKSUM_PARTIAL && skb->len > 1540) {
149862306a36Sopenharmony_ci		csring = mac->cs[mac->last_cs];
149962306a36Sopenharmony_ci		mac->last_cs = (mac->last_cs + 1) % mac->num_cs;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci		pasemi_mac_queue_csdesc(skb, map, map_size, txring, csring);
150262306a36Sopenharmony_ci	}
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	fill = txring->next_to_fill;
150562306a36Sopenharmony_ci	TX_DESC(txring, fill) = mactx;
150662306a36Sopenharmony_ci	TX_DESC_INFO(txring, fill).dma = nfrags;
150762306a36Sopenharmony_ci	fill++;
150862306a36Sopenharmony_ci	TX_DESC_INFO(txring, fill).skb = skb;
150962306a36Sopenharmony_ci	for (i = 0; i <= nfrags; i++) {
151062306a36Sopenharmony_ci		TX_DESC(txring, fill+i) =
151162306a36Sopenharmony_ci			XCT_PTR_LEN(map_size[i]) | XCT_PTR_ADDR(map[i]);
151262306a36Sopenharmony_ci		TX_DESC_INFO(txring, fill+i).dma = map[i];
151362306a36Sopenharmony_ci	}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	/* We have to add an even number of 8-byte entries to the ring
151662306a36Sopenharmony_ci	 * even if the last one is unused. That means always an odd number
151762306a36Sopenharmony_ci	 * of pointers + one mactx descriptor.
151862306a36Sopenharmony_ci	 */
151962306a36Sopenharmony_ci	if (nfrags & 1)
152062306a36Sopenharmony_ci		nfrags++;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	txring->next_to_fill = (fill + nfrags + 1) & (TX_RING_SIZE-1);
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	dev->stats.tx_packets++;
152562306a36Sopenharmony_ci	dev->stats.tx_bytes += skb->len;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	spin_unlock_irqrestore(&txring->lock, flags);
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	write_dma_reg(PAS_DMA_TXCHAN_INCR(txring->chan.chno), (nfrags+2) >> 1);
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	return NETDEV_TX_OK;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ciout_err:
153462306a36Sopenharmony_ci	spin_unlock_irqrestore(&txring->lock, flags);
153562306a36Sopenharmony_ciout_err_nolock:
153662306a36Sopenharmony_ci	while (nfrags--)
153762306a36Sopenharmony_ci		dma_unmap_single(&mac->dma_pdev->dev, map[nfrags],
153862306a36Sopenharmony_ci				 map_size[nfrags], DMA_TO_DEVICE);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	return NETDEV_TX_BUSY;
154162306a36Sopenharmony_ci}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_cistatic void pasemi_mac_set_rx_mode(struct net_device *dev)
154462306a36Sopenharmony_ci{
154562306a36Sopenharmony_ci	const struct pasemi_mac *mac = netdev_priv(dev);
154662306a36Sopenharmony_ci	unsigned int flags;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	/* Set promiscuous */
155162306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC)
155262306a36Sopenharmony_ci		flags |= PAS_MAC_CFG_PCFG_PR;
155362306a36Sopenharmony_ci	else
155462306a36Sopenharmony_ci		flags &= ~PAS_MAC_CFG_PCFG_PR;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags);
155762306a36Sopenharmony_ci}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_cistatic int pasemi_mac_poll(struct napi_struct *napi, int budget)
156162306a36Sopenharmony_ci{
156262306a36Sopenharmony_ci	struct pasemi_mac *mac = container_of(napi, struct pasemi_mac, napi);
156362306a36Sopenharmony_ci	int pkts;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	pasemi_mac_clean_tx(tx_ring(mac));
156662306a36Sopenharmony_ci	pkts = pasemi_mac_clean_rx(rx_ring(mac), budget);
156762306a36Sopenharmony_ci	if (pkts < budget) {
156862306a36Sopenharmony_ci		/* all done, no more packets present */
156962306a36Sopenharmony_ci		napi_complete_done(napi, pkts);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci		pasemi_mac_restart_rx_intr(mac);
157262306a36Sopenharmony_ci		pasemi_mac_restart_tx_intr(mac);
157362306a36Sopenharmony_ci	}
157462306a36Sopenharmony_ci	return pkts;
157562306a36Sopenharmony_ci}
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
157862306a36Sopenharmony_ci/*
157962306a36Sopenharmony_ci * Polling 'interrupt' - used by things like netconsole to send skbs
158062306a36Sopenharmony_ci * without having to re-enable interrupts. It's not called while
158162306a36Sopenharmony_ci * the interrupt routine is executing.
158262306a36Sopenharmony_ci */
158362306a36Sopenharmony_cistatic void pasemi_mac_netpoll(struct net_device *dev)
158462306a36Sopenharmony_ci{
158562306a36Sopenharmony_ci	const struct pasemi_mac *mac = netdev_priv(dev);
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	disable_irq(mac->tx->chan.irq);
158862306a36Sopenharmony_ci	pasemi_mac_tx_intr(mac->tx->chan.irq, mac->tx);
158962306a36Sopenharmony_ci	enable_irq(mac->tx->chan.irq);
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	disable_irq(mac->rx->chan.irq);
159262306a36Sopenharmony_ci	pasemi_mac_rx_intr(mac->rx->chan.irq, mac->rx);
159362306a36Sopenharmony_ci	enable_irq(mac->rx->chan.irq);
159462306a36Sopenharmony_ci}
159562306a36Sopenharmony_ci#endif
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_cistatic int pasemi_mac_change_mtu(struct net_device *dev, int new_mtu)
159862306a36Sopenharmony_ci{
159962306a36Sopenharmony_ci	struct pasemi_mac *mac = netdev_priv(dev);
160062306a36Sopenharmony_ci	unsigned int reg;
160162306a36Sopenharmony_ci	unsigned int rcmdsta = 0;
160262306a36Sopenharmony_ci	int running;
160362306a36Sopenharmony_ci	int ret = 0;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	running = netif_running(dev);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	if (running) {
160862306a36Sopenharmony_ci		/* Need to stop the interface, clean out all already
160962306a36Sopenharmony_ci		 * received buffers, free all unused buffers on the RX
161062306a36Sopenharmony_ci		 * interface ring, then finally re-fill the rx ring with
161162306a36Sopenharmony_ci		 * the new-size buffers and restart.
161262306a36Sopenharmony_ci		 */
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci		napi_disable(&mac->napi);
161562306a36Sopenharmony_ci		netif_tx_disable(dev);
161662306a36Sopenharmony_ci		pasemi_mac_intf_disable(mac);
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci		rcmdsta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if));
161962306a36Sopenharmony_ci		pasemi_mac_pause_rxint(mac);
162062306a36Sopenharmony_ci		pasemi_mac_clean_rx(rx_ring(mac), RX_RING_SIZE);
162162306a36Sopenharmony_ci		pasemi_mac_free_rx_buffers(mac);
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	/* Setup checksum channels if large MTU and none already allocated */
162662306a36Sopenharmony_ci	if (new_mtu > PE_DEF_MTU && !mac->num_cs) {
162762306a36Sopenharmony_ci		pasemi_mac_setup_csrings(mac);
162862306a36Sopenharmony_ci		if (!mac->num_cs) {
162962306a36Sopenharmony_ci			ret = -ENOMEM;
163062306a36Sopenharmony_ci			goto out;
163162306a36Sopenharmony_ci		}
163262306a36Sopenharmony_ci	}
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	/* Change maxf, i.e. what size frames are accepted.
163562306a36Sopenharmony_ci	 * Need room for ethernet header and CRC word
163662306a36Sopenharmony_ci	 */
163762306a36Sopenharmony_ci	reg = read_mac_reg(mac, PAS_MAC_CFG_MACCFG);
163862306a36Sopenharmony_ci	reg &= ~PAS_MAC_CFG_MACCFG_MAXF_M;
163962306a36Sopenharmony_ci	reg |= PAS_MAC_CFG_MACCFG_MAXF(new_mtu + ETH_HLEN + 4);
164062306a36Sopenharmony_ci	write_mac_reg(mac, PAS_MAC_CFG_MACCFG, reg);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	dev->mtu = new_mtu;
164362306a36Sopenharmony_ci	/* MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */
164462306a36Sopenharmony_ci	mac->bufsz = new_mtu + ETH_HLEN + ETH_FCS_LEN + LOCAL_SKB_ALIGN + 128;
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ciout:
164762306a36Sopenharmony_ci	if (running) {
164862306a36Sopenharmony_ci		write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if),
164962306a36Sopenharmony_ci			      rcmdsta | PAS_DMA_RXINT_RCMDSTA_EN);
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci		rx_ring(mac)->next_to_fill = 0;
165262306a36Sopenharmony_ci		pasemi_mac_replenish_rx_ring(dev, RX_RING_SIZE-1);
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci		napi_enable(&mac->napi);
165562306a36Sopenharmony_ci		netif_start_queue(dev);
165662306a36Sopenharmony_ci		pasemi_mac_intf_enable(mac);
165762306a36Sopenharmony_ci	}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	return ret;
166062306a36Sopenharmony_ci}
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_cistatic const struct net_device_ops pasemi_netdev_ops = {
166362306a36Sopenharmony_ci	.ndo_open		= pasemi_mac_open,
166462306a36Sopenharmony_ci	.ndo_stop		= pasemi_mac_close,
166562306a36Sopenharmony_ci	.ndo_start_xmit		= pasemi_mac_start_tx,
166662306a36Sopenharmony_ci	.ndo_set_rx_mode	= pasemi_mac_set_rx_mode,
166762306a36Sopenharmony_ci	.ndo_set_mac_address	= pasemi_mac_set_mac_addr,
166862306a36Sopenharmony_ci	.ndo_change_mtu		= pasemi_mac_change_mtu,
166962306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
167062306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
167162306a36Sopenharmony_ci	.ndo_poll_controller	= pasemi_mac_netpoll,
167262306a36Sopenharmony_ci#endif
167362306a36Sopenharmony_ci};
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_cistatic int
167662306a36Sopenharmony_cipasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
167762306a36Sopenharmony_ci{
167862306a36Sopenharmony_ci	struct net_device *dev;
167962306a36Sopenharmony_ci	struct pasemi_mac *mac;
168062306a36Sopenharmony_ci	int err, ret;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	err = pci_enable_device(pdev);
168362306a36Sopenharmony_ci	if (err)
168462306a36Sopenharmony_ci		return err;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct pasemi_mac));
168762306a36Sopenharmony_ci	if (dev == NULL) {
168862306a36Sopenharmony_ci		err = -ENOMEM;
168962306a36Sopenharmony_ci		goto out_disable_device;
169062306a36Sopenharmony_ci	}
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	pci_set_drvdata(pdev, dev);
169362306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	mac = netdev_priv(dev);
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	mac->pdev = pdev;
169862306a36Sopenharmony_ci	mac->netdev = dev;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	netif_napi_add(dev, &mac->napi, pasemi_mac_poll);
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	dev->features = NETIF_F_IP_CSUM | NETIF_F_LLTX | NETIF_F_SG |
170362306a36Sopenharmony_ci			NETIF_F_HIGHDMA | NETIF_F_GSO;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	mac->dma_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa007, NULL);
170662306a36Sopenharmony_ci	if (!mac->dma_pdev) {
170762306a36Sopenharmony_ci		dev_err(&mac->pdev->dev, "Can't find DMA Controller\n");
170862306a36Sopenharmony_ci		err = -ENODEV;
170962306a36Sopenharmony_ci		goto out;
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci	dma_set_mask(&mac->dma_pdev->dev, DMA_BIT_MASK(64));
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	mac->iob_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa001, NULL);
171462306a36Sopenharmony_ci	if (!mac->iob_pdev) {
171562306a36Sopenharmony_ci		dev_err(&mac->pdev->dev, "Can't find I/O Bridge\n");
171662306a36Sopenharmony_ci		err = -ENODEV;
171762306a36Sopenharmony_ci		goto out;
171862306a36Sopenharmony_ci	}
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	/* get mac addr from device tree */
172162306a36Sopenharmony_ci	if (pasemi_get_mac_addr(mac) || !is_valid_ether_addr(mac->mac_addr)) {
172262306a36Sopenharmony_ci		err = -ENODEV;
172362306a36Sopenharmony_ci		goto out;
172462306a36Sopenharmony_ci	}
172562306a36Sopenharmony_ci	eth_hw_addr_set(dev, mac->mac_addr);
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	ret = mac_to_intf(mac);
172862306a36Sopenharmony_ci	if (ret < 0) {
172962306a36Sopenharmony_ci		dev_err(&mac->pdev->dev, "Can't map DMA interface\n");
173062306a36Sopenharmony_ci		err = -ENODEV;
173162306a36Sopenharmony_ci		goto out;
173262306a36Sopenharmony_ci	}
173362306a36Sopenharmony_ci	mac->dma_if = ret;
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	switch (pdev->device) {
173662306a36Sopenharmony_ci	case 0xa005:
173762306a36Sopenharmony_ci		mac->type = MAC_TYPE_GMAC;
173862306a36Sopenharmony_ci		break;
173962306a36Sopenharmony_ci	case 0xa006:
174062306a36Sopenharmony_ci		mac->type = MAC_TYPE_XAUI;
174162306a36Sopenharmony_ci		break;
174262306a36Sopenharmony_ci	default:
174362306a36Sopenharmony_ci		err = -ENODEV;
174462306a36Sopenharmony_ci		goto out;
174562306a36Sopenharmony_ci	}
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	dev->netdev_ops = &pasemi_netdev_ops;
174862306a36Sopenharmony_ci	dev->mtu = PE_DEF_MTU;
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	/* MTU range: 64 - 9000 */
175162306a36Sopenharmony_ci	dev->min_mtu = PE_MIN_MTU;
175262306a36Sopenharmony_ci	dev->max_mtu = PE_MAX_MTU;
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	/* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */
175562306a36Sopenharmony_ci	mac->bufsz = dev->mtu + ETH_HLEN + ETH_FCS_LEN + LOCAL_SKB_ALIGN + 128;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	dev->ethtool_ops = &pasemi_mac_ethtool_ops;
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	if (err)
176062306a36Sopenharmony_ci		goto out;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	mac->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	/* Enable most messages by default */
176562306a36Sopenharmony_ci	mac->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	err = register_netdev(dev);
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	if (err) {
177062306a36Sopenharmony_ci		dev_err(&mac->pdev->dev, "register_netdev failed with error %d\n",
177162306a36Sopenharmony_ci			err);
177262306a36Sopenharmony_ci		goto out;
177362306a36Sopenharmony_ci	} else if (netif_msg_probe(mac)) {
177462306a36Sopenharmony_ci		printk(KERN_INFO "%s: PA Semi %s: intf %d, hw addr %pM\n",
177562306a36Sopenharmony_ci		       dev->name, mac->type == MAC_TYPE_GMAC ? "GMAC" : "XAUI",
177662306a36Sopenharmony_ci		       mac->dma_if, dev->dev_addr);
177762306a36Sopenharmony_ci	}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	return err;
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ciout:
178262306a36Sopenharmony_ci	pci_dev_put(mac->iob_pdev);
178362306a36Sopenharmony_ci	pci_dev_put(mac->dma_pdev);
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	free_netdev(dev);
178662306a36Sopenharmony_ciout_disable_device:
178762306a36Sopenharmony_ci	pci_disable_device(pdev);
178862306a36Sopenharmony_ci	return err;
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci}
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_cistatic void pasemi_mac_remove(struct pci_dev *pdev)
179362306a36Sopenharmony_ci{
179462306a36Sopenharmony_ci	struct net_device *netdev = pci_get_drvdata(pdev);
179562306a36Sopenharmony_ci	struct pasemi_mac *mac;
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	if (!netdev)
179862306a36Sopenharmony_ci		return;
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	mac = netdev_priv(netdev);
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	unregister_netdev(netdev);
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	pci_disable_device(pdev);
180562306a36Sopenharmony_ci	pci_dev_put(mac->dma_pdev);
180662306a36Sopenharmony_ci	pci_dev_put(mac->iob_pdev);
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	pasemi_dma_free_chan(&mac->tx->chan);
180962306a36Sopenharmony_ci	pasemi_dma_free_chan(&mac->rx->chan);
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	free_netdev(netdev);
181262306a36Sopenharmony_ci}
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_cistatic const struct pci_device_id pasemi_mac_pci_tbl[] = {
181562306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_PASEMI, 0xa005) },
181662306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_PASEMI, 0xa006) },
181762306a36Sopenharmony_ci	{ },
181862306a36Sopenharmony_ci};
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pasemi_mac_pci_tbl);
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_cistatic struct pci_driver pasemi_mac_driver = {
182362306a36Sopenharmony_ci	.name		= "pasemi_mac",
182462306a36Sopenharmony_ci	.id_table	= pasemi_mac_pci_tbl,
182562306a36Sopenharmony_ci	.probe		= pasemi_mac_probe,
182662306a36Sopenharmony_ci	.remove		= pasemi_mac_remove,
182762306a36Sopenharmony_ci};
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_cistatic void __exit pasemi_mac_cleanup_module(void)
183062306a36Sopenharmony_ci{
183162306a36Sopenharmony_ci	pci_unregister_driver(&pasemi_mac_driver);
183262306a36Sopenharmony_ci}
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_cistatic int pasemi_mac_init_module(void)
183562306a36Sopenharmony_ci{
183662306a36Sopenharmony_ci	int err;
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	err = pasemi_dma_init();
183962306a36Sopenharmony_ci	if (err)
184062306a36Sopenharmony_ci		return err;
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci	return pci_register_driver(&pasemi_mac_driver);
184362306a36Sopenharmony_ci}
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_cimodule_init(pasemi_mac_init_module);
184662306a36Sopenharmony_cimodule_exit(pasemi_mac_cleanup_module);
1847