162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
362306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
462306a36Sopenharmony_ci * for more details.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2009-2012 Cavium, Inc
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/platform_device.h>
1062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1162306a36Sopenharmony_ci#include <linux/etherdevice.h>
1262306a36Sopenharmony_ci#include <linux/capability.h>
1362306a36Sopenharmony_ci#include <linux/net_tstamp.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/netdevice.h>
1662306a36Sopenharmony_ci#include <linux/spinlock.h>
1762306a36Sopenharmony_ci#include <linux/if_vlan.h>
1862306a36Sopenharmony_ci#include <linux/of_mdio.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/of_net.h>
2162306a36Sopenharmony_ci#include <linux/init.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <linux/phy.h>
2462306a36Sopenharmony_ci#include <linux/io.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <asm/octeon/octeon.h>
2762306a36Sopenharmony_ci#include <asm/octeon/cvmx-mixx-defs.h>
2862306a36Sopenharmony_ci#include <asm/octeon/cvmx-agl-defs.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define DRV_NAME "octeon_mgmt"
3162306a36Sopenharmony_ci#define DRV_DESCRIPTION \
3262306a36Sopenharmony_ci	"Cavium Networks Octeon MII (management) port Network Driver"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define OCTEON_MGMT_NAPI_WEIGHT 16
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* Ring sizes that are powers of two allow for more efficient modulo
3762306a36Sopenharmony_ci * opertions.
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_ci#define OCTEON_MGMT_RX_RING_SIZE 512
4062306a36Sopenharmony_ci#define OCTEON_MGMT_TX_RING_SIZE 128
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* Allow 8 bytes for vlan and FCS. */
4362306a36Sopenharmony_ci#define OCTEON_MGMT_RX_HEADROOM (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciunion mgmt_port_ring_entry {
4662306a36Sopenharmony_ci	u64 d64;
4762306a36Sopenharmony_ci	struct {
4862306a36Sopenharmony_ci#define RING_ENTRY_CODE_DONE 0xf
4962306a36Sopenharmony_ci#define RING_ENTRY_CODE_MORE 0x10
5062306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD
5162306a36Sopenharmony_ci		u64 reserved_62_63:2;
5262306a36Sopenharmony_ci		/* Length of the buffer/packet in bytes */
5362306a36Sopenharmony_ci		u64 len:14;
5462306a36Sopenharmony_ci		/* For TX, signals that the packet should be timestamped */
5562306a36Sopenharmony_ci		u64 tstamp:1;
5662306a36Sopenharmony_ci		/* The RX error code */
5762306a36Sopenharmony_ci		u64 code:7;
5862306a36Sopenharmony_ci		/* Physical address of the buffer */
5962306a36Sopenharmony_ci		u64 addr:40;
6062306a36Sopenharmony_ci#else
6162306a36Sopenharmony_ci		u64 addr:40;
6262306a36Sopenharmony_ci		u64 code:7;
6362306a36Sopenharmony_ci		u64 tstamp:1;
6462306a36Sopenharmony_ci		u64 len:14;
6562306a36Sopenharmony_ci		u64 reserved_62_63:2;
6662306a36Sopenharmony_ci#endif
6762306a36Sopenharmony_ci	} s;
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define MIX_ORING1	0x0
7162306a36Sopenharmony_ci#define MIX_ORING2	0x8
7262306a36Sopenharmony_ci#define MIX_IRING1	0x10
7362306a36Sopenharmony_ci#define MIX_IRING2	0x18
7462306a36Sopenharmony_ci#define MIX_CTL		0x20
7562306a36Sopenharmony_ci#define MIX_IRHWM	0x28
7662306a36Sopenharmony_ci#define MIX_IRCNT	0x30
7762306a36Sopenharmony_ci#define MIX_ORHWM	0x38
7862306a36Sopenharmony_ci#define MIX_ORCNT	0x40
7962306a36Sopenharmony_ci#define MIX_ISR		0x48
8062306a36Sopenharmony_ci#define MIX_INTENA	0x50
8162306a36Sopenharmony_ci#define MIX_REMCNT	0x58
8262306a36Sopenharmony_ci#define MIX_BIST	0x78
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define AGL_GMX_PRT_CFG			0x10
8562306a36Sopenharmony_ci#define AGL_GMX_RX_FRM_CTL		0x18
8662306a36Sopenharmony_ci#define AGL_GMX_RX_FRM_MAX		0x30
8762306a36Sopenharmony_ci#define AGL_GMX_RX_JABBER		0x38
8862306a36Sopenharmony_ci#define AGL_GMX_RX_STATS_CTL		0x50
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define AGL_GMX_RX_STATS_PKTS_DRP	0xb0
9162306a36Sopenharmony_ci#define AGL_GMX_RX_STATS_OCTS_DRP	0xb8
9262306a36Sopenharmony_ci#define AGL_GMX_RX_STATS_PKTS_BAD	0xc0
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define AGL_GMX_RX_ADR_CTL		0x100
9562306a36Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM_EN		0x108
9662306a36Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM0		0x180
9762306a36Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM1		0x188
9862306a36Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM2		0x190
9962306a36Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM3		0x198
10062306a36Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM4		0x1a0
10162306a36Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM5		0x1a8
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define AGL_GMX_TX_CLK			0x208
10462306a36Sopenharmony_ci#define AGL_GMX_TX_STATS_CTL		0x268
10562306a36Sopenharmony_ci#define AGL_GMX_TX_CTL			0x270
10662306a36Sopenharmony_ci#define AGL_GMX_TX_STAT0		0x280
10762306a36Sopenharmony_ci#define AGL_GMX_TX_STAT1		0x288
10862306a36Sopenharmony_ci#define AGL_GMX_TX_STAT2		0x290
10962306a36Sopenharmony_ci#define AGL_GMX_TX_STAT3		0x298
11062306a36Sopenharmony_ci#define AGL_GMX_TX_STAT4		0x2a0
11162306a36Sopenharmony_ci#define AGL_GMX_TX_STAT5		0x2a8
11262306a36Sopenharmony_ci#define AGL_GMX_TX_STAT6		0x2b0
11362306a36Sopenharmony_ci#define AGL_GMX_TX_STAT7		0x2b8
11462306a36Sopenharmony_ci#define AGL_GMX_TX_STAT8		0x2c0
11562306a36Sopenharmony_ci#define AGL_GMX_TX_STAT9		0x2c8
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistruct octeon_mgmt {
11862306a36Sopenharmony_ci	struct net_device *netdev;
11962306a36Sopenharmony_ci	u64 mix;
12062306a36Sopenharmony_ci	u64 agl;
12162306a36Sopenharmony_ci	u64 agl_prt_ctl;
12262306a36Sopenharmony_ci	int port;
12362306a36Sopenharmony_ci	int irq;
12462306a36Sopenharmony_ci	bool has_rx_tstamp;
12562306a36Sopenharmony_ci	u64 *tx_ring;
12662306a36Sopenharmony_ci	dma_addr_t tx_ring_handle;
12762306a36Sopenharmony_ci	unsigned int tx_next;
12862306a36Sopenharmony_ci	unsigned int tx_next_clean;
12962306a36Sopenharmony_ci	unsigned int tx_current_fill;
13062306a36Sopenharmony_ci	/* The tx_list lock also protects the ring related variables */
13162306a36Sopenharmony_ci	struct sk_buff_head tx_list;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* RX variables only touched in napi_poll.  No locking necessary. */
13462306a36Sopenharmony_ci	u64 *rx_ring;
13562306a36Sopenharmony_ci	dma_addr_t rx_ring_handle;
13662306a36Sopenharmony_ci	unsigned int rx_next;
13762306a36Sopenharmony_ci	unsigned int rx_next_fill;
13862306a36Sopenharmony_ci	unsigned int rx_current_fill;
13962306a36Sopenharmony_ci	struct sk_buff_head rx_list;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	spinlock_t lock;
14262306a36Sopenharmony_ci	unsigned int last_duplex;
14362306a36Sopenharmony_ci	unsigned int last_link;
14462306a36Sopenharmony_ci	unsigned int last_speed;
14562306a36Sopenharmony_ci	struct device *dev;
14662306a36Sopenharmony_ci	struct napi_struct napi;
14762306a36Sopenharmony_ci	struct tasklet_struct tx_clean_tasklet;
14862306a36Sopenharmony_ci	struct device_node *phy_np;
14962306a36Sopenharmony_ci	resource_size_t mix_phys;
15062306a36Sopenharmony_ci	resource_size_t mix_size;
15162306a36Sopenharmony_ci	resource_size_t agl_phys;
15262306a36Sopenharmony_ci	resource_size_t agl_size;
15362306a36Sopenharmony_ci	resource_size_t agl_prt_ctl_phys;
15462306a36Sopenharmony_ci	resource_size_t agl_prt_ctl_size;
15562306a36Sopenharmony_ci};
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic void octeon_mgmt_set_rx_irq(struct octeon_mgmt *p, int enable)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	union cvmx_mixx_intena mix_intena;
16062306a36Sopenharmony_ci	unsigned long flags;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	spin_lock_irqsave(&p->lock, flags);
16362306a36Sopenharmony_ci	mix_intena.u64 = cvmx_read_csr(p->mix + MIX_INTENA);
16462306a36Sopenharmony_ci	mix_intena.s.ithena = enable ? 1 : 0;
16562306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64);
16662306a36Sopenharmony_ci	spin_unlock_irqrestore(&p->lock, flags);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic void octeon_mgmt_set_tx_irq(struct octeon_mgmt *p, int enable)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	union cvmx_mixx_intena mix_intena;
17262306a36Sopenharmony_ci	unsigned long flags;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	spin_lock_irqsave(&p->lock, flags);
17562306a36Sopenharmony_ci	mix_intena.u64 = cvmx_read_csr(p->mix + MIX_INTENA);
17662306a36Sopenharmony_ci	mix_intena.s.othena = enable ? 1 : 0;
17762306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64);
17862306a36Sopenharmony_ci	spin_unlock_irqrestore(&p->lock, flags);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void octeon_mgmt_enable_rx_irq(struct octeon_mgmt *p)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	octeon_mgmt_set_rx_irq(p, 1);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic void octeon_mgmt_disable_rx_irq(struct octeon_mgmt *p)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	octeon_mgmt_set_rx_irq(p, 0);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic void octeon_mgmt_enable_tx_irq(struct octeon_mgmt *p)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	octeon_mgmt_set_tx_irq(p, 1);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void octeon_mgmt_disable_tx_irq(struct octeon_mgmt *p)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	octeon_mgmt_set_tx_irq(p, 0);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic unsigned int ring_max_fill(unsigned int ring_size)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	return ring_size - 8;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic unsigned int ring_size_to_bytes(unsigned int ring_size)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	return ring_size * sizeof(union mgmt_port_ring_entry);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic void octeon_mgmt_rx_fill_ring(struct net_device *netdev)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	while (p->rx_current_fill < ring_max_fill(OCTEON_MGMT_RX_RING_SIZE)) {
21662306a36Sopenharmony_ci		unsigned int size;
21762306a36Sopenharmony_ci		union mgmt_port_ring_entry re;
21862306a36Sopenharmony_ci		struct sk_buff *skb;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		/* CN56XX pass 1 needs 8 bytes of padding.  */
22162306a36Sopenharmony_ci		size = netdev->mtu + OCTEON_MGMT_RX_HEADROOM + 8 + NET_IP_ALIGN;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		skb = netdev_alloc_skb(netdev, size);
22462306a36Sopenharmony_ci		if (!skb)
22562306a36Sopenharmony_ci			break;
22662306a36Sopenharmony_ci		skb_reserve(skb, NET_IP_ALIGN);
22762306a36Sopenharmony_ci		__skb_queue_tail(&p->rx_list, skb);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		re.d64 = 0;
23062306a36Sopenharmony_ci		re.s.len = size;
23162306a36Sopenharmony_ci		re.s.addr = dma_map_single(p->dev, skb->data,
23262306a36Sopenharmony_ci					   size,
23362306a36Sopenharmony_ci					   DMA_FROM_DEVICE);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		/* Put it in the ring.  */
23662306a36Sopenharmony_ci		p->rx_ring[p->rx_next_fill] = re.d64;
23762306a36Sopenharmony_ci		/* Make sure there is no reorder of filling the ring and ringing
23862306a36Sopenharmony_ci		 * the bell
23962306a36Sopenharmony_ci		 */
24062306a36Sopenharmony_ci		wmb();
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		dma_sync_single_for_device(p->dev, p->rx_ring_handle,
24362306a36Sopenharmony_ci					   ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE),
24462306a36Sopenharmony_ci					   DMA_BIDIRECTIONAL);
24562306a36Sopenharmony_ci		p->rx_next_fill =
24662306a36Sopenharmony_ci			(p->rx_next_fill + 1) % OCTEON_MGMT_RX_RING_SIZE;
24762306a36Sopenharmony_ci		p->rx_current_fill++;
24862306a36Sopenharmony_ci		/* Ring the bell.  */
24962306a36Sopenharmony_ci		cvmx_write_csr(p->mix + MIX_IRING2, 1);
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	union cvmx_mixx_orcnt mix_orcnt;
25662306a36Sopenharmony_ci	union mgmt_port_ring_entry re;
25762306a36Sopenharmony_ci	struct sk_buff *skb;
25862306a36Sopenharmony_ci	int cleaned = 0;
25962306a36Sopenharmony_ci	unsigned long flags;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT);
26262306a36Sopenharmony_ci	while (mix_orcnt.s.orcnt) {
26362306a36Sopenharmony_ci		spin_lock_irqsave(&p->tx_list.lock, flags);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci		mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		if (mix_orcnt.s.orcnt == 0) {
26862306a36Sopenharmony_ci			spin_unlock_irqrestore(&p->tx_list.lock, flags);
26962306a36Sopenharmony_ci			break;
27062306a36Sopenharmony_ci		}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci		dma_sync_single_for_cpu(p->dev, p->tx_ring_handle,
27362306a36Sopenharmony_ci					ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE),
27462306a36Sopenharmony_ci					DMA_BIDIRECTIONAL);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		re.d64 = p->tx_ring[p->tx_next_clean];
27762306a36Sopenharmony_ci		p->tx_next_clean =
27862306a36Sopenharmony_ci			(p->tx_next_clean + 1) % OCTEON_MGMT_TX_RING_SIZE;
27962306a36Sopenharmony_ci		skb = __skb_dequeue(&p->tx_list);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		mix_orcnt.u64 = 0;
28262306a36Sopenharmony_ci		mix_orcnt.s.orcnt = 1;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		/* Acknowledge to hardware that we have the buffer.  */
28562306a36Sopenharmony_ci		cvmx_write_csr(p->mix + MIX_ORCNT, mix_orcnt.u64);
28662306a36Sopenharmony_ci		p->tx_current_fill--;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		spin_unlock_irqrestore(&p->tx_list.lock, flags);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		dma_unmap_single(p->dev, re.s.addr, re.s.len,
29162306a36Sopenharmony_ci				 DMA_TO_DEVICE);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		/* Read the hardware TX timestamp if one was recorded */
29462306a36Sopenharmony_ci		if (unlikely(re.s.tstamp)) {
29562306a36Sopenharmony_ci			struct skb_shared_hwtstamps ts;
29662306a36Sopenharmony_ci			u64 ns;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci			memset(&ts, 0, sizeof(ts));
29962306a36Sopenharmony_ci			/* Read the timestamp */
30062306a36Sopenharmony_ci			ns = cvmx_read_csr(CVMX_MIXX_TSTAMP(p->port));
30162306a36Sopenharmony_ci			/* Remove the timestamp from the FIFO */
30262306a36Sopenharmony_ci			cvmx_write_csr(CVMX_MIXX_TSCTL(p->port), 0);
30362306a36Sopenharmony_ci			/* Tell the kernel about the timestamp */
30462306a36Sopenharmony_ci			ts.hwtstamp = ns_to_ktime(ns);
30562306a36Sopenharmony_ci			skb_tstamp_tx(skb, &ts);
30662306a36Sopenharmony_ci		}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
30962306a36Sopenharmony_ci		cleaned++;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT);
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (cleaned && netif_queue_stopped(p->netdev))
31562306a36Sopenharmony_ci		netif_wake_queue(p->netdev);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic void octeon_mgmt_clean_tx_tasklet(struct tasklet_struct *t)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct octeon_mgmt *p = from_tasklet(p, t, tx_clean_tasklet);
32162306a36Sopenharmony_ci	octeon_mgmt_clean_tx_buffers(p);
32262306a36Sopenharmony_ci	octeon_mgmt_enable_tx_irq(p);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void octeon_mgmt_update_rx_stats(struct net_device *netdev)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
32862306a36Sopenharmony_ci	unsigned long flags;
32962306a36Sopenharmony_ci	u64 drop, bad;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/* These reads also clear the count registers.  */
33262306a36Sopenharmony_ci	drop = cvmx_read_csr(p->agl + AGL_GMX_RX_STATS_PKTS_DRP);
33362306a36Sopenharmony_ci	bad = cvmx_read_csr(p->agl + AGL_GMX_RX_STATS_PKTS_BAD);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (drop || bad) {
33662306a36Sopenharmony_ci		/* Do an atomic update. */
33762306a36Sopenharmony_ci		spin_lock_irqsave(&p->lock, flags);
33862306a36Sopenharmony_ci		netdev->stats.rx_errors += bad;
33962306a36Sopenharmony_ci		netdev->stats.rx_dropped += drop;
34062306a36Sopenharmony_ci		spin_unlock_irqrestore(&p->lock, flags);
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic void octeon_mgmt_update_tx_stats(struct net_device *netdev)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
34762306a36Sopenharmony_ci	unsigned long flags;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	union cvmx_agl_gmx_txx_stat0 s0;
35062306a36Sopenharmony_ci	union cvmx_agl_gmx_txx_stat1 s1;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* These reads also clear the count registers.  */
35362306a36Sopenharmony_ci	s0.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_STAT0);
35462306a36Sopenharmony_ci	s1.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_STAT1);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (s0.s.xsdef || s0.s.xscol || s1.s.scol || s1.s.mcol) {
35762306a36Sopenharmony_ci		/* Do an atomic update. */
35862306a36Sopenharmony_ci		spin_lock_irqsave(&p->lock, flags);
35962306a36Sopenharmony_ci		netdev->stats.tx_errors += s0.s.xsdef + s0.s.xscol;
36062306a36Sopenharmony_ci		netdev->stats.collisions += s1.s.scol + s1.s.mcol;
36162306a36Sopenharmony_ci		spin_unlock_irqrestore(&p->lock, flags);
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci/*
36662306a36Sopenharmony_ci * Dequeue a receive skb and its corresponding ring entry.  The ring
36762306a36Sopenharmony_ci * entry is returned, *pskb is updated to point to the skb.
36862306a36Sopenharmony_ci */
36962306a36Sopenharmony_cistatic u64 octeon_mgmt_dequeue_rx_buffer(struct octeon_mgmt *p,
37062306a36Sopenharmony_ci					 struct sk_buff **pskb)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	union mgmt_port_ring_entry re;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	dma_sync_single_for_cpu(p->dev, p->rx_ring_handle,
37562306a36Sopenharmony_ci				ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE),
37662306a36Sopenharmony_ci				DMA_BIDIRECTIONAL);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	re.d64 = p->rx_ring[p->rx_next];
37962306a36Sopenharmony_ci	p->rx_next = (p->rx_next + 1) % OCTEON_MGMT_RX_RING_SIZE;
38062306a36Sopenharmony_ci	p->rx_current_fill--;
38162306a36Sopenharmony_ci	*pskb = __skb_dequeue(&p->rx_list);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	dma_unmap_single(p->dev, re.s.addr,
38462306a36Sopenharmony_ci			 ETH_FRAME_LEN + OCTEON_MGMT_RX_HEADROOM,
38562306a36Sopenharmony_ci			 DMA_FROM_DEVICE);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return re.d64;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic int octeon_mgmt_receive_one(struct octeon_mgmt *p)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct net_device *netdev = p->netdev;
39462306a36Sopenharmony_ci	union cvmx_mixx_ircnt mix_ircnt;
39562306a36Sopenharmony_ci	union mgmt_port_ring_entry re;
39662306a36Sopenharmony_ci	struct sk_buff *skb;
39762306a36Sopenharmony_ci	struct sk_buff *skb2;
39862306a36Sopenharmony_ci	struct sk_buff *skb_new;
39962306a36Sopenharmony_ci	union mgmt_port_ring_entry re2;
40062306a36Sopenharmony_ci	int rc = 1;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	re.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb);
40462306a36Sopenharmony_ci	if (likely(re.s.code == RING_ENTRY_CODE_DONE)) {
40562306a36Sopenharmony_ci		/* A good packet, send it up. */
40662306a36Sopenharmony_ci		skb_put(skb, re.s.len);
40762306a36Sopenharmony_cigood:
40862306a36Sopenharmony_ci		/* Process the RX timestamp if it was recorded */
40962306a36Sopenharmony_ci		if (p->has_rx_tstamp) {
41062306a36Sopenharmony_ci			/* The first 8 bytes are the timestamp */
41162306a36Sopenharmony_ci			u64 ns = *(u64 *)skb->data;
41262306a36Sopenharmony_ci			struct skb_shared_hwtstamps *ts;
41362306a36Sopenharmony_ci			ts = skb_hwtstamps(skb);
41462306a36Sopenharmony_ci			ts->hwtstamp = ns_to_ktime(ns);
41562306a36Sopenharmony_ci			__skb_pull(skb, 8);
41662306a36Sopenharmony_ci		}
41762306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, netdev);
41862306a36Sopenharmony_ci		netdev->stats.rx_packets++;
41962306a36Sopenharmony_ci		netdev->stats.rx_bytes += skb->len;
42062306a36Sopenharmony_ci		netif_receive_skb(skb);
42162306a36Sopenharmony_ci		rc = 0;
42262306a36Sopenharmony_ci	} else if (re.s.code == RING_ENTRY_CODE_MORE) {
42362306a36Sopenharmony_ci		/* Packet split across skbs.  This can happen if we
42462306a36Sopenharmony_ci		 * increase the MTU.  Buffers that are already in the
42562306a36Sopenharmony_ci		 * rx ring can then end up being too small.  As the rx
42662306a36Sopenharmony_ci		 * ring is refilled, buffers sized for the new MTU
42762306a36Sopenharmony_ci		 * will be used and we should go back to the normal
42862306a36Sopenharmony_ci		 * non-split case.
42962306a36Sopenharmony_ci		 */
43062306a36Sopenharmony_ci		skb_put(skb, re.s.len);
43162306a36Sopenharmony_ci		do {
43262306a36Sopenharmony_ci			re2.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb2);
43362306a36Sopenharmony_ci			if (re2.s.code != RING_ENTRY_CODE_MORE
43462306a36Sopenharmony_ci				&& re2.s.code != RING_ENTRY_CODE_DONE)
43562306a36Sopenharmony_ci				goto split_error;
43662306a36Sopenharmony_ci			skb_put(skb2,  re2.s.len);
43762306a36Sopenharmony_ci			skb_new = skb_copy_expand(skb, 0, skb2->len,
43862306a36Sopenharmony_ci						  GFP_ATOMIC);
43962306a36Sopenharmony_ci			if (!skb_new)
44062306a36Sopenharmony_ci				goto split_error;
44162306a36Sopenharmony_ci			if (skb_copy_bits(skb2, 0, skb_tail_pointer(skb_new),
44262306a36Sopenharmony_ci					  skb2->len))
44362306a36Sopenharmony_ci				goto split_error;
44462306a36Sopenharmony_ci			skb_put(skb_new, skb2->len);
44562306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
44662306a36Sopenharmony_ci			dev_kfree_skb_any(skb2);
44762306a36Sopenharmony_ci			skb = skb_new;
44862306a36Sopenharmony_ci		} while (re2.s.code == RING_ENTRY_CODE_MORE);
44962306a36Sopenharmony_ci		goto good;
45062306a36Sopenharmony_ci	} else {
45162306a36Sopenharmony_ci		/* Some other error, discard it. */
45262306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
45362306a36Sopenharmony_ci		/* Error statistics are accumulated in
45462306a36Sopenharmony_ci		 * octeon_mgmt_update_rx_stats.
45562306a36Sopenharmony_ci		 */
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci	goto done;
45862306a36Sopenharmony_cisplit_error:
45962306a36Sopenharmony_ci	/* Discard the whole mess. */
46062306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
46162306a36Sopenharmony_ci	dev_kfree_skb_any(skb2);
46262306a36Sopenharmony_ci	while (re2.s.code == RING_ENTRY_CODE_MORE) {
46362306a36Sopenharmony_ci		re2.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb2);
46462306a36Sopenharmony_ci		dev_kfree_skb_any(skb2);
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci	netdev->stats.rx_errors++;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cidone:
46962306a36Sopenharmony_ci	/* Tell the hardware we processed a packet.  */
47062306a36Sopenharmony_ci	mix_ircnt.u64 = 0;
47162306a36Sopenharmony_ci	mix_ircnt.s.ircnt = 1;
47262306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_IRCNT, mix_ircnt.u64);
47362306a36Sopenharmony_ci	return rc;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int octeon_mgmt_receive_packets(struct octeon_mgmt *p, int budget)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	unsigned int work_done = 0;
47962306a36Sopenharmony_ci	union cvmx_mixx_ircnt mix_ircnt;
48062306a36Sopenharmony_ci	int rc;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	mix_ircnt.u64 = cvmx_read_csr(p->mix + MIX_IRCNT);
48362306a36Sopenharmony_ci	while (work_done < budget && mix_ircnt.s.ircnt) {
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		rc = octeon_mgmt_receive_one(p);
48662306a36Sopenharmony_ci		if (!rc)
48762306a36Sopenharmony_ci			work_done++;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		/* Check for more packets. */
49062306a36Sopenharmony_ci		mix_ircnt.u64 = cvmx_read_csr(p->mix + MIX_IRCNT);
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	octeon_mgmt_rx_fill_ring(p->netdev);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	return work_done;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic int octeon_mgmt_napi_poll(struct napi_struct *napi, int budget)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct octeon_mgmt *p = container_of(napi, struct octeon_mgmt, napi);
50162306a36Sopenharmony_ci	struct net_device *netdev = p->netdev;
50262306a36Sopenharmony_ci	unsigned int work_done = 0;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	work_done = octeon_mgmt_receive_packets(p, budget);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (work_done < budget) {
50762306a36Sopenharmony_ci		/* We stopped because no more packets were available. */
50862306a36Sopenharmony_ci		napi_complete_done(napi, work_done);
50962306a36Sopenharmony_ci		octeon_mgmt_enable_rx_irq(p);
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci	octeon_mgmt_update_rx_stats(netdev);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	return work_done;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci/* Reset the hardware to clean state.  */
51762306a36Sopenharmony_cistatic void octeon_mgmt_reset_hw(struct octeon_mgmt *p)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	union cvmx_mixx_ctl mix_ctl;
52062306a36Sopenharmony_ci	union cvmx_mixx_bist mix_bist;
52162306a36Sopenharmony_ci	union cvmx_agl_gmx_bist agl_gmx_bist;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	mix_ctl.u64 = 0;
52462306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
52562306a36Sopenharmony_ci	do {
52662306a36Sopenharmony_ci		mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL);
52762306a36Sopenharmony_ci	} while (mix_ctl.s.busy);
52862306a36Sopenharmony_ci	mix_ctl.s.reset = 1;
52962306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
53062306a36Sopenharmony_ci	cvmx_read_csr(p->mix + MIX_CTL);
53162306a36Sopenharmony_ci	octeon_io_clk_delay(64);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	mix_bist.u64 = cvmx_read_csr(p->mix + MIX_BIST);
53462306a36Sopenharmony_ci	if (mix_bist.u64)
53562306a36Sopenharmony_ci		dev_warn(p->dev, "MIX failed BIST (0x%016llx)\n",
53662306a36Sopenharmony_ci			(unsigned long long)mix_bist.u64);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	agl_gmx_bist.u64 = cvmx_read_csr(CVMX_AGL_GMX_BIST);
53962306a36Sopenharmony_ci	if (agl_gmx_bist.u64)
54062306a36Sopenharmony_ci		dev_warn(p->dev, "AGL failed BIST (0x%016llx)\n",
54162306a36Sopenharmony_ci			 (unsigned long long)agl_gmx_bist.u64);
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistruct octeon_mgmt_cam_state {
54562306a36Sopenharmony_ci	u64 cam[6];
54662306a36Sopenharmony_ci	u64 cam_mask;
54762306a36Sopenharmony_ci	int cam_index;
54862306a36Sopenharmony_ci};
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic void octeon_mgmt_cam_state_add(struct octeon_mgmt_cam_state *cs,
55162306a36Sopenharmony_ci				      const unsigned char *addr)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	int i;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	for (i = 0; i < 6; i++)
55662306a36Sopenharmony_ci		cs->cam[i] |= (u64)addr[i] << (8 * (cs->cam_index));
55762306a36Sopenharmony_ci	cs->cam_mask |= (1ULL << cs->cam_index);
55862306a36Sopenharmony_ci	cs->cam_index++;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic void octeon_mgmt_set_rx_filtering(struct net_device *netdev)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
56462306a36Sopenharmony_ci	union cvmx_agl_gmx_rxx_adr_ctl adr_ctl;
56562306a36Sopenharmony_ci	union cvmx_agl_gmx_prtx_cfg agl_gmx_prtx;
56662306a36Sopenharmony_ci	unsigned long flags;
56762306a36Sopenharmony_ci	unsigned int prev_packet_enable;
56862306a36Sopenharmony_ci	unsigned int cam_mode = 1; /* 1 - Accept on CAM match */
56962306a36Sopenharmony_ci	unsigned int multicast_mode = 1; /* 1 - Reject all multicast.  */
57062306a36Sopenharmony_ci	struct octeon_mgmt_cam_state cam_state;
57162306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
57262306a36Sopenharmony_ci	int available_cam_entries;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	memset(&cam_state, 0, sizeof(cam_state));
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	if ((netdev->flags & IFF_PROMISC) || netdev->uc.count > 7) {
57762306a36Sopenharmony_ci		cam_mode = 0;
57862306a36Sopenharmony_ci		available_cam_entries = 8;
57962306a36Sopenharmony_ci	} else {
58062306a36Sopenharmony_ci		/* One CAM entry for the primary address, leaves seven
58162306a36Sopenharmony_ci		 * for the secondary addresses.
58262306a36Sopenharmony_ci		 */
58362306a36Sopenharmony_ci		available_cam_entries = 7 - netdev->uc.count;
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	if (netdev->flags & IFF_MULTICAST) {
58762306a36Sopenharmony_ci		if (cam_mode == 0 || (netdev->flags & IFF_ALLMULTI) ||
58862306a36Sopenharmony_ci		    netdev_mc_count(netdev) > available_cam_entries)
58962306a36Sopenharmony_ci			multicast_mode = 2; /* 2 - Accept all multicast.  */
59062306a36Sopenharmony_ci		else
59162306a36Sopenharmony_ci			multicast_mode = 0; /* 0 - Use CAM.  */
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (cam_mode == 1) {
59562306a36Sopenharmony_ci		/* Add primary address. */
59662306a36Sopenharmony_ci		octeon_mgmt_cam_state_add(&cam_state, netdev->dev_addr);
59762306a36Sopenharmony_ci		netdev_for_each_uc_addr(ha, netdev)
59862306a36Sopenharmony_ci			octeon_mgmt_cam_state_add(&cam_state, ha->addr);
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci	if (multicast_mode == 0) {
60162306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, netdev)
60262306a36Sopenharmony_ci			octeon_mgmt_cam_state_add(&cam_state, ha->addr);
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	spin_lock_irqsave(&p->lock, flags);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	/* Disable packet I/O. */
60862306a36Sopenharmony_ci	agl_gmx_prtx.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
60962306a36Sopenharmony_ci	prev_packet_enable = agl_gmx_prtx.s.en;
61062306a36Sopenharmony_ci	agl_gmx_prtx.s.en = 0;
61162306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, agl_gmx_prtx.u64);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	adr_ctl.u64 = 0;
61462306a36Sopenharmony_ci	adr_ctl.s.cam_mode = cam_mode;
61562306a36Sopenharmony_ci	adr_ctl.s.mcst = multicast_mode;
61662306a36Sopenharmony_ci	adr_ctl.s.bcst = 1;     /* Allow broadcast */
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CTL, adr_ctl.u64);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM0, cam_state.cam[0]);
62162306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM1, cam_state.cam[1]);
62262306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM2, cam_state.cam[2]);
62362306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM3, cam_state.cam[3]);
62462306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM4, cam_state.cam[4]);
62562306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM5, cam_state.cam[5]);
62662306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM_EN, cam_state.cam_mask);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	/* Restore packet I/O. */
62962306a36Sopenharmony_ci	agl_gmx_prtx.s.en = prev_packet_enable;
63062306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, agl_gmx_prtx.u64);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	spin_unlock_irqrestore(&p->lock, flags);
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic int octeon_mgmt_set_mac_address(struct net_device *netdev, void *addr)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	int r = eth_mac_addr(netdev, addr);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	if (r)
64062306a36Sopenharmony_ci		return r;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	octeon_mgmt_set_rx_filtering(netdev);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	return 0;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic int octeon_mgmt_change_mtu(struct net_device *netdev, int new_mtu)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
65062306a36Sopenharmony_ci	int max_packet = new_mtu + ETH_HLEN + ETH_FCS_LEN;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	netdev->mtu = new_mtu;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	/* HW lifts the limit if the frame is VLAN tagged
65562306a36Sopenharmony_ci	 * (+4 bytes per each tag, up to two tags)
65662306a36Sopenharmony_ci	 */
65762306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_MAX, max_packet);
65862306a36Sopenharmony_ci	/* Set the hardware to truncate packets larger than the MTU. The jabber
65962306a36Sopenharmony_ci	 * register must be set to a multiple of 8 bytes, so round up. JABBER is
66062306a36Sopenharmony_ci	 * an unconditional limit, so we need to account for two possible VLAN
66162306a36Sopenharmony_ci	 * tags.
66262306a36Sopenharmony_ci	 */
66362306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_JABBER,
66462306a36Sopenharmony_ci		       (max_packet + 7 + VLAN_HLEN * 2) & 0xfff8);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	return 0;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic irqreturn_t octeon_mgmt_interrupt(int cpl, void *dev_id)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct net_device *netdev = dev_id;
67262306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
67362306a36Sopenharmony_ci	union cvmx_mixx_isr mixx_isr;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	mixx_isr.u64 = cvmx_read_csr(p->mix + MIX_ISR);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* Clear any pending interrupts */
67862306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_ISR, mixx_isr.u64);
67962306a36Sopenharmony_ci	cvmx_read_csr(p->mix + MIX_ISR);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (mixx_isr.s.irthresh) {
68262306a36Sopenharmony_ci		octeon_mgmt_disable_rx_irq(p);
68362306a36Sopenharmony_ci		napi_schedule(&p->napi);
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci	if (mixx_isr.s.orthresh) {
68662306a36Sopenharmony_ci		octeon_mgmt_disable_tx_irq(p);
68762306a36Sopenharmony_ci		tasklet_schedule(&p->tx_clean_tasklet);
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	return IRQ_HANDLED;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic int octeon_mgmt_ioctl_hwtstamp(struct net_device *netdev,
69462306a36Sopenharmony_ci				      struct ifreq *rq, int cmd)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
69762306a36Sopenharmony_ci	struct hwtstamp_config config;
69862306a36Sopenharmony_ci	union cvmx_mio_ptp_clock_cfg ptp;
69962306a36Sopenharmony_ci	union cvmx_agl_gmx_rxx_frm_ctl rxx_frm_ctl;
70062306a36Sopenharmony_ci	bool have_hw_timestamps = false;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	if (copy_from_user(&config, rq->ifr_data, sizeof(config)))
70362306a36Sopenharmony_ci		return -EFAULT;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	/* Check the status of hardware for tiemstamps */
70662306a36Sopenharmony_ci	if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
70762306a36Sopenharmony_ci		/* Get the current state of the PTP clock */
70862306a36Sopenharmony_ci		ptp.u64 = cvmx_read_csr(CVMX_MIO_PTP_CLOCK_CFG);
70962306a36Sopenharmony_ci		if (!ptp.s.ext_clk_en) {
71062306a36Sopenharmony_ci			/* The clock has not been configured to use an
71162306a36Sopenharmony_ci			 * external source.  Program it to use the main clock
71262306a36Sopenharmony_ci			 * reference.
71362306a36Sopenharmony_ci			 */
71462306a36Sopenharmony_ci			u64 clock_comp = (NSEC_PER_SEC << 32) /	octeon_get_io_clock_rate();
71562306a36Sopenharmony_ci			if (!ptp.s.ptp_en)
71662306a36Sopenharmony_ci				cvmx_write_csr(CVMX_MIO_PTP_CLOCK_COMP, clock_comp);
71762306a36Sopenharmony_ci			netdev_info(netdev,
71862306a36Sopenharmony_ci				    "PTP Clock using sclk reference @ %lldHz\n",
71962306a36Sopenharmony_ci				    (NSEC_PER_SEC << 32) / clock_comp);
72062306a36Sopenharmony_ci		} else {
72162306a36Sopenharmony_ci			/* The clock is already programmed to use a GPIO */
72262306a36Sopenharmony_ci			u64 clock_comp = cvmx_read_csr(CVMX_MIO_PTP_CLOCK_COMP);
72362306a36Sopenharmony_ci			netdev_info(netdev,
72462306a36Sopenharmony_ci				    "PTP Clock using GPIO%d @ %lld Hz\n",
72562306a36Sopenharmony_ci				    ptp.s.ext_clk_in, (NSEC_PER_SEC << 32) / clock_comp);
72662306a36Sopenharmony_ci		}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		/* Enable the clock if it wasn't done already */
72962306a36Sopenharmony_ci		if (!ptp.s.ptp_en) {
73062306a36Sopenharmony_ci			ptp.s.ptp_en = 1;
73162306a36Sopenharmony_ci			cvmx_write_csr(CVMX_MIO_PTP_CLOCK_CFG, ptp.u64);
73262306a36Sopenharmony_ci		}
73362306a36Sopenharmony_ci		have_hw_timestamps = true;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (!have_hw_timestamps)
73762306a36Sopenharmony_ci		return -EINVAL;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	switch (config.tx_type) {
74062306a36Sopenharmony_ci	case HWTSTAMP_TX_OFF:
74162306a36Sopenharmony_ci	case HWTSTAMP_TX_ON:
74262306a36Sopenharmony_ci		break;
74362306a36Sopenharmony_ci	default:
74462306a36Sopenharmony_ci		return -ERANGE;
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	switch (config.rx_filter) {
74862306a36Sopenharmony_ci	case HWTSTAMP_FILTER_NONE:
74962306a36Sopenharmony_ci		p->has_rx_tstamp = false;
75062306a36Sopenharmony_ci		rxx_frm_ctl.u64 = cvmx_read_csr(p->agl + AGL_GMX_RX_FRM_CTL);
75162306a36Sopenharmony_ci		rxx_frm_ctl.s.ptp_mode = 0;
75262306a36Sopenharmony_ci		cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64);
75362306a36Sopenharmony_ci		break;
75462306a36Sopenharmony_ci	case HWTSTAMP_FILTER_ALL:
75562306a36Sopenharmony_ci	case HWTSTAMP_FILTER_SOME:
75662306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
75762306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
75862306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
75962306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
76062306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
76162306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
76262306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
76362306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
76462306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
76562306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_EVENT:
76662306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_SYNC:
76762306a36Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
76862306a36Sopenharmony_ci	case HWTSTAMP_FILTER_NTP_ALL:
76962306a36Sopenharmony_ci		p->has_rx_tstamp = have_hw_timestamps;
77062306a36Sopenharmony_ci		config.rx_filter = HWTSTAMP_FILTER_ALL;
77162306a36Sopenharmony_ci		if (p->has_rx_tstamp) {
77262306a36Sopenharmony_ci			rxx_frm_ctl.u64 = cvmx_read_csr(p->agl + AGL_GMX_RX_FRM_CTL);
77362306a36Sopenharmony_ci			rxx_frm_ctl.s.ptp_mode = 1;
77462306a36Sopenharmony_ci			cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64);
77562306a36Sopenharmony_ci		}
77662306a36Sopenharmony_ci		break;
77762306a36Sopenharmony_ci	default:
77862306a36Sopenharmony_ci		return -ERANGE;
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	if (copy_to_user(rq->ifr_data, &config, sizeof(config)))
78262306a36Sopenharmony_ci		return -EFAULT;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	return 0;
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_cistatic int octeon_mgmt_ioctl(struct net_device *netdev,
78862306a36Sopenharmony_ci			     struct ifreq *rq, int cmd)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	switch (cmd) {
79162306a36Sopenharmony_ci	case SIOCSHWTSTAMP:
79262306a36Sopenharmony_ci		return octeon_mgmt_ioctl_hwtstamp(netdev, rq, cmd);
79362306a36Sopenharmony_ci	default:
79462306a36Sopenharmony_ci		return phy_do_ioctl(netdev, rq, cmd);
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic void octeon_mgmt_disable_link(struct octeon_mgmt *p)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	union cvmx_agl_gmx_prtx_cfg prtx_cfg;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	/* Disable GMX before we make any changes. */
80362306a36Sopenharmony_ci	prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
80462306a36Sopenharmony_ci	prtx_cfg.s.en = 0;
80562306a36Sopenharmony_ci	prtx_cfg.s.tx_en = 0;
80662306a36Sopenharmony_ci	prtx_cfg.s.rx_en = 0;
80762306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
81062306a36Sopenharmony_ci		int i;
81162306a36Sopenharmony_ci		for (i = 0; i < 10; i++) {
81262306a36Sopenharmony_ci			prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
81362306a36Sopenharmony_ci			if (prtx_cfg.s.tx_idle == 1 || prtx_cfg.s.rx_idle == 1)
81462306a36Sopenharmony_ci				break;
81562306a36Sopenharmony_ci			mdelay(1);
81662306a36Sopenharmony_ci			i++;
81762306a36Sopenharmony_ci		}
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic void octeon_mgmt_enable_link(struct octeon_mgmt *p)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	union cvmx_agl_gmx_prtx_cfg prtx_cfg;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	/* Restore the GMX enable state only if link is set */
82662306a36Sopenharmony_ci	prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
82762306a36Sopenharmony_ci	prtx_cfg.s.tx_en = 1;
82862306a36Sopenharmony_ci	prtx_cfg.s.rx_en = 1;
82962306a36Sopenharmony_ci	prtx_cfg.s.en = 1;
83062306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64);
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_cistatic void octeon_mgmt_update_link(struct octeon_mgmt *p)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	struct net_device *ndev = p->netdev;
83662306a36Sopenharmony_ci	struct phy_device *phydev = ndev->phydev;
83762306a36Sopenharmony_ci	union cvmx_agl_gmx_prtx_cfg prtx_cfg;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (!phydev->link)
84262306a36Sopenharmony_ci		prtx_cfg.s.duplex = 1;
84362306a36Sopenharmony_ci	else
84462306a36Sopenharmony_ci		prtx_cfg.s.duplex = phydev->duplex;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	switch (phydev->speed) {
84762306a36Sopenharmony_ci	case 10:
84862306a36Sopenharmony_ci		prtx_cfg.s.speed = 0;
84962306a36Sopenharmony_ci		prtx_cfg.s.slottime = 0;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci		if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
85262306a36Sopenharmony_ci			prtx_cfg.s.burst = 1;
85362306a36Sopenharmony_ci			prtx_cfg.s.speed_msb = 1;
85462306a36Sopenharmony_ci		}
85562306a36Sopenharmony_ci		break;
85662306a36Sopenharmony_ci	case 100:
85762306a36Sopenharmony_ci		prtx_cfg.s.speed = 0;
85862306a36Sopenharmony_ci		prtx_cfg.s.slottime = 0;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci		if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
86162306a36Sopenharmony_ci			prtx_cfg.s.burst = 1;
86262306a36Sopenharmony_ci			prtx_cfg.s.speed_msb = 0;
86362306a36Sopenharmony_ci		}
86462306a36Sopenharmony_ci		break;
86562306a36Sopenharmony_ci	case 1000:
86662306a36Sopenharmony_ci		/* 1000 MBits is only supported on 6XXX chips */
86762306a36Sopenharmony_ci		if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
86862306a36Sopenharmony_ci			prtx_cfg.s.speed = 1;
86962306a36Sopenharmony_ci			prtx_cfg.s.speed_msb = 0;
87062306a36Sopenharmony_ci			/* Only matters for half-duplex */
87162306a36Sopenharmony_ci			prtx_cfg.s.slottime = 1;
87262306a36Sopenharmony_ci			prtx_cfg.s.burst = phydev->duplex;
87362306a36Sopenharmony_ci		}
87462306a36Sopenharmony_ci		break;
87562306a36Sopenharmony_ci	case 0:  /* No link */
87662306a36Sopenharmony_ci	default:
87762306a36Sopenharmony_ci		break;
87862306a36Sopenharmony_ci	}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	/* Write the new GMX setting with the port still disabled. */
88162306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	/* Read GMX CFG again to make sure the config is completed. */
88462306a36Sopenharmony_ci	prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
88762306a36Sopenharmony_ci		union cvmx_agl_gmx_txx_clk agl_clk;
88862306a36Sopenharmony_ci		union cvmx_agl_prtx_ctl prtx_ctl;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci		prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl);
89162306a36Sopenharmony_ci		agl_clk.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_CLK);
89262306a36Sopenharmony_ci		/* MII (both speeds) and RGMII 1000 speed. */
89362306a36Sopenharmony_ci		agl_clk.s.clk_cnt = 1;
89462306a36Sopenharmony_ci		if (prtx_ctl.s.mode == 0) { /* RGMII mode */
89562306a36Sopenharmony_ci			if (phydev->speed == 10)
89662306a36Sopenharmony_ci				agl_clk.s.clk_cnt = 50;
89762306a36Sopenharmony_ci			else if (phydev->speed == 100)
89862306a36Sopenharmony_ci				agl_clk.s.clk_cnt = 5;
89962306a36Sopenharmony_ci		}
90062306a36Sopenharmony_ci		cvmx_write_csr(p->agl + AGL_GMX_TX_CLK, agl_clk.u64);
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_cistatic void octeon_mgmt_adjust_link(struct net_device *netdev)
90562306a36Sopenharmony_ci{
90662306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
90762306a36Sopenharmony_ci	struct phy_device *phydev = netdev->phydev;
90862306a36Sopenharmony_ci	unsigned long flags;
90962306a36Sopenharmony_ci	int link_changed = 0;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	if (!phydev)
91262306a36Sopenharmony_ci		return;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	spin_lock_irqsave(&p->lock, flags);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	if (!phydev->link && p->last_link)
91862306a36Sopenharmony_ci		link_changed = -1;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (phydev->link &&
92162306a36Sopenharmony_ci	    (p->last_duplex != phydev->duplex ||
92262306a36Sopenharmony_ci	     p->last_link != phydev->link ||
92362306a36Sopenharmony_ci	     p->last_speed != phydev->speed)) {
92462306a36Sopenharmony_ci		octeon_mgmt_disable_link(p);
92562306a36Sopenharmony_ci		link_changed = 1;
92662306a36Sopenharmony_ci		octeon_mgmt_update_link(p);
92762306a36Sopenharmony_ci		octeon_mgmt_enable_link(p);
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	p->last_link = phydev->link;
93162306a36Sopenharmony_ci	p->last_speed = phydev->speed;
93262306a36Sopenharmony_ci	p->last_duplex = phydev->duplex;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	spin_unlock_irqrestore(&p->lock, flags);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	if (link_changed != 0) {
93762306a36Sopenharmony_ci		if (link_changed > 0)
93862306a36Sopenharmony_ci			netdev_info(netdev, "Link is up - %d/%s\n",
93962306a36Sopenharmony_ci				    phydev->speed, phydev->duplex == DUPLEX_FULL ? "Full" : "Half");
94062306a36Sopenharmony_ci		else
94162306a36Sopenharmony_ci			netdev_info(netdev, "Link is down\n");
94262306a36Sopenharmony_ci	}
94362306a36Sopenharmony_ci}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_cistatic int octeon_mgmt_init_phy(struct net_device *netdev)
94662306a36Sopenharmony_ci{
94762306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
94862306a36Sopenharmony_ci	struct phy_device *phydev = NULL;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if (octeon_is_simulation() || p->phy_np == NULL) {
95162306a36Sopenharmony_ci		/* No PHYs in the simulator. */
95262306a36Sopenharmony_ci		netif_carrier_on(netdev);
95362306a36Sopenharmony_ci		return 0;
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	phydev = of_phy_connect(netdev, p->phy_np,
95762306a36Sopenharmony_ci				octeon_mgmt_adjust_link, 0,
95862306a36Sopenharmony_ci				PHY_INTERFACE_MODE_MII);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	if (!phydev)
96162306a36Sopenharmony_ci		return -EPROBE_DEFER;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	return 0;
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_cistatic int octeon_mgmt_open(struct net_device *netdev)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
96962306a36Sopenharmony_ci	union cvmx_mixx_ctl mix_ctl;
97062306a36Sopenharmony_ci	union cvmx_agl_gmx_inf_mode agl_gmx_inf_mode;
97162306a36Sopenharmony_ci	union cvmx_mixx_oring1 oring1;
97262306a36Sopenharmony_ci	union cvmx_mixx_iring1 iring1;
97362306a36Sopenharmony_ci	union cvmx_agl_gmx_rxx_frm_ctl rxx_frm_ctl;
97462306a36Sopenharmony_ci	union cvmx_mixx_irhwm mix_irhwm;
97562306a36Sopenharmony_ci	union cvmx_mixx_orhwm mix_orhwm;
97662306a36Sopenharmony_ci	union cvmx_mixx_intena mix_intena;
97762306a36Sopenharmony_ci	struct sockaddr sa;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	/* Allocate ring buffers.  */
98062306a36Sopenharmony_ci	p->tx_ring = kzalloc(ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE),
98162306a36Sopenharmony_ci			     GFP_KERNEL);
98262306a36Sopenharmony_ci	if (!p->tx_ring)
98362306a36Sopenharmony_ci		return -ENOMEM;
98462306a36Sopenharmony_ci	p->tx_ring_handle =
98562306a36Sopenharmony_ci		dma_map_single(p->dev, p->tx_ring,
98662306a36Sopenharmony_ci			       ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE),
98762306a36Sopenharmony_ci			       DMA_BIDIRECTIONAL);
98862306a36Sopenharmony_ci	p->tx_next = 0;
98962306a36Sopenharmony_ci	p->tx_next_clean = 0;
99062306a36Sopenharmony_ci	p->tx_current_fill = 0;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	p->rx_ring = kzalloc(ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE),
99462306a36Sopenharmony_ci			     GFP_KERNEL);
99562306a36Sopenharmony_ci	if (!p->rx_ring)
99662306a36Sopenharmony_ci		goto err_nomem;
99762306a36Sopenharmony_ci	p->rx_ring_handle =
99862306a36Sopenharmony_ci		dma_map_single(p->dev, p->rx_ring,
99962306a36Sopenharmony_ci			       ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE),
100062306a36Sopenharmony_ci			       DMA_BIDIRECTIONAL);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	p->rx_next = 0;
100362306a36Sopenharmony_ci	p->rx_next_fill = 0;
100462306a36Sopenharmony_ci	p->rx_current_fill = 0;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	octeon_mgmt_reset_hw(p);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	/* Bring it out of reset if needed. */
101162306a36Sopenharmony_ci	if (mix_ctl.s.reset) {
101262306a36Sopenharmony_ci		mix_ctl.s.reset = 0;
101362306a36Sopenharmony_ci		cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
101462306a36Sopenharmony_ci		do {
101562306a36Sopenharmony_ci			mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL);
101662306a36Sopenharmony_ci		} while (mix_ctl.s.reset);
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	if (OCTEON_IS_MODEL(OCTEON_CN5XXX)) {
102062306a36Sopenharmony_ci		agl_gmx_inf_mode.u64 = 0;
102162306a36Sopenharmony_ci		agl_gmx_inf_mode.s.en = 1;
102262306a36Sopenharmony_ci		cvmx_write_csr(CVMX_AGL_GMX_INF_MODE, agl_gmx_inf_mode.u64);
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ci	if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X)
102562306a36Sopenharmony_ci		|| OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X)) {
102662306a36Sopenharmony_ci		/* Force compensation values, as they are not
102762306a36Sopenharmony_ci		 * determined properly by HW
102862306a36Sopenharmony_ci		 */
102962306a36Sopenharmony_ci		union cvmx_agl_gmx_drv_ctl drv_ctl;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci		drv_ctl.u64 = cvmx_read_csr(CVMX_AGL_GMX_DRV_CTL);
103262306a36Sopenharmony_ci		if (p->port) {
103362306a36Sopenharmony_ci			drv_ctl.s.byp_en1 = 1;
103462306a36Sopenharmony_ci			drv_ctl.s.nctl1 = 6;
103562306a36Sopenharmony_ci			drv_ctl.s.pctl1 = 6;
103662306a36Sopenharmony_ci		} else {
103762306a36Sopenharmony_ci			drv_ctl.s.byp_en = 1;
103862306a36Sopenharmony_ci			drv_ctl.s.nctl = 6;
103962306a36Sopenharmony_ci			drv_ctl.s.pctl = 6;
104062306a36Sopenharmony_ci		}
104162306a36Sopenharmony_ci		cvmx_write_csr(CVMX_AGL_GMX_DRV_CTL, drv_ctl.u64);
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	oring1.u64 = 0;
104562306a36Sopenharmony_ci	oring1.s.obase = p->tx_ring_handle >> 3;
104662306a36Sopenharmony_ci	oring1.s.osize = OCTEON_MGMT_TX_RING_SIZE;
104762306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_ORING1, oring1.u64);
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	iring1.u64 = 0;
105062306a36Sopenharmony_ci	iring1.s.ibase = p->rx_ring_handle >> 3;
105162306a36Sopenharmony_ci	iring1.s.isize = OCTEON_MGMT_RX_RING_SIZE;
105262306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_IRING1, iring1.u64);
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	memcpy(sa.sa_data, netdev->dev_addr, ETH_ALEN);
105562306a36Sopenharmony_ci	octeon_mgmt_set_mac_address(netdev, &sa);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	octeon_mgmt_change_mtu(netdev, netdev->mtu);
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	/* Enable the port HW. Packets are not allowed until
106062306a36Sopenharmony_ci	 * cvmx_mgmt_port_enable() is called.
106162306a36Sopenharmony_ci	 */
106262306a36Sopenharmony_ci	mix_ctl.u64 = 0;
106362306a36Sopenharmony_ci	mix_ctl.s.crc_strip = 1;    /* Strip the ending CRC */
106462306a36Sopenharmony_ci	mix_ctl.s.en = 1;           /* Enable the port */
106562306a36Sopenharmony_ci	mix_ctl.s.nbtarb = 0;       /* Arbitration mode */
106662306a36Sopenharmony_ci	/* MII CB-request FIFO programmable high watermark */
106762306a36Sopenharmony_ci	mix_ctl.s.mrq_hwm = 1;
106862306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN
106962306a36Sopenharmony_ci	mix_ctl.s.lendian = 1;
107062306a36Sopenharmony_ci#endif
107162306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	/* Read the PHY to find the mode of the interface. */
107462306a36Sopenharmony_ci	if (octeon_mgmt_init_phy(netdev)) {
107562306a36Sopenharmony_ci		dev_err(p->dev, "Cannot initialize PHY on MIX%d.\n", p->port);
107662306a36Sopenharmony_ci		goto err_noirq;
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	/* Set the mode of the interface, RGMII/MII. */
108062306a36Sopenharmony_ci	if (OCTEON_IS_MODEL(OCTEON_CN6XXX) && netdev->phydev) {
108162306a36Sopenharmony_ci		union cvmx_agl_prtx_ctl agl_prtx_ctl;
108262306a36Sopenharmony_ci		int rgmii_mode =
108362306a36Sopenharmony_ci			(linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
108462306a36Sopenharmony_ci					   netdev->phydev->supported) |
108562306a36Sopenharmony_ci			 linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
108662306a36Sopenharmony_ci					   netdev->phydev->supported)) != 0;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci		agl_prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl);
108962306a36Sopenharmony_ci		agl_prtx_ctl.s.mode = rgmii_mode ? 0 : 1;
109062306a36Sopenharmony_ci		cvmx_write_csr(p->agl_prt_ctl,	agl_prtx_ctl.u64);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci		/* MII clocks counts are based on the 125Mhz
109362306a36Sopenharmony_ci		 * reference, which has an 8nS period. So our delays
109462306a36Sopenharmony_ci		 * need to be multiplied by this factor.
109562306a36Sopenharmony_ci		 */
109662306a36Sopenharmony_ci#define NS_PER_PHY_CLK 8
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci		/* Take the DLL and clock tree out of reset */
109962306a36Sopenharmony_ci		agl_prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl);
110062306a36Sopenharmony_ci		agl_prtx_ctl.s.clkrst = 0;
110162306a36Sopenharmony_ci		if (rgmii_mode) {
110262306a36Sopenharmony_ci			agl_prtx_ctl.s.dllrst = 0;
110362306a36Sopenharmony_ci			agl_prtx_ctl.s.clktx_byp = 0;
110462306a36Sopenharmony_ci		}
110562306a36Sopenharmony_ci		cvmx_write_csr(p->agl_prt_ctl,	agl_prtx_ctl.u64);
110662306a36Sopenharmony_ci		cvmx_read_csr(p->agl_prt_ctl); /* Force write out before wait */
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci		/* Wait for the DLL to lock. External 125 MHz
110962306a36Sopenharmony_ci		 * reference clock must be stable at this point.
111062306a36Sopenharmony_ci		 */
111162306a36Sopenharmony_ci		ndelay(256 * NS_PER_PHY_CLK);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci		/* Enable the interface */
111462306a36Sopenharmony_ci		agl_prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl);
111562306a36Sopenharmony_ci		agl_prtx_ctl.s.enable = 1;
111662306a36Sopenharmony_ci		cvmx_write_csr(p->agl_prt_ctl, agl_prtx_ctl.u64);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci		/* Read the value back to force the previous write */
111962306a36Sopenharmony_ci		agl_prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci		/* Enable the compensation controller */
112262306a36Sopenharmony_ci		agl_prtx_ctl.s.comp = 1;
112362306a36Sopenharmony_ci		agl_prtx_ctl.s.drv_byp = 0;
112462306a36Sopenharmony_ci		cvmx_write_csr(p->agl_prt_ctl,	agl_prtx_ctl.u64);
112562306a36Sopenharmony_ci		/* Force write out before wait. */
112662306a36Sopenharmony_ci		cvmx_read_csr(p->agl_prt_ctl);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci		/* For compensation state to lock. */
112962306a36Sopenharmony_ci		ndelay(1040 * NS_PER_PHY_CLK);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci		/* Default Interframe Gaps are too small.  Recommended
113262306a36Sopenharmony_ci		 * workaround is.
113362306a36Sopenharmony_ci		 *
113462306a36Sopenharmony_ci		 * AGL_GMX_TX_IFG[IFG1]=14
113562306a36Sopenharmony_ci		 * AGL_GMX_TX_IFG[IFG2]=10
113662306a36Sopenharmony_ci		 */
113762306a36Sopenharmony_ci		cvmx_write_csr(CVMX_AGL_GMX_TX_IFG, 0xae);
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	octeon_mgmt_rx_fill_ring(netdev);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	/* Clear statistics. */
114362306a36Sopenharmony_ci	/* Clear on read. */
114462306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_CTL, 1);
114562306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_PKTS_DRP, 0);
114662306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_PKTS_BAD, 0);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_TX_STATS_CTL, 1);
114962306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_TX_STAT0, 0);
115062306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_TX_STAT1, 0);
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	/* Clear any pending interrupts */
115362306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_ISR, cvmx_read_csr(p->mix + MIX_ISR));
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	if (request_irq(p->irq, octeon_mgmt_interrupt, 0, netdev->name,
115662306a36Sopenharmony_ci			netdev)) {
115762306a36Sopenharmony_ci		dev_err(p->dev, "request_irq(%d) failed.\n", p->irq);
115862306a36Sopenharmony_ci		goto err_noirq;
115962306a36Sopenharmony_ci	}
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	/* Interrupt every single RX packet */
116262306a36Sopenharmony_ci	mix_irhwm.u64 = 0;
116362306a36Sopenharmony_ci	mix_irhwm.s.irhwm = 0;
116462306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_IRHWM, mix_irhwm.u64);
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	/* Interrupt when we have 1 or more packets to clean.  */
116762306a36Sopenharmony_ci	mix_orhwm.u64 = 0;
116862306a36Sopenharmony_ci	mix_orhwm.s.orhwm = 0;
116962306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_ORHWM, mix_orhwm.u64);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	/* Enable receive and transmit interrupts */
117262306a36Sopenharmony_ci	mix_intena.u64 = 0;
117362306a36Sopenharmony_ci	mix_intena.s.ithena = 1;
117462306a36Sopenharmony_ci	mix_intena.s.othena = 1;
117562306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	/* Enable packet I/O. */
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	rxx_frm_ctl.u64 = 0;
118062306a36Sopenharmony_ci	rxx_frm_ctl.s.ptp_mode = p->has_rx_tstamp ? 1 : 0;
118162306a36Sopenharmony_ci	rxx_frm_ctl.s.pre_align = 1;
118262306a36Sopenharmony_ci	/* When set, disables the length check for non-min sized pkts
118362306a36Sopenharmony_ci	 * with padding in the client data.
118462306a36Sopenharmony_ci	 */
118562306a36Sopenharmony_ci	rxx_frm_ctl.s.pad_len = 1;
118662306a36Sopenharmony_ci	/* When set, disables the length check for VLAN pkts */
118762306a36Sopenharmony_ci	rxx_frm_ctl.s.vlan_len = 1;
118862306a36Sopenharmony_ci	/* When set, PREAMBLE checking is  less strict */
118962306a36Sopenharmony_ci	rxx_frm_ctl.s.pre_free = 1;
119062306a36Sopenharmony_ci	/* Control Pause Frames can match station SMAC */
119162306a36Sopenharmony_ci	rxx_frm_ctl.s.ctl_smac = 0;
119262306a36Sopenharmony_ci	/* Control Pause Frames can match globally assign Multicast address */
119362306a36Sopenharmony_ci	rxx_frm_ctl.s.ctl_mcst = 1;
119462306a36Sopenharmony_ci	/* Forward pause information to TX block */
119562306a36Sopenharmony_ci	rxx_frm_ctl.s.ctl_bck = 1;
119662306a36Sopenharmony_ci	/* Drop Control Pause Frames */
119762306a36Sopenharmony_ci	rxx_frm_ctl.s.ctl_drp = 1;
119862306a36Sopenharmony_ci	/* Strip off the preamble */
119962306a36Sopenharmony_ci	rxx_frm_ctl.s.pre_strp = 1;
120062306a36Sopenharmony_ci	/* This port is configured to send PREAMBLE+SFD to begin every
120162306a36Sopenharmony_ci	 * frame.  GMX checks that the PREAMBLE is sent correctly.
120262306a36Sopenharmony_ci	 */
120362306a36Sopenharmony_ci	rxx_frm_ctl.s.pre_chk = 1;
120462306a36Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64);
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	/* Configure the port duplex, speed and enables */
120762306a36Sopenharmony_ci	octeon_mgmt_disable_link(p);
120862306a36Sopenharmony_ci	if (netdev->phydev)
120962306a36Sopenharmony_ci		octeon_mgmt_update_link(p);
121062306a36Sopenharmony_ci	octeon_mgmt_enable_link(p);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	p->last_link = 0;
121362306a36Sopenharmony_ci	p->last_speed = 0;
121462306a36Sopenharmony_ci	/* PHY is not present in simulator. The carrier is enabled
121562306a36Sopenharmony_ci	 * while initializing the phy for simulator, leave it enabled.
121662306a36Sopenharmony_ci	 */
121762306a36Sopenharmony_ci	if (netdev->phydev) {
121862306a36Sopenharmony_ci		netif_carrier_off(netdev);
121962306a36Sopenharmony_ci		phy_start(netdev->phydev);
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	netif_wake_queue(netdev);
122362306a36Sopenharmony_ci	napi_enable(&p->napi);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	return 0;
122662306a36Sopenharmony_cierr_noirq:
122762306a36Sopenharmony_ci	octeon_mgmt_reset_hw(p);
122862306a36Sopenharmony_ci	dma_unmap_single(p->dev, p->rx_ring_handle,
122962306a36Sopenharmony_ci			 ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE),
123062306a36Sopenharmony_ci			 DMA_BIDIRECTIONAL);
123162306a36Sopenharmony_ci	kfree(p->rx_ring);
123262306a36Sopenharmony_cierr_nomem:
123362306a36Sopenharmony_ci	dma_unmap_single(p->dev, p->tx_ring_handle,
123462306a36Sopenharmony_ci			 ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE),
123562306a36Sopenharmony_ci			 DMA_BIDIRECTIONAL);
123662306a36Sopenharmony_ci	kfree(p->tx_ring);
123762306a36Sopenharmony_ci	return -ENOMEM;
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic int octeon_mgmt_stop(struct net_device *netdev)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	napi_disable(&p->napi);
124562306a36Sopenharmony_ci	netif_stop_queue(netdev);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	if (netdev->phydev) {
124862306a36Sopenharmony_ci		phy_stop(netdev->phydev);
124962306a36Sopenharmony_ci		phy_disconnect(netdev->phydev);
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	netif_carrier_off(netdev);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	octeon_mgmt_reset_hw(p);
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	free_irq(p->irq, netdev);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	/* dma_unmap is a nop on Octeon, so just free everything.  */
125962306a36Sopenharmony_ci	skb_queue_purge(&p->tx_list);
126062306a36Sopenharmony_ci	skb_queue_purge(&p->rx_list);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	dma_unmap_single(p->dev, p->rx_ring_handle,
126362306a36Sopenharmony_ci			 ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE),
126462306a36Sopenharmony_ci			 DMA_BIDIRECTIONAL);
126562306a36Sopenharmony_ci	kfree(p->rx_ring);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	dma_unmap_single(p->dev, p->tx_ring_handle,
126862306a36Sopenharmony_ci			 ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE),
126962306a36Sopenharmony_ci			 DMA_BIDIRECTIONAL);
127062306a36Sopenharmony_ci	kfree(p->tx_ring);
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	return 0;
127362306a36Sopenharmony_ci}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_cistatic netdev_tx_t
127662306a36Sopenharmony_ciocteon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
127962306a36Sopenharmony_ci	union mgmt_port_ring_entry re;
128062306a36Sopenharmony_ci	unsigned long flags;
128162306a36Sopenharmony_ci	netdev_tx_t rv = NETDEV_TX_BUSY;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	re.d64 = 0;
128462306a36Sopenharmony_ci	re.s.tstamp = ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) != 0);
128562306a36Sopenharmony_ci	re.s.len = skb->len;
128662306a36Sopenharmony_ci	re.s.addr = dma_map_single(p->dev, skb->data,
128762306a36Sopenharmony_ci				   skb->len,
128862306a36Sopenharmony_ci				   DMA_TO_DEVICE);
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	spin_lock_irqsave(&p->tx_list.lock, flags);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	if (unlikely(p->tx_current_fill >= ring_max_fill(OCTEON_MGMT_TX_RING_SIZE) - 1)) {
129362306a36Sopenharmony_ci		spin_unlock_irqrestore(&p->tx_list.lock, flags);
129462306a36Sopenharmony_ci		netif_stop_queue(netdev);
129562306a36Sopenharmony_ci		spin_lock_irqsave(&p->tx_list.lock, flags);
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	if (unlikely(p->tx_current_fill >=
129962306a36Sopenharmony_ci		     ring_max_fill(OCTEON_MGMT_TX_RING_SIZE))) {
130062306a36Sopenharmony_ci		spin_unlock_irqrestore(&p->tx_list.lock, flags);
130162306a36Sopenharmony_ci		dma_unmap_single(p->dev, re.s.addr, re.s.len,
130262306a36Sopenharmony_ci				 DMA_TO_DEVICE);
130362306a36Sopenharmony_ci		goto out;
130462306a36Sopenharmony_ci	}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	__skb_queue_tail(&p->tx_list, skb);
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	/* Put it in the ring.  */
130962306a36Sopenharmony_ci	p->tx_ring[p->tx_next] = re.d64;
131062306a36Sopenharmony_ci	p->tx_next = (p->tx_next + 1) % OCTEON_MGMT_TX_RING_SIZE;
131162306a36Sopenharmony_ci	p->tx_current_fill++;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	spin_unlock_irqrestore(&p->tx_list.lock, flags);
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	dma_sync_single_for_device(p->dev, p->tx_ring_handle,
131662306a36Sopenharmony_ci				   ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE),
131762306a36Sopenharmony_ci				   DMA_BIDIRECTIONAL);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	netdev->stats.tx_packets++;
132062306a36Sopenharmony_ci	netdev->stats.tx_bytes += skb->len;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	/* Ring the bell.  */
132362306a36Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_ORING2, 1);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	netif_trans_update(netdev);
132662306a36Sopenharmony_ci	rv = NETDEV_TX_OK;
132762306a36Sopenharmony_ciout:
132862306a36Sopenharmony_ci	octeon_mgmt_update_tx_stats(netdev);
132962306a36Sopenharmony_ci	return rv;
133062306a36Sopenharmony_ci}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
133362306a36Sopenharmony_cistatic void octeon_mgmt_poll_controller(struct net_device *netdev)
133462306a36Sopenharmony_ci{
133562306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	octeon_mgmt_receive_packets(p, 16);
133862306a36Sopenharmony_ci	octeon_mgmt_update_rx_stats(netdev);
133962306a36Sopenharmony_ci}
134062306a36Sopenharmony_ci#endif
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_cistatic void octeon_mgmt_get_drvinfo(struct net_device *netdev,
134362306a36Sopenharmony_ci				    struct ethtool_drvinfo *info)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_cistatic int octeon_mgmt_nway_reset(struct net_device *dev)
134962306a36Sopenharmony_ci{
135062306a36Sopenharmony_ci	if (!capable(CAP_NET_ADMIN))
135162306a36Sopenharmony_ci		return -EPERM;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	if (dev->phydev)
135462306a36Sopenharmony_ci		return phy_start_aneg(dev->phydev);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	return -EOPNOTSUPP;
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_cistatic const struct ethtool_ops octeon_mgmt_ethtool_ops = {
136062306a36Sopenharmony_ci	.get_drvinfo = octeon_mgmt_get_drvinfo,
136162306a36Sopenharmony_ci	.nway_reset = octeon_mgmt_nway_reset,
136262306a36Sopenharmony_ci	.get_link = ethtool_op_get_link,
136362306a36Sopenharmony_ci	.get_link_ksettings = phy_ethtool_get_link_ksettings,
136462306a36Sopenharmony_ci	.set_link_ksettings = phy_ethtool_set_link_ksettings,
136562306a36Sopenharmony_ci};
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistatic const struct net_device_ops octeon_mgmt_ops = {
136862306a36Sopenharmony_ci	.ndo_open =			octeon_mgmt_open,
136962306a36Sopenharmony_ci	.ndo_stop =			octeon_mgmt_stop,
137062306a36Sopenharmony_ci	.ndo_start_xmit =		octeon_mgmt_xmit,
137162306a36Sopenharmony_ci	.ndo_set_rx_mode =		octeon_mgmt_set_rx_filtering,
137262306a36Sopenharmony_ci	.ndo_set_mac_address =		octeon_mgmt_set_mac_address,
137362306a36Sopenharmony_ci	.ndo_eth_ioctl =			octeon_mgmt_ioctl,
137462306a36Sopenharmony_ci	.ndo_change_mtu =		octeon_mgmt_change_mtu,
137562306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
137662306a36Sopenharmony_ci	.ndo_poll_controller =		octeon_mgmt_poll_controller,
137762306a36Sopenharmony_ci#endif
137862306a36Sopenharmony_ci};
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_cistatic int octeon_mgmt_probe(struct platform_device *pdev)
138162306a36Sopenharmony_ci{
138262306a36Sopenharmony_ci	struct net_device *netdev;
138362306a36Sopenharmony_ci	struct octeon_mgmt *p;
138462306a36Sopenharmony_ci	const __be32 *data;
138562306a36Sopenharmony_ci	struct resource *res_mix;
138662306a36Sopenharmony_ci	struct resource *res_agl;
138762306a36Sopenharmony_ci	struct resource *res_agl_prt_ctl;
138862306a36Sopenharmony_ci	int len;
138962306a36Sopenharmony_ci	int result;
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	netdev = alloc_etherdev(sizeof(struct octeon_mgmt));
139262306a36Sopenharmony_ci	if (netdev == NULL)
139362306a36Sopenharmony_ci		return -ENOMEM;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	SET_NETDEV_DEV(netdev, &pdev->dev);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	platform_set_drvdata(pdev, netdev);
139862306a36Sopenharmony_ci	p = netdev_priv(netdev);
139962306a36Sopenharmony_ci	netif_napi_add_weight(netdev, &p->napi, octeon_mgmt_napi_poll,
140062306a36Sopenharmony_ci			      OCTEON_MGMT_NAPI_WEIGHT);
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	p->netdev = netdev;
140362306a36Sopenharmony_ci	p->dev = &pdev->dev;
140462306a36Sopenharmony_ci	p->has_rx_tstamp = false;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	data = of_get_property(pdev->dev.of_node, "cell-index", &len);
140762306a36Sopenharmony_ci	if (data && len == sizeof(*data)) {
140862306a36Sopenharmony_ci		p->port = be32_to_cpup(data);
140962306a36Sopenharmony_ci	} else {
141062306a36Sopenharmony_ci		dev_err(&pdev->dev, "no 'cell-index' property\n");
141162306a36Sopenharmony_ci		result = -ENXIO;
141262306a36Sopenharmony_ci		goto err;
141362306a36Sopenharmony_ci	}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	snprintf(netdev->name, IFNAMSIZ, "mgmt%d", p->port);
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	result = platform_get_irq(pdev, 0);
141862306a36Sopenharmony_ci	if (result < 0)
141962306a36Sopenharmony_ci		goto err;
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	p->irq = result;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	res_mix = platform_get_resource(pdev, IORESOURCE_MEM, 0);
142462306a36Sopenharmony_ci	if (res_mix == NULL) {
142562306a36Sopenharmony_ci		dev_err(&pdev->dev, "no 'reg' resource\n");
142662306a36Sopenharmony_ci		result = -ENXIO;
142762306a36Sopenharmony_ci		goto err;
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	res_agl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
143162306a36Sopenharmony_ci	if (res_agl == NULL) {
143262306a36Sopenharmony_ci		dev_err(&pdev->dev, "no 'reg' resource\n");
143362306a36Sopenharmony_ci		result = -ENXIO;
143462306a36Sopenharmony_ci		goto err;
143562306a36Sopenharmony_ci	}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	res_agl_prt_ctl = platform_get_resource(pdev, IORESOURCE_MEM, 3);
143862306a36Sopenharmony_ci	if (res_agl_prt_ctl == NULL) {
143962306a36Sopenharmony_ci		dev_err(&pdev->dev, "no 'reg' resource\n");
144062306a36Sopenharmony_ci		result = -ENXIO;
144162306a36Sopenharmony_ci		goto err;
144262306a36Sopenharmony_ci	}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	p->mix_phys = res_mix->start;
144562306a36Sopenharmony_ci	p->mix_size = resource_size(res_mix);
144662306a36Sopenharmony_ci	p->agl_phys = res_agl->start;
144762306a36Sopenharmony_ci	p->agl_size = resource_size(res_agl);
144862306a36Sopenharmony_ci	p->agl_prt_ctl_phys = res_agl_prt_ctl->start;
144962306a36Sopenharmony_ci	p->agl_prt_ctl_size = resource_size(res_agl_prt_ctl);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	if (!devm_request_mem_region(&pdev->dev, p->mix_phys, p->mix_size,
145362306a36Sopenharmony_ci				     res_mix->name)) {
145462306a36Sopenharmony_ci		dev_err(&pdev->dev, "request_mem_region (%s) failed\n",
145562306a36Sopenharmony_ci			res_mix->name);
145662306a36Sopenharmony_ci		result = -ENXIO;
145762306a36Sopenharmony_ci		goto err;
145862306a36Sopenharmony_ci	}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	if (!devm_request_mem_region(&pdev->dev, p->agl_phys, p->agl_size,
146162306a36Sopenharmony_ci				     res_agl->name)) {
146262306a36Sopenharmony_ci		result = -ENXIO;
146362306a36Sopenharmony_ci		dev_err(&pdev->dev, "request_mem_region (%s) failed\n",
146462306a36Sopenharmony_ci			res_agl->name);
146562306a36Sopenharmony_ci		goto err;
146662306a36Sopenharmony_ci	}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	if (!devm_request_mem_region(&pdev->dev, p->agl_prt_ctl_phys,
146962306a36Sopenharmony_ci				     p->agl_prt_ctl_size, res_agl_prt_ctl->name)) {
147062306a36Sopenharmony_ci		result = -ENXIO;
147162306a36Sopenharmony_ci		dev_err(&pdev->dev, "request_mem_region (%s) failed\n",
147262306a36Sopenharmony_ci			res_agl_prt_ctl->name);
147362306a36Sopenharmony_ci		goto err;
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	p->mix = (u64)devm_ioremap(&pdev->dev, p->mix_phys, p->mix_size);
147762306a36Sopenharmony_ci	p->agl = (u64)devm_ioremap(&pdev->dev, p->agl_phys, p->agl_size);
147862306a36Sopenharmony_ci	p->agl_prt_ctl = (u64)devm_ioremap(&pdev->dev, p->agl_prt_ctl_phys,
147962306a36Sopenharmony_ci					   p->agl_prt_ctl_size);
148062306a36Sopenharmony_ci	if (!p->mix || !p->agl || !p->agl_prt_ctl) {
148162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to map I/O memory\n");
148262306a36Sopenharmony_ci		result = -ENOMEM;
148362306a36Sopenharmony_ci		goto err;
148462306a36Sopenharmony_ci	}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	spin_lock_init(&p->lock);
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	skb_queue_head_init(&p->tx_list);
148962306a36Sopenharmony_ci	skb_queue_head_init(&p->rx_list);
149062306a36Sopenharmony_ci	tasklet_setup(&p->tx_clean_tasklet,
149162306a36Sopenharmony_ci		      octeon_mgmt_clean_tx_tasklet);
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	netdev->priv_flags |= IFF_UNICAST_FLT;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	netdev->netdev_ops = &octeon_mgmt_ops;
149662306a36Sopenharmony_ci	netdev->ethtool_ops = &octeon_mgmt_ethtool_ops;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	netdev->min_mtu = 64 - OCTEON_MGMT_RX_HEADROOM;
149962306a36Sopenharmony_ci	netdev->max_mtu = 16383 - OCTEON_MGMT_RX_HEADROOM - VLAN_HLEN;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	result = of_get_ethdev_address(pdev->dev.of_node, netdev);
150262306a36Sopenharmony_ci	if (result)
150362306a36Sopenharmony_ci		eth_hw_addr_random(netdev);
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	p->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	result = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
150862306a36Sopenharmony_ci	if (result)
150962306a36Sopenharmony_ci		goto err;
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	netif_carrier_off(netdev);
151262306a36Sopenharmony_ci	result = register_netdev(netdev);
151362306a36Sopenharmony_ci	if (result)
151462306a36Sopenharmony_ci		goto err;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	return 0;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_cierr:
151962306a36Sopenharmony_ci	of_node_put(p->phy_np);
152062306a36Sopenharmony_ci	free_netdev(netdev);
152162306a36Sopenharmony_ci	return result;
152262306a36Sopenharmony_ci}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_cistatic int octeon_mgmt_remove(struct platform_device *pdev)
152562306a36Sopenharmony_ci{
152662306a36Sopenharmony_ci	struct net_device *netdev = platform_get_drvdata(pdev);
152762306a36Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	unregister_netdev(netdev);
153062306a36Sopenharmony_ci	of_node_put(p->phy_np);
153162306a36Sopenharmony_ci	free_netdev(netdev);
153262306a36Sopenharmony_ci	return 0;
153362306a36Sopenharmony_ci}
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_cistatic const struct of_device_id octeon_mgmt_match[] = {
153662306a36Sopenharmony_ci	{
153762306a36Sopenharmony_ci		.compatible = "cavium,octeon-5750-mix",
153862306a36Sopenharmony_ci	},
153962306a36Sopenharmony_ci	{},
154062306a36Sopenharmony_ci};
154162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, octeon_mgmt_match);
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_cistatic struct platform_driver octeon_mgmt_driver = {
154462306a36Sopenharmony_ci	.driver = {
154562306a36Sopenharmony_ci		.name		= "octeon_mgmt",
154662306a36Sopenharmony_ci		.of_match_table = octeon_mgmt_match,
154762306a36Sopenharmony_ci	},
154862306a36Sopenharmony_ci	.probe		= octeon_mgmt_probe,
154962306a36Sopenharmony_ci	.remove		= octeon_mgmt_remove,
155062306a36Sopenharmony_ci};
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_cimodule_platform_driver(octeon_mgmt_driver);
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ciMODULE_SOFTDEP("pre: mdio-cavium");
155562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION);
155662306a36Sopenharmony_ciMODULE_AUTHOR("David Daney");
155762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1558