18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2009-2012 Cavium, Inc
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
128c2ecf20Sopenharmony_ci#include <linux/capability.h>
138c2ecf20Sopenharmony_ci#include <linux/net_tstamp.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
168c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
178c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
188c2ecf20Sopenharmony_ci#include <linux/of_mdio.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/of_net.h>
218c2ecf20Sopenharmony_ci#include <linux/init.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <linux/phy.h>
248c2ecf20Sopenharmony_ci#include <linux/io.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <asm/octeon/octeon.h>
278c2ecf20Sopenharmony_ci#include <asm/octeon/cvmx-mixx-defs.h>
288c2ecf20Sopenharmony_ci#include <asm/octeon/cvmx-agl-defs.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define DRV_NAME "octeon_mgmt"
318c2ecf20Sopenharmony_ci#define DRV_DESCRIPTION \
328c2ecf20Sopenharmony_ci	"Cavium Networks Octeon MII (management) port Network Driver"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define OCTEON_MGMT_NAPI_WEIGHT 16
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* Ring sizes that are powers of two allow for more efficient modulo
378c2ecf20Sopenharmony_ci * opertions.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_ci#define OCTEON_MGMT_RX_RING_SIZE 512
408c2ecf20Sopenharmony_ci#define OCTEON_MGMT_TX_RING_SIZE 128
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Allow 8 bytes for vlan and FCS. */
438c2ecf20Sopenharmony_ci#define OCTEON_MGMT_RX_HEADROOM (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN)
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciunion mgmt_port_ring_entry {
468c2ecf20Sopenharmony_ci	u64 d64;
478c2ecf20Sopenharmony_ci	struct {
488c2ecf20Sopenharmony_ci#define RING_ENTRY_CODE_DONE 0xf
498c2ecf20Sopenharmony_ci#define RING_ENTRY_CODE_MORE 0x10
508c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD
518c2ecf20Sopenharmony_ci		u64 reserved_62_63:2;
528c2ecf20Sopenharmony_ci		/* Length of the buffer/packet in bytes */
538c2ecf20Sopenharmony_ci		u64 len:14;
548c2ecf20Sopenharmony_ci		/* For TX, signals that the packet should be timestamped */
558c2ecf20Sopenharmony_ci		u64 tstamp:1;
568c2ecf20Sopenharmony_ci		/* The RX error code */
578c2ecf20Sopenharmony_ci		u64 code:7;
588c2ecf20Sopenharmony_ci		/* Physical address of the buffer */
598c2ecf20Sopenharmony_ci		u64 addr:40;
608c2ecf20Sopenharmony_ci#else
618c2ecf20Sopenharmony_ci		u64 addr:40;
628c2ecf20Sopenharmony_ci		u64 code:7;
638c2ecf20Sopenharmony_ci		u64 tstamp:1;
648c2ecf20Sopenharmony_ci		u64 len:14;
658c2ecf20Sopenharmony_ci		u64 reserved_62_63:2;
668c2ecf20Sopenharmony_ci#endif
678c2ecf20Sopenharmony_ci	} s;
688c2ecf20Sopenharmony_ci};
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define MIX_ORING1	0x0
718c2ecf20Sopenharmony_ci#define MIX_ORING2	0x8
728c2ecf20Sopenharmony_ci#define MIX_IRING1	0x10
738c2ecf20Sopenharmony_ci#define MIX_IRING2	0x18
748c2ecf20Sopenharmony_ci#define MIX_CTL		0x20
758c2ecf20Sopenharmony_ci#define MIX_IRHWM	0x28
768c2ecf20Sopenharmony_ci#define MIX_IRCNT	0x30
778c2ecf20Sopenharmony_ci#define MIX_ORHWM	0x38
788c2ecf20Sopenharmony_ci#define MIX_ORCNT	0x40
798c2ecf20Sopenharmony_ci#define MIX_ISR		0x48
808c2ecf20Sopenharmony_ci#define MIX_INTENA	0x50
818c2ecf20Sopenharmony_ci#define MIX_REMCNT	0x58
828c2ecf20Sopenharmony_ci#define MIX_BIST	0x78
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#define AGL_GMX_PRT_CFG			0x10
858c2ecf20Sopenharmony_ci#define AGL_GMX_RX_FRM_CTL		0x18
868c2ecf20Sopenharmony_ci#define AGL_GMX_RX_FRM_MAX		0x30
878c2ecf20Sopenharmony_ci#define AGL_GMX_RX_JABBER		0x38
888c2ecf20Sopenharmony_ci#define AGL_GMX_RX_STATS_CTL		0x50
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#define AGL_GMX_RX_STATS_PKTS_DRP	0xb0
918c2ecf20Sopenharmony_ci#define AGL_GMX_RX_STATS_OCTS_DRP	0xb8
928c2ecf20Sopenharmony_ci#define AGL_GMX_RX_STATS_PKTS_BAD	0xc0
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CTL		0x100
958c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM_EN		0x108
968c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM0		0x180
978c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM1		0x188
988c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM2		0x190
998c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM3		0x198
1008c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM4		0x1a0
1018c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM5		0x1a8
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#define AGL_GMX_TX_CLK			0x208
1048c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STATS_CTL		0x268
1058c2ecf20Sopenharmony_ci#define AGL_GMX_TX_CTL			0x270
1068c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT0		0x280
1078c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT1		0x288
1088c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT2		0x290
1098c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT3		0x298
1108c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT4		0x2a0
1118c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT5		0x2a8
1128c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT6		0x2b0
1138c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT7		0x2b8
1148c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT8		0x2c0
1158c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT9		0x2c8
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistruct octeon_mgmt {
1188c2ecf20Sopenharmony_ci	struct net_device *netdev;
1198c2ecf20Sopenharmony_ci	u64 mix;
1208c2ecf20Sopenharmony_ci	u64 agl;
1218c2ecf20Sopenharmony_ci	u64 agl_prt_ctl;
1228c2ecf20Sopenharmony_ci	int port;
1238c2ecf20Sopenharmony_ci	int irq;
1248c2ecf20Sopenharmony_ci	bool has_rx_tstamp;
1258c2ecf20Sopenharmony_ci	u64 *tx_ring;
1268c2ecf20Sopenharmony_ci	dma_addr_t tx_ring_handle;
1278c2ecf20Sopenharmony_ci	unsigned int tx_next;
1288c2ecf20Sopenharmony_ci	unsigned int tx_next_clean;
1298c2ecf20Sopenharmony_ci	unsigned int tx_current_fill;
1308c2ecf20Sopenharmony_ci	/* The tx_list lock also protects the ring related variables */
1318c2ecf20Sopenharmony_ci	struct sk_buff_head tx_list;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/* RX variables only touched in napi_poll.  No locking necessary. */
1348c2ecf20Sopenharmony_ci	u64 *rx_ring;
1358c2ecf20Sopenharmony_ci	dma_addr_t rx_ring_handle;
1368c2ecf20Sopenharmony_ci	unsigned int rx_next;
1378c2ecf20Sopenharmony_ci	unsigned int rx_next_fill;
1388c2ecf20Sopenharmony_ci	unsigned int rx_current_fill;
1398c2ecf20Sopenharmony_ci	struct sk_buff_head rx_list;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	spinlock_t lock;
1428c2ecf20Sopenharmony_ci	unsigned int last_duplex;
1438c2ecf20Sopenharmony_ci	unsigned int last_link;
1448c2ecf20Sopenharmony_ci	unsigned int last_speed;
1458c2ecf20Sopenharmony_ci	struct device *dev;
1468c2ecf20Sopenharmony_ci	struct napi_struct napi;
1478c2ecf20Sopenharmony_ci	struct tasklet_struct tx_clean_tasklet;
1488c2ecf20Sopenharmony_ci	struct device_node *phy_np;
1498c2ecf20Sopenharmony_ci	resource_size_t mix_phys;
1508c2ecf20Sopenharmony_ci	resource_size_t mix_size;
1518c2ecf20Sopenharmony_ci	resource_size_t agl_phys;
1528c2ecf20Sopenharmony_ci	resource_size_t agl_size;
1538c2ecf20Sopenharmony_ci	resource_size_t agl_prt_ctl_phys;
1548c2ecf20Sopenharmony_ci	resource_size_t agl_prt_ctl_size;
1558c2ecf20Sopenharmony_ci};
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic void octeon_mgmt_set_rx_irq(struct octeon_mgmt *p, int enable)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	union cvmx_mixx_intena mix_intena;
1608c2ecf20Sopenharmony_ci	unsigned long flags;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p->lock, flags);
1638c2ecf20Sopenharmony_ci	mix_intena.u64 = cvmx_read_csr(p->mix + MIX_INTENA);
1648c2ecf20Sopenharmony_ci	mix_intena.s.ithena = enable ? 1 : 0;
1658c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64);
1668c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p->lock, flags);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic void octeon_mgmt_set_tx_irq(struct octeon_mgmt *p, int enable)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	union cvmx_mixx_intena mix_intena;
1728c2ecf20Sopenharmony_ci	unsigned long flags;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p->lock, flags);
1758c2ecf20Sopenharmony_ci	mix_intena.u64 = cvmx_read_csr(p->mix + MIX_INTENA);
1768c2ecf20Sopenharmony_ci	mix_intena.s.othena = enable ? 1 : 0;
1778c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64);
1788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p->lock, flags);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic void octeon_mgmt_enable_rx_irq(struct octeon_mgmt *p)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	octeon_mgmt_set_rx_irq(p, 1);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic void octeon_mgmt_disable_rx_irq(struct octeon_mgmt *p)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	octeon_mgmt_set_rx_irq(p, 0);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic void octeon_mgmt_enable_tx_irq(struct octeon_mgmt *p)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	octeon_mgmt_set_tx_irq(p, 1);
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic void octeon_mgmt_disable_tx_irq(struct octeon_mgmt *p)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	octeon_mgmt_set_tx_irq(p, 0);
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic unsigned int ring_max_fill(unsigned int ring_size)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	return ring_size - 8;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic unsigned int ring_size_to_bytes(unsigned int ring_size)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	return ring_size * sizeof(union mgmt_port_ring_entry);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic void octeon_mgmt_rx_fill_ring(struct net_device *netdev)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	while (p->rx_current_fill < ring_max_fill(OCTEON_MGMT_RX_RING_SIZE)) {
2168c2ecf20Sopenharmony_ci		unsigned int size;
2178c2ecf20Sopenharmony_ci		union mgmt_port_ring_entry re;
2188c2ecf20Sopenharmony_ci		struct sk_buff *skb;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		/* CN56XX pass 1 needs 8 bytes of padding.  */
2218c2ecf20Sopenharmony_ci		size = netdev->mtu + OCTEON_MGMT_RX_HEADROOM + 8 + NET_IP_ALIGN;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci		skb = netdev_alloc_skb(netdev, size);
2248c2ecf20Sopenharmony_ci		if (!skb)
2258c2ecf20Sopenharmony_ci			break;
2268c2ecf20Sopenharmony_ci		skb_reserve(skb, NET_IP_ALIGN);
2278c2ecf20Sopenharmony_ci		__skb_queue_tail(&p->rx_list, skb);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		re.d64 = 0;
2308c2ecf20Sopenharmony_ci		re.s.len = size;
2318c2ecf20Sopenharmony_ci		re.s.addr = dma_map_single(p->dev, skb->data,
2328c2ecf20Sopenharmony_ci					   size,
2338c2ecf20Sopenharmony_ci					   DMA_FROM_DEVICE);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		/* Put it in the ring.  */
2368c2ecf20Sopenharmony_ci		p->rx_ring[p->rx_next_fill] = re.d64;
2378c2ecf20Sopenharmony_ci		/* Make sure there is no reorder of filling the ring and ringing
2388c2ecf20Sopenharmony_ci		 * the bell
2398c2ecf20Sopenharmony_ci		 */
2408c2ecf20Sopenharmony_ci		wmb();
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci		dma_sync_single_for_device(p->dev, p->rx_ring_handle,
2438c2ecf20Sopenharmony_ci					   ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE),
2448c2ecf20Sopenharmony_ci					   DMA_BIDIRECTIONAL);
2458c2ecf20Sopenharmony_ci		p->rx_next_fill =
2468c2ecf20Sopenharmony_ci			(p->rx_next_fill + 1) % OCTEON_MGMT_RX_RING_SIZE;
2478c2ecf20Sopenharmony_ci		p->rx_current_fill++;
2488c2ecf20Sopenharmony_ci		/* Ring the bell.  */
2498c2ecf20Sopenharmony_ci		cvmx_write_csr(p->mix + MIX_IRING2, 1);
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	union cvmx_mixx_orcnt mix_orcnt;
2568c2ecf20Sopenharmony_ci	union mgmt_port_ring_entry re;
2578c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2588c2ecf20Sopenharmony_ci	int cleaned = 0;
2598c2ecf20Sopenharmony_ci	unsigned long flags;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT);
2628c2ecf20Sopenharmony_ci	while (mix_orcnt.s.orcnt) {
2638c2ecf20Sopenharmony_ci		spin_lock_irqsave(&p->tx_list.lock, flags);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci		mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		if (mix_orcnt.s.orcnt == 0) {
2688c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&p->tx_list.lock, flags);
2698c2ecf20Sopenharmony_ci			break;
2708c2ecf20Sopenharmony_ci		}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		dma_sync_single_for_cpu(p->dev, p->tx_ring_handle,
2738c2ecf20Sopenharmony_ci					ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE),
2748c2ecf20Sopenharmony_ci					DMA_BIDIRECTIONAL);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		re.d64 = p->tx_ring[p->tx_next_clean];
2778c2ecf20Sopenharmony_ci		p->tx_next_clean =
2788c2ecf20Sopenharmony_ci			(p->tx_next_clean + 1) % OCTEON_MGMT_TX_RING_SIZE;
2798c2ecf20Sopenharmony_ci		skb = __skb_dequeue(&p->tx_list);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		mix_orcnt.u64 = 0;
2828c2ecf20Sopenharmony_ci		mix_orcnt.s.orcnt = 1;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		/* Acknowledge to hardware that we have the buffer.  */
2858c2ecf20Sopenharmony_ci		cvmx_write_csr(p->mix + MIX_ORCNT, mix_orcnt.u64);
2868c2ecf20Sopenharmony_ci		p->tx_current_fill--;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&p->tx_list.lock, flags);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci		dma_unmap_single(p->dev, re.s.addr, re.s.len,
2918c2ecf20Sopenharmony_ci				 DMA_TO_DEVICE);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		/* Read the hardware TX timestamp if one was recorded */
2948c2ecf20Sopenharmony_ci		if (unlikely(re.s.tstamp)) {
2958c2ecf20Sopenharmony_ci			struct skb_shared_hwtstamps ts;
2968c2ecf20Sopenharmony_ci			u64 ns;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci			memset(&ts, 0, sizeof(ts));
2998c2ecf20Sopenharmony_ci			/* Read the timestamp */
3008c2ecf20Sopenharmony_ci			ns = cvmx_read_csr(CVMX_MIXX_TSTAMP(p->port));
3018c2ecf20Sopenharmony_ci			/* Remove the timestamp from the FIFO */
3028c2ecf20Sopenharmony_ci			cvmx_write_csr(CVMX_MIXX_TSCTL(p->port), 0);
3038c2ecf20Sopenharmony_ci			/* Tell the kernel about the timestamp */
3048c2ecf20Sopenharmony_ci			ts.hwtstamp = ns_to_ktime(ns);
3058c2ecf20Sopenharmony_ci			skb_tstamp_tx(skb, &ts);
3068c2ecf20Sopenharmony_ci		}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
3098c2ecf20Sopenharmony_ci		cleaned++;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT);
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (cleaned && netif_queue_stopped(p->netdev))
3158c2ecf20Sopenharmony_ci		netif_wake_queue(p->netdev);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic void octeon_mgmt_clean_tx_tasklet(struct tasklet_struct *t)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = from_tasklet(p, t, tx_clean_tasklet);
3218c2ecf20Sopenharmony_ci	octeon_mgmt_clean_tx_buffers(p);
3228c2ecf20Sopenharmony_ci	octeon_mgmt_enable_tx_irq(p);
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic void octeon_mgmt_update_rx_stats(struct net_device *netdev)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
3288c2ecf20Sopenharmony_ci	unsigned long flags;
3298c2ecf20Sopenharmony_ci	u64 drop, bad;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/* These reads also clear the count registers.  */
3328c2ecf20Sopenharmony_ci	drop = cvmx_read_csr(p->agl + AGL_GMX_RX_STATS_PKTS_DRP);
3338c2ecf20Sopenharmony_ci	bad = cvmx_read_csr(p->agl + AGL_GMX_RX_STATS_PKTS_BAD);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (drop || bad) {
3368c2ecf20Sopenharmony_ci		/* Do an atomic update. */
3378c2ecf20Sopenharmony_ci		spin_lock_irqsave(&p->lock, flags);
3388c2ecf20Sopenharmony_ci		netdev->stats.rx_errors += bad;
3398c2ecf20Sopenharmony_ci		netdev->stats.rx_dropped += drop;
3408c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&p->lock, flags);
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic void octeon_mgmt_update_tx_stats(struct net_device *netdev)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
3478c2ecf20Sopenharmony_ci	unsigned long flags;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	union cvmx_agl_gmx_txx_stat0 s0;
3508c2ecf20Sopenharmony_ci	union cvmx_agl_gmx_txx_stat1 s1;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/* These reads also clear the count registers.  */
3538c2ecf20Sopenharmony_ci	s0.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_STAT0);
3548c2ecf20Sopenharmony_ci	s1.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_STAT1);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (s0.s.xsdef || s0.s.xscol || s1.s.scol || s1.s.mcol) {
3578c2ecf20Sopenharmony_ci		/* Do an atomic update. */
3588c2ecf20Sopenharmony_ci		spin_lock_irqsave(&p->lock, flags);
3598c2ecf20Sopenharmony_ci		netdev->stats.tx_errors += s0.s.xsdef + s0.s.xscol;
3608c2ecf20Sopenharmony_ci		netdev->stats.collisions += s1.s.scol + s1.s.mcol;
3618c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&p->lock, flags);
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci/*
3668c2ecf20Sopenharmony_ci * Dequeue a receive skb and its corresponding ring entry.  The ring
3678c2ecf20Sopenharmony_ci * entry is returned, *pskb is updated to point to the skb.
3688c2ecf20Sopenharmony_ci */
3698c2ecf20Sopenharmony_cistatic u64 octeon_mgmt_dequeue_rx_buffer(struct octeon_mgmt *p,
3708c2ecf20Sopenharmony_ci					 struct sk_buff **pskb)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	union mgmt_port_ring_entry re;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	dma_sync_single_for_cpu(p->dev, p->rx_ring_handle,
3758c2ecf20Sopenharmony_ci				ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE),
3768c2ecf20Sopenharmony_ci				DMA_BIDIRECTIONAL);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	re.d64 = p->rx_ring[p->rx_next];
3798c2ecf20Sopenharmony_ci	p->rx_next = (p->rx_next + 1) % OCTEON_MGMT_RX_RING_SIZE;
3808c2ecf20Sopenharmony_ci	p->rx_current_fill--;
3818c2ecf20Sopenharmony_ci	*pskb = __skb_dequeue(&p->rx_list);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	dma_unmap_single(p->dev, re.s.addr,
3848c2ecf20Sopenharmony_ci			 ETH_FRAME_LEN + OCTEON_MGMT_RX_HEADROOM,
3858c2ecf20Sopenharmony_ci			 DMA_FROM_DEVICE);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	return re.d64;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic int octeon_mgmt_receive_one(struct octeon_mgmt *p)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct net_device *netdev = p->netdev;
3948c2ecf20Sopenharmony_ci	union cvmx_mixx_ircnt mix_ircnt;
3958c2ecf20Sopenharmony_ci	union mgmt_port_ring_entry re;
3968c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3978c2ecf20Sopenharmony_ci	struct sk_buff *skb2;
3988c2ecf20Sopenharmony_ci	struct sk_buff *skb_new;
3998c2ecf20Sopenharmony_ci	union mgmt_port_ring_entry re2;
4008c2ecf20Sopenharmony_ci	int rc = 1;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	re.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb);
4048c2ecf20Sopenharmony_ci	if (likely(re.s.code == RING_ENTRY_CODE_DONE)) {
4058c2ecf20Sopenharmony_ci		/* A good packet, send it up. */
4068c2ecf20Sopenharmony_ci		skb_put(skb, re.s.len);
4078c2ecf20Sopenharmony_cigood:
4088c2ecf20Sopenharmony_ci		/* Process the RX timestamp if it was recorded */
4098c2ecf20Sopenharmony_ci		if (p->has_rx_tstamp) {
4108c2ecf20Sopenharmony_ci			/* The first 8 bytes are the timestamp */
4118c2ecf20Sopenharmony_ci			u64 ns = *(u64 *)skb->data;
4128c2ecf20Sopenharmony_ci			struct skb_shared_hwtstamps *ts;
4138c2ecf20Sopenharmony_ci			ts = skb_hwtstamps(skb);
4148c2ecf20Sopenharmony_ci			ts->hwtstamp = ns_to_ktime(ns);
4158c2ecf20Sopenharmony_ci			__skb_pull(skb, 8);
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci		skb->protocol = eth_type_trans(skb, netdev);
4188c2ecf20Sopenharmony_ci		netdev->stats.rx_packets++;
4198c2ecf20Sopenharmony_ci		netdev->stats.rx_bytes += skb->len;
4208c2ecf20Sopenharmony_ci		netif_receive_skb(skb);
4218c2ecf20Sopenharmony_ci		rc = 0;
4228c2ecf20Sopenharmony_ci	} else if (re.s.code == RING_ENTRY_CODE_MORE) {
4238c2ecf20Sopenharmony_ci		/* Packet split across skbs.  This can happen if we
4248c2ecf20Sopenharmony_ci		 * increase the MTU.  Buffers that are already in the
4258c2ecf20Sopenharmony_ci		 * rx ring can then end up being too small.  As the rx
4268c2ecf20Sopenharmony_ci		 * ring is refilled, buffers sized for the new MTU
4278c2ecf20Sopenharmony_ci		 * will be used and we should go back to the normal
4288c2ecf20Sopenharmony_ci		 * non-split case.
4298c2ecf20Sopenharmony_ci		 */
4308c2ecf20Sopenharmony_ci		skb_put(skb, re.s.len);
4318c2ecf20Sopenharmony_ci		do {
4328c2ecf20Sopenharmony_ci			re2.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb2);
4338c2ecf20Sopenharmony_ci			if (re2.s.code != RING_ENTRY_CODE_MORE
4348c2ecf20Sopenharmony_ci				&& re2.s.code != RING_ENTRY_CODE_DONE)
4358c2ecf20Sopenharmony_ci				goto split_error;
4368c2ecf20Sopenharmony_ci			skb_put(skb2,  re2.s.len);
4378c2ecf20Sopenharmony_ci			skb_new = skb_copy_expand(skb, 0, skb2->len,
4388c2ecf20Sopenharmony_ci						  GFP_ATOMIC);
4398c2ecf20Sopenharmony_ci			if (!skb_new)
4408c2ecf20Sopenharmony_ci				goto split_error;
4418c2ecf20Sopenharmony_ci			if (skb_copy_bits(skb2, 0, skb_tail_pointer(skb_new),
4428c2ecf20Sopenharmony_ci					  skb2->len))
4438c2ecf20Sopenharmony_ci				goto split_error;
4448c2ecf20Sopenharmony_ci			skb_put(skb_new, skb2->len);
4458c2ecf20Sopenharmony_ci			dev_kfree_skb_any(skb);
4468c2ecf20Sopenharmony_ci			dev_kfree_skb_any(skb2);
4478c2ecf20Sopenharmony_ci			skb = skb_new;
4488c2ecf20Sopenharmony_ci		} while (re2.s.code == RING_ENTRY_CODE_MORE);
4498c2ecf20Sopenharmony_ci		goto good;
4508c2ecf20Sopenharmony_ci	} else {
4518c2ecf20Sopenharmony_ci		/* Some other error, discard it. */
4528c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
4538c2ecf20Sopenharmony_ci		/* Error statistics are accumulated in
4548c2ecf20Sopenharmony_ci		 * octeon_mgmt_update_rx_stats.
4558c2ecf20Sopenharmony_ci		 */
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci	goto done;
4588c2ecf20Sopenharmony_cisplit_error:
4598c2ecf20Sopenharmony_ci	/* Discard the whole mess. */
4608c2ecf20Sopenharmony_ci	dev_kfree_skb_any(skb);
4618c2ecf20Sopenharmony_ci	dev_kfree_skb_any(skb2);
4628c2ecf20Sopenharmony_ci	while (re2.s.code == RING_ENTRY_CODE_MORE) {
4638c2ecf20Sopenharmony_ci		re2.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb2);
4648c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb2);
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci	netdev->stats.rx_errors++;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cidone:
4698c2ecf20Sopenharmony_ci	/* Tell the hardware we processed a packet.  */
4708c2ecf20Sopenharmony_ci	mix_ircnt.u64 = 0;
4718c2ecf20Sopenharmony_ci	mix_ircnt.s.ircnt = 1;
4728c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_IRCNT, mix_ircnt.u64);
4738c2ecf20Sopenharmony_ci	return rc;
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic int octeon_mgmt_receive_packets(struct octeon_mgmt *p, int budget)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	unsigned int work_done = 0;
4798c2ecf20Sopenharmony_ci	union cvmx_mixx_ircnt mix_ircnt;
4808c2ecf20Sopenharmony_ci	int rc;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	mix_ircnt.u64 = cvmx_read_csr(p->mix + MIX_IRCNT);
4838c2ecf20Sopenharmony_ci	while (work_done < budget && mix_ircnt.s.ircnt) {
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		rc = octeon_mgmt_receive_one(p);
4868c2ecf20Sopenharmony_ci		if (!rc)
4878c2ecf20Sopenharmony_ci			work_done++;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci		/* Check for more packets. */
4908c2ecf20Sopenharmony_ci		mix_ircnt.u64 = cvmx_read_csr(p->mix + MIX_IRCNT);
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	octeon_mgmt_rx_fill_ring(p->netdev);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	return work_done;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic int octeon_mgmt_napi_poll(struct napi_struct *napi, int budget)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = container_of(napi, struct octeon_mgmt, napi);
5018c2ecf20Sopenharmony_ci	struct net_device *netdev = p->netdev;
5028c2ecf20Sopenharmony_ci	unsigned int work_done = 0;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	work_done = octeon_mgmt_receive_packets(p, budget);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (work_done < budget) {
5078c2ecf20Sopenharmony_ci		/* We stopped because no more packets were available. */
5088c2ecf20Sopenharmony_ci		napi_complete_done(napi, work_done);
5098c2ecf20Sopenharmony_ci		octeon_mgmt_enable_rx_irq(p);
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci	octeon_mgmt_update_rx_stats(netdev);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	return work_done;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci/* Reset the hardware to clean state.  */
5178c2ecf20Sopenharmony_cistatic void octeon_mgmt_reset_hw(struct octeon_mgmt *p)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	union cvmx_mixx_ctl mix_ctl;
5208c2ecf20Sopenharmony_ci	union cvmx_mixx_bist mix_bist;
5218c2ecf20Sopenharmony_ci	union cvmx_agl_gmx_bist agl_gmx_bist;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	mix_ctl.u64 = 0;
5248c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
5258c2ecf20Sopenharmony_ci	do {
5268c2ecf20Sopenharmony_ci		mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL);
5278c2ecf20Sopenharmony_ci	} while (mix_ctl.s.busy);
5288c2ecf20Sopenharmony_ci	mix_ctl.s.reset = 1;
5298c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
5308c2ecf20Sopenharmony_ci	cvmx_read_csr(p->mix + MIX_CTL);
5318c2ecf20Sopenharmony_ci	octeon_io_clk_delay(64);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	mix_bist.u64 = cvmx_read_csr(p->mix + MIX_BIST);
5348c2ecf20Sopenharmony_ci	if (mix_bist.u64)
5358c2ecf20Sopenharmony_ci		dev_warn(p->dev, "MIX failed BIST (0x%016llx)\n",
5368c2ecf20Sopenharmony_ci			(unsigned long long)mix_bist.u64);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	agl_gmx_bist.u64 = cvmx_read_csr(CVMX_AGL_GMX_BIST);
5398c2ecf20Sopenharmony_ci	if (agl_gmx_bist.u64)
5408c2ecf20Sopenharmony_ci		dev_warn(p->dev, "AGL failed BIST (0x%016llx)\n",
5418c2ecf20Sopenharmony_ci			 (unsigned long long)agl_gmx_bist.u64);
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistruct octeon_mgmt_cam_state {
5458c2ecf20Sopenharmony_ci	u64 cam[6];
5468c2ecf20Sopenharmony_ci	u64 cam_mask;
5478c2ecf20Sopenharmony_ci	int cam_index;
5488c2ecf20Sopenharmony_ci};
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic void octeon_mgmt_cam_state_add(struct octeon_mgmt_cam_state *cs,
5518c2ecf20Sopenharmony_ci				      unsigned char *addr)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	int i;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++)
5568c2ecf20Sopenharmony_ci		cs->cam[i] |= (u64)addr[i] << (8 * (cs->cam_index));
5578c2ecf20Sopenharmony_ci	cs->cam_mask |= (1ULL << cs->cam_index);
5588c2ecf20Sopenharmony_ci	cs->cam_index++;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic void octeon_mgmt_set_rx_filtering(struct net_device *netdev)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
5648c2ecf20Sopenharmony_ci	union cvmx_agl_gmx_rxx_adr_ctl adr_ctl;
5658c2ecf20Sopenharmony_ci	union cvmx_agl_gmx_prtx_cfg agl_gmx_prtx;
5668c2ecf20Sopenharmony_ci	unsigned long flags;
5678c2ecf20Sopenharmony_ci	unsigned int prev_packet_enable;
5688c2ecf20Sopenharmony_ci	unsigned int cam_mode = 1; /* 1 - Accept on CAM match */
5698c2ecf20Sopenharmony_ci	unsigned int multicast_mode = 1; /* 1 - Reject all multicast.  */
5708c2ecf20Sopenharmony_ci	struct octeon_mgmt_cam_state cam_state;
5718c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
5728c2ecf20Sopenharmony_ci	int available_cam_entries;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	memset(&cam_state, 0, sizeof(cam_state));
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	if ((netdev->flags & IFF_PROMISC) || netdev->uc.count > 7) {
5778c2ecf20Sopenharmony_ci		cam_mode = 0;
5788c2ecf20Sopenharmony_ci		available_cam_entries = 8;
5798c2ecf20Sopenharmony_ci	} else {
5808c2ecf20Sopenharmony_ci		/* One CAM entry for the primary address, leaves seven
5818c2ecf20Sopenharmony_ci		 * for the secondary addresses.
5828c2ecf20Sopenharmony_ci		 */
5838c2ecf20Sopenharmony_ci		available_cam_entries = 7 - netdev->uc.count;
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	if (netdev->flags & IFF_MULTICAST) {
5878c2ecf20Sopenharmony_ci		if (cam_mode == 0 || (netdev->flags & IFF_ALLMULTI) ||
5888c2ecf20Sopenharmony_ci		    netdev_mc_count(netdev) > available_cam_entries)
5898c2ecf20Sopenharmony_ci			multicast_mode = 2; /* 2 - Accept all multicast.  */
5908c2ecf20Sopenharmony_ci		else
5918c2ecf20Sopenharmony_ci			multicast_mode = 0; /* 0 - Use CAM.  */
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (cam_mode == 1) {
5958c2ecf20Sopenharmony_ci		/* Add primary address. */
5968c2ecf20Sopenharmony_ci		octeon_mgmt_cam_state_add(&cam_state, netdev->dev_addr);
5978c2ecf20Sopenharmony_ci		netdev_for_each_uc_addr(ha, netdev)
5988c2ecf20Sopenharmony_ci			octeon_mgmt_cam_state_add(&cam_state, ha->addr);
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci	if (multicast_mode == 0) {
6018c2ecf20Sopenharmony_ci		netdev_for_each_mc_addr(ha, netdev)
6028c2ecf20Sopenharmony_ci			octeon_mgmt_cam_state_add(&cam_state, ha->addr);
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p->lock, flags);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	/* Disable packet I/O. */
6088c2ecf20Sopenharmony_ci	agl_gmx_prtx.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
6098c2ecf20Sopenharmony_ci	prev_packet_enable = agl_gmx_prtx.s.en;
6108c2ecf20Sopenharmony_ci	agl_gmx_prtx.s.en = 0;
6118c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, agl_gmx_prtx.u64);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	adr_ctl.u64 = 0;
6148c2ecf20Sopenharmony_ci	adr_ctl.s.cam_mode = cam_mode;
6158c2ecf20Sopenharmony_ci	adr_ctl.s.mcst = multicast_mode;
6168c2ecf20Sopenharmony_ci	adr_ctl.s.bcst = 1;     /* Allow broadcast */
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CTL, adr_ctl.u64);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM0, cam_state.cam[0]);
6218c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM1, cam_state.cam[1]);
6228c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM2, cam_state.cam[2]);
6238c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM3, cam_state.cam[3]);
6248c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM4, cam_state.cam[4]);
6258c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM5, cam_state.cam[5]);
6268c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM_EN, cam_state.cam_mask);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	/* Restore packet I/O. */
6298c2ecf20Sopenharmony_ci	agl_gmx_prtx.s.en = prev_packet_enable;
6308c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, agl_gmx_prtx.u64);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p->lock, flags);
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_cistatic int octeon_mgmt_set_mac_address(struct net_device *netdev, void *addr)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	int r = eth_mac_addr(netdev, addr);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (r)
6408c2ecf20Sopenharmony_ci		return r;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	octeon_mgmt_set_rx_filtering(netdev);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	return 0;
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_cistatic int octeon_mgmt_change_mtu(struct net_device *netdev, int new_mtu)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
6508c2ecf20Sopenharmony_ci	int max_packet = new_mtu + ETH_HLEN + ETH_FCS_LEN;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	netdev->mtu = new_mtu;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	/* HW lifts the limit if the frame is VLAN tagged
6558c2ecf20Sopenharmony_ci	 * (+4 bytes per each tag, up to two tags)
6568c2ecf20Sopenharmony_ci	 */
6578c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_MAX, max_packet);
6588c2ecf20Sopenharmony_ci	/* Set the hardware to truncate packets larger than the MTU. The jabber
6598c2ecf20Sopenharmony_ci	 * register must be set to a multiple of 8 bytes, so round up. JABBER is
6608c2ecf20Sopenharmony_ci	 * an unconditional limit, so we need to account for two possible VLAN
6618c2ecf20Sopenharmony_ci	 * tags.
6628c2ecf20Sopenharmony_ci	 */
6638c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_JABBER,
6648c2ecf20Sopenharmony_ci		       (max_packet + 7 + VLAN_HLEN * 2) & 0xfff8);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	return 0;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cistatic irqreturn_t octeon_mgmt_interrupt(int cpl, void *dev_id)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	struct net_device *netdev = dev_id;
6728c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
6738c2ecf20Sopenharmony_ci	union cvmx_mixx_isr mixx_isr;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	mixx_isr.u64 = cvmx_read_csr(p->mix + MIX_ISR);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	/* Clear any pending interrupts */
6788c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_ISR, mixx_isr.u64);
6798c2ecf20Sopenharmony_ci	cvmx_read_csr(p->mix + MIX_ISR);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (mixx_isr.s.irthresh) {
6828c2ecf20Sopenharmony_ci		octeon_mgmt_disable_rx_irq(p);
6838c2ecf20Sopenharmony_ci		napi_schedule(&p->napi);
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci	if (mixx_isr.s.orthresh) {
6868c2ecf20Sopenharmony_ci		octeon_mgmt_disable_tx_irq(p);
6878c2ecf20Sopenharmony_ci		tasklet_schedule(&p->tx_clean_tasklet);
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistatic int octeon_mgmt_ioctl_hwtstamp(struct net_device *netdev,
6948c2ecf20Sopenharmony_ci				      struct ifreq *rq, int cmd)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
6978c2ecf20Sopenharmony_ci	struct hwtstamp_config config;
6988c2ecf20Sopenharmony_ci	union cvmx_mio_ptp_clock_cfg ptp;
6998c2ecf20Sopenharmony_ci	union cvmx_agl_gmx_rxx_frm_ctl rxx_frm_ctl;
7008c2ecf20Sopenharmony_ci	bool have_hw_timestamps = false;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	if (copy_from_user(&config, rq->ifr_data, sizeof(config)))
7038c2ecf20Sopenharmony_ci		return -EFAULT;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (config.flags) /* reserved for future extensions */
7068c2ecf20Sopenharmony_ci		return -EINVAL;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	/* Check the status of hardware for tiemstamps */
7098c2ecf20Sopenharmony_ci	if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
7108c2ecf20Sopenharmony_ci		/* Get the current state of the PTP clock */
7118c2ecf20Sopenharmony_ci		ptp.u64 = cvmx_read_csr(CVMX_MIO_PTP_CLOCK_CFG);
7128c2ecf20Sopenharmony_ci		if (!ptp.s.ext_clk_en) {
7138c2ecf20Sopenharmony_ci			/* The clock has not been configured to use an
7148c2ecf20Sopenharmony_ci			 * external source.  Program it to use the main clock
7158c2ecf20Sopenharmony_ci			 * reference.
7168c2ecf20Sopenharmony_ci			 */
7178c2ecf20Sopenharmony_ci			u64 clock_comp = (NSEC_PER_SEC << 32) /	octeon_get_io_clock_rate();
7188c2ecf20Sopenharmony_ci			if (!ptp.s.ptp_en)
7198c2ecf20Sopenharmony_ci				cvmx_write_csr(CVMX_MIO_PTP_CLOCK_COMP, clock_comp);
7208c2ecf20Sopenharmony_ci			netdev_info(netdev,
7218c2ecf20Sopenharmony_ci				    "PTP Clock using sclk reference @ %lldHz\n",
7228c2ecf20Sopenharmony_ci				    (NSEC_PER_SEC << 32) / clock_comp);
7238c2ecf20Sopenharmony_ci		} else {
7248c2ecf20Sopenharmony_ci			/* The clock is already programmed to use a GPIO */
7258c2ecf20Sopenharmony_ci			u64 clock_comp = cvmx_read_csr(CVMX_MIO_PTP_CLOCK_COMP);
7268c2ecf20Sopenharmony_ci			netdev_info(netdev,
7278c2ecf20Sopenharmony_ci				    "PTP Clock using GPIO%d @ %lld Hz\n",
7288c2ecf20Sopenharmony_ci				    ptp.s.ext_clk_in, (NSEC_PER_SEC << 32) / clock_comp);
7298c2ecf20Sopenharmony_ci		}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci		/* Enable the clock if it wasn't done already */
7328c2ecf20Sopenharmony_ci		if (!ptp.s.ptp_en) {
7338c2ecf20Sopenharmony_ci			ptp.s.ptp_en = 1;
7348c2ecf20Sopenharmony_ci			cvmx_write_csr(CVMX_MIO_PTP_CLOCK_CFG, ptp.u64);
7358c2ecf20Sopenharmony_ci		}
7368c2ecf20Sopenharmony_ci		have_hw_timestamps = true;
7378c2ecf20Sopenharmony_ci	}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	if (!have_hw_timestamps)
7408c2ecf20Sopenharmony_ci		return -EINVAL;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	switch (config.tx_type) {
7438c2ecf20Sopenharmony_ci	case HWTSTAMP_TX_OFF:
7448c2ecf20Sopenharmony_ci	case HWTSTAMP_TX_ON:
7458c2ecf20Sopenharmony_ci		break;
7468c2ecf20Sopenharmony_ci	default:
7478c2ecf20Sopenharmony_ci		return -ERANGE;
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	switch (config.rx_filter) {
7518c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_NONE:
7528c2ecf20Sopenharmony_ci		p->has_rx_tstamp = false;
7538c2ecf20Sopenharmony_ci		rxx_frm_ctl.u64 = cvmx_read_csr(p->agl + AGL_GMX_RX_FRM_CTL);
7548c2ecf20Sopenharmony_ci		rxx_frm_ctl.s.ptp_mode = 0;
7558c2ecf20Sopenharmony_ci		cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64);
7568c2ecf20Sopenharmony_ci		break;
7578c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_ALL:
7588c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_SOME:
7598c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
7608c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
7618c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
7628c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
7638c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
7648c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
7658c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
7668c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
7678c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
7688c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_EVENT:
7698c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_SYNC:
7708c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
7718c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_NTP_ALL:
7728c2ecf20Sopenharmony_ci		p->has_rx_tstamp = have_hw_timestamps;
7738c2ecf20Sopenharmony_ci		config.rx_filter = HWTSTAMP_FILTER_ALL;
7748c2ecf20Sopenharmony_ci		if (p->has_rx_tstamp) {
7758c2ecf20Sopenharmony_ci			rxx_frm_ctl.u64 = cvmx_read_csr(p->agl + AGL_GMX_RX_FRM_CTL);
7768c2ecf20Sopenharmony_ci			rxx_frm_ctl.s.ptp_mode = 1;
7778c2ecf20Sopenharmony_ci			cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64);
7788c2ecf20Sopenharmony_ci		}
7798c2ecf20Sopenharmony_ci		break;
7808c2ecf20Sopenharmony_ci	default:
7818c2ecf20Sopenharmony_ci		return -ERANGE;
7828c2ecf20Sopenharmony_ci	}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	if (copy_to_user(rq->ifr_data, &config, sizeof(config)))
7858c2ecf20Sopenharmony_ci		return -EFAULT;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	return 0;
7888c2ecf20Sopenharmony_ci}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_cistatic int octeon_mgmt_ioctl(struct net_device *netdev,
7918c2ecf20Sopenharmony_ci			     struct ifreq *rq, int cmd)
7928c2ecf20Sopenharmony_ci{
7938c2ecf20Sopenharmony_ci	switch (cmd) {
7948c2ecf20Sopenharmony_ci	case SIOCSHWTSTAMP:
7958c2ecf20Sopenharmony_ci		return octeon_mgmt_ioctl_hwtstamp(netdev, rq, cmd);
7968c2ecf20Sopenharmony_ci	default:
7978c2ecf20Sopenharmony_ci		return phy_do_ioctl(netdev, rq, cmd);
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_cistatic void octeon_mgmt_disable_link(struct octeon_mgmt *p)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	union cvmx_agl_gmx_prtx_cfg prtx_cfg;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	/* Disable GMX before we make any changes. */
8068c2ecf20Sopenharmony_ci	prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
8078c2ecf20Sopenharmony_ci	prtx_cfg.s.en = 0;
8088c2ecf20Sopenharmony_ci	prtx_cfg.s.tx_en = 0;
8098c2ecf20Sopenharmony_ci	prtx_cfg.s.rx_en = 0;
8108c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
8138c2ecf20Sopenharmony_ci		int i;
8148c2ecf20Sopenharmony_ci		for (i = 0; i < 10; i++) {
8158c2ecf20Sopenharmony_ci			prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
8168c2ecf20Sopenharmony_ci			if (prtx_cfg.s.tx_idle == 1 || prtx_cfg.s.rx_idle == 1)
8178c2ecf20Sopenharmony_ci				break;
8188c2ecf20Sopenharmony_ci			mdelay(1);
8198c2ecf20Sopenharmony_ci			i++;
8208c2ecf20Sopenharmony_ci		}
8218c2ecf20Sopenharmony_ci	}
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic void octeon_mgmt_enable_link(struct octeon_mgmt *p)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	union cvmx_agl_gmx_prtx_cfg prtx_cfg;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	/* Restore the GMX enable state only if link is set */
8298c2ecf20Sopenharmony_ci	prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
8308c2ecf20Sopenharmony_ci	prtx_cfg.s.tx_en = 1;
8318c2ecf20Sopenharmony_ci	prtx_cfg.s.rx_en = 1;
8328c2ecf20Sopenharmony_ci	prtx_cfg.s.en = 1;
8338c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64);
8348c2ecf20Sopenharmony_ci}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_cistatic void octeon_mgmt_update_link(struct octeon_mgmt *p)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct net_device *ndev = p->netdev;
8398c2ecf20Sopenharmony_ci	struct phy_device *phydev = ndev->phydev;
8408c2ecf20Sopenharmony_ci	union cvmx_agl_gmx_prtx_cfg prtx_cfg;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	if (!phydev->link)
8458c2ecf20Sopenharmony_ci		prtx_cfg.s.duplex = 1;
8468c2ecf20Sopenharmony_ci	else
8478c2ecf20Sopenharmony_ci		prtx_cfg.s.duplex = phydev->duplex;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	switch (phydev->speed) {
8508c2ecf20Sopenharmony_ci	case 10:
8518c2ecf20Sopenharmony_ci		prtx_cfg.s.speed = 0;
8528c2ecf20Sopenharmony_ci		prtx_cfg.s.slottime = 0;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci		if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
8558c2ecf20Sopenharmony_ci			prtx_cfg.s.burst = 1;
8568c2ecf20Sopenharmony_ci			prtx_cfg.s.speed_msb = 1;
8578c2ecf20Sopenharmony_ci		}
8588c2ecf20Sopenharmony_ci		break;
8598c2ecf20Sopenharmony_ci	case 100:
8608c2ecf20Sopenharmony_ci		prtx_cfg.s.speed = 0;
8618c2ecf20Sopenharmony_ci		prtx_cfg.s.slottime = 0;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci		if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
8648c2ecf20Sopenharmony_ci			prtx_cfg.s.burst = 1;
8658c2ecf20Sopenharmony_ci			prtx_cfg.s.speed_msb = 0;
8668c2ecf20Sopenharmony_ci		}
8678c2ecf20Sopenharmony_ci		break;
8688c2ecf20Sopenharmony_ci	case 1000:
8698c2ecf20Sopenharmony_ci		/* 1000 MBits is only supported on 6XXX chips */
8708c2ecf20Sopenharmony_ci		if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
8718c2ecf20Sopenharmony_ci			prtx_cfg.s.speed = 1;
8728c2ecf20Sopenharmony_ci			prtx_cfg.s.speed_msb = 0;
8738c2ecf20Sopenharmony_ci			/* Only matters for half-duplex */
8748c2ecf20Sopenharmony_ci			prtx_cfg.s.slottime = 1;
8758c2ecf20Sopenharmony_ci			prtx_cfg.s.burst = phydev->duplex;
8768c2ecf20Sopenharmony_ci		}
8778c2ecf20Sopenharmony_ci		break;
8788c2ecf20Sopenharmony_ci	case 0:  /* No link */
8798c2ecf20Sopenharmony_ci	default:
8808c2ecf20Sopenharmony_ci		break;
8818c2ecf20Sopenharmony_ci	}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	/* Write the new GMX setting with the port still disabled. */
8848c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	/* Read GMX CFG again to make sure the config is completed. */
8878c2ecf20Sopenharmony_ci	prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
8908c2ecf20Sopenharmony_ci		union cvmx_agl_gmx_txx_clk agl_clk;
8918c2ecf20Sopenharmony_ci		union cvmx_agl_prtx_ctl prtx_ctl;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci		prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl);
8948c2ecf20Sopenharmony_ci		agl_clk.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_CLK);
8958c2ecf20Sopenharmony_ci		/* MII (both speeds) and RGMII 1000 speed. */
8968c2ecf20Sopenharmony_ci		agl_clk.s.clk_cnt = 1;
8978c2ecf20Sopenharmony_ci		if (prtx_ctl.s.mode == 0) { /* RGMII mode */
8988c2ecf20Sopenharmony_ci			if (phydev->speed == 10)
8998c2ecf20Sopenharmony_ci				agl_clk.s.clk_cnt = 50;
9008c2ecf20Sopenharmony_ci			else if (phydev->speed == 100)
9018c2ecf20Sopenharmony_ci				agl_clk.s.clk_cnt = 5;
9028c2ecf20Sopenharmony_ci		}
9038c2ecf20Sopenharmony_ci		cvmx_write_csr(p->agl + AGL_GMX_TX_CLK, agl_clk.u64);
9048c2ecf20Sopenharmony_ci	}
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_cistatic void octeon_mgmt_adjust_link(struct net_device *netdev)
9088c2ecf20Sopenharmony_ci{
9098c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
9108c2ecf20Sopenharmony_ci	struct phy_device *phydev = netdev->phydev;
9118c2ecf20Sopenharmony_ci	unsigned long flags;
9128c2ecf20Sopenharmony_ci	int link_changed = 0;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	if (!phydev)
9158c2ecf20Sopenharmony_ci		return;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p->lock, flags);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	if (!phydev->link && p->last_link)
9218c2ecf20Sopenharmony_ci		link_changed = -1;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	if (phydev->link &&
9248c2ecf20Sopenharmony_ci	    (p->last_duplex != phydev->duplex ||
9258c2ecf20Sopenharmony_ci	     p->last_link != phydev->link ||
9268c2ecf20Sopenharmony_ci	     p->last_speed != phydev->speed)) {
9278c2ecf20Sopenharmony_ci		octeon_mgmt_disable_link(p);
9288c2ecf20Sopenharmony_ci		link_changed = 1;
9298c2ecf20Sopenharmony_ci		octeon_mgmt_update_link(p);
9308c2ecf20Sopenharmony_ci		octeon_mgmt_enable_link(p);
9318c2ecf20Sopenharmony_ci	}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	p->last_link = phydev->link;
9348c2ecf20Sopenharmony_ci	p->last_speed = phydev->speed;
9358c2ecf20Sopenharmony_ci	p->last_duplex = phydev->duplex;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p->lock, flags);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	if (link_changed != 0) {
9408c2ecf20Sopenharmony_ci		if (link_changed > 0)
9418c2ecf20Sopenharmony_ci			netdev_info(netdev, "Link is up - %d/%s\n",
9428c2ecf20Sopenharmony_ci				    phydev->speed, phydev->duplex == DUPLEX_FULL ? "Full" : "Half");
9438c2ecf20Sopenharmony_ci		else
9448c2ecf20Sopenharmony_ci			netdev_info(netdev, "Link is down\n");
9458c2ecf20Sopenharmony_ci	}
9468c2ecf20Sopenharmony_ci}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_cistatic int octeon_mgmt_init_phy(struct net_device *netdev)
9498c2ecf20Sopenharmony_ci{
9508c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
9518c2ecf20Sopenharmony_ci	struct phy_device *phydev = NULL;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	if (octeon_is_simulation() || p->phy_np == NULL) {
9548c2ecf20Sopenharmony_ci		/* No PHYs in the simulator. */
9558c2ecf20Sopenharmony_ci		netif_carrier_on(netdev);
9568c2ecf20Sopenharmony_ci		return 0;
9578c2ecf20Sopenharmony_ci	}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	phydev = of_phy_connect(netdev, p->phy_np,
9608c2ecf20Sopenharmony_ci				octeon_mgmt_adjust_link, 0,
9618c2ecf20Sopenharmony_ci				PHY_INTERFACE_MODE_MII);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	if (!phydev)
9648c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	return 0;
9678c2ecf20Sopenharmony_ci}
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_cistatic int octeon_mgmt_open(struct net_device *netdev)
9708c2ecf20Sopenharmony_ci{
9718c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
9728c2ecf20Sopenharmony_ci	union cvmx_mixx_ctl mix_ctl;
9738c2ecf20Sopenharmony_ci	union cvmx_agl_gmx_inf_mode agl_gmx_inf_mode;
9748c2ecf20Sopenharmony_ci	union cvmx_mixx_oring1 oring1;
9758c2ecf20Sopenharmony_ci	union cvmx_mixx_iring1 iring1;
9768c2ecf20Sopenharmony_ci	union cvmx_agl_gmx_rxx_frm_ctl rxx_frm_ctl;
9778c2ecf20Sopenharmony_ci	union cvmx_mixx_irhwm mix_irhwm;
9788c2ecf20Sopenharmony_ci	union cvmx_mixx_orhwm mix_orhwm;
9798c2ecf20Sopenharmony_ci	union cvmx_mixx_intena mix_intena;
9808c2ecf20Sopenharmony_ci	struct sockaddr sa;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	/* Allocate ring buffers.  */
9838c2ecf20Sopenharmony_ci	p->tx_ring = kzalloc(ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE),
9848c2ecf20Sopenharmony_ci			     GFP_KERNEL);
9858c2ecf20Sopenharmony_ci	if (!p->tx_ring)
9868c2ecf20Sopenharmony_ci		return -ENOMEM;
9878c2ecf20Sopenharmony_ci	p->tx_ring_handle =
9888c2ecf20Sopenharmony_ci		dma_map_single(p->dev, p->tx_ring,
9898c2ecf20Sopenharmony_ci			       ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE),
9908c2ecf20Sopenharmony_ci			       DMA_BIDIRECTIONAL);
9918c2ecf20Sopenharmony_ci	p->tx_next = 0;
9928c2ecf20Sopenharmony_ci	p->tx_next_clean = 0;
9938c2ecf20Sopenharmony_ci	p->tx_current_fill = 0;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	p->rx_ring = kzalloc(ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE),
9978c2ecf20Sopenharmony_ci			     GFP_KERNEL);
9988c2ecf20Sopenharmony_ci	if (!p->rx_ring)
9998c2ecf20Sopenharmony_ci		goto err_nomem;
10008c2ecf20Sopenharmony_ci	p->rx_ring_handle =
10018c2ecf20Sopenharmony_ci		dma_map_single(p->dev, p->rx_ring,
10028c2ecf20Sopenharmony_ci			       ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE),
10038c2ecf20Sopenharmony_ci			       DMA_BIDIRECTIONAL);
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	p->rx_next = 0;
10068c2ecf20Sopenharmony_ci	p->rx_next_fill = 0;
10078c2ecf20Sopenharmony_ci	p->rx_current_fill = 0;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	octeon_mgmt_reset_hw(p);
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	/* Bring it out of reset if needed. */
10148c2ecf20Sopenharmony_ci	if (mix_ctl.s.reset) {
10158c2ecf20Sopenharmony_ci		mix_ctl.s.reset = 0;
10168c2ecf20Sopenharmony_ci		cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
10178c2ecf20Sopenharmony_ci		do {
10188c2ecf20Sopenharmony_ci			mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL);
10198c2ecf20Sopenharmony_ci		} while (mix_ctl.s.reset);
10208c2ecf20Sopenharmony_ci	}
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	if (OCTEON_IS_MODEL(OCTEON_CN5XXX)) {
10238c2ecf20Sopenharmony_ci		agl_gmx_inf_mode.u64 = 0;
10248c2ecf20Sopenharmony_ci		agl_gmx_inf_mode.s.en = 1;
10258c2ecf20Sopenharmony_ci		cvmx_write_csr(CVMX_AGL_GMX_INF_MODE, agl_gmx_inf_mode.u64);
10268c2ecf20Sopenharmony_ci	}
10278c2ecf20Sopenharmony_ci	if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X)
10288c2ecf20Sopenharmony_ci		|| OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X)) {
10298c2ecf20Sopenharmony_ci		/* Force compensation values, as they are not
10308c2ecf20Sopenharmony_ci		 * determined properly by HW
10318c2ecf20Sopenharmony_ci		 */
10328c2ecf20Sopenharmony_ci		union cvmx_agl_gmx_drv_ctl drv_ctl;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci		drv_ctl.u64 = cvmx_read_csr(CVMX_AGL_GMX_DRV_CTL);
10358c2ecf20Sopenharmony_ci		if (p->port) {
10368c2ecf20Sopenharmony_ci			drv_ctl.s.byp_en1 = 1;
10378c2ecf20Sopenharmony_ci			drv_ctl.s.nctl1 = 6;
10388c2ecf20Sopenharmony_ci			drv_ctl.s.pctl1 = 6;
10398c2ecf20Sopenharmony_ci		} else {
10408c2ecf20Sopenharmony_ci			drv_ctl.s.byp_en = 1;
10418c2ecf20Sopenharmony_ci			drv_ctl.s.nctl = 6;
10428c2ecf20Sopenharmony_ci			drv_ctl.s.pctl = 6;
10438c2ecf20Sopenharmony_ci		}
10448c2ecf20Sopenharmony_ci		cvmx_write_csr(CVMX_AGL_GMX_DRV_CTL, drv_ctl.u64);
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	oring1.u64 = 0;
10488c2ecf20Sopenharmony_ci	oring1.s.obase = p->tx_ring_handle >> 3;
10498c2ecf20Sopenharmony_ci	oring1.s.osize = OCTEON_MGMT_TX_RING_SIZE;
10508c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_ORING1, oring1.u64);
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	iring1.u64 = 0;
10538c2ecf20Sopenharmony_ci	iring1.s.ibase = p->rx_ring_handle >> 3;
10548c2ecf20Sopenharmony_ci	iring1.s.isize = OCTEON_MGMT_RX_RING_SIZE;
10558c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_IRING1, iring1.u64);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	memcpy(sa.sa_data, netdev->dev_addr, ETH_ALEN);
10588c2ecf20Sopenharmony_ci	octeon_mgmt_set_mac_address(netdev, &sa);
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	octeon_mgmt_change_mtu(netdev, netdev->mtu);
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	/* Enable the port HW. Packets are not allowed until
10638c2ecf20Sopenharmony_ci	 * cvmx_mgmt_port_enable() is called.
10648c2ecf20Sopenharmony_ci	 */
10658c2ecf20Sopenharmony_ci	mix_ctl.u64 = 0;
10668c2ecf20Sopenharmony_ci	mix_ctl.s.crc_strip = 1;    /* Strip the ending CRC */
10678c2ecf20Sopenharmony_ci	mix_ctl.s.en = 1;           /* Enable the port */
10688c2ecf20Sopenharmony_ci	mix_ctl.s.nbtarb = 0;       /* Arbitration mode */
10698c2ecf20Sopenharmony_ci	/* MII CB-request FIFO programmable high watermark */
10708c2ecf20Sopenharmony_ci	mix_ctl.s.mrq_hwm = 1;
10718c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN
10728c2ecf20Sopenharmony_ci	mix_ctl.s.lendian = 1;
10738c2ecf20Sopenharmony_ci#endif
10748c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64);
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	/* Read the PHY to find the mode of the interface. */
10778c2ecf20Sopenharmony_ci	if (octeon_mgmt_init_phy(netdev)) {
10788c2ecf20Sopenharmony_ci		dev_err(p->dev, "Cannot initialize PHY on MIX%d.\n", p->port);
10798c2ecf20Sopenharmony_ci		goto err_noirq;
10808c2ecf20Sopenharmony_ci	}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	/* Set the mode of the interface, RGMII/MII. */
10838c2ecf20Sopenharmony_ci	if (OCTEON_IS_MODEL(OCTEON_CN6XXX) && netdev->phydev) {
10848c2ecf20Sopenharmony_ci		union cvmx_agl_prtx_ctl agl_prtx_ctl;
10858c2ecf20Sopenharmony_ci		int rgmii_mode =
10868c2ecf20Sopenharmony_ci			(linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
10878c2ecf20Sopenharmony_ci					   netdev->phydev->supported) |
10888c2ecf20Sopenharmony_ci			 linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
10898c2ecf20Sopenharmony_ci					   netdev->phydev->supported)) != 0;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci		agl_prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl);
10928c2ecf20Sopenharmony_ci		agl_prtx_ctl.s.mode = rgmii_mode ? 0 : 1;
10938c2ecf20Sopenharmony_ci		cvmx_write_csr(p->agl_prt_ctl,	agl_prtx_ctl.u64);
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci		/* MII clocks counts are based on the 125Mhz
10968c2ecf20Sopenharmony_ci		 * reference, which has an 8nS period. So our delays
10978c2ecf20Sopenharmony_ci		 * need to be multiplied by this factor.
10988c2ecf20Sopenharmony_ci		 */
10998c2ecf20Sopenharmony_ci#define NS_PER_PHY_CLK 8
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci		/* Take the DLL and clock tree out of reset */
11028c2ecf20Sopenharmony_ci		agl_prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl);
11038c2ecf20Sopenharmony_ci		agl_prtx_ctl.s.clkrst = 0;
11048c2ecf20Sopenharmony_ci		if (rgmii_mode) {
11058c2ecf20Sopenharmony_ci			agl_prtx_ctl.s.dllrst = 0;
11068c2ecf20Sopenharmony_ci			agl_prtx_ctl.s.clktx_byp = 0;
11078c2ecf20Sopenharmony_ci		}
11088c2ecf20Sopenharmony_ci		cvmx_write_csr(p->agl_prt_ctl,	agl_prtx_ctl.u64);
11098c2ecf20Sopenharmony_ci		cvmx_read_csr(p->agl_prt_ctl); /* Force write out before wait */
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci		/* Wait for the DLL to lock. External 125 MHz
11128c2ecf20Sopenharmony_ci		 * reference clock must be stable at this point.
11138c2ecf20Sopenharmony_ci		 */
11148c2ecf20Sopenharmony_ci		ndelay(256 * NS_PER_PHY_CLK);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci		/* Enable the interface */
11178c2ecf20Sopenharmony_ci		agl_prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl);
11188c2ecf20Sopenharmony_ci		agl_prtx_ctl.s.enable = 1;
11198c2ecf20Sopenharmony_ci		cvmx_write_csr(p->agl_prt_ctl, agl_prtx_ctl.u64);
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci		/* Read the value back to force the previous write */
11228c2ecf20Sopenharmony_ci		agl_prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci		/* Enable the compensation controller */
11258c2ecf20Sopenharmony_ci		agl_prtx_ctl.s.comp = 1;
11268c2ecf20Sopenharmony_ci		agl_prtx_ctl.s.drv_byp = 0;
11278c2ecf20Sopenharmony_ci		cvmx_write_csr(p->agl_prt_ctl,	agl_prtx_ctl.u64);
11288c2ecf20Sopenharmony_ci		/* Force write out before wait. */
11298c2ecf20Sopenharmony_ci		cvmx_read_csr(p->agl_prt_ctl);
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci		/* For compensation state to lock. */
11328c2ecf20Sopenharmony_ci		ndelay(1040 * NS_PER_PHY_CLK);
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci		/* Default Interframe Gaps are too small.  Recommended
11358c2ecf20Sopenharmony_ci		 * workaround is.
11368c2ecf20Sopenharmony_ci		 *
11378c2ecf20Sopenharmony_ci		 * AGL_GMX_TX_IFG[IFG1]=14
11388c2ecf20Sopenharmony_ci		 * AGL_GMX_TX_IFG[IFG2]=10
11398c2ecf20Sopenharmony_ci		 */
11408c2ecf20Sopenharmony_ci		cvmx_write_csr(CVMX_AGL_GMX_TX_IFG, 0xae);
11418c2ecf20Sopenharmony_ci	}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	octeon_mgmt_rx_fill_ring(netdev);
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	/* Clear statistics. */
11468c2ecf20Sopenharmony_ci	/* Clear on read. */
11478c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_CTL, 1);
11488c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_PKTS_DRP, 0);
11498c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_PKTS_BAD, 0);
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_TX_STATS_CTL, 1);
11528c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_TX_STAT0, 0);
11538c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_TX_STAT1, 0);
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	/* Clear any pending interrupts */
11568c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_ISR, cvmx_read_csr(p->mix + MIX_ISR));
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	if (request_irq(p->irq, octeon_mgmt_interrupt, 0, netdev->name,
11598c2ecf20Sopenharmony_ci			netdev)) {
11608c2ecf20Sopenharmony_ci		dev_err(p->dev, "request_irq(%d) failed.\n", p->irq);
11618c2ecf20Sopenharmony_ci		goto err_noirq;
11628c2ecf20Sopenharmony_ci	}
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	/* Interrupt every single RX packet */
11658c2ecf20Sopenharmony_ci	mix_irhwm.u64 = 0;
11668c2ecf20Sopenharmony_ci	mix_irhwm.s.irhwm = 0;
11678c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_IRHWM, mix_irhwm.u64);
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	/* Interrupt when we have 1 or more packets to clean.  */
11708c2ecf20Sopenharmony_ci	mix_orhwm.u64 = 0;
11718c2ecf20Sopenharmony_ci	mix_orhwm.s.orhwm = 0;
11728c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_ORHWM, mix_orhwm.u64);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	/* Enable receive and transmit interrupts */
11758c2ecf20Sopenharmony_ci	mix_intena.u64 = 0;
11768c2ecf20Sopenharmony_ci	mix_intena.s.ithena = 1;
11778c2ecf20Sopenharmony_ci	mix_intena.s.othena = 1;
11788c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64);
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	/* Enable packet I/O. */
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	rxx_frm_ctl.u64 = 0;
11838c2ecf20Sopenharmony_ci	rxx_frm_ctl.s.ptp_mode = p->has_rx_tstamp ? 1 : 0;
11848c2ecf20Sopenharmony_ci	rxx_frm_ctl.s.pre_align = 1;
11858c2ecf20Sopenharmony_ci	/* When set, disables the length check for non-min sized pkts
11868c2ecf20Sopenharmony_ci	 * with padding in the client data.
11878c2ecf20Sopenharmony_ci	 */
11888c2ecf20Sopenharmony_ci	rxx_frm_ctl.s.pad_len = 1;
11898c2ecf20Sopenharmony_ci	/* When set, disables the length check for VLAN pkts */
11908c2ecf20Sopenharmony_ci	rxx_frm_ctl.s.vlan_len = 1;
11918c2ecf20Sopenharmony_ci	/* When set, PREAMBLE checking is  less strict */
11928c2ecf20Sopenharmony_ci	rxx_frm_ctl.s.pre_free = 1;
11938c2ecf20Sopenharmony_ci	/* Control Pause Frames can match station SMAC */
11948c2ecf20Sopenharmony_ci	rxx_frm_ctl.s.ctl_smac = 0;
11958c2ecf20Sopenharmony_ci	/* Control Pause Frames can match globally assign Multicast address */
11968c2ecf20Sopenharmony_ci	rxx_frm_ctl.s.ctl_mcst = 1;
11978c2ecf20Sopenharmony_ci	/* Forward pause information to TX block */
11988c2ecf20Sopenharmony_ci	rxx_frm_ctl.s.ctl_bck = 1;
11998c2ecf20Sopenharmony_ci	/* Drop Control Pause Frames */
12008c2ecf20Sopenharmony_ci	rxx_frm_ctl.s.ctl_drp = 1;
12018c2ecf20Sopenharmony_ci	/* Strip off the preamble */
12028c2ecf20Sopenharmony_ci	rxx_frm_ctl.s.pre_strp = 1;
12038c2ecf20Sopenharmony_ci	/* This port is configured to send PREAMBLE+SFD to begin every
12048c2ecf20Sopenharmony_ci	 * frame.  GMX checks that the PREAMBLE is sent correctly.
12058c2ecf20Sopenharmony_ci	 */
12068c2ecf20Sopenharmony_ci	rxx_frm_ctl.s.pre_chk = 1;
12078c2ecf20Sopenharmony_ci	cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64);
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	/* Configure the port duplex, speed and enables */
12108c2ecf20Sopenharmony_ci	octeon_mgmt_disable_link(p);
12118c2ecf20Sopenharmony_ci	if (netdev->phydev)
12128c2ecf20Sopenharmony_ci		octeon_mgmt_update_link(p);
12138c2ecf20Sopenharmony_ci	octeon_mgmt_enable_link(p);
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	p->last_link = 0;
12168c2ecf20Sopenharmony_ci	p->last_speed = 0;
12178c2ecf20Sopenharmony_ci	/* PHY is not present in simulator. The carrier is enabled
12188c2ecf20Sopenharmony_ci	 * while initializing the phy for simulator, leave it enabled.
12198c2ecf20Sopenharmony_ci	 */
12208c2ecf20Sopenharmony_ci	if (netdev->phydev) {
12218c2ecf20Sopenharmony_ci		netif_carrier_off(netdev);
12228c2ecf20Sopenharmony_ci		phy_start(netdev->phydev);
12238c2ecf20Sopenharmony_ci	}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	netif_wake_queue(netdev);
12268c2ecf20Sopenharmony_ci	napi_enable(&p->napi);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	return 0;
12298c2ecf20Sopenharmony_cierr_noirq:
12308c2ecf20Sopenharmony_ci	octeon_mgmt_reset_hw(p);
12318c2ecf20Sopenharmony_ci	dma_unmap_single(p->dev, p->rx_ring_handle,
12328c2ecf20Sopenharmony_ci			 ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE),
12338c2ecf20Sopenharmony_ci			 DMA_BIDIRECTIONAL);
12348c2ecf20Sopenharmony_ci	kfree(p->rx_ring);
12358c2ecf20Sopenharmony_cierr_nomem:
12368c2ecf20Sopenharmony_ci	dma_unmap_single(p->dev, p->tx_ring_handle,
12378c2ecf20Sopenharmony_ci			 ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE),
12388c2ecf20Sopenharmony_ci			 DMA_BIDIRECTIONAL);
12398c2ecf20Sopenharmony_ci	kfree(p->tx_ring);
12408c2ecf20Sopenharmony_ci	return -ENOMEM;
12418c2ecf20Sopenharmony_ci}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_cistatic int octeon_mgmt_stop(struct net_device *netdev)
12448c2ecf20Sopenharmony_ci{
12458c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	napi_disable(&p->napi);
12488c2ecf20Sopenharmony_ci	netif_stop_queue(netdev);
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	if (netdev->phydev) {
12518c2ecf20Sopenharmony_ci		phy_stop(netdev->phydev);
12528c2ecf20Sopenharmony_ci		phy_disconnect(netdev->phydev);
12538c2ecf20Sopenharmony_ci	}
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	netif_carrier_off(netdev);
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	octeon_mgmt_reset_hw(p);
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	free_irq(p->irq, netdev);
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	/* dma_unmap is a nop on Octeon, so just free everything.  */
12628c2ecf20Sopenharmony_ci	skb_queue_purge(&p->tx_list);
12638c2ecf20Sopenharmony_ci	skb_queue_purge(&p->rx_list);
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	dma_unmap_single(p->dev, p->rx_ring_handle,
12668c2ecf20Sopenharmony_ci			 ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE),
12678c2ecf20Sopenharmony_ci			 DMA_BIDIRECTIONAL);
12688c2ecf20Sopenharmony_ci	kfree(p->rx_ring);
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	dma_unmap_single(p->dev, p->tx_ring_handle,
12718c2ecf20Sopenharmony_ci			 ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE),
12728c2ecf20Sopenharmony_ci			 DMA_BIDIRECTIONAL);
12738c2ecf20Sopenharmony_ci	kfree(p->tx_ring);
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	return 0;
12768c2ecf20Sopenharmony_ci}
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_cistatic netdev_tx_t
12798c2ecf20Sopenharmony_ciocteon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev)
12808c2ecf20Sopenharmony_ci{
12818c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
12828c2ecf20Sopenharmony_ci	union mgmt_port_ring_entry re;
12838c2ecf20Sopenharmony_ci	unsigned long flags;
12848c2ecf20Sopenharmony_ci	netdev_tx_t rv = NETDEV_TX_BUSY;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	re.d64 = 0;
12878c2ecf20Sopenharmony_ci	re.s.tstamp = ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) != 0);
12888c2ecf20Sopenharmony_ci	re.s.len = skb->len;
12898c2ecf20Sopenharmony_ci	re.s.addr = dma_map_single(p->dev, skb->data,
12908c2ecf20Sopenharmony_ci				   skb->len,
12918c2ecf20Sopenharmony_ci				   DMA_TO_DEVICE);
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	spin_lock_irqsave(&p->tx_list.lock, flags);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	if (unlikely(p->tx_current_fill >= ring_max_fill(OCTEON_MGMT_TX_RING_SIZE) - 1)) {
12968c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&p->tx_list.lock, flags);
12978c2ecf20Sopenharmony_ci		netif_stop_queue(netdev);
12988c2ecf20Sopenharmony_ci		spin_lock_irqsave(&p->tx_list.lock, flags);
12998c2ecf20Sopenharmony_ci	}
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	if (unlikely(p->tx_current_fill >=
13028c2ecf20Sopenharmony_ci		     ring_max_fill(OCTEON_MGMT_TX_RING_SIZE))) {
13038c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&p->tx_list.lock, flags);
13048c2ecf20Sopenharmony_ci		dma_unmap_single(p->dev, re.s.addr, re.s.len,
13058c2ecf20Sopenharmony_ci				 DMA_TO_DEVICE);
13068c2ecf20Sopenharmony_ci		goto out;
13078c2ecf20Sopenharmony_ci	}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	__skb_queue_tail(&p->tx_list, skb);
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	/* Put it in the ring.  */
13128c2ecf20Sopenharmony_ci	p->tx_ring[p->tx_next] = re.d64;
13138c2ecf20Sopenharmony_ci	p->tx_next = (p->tx_next + 1) % OCTEON_MGMT_TX_RING_SIZE;
13148c2ecf20Sopenharmony_ci	p->tx_current_fill++;
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&p->tx_list.lock, flags);
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	dma_sync_single_for_device(p->dev, p->tx_ring_handle,
13198c2ecf20Sopenharmony_ci				   ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE),
13208c2ecf20Sopenharmony_ci				   DMA_BIDIRECTIONAL);
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	netdev->stats.tx_packets++;
13238c2ecf20Sopenharmony_ci	netdev->stats.tx_bytes += skb->len;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	/* Ring the bell.  */
13268c2ecf20Sopenharmony_ci	cvmx_write_csr(p->mix + MIX_ORING2, 1);
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	netif_trans_update(netdev);
13298c2ecf20Sopenharmony_ci	rv = NETDEV_TX_OK;
13308c2ecf20Sopenharmony_ciout:
13318c2ecf20Sopenharmony_ci	octeon_mgmt_update_tx_stats(netdev);
13328c2ecf20Sopenharmony_ci	return rv;
13338c2ecf20Sopenharmony_ci}
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
13368c2ecf20Sopenharmony_cistatic void octeon_mgmt_poll_controller(struct net_device *netdev)
13378c2ecf20Sopenharmony_ci{
13388c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	octeon_mgmt_receive_packets(p, 16);
13418c2ecf20Sopenharmony_ci	octeon_mgmt_update_rx_stats(netdev);
13428c2ecf20Sopenharmony_ci}
13438c2ecf20Sopenharmony_ci#endif
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_cistatic void octeon_mgmt_get_drvinfo(struct net_device *netdev,
13468c2ecf20Sopenharmony_ci				    struct ethtool_drvinfo *info)
13478c2ecf20Sopenharmony_ci{
13488c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
13498c2ecf20Sopenharmony_ci}
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_cistatic int octeon_mgmt_nway_reset(struct net_device *dev)
13528c2ecf20Sopenharmony_ci{
13538c2ecf20Sopenharmony_ci	if (!capable(CAP_NET_ADMIN))
13548c2ecf20Sopenharmony_ci		return -EPERM;
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	if (dev->phydev)
13578c2ecf20Sopenharmony_ci		return phy_start_aneg(dev->phydev);
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
13608c2ecf20Sopenharmony_ci}
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_cistatic const struct ethtool_ops octeon_mgmt_ethtool_ops = {
13638c2ecf20Sopenharmony_ci	.get_drvinfo = octeon_mgmt_get_drvinfo,
13648c2ecf20Sopenharmony_ci	.nway_reset = octeon_mgmt_nway_reset,
13658c2ecf20Sopenharmony_ci	.get_link = ethtool_op_get_link,
13668c2ecf20Sopenharmony_ci	.get_link_ksettings = phy_ethtool_get_link_ksettings,
13678c2ecf20Sopenharmony_ci	.set_link_ksettings = phy_ethtool_set_link_ksettings,
13688c2ecf20Sopenharmony_ci};
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_cistatic const struct net_device_ops octeon_mgmt_ops = {
13718c2ecf20Sopenharmony_ci	.ndo_open =			octeon_mgmt_open,
13728c2ecf20Sopenharmony_ci	.ndo_stop =			octeon_mgmt_stop,
13738c2ecf20Sopenharmony_ci	.ndo_start_xmit =		octeon_mgmt_xmit,
13748c2ecf20Sopenharmony_ci	.ndo_set_rx_mode =		octeon_mgmt_set_rx_filtering,
13758c2ecf20Sopenharmony_ci	.ndo_set_mac_address =		octeon_mgmt_set_mac_address,
13768c2ecf20Sopenharmony_ci	.ndo_do_ioctl =			octeon_mgmt_ioctl,
13778c2ecf20Sopenharmony_ci	.ndo_change_mtu =		octeon_mgmt_change_mtu,
13788c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
13798c2ecf20Sopenharmony_ci	.ndo_poll_controller =		octeon_mgmt_poll_controller,
13808c2ecf20Sopenharmony_ci#endif
13818c2ecf20Sopenharmony_ci};
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_cistatic int octeon_mgmt_probe(struct platform_device *pdev)
13848c2ecf20Sopenharmony_ci{
13858c2ecf20Sopenharmony_ci	struct net_device *netdev;
13868c2ecf20Sopenharmony_ci	struct octeon_mgmt *p;
13878c2ecf20Sopenharmony_ci	const __be32 *data;
13888c2ecf20Sopenharmony_ci	const u8 *mac;
13898c2ecf20Sopenharmony_ci	struct resource *res_mix;
13908c2ecf20Sopenharmony_ci	struct resource *res_agl;
13918c2ecf20Sopenharmony_ci	struct resource *res_agl_prt_ctl;
13928c2ecf20Sopenharmony_ci	int len;
13938c2ecf20Sopenharmony_ci	int result;
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	netdev = alloc_etherdev(sizeof(struct octeon_mgmt));
13968c2ecf20Sopenharmony_ci	if (netdev == NULL)
13978c2ecf20Sopenharmony_ci		return -ENOMEM;
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(netdev, &pdev->dev);
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, netdev);
14028c2ecf20Sopenharmony_ci	p = netdev_priv(netdev);
14038c2ecf20Sopenharmony_ci	netif_napi_add(netdev, &p->napi, octeon_mgmt_napi_poll,
14048c2ecf20Sopenharmony_ci		       OCTEON_MGMT_NAPI_WEIGHT);
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	p->netdev = netdev;
14078c2ecf20Sopenharmony_ci	p->dev = &pdev->dev;
14088c2ecf20Sopenharmony_ci	p->has_rx_tstamp = false;
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	data = of_get_property(pdev->dev.of_node, "cell-index", &len);
14118c2ecf20Sopenharmony_ci	if (data && len == sizeof(*data)) {
14128c2ecf20Sopenharmony_ci		p->port = be32_to_cpup(data);
14138c2ecf20Sopenharmony_ci	} else {
14148c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no 'cell-index' property\n");
14158c2ecf20Sopenharmony_ci		result = -ENXIO;
14168c2ecf20Sopenharmony_ci		goto err;
14178c2ecf20Sopenharmony_ci	}
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	snprintf(netdev->name, IFNAMSIZ, "mgmt%d", p->port);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	result = platform_get_irq(pdev, 0);
14228c2ecf20Sopenharmony_ci	if (result < 0)
14238c2ecf20Sopenharmony_ci		goto err;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	p->irq = result;
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	res_mix = platform_get_resource(pdev, IORESOURCE_MEM, 0);
14288c2ecf20Sopenharmony_ci	if (res_mix == NULL) {
14298c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no 'reg' resource\n");
14308c2ecf20Sopenharmony_ci		result = -ENXIO;
14318c2ecf20Sopenharmony_ci		goto err;
14328c2ecf20Sopenharmony_ci	}
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	res_agl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
14358c2ecf20Sopenharmony_ci	if (res_agl == NULL) {
14368c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no 'reg' resource\n");
14378c2ecf20Sopenharmony_ci		result = -ENXIO;
14388c2ecf20Sopenharmony_ci		goto err;
14398c2ecf20Sopenharmony_ci	}
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	res_agl_prt_ctl = platform_get_resource(pdev, IORESOURCE_MEM, 3);
14428c2ecf20Sopenharmony_ci	if (res_agl_prt_ctl == NULL) {
14438c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no 'reg' resource\n");
14448c2ecf20Sopenharmony_ci		result = -ENXIO;
14458c2ecf20Sopenharmony_ci		goto err;
14468c2ecf20Sopenharmony_ci	}
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	p->mix_phys = res_mix->start;
14498c2ecf20Sopenharmony_ci	p->mix_size = resource_size(res_mix);
14508c2ecf20Sopenharmony_ci	p->agl_phys = res_agl->start;
14518c2ecf20Sopenharmony_ci	p->agl_size = resource_size(res_agl);
14528c2ecf20Sopenharmony_ci	p->agl_prt_ctl_phys = res_agl_prt_ctl->start;
14538c2ecf20Sopenharmony_ci	p->agl_prt_ctl_size = resource_size(res_agl_prt_ctl);
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	if (!devm_request_mem_region(&pdev->dev, p->mix_phys, p->mix_size,
14578c2ecf20Sopenharmony_ci				     res_mix->name)) {
14588c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "request_mem_region (%s) failed\n",
14598c2ecf20Sopenharmony_ci			res_mix->name);
14608c2ecf20Sopenharmony_ci		result = -ENXIO;
14618c2ecf20Sopenharmony_ci		goto err;
14628c2ecf20Sopenharmony_ci	}
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	if (!devm_request_mem_region(&pdev->dev, p->agl_phys, p->agl_size,
14658c2ecf20Sopenharmony_ci				     res_agl->name)) {
14668c2ecf20Sopenharmony_ci		result = -ENXIO;
14678c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "request_mem_region (%s) failed\n",
14688c2ecf20Sopenharmony_ci			res_agl->name);
14698c2ecf20Sopenharmony_ci		goto err;
14708c2ecf20Sopenharmony_ci	}
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci	if (!devm_request_mem_region(&pdev->dev, p->agl_prt_ctl_phys,
14738c2ecf20Sopenharmony_ci				     p->agl_prt_ctl_size, res_agl_prt_ctl->name)) {
14748c2ecf20Sopenharmony_ci		result = -ENXIO;
14758c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "request_mem_region (%s) failed\n",
14768c2ecf20Sopenharmony_ci			res_agl_prt_ctl->name);
14778c2ecf20Sopenharmony_ci		goto err;
14788c2ecf20Sopenharmony_ci	}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	p->mix = (u64)devm_ioremap(&pdev->dev, p->mix_phys, p->mix_size);
14818c2ecf20Sopenharmony_ci	p->agl = (u64)devm_ioremap(&pdev->dev, p->agl_phys, p->agl_size);
14828c2ecf20Sopenharmony_ci	p->agl_prt_ctl = (u64)devm_ioremap(&pdev->dev, p->agl_prt_ctl_phys,
14838c2ecf20Sopenharmony_ci					   p->agl_prt_ctl_size);
14848c2ecf20Sopenharmony_ci	if (!p->mix || !p->agl || !p->agl_prt_ctl) {
14858c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to map I/O memory\n");
14868c2ecf20Sopenharmony_ci		result = -ENOMEM;
14878c2ecf20Sopenharmony_ci		goto err;
14888c2ecf20Sopenharmony_ci	}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	spin_lock_init(&p->lock);
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	skb_queue_head_init(&p->tx_list);
14938c2ecf20Sopenharmony_ci	skb_queue_head_init(&p->rx_list);
14948c2ecf20Sopenharmony_ci	tasklet_setup(&p->tx_clean_tasklet,
14958c2ecf20Sopenharmony_ci		      octeon_mgmt_clean_tx_tasklet);
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	netdev->priv_flags |= IFF_UNICAST_FLT;
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	netdev->netdev_ops = &octeon_mgmt_ops;
15008c2ecf20Sopenharmony_ci	netdev->ethtool_ops = &octeon_mgmt_ethtool_ops;
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	netdev->min_mtu = 64 - OCTEON_MGMT_RX_HEADROOM;
15038c2ecf20Sopenharmony_ci	netdev->max_mtu = 16383 - OCTEON_MGMT_RX_HEADROOM - VLAN_HLEN;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	mac = of_get_mac_address(pdev->dev.of_node);
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	if (!IS_ERR(mac))
15088c2ecf20Sopenharmony_ci		ether_addr_copy(netdev->dev_addr, mac);
15098c2ecf20Sopenharmony_ci	else
15108c2ecf20Sopenharmony_ci		eth_hw_addr_random(netdev);
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	p->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	result = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
15158c2ecf20Sopenharmony_ci	if (result)
15168c2ecf20Sopenharmony_ci		goto err;
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	netif_carrier_off(netdev);
15198c2ecf20Sopenharmony_ci	result = register_netdev(netdev);
15208c2ecf20Sopenharmony_ci	if (result)
15218c2ecf20Sopenharmony_ci		goto err;
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	return 0;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_cierr:
15268c2ecf20Sopenharmony_ci	of_node_put(p->phy_np);
15278c2ecf20Sopenharmony_ci	free_netdev(netdev);
15288c2ecf20Sopenharmony_ci	return result;
15298c2ecf20Sopenharmony_ci}
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_cistatic int octeon_mgmt_remove(struct platform_device *pdev)
15328c2ecf20Sopenharmony_ci{
15338c2ecf20Sopenharmony_ci	struct net_device *netdev = platform_get_drvdata(pdev);
15348c2ecf20Sopenharmony_ci	struct octeon_mgmt *p = netdev_priv(netdev);
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	unregister_netdev(netdev);
15378c2ecf20Sopenharmony_ci	of_node_put(p->phy_np);
15388c2ecf20Sopenharmony_ci	free_netdev(netdev);
15398c2ecf20Sopenharmony_ci	return 0;
15408c2ecf20Sopenharmony_ci}
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_cistatic const struct of_device_id octeon_mgmt_match[] = {
15438c2ecf20Sopenharmony_ci	{
15448c2ecf20Sopenharmony_ci		.compatible = "cavium,octeon-5750-mix",
15458c2ecf20Sopenharmony_ci	},
15468c2ecf20Sopenharmony_ci	{},
15478c2ecf20Sopenharmony_ci};
15488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, octeon_mgmt_match);
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_cistatic struct platform_driver octeon_mgmt_driver = {
15518c2ecf20Sopenharmony_ci	.driver = {
15528c2ecf20Sopenharmony_ci		.name		= "octeon_mgmt",
15538c2ecf20Sopenharmony_ci		.of_match_table = octeon_mgmt_match,
15548c2ecf20Sopenharmony_ci	},
15558c2ecf20Sopenharmony_ci	.probe		= octeon_mgmt_probe,
15568c2ecf20Sopenharmony_ci	.remove		= octeon_mgmt_remove,
15578c2ecf20Sopenharmony_ci};
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_cistatic int __init octeon_mgmt_mod_init(void)
15608c2ecf20Sopenharmony_ci{
15618c2ecf20Sopenharmony_ci	return platform_driver_register(&octeon_mgmt_driver);
15628c2ecf20Sopenharmony_ci}
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_cistatic void __exit octeon_mgmt_mod_exit(void)
15658c2ecf20Sopenharmony_ci{
15668c2ecf20Sopenharmony_ci	platform_driver_unregister(&octeon_mgmt_driver);
15678c2ecf20Sopenharmony_ci}
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_cimodule_init(octeon_mgmt_mod_init);
15708c2ecf20Sopenharmony_cimodule_exit(octeon_mgmt_mod_exit);
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ciMODULE_SOFTDEP("pre: mdio-cavium");
15738c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION);
15748c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Daney");
15758c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1576