diff options
Diffstat (limited to 'drivers/net/octeon/octeon_mgmt.c')
| -rw-r--r-- | drivers/net/octeon/octeon_mgmt.c | 1165 | 
1 files changed, 0 insertions, 1165 deletions
diff --git a/drivers/net/octeon/octeon_mgmt.c b/drivers/net/octeon/octeon_mgmt.c deleted file mode 100644 index b264f0f4560..00000000000 --- a/drivers/net/octeon/octeon_mgmt.c +++ /dev/null @@ -1,1165 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License.  See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2009 Cavium Networks - */ - -#include <linux/capability.h> -#include <linux/dma-mapping.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/if_vlan.h> -#include <linux/slab.h> -#include <linux/phy.h> -#include <linux/spinlock.h> - -#include <asm/octeon/octeon.h> -#include <asm/octeon/cvmx-mixx-defs.h> -#include <asm/octeon/cvmx-agl-defs.h> - -#define DRV_NAME "octeon_mgmt" -#define DRV_VERSION "2.0" -#define DRV_DESCRIPTION \ -	"Cavium Networks Octeon MII (management) port Network Driver" - -#define OCTEON_MGMT_NAPI_WEIGHT 16 - -/* - * Ring sizes that are powers of two allow for more efficient modulo - * opertions. - */ -#define OCTEON_MGMT_RX_RING_SIZE 512 -#define OCTEON_MGMT_TX_RING_SIZE 128 - -/* Allow 8 bytes for vlan and FCS. */ -#define OCTEON_MGMT_RX_HEADROOM (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN) - -union mgmt_port_ring_entry { -	u64 d64; -	struct { -		u64    reserved_62_63:2; -		/* Length of the buffer/packet in bytes */ -		u64    len:14; -		/* For TX, signals that the packet should be timestamped */ -		u64    tstamp:1; -		/* The RX error code */ -		u64    code:7; -#define RING_ENTRY_CODE_DONE 0xf -#define RING_ENTRY_CODE_MORE 0x10 -		/* Physical address of the buffer */ -		u64    addr:40; -	} s; -}; - -struct octeon_mgmt { -	struct net_device *netdev; -	int port; -	int irq; -	u64 *tx_ring; -	dma_addr_t tx_ring_handle; -	unsigned int tx_next; -	unsigned int tx_next_clean; -	unsigned int tx_current_fill; -	/* The tx_list lock also protects the ring related variables */ -	struct sk_buff_head tx_list; - -	/* RX variables only touched in napi_poll.  No locking necessary. */ -	u64 *rx_ring; -	dma_addr_t rx_ring_handle; -	unsigned int rx_next; -	unsigned int rx_next_fill; -	unsigned int rx_current_fill; -	struct sk_buff_head rx_list; - -	spinlock_t lock; -	unsigned int last_duplex; -	unsigned int last_link; -	struct device *dev; -	struct napi_struct napi; -	struct tasklet_struct tx_clean_tasklet; -	struct phy_device *phydev; -}; - -static void octeon_mgmt_set_rx_irq(struct octeon_mgmt *p, int enable) -{ -	int port = p->port; -	union cvmx_mixx_intena mix_intena; -	unsigned long flags; - -	spin_lock_irqsave(&p->lock, flags); -	mix_intena.u64 = cvmx_read_csr(CVMX_MIXX_INTENA(port)); -	mix_intena.s.ithena = enable ? 1 : 0; -	cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64); -	spin_unlock_irqrestore(&p->lock, flags); -} - -static void octeon_mgmt_set_tx_irq(struct octeon_mgmt *p, int enable) -{ -	int port = p->port; -	union cvmx_mixx_intena mix_intena; -	unsigned long flags; - -	spin_lock_irqsave(&p->lock, flags); -	mix_intena.u64 = cvmx_read_csr(CVMX_MIXX_INTENA(port)); -	mix_intena.s.othena = enable ? 1 : 0; -	cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64); -	spin_unlock_irqrestore(&p->lock, flags); -} - -static inline void octeon_mgmt_enable_rx_irq(struct octeon_mgmt *p) -{ -	octeon_mgmt_set_rx_irq(p, 1); -} - -static inline void octeon_mgmt_disable_rx_irq(struct octeon_mgmt *p) -{ -	octeon_mgmt_set_rx_irq(p, 0); -} - -static inline void octeon_mgmt_enable_tx_irq(struct octeon_mgmt *p) -{ -	octeon_mgmt_set_tx_irq(p, 1); -} - -static inline void octeon_mgmt_disable_tx_irq(struct octeon_mgmt *p) -{ -	octeon_mgmt_set_tx_irq(p, 0); -} - -static unsigned int ring_max_fill(unsigned int ring_size) -{ -	return ring_size - 8; -} - -static unsigned int ring_size_to_bytes(unsigned int ring_size) -{ -	return ring_size * sizeof(union mgmt_port_ring_entry); -} - -static void octeon_mgmt_rx_fill_ring(struct net_device *netdev) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); -	int port = p->port; - -	while (p->rx_current_fill < ring_max_fill(OCTEON_MGMT_RX_RING_SIZE)) { -		unsigned int size; -		union mgmt_port_ring_entry re; -		struct sk_buff *skb; - -		/* CN56XX pass 1 needs 8 bytes of padding.  */ -		size = netdev->mtu + OCTEON_MGMT_RX_HEADROOM + 8 + NET_IP_ALIGN; - -		skb = netdev_alloc_skb(netdev, size); -		if (!skb) -			break; -		skb_reserve(skb, NET_IP_ALIGN); -		__skb_queue_tail(&p->rx_list, skb); - -		re.d64 = 0; -		re.s.len = size; -		re.s.addr = dma_map_single(p->dev, skb->data, -					   size, -					   DMA_FROM_DEVICE); - -		/* Put it in the ring.  */ -		p->rx_ring[p->rx_next_fill] = re.d64; -		dma_sync_single_for_device(p->dev, p->rx_ring_handle, -					   ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), -					   DMA_BIDIRECTIONAL); -		p->rx_next_fill = -			(p->rx_next_fill + 1) % OCTEON_MGMT_RX_RING_SIZE; -		p->rx_current_fill++; -		/* Ring the bell.  */ -		cvmx_write_csr(CVMX_MIXX_IRING2(port), 1); -	} -} - -static void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p) -{ -	int port = p->port; -	union cvmx_mixx_orcnt mix_orcnt; -	union mgmt_port_ring_entry re; -	struct sk_buff *skb; -	int cleaned = 0; -	unsigned long flags; - -	mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port)); -	while (mix_orcnt.s.orcnt) { -		spin_lock_irqsave(&p->tx_list.lock, flags); - -		mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port)); - -		if (mix_orcnt.s.orcnt == 0) { -			spin_unlock_irqrestore(&p->tx_list.lock, flags); -			break; -		} - -		dma_sync_single_for_cpu(p->dev, p->tx_ring_handle, -					ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), -					DMA_BIDIRECTIONAL); - -		re.d64 = p->tx_ring[p->tx_next_clean]; -		p->tx_next_clean = -			(p->tx_next_clean + 1) % OCTEON_MGMT_TX_RING_SIZE; -		skb = __skb_dequeue(&p->tx_list); - -		mix_orcnt.u64 = 0; -		mix_orcnt.s.orcnt = 1; - -		/* Acknowledge to hardware that we have the buffer.  */ -		cvmx_write_csr(CVMX_MIXX_ORCNT(port), mix_orcnt.u64); -		p->tx_current_fill--; - -		spin_unlock_irqrestore(&p->tx_list.lock, flags); - -		dma_unmap_single(p->dev, re.s.addr, re.s.len, -				 DMA_TO_DEVICE); -		dev_kfree_skb_any(skb); -		cleaned++; - -		mix_orcnt.u64 = cvmx_read_csr(CVMX_MIXX_ORCNT(port)); -	} - -	if (cleaned && netif_queue_stopped(p->netdev)) -		netif_wake_queue(p->netdev); -} - -static void octeon_mgmt_clean_tx_tasklet(unsigned long arg) -{ -	struct octeon_mgmt *p = (struct octeon_mgmt *)arg; -	octeon_mgmt_clean_tx_buffers(p); -	octeon_mgmt_enable_tx_irq(p); -} - -static void octeon_mgmt_update_rx_stats(struct net_device *netdev) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); -	int port = p->port; -	unsigned long flags; -	u64 drop, bad; - -	/* These reads also clear the count registers.  */ -	drop = cvmx_read_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_DRP(port)); -	bad = cvmx_read_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_BAD(port)); - -	if (drop || bad) { -		/* Do an atomic update. */ -		spin_lock_irqsave(&p->lock, flags); -		netdev->stats.rx_errors += bad; -		netdev->stats.rx_dropped += drop; -		spin_unlock_irqrestore(&p->lock, flags); -	} -} - -static void octeon_mgmt_update_tx_stats(struct net_device *netdev) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); -	int port = p->port; -	unsigned long flags; - -	union cvmx_agl_gmx_txx_stat0 s0; -	union cvmx_agl_gmx_txx_stat1 s1; - -	/* These reads also clear the count registers.  */ -	s0.u64 = cvmx_read_csr(CVMX_AGL_GMX_TXX_STAT0(port)); -	s1.u64 = cvmx_read_csr(CVMX_AGL_GMX_TXX_STAT1(port)); - -	if (s0.s.xsdef || s0.s.xscol || s1.s.scol || s1.s.mcol) { -		/* Do an atomic update. */ -		spin_lock_irqsave(&p->lock, flags); -		netdev->stats.tx_errors += s0.s.xsdef + s0.s.xscol; -		netdev->stats.collisions += s1.s.scol + s1.s.mcol; -		spin_unlock_irqrestore(&p->lock, flags); -	} -} - -/* - * Dequeue a receive skb and its corresponding ring entry.  The ring - * entry is returned, *pskb is updated to point to the skb. - */ -static u64 octeon_mgmt_dequeue_rx_buffer(struct octeon_mgmt *p, -					 struct sk_buff **pskb) -{ -	union mgmt_port_ring_entry re; - -	dma_sync_single_for_cpu(p->dev, p->rx_ring_handle, -				ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), -				DMA_BIDIRECTIONAL); - -	re.d64 = p->rx_ring[p->rx_next]; -	p->rx_next = (p->rx_next + 1) % OCTEON_MGMT_RX_RING_SIZE; -	p->rx_current_fill--; -	*pskb = __skb_dequeue(&p->rx_list); - -	dma_unmap_single(p->dev, re.s.addr, -			 ETH_FRAME_LEN + OCTEON_MGMT_RX_HEADROOM, -			 DMA_FROM_DEVICE); - -	return re.d64; -} - - -static int octeon_mgmt_receive_one(struct octeon_mgmt *p) -{ -	int port = p->port; -	struct net_device *netdev = p->netdev; -	union cvmx_mixx_ircnt mix_ircnt; -	union mgmt_port_ring_entry re; -	struct sk_buff *skb; -	struct sk_buff *skb2; -	struct sk_buff *skb_new; -	union mgmt_port_ring_entry re2; -	int rc = 1; - - -	re.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb); -	if (likely(re.s.code == RING_ENTRY_CODE_DONE)) { -		/* A good packet, send it up. */ -		skb_put(skb, re.s.len); -good: -		skb->protocol = eth_type_trans(skb, netdev); -		netdev->stats.rx_packets++; -		netdev->stats.rx_bytes += skb->len; -		netif_receive_skb(skb); -		rc = 0; -	} else if (re.s.code == RING_ENTRY_CODE_MORE) { -		/* -		 * Packet split across skbs.  This can happen if we -		 * increase the MTU.  Buffers that are already in the -		 * rx ring can then end up being too small.  As the rx -		 * ring is refilled, buffers sized for the new MTU -		 * will be used and we should go back to the normal -		 * non-split case. -		 */ -		skb_put(skb, re.s.len); -		do { -			re2.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb2); -			if (re2.s.code != RING_ENTRY_CODE_MORE -				&& re2.s.code != RING_ENTRY_CODE_DONE) -				goto split_error; -			skb_put(skb2,  re2.s.len); -			skb_new = skb_copy_expand(skb, 0, skb2->len, -						  GFP_ATOMIC); -			if (!skb_new) -				goto split_error; -			if (skb_copy_bits(skb2, 0, skb_tail_pointer(skb_new), -					  skb2->len)) -				goto split_error; -			skb_put(skb_new, skb2->len); -			dev_kfree_skb_any(skb); -			dev_kfree_skb_any(skb2); -			skb = skb_new; -		} while (re2.s.code == RING_ENTRY_CODE_MORE); -		goto good; -	} else { -		/* Some other error, discard it. */ -		dev_kfree_skb_any(skb); -		/* -		 * Error statistics are accumulated in -		 * octeon_mgmt_update_rx_stats. -		 */ -	} -	goto done; -split_error: -	/* Discard the whole mess. */ -	dev_kfree_skb_any(skb); -	dev_kfree_skb_any(skb2); -	while (re2.s.code == RING_ENTRY_CODE_MORE) { -		re2.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb2); -		dev_kfree_skb_any(skb2); -	} -	netdev->stats.rx_errors++; - -done: -	/* Tell the hardware we processed a packet.  */ -	mix_ircnt.u64 = 0; -	mix_ircnt.s.ircnt = 1; -	cvmx_write_csr(CVMX_MIXX_IRCNT(port), mix_ircnt.u64); -	return rc; -} - -static int octeon_mgmt_receive_packets(struct octeon_mgmt *p, int budget) -{ -	int port = p->port; -	unsigned int work_done = 0; -	union cvmx_mixx_ircnt mix_ircnt; -	int rc; - -	mix_ircnt.u64 = cvmx_read_csr(CVMX_MIXX_IRCNT(port)); -	while (work_done < budget && mix_ircnt.s.ircnt) { - -		rc = octeon_mgmt_receive_one(p); -		if (!rc) -			work_done++; - -		/* Check for more packets. */ -		mix_ircnt.u64 = cvmx_read_csr(CVMX_MIXX_IRCNT(port)); -	} - -	octeon_mgmt_rx_fill_ring(p->netdev); - -	return work_done; -} - -static int octeon_mgmt_napi_poll(struct napi_struct *napi, int budget) -{ -	struct octeon_mgmt *p = container_of(napi, struct octeon_mgmt, napi); -	struct net_device *netdev = p->netdev; -	unsigned int work_done = 0; - -	work_done = octeon_mgmt_receive_packets(p, budget); - -	if (work_done < budget) { -		/* We stopped because no more packets were available. */ -		napi_complete(napi); -		octeon_mgmt_enable_rx_irq(p); -	} -	octeon_mgmt_update_rx_stats(netdev); - -	return work_done; -} - -/* Reset the hardware to clean state.  */ -static void octeon_mgmt_reset_hw(struct octeon_mgmt *p) -{ -	union cvmx_mixx_ctl mix_ctl; -	union cvmx_mixx_bist mix_bist; -	union cvmx_agl_gmx_bist agl_gmx_bist; - -	mix_ctl.u64 = 0; -	cvmx_write_csr(CVMX_MIXX_CTL(p->port), mix_ctl.u64); -	do { -		mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(p->port)); -	} while (mix_ctl.s.busy); -	mix_ctl.s.reset = 1; -	cvmx_write_csr(CVMX_MIXX_CTL(p->port), mix_ctl.u64); -	cvmx_read_csr(CVMX_MIXX_CTL(p->port)); -	cvmx_wait(64); - -	mix_bist.u64 = cvmx_read_csr(CVMX_MIXX_BIST(p->port)); -	if (mix_bist.u64) -		dev_warn(p->dev, "MIX failed BIST (0x%016llx)\n", -			(unsigned long long)mix_bist.u64); - -	agl_gmx_bist.u64 = cvmx_read_csr(CVMX_AGL_GMX_BIST); -	if (agl_gmx_bist.u64) -		dev_warn(p->dev, "AGL failed BIST (0x%016llx)\n", -			 (unsigned long long)agl_gmx_bist.u64); -} - -struct octeon_mgmt_cam_state { -	u64 cam[6]; -	u64 cam_mask; -	int cam_index; -}; - -static void octeon_mgmt_cam_state_add(struct octeon_mgmt_cam_state *cs, -				      unsigned char *addr) -{ -	int i; - -	for (i = 0; i < 6; i++) -		cs->cam[i] |= (u64)addr[i] << (8 * (cs->cam_index)); -	cs->cam_mask |= (1ULL << cs->cam_index); -	cs->cam_index++; -} - -static void octeon_mgmt_set_rx_filtering(struct net_device *netdev) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); -	int port = p->port; -	union cvmx_agl_gmx_rxx_adr_ctl adr_ctl; -	union cvmx_agl_gmx_prtx_cfg agl_gmx_prtx; -	unsigned long flags; -	unsigned int prev_packet_enable; -	unsigned int cam_mode = 1; /* 1 - Accept on CAM match */ -	unsigned int multicast_mode = 1; /* 1 - Reject all multicast.  */ -	struct octeon_mgmt_cam_state cam_state; -	struct netdev_hw_addr *ha; -	int available_cam_entries; - -	memset(&cam_state, 0, sizeof(cam_state)); - -	if ((netdev->flags & IFF_PROMISC) || netdev->uc.count > 7) { -		cam_mode = 0; -		available_cam_entries = 8; -	} else { -		/* -		 * One CAM entry for the primary address, leaves seven -		 * for the secondary addresses. -		 */ -		available_cam_entries = 7 - netdev->uc.count; -	} - -	if (netdev->flags & IFF_MULTICAST) { -		if (cam_mode == 0 || (netdev->flags & IFF_ALLMULTI) || -		    netdev_mc_count(netdev) > available_cam_entries) -			multicast_mode = 2; /* 2 - Accept all multicast.  */ -		else -			multicast_mode = 0; /* 0 - Use CAM.  */ -	} - -	if (cam_mode == 1) { -		/* Add primary address. */ -		octeon_mgmt_cam_state_add(&cam_state, netdev->dev_addr); -		netdev_for_each_uc_addr(ha, netdev) -			octeon_mgmt_cam_state_add(&cam_state, ha->addr); -	} -	if (multicast_mode == 0) { -		netdev_for_each_mc_addr(ha, netdev) -			octeon_mgmt_cam_state_add(&cam_state, ha->addr); -	} - -	spin_lock_irqsave(&p->lock, flags); - -	/* Disable packet I/O. */ -	agl_gmx_prtx.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); -	prev_packet_enable = agl_gmx_prtx.s.en; -	agl_gmx_prtx.s.en = 0; -	cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); - -	adr_ctl.u64 = 0; -	adr_ctl.s.cam_mode = cam_mode; -	adr_ctl.s.mcst = multicast_mode; -	adr_ctl.s.bcst = 1;     /* Allow broadcast */ - -	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CTL(port), adr_ctl.u64); - -	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM0(port), cam_state.cam[0]); -	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM1(port), cam_state.cam[1]); -	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM2(port), cam_state.cam[2]); -	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM3(port), cam_state.cam[3]); -	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM4(port), cam_state.cam[4]); -	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM5(port), cam_state.cam[5]); -	cvmx_write_csr(CVMX_AGL_GMX_RXX_ADR_CAM_EN(port), cam_state.cam_mask); - -	/* Restore packet I/O. */ -	agl_gmx_prtx.s.en = prev_packet_enable; -	cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), agl_gmx_prtx.u64); - -	spin_unlock_irqrestore(&p->lock, flags); -} - -static int octeon_mgmt_set_mac_address(struct net_device *netdev, void *addr) -{ -	struct sockaddr *sa = addr; - -	if (!is_valid_ether_addr(sa->sa_data)) -		return -EADDRNOTAVAIL; - -	memcpy(netdev->dev_addr, sa->sa_data, ETH_ALEN); - -	octeon_mgmt_set_rx_filtering(netdev); - -	return 0; -} - -static int octeon_mgmt_change_mtu(struct net_device *netdev, int new_mtu) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); -	int port = p->port; -	int size_without_fcs = new_mtu + OCTEON_MGMT_RX_HEADROOM; - -	/* -	 * Limit the MTU to make sure the ethernet packets are between -	 * 64 bytes and 16383 bytes. -	 */ -	if (size_without_fcs < 64 || size_without_fcs > 16383) { -		dev_warn(p->dev, "MTU must be between %d and %d.\n", -			 64 - OCTEON_MGMT_RX_HEADROOM, -			 16383 - OCTEON_MGMT_RX_HEADROOM); -		return -EINVAL; -	} - -	netdev->mtu = new_mtu; - -	cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_MAX(port), size_without_fcs); -	cvmx_write_csr(CVMX_AGL_GMX_RXX_JABBER(port), -		       (size_without_fcs + 7) & 0xfff8); - -	return 0; -} - -static irqreturn_t octeon_mgmt_interrupt(int cpl, void *dev_id) -{ -	struct net_device *netdev = dev_id; -	struct octeon_mgmt *p = netdev_priv(netdev); -	int port = p->port; -	union cvmx_mixx_isr mixx_isr; - -	mixx_isr.u64 = cvmx_read_csr(CVMX_MIXX_ISR(port)); - -	/* Clear any pending interrupts */ -	cvmx_write_csr(CVMX_MIXX_ISR(port), mixx_isr.u64); -	cvmx_read_csr(CVMX_MIXX_ISR(port)); - -	if (mixx_isr.s.irthresh) { -		octeon_mgmt_disable_rx_irq(p); -		napi_schedule(&p->napi); -	} -	if (mixx_isr.s.orthresh) { -		octeon_mgmt_disable_tx_irq(p); -		tasklet_schedule(&p->tx_clean_tasklet); -	} - -	return IRQ_HANDLED; -} - -static int octeon_mgmt_ioctl(struct net_device *netdev, -			     struct ifreq *rq, int cmd) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); - -	if (!netif_running(netdev)) -		return -EINVAL; - -	if (!p->phydev) -		return -EINVAL; - -	return phy_mii_ioctl(p->phydev, rq, cmd); -} - -static void octeon_mgmt_adjust_link(struct net_device *netdev) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); -	int port = p->port; -	union cvmx_agl_gmx_prtx_cfg prtx_cfg; -	unsigned long flags; -	int link_changed = 0; - -	spin_lock_irqsave(&p->lock, flags); -	if (p->phydev->link) { -		if (!p->last_link) -			link_changed = 1; -		if (p->last_duplex != p->phydev->duplex) { -			p->last_duplex = p->phydev->duplex; -			prtx_cfg.u64 = -				cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); -			prtx_cfg.s.duplex = p->phydev->duplex; -			cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), -				       prtx_cfg.u64); -		} -	} else { -		if (p->last_link) -			link_changed = -1; -	} -	p->last_link = p->phydev->link; -	spin_unlock_irqrestore(&p->lock, flags); - -	if (link_changed != 0) { -		if (link_changed > 0) { -			netif_carrier_on(netdev); -			pr_info("%s: Link is up - %d/%s\n", netdev->name, -				p->phydev->speed, -				DUPLEX_FULL == p->phydev->duplex ? -				"Full" : "Half"); -		} else { -			netif_carrier_off(netdev); -			pr_info("%s: Link is down\n", netdev->name); -		} -	} -} - -static int octeon_mgmt_init_phy(struct net_device *netdev) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); -	char phy_id[20]; - -	if (octeon_is_simulation()) { -		/* No PHYs in the simulator. */ -		netif_carrier_on(netdev); -		return 0; -	} - -	snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "0", p->port); - -	p->phydev = phy_connect(netdev, phy_id, octeon_mgmt_adjust_link, 0, -				PHY_INTERFACE_MODE_MII); - -	if (IS_ERR(p->phydev)) { -		p->phydev = NULL; -		return -1; -	} - -	phy_start_aneg(p->phydev); - -	return 0; -} - -static int octeon_mgmt_open(struct net_device *netdev) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); -	int port = p->port; -	union cvmx_mixx_ctl mix_ctl; -	union cvmx_agl_gmx_inf_mode agl_gmx_inf_mode; -	union cvmx_mixx_oring1 oring1; -	union cvmx_mixx_iring1 iring1; -	union cvmx_agl_gmx_prtx_cfg prtx_cfg; -	union cvmx_agl_gmx_rxx_frm_ctl rxx_frm_ctl; -	union cvmx_mixx_irhwm mix_irhwm; -	union cvmx_mixx_orhwm mix_orhwm; -	union cvmx_mixx_intena mix_intena; -	struct sockaddr sa; - -	/* Allocate ring buffers.  */ -	p->tx_ring = kzalloc(ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), -			     GFP_KERNEL); -	if (!p->tx_ring) -		return -ENOMEM; -	p->tx_ring_handle = -		dma_map_single(p->dev, p->tx_ring, -			       ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), -			       DMA_BIDIRECTIONAL); -	p->tx_next = 0; -	p->tx_next_clean = 0; -	p->tx_current_fill = 0; - - -	p->rx_ring = kzalloc(ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), -			     GFP_KERNEL); -	if (!p->rx_ring) -		goto err_nomem; -	p->rx_ring_handle = -		dma_map_single(p->dev, p->rx_ring, -			       ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), -			       DMA_BIDIRECTIONAL); - -	p->rx_next = 0; -	p->rx_next_fill = 0; -	p->rx_current_fill = 0; - -	octeon_mgmt_reset_hw(p); - -	mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port)); - -	/* Bring it out of reset if needed. */ -	if (mix_ctl.s.reset) { -		mix_ctl.s.reset = 0; -		cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64); -		do { -			mix_ctl.u64 = cvmx_read_csr(CVMX_MIXX_CTL(port)); -		} while (mix_ctl.s.reset); -	} - -	agl_gmx_inf_mode.u64 = 0; -	agl_gmx_inf_mode.s.en = 1; -	cvmx_write_csr(CVMX_AGL_GMX_INF_MODE, agl_gmx_inf_mode.u64); - -	oring1.u64 = 0; -	oring1.s.obase = p->tx_ring_handle >> 3; -	oring1.s.osize = OCTEON_MGMT_TX_RING_SIZE; -	cvmx_write_csr(CVMX_MIXX_ORING1(port), oring1.u64); - -	iring1.u64 = 0; -	iring1.s.ibase = p->rx_ring_handle >> 3; -	iring1.s.isize = OCTEON_MGMT_RX_RING_SIZE; -	cvmx_write_csr(CVMX_MIXX_IRING1(port), iring1.u64); - -	/* Disable packet I/O. */ -	prtx_cfg.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); -	prtx_cfg.s.en = 0; -	cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), prtx_cfg.u64); - -	memcpy(sa.sa_data, netdev->dev_addr, ETH_ALEN); -	octeon_mgmt_set_mac_address(netdev, &sa); - -	octeon_mgmt_change_mtu(netdev, netdev->mtu); - -	/* -	 * Enable the port HW. Packets are not allowed until -	 * cvmx_mgmt_port_enable() is called. -	 */ -	mix_ctl.u64 = 0; -	mix_ctl.s.crc_strip = 1;    /* Strip the ending CRC */ -	mix_ctl.s.en = 1;           /* Enable the port */ -	mix_ctl.s.nbtarb = 0;       /* Arbitration mode */ -	/* MII CB-request FIFO programmable high watermark */ -	mix_ctl.s.mrq_hwm = 1; -	cvmx_write_csr(CVMX_MIXX_CTL(port), mix_ctl.u64); - -	if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X) -	    || OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X)) { -		/* -		 * Force compensation values, as they are not -		 * determined properly by HW -		 */ -		union cvmx_agl_gmx_drv_ctl drv_ctl; - -		drv_ctl.u64 = cvmx_read_csr(CVMX_AGL_GMX_DRV_CTL); -		if (port) { -			drv_ctl.s.byp_en1 = 1; -			drv_ctl.s.nctl1 = 6; -			drv_ctl.s.pctl1 = 6; -		} else { -			drv_ctl.s.byp_en = 1; -			drv_ctl.s.nctl = 6; -			drv_ctl.s.pctl = 6; -		} -		cvmx_write_csr(CVMX_AGL_GMX_DRV_CTL, drv_ctl.u64); -	} - -	octeon_mgmt_rx_fill_ring(netdev); - -	/* Clear statistics. */ -	/* Clear on read. */ -	cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_CTL(port), 1); -	cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_DRP(port), 0); -	cvmx_write_csr(CVMX_AGL_GMX_RXX_STATS_PKTS_BAD(port), 0); - -	cvmx_write_csr(CVMX_AGL_GMX_TXX_STATS_CTL(port), 1); -	cvmx_write_csr(CVMX_AGL_GMX_TXX_STAT0(port), 0); -	cvmx_write_csr(CVMX_AGL_GMX_TXX_STAT1(port), 0); - -	/* Clear any pending interrupts */ -	cvmx_write_csr(CVMX_MIXX_ISR(port), cvmx_read_csr(CVMX_MIXX_ISR(port))); - -	if (request_irq(p->irq, octeon_mgmt_interrupt, 0, netdev->name, -			netdev)) { -		dev_err(p->dev, "request_irq(%d) failed.\n", p->irq); -		goto err_noirq; -	} - -	/* Interrupt every single RX packet */ -	mix_irhwm.u64 = 0; -	mix_irhwm.s.irhwm = 0; -	cvmx_write_csr(CVMX_MIXX_IRHWM(port), mix_irhwm.u64); - -	/* Interrupt when we have 1 or more packets to clean.  */ -	mix_orhwm.u64 = 0; -	mix_orhwm.s.orhwm = 1; -	cvmx_write_csr(CVMX_MIXX_ORHWM(port), mix_orhwm.u64); - -	/* Enable receive and transmit interrupts */ -	mix_intena.u64 = 0; -	mix_intena.s.ithena = 1; -	mix_intena.s.othena = 1; -	cvmx_write_csr(CVMX_MIXX_INTENA(port), mix_intena.u64); - - -	/* Enable packet I/O. */ - -	rxx_frm_ctl.u64 = 0; -	rxx_frm_ctl.s.pre_align = 1; -	/* -	 * When set, disables the length check for non-min sized pkts -	 * with padding in the client data. -	 */ -	rxx_frm_ctl.s.pad_len = 1; -	/* When set, disables the length check for VLAN pkts */ -	rxx_frm_ctl.s.vlan_len = 1; -	/* When set, PREAMBLE checking is  less strict */ -	rxx_frm_ctl.s.pre_free = 1; -	/* Control Pause Frames can match station SMAC */ -	rxx_frm_ctl.s.ctl_smac = 0; -	/* Control Pause Frames can match globally assign Multicast address */ -	rxx_frm_ctl.s.ctl_mcst = 1; -	/* Forward pause information to TX block */ -	rxx_frm_ctl.s.ctl_bck = 1; -	/* Drop Control Pause Frames */ -	rxx_frm_ctl.s.ctl_drp = 1; -	/* Strip off the preamble */ -	rxx_frm_ctl.s.pre_strp = 1; -	/* -	 * This port is configured to send PREAMBLE+SFD to begin every -	 * frame.  GMX checks that the PREAMBLE is sent correctly. -	 */ -	rxx_frm_ctl.s.pre_chk = 1; -	cvmx_write_csr(CVMX_AGL_GMX_RXX_FRM_CTL(port), rxx_frm_ctl.u64); - -	/* Enable the AGL block */ -	agl_gmx_inf_mode.u64 = 0; -	agl_gmx_inf_mode.s.en = 1; -	cvmx_write_csr(CVMX_AGL_GMX_INF_MODE, agl_gmx_inf_mode.u64); - -	/* Configure the port duplex and enables */ -	prtx_cfg.u64 = cvmx_read_csr(CVMX_AGL_GMX_PRTX_CFG(port)); -	prtx_cfg.s.tx_en = 1; -	prtx_cfg.s.rx_en = 1; -	prtx_cfg.s.en = 1; -	p->last_duplex = 1; -	prtx_cfg.s.duplex = p->last_duplex; -	cvmx_write_csr(CVMX_AGL_GMX_PRTX_CFG(port), prtx_cfg.u64); - -	p->last_link = 0; -	netif_carrier_off(netdev); - -	if (octeon_mgmt_init_phy(netdev)) { -		dev_err(p->dev, "Cannot initialize PHY.\n"); -		goto err_noirq; -	} - -	netif_wake_queue(netdev); -	napi_enable(&p->napi); - -	return 0; -err_noirq: -	octeon_mgmt_reset_hw(p); -	dma_unmap_single(p->dev, p->rx_ring_handle, -			 ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), -			 DMA_BIDIRECTIONAL); -	kfree(p->rx_ring); -err_nomem: -	dma_unmap_single(p->dev, p->tx_ring_handle, -			 ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), -			 DMA_BIDIRECTIONAL); -	kfree(p->tx_ring); -	return -ENOMEM; -} - -static int octeon_mgmt_stop(struct net_device *netdev) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); - -	napi_disable(&p->napi); -	netif_stop_queue(netdev); - -	if (p->phydev) -		phy_disconnect(p->phydev); - -	netif_carrier_off(netdev); - -	octeon_mgmt_reset_hw(p); - -	free_irq(p->irq, netdev); - -	/* dma_unmap is a nop on Octeon, so just free everything.  */ -	skb_queue_purge(&p->tx_list); -	skb_queue_purge(&p->rx_list); - -	dma_unmap_single(p->dev, p->rx_ring_handle, -			 ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), -			 DMA_BIDIRECTIONAL); -	kfree(p->rx_ring); - -	dma_unmap_single(p->dev, p->tx_ring_handle, -			 ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), -			 DMA_BIDIRECTIONAL); -	kfree(p->tx_ring); - -	return 0; -} - -static int octeon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); -	int port = p->port; -	union mgmt_port_ring_entry re; -	unsigned long flags; -	int rv = NETDEV_TX_BUSY; - -	re.d64 = 0; -	re.s.len = skb->len; -	re.s.addr = dma_map_single(p->dev, skb->data, -				   skb->len, -				   DMA_TO_DEVICE); - -	spin_lock_irqsave(&p->tx_list.lock, flags); - -	if (unlikely(p->tx_current_fill >= ring_max_fill(OCTEON_MGMT_TX_RING_SIZE) - 1)) { -		spin_unlock_irqrestore(&p->tx_list.lock, flags); -		netif_stop_queue(netdev); -		spin_lock_irqsave(&p->tx_list.lock, flags); -	} - -	if (unlikely(p->tx_current_fill >= -		     ring_max_fill(OCTEON_MGMT_TX_RING_SIZE))) { -		spin_unlock_irqrestore(&p->tx_list.lock, flags); -		dma_unmap_single(p->dev, re.s.addr, re.s.len, -				 DMA_TO_DEVICE); -		goto out; -	} - -	__skb_queue_tail(&p->tx_list, skb); - -	/* Put it in the ring.  */ -	p->tx_ring[p->tx_next] = re.d64; -	p->tx_next = (p->tx_next + 1) % OCTEON_MGMT_TX_RING_SIZE; -	p->tx_current_fill++; - -	spin_unlock_irqrestore(&p->tx_list.lock, flags); - -	dma_sync_single_for_device(p->dev, p->tx_ring_handle, -				   ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), -				   DMA_BIDIRECTIONAL); - -	netdev->stats.tx_packets++; -	netdev->stats.tx_bytes += skb->len; - -	/* Ring the bell.  */ -	cvmx_write_csr(CVMX_MIXX_ORING2(port), 1); - -	rv = NETDEV_TX_OK; -out: -	octeon_mgmt_update_tx_stats(netdev); -	return rv; -} - -#ifdef CONFIG_NET_POLL_CONTROLLER -static void octeon_mgmt_poll_controller(struct net_device *netdev) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); - -	octeon_mgmt_receive_packets(p, 16); -	octeon_mgmt_update_rx_stats(netdev); -} -#endif - -static void octeon_mgmt_get_drvinfo(struct net_device *netdev, -				    struct ethtool_drvinfo *info) -{ -	strncpy(info->driver, DRV_NAME, sizeof(info->driver)); -	strncpy(info->version, DRV_VERSION, sizeof(info->version)); -	strncpy(info->fw_version, "N/A", sizeof(info->fw_version)); -	strncpy(info->bus_info, "N/A", sizeof(info->bus_info)); -	info->n_stats = 0; -	info->testinfo_len = 0; -	info->regdump_len = 0; -	info->eedump_len = 0; -} - -static int octeon_mgmt_get_settings(struct net_device *netdev, -				    struct ethtool_cmd *cmd) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); - -	if (p->phydev) -		return phy_ethtool_gset(p->phydev, cmd); - -	return -EINVAL; -} - -static int octeon_mgmt_set_settings(struct net_device *netdev, -				    struct ethtool_cmd *cmd) -{ -	struct octeon_mgmt *p = netdev_priv(netdev); - -	if (!capable(CAP_NET_ADMIN)) -		return -EPERM; - -	if (p->phydev) -		return phy_ethtool_sset(p->phydev, cmd); - -	return -EINVAL; -} - -static const struct ethtool_ops octeon_mgmt_ethtool_ops = { -	.get_drvinfo = octeon_mgmt_get_drvinfo, -	.get_link = ethtool_op_get_link, -	.get_settings = octeon_mgmt_get_settings, -	.set_settings = octeon_mgmt_set_settings -}; - -static const struct net_device_ops octeon_mgmt_ops = { -	.ndo_open =			octeon_mgmt_open, -	.ndo_stop =			octeon_mgmt_stop, -	.ndo_start_xmit =		octeon_mgmt_xmit, -	.ndo_set_rx_mode = 		octeon_mgmt_set_rx_filtering, -	.ndo_set_multicast_list =	octeon_mgmt_set_rx_filtering, -	.ndo_set_mac_address =		octeon_mgmt_set_mac_address, -	.ndo_do_ioctl = 		octeon_mgmt_ioctl, -	.ndo_change_mtu =		octeon_mgmt_change_mtu, -#ifdef CONFIG_NET_POLL_CONTROLLER -	.ndo_poll_controller =		octeon_mgmt_poll_controller, -#endif -}; - -static int __devinit octeon_mgmt_probe(struct platform_device *pdev) -{ -	struct resource *res_irq; -	struct net_device *netdev; -	struct octeon_mgmt *p; -	int i; - -	netdev = alloc_etherdev(sizeof(struct octeon_mgmt)); -	if (netdev == NULL) -		return -ENOMEM; - -	dev_set_drvdata(&pdev->dev, netdev); -	p = netdev_priv(netdev); -	netif_napi_add(netdev, &p->napi, octeon_mgmt_napi_poll, -		       OCTEON_MGMT_NAPI_WEIGHT); - -	p->netdev = netdev; -	p->dev = &pdev->dev; - -	p->port = pdev->id; -	snprintf(netdev->name, IFNAMSIZ, "mgmt%d", p->port); - -	res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); -	if (!res_irq) -		goto err; - -	p->irq = res_irq->start; -	spin_lock_init(&p->lock); - -	skb_queue_head_init(&p->tx_list); -	skb_queue_head_init(&p->rx_list); -	tasklet_init(&p->tx_clean_tasklet, -		     octeon_mgmt_clean_tx_tasklet, (unsigned long)p); - -	netdev->netdev_ops = &octeon_mgmt_ops; -	netdev->ethtool_ops = &octeon_mgmt_ethtool_ops; - -	/* The mgmt ports get the first N MACs.  */ -	for (i = 0; i < 6; i++) -		netdev->dev_addr[i] = octeon_bootinfo->mac_addr_base[i]; -	netdev->dev_addr[5] += p->port; - -	if (p->port >= octeon_bootinfo->mac_addr_count) -		dev_err(&pdev->dev, -			"Error %s: Using MAC outside of the assigned range: %pM\n", -			netdev->name, netdev->dev_addr); - -	if (register_netdev(netdev)) -		goto err; - -	dev_info(&pdev->dev, "Version " DRV_VERSION "\n"); -	return 0; -err: -	free_netdev(netdev); -	return -ENOENT; -} - -static int __devexit octeon_mgmt_remove(struct platform_device *pdev) -{ -	struct net_device *netdev = dev_get_drvdata(&pdev->dev); - -	unregister_netdev(netdev); -	free_netdev(netdev); -	return 0; -} - -static struct platform_driver octeon_mgmt_driver = { -	.driver = { -		.name		= "octeon_mgmt", -		.owner		= THIS_MODULE, -	}, -	.probe		= octeon_mgmt_probe, -	.remove		= __devexit_p(octeon_mgmt_remove), -}; - -extern void octeon_mdiobus_force_mod_depencency(void); - -static int __init octeon_mgmt_mod_init(void) -{ -	/* Force our mdiobus driver module to be loaded first. */ -	octeon_mdiobus_force_mod_depencency(); -	return platform_driver_register(&octeon_mgmt_driver); -} - -static void __exit octeon_mgmt_mod_exit(void) -{ -	platform_driver_unregister(&octeon_mgmt_driver); -} - -module_init(octeon_mgmt_mod_init); -module_exit(octeon_mgmt_mod_exit); - -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_AUTHOR("David Daney"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION);  | 
